From 3c57dd931145d43f2b0aef96c4d178135956bf91 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 05:13:10 +0200 Subject: Adding upstream version 2.10.36. Signed-off-by: Daniel Baumann --- app/Makefile.am | 290 + app/Makefile.in | 1664 ++ app/about.h | 53 + app/actions/Makefile.am | 202 + app/actions/Makefile.in | 1489 ++ app/actions/actions-types.h | 54 + app/actions/actions.c | 737 + app/actions/actions.h | 120 + app/actions/brush-editor-actions.c | 87 + app/actions/brush-editor-actions.h | 27 + app/actions/brushes-actions.c | 149 + app/actions/brushes-actions.h | 27 + app/actions/buffers-actions.c | 136 + app/actions/buffers-actions.h | 27 + app/actions/buffers-commands.c | 138 + app/actions/buffers-commands.h | 33 + app/actions/channels-actions.c | 348 + app/actions/channels-actions.h | 27 + app/actions/channels-commands.c | 567 + app/actions/channels-commands.h | 77 + app/actions/colormap-actions.c | 173 + app/actions/colormap-actions.h | 27 + app/actions/colormap-commands.c | 96 + app/actions/colormap-commands.h | 33 + app/actions/context-actions.c | 1294 ++ app/actions/context-actions.h | 27 + app/actions/context-commands.c | 975 ++ app/actions/context-commands.h | 140 + app/actions/cursor-info-actions.c | 81 + app/actions/cursor-info-actions.h | 27 + app/actions/cursor-info-commands.c | 41 + app/actions/cursor-info-commands.h | 27 + app/actions/dashboard-actions.c | 230 + app/actions/dashboard-actions.h | 27 + app/actions/dashboard-commands.c | 406 + app/actions/dashboard-commands.h | 48 + app/actions/data-commands.c | 304 + app/actions/data-commands.h | 48 + app/actions/data-editor-commands.c | 43 + app/actions/data-editor-commands.h | 27 + app/actions/debug-actions.c | 107 + app/actions/debug-actions.h | 27 + app/actions/debug-commands.c | 430 + app/actions/debug-commands.h | 46 + app/actions/dialogs-actions.c | 368 + app/actions/dialogs-actions.h | 38 + app/actions/dialogs-commands.c | 78 + app/actions/dialogs-commands.h | 30 + app/actions/dock-actions.c | 141 + app/actions/dock-actions.h | 27 + app/actions/dock-commands.c | 85 + app/actions/dock-commands.h | 30 + app/actions/dockable-actions.c | 375 + app/actions/dockable-actions.h | 27 + app/actions/dockable-commands.c | 298 + app/actions/dockable-commands.h | 49 + app/actions/documents-actions.c | 143 + app/actions/documents-actions.h | 27 + app/actions/documents-commands.c | 410 + app/actions/documents-commands.h | 54 + app/actions/drawable-actions.c | 231 + app/actions/drawable-actions.h | 27 + app/actions/drawable-commands.c | 304 + app/actions/drawable-commands.h | 50 + app/actions/dynamics-actions.c | 137 + app/actions/dynamics-actions.h | 27 + app/actions/dynamics-editor-actions.c | 89 + app/actions/dynamics-editor-actions.h | 27 + app/actions/edit-actions.c | 417 + app/actions/edit-actions.h | 27 + app/actions/edit-commands.c | 721 + app/actions/edit-commands.h | 76 + app/actions/error-console-actions.c | 152 + app/actions/error-console-actions.h | 27 + app/actions/error-console-commands.c | 201 + app/actions/error-console-commands.h | 43 + app/actions/file-actions.c | 452 + app/actions/file-actions.h | 27 + app/actions/file-commands.c | 837 + app/actions/file-commands.h | 63 + app/actions/filters-actions.c | 1229 ++ app/actions/filters-actions.h | 27 + app/actions/filters-commands.c | 265 + app/actions/filters-commands.h | 37 + app/actions/fonts-actions.c | 73 + app/actions/fonts-actions.h | 27 + app/actions/gimpgeglprocedure.c | 476 + app/actions/gimpgeglprocedure.h | 70 + app/actions/gradient-editor-actions.c | 906 ++ app/actions/gradient-editor-actions.h | 27 + app/actions/gradient-editor-commands.c | 738 + app/actions/gradient-editor-commands.h | 99 + app/actions/gradients-actions.c | 150 + app/actions/gradients-actions.h | 27 + app/actions/gradients-commands.c | 151 + app/actions/gradients-commands.h | 27 + app/actions/help-actions.c | 66 + app/actions/help-actions.h | 27 + app/actions/help-commands.c | 57 + app/actions/help-commands.h | 30 + app/actions/image-actions.c | 497 + app/actions/image-actions.h | 27 + app/actions/image-commands.c | 1581 ++ app/actions/image-commands.h | 101 + app/actions/images-actions.c | 98 + app/actions/images-actions.h | 27 + app/actions/images-commands.c | 117 + app/actions/images-commands.h | 33 + app/actions/items-actions.c | 142 + app/actions/items-actions.h | 29 + app/actions/items-commands.c | 419 + app/actions/items-commands.h | 72 + app/actions/layers-actions.c | 1027 ++ app/actions/layers-actions.h | 27 + app/actions/layers-commands.c | 1749 +++ app/actions/layers-commands.h | 173 + app/actions/mypaint-brushes-actions.c | 142 + app/actions/mypaint-brushes-actions.h | 27 + app/actions/palette-editor-actions.c | 183 + app/actions/palette-editor-actions.h | 27 + app/actions/palette-editor-commands.c | 96 + app/actions/palette-editor-commands.h | 37 + app/actions/palettes-actions.c | 159 + app/actions/palettes-actions.h | 27 + app/actions/palettes-commands.c | 152 + app/actions/palettes-commands.h | 30 + app/actions/patterns-actions.c | 149 + app/actions/patterns-actions.h | 27 + app/actions/plug-in-actions.c | 517 + app/actions/plug-in-actions.h | 27 + app/actions/plug-in-commands.c | 221 + app/actions/plug-in-commands.h | 31 + app/actions/procedure-commands.c | 326 + app/actions/procedure-commands.h | 46 + app/actions/quick-mask-actions.c | 138 + app/actions/quick-mask-actions.h | 27 + app/actions/quick-mask-commands.c | 182 + app/actions/quick-mask-commands.h | 33 + app/actions/sample-points-actions.c | 81 + app/actions/sample-points-actions.h | 27 + app/actions/sample-points-commands.c | 41 + app/actions/sample-points-commands.h | 27 + app/actions/select-actions.c | 199 + app/actions/select-actions.h | 27 + app/actions/select-commands.c | 685 + app/actions/select-commands.h | 70 + app/actions/templates-actions.c | 105 + app/actions/templates-actions.h | 27 + app/actions/templates-commands.c | 345 + app/actions/templates-commands.h | 39 + app/actions/text-editor-actions.c | 148 + app/actions/text-editor-actions.h | 27 + app/actions/text-editor-commands.c | 153 + app/actions/text-editor-commands.h | 33 + app/actions/text-tool-actions.c | 229 + app/actions/text-tool-actions.h | 27 + app/actions/text-tool-commands.c | 229 + app/actions/text-tool-commands.h | 51 + app/actions/tool-options-actions.c | 238 + app/actions/tool-options-actions.h | 27 + app/actions/tool-options-commands.c | 268 + app/actions/tool-options-commands.h | 47 + app/actions/tool-preset-editor-actions.c | 105 + app/actions/tool-preset-editor-actions.h | 27 + app/actions/tool-preset-editor-commands.c | 90 + app/actions/tool-preset-editor-commands.h | 30 + app/actions/tool-presets-actions.c | 155 + app/actions/tool-presets-actions.h | 27 + app/actions/tool-presets-commands.c | 95 + app/actions/tool-presets-commands.h | 30 + app/actions/tools-actions.c | 812 + app/actions/tools-actions.h | 27 + app/actions/tools-commands.c | 820 + app/actions/tools-commands.h | 120 + app/actions/vectors-actions.c | 465 + app/actions/vectors-actions.h | 27 + app/actions/vectors-commands.c | 892 ++ app/actions/vectors-commands.h | 112 + app/actions/view-actions.c | 1211 ++ app/actions/view-actions.h | 27 + app/actions/view-commands.c | 1281 ++ app/actions/view-commands.h | 181 + app/actions/window-actions.c | 297 + app/actions/window-actions.h | 28 + app/actions/window-commands.c | 158 + app/actions/window-commands.h | 33 + app/actions/windows-actions.c | 618 + app/actions/windows-actions.h | 28 + app/actions/windows-commands.c | 225 + app/actions/windows-commands.h | 53 + app/app.c | 523 + app/app.h | 57 + app/config/Makefile.am | 165 + app/config/Makefile.in | 1505 ++ app/config/config-enums.c | 397 + app/config/config-enums.h | 173 + app/config/config-types.h | 59 + app/config/gimpconfig-dump.c | 644 + app/config/gimpconfig-dump.h | 37 + app/config/gimpconfig-file.c | 241 + app/config/gimpconfig-file.h | 36 + app/config/gimpconfig-utils.c | 223 + app/config/gimpconfig-utils.h | 36 + app/config/gimpcoreconfig.c | 1415 ++ app/config/gimpcoreconfig.h | 123 + app/config/gimpdialogconfig.c | 991 ++ app/config/gimpdialogconfig.h | 123 + app/config/gimpdisplayconfig.c | 623 + app/config/gimpdisplayconfig.h | 81 + app/config/gimpdisplayoptions.c | 597 + app/config/gimpdisplayoptions.h | 78 + app/config/gimpgeglconfig.c | 273 + app/config/gimpgeglconfig.h | 55 + app/config/gimpguiconfig.c | 1017 ++ app/config/gimpguiconfig.h | 110 + app/config/gimplangrc.c | 280 + app/config/gimplangrc.h | 60 + app/config/gimppluginconfig.c | 217 + app/config/gimppluginconfig.h | 56 + app/config/gimprc-blurbs.h | 747 + app/config/gimprc-deserialize.c | 174 + app/config/gimprc-deserialize.h | 31 + app/config/gimprc-serialize.c | 120 + app/config/gimprc-serialize.h | 30 + app/config/gimprc-unknown.c | 214 + app/config/gimprc-unknown.h | 40 + app/config/gimprc.c | 593 + app/config/gimprc.h | 77 + app/config/gimpxmlparser.c | 404 + app/config/gimpxmlparser.h | 46 + app/config/test-config.c | 278 + app/core/Makefile.am | 550 + app/core/Makefile.in | 2438 +++ app/core/core-enums.c | 1316 ++ app/core/core-enums.h | 701 + app/core/core-types.h | 303 + app/core/gimp-atomic.c | 95 + app/core/gimp-atomic.h | 32 + app/core/gimp-batch.c | 203 + app/core/gimp-batch.h | 27 + app/core/gimp-cairo.c | 217 + app/core/gimp-cairo.h | 51 + app/core/gimp-contexts.c | 161 + app/core/gimp-contexts.h | 36 + app/core/gimp-data-factories.c | 433 + app/core/gimp-data-factories.h | 36 + app/core/gimp-edit.c | 771 + app/core/gimp-edit.h | 61 + app/core/gimp-filter-history.c | 160 + app/core/gimp-filter-history.h | 35 + app/core/gimp-gradients.c | 183 + app/core/gimp-gradients.h | 34 + app/core/gimp-gui.c | 585 + app/core/gimp-gui.h | 211 + app/core/gimp-internal-data.c | 346 + app/core/gimp-internal-data.h | 34 + app/core/gimp-memsize.c | 341 + app/core/gimp-memsize.h | 60 + app/core/gimp-modules.c | 227 + app/core/gimp-modules.h | 34 + app/core/gimp-palettes.c | 143 + app/core/gimp-palettes.h | 35 + app/core/gimp-parallel.cc | 553 + app/core/gimp-parallel.h | 157 + app/core/gimp-parasites.c | 170 + app/core/gimp-parasites.h | 41 + app/core/gimp-spawn.c | 250 + app/core/gimp-spawn.h | 34 + app/core/gimp-tags.c | 271 + app/core/gimp-tags.h | 25 + app/core/gimp-templates.c | 213 + app/core/gimp-templates.h | 28 + app/core/gimp-transform-3d-utils.c | 359 + app/core/gimp-transform-3d-utils.h | 95 + app/core/gimp-transform-resize.c | 841 + app/core/gimp-transform-resize.h | 34 + app/core/gimp-transform-utils.c | 1211 ++ app/core/gimp-transform-utils.h | 125 + app/core/gimp-units.c | 488 + app/core/gimp-units.h | 29 + app/core/gimp-user-install.c | 977 ++ app/core/gimp-user-install.h | 39 + app/core/gimp-utils.c | 1098 ++ app/core/gimp-utils.h | 116 + app/core/gimp.c | 1224 ++ app/core/gimp.h | 248 + app/core/gimpasync.c | 752 + app/core/gimpasync.h | 95 + app/core/gimpasyncset.c | 348 + app/core/gimpasyncset.h | 61 + app/core/gimpauxitem.c | 147 + app/core/gimpauxitem.h | 56 + app/core/gimpauxitemundo.c | 138 + app/core/gimpauxitemundo.h | 52 + app/core/gimpbacktrace-backend.h | 34 + app/core/gimpbacktrace-linux.c | 725 + app/core/gimpbacktrace-none.c | 126 + app/core/gimpbacktrace-windows.c | 706 + app/core/gimpbacktrace.h | 70 + app/core/gimpbezierdesc.c | 202 + app/core/gimpbezierdesc.h | 47 + app/core/gimpboundary.c | 1016 ++ app/core/gimpboundary.h | 68 + app/core/gimpbrush-boundary.c | 130 + app/core/gimpbrush-boundary.h | 32 + app/core/gimpbrush-header.h | 49 + app/core/gimpbrush-load.c | 1213 ++ app/core/gimpbrush-load.h | 43 + app/core/gimpbrush-mipmap.cc | 514 + app/core/gimpbrush-mipmap.h | 38 + app/core/gimpbrush-private.h | 47 + app/core/gimpbrush-save.c | 107 + app/core/gimpbrush-save.h | 28 + app/core/gimpbrush-transform.cc | 1043 ++ app/core/gimpbrush-transform.h | 59 + app/core/gimpbrush.c | 945 ++ app/core/gimpbrush.h | 152 + app/core/gimpbrushcache.c | 299 + app/core/gimpbrushcache.h | 83 + app/core/gimpbrushclipboard.c | 298 + app/core/gimpbrushclipboard.h | 58 + app/core/gimpbrushgenerated-load.c | 284 + app/core/gimpbrushgenerated-load.h | 33 + app/core/gimpbrushgenerated-save.c | 119 + app/core/gimpbrushgenerated-save.h | 30 + app/core/gimpbrushgenerated.c | 875 ++ app/core/gimpbrushgenerated.h | 88 + app/core/gimpbrushpipe-load.c | 163 + app/core/gimpbrushpipe-load.h | 32 + app/core/gimpbrushpipe-save.c | 59 + app/core/gimpbrushpipe-save.h | 28 + app/core/gimpbrushpipe.c | 420 + app/core/gimpbrushpipe.h | 80 + app/core/gimpbuffer.c | 541 + app/core/gimpbuffer.h | 90 + app/core/gimpcancelable.c | 72 + app/core/gimpcancelable.h | 47 + app/core/gimpchannel-combine.c | 471 + app/core/gimpchannel-combine.h | 56 + app/core/gimpchannel-select.c | 605 + app/core/gimpchannel-select.h | 160 + app/core/gimpchannel.c | 1956 +++ app/core/gimpchannel.h | 216 + app/core/gimpchannelpropundo.c | 108 + app/core/gimpchannelpropundo.h | 52 + app/core/gimpchannelundo.c | 214 + app/core/gimpchannelundo.h | 54 + app/core/gimpchunkiterator.c | 555 + app/core/gimpchunkiterator.h | 44 + app/core/gimpcontainer-filter.c | 174 + app/core/gimpcontainer-filter.h | 38 + app/core/gimpcontainer.c | 1167 ++ app/core/gimpcontainer.h | 150 + app/core/gimpcontext.c | 3828 +++++ app/core/gimpcontext.h | 360 + app/core/gimpcoords-interpolate.c | 371 + app/core/gimpcoords-interpolate.h | 41 + app/core/gimpcoords.c | 248 + app/core/gimpcoords.h | 57 + app/core/gimpcurve-load.c | 54 + app/core/gimpcurve-load.h | 30 + app/core/gimpcurve-map.c | 253 + app/core/gimpcurve-map.h | 34 + app/core/gimpcurve-save.c | 44 + app/core/gimpcurve-save.h | 28 + app/core/gimpcurve.c | 1289 ++ app/core/gimpcurve.h | 124 + app/core/gimpdashpattern.c | 355 + app/core/gimpdashpattern.h | 53 + app/core/gimpdata.c | 1245 ++ app/core/gimpdata.h | 133 + app/core/gimpdatafactory.c | 945 ++ app/core/gimpdatafactory.h | 125 + app/core/gimpdataloaderfactory.c | 562 + app/core/gimpdataloaderfactory.h | 77 + app/core/gimpdocumentlist.c | 106 + app/core/gimpdocumentlist.h | 54 + app/core/gimpdrawable-bucket-fill.c | 499 + app/core/gimpdrawable-bucket-fill.h | 59 + app/core/gimpdrawable-combine.c | 144 + app/core/gimpdrawable-combine.h | 39 + app/core/gimpdrawable-edit.c | 231 + app/core/gimpdrawable-edit.h | 29 + app/core/gimpdrawable-equalize.c | 71 + app/core/gimpdrawable-equalize.h | 26 + app/core/gimpdrawable-fill.c | 279 + app/core/gimpdrawable-fill.h | 60 + app/core/gimpdrawable-filters.c | 343 + app/core/gimpdrawable-filters.h | 46 + app/core/gimpdrawable-floating-selection.c | 512 + app/core/gimpdrawable-floating-selection.h | 31 + app/core/gimpdrawable-foreground-extract.c | 150 + app/core/gimpdrawable-foreground-extract.h | 31 + app/core/gimpdrawable-gradient.c | 313 + app/core/gimpdrawable-gradient.h | 57 + app/core/gimpdrawable-histogram.c | 258 + app/core/gimpdrawable-histogram.h | 32 + app/core/gimpdrawable-levels.c | 77 + app/core/gimpdrawable-levels.h | 26 + app/core/gimpdrawable-offset.c | 83 + app/core/gimpdrawable-offset.h | 30 + app/core/gimpdrawable-operation.c | 128 + app/core/gimpdrawable-operation.h | 43 + app/core/gimpdrawable-preview.c | 492 + app/core/gimpdrawable-preview.h | 63 + app/core/gimpdrawable-private.h | 44 + app/core/gimpdrawable-shadow.c | 110 + app/core/gimpdrawable-shadow.h | 32 + app/core/gimpdrawable-stroke.c | 161 + app/core/gimpdrawable-stroke.h | 45 + app/core/gimpdrawable-transform.c | 1070 ++ app/core/gimpdrawable-transform.h | 94 + app/core/gimpdrawable.c | 1916 +++ app/core/gimpdrawable.h | 227 + app/core/gimpdrawablefilter.c | 1364 ++ app/core/gimpdrawablefilter.h | 108 + app/core/gimpdrawablemodundo.c | 216 + app/core/gimpdrawablemodundo.h | 55 + app/core/gimpdrawablestack.c | 224 + app/core/gimpdrawablestack.h | 66 + app/core/gimpdrawableundo.c | 207 + app/core/gimpdrawableundo.h | 54 + app/core/gimpdynamics-load.c | 55 + app/core/gimpdynamics-load.h | 31 + app/core/gimpdynamics-save.c | 44 + app/core/gimpdynamics-save.h | 28 + app/core/gimpdynamics.c | 653 + app/core/gimpdynamics.h | 77 + app/core/gimpdynamicsoutput.c | 767 + app/core/gimpdynamicsoutput.h | 68 + app/core/gimperror.c | 36 + app/core/gimperror.h | 33 + app/core/gimpfilloptions.c | 548 + app/core/gimpfilloptions.h | 95 + app/core/gimpfilter.c | 315 + app/core/gimpfilter.h | 73 + app/core/gimpfilteredcontainer.c | 373 + app/core/gimpfilteredcontainer.h | 68 + app/core/gimpfilterstack.c | 350 + app/core/gimpfilterstack.h | 55 + app/core/gimpfloatingselectionundo.c | 135 + app/core/gimpfloatingselectionundo.h | 52 + app/core/gimpgradient-load.c | 575 + app/core/gimpgradient-load.h | 36 + app/core/gimpgradient-save.c | 221 + app/core/gimpgradient-save.h | 32 + app/core/gimpgradient.c | 2297 +++ app/core/gimpgradient.h | 296 + app/core/gimpgrid.c | 359 + app/core/gimpgrid.h | 81 + app/core/gimpgrouplayer.c | 2297 +++ app/core/gimpgrouplayer.h | 78 + app/core/gimpgrouplayerundo.c | 253 + app/core/gimpgrouplayerundo.h | 57 + app/core/gimpguide.c | 242 + app/core/gimpguide.h | 75 + app/core/gimpguideundo.c | 116 + app/core/gimpguideundo.h | 53 + app/core/gimphistogram.c | 1261 ++ app/core/gimphistogram.h | 105 + app/core/gimpidtable.c | 227 + app/core/gimpidtable.h | 68 + app/core/gimpimage-arrange.c | 388 + app/core/gimpimage-arrange.h | 29 + app/core/gimpimage-color-profile.c | 822 + app/core/gimpimage-color-profile.h | 113 + app/core/gimpimage-colormap.c | 362 + app/core/gimpimage-colormap.h | 55 + app/core/gimpimage-convert-data.h | 143 + app/core/gimpimage-convert-fsdither.h | 559 + app/core/gimpimage-convert-indexed.c | 4567 ++++++ app/core/gimpimage-convert-indexed.h | 41 + app/core/gimpimage-convert-precision.c | 304 + app/core/gimpimage-convert-precision.h | 36 + app/core/gimpimage-convert-type.c | 162 + app/core/gimpimage-convert-type.h | 29 + app/core/gimpimage-crop.c | 234 + app/core/gimpimage-crop.h | 32 + app/core/gimpimage-duplicate.c | 535 + app/core/gimpimage-duplicate.h | 25 + app/core/gimpimage-flip.c | 276 + app/core/gimpimage-flip.h | 34 + app/core/gimpimage-grid.c | 67 + app/core/gimpimage-grid.h | 31 + app/core/gimpimage-guides.c | 216 + app/core/gimpimage-guides.h | 54 + app/core/gimpimage-item-list.c | 401 + app/core/gimpimage-item-list.h | 63 + app/core/gimpimage-merge.c | 745 + app/core/gimpimage-merge.h | 46 + app/core/gimpimage-metadata.c | 184 + app/core/gimpimage-metadata.h | 33 + app/core/gimpimage-new.c | 393 + app/core/gimpimage-new.h | 42 + app/core/gimpimage-pick-color.c | 147 + app/core/gimpimage-pick-color.h | 35 + app/core/gimpimage-pick-item.c | 381 + app/core/gimpimage-pick-item.h | 57 + app/core/gimpimage-preview.c | 214 + app/core/gimpimage-preview.h | 51 + app/core/gimpimage-private.h | 149 + app/core/gimpimage-quick-mask.c | 212 + app/core/gimpimage-quick-mask.h | 43 + app/core/gimpimage-resize.c | 327 + app/core/gimpimage-resize.h | 53 + app/core/gimpimage-rotate.c | 377 + app/core/gimpimage-rotate.h | 28 + app/core/gimpimage-sample-points.c | 213 + app/core/gimpimage-sample-points.h | 60 + app/core/gimpimage-scale.c | 260 + app/core/gimpimage-scale.h | 36 + app/core/gimpimage-snap.c | 719 + app/core/gimpimage-snap.h | 63 + app/core/gimpimage-symmetry.c | 189 + app/core/gimpimage-symmetry.h | 40 + app/core/gimpimage-transform.c | 338 + app/core/gimpimage-transform.h | 34 + app/core/gimpimage-undo-push.c | 1060 ++ app/core/gimpimage-undo-push.h | 256 + app/core/gimpimage-undo.c | 695 + app/core/gimpimage-undo.h | 57 + app/core/gimpimage.c | 5191 +++++++ app/core/gimpimage.h | 463 + app/core/gimpimagefile.c | 1078 ++ app/core/gimpimagefile.h | 90 + app/core/gimpimageproxy.c | 877 ++ app/core/gimpimageproxy.h | 65 + app/core/gimpimageundo.c | 535 + app/core/gimpimageundo.h | 69 + app/core/gimpitem-exclusive.c | 276 + app/core/gimpitem-exclusive.h | 31 + app/core/gimpitem-linked.c | 187 + app/core/gimpitem-linked.h | 48 + app/core/gimpitem-preview.c | 133 + app/core/gimpitem-preview.h | 40 + app/core/gimpitem.c | 2689 ++++ app/core/gimpitem.h | 405 + app/core/gimpitempropundo.c | 358 + app/core/gimpitempropundo.h | 63 + app/core/gimpitemstack.c | 348 + app/core/gimpitemstack.h | 66 + app/core/gimpitemtree.c | 714 + app/core/gimpitemtree.h | 89 + app/core/gimpitemundo.c | 139 + app/core/gimpitemundo.h | 52 + app/core/gimplayer-floating-selection.c | 332 + app/core/gimplayer-floating-selection.h | 33 + app/core/gimplayer-new.c | 253 + app/core/gimplayer-new.h | 51 + app/core/gimplayer.c | 2943 ++++ app/core/gimplayer.h | 243 + app/core/gimplayermask.c | 297 + app/core/gimplayermask.h | 61 + app/core/gimplayermaskpropundo.c | 122 + app/core/gimplayermaskpropundo.h | 53 + app/core/gimplayermaskundo.c | 195 + app/core/gimplayermaskundo.h | 52 + app/core/gimplayerpropundo.c | 157 + app/core/gimplayerpropundo.h | 57 + app/core/gimplayerstack.c | 243 + app/core/gimplayerstack.h | 51 + app/core/gimplayerundo.c | 212 + app/core/gimplayerundo.h | 54 + app/core/gimplineart.c | 2979 ++++ app/core/gimplineart.h | 73 + app/core/gimplist.c | 691 + app/core/gimplist.h | 70 + app/core/gimpmarshal.c | 1901 +++ app/core/gimpmarshal.h | 378 + app/core/gimpmarshal.list | 74 + app/core/gimpmaskundo.c | 292 + app/core/gimpmaskundo.h | 58 + app/core/gimpmybrush-load.c | 153 + app/core/gimpmybrush-load.h | 33 + app/core/gimpmybrush-private.h | 35 + app/core/gimpmybrush.c | 281 + app/core/gimpmybrush.h | 66 + app/core/gimpobject.c | 512 + app/core/gimpobject.h | 74 + app/core/gimpobjectqueue.c | 198 + app/core/gimpobjectqueue.h | 66 + app/core/gimppaintinfo.c | 142 + app/core/gimppaintinfo.h | 69 + app/core/gimppalette-import.c | 574 + app/core/gimppalette-import.h | 48 + app/core/gimppalette-load.c | 1328 ++ app/core/gimppalette-load.h | 76 + app/core/gimppalette-save.c | 77 + app/core/gimppalette-save.h | 28 + app/core/gimppalette.c | 721 + app/core/gimppalette.h | 103 + app/core/gimppalettemru.c | 230 + app/core/gimppalettemru.h | 62 + app/core/gimpparamspecs-desc.c | 193 + app/core/gimpparamspecs-desc.h | 25 + app/core/gimpparamspecs-duplicate.c | 269 + app/core/gimpparamspecs-duplicate.h | 28 + app/core/gimpparamspecs.c | 2925 ++++ app/core/gimpparamspecs.h | 904 ++ app/core/gimpparasitelist.c | 453 + app/core/gimpparasitelist.h | 69 + app/core/gimppattern-header.h | 48 + app/core/gimppattern-load.c | 220 + app/core/gimppattern-load.h | 35 + app/core/gimppattern-save.c | 87 + app/core/gimppattern-save.h | 28 + app/core/gimppattern.c | 319 + app/core/gimppattern.h | 58 + app/core/gimppatternclipboard.c | 213 + app/core/gimppatternclipboard.h | 56 + app/core/gimppdbprogress.c | 408 + app/core/gimppdbprogress.h | 66 + app/core/gimppickable-auto-shrink.c | 312 + app/core/gimppickable-auto-shrink.h | 41 + app/core/gimppickable-contiguous-region.cc | 1123 ++ app/core/gimppickable-contiguous-region.h | 43 + app/core/gimppickable.c | 378 + app/core/gimppickable.h | 110 + app/core/gimpprogress.c | 266 + app/core/gimpprogress.h | 99 + app/core/gimpprojectable.c | 262 + app/core/gimpprojectable.h | 90 + app/core/gimpprojection.c | 1132 ++ app/core/gimpprojection.h | 83 + app/core/gimpsamplepoint.c | 215 + app/core/gimpsamplepoint.h | 68 + app/core/gimpsamplepointundo.c | 121 + app/core/gimpsamplepointundo.h | 54 + app/core/gimpscanconvert.c | 647 + app/core/gimpscanconvert.h | 81 + app/core/gimpselection.c | 863 ++ app/core/gimpselection.h | 76 + app/core/gimpsettings.c | 195 + app/core/gimpsettings.h | 57 + app/core/gimpstrokeoptions.c | 638 + app/core/gimpstrokeoptions.h | 81 + app/core/gimpsubprogress.c | 317 + app/core/gimpsubprogress.h | 59 + app/core/gimpsymmetry-mandala.c | 574 + app/core/gimpsymmetry-mandala.h | 61 + app/core/gimpsymmetry-mirror.c | 738 + app/core/gimpsymmetry-mirror.h | 62 + app/core/gimpsymmetry-tiling.c | 442 + app/core/gimpsymmetry-tiling.h | 58 + app/core/gimpsymmetry.c | 591 + app/core/gimpsymmetry.h | 106 + app/core/gimptag.c | 442 + app/core/gimptag.h | 80 + app/core/gimptagcache.c | 646 + app/core/gimptagcache.h | 63 + app/core/gimptagged.c | 260 + app/core/gimptagged.h | 72 + app/core/gimptaggedcontainer.c | 483 + app/core/gimptaggedcontainer.h | 67 + app/core/gimptempbuf.c | 450 + app/core/gimptempbuf.h | 67 + app/core/gimptemplate.c | 595 + app/core/gimptemplate.h | 97 + app/core/gimptilehandlerprojectable.c | 91 + app/core/gimptilehandlerprojectable.h | 64 + app/core/gimptoolgroup.c | 413 + app/core/gimptoolgroup.h | 68 + app/core/gimptoolinfo.c | 263 + app/core/gimptoolinfo.h | 95 + app/core/gimptoolitem.c | 227 + app/core/gimptoolitem.h | 70 + app/core/gimptooloptions.c | 378 + app/core/gimptooloptions.h | 73 + app/core/gimptoolpreset-load.c | 71 + app/core/gimptoolpreset-load.h | 31 + app/core/gimptoolpreset-save.c | 44 + app/core/gimptoolpreset-save.h | 28 + app/core/gimptoolpreset.c | 705 + app/core/gimptoolpreset.h | 67 + app/core/gimptreehandler.c | 238 + app/core/gimptreehandler.h | 64 + app/core/gimptreeproxy.c | 634 + app/core/gimptreeproxy.h | 66 + app/core/gimptriviallycancelablewaitable.c | 92 + app/core/gimptriviallycancelablewaitable.h | 57 + app/core/gimpuncancelablewaitable.c | 137 + app/core/gimpuncancelablewaitable.h | 54 + app/core/gimpundo.c | 585 + app/core/gimpundo.h | 99 + app/core/gimpundostack.c | 208 + app/core/gimpundostack.h | 64 + app/core/gimpunit.c | 305 + app/core/gimpunit.h | 61 + app/core/gimpviewable.c | 1430 ++ app/core/gimpviewable.h | 201 + app/core/gimpwaitable.c | 118 + app/core/gimpwaitable.h | 55 + app/dialogs/Makefile.am | 120 + app/dialogs/Makefile.in | 1197 ++ app/dialogs/about-dialog.c | 879 ++ app/dialogs/about-dialog.h | 25 + app/dialogs/action-search-dialog.c | 358 + app/dialogs/action-search-dialog.h | 29 + app/dialogs/authors.h | 189 + app/dialogs/authors.xsl | 83 + app/dialogs/channel-options-dialog.c | 247 + app/dialogs/channel-options-dialog.h | 60 + app/dialogs/color-profile-dialog.c | 494 + app/dialogs/color-profile-dialog.h | 56 + app/dialogs/color-profile-import-dialog.c | 216 + app/dialogs/color-profile-import-dialog.h | 35 + app/dialogs/convert-indexed-dialog.c | 436 + app/dialogs/convert-indexed-dialog.h | 48 + app/dialogs/convert-precision-dialog.c | 342 + app/dialogs/convert-precision-dialog.h | 50 + app/dialogs/data-delete-dialog.c | 159 + app/dialogs/data-delete-dialog.h | 28 + app/dialogs/dialogs-constructors.c | 894 ++ app/dialogs/dialogs-constructors.h | 308 + app/dialogs/dialogs-types.h | 37 + app/dialogs/dialogs.c | 749 + app/dialogs/dialogs.h | 50 + app/dialogs/file-open-dialog.c | 276 + app/dialogs/file-open-dialog.h | 25 + app/dialogs/file-open-location-dialog.c | 289 + app/dialogs/file-open-location-dialog.h | 25 + app/dialogs/file-save-dialog.c | 816 + app/dialogs/file-save-dialog.h | 42 + app/dialogs/fill-dialog.c | 183 + app/dialogs/fill-dialog.h | 45 + app/dialogs/grid-dialog.c | 173 + app/dialogs/grid-dialog.h | 29 + app/dialogs/image-merge-layers-dialog.c | 192 + app/dialogs/image-merge-layers-dialog.h | 42 + app/dialogs/image-new-dialog.c | 380 + app/dialogs/image-new-dialog.h | 29 + app/dialogs/image-properties-dialog.c | 100 + app/dialogs/image-properties-dialog.h | 30 + app/dialogs/image-scale-dialog.c | 291 + app/dialogs/image-scale-dialog.h | 31 + app/dialogs/input-devices-dialog.c | 102 + app/dialogs/input-devices-dialog.h | 25 + app/dialogs/item-options-dialog.c | 491 + app/dialogs/item-options-dialog.h | 74 + app/dialogs/keyboard-shortcuts-dialog.c | 122 + app/dialogs/keyboard-shortcuts-dialog.h | 25 + app/dialogs/layer-add-mask-dialog.c | 229 + app/dialogs/layer-add-mask-dialog.h | 39 + app/dialogs/layer-options-dialog.c | 592 + app/dialogs/layer-options-dialog.h | 73 + app/dialogs/lebl-dialog.c | 869 ++ app/dialogs/lebl-dialog.h | 15397 +++++++++++++++++++ app/dialogs/module-dialog.c | 526 + app/dialogs/module-dialog.h | 27 + app/dialogs/palette-import-dialog.c | 886 ++ app/dialogs/palette-import-dialog.h | 25 + app/dialogs/preferences-dialog-utils.c | 402 + app/dialogs/preferences-dialog-utils.h | 134 + app/dialogs/preferences-dialog.c | 3409 ++++ app/dialogs/preferences-dialog.h | 25 + app/dialogs/print-size-dialog.c | 454 + app/dialogs/print-size-dialog.h | 41 + app/dialogs/quit-dialog.c | 614 + app/dialogs/quit-dialog.h | 28 + app/dialogs/resize-dialog.c | 853 + app/dialogs/resize-dialog.h | 54 + app/dialogs/resolution-calibrate-dialog.c | 204 + app/dialogs/resolution-calibrate-dialog.h | 26 + app/dialogs/scale-dialog.c | 311 + app/dialogs/scale-dialog.h | 35 + app/dialogs/stroke-dialog.c | 303 + app/dialogs/stroke-dialog.h | 44 + app/dialogs/template-options-dialog.c | 180 + app/dialogs/template-options-dialog.h | 41 + app/dialogs/tips-dialog.c | 289 + app/dialogs/tips-dialog.h | 25 + app/dialogs/tips-parser.c | 477 + app/dialogs/tips-parser.h | 44 + app/dialogs/user-install-dialog.c | 153 + app/dialogs/user-install-dialog.h | 25 + app/dialogs/vectors-export-dialog.c | 179 + app/dialogs/vectors-export-dialog.h | 38 + app/dialogs/vectors-import-dialog.c | 209 + app/dialogs/vectors-import-dialog.h | 40 + app/dialogs/vectors-options-dialog.c | 160 + app/dialogs/vectors-options-dialog.h | 54 + app/display/Makefile.am | 247 + app/display/Makefile.in | 1546 ++ app/display/display-enums.c | 592 + app/display/display-enums.h | 275 + app/display/display-types.h | 53 + app/display/gimpcanvas-style.c | 458 + app/display/gimpcanvas-style.h | 81 + app/display/gimpcanvas.c | 298 + app/display/gimpcanvas.h | 74 + app/display/gimpcanvasarc.c | 369 + app/display/gimpcanvasarc.h | 69 + app/display/gimpcanvasboundary.c | 384 + app/display/gimpcanvasboundary.h | 60 + app/display/gimpcanvasbufferpreview.c | 263 + app/display/gimpcanvasbufferpreview.h | 56 + app/display/gimpcanvascanvasboundary.c | 270 + app/display/gimpcanvascanvasboundary.h | 58 + app/display/gimpcanvascorner.c | 468 + app/display/gimpcanvascorner.h | 72 + app/display/gimpcanvascursor.c | 228 + app/display/gimpcanvascursor.h | 59 + app/display/gimpcanvasgrid.c | 414 + app/display/gimpcanvasgrid.h | 56 + app/display/gimpcanvasgroup.c | 387 + app/display/gimpcanvasgroup.h | 68 + app/display/gimpcanvasguide.c | 295 + app/display/gimpcanvasguide.h | 62 + app/display/gimpcanvashandle.c | 703 + app/display/gimpcanvashandle.h | 92 + app/display/gimpcanvasitem-utils.c | 474 + app/display/gimpcanvasitem-utils.h | 81 + app/display/gimpcanvasitem.c | 731 + app/display/gimpcanvasitem.h | 147 + app/display/gimpcanvaslayerboundary.c | 322 + app/display/gimpcanvaslayerboundary.h | 58 + app/display/gimpcanvaslimit.c | 760 + app/display/gimpcanvaslimit.h | 84 + app/display/gimpcanvasline.c | 281 + app/display/gimpcanvasline.h | 65 + app/display/gimpcanvaspassepartout.c | 235 + app/display/gimpcanvaspassepartout.h | 59 + app/display/gimpcanvaspath.c | 352 + app/display/gimpcanvaspath.h | 63 + app/display/gimpcanvaspen.c | 231 + app/display/gimpcanvaspen.h | 60 + app/display/gimpcanvaspolygon.c | 505 + app/display/gimpcanvaspolygon.h | 68 + app/display/gimpcanvasprogress.c | 459 + app/display/gimpcanvasprogress.h | 58 + app/display/gimpcanvasproxygroup.c | 197 + app/display/gimpcanvasproxygroup.h | 63 + app/display/gimpcanvasrectangle.c | 360 + app/display/gimpcanvasrectangle.h | 66 + app/display/gimpcanvasrectangleguides.c | 422 + app/display/gimpcanvasrectangleguides.h | 69 + app/display/gimpcanvassamplepoint.c | 356 + app/display/gimpcanvassamplepoint.h | 63 + app/display/gimpcanvastextcursor.c | 386 + app/display/gimpcanvastextcursor.h | 58 + app/display/gimpcanvastransformguides.c | 683 + app/display/gimpcanvastransformguides.h | 73 + app/display/gimpcanvastransformpreview.c | 791 + app/display/gimpcanvastransformpreview.h | 61 + app/display/gimpcursorview.c | 887 ++ app/display/gimpcursorview.h | 68 + app/display/gimpdisplay-foreach.c | 308 + app/display/gimpdisplay-foreach.h | 36 + app/display/gimpdisplay-handlers.c | 128 + app/display/gimpdisplay-handlers.h | 26 + app/display/gimpdisplay.c | 985 ++ app/display/gimpdisplay.h | 99 + app/display/gimpdisplayshell-actions.c | 149 + app/display/gimpdisplayshell-actions.h | 33 + app/display/gimpdisplayshell-appearance.c | 606 + app/display/gimpdisplayshell-appearance.h | 92 + app/display/gimpdisplayshell-autoscroll.c | 184 + app/display/gimpdisplayshell-autoscroll.h | 28 + app/display/gimpdisplayshell-callbacks.c | 652 + app/display/gimpdisplayshell-callbacks.h | 48 + app/display/gimpdisplayshell-close.c | 447 + app/display/gimpdisplayshell-close.h | 26 + app/display/gimpdisplayshell-cursor.c | 295 + app/display/gimpdisplayshell-cursor.h | 47 + app/display/gimpdisplayshell-dnd.c | 770 + app/display/gimpdisplayshell-dnd.h | 25 + app/display/gimpdisplayshell-draw.c | 256 + app/display/gimpdisplayshell-draw.h | 41 + app/display/gimpdisplayshell-expose.c | 76 + app/display/gimpdisplayshell-expose.h | 32 + app/display/gimpdisplayshell-filter-dialog.c | 151 + app/display/gimpdisplayshell-filter-dialog.h | 25 + app/display/gimpdisplayshell-filter.c | 116 + app/display/gimpdisplayshell-filter.h | 28 + app/display/gimpdisplayshell-grab.c | 124 + app/display/gimpdisplayshell-grab.h | 36 + app/display/gimpdisplayshell-handlers.c | 1239 ++ app/display/gimpdisplayshell-handlers.h | 26 + app/display/gimpdisplayshell-icon.c | 140 + app/display/gimpdisplayshell-icon.h | 26 + app/display/gimpdisplayshell-items.c | 284 + app/display/gimpdisplayshell-items.h | 49 + app/display/gimpdisplayshell-layer-select.c | 305 + app/display/gimpdisplayshell-layer-select.h | 27 + app/display/gimpdisplayshell-profile.c | 336 + app/display/gimpdisplayshell-profile.h | 30 + app/display/gimpdisplayshell-progress.c | 163 + app/display/gimpdisplayshell-progress.h | 28 + app/display/gimpdisplayshell-render.c | 360 + app/display/gimpdisplayshell-render.h | 29 + app/display/gimpdisplayshell-rotate-dialog.c | 293 + app/display/gimpdisplayshell-rotate-dialog.h | 25 + app/display/gimpdisplayshell-rotate.c | 251 + app/display/gimpdisplayshell-rotate.h | 40 + app/display/gimpdisplayshell-rulers.c | 181 + app/display/gimpdisplayshell-rulers.h | 25 + app/display/gimpdisplayshell-scale-dialog.c | 293 + app/display/gimpdisplayshell-scale-dialog.h | 25 + app/display/gimpdisplayshell-scale.c | 1449 ++ app/display/gimpdisplayshell-scale.h | 108 + app/display/gimpdisplayshell-scroll.c | 529 + app/display/gimpdisplayshell-scroll.h | 59 + app/display/gimpdisplayshell-scrollbars.c | 244 + app/display/gimpdisplayshell-scrollbars.h | 36 + app/display/gimpdisplayshell-selection.c | 504 + app/display/gimpdisplayshell-selection.h | 37 + app/display/gimpdisplayshell-title.c | 564 + app/display/gimpdisplayshell-title.h | 25 + app/display/gimpdisplayshell-tool-events.c | 2144 +++ app/display/gimpdisplayshell-tool-events.h | 52 + app/display/gimpdisplayshell-transform.c | 1025 ++ app/display/gimpdisplayshell-transform.h | 200 + app/display/gimpdisplayshell-utils.c | 220 + app/display/gimpdisplayshell-utils.h | 45 + app/display/gimpdisplayshell.c | 2141 +++ app/display/gimpdisplayshell.h | 341 + app/display/gimpdisplayxfer.c | 289 + app/display/gimpdisplayxfer.h | 37 + app/display/gimpimagewindow.c | 2428 +++ app/display/gimpimagewindow.h | 100 + app/display/gimpmotionbuffer.c | 594 + app/display/gimpmotionbuffer.h | 99 + app/display/gimpmultiwindowstrategy.c | 89 + app/display/gimpmultiwindowstrategy.h | 54 + app/display/gimpnavigationeditor.c | 890 ++ app/display/gimpnavigationeditor.h | 79 + app/display/gimpscalecombobox.c | 562 + app/display/gimpscalecombobox.h | 60 + app/display/gimpsinglewindowstrategy.c | 157 + app/display/gimpsinglewindowstrategy.h | 54 + app/display/gimpstatusbar.c | 1750 +++ app/display/gimpstatusbar.h | 158 + app/display/gimptoolcompass.c | 1218 ++ app/display/gimptoolcompass.h | 72 + app/display/gimptooldialog.c | 208 + app/display/gimptooldialog.h | 58 + app/display/gimptoolfocus.c | 1209 ++ app/display/gimptoolfocus.h | 58 + app/display/gimptoolgui.c | 1037 ++ app/display/gimptoolgui.h | 115 + app/display/gimptoolgyroscope.c | 879 ++ app/display/gimptoolgyroscope.h | 58 + app/display/gimptoolhandlegrid.c | 1217 ++ app/display/gimptoolhandlegrid.h | 62 + app/display/gimptoolline.c | 1796 +++ app/display/gimptoolline.h | 99 + app/display/gimptoolpath.c | 1904 +++ app/display/gimptoolpath.h | 68 + app/display/gimptoolpolygon.c | 1495 ++ app/display/gimptoolpolygon.h | 66 + app/display/gimptoolrectangle.c | 4292 ++++++ app/display/gimptoolrectangle.h | 124 + app/display/gimptoolrotategrid.c | 330 + app/display/gimptoolrotategrid.h | 65 + app/display/gimptoolsheargrid.c | 360 + app/display/gimptoolsheargrid.h | 65 + app/display/gimptooltransform3dgrid.c | 1162 ++ app/display/gimptooltransform3dgrid.h | 65 + app/display/gimptooltransformgrid.c | 2494 +++ app/display/gimptooltransformgrid.h | 99 + app/display/gimptoolwidget.c | 1117 ++ app/display/gimptoolwidget.h | 318 + app/display/gimptoolwidgetgroup.c | 731 + app/display/gimptoolwidgetgroup.h | 65 + app/errors.c | 475 + app/errors.h | 39 + app/file-data/Makefile.am | 24 + app/file-data/Makefile.in | 934 ++ app/file-data/file-data-gbr.c | 417 + app/file-data/file-data-gbr.h | 44 + app/file-data/file-data-gih.c | 364 + app/file-data/file-data-gih.h | 37 + app/file-data/file-data-pat.c | 263 + app/file-data/file-data-pat.h | 37 + app/file-data/file-data.c | 503 + app/file-data/file-data.h | 26 + app/file/Makefile.am | 26 + app/file/Makefile.in | 938 ++ app/file/file-import.c | 109 + app/file/file-import.h | 31 + app/file/file-open.c | 833 + app/file/file-open.h | 87 + app/file/file-remote.c | 401 + app/file/file-remote.h | 48 + app/file/file-save.c | 325 + app/file/file-save.h | 36 + app/file/file-utils.c | 249 + app/file/file-utils.h | 33 + app/file/gimp-file.h | 30 + app/gegl/Makefile.am | 99 + app/gegl/Makefile.in | 1116 ++ app/gegl/gimp-babl-compat.c | 93 + app/gegl/gimp-babl-compat.h | 31 + app/gegl/gimp-babl.c | 1415 ++ app/gegl/gimp-babl.h | 62 + app/gegl/gimp-gegl-apply-operation.c | 827 + app/gegl/gimp-gegl-apply-operation.h | 172 + app/gegl/gimp-gegl-enums.c | 43 + app/gegl/gimp-gegl-enums.h | 35 + app/gegl/gimp-gegl-loops-sse2.c | 127 + app/gegl/gimp-gegl-loops-sse2.h | 40 + app/gegl/gimp-gegl-loops.cc | 1089 ++ app/gegl/gimp-gegl-loops.h | 109 + app/gegl/gimp-gegl-mask-combine.cc | 653 + app/gegl/gimp-gegl-mask-combine.h | 51 + app/gegl/gimp-gegl-mask.c | 253 + app/gegl/gimp-gegl-mask.h | 30 + app/gegl/gimp-gegl-nodes.c | 260 + app/gegl/gimp-gegl-nodes.h | 52 + app/gegl/gimp-gegl-tile-compat.c | 79 + app/gegl/gimp-gegl-tile-compat.h | 36 + app/gegl/gimp-gegl-types.h | 34 + app/gegl/gimp-gegl-utils.c | 348 + app/gegl/gimp-gegl-utils.h | 60 + app/gegl/gimp-gegl.c | 171 + app/gegl/gimp-gegl.h | 29 + app/gegl/gimpapplicator.c | 652 + app/gegl/gimpapplicator.h | 146 + app/gegl/gimptilehandlervalidate.c | 766 + app/gegl/gimptilehandlervalidate.h | 112 + app/gimp-debug.c | 122 + app/gimp-debug.h | 34 + app/gimp-intl.h | 27 + app/gimp-log.c | 219 + app/gimp-log.h | 138 + app/gimp-priorities.h | 44 + app/gimp-update.c | 593 + app/gimp-update.h | 30 + app/gimp-version.c | 311 + app/gimp-version.h | 31 + app/gui/Makefile.am | 80 + app/gui/Makefile.in | 1018 ++ app/gui/dbus-service.xml | 31 + app/gui/gimpdbusservice-generated.c | 1721 +++ app/gui/gimpdbusservice-generated.h | 282 + app/gui/gimpdbusservice.c | 457 + app/gui/gimpdbusservice.h | 69 + app/gui/gimpuiconfigurer.c | 622 + app/gui/gimpuiconfigurer.h | 57 + app/gui/gui-message.c | 501 + app/gui/gui-message.h | 29 + app/gui/gui-types.h | 27 + app/gui/gui-unique.c | 442 + app/gui/gui-unique.h | 31 + app/gui/gui-vtable.c | 934 ++ app/gui/gui-vtable.h | 31 + app/gui/gui.c | 1077 ++ app/gui/gui.h | 30 + app/gui/icon-themes.c | 250 + app/gui/icon-themes.h | 34 + app/gui/session.c | 486 + app/gui/session.h | 35 + app/gui/splash.c | 736 + app/gui/splash.h | 32 + app/gui/themes.c | 535 + app/gui/themes.h | 34 + app/language.c | 739 + app/language.h | 29 + app/main.c | 953 ++ app/menus/Makefile.am | 34 + app/menus/Makefile.in | 963 ++ app/menus/dockable-menu.c | 34 + app/menus/dockable-menu.h | 26 + app/menus/file-menu.c | 120 + app/menus/file-menu.h | 26 + app/menus/filters-menu.c | 64 + app/menus/filters-menu.h | 26 + app/menus/image-menu.c | 52 + app/menus/image-menu.h | 26 + app/menus/menus-types.h | 25 + app/menus/menus.c | 519 + app/menus/menus.h | 38 + app/menus/plug-in-menus.c | 574 + app/menus/plug-in-menus.h | 26 + app/menus/tool-options-menu.c | 161 + app/menus/tool-options-menu.h | 26 + app/menus/window-menu.c | 168 + app/menus/window-menu.h | 27 + app/menus/windows-menu.c | 440 + app/menus/windows-menu.h | 26 + app/operations/Makefile.am | 140 + app/operations/Makefile.in | 1376 ++ app/operations/gimp-operation-config.c | 827 + app/operations/gimp-operation-config.h | 53 + app/operations/gimp-operations.c | 225 + app/operations/gimp-operations.h | 27 + app/operations/gimpbrightnesscontrastconfig.c | 248 + app/operations/gimpbrightnesscontrastconfig.h | 58 + app/operations/gimpcageconfig.c | 832 + app/operations/gimpcageconfig.h | 108 + app/operations/gimpcolorbalanceconfig.c | 382 + app/operations/gimpcolorbalanceconfig.h | 62 + app/operations/gimpcurvesconfig.c | 695 + app/operations/gimpcurvesconfig.h | 81 + app/operations/gimphuesaturationconfig.c | 367 + app/operations/gimphuesaturationconfig.h | 62 + app/operations/gimplevelsconfig.c | 964 ++ app/operations/gimplevelsconfig.h | 92 + app/operations/gimpoperationborder.c | 748 + app/operations/gimpoperationborder.h | 58 + app/operations/gimpoperationbrightnesscontrast.c | 140 + app/operations/gimpoperationbrightnesscontrast.h | 53 + app/operations/gimpoperationbuffersourcevalidate.c | 307 + app/operations/gimpoperationbuffersourcevalidate.h | 52 + app/operations/gimpoperationcagecoefcalc.c | 294 + app/operations/gimpoperationcagecoefcalc.h | 62 + app/operations/gimpoperationcagetransform.c | 603 + app/operations/gimpoperationcagetransform.h | 58 + app/operations/gimpoperationcolorbalance.c | 198 + app/operations/gimpoperationcolorbalance.h | 53 + app/operations/gimpoperationcolorize.c | 274 + app/operations/gimpoperationcolorize.h | 57 + app/operations/gimpoperationcomposecrop.c | 329 + app/operations/gimpoperationcomposecrop.h | 55 + app/operations/gimpoperationcurves.c | 118 + app/operations/gimpoperationcurves.h | 53 + app/operations/gimpoperationdesaturate.c | 257 + app/operations/gimpoperationdesaturate.h | 55 + app/operations/gimpoperationequalize.c | 248 + app/operations/gimpoperationequalize.h | 57 + app/operations/gimpoperationfillsource.c | 254 + app/operations/gimpoperationfillsource.h | 55 + app/operations/gimpoperationflood.c | 1104 ++ app/operations/gimpoperationflood.h | 53 + app/operations/gimpoperationgradient.c | 1283 ++ app/operations/gimpoperationgradient.h | 73 + app/operations/gimpoperationgrow.c | 391 + app/operations/gimpoperationgrow.h | 56 + app/operations/gimpoperationhistogramsink.c | 244 + app/operations/gimpoperationhistogramsink.h | 56 + app/operations/gimpoperationhuesaturation.c | 302 + app/operations/gimpoperationhuesaturation.h | 58 + app/operations/gimpoperationlevels.c | 208 + app/operations/gimpoperationlevels.h | 57 + app/operations/gimpoperationmaskcomponents.cc | 586 + app/operations/gimpoperationmaskcomponents.h | 68 + app/operations/gimpoperationoffset.c | 497 + app/operations/gimpoperationoffset.h | 55 + app/operations/gimpoperationpointfilter.c | 131 + app/operations/gimpoperationpointfilter.h | 73 + app/operations/gimpoperationposterize.c | 165 + app/operations/gimpoperationposterize.h | 55 + app/operations/gimpoperationprofiletransform.c | 307 + app/operations/gimpoperationprofiletransform.h | 65 + app/operations/gimpoperationscalarmultiply.c | 189 + app/operations/gimpoperationscalarmultiply.h | 56 + app/operations/gimpoperationsemiflatten.c | 191 + app/operations/gimpoperationsemiflatten.h | 55 + app/operations/gimpoperationsetalpha.c | 188 + app/operations/gimpoperationsetalpha.h | 55 + app/operations/gimpoperationsettings.c | 320 + app/operations/gimpoperationsettings.h | 75 + app/operations/gimpoperationshrink.c | 443 + app/operations/gimpoperationshrink.h | 57 + app/operations/gimpoperationthreshold.c | 232 + app/operations/gimpoperationthreshold.h | 57 + app/operations/gimpoperationthresholdalpha.c | 178 + app/operations/gimpoperationthresholdalpha.h | 56 + app/operations/layer-modes-legacy/Makefile.am | 54 + app/operations/layer-modes-legacy/Makefile.in | 1038 ++ .../gimpoperationadditionlegacy.c | 126 + .../gimpoperationadditionlegacy.h | 53 + .../layer-modes-legacy/gimpoperationburnlegacy.c | 128 + .../layer-modes-legacy/gimpoperationburnlegacy.h | 53 + .../gimpoperationdarkenonlylegacy.c | 124 + .../gimpoperationdarkenonlylegacy.h | 53 + .../gimpoperationdifferencelegacy.c | 126 + .../gimpoperationdifferencelegacy.h | 53 + .../layer-modes-legacy/gimpoperationdividelegacy.c | 127 + .../layer-modes-legacy/gimpoperationdividelegacy.h | 53 + .../layer-modes-legacy/gimpoperationdodgelegacy.c | 127 + .../layer-modes-legacy/gimpoperationdodgelegacy.h | 53 + .../gimpoperationgrainextractlegacy.c | 125 + .../gimpoperationgrainextractlegacy.h | 53 + .../gimpoperationgrainmergelegacy.c | 125 + .../gimpoperationgrainmergelegacy.h | 53 + .../gimpoperationhardlightlegacy.c | 135 + .../gimpoperationhardlightlegacy.h | 53 + .../gimpoperationhslcolorlegacy.c | 141 + .../gimpoperationhslcolorlegacy.h | 53 + .../layer-modes-legacy/gimpoperationhsvhuelegacy.c | 146 + .../layer-modes-legacy/gimpoperationhsvhuelegacy.h | 53 + .../gimpoperationhsvsaturationlegacy.c | 140 + .../gimpoperationhsvsaturationlegacy.h | 53 + .../gimpoperationhsvvaluelegacy.c | 140 + .../gimpoperationhsvvaluelegacy.h | 53 + .../gimpoperationlightenonlylegacy.c | 124 + .../gimpoperationlightenonlylegacy.h | 53 + .../gimpoperationmultiplylegacy.c | 124 + .../gimpoperationmultiplylegacy.h | 53 + .../layer-modes-legacy/gimpoperationscreenlegacy.c | 124 + .../layer-modes-legacy/gimpoperationscreenlegacy.h | 53 + .../gimpoperationsoftlightlegacy.c | 157 + .../gimpoperationsoftlightlegacy.h | 53 + .../gimpoperationsubtractlegacy.c | 125 + .../gimpoperationsubtractlegacy.h | 53 + app/operations/layer-modes/Makefile.am | 79 + app/operations/layer-modes/Makefile.in | 1115 ++ app/operations/layer-modes/gimp-layer-modes.c | 1522 ++ app/operations/layer-modes/gimp-layer-modes.h | 73 + .../layer-modes/gimpoperationantierase.c | 188 + .../layer-modes/gimpoperationantierase.h | 53 + app/operations/layer-modes/gimpoperationbehind.c | 236 + app/operations/layer-modes/gimpoperationbehind.h | 53 + app/operations/layer-modes/gimpoperationdissolve.c | 175 + app/operations/layer-modes/gimpoperationdissolve.h | 53 + app/operations/layer-modes/gimpoperationerase.c | 214 + app/operations/layer-modes/gimpoperationerase.h | 53 + .../layer-modes/gimpoperationlayermode-blend.c | 1215 ++ .../layer-modes/gimpoperationlayermode-blend.h | 200 + .../gimpoperationlayermode-composite-sse2.c | 105 + .../layer-modes/gimpoperationlayermode-composite.c | 434 + .../layer-modes/gimpoperationlayermode-composite.h | 98 + .../layer-modes/gimpoperationlayermode.c | 914 ++ .../layer-modes/gimpoperationlayermode.h | 89 + app/operations/layer-modes/gimpoperationmerge.c | 243 + app/operations/layer-modes/gimpoperationmerge.h | 54 + .../layer-modes/gimpoperationnormal-sse2.c | 264 + .../layer-modes/gimpoperationnormal-sse4.c | 260 + app/operations/layer-modes/gimpoperationnormal.c | 266 + app/operations/layer-modes/gimpoperationnormal.h | 91 + .../layer-modes/gimpoperationpassthrough.c | 55 + .../layer-modes/gimpoperationpassthrough.h | 54 + app/operations/layer-modes/gimpoperationreplace.c | 347 + app/operations/layer-modes/gimpoperationreplace.h | 53 + app/operations/layer-modes/gimpoperationsplit.c | 213 + app/operations/layer-modes/gimpoperationsplit.h | 54 + app/operations/operations-enums.c | 370 + app/operations/operations-enums.h | 190 + app/operations/operations-types.h | 76 + app/operations/tests/Makefile.am | 71 + app/operations/tests/Makefile.in | 815 + app/paint/Makefile.am | 125 + app/paint/Makefile.in | 1208 ++ app/paint/gimp-paint.c | 140 + app/paint/gimp-paint.h | 26 + app/paint/gimpairbrush.c | 266 + app/paint/gimpairbrush.h | 64 + app/paint/gimpairbrushoptions.c | 155 + app/paint/gimpairbrushoptions.h | 53 + app/paint/gimpbrushcore-kernels.h | 116 + app/paint/gimpbrushcore-loops.cc | 647 + app/paint/gimpbrushcore-loops.h | 37 + app/paint/gimpbrushcore.c | 1344 ++ app/paint/gimpbrushcore.h | 152 + app/paint/gimpclone.c | 256 + app/paint/gimpclone.h | 52 + app/paint/gimpcloneoptions.c | 114 + app/paint/gimpcloneoptions.h | 51 + app/paint/gimpconvolve.c | 277 + app/paint/gimpconvolve.h | 54 + app/paint/gimpconvolveoptions.c | 129 + app/paint/gimpconvolveoptions.h | 52 + app/paint/gimpdodgeburn.c | 201 + app/paint/gimpdodgeburn.h | 51 + app/paint/gimpdodgeburnoptions.c | 145 + app/paint/gimpdodgeburnoptions.h | 53 + app/paint/gimperaser.c | 130 + app/paint/gimperaser.h | 52 + app/paint/gimperaseroptions.c | 113 + app/paint/gimperaseroptions.h | 51 + app/paint/gimpheal.c | 642 + app/paint/gimpheal.h | 52 + app/paint/gimpink-blob.c | 875 ++ app/paint/gimpink-blob.h | 87 + app/paint/gimpink.c | 785 + app/paint/gimpink.h | 58 + app/paint/gimpinkoptions.c | 208 + app/paint/gimpinkoptions.h | 60 + app/paint/gimpinkundo.c | 125 + app/paint/gimpinkundo.h | 52 + app/paint/gimpmybrushcore.c | 421 + app/paint/gimpmybrushcore.h | 55 + app/paint/gimpmybrushoptions.c | 219 + app/paint/gimpmybrushoptions.h | 55 + app/paint/gimpmybrushsurface.c | 560 + app/paint/gimpmybrushsurface.h | 33 + app/paint/gimppaintbrush.c | 395 + app/paint/gimppaintbrush.h | 80 + app/paint/gimppaintcore-loops.cc | 2268 +++ app/paint/gimppaintcore-loops.h | 70 + app/paint/gimppaintcore-stroke.c | 388 + app/paint/gimppaintcore-stroke.h | 48 + app/paint/gimppaintcore.c | 1242 ++ app/paint/gimppaintcore.h | 216 + app/paint/gimppaintcoreundo.c | 172 + app/paint/gimppaintcoreundo.h | 53 + app/paint/gimppaintoptions.c | 1272 ++ app/paint/gimppaintoptions.h | 176 + app/paint/gimppencil.c | 54 + app/paint/gimppencil.h | 52 + app/paint/gimppenciloptions.c | 110 + app/paint/gimppenciloptions.h | 49 + app/paint/gimpperspectiveclone.c | 537 + app/paint/gimpperspectiveclone.h | 74 + app/paint/gimpperspectivecloneoptions.c | 112 + app/paint/gimpperspectivecloneoptions.h | 51 + app/paint/gimpsmudge.c | 575 + app/paint/gimpsmudge.h | 55 + app/paint/gimpsmudgeoptions.c | 159 + app/paint/gimpsmudgeoptions.h | 54 + app/paint/gimpsourcecore.c | 679 + app/paint/gimpsourcecore.h | 110 + app/paint/gimpsourceoptions.c | 124 + app/paint/gimpsourceoptions.h | 52 + app/paint/paint-enums.c | 104 + app/paint/paint-enums.h | 85 + app/paint/paint-types.h | 76 + app/pdb/Makefile.am | 93 + app/pdb/Makefile.in | 1264 ++ app/pdb/README | 7 + app/pdb/brush-cmds.c | 1677 ++ app/pdb/brush-select-cmds.c | 285 + app/pdb/brushes-cmds.c | 470 + app/pdb/buffer-cmds.c | 507 + app/pdb/channel-cmds.c | 736 + app/pdb/color-cmds.c | 1413 ++ app/pdb/context-cmds.c | 5752 +++++++ app/pdb/debug-cmds.c | 140 + app/pdb/display-cmds.c | 363 + app/pdb/drawable-cmds.c | 1976 +++ app/pdb/drawable-color-cmds.c | 1551 ++ app/pdb/drawable-edit-cmds.c | 620 + app/pdb/drawable-transform-cmds.c | 2894 ++++ app/pdb/dynamics-cmds.c | 144 + app/pdb/edit-cmds.c | 1653 ++ app/pdb/fileops-cmds.c | 1184 ++ app/pdb/floating-sel-cmds.c | 366 + app/pdb/font-select-cmds.c | 227 + app/pdb/fonts-cmds.c | 151 + app/pdb/gimp-cmds.c | 444 + app/pdb/gimp-pdb-compat.c | 562 + app/pdb/gimp-pdb-compat.h | 36 + app/pdb/gimppdb-query.c | 649 + app/pdb/gimppdb-query.h | 49 + app/pdb/gimppdb-utils.c | 846 + app/pdb/gimppdb-utils.h | 117 + app/pdb/gimppdb.c | 518 + app/pdb/gimppdb.h | 90 + app/pdb/gimppdbcontext.c | 554 + app/pdb/gimppdbcontext.h | 83 + app/pdb/gimppdberror.c | 36 + app/pdb/gimppdberror.h | 38 + app/pdb/gimpprocedure.c | 897 ++ app/pdb/gimpprocedure.h | 164 + app/pdb/gimprc-cmds.c | 502 + app/pdb/gradient-cmds.c | 2658 ++++ app/pdb/gradient-select-cmds.c | 236 + app/pdb/gradients-cmds.c | 492 + app/pdb/help-cmds.c | 108 + app/pdb/image-cmds.c | 5894 +++++++ app/pdb/image-color-profile-cmds.c | 549 + app/pdb/image-convert-cmds.c | 450 + app/pdb/image-grid-cmds.c | 708 + app/pdb/image-guides-cmds.c | 477 + app/pdb/image-sample-points-cmds.c | 350 + app/pdb/image-select-cmds.c | 718 + app/pdb/image-transform-cmds.c | 522 + app/pdb/image-undo-cmds.c | 477 + app/pdb/internal-procs.c | 95 + app/pdb/internal-procs.h | 85 + app/pdb/item-cmds.c | 1970 +++ app/pdb/item-transform-cmds.c | 1669 ++ app/pdb/layer-cmds.c | 2540 +++ app/pdb/message-cmds.c | 188 + app/pdb/paint-tools-cmds.c | 1645 ++ app/pdb/palette-cmds.c | 1107 ++ app/pdb/palette-select-cmds.c | 223 + app/pdb/palettes-cmds.c | 324 + app/pdb/paths-cmds.c | 1283 ++ app/pdb/pattern-cmds.c | 252 + app/pdb/pattern-select-cmds.c | 223 + app/pdb/patterns-cmds.c | 341 + app/pdb/pdb-types.h | 54 + app/pdb/plug-in-cmds.c | 751 + app/pdb/plug-in-compat-cmds.c | 9622 ++++++++++++ app/pdb/procedural-db-cmds.c | 926 ++ app/pdb/progress-cmds.c | 502 + app/pdb/selection-cmds.c | 1112 ++ app/pdb/selection-tools-cmds.c | 1052 ++ app/pdb/text-layer-cmds.c | 2196 +++ app/pdb/text-tool-cmds.c | 670 + app/pdb/transform-tools-cmds.c | 934 ++ app/pdb/unit-cmds.c | 782 + app/pdb/vectors-cmds.c | 2518 +++ app/plug-in/Makefile.am | 106 + app/plug-in/Makefile.in | 1126 ++ app/plug-in/gimpenvirontable.c | 518 + app/plug-in/gimpenvirontable.h | 72 + app/plug-in/gimpinterpreterdb.c | 875 ++ app/plug-in/gimpinterpreterdb.h | 70 + app/plug-in/gimpplugin-cleanup.c | 578 + app/plug-in/gimpplugin-cleanup.h | 53 + app/plug-in/gimpplugin-context.c | 81 + app/plug-in/gimpplugin-context.h | 28 + app/plug-in/gimpplugin-message.c | 1063 ++ app/plug-in/gimpplugin-message.h | 28 + app/plug-in/gimpplugin-progress.c | 366 + app/plug-in/gimpplugin-progress.h | 47 + app/plug-in/gimpplugin.c | 1060 ++ app/plug-in/gimpplugin.h | 127 + app/plug-in/gimpplugindebug.c | 142 + app/plug-in/gimpplugindebug.h | 43 + app/plug-in/gimpplugindef.c | 235 + app/plug-in/gimpplugindef.h | 82 + app/plug-in/gimppluginerror.c | 36 + app/plug-in/gimppluginerror.h | 35 + app/plug-in/gimppluginmanager-call.c | 375 + app/plug-in/gimppluginmanager-call.h | 59 + app/plug-in/gimppluginmanager-data.c | 133 + app/plug-in/gimppluginmanager-data.h | 35 + app/plug-in/gimppluginmanager-file-procedure.c | 716 + app/plug-in/gimppluginmanager-file-procedure.h | 37 + app/plug-in/gimppluginmanager-file.c | 451 + app/plug-in/gimppluginmanager-file.h | 75 + app/plug-in/gimppluginmanager-help-domain.c | 160 + app/plug-in/gimppluginmanager-help-domain.h | 43 + app/plug-in/gimppluginmanager-locale-domain.c | 180 + app/plug-in/gimppluginmanager-locale-domain.h | 43 + app/plug-in/gimppluginmanager-menu-branch.c | 92 + app/plug-in/gimppluginmanager-menu-branch.h | 42 + app/plug-in/gimppluginmanager-query.c | 162 + app/plug-in/gimppluginmanager-query.h | 34 + app/plug-in/gimppluginmanager-restore.c | 1065 ++ app/plug-in/gimppluginmanager-restore.h | 29 + app/plug-in/gimppluginmanager.c | 426 + app/plug-in/gimppluginmanager.h | 118 + app/plug-in/gimppluginprocedure.c | 1293 ++ app/plug-in/gimppluginprocedure.h | 140 + app/plug-in/gimppluginprocframe.c | 200 + app/plug-in/gimppluginprocframe.h | 67 + app/plug-in/gimppluginshm.c | 301 + app/plug-in/gimppluginshm.h | 31 + app/plug-in/gimptemporaryprocedure.c | 156 + app/plug-in/gimptemporaryprocedure.h | 55 + app/plug-in/plug-in-enums.c | 118 + app/plug-in/plug-in-enums.h | 64 + app/plug-in/plug-in-menu-path.c | 141 + app/plug-in/plug-in-menu-path.h | 28 + app/plug-in/plug-in-params.c | 442 + app/plug-in/plug-in-params.h | 32 + app/plug-in/plug-in-rc.c | 1137 ++ app/plug-in/plug-in-rc.h | 33 + app/plug-in/plug-in-types.h | 40 + app/propgui/Makefile.am | 63 + app/propgui/Makefile.in | 1061 ++ app/propgui/gimppropgui-channel-mixer.c | 141 + app/propgui/gimppropgui-channel-mixer.h | 35 + app/propgui/gimppropgui-color-balance.c | 149 + app/propgui/gimppropgui-color-balance.h | 35 + app/propgui/gimppropgui-color-rotate.c | 260 + app/propgui/gimppropgui-color-rotate.h | 35 + app/propgui/gimppropgui-color-to-alpha.c | 140 + app/propgui/gimppropgui-color-to-alpha.h | 35 + app/propgui/gimppropgui-convolution-matrix.c | 309 + app/propgui/gimppropgui-convolution-matrix.h | 35 + app/propgui/gimppropgui-diffraction-patterns.c | 105 + app/propgui/gimppropgui-diffraction-patterns.h | 35 + app/propgui/gimppropgui-eval.c | 1039 ++ app/propgui/gimppropgui-eval.h | 36 + app/propgui/gimppropgui-focus-blur.c | 246 + app/propgui/gimppropgui-focus-blur.h | 36 + app/propgui/gimppropgui-generic.c | 377 + app/propgui/gimppropgui-generic.h | 36 + app/propgui/gimppropgui-hue-saturation.c | 288 + app/propgui/gimppropgui-hue-saturation.h | 35 + app/propgui/gimppropgui-motion-blur-circular.c | 151 + app/propgui/gimppropgui-motion-blur-circular.h | 35 + app/propgui/gimppropgui-motion-blur-linear.c | 145 + app/propgui/gimppropgui-motion-blur-linear.h | 35 + app/propgui/gimppropgui-motion-blur-zoom.c | 146 + app/propgui/gimppropgui-motion-blur-zoom.h | 35 + app/propgui/gimppropgui-newsprint.c | 444 + app/propgui/gimppropgui-newsprint.h | 35 + app/propgui/gimppropgui-panorama-projection.c | 144 + app/propgui/gimppropgui-panorama-projection.h | 35 + app/propgui/gimppropgui-recursive-transform.c | 334 + app/propgui/gimppropgui-recursive-transform.h | 35 + app/propgui/gimppropgui-shadows-highlights.c | 122 + app/propgui/gimppropgui-shadows-highlights.h | 35 + app/propgui/gimppropgui-spiral.c | 239 + app/propgui/gimppropgui-spiral.h | 35 + app/propgui/gimppropgui-supernova.c | 144 + app/propgui/gimppropgui-supernova.h | 35 + app/propgui/gimppropgui-utils.c | 221 + app/propgui/gimppropgui-utils.h | 32 + app/propgui/gimppropgui-vignette.c | 202 + app/propgui/gimppropgui-vignette.h | 36 + app/propgui/gimppropgui.c | 702 + app/propgui/gimppropgui.h | 60 + app/propgui/propgui-types.h | 145 + app/sanity.c | 738 + app/sanity.h | 30 + app/signals.c | 186 + app/signals.h | 29 + app/tests.c | 250 + app/tests.h | 41 + app/tests/Makefile.am | 169 + app/tests/Makefile.in | 1947 +++ app/tests/files/Makefile.am | 2 + app/tests/files/Makefile.in | 749 + app/tests/files/gimp-2-6-file.xcf | Bin 0 -> 1872 bytes app/tests/gimp-app-test-utils.c | 460 + app/tests/gimp-app-test-utils.h | 42 + app/tests/gimp-test-session-utils.c | 244 + app/tests/gimp-test-session-utils.h | 29 + app/tests/gimpdir-empty/Makefile.am | 7 + app/tests/gimpdir-empty/Makefile.in | 934 ++ app/tests/gimpdir-empty/brushes/Makefile.am | 1 + app/tests/gimpdir-empty/brushes/Makefile.in | 748 + app/tests/gimpdir-empty/gradients/Makefile.am | 1 + app/tests/gimpdir-empty/gradients/Makefile.in | 748 + app/tests/gimpdir-empty/patterns/Makefile.am | 1 + app/tests/gimpdir-empty/patterns/Makefile.in | 748 + app/tests/gimpdir-empty/tags.xml | 24 + app/tests/gimpdir/Makefile.am | 15 + app/tests/gimpdir/Makefile.in | 942 ++ app/tests/gimpdir/brushes/Makefile.am | 1 + app/tests/gimpdir/brushes/Makefile.in | 748 + app/tests/gimpdir/dockrc | 44 + app/tests/gimpdir/dockrc-2-8 | 44 + app/tests/gimpdir/dockrc-expected | 44 + app/tests/gimpdir/gradients/Makefile.am | 1 + app/tests/gimpdir/gradients/Makefile.in | 748 + app/tests/gimpdir/patterns/Makefile.am | 1 + app/tests/gimpdir/patterns/Makefile.in | 748 + app/tests/gimpdir/sessionrc | 117 + app/tests/gimpdir/sessionrc-2-8-multi-window | 105 + app/tests/gimpdir/sessionrc-2-8-single-window | 63 + app/tests/gimpdir/sessionrc-expected-multi-window | 122 + app/tests/gimpdir/sessionrc-expected-single-window | 75 + app/tests/gimpdir/tags.xml | 24 + app/tests/test-core.c | 293 + app/tests/test-gimpidtable.c | 227 + app/tests/test-save-and-export.c | 395 + .../test-session-2-8-compatibility-multi-window.c | 71 + .../test-session-2-8-compatibility-single-window.c | 70 + app/tests/test-single-window-mode.c | 158 + app/tests/test-tools.c | 492 + app/tests/test-ui.c | 980 ++ app/tests/test-xcf.c | 1016 ++ app/text/Makefile.am | 80 + app/text/Makefile.in | 1031 ++ app/text/gimpfont.c | 820 + app/text/gimpfont.h | 45 + app/text/gimpfontfactory.c | 677 + app/text/gimpfontfactory.h | 58 + app/text/gimptext-compat.c | 207 + app/text/gimptext-compat.h | 45 + app/text/gimptext-parasite.c | 204 + app/text/gimptext-parasite.h | 34 + app/text/gimptext-vectors.c | 255 + app/text/gimptext-vectors.h | 29 + app/text/gimptext-xlfd.c | 301 + app/text/gimptext-xlfd.h | 36 + app/text/gimptext.c | 596 + app/text/gimptext.h | 83 + app/text/gimptextlayer-transform.c | 201 + app/text/gimptextlayer-transform.h | 53 + app/text/gimptextlayer-xcf.c | 220 + app/text/gimptextlayer-xcf.h | 34 + app/text/gimptextlayer.c | 861 ++ app/text/gimptextlayer.h | 78 + app/text/gimptextlayout-render.c | 77 + app/text/gimptextlayout-render.h | 31 + app/text/gimptextlayout.c | 805 + app/text/gimptextlayout.h | 79 + app/text/gimptextundo.c | 307 + app/text/gimptextundo.h | 55 + app/text/text-enums.c | 73 + app/text/text-enums.h | 45 + app/text/text-types.h | 38 + app/tools/Makefile.am | 275 + app/tools/Makefile.in | 1657 ++ app/tools/gimp-tool-options-manager.c | 462 + app/tools/gimp-tool-options-manager.h | 29 + app/tools/gimp-tools.c | 831 + app/tools/gimp-tools.h | 45 + app/tools/gimpairbrushtool.c | 150 + app/tools/gimpairbrushtool.h | 53 + app/tools/gimpalignoptions.c | 403 + app/tools/gimpalignoptions.h | 64 + app/tools/gimpaligntool.c | 824 + app/tools/gimpaligntool.h | 76 + app/tools/gimpbrightnesscontrasttool.c | 314 + app/tools/gimpbrightnesscontrasttool.h | 61 + app/tools/gimpbrushtool.c | 451 + app/tools/gimpbrushtool.h | 63 + app/tools/gimpbucketfilloptions.c | 544 + app/tools/gimpbucketfilloptions.h | 68 + app/tools/gimpbucketfilltool.c | 1052 ++ app/tools/gimpbucketfilltool.h | 58 + app/tools/gimpbycolorselecttool.c | 143 + app/tools/gimpbycolorselecttool.h | 55 + app/tools/gimpcageoptions.c | 152 + app/tools/gimpcageoptions.h | 57 + app/tools/gimpcagetool.c | 1301 ++ app/tools/gimpcagetool.h | 85 + app/tools/gimpcloneoptions-gui.c | 108 + app/tools/gimpcloneoptions-gui.h | 25 + app/tools/gimpclonetool.c | 108 + app/tools/gimpclonetool.h | 53 + app/tools/gimpcoloroptions.c | 170 + app/tools/gimpcoloroptions.h | 55 + app/tools/gimpcolorpickeroptions.c | 208 + app/tools/gimpcolorpickeroptions.h | 52 + app/tools/gimpcolorpickertool.c | 455 + app/tools/gimpcolorpickertool.h | 60 + app/tools/gimpcolortool.c | 702 + app/tools/gimpcolortool.h | 87 + app/tools/gimpconvolvetool.c | 230 + app/tools/gimpconvolvetool.h | 57 + app/tools/gimpcropoptions.c | 240 + app/tools/gimpcropoptions.h | 61 + app/tools/gimpcroptool.c | 723 + app/tools/gimpcroptool.h | 62 + app/tools/gimpcurvestool.c | 1156 ++ app/tools/gimpcurvestool.h | 69 + app/tools/gimpdodgeburntool.c | 243 + app/tools/gimpdodgeburntool.h | 56 + app/tools/gimpdrawtool.c | 1281 ++ app/tools/gimpdrawtool.h | 206 + app/tools/gimpeditselectiontool.c | 1287 ++ app/tools/gimpeditselectiontool.h | 50 + app/tools/gimpellipseselecttool.c | 112 + app/tools/gimpellipseselecttool.h | 53 + app/tools/gimperasertool.c | 176 + app/tools/gimperasertool.h | 55 + app/tools/gimpfilteroptions.c | 269 + app/tools/gimpfilteroptions.h | 63 + app/tools/gimpfiltertool-settings.c | 252 + app/tools/gimpfiltertool-settings.h | 37 + app/tools/gimpfiltertool-widgets.c | 964 ++ app/tools/gimpfiltertool-widgets.h | 36 + app/tools/gimpfiltertool.c | 2027 +++ app/tools/gimpfiltertool.h | 149 + app/tools/gimpflipoptions.c | 164 + app/tools/gimpflipoptions.h | 52 + app/tools/gimpfliptool.c | 460 + app/tools/gimpfliptool.h | 57 + app/tools/gimpforegroundselectoptions.c | 395 + app/tools/gimpforegroundselectoptions.h | 64 + app/tools/gimpforegroundselecttool.c | 1396 ++ app/tools/gimpforegroundselecttool.h | 78 + app/tools/gimpforegroundselecttoolundo.c | 168 + app/tools/gimpforegroundselecttoolundo.h | 56 + app/tools/gimpfreeselecttool.c | 355 + app/tools/gimpfreeselecttool.h | 56 + app/tools/gimpfuzzyselecttool.c | 132 + app/tools/gimpfuzzyselecttool.h | 55 + app/tools/gimpgegltool.c | 559 + app/tools/gimpgegltool.h | 57 + app/tools/gimpgenerictransformtool.c | 172 + app/tools/gimpgenerictransformtool.h | 58 + app/tools/gimpgradientoptions.c | 414 + app/tools/gimpgradientoptions.h | 65 + app/tools/gimpgradienttool-editor.c | 2520 +++ app/tools/gimpgradienttool-editor.h | 48 + app/tools/gimpgradienttool.c | 1106 ++ app/tools/gimpgradienttool.h | 111 + app/tools/gimpguidetool.c | 546 + app/tools/gimpguidetool.h | 74 + app/tools/gimphandletransformoptions.c | 202 + app/tools/gimphandletransformoptions.h | 54 + app/tools/gimphandletransformtool.c | 384 + app/tools/gimphandletransformtool.h | 57 + app/tools/gimphealtool.c | 111 + app/tools/gimphealtool.h | 53 + app/tools/gimphistogramoptions.c | 113 + app/tools/gimphistogramoptions.h | 52 + app/tools/gimpinkoptions-gui.c | 143 + app/tools/gimpinkoptions-gui.h | 25 + app/tools/gimpinktool.c | 122 + app/tools/gimpinktool.h | 55 + app/tools/gimpiscissorsoptions.c | 133 + app/tools/gimpiscissorsoptions.h | 49 + app/tools/gimpiscissorstool.c | 2188 +++ app/tools/gimpiscissorstool.h | 97 + app/tools/gimplevelstool.c | 1055 ++ app/tools/gimplevelstool.h | 76 + app/tools/gimpmagnifyoptions.c | 202 + app/tools/gimpmagnifyoptions.h | 50 + app/tools/gimpmagnifytool.c | 293 + app/tools/gimpmagnifytool.h | 60 + app/tools/gimpmeasureoptions.c | 183 + app/tools/gimpmeasureoptions.h | 58 + app/tools/gimpmeasuretool.c | 890 ++ app/tools/gimpmeasuretool.h | 71 + app/tools/gimpmoveoptions.c | 227 + app/tools/gimpmoveoptions.h | 53 + app/tools/gimpmovetool.c | 665 + app/tools/gimpmovetool.h | 63 + app/tools/gimpmybrushoptions-gui.c | 88 + app/tools/gimpmybrushoptions-gui.h | 25 + app/tools/gimpmybrushtool.c | 169 + app/tools/gimpmybrushtool.h | 60 + app/tools/gimpnpointdeformationoptions.c | 264 + app/tools/gimpnpointdeformationoptions.h | 67 + app/tools/gimpnpointdeformationtool.c | 1020 ++ app/tools/gimpnpointdeformationtool.h | 95 + app/tools/gimpoffsettool.c | 787 + app/tools/gimpoffsettool.h | 63 + app/tools/gimpoperationtool.c | 914 ++ app/tools/gimpoperationtool.h | 71 + app/tools/gimppaintbrushtool.c | 94 + app/tools/gimppaintbrushtool.h | 53 + app/tools/gimppaintoptions-gui.c | 582 + app/tools/gimppaintoptions-gui.h | 27 + app/tools/gimppainttool-paint.c | 540 + app/tools/gimppainttool-paint.h | 48 + app/tools/gimppainttool.c | 991 ++ app/tools/gimppainttool.h | 109 + app/tools/gimppenciltool.c | 70 + app/tools/gimppenciltool.h | 53 + app/tools/gimpperspectiveclonetool.c | 913 ++ app/tools/gimpperspectiveclonetool.h | 72 + app/tools/gimpperspectivetool.c | 292 + app/tools/gimpperspectivetool.h | 53 + app/tools/gimppolygonselecttool.c | 555 + app/tools/gimppolygonselecttool.h | 69 + app/tools/gimprectangleoptions.c | 1266 ++ app/tools/gimprectangleoptions.h | 181 + app/tools/gimprectangleselectoptions.c | 203 + app/tools/gimprectangleselectoptions.h | 50 + app/tools/gimprectangleselecttool.c | 918 ++ app/tools/gimprectangleselecttool.h | 67 + app/tools/gimpregionselectoptions.c | 290 + app/tools/gimpregionselectoptions.h | 54 + app/tools/gimpregionselecttool.c | 386 + app/tools/gimpregionselecttool.h | 66 + app/tools/gimprotatetool.c | 537 + app/tools/gimprotatetool.h | 58 + app/tools/gimpsamplepointtool.c | 373 + app/tools/gimpsamplepointtool.h | 62 + app/tools/gimpscaletool.c | 474 + app/tools/gimpscaletool.h | 54 + app/tools/gimpseamlesscloneoptions.c | 138 + app/tools/gimpseamlesscloneoptions.h | 57 + app/tools/gimpseamlessclonetool.c | 845 + app/tools/gimpseamlessclonetool.h | 86 + app/tools/gimpselectionoptions.c | 292 + app/tools/gimpselectionoptions.h | 56 + app/tools/gimpselectiontool.c | 830 + app/tools/gimpselectiontool.h | 78 + app/tools/gimpsheartool.c | 331 + app/tools/gimpsheartool.h | 56 + app/tools/gimpsmudgetool.c | 115 + app/tools/gimpsmudgetool.h | 53 + app/tools/gimpsourcetool.c | 519 + app/tools/gimpsourcetool.h | 66 + app/tools/gimptextoptions.c | 743 + app/tools/gimptextoptions.h | 80 + app/tools/gimptexttool-editor.c | 1871 +++ app/tools/gimptexttool-editor.h | 56 + app/tools/gimptexttool.c | 2388 +++ app/tools/gimptexttool.h | 132 + app/tools/gimpthresholdtool.c | 436 + app/tools/gimpthresholdtool.h | 59 + app/tools/gimptilehandleriscissors.c | 331 + app/tools/gimptilehandleriscissors.h | 59 + app/tools/gimptool-progress.c | 260 + app/tools/gimptool-progress.h | 28 + app/tools/gimptool.c | 1512 ++ app/tools/gimptool.h | 299 + app/tools/gimptoolcontrol.c | 727 + app/tools/gimptoolcontrol.h | 254 + app/tools/gimptooloptions-gui.c | 63 + app/tools/gimptooloptions-gui.h | 26 + app/tools/gimptools-utils.c | 80 + app/tools/gimptools-utils.h | 26 + app/tools/gimptransform3doptions.c | 225 + app/tools/gimptransform3doptions.h | 59 + app/tools/gimptransform3dtool.c | 1036 ++ app/tools/gimptransform3dtool.h | 67 + app/tools/gimptransformgridoptions.c | 712 + app/tools/gimptransformgridoptions.h | 76 + app/tools/gimptransformgridtool.c | 2209 +++ app/tools/gimptransformgridtool.h | 118 + app/tools/gimptransformgridtoolundo.c | 220 + app/tools/gimptransformgridtoolundo.h | 56 + app/tools/gimptransformoptions.c | 271 + app/tools/gimptransformoptions.h | 64 + app/tools/gimptransformtool.c | 925 ++ app/tools/gimptransformtool.h | 104 + app/tools/gimpunifiedtransformtool.c | 355 + app/tools/gimpunifiedtransformtool.h | 53 + app/tools/gimpvectoroptions.c | 219 + app/tools/gimpvectoroptions.h | 55 + app/tools/gimpvectortool.c | 852 + app/tools/gimpvectortool.h | 67 + app/tools/gimpwarpoptions.c | 407 + app/tools/gimpwarpoptions.h | 75 + app/tools/gimpwarptool.c | 1484 ++ app/tools/gimpwarptool.h | 78 + app/tools/tool_manager.c | 966 ++ app/tools/tool_manager.h | 96 + app/tools/tools-enums.c | 342 + app/tools/tools-enums.h | 203 + app/tools/tools-types.h | 68 + app/unique.c | 304 + app/unique.h | 29 + app/vectors/Makefile.am | 44 + app/vectors/Makefile.in | 992 ++ app/vectors/gimpanchor.c | 71 + app/vectors/gimpanchor.h | 48 + app/vectors/gimpbezierstroke.c | 2309 +++ app/vectors/gimpbezierstroke.h | 84 + app/vectors/gimpstroke-new.c | 47 + app/vectors/gimpstroke-new.h | 31 + app/vectors/gimpstroke.c | 1431 ++ app/vectors/gimpstroke.h | 343 + app/vectors/gimpvectors-compat.c | 285 + app/vectors/gimpvectors-compat.h | 48 + app/vectors/gimpvectors-export.c | 333 + app/vectors/gimpvectors-export.h | 30 + app/vectors/gimpvectors-import.c | 1784 +++ app/vectors/gimpvectors-import.h | 44 + app/vectors/gimpvectors-preview.c | 93 + app/vectors/gimpvectors-preview.h | 32 + app/vectors/gimpvectors-warp.c | 210 + app/vectors/gimpvectors-warp.h | 36 + app/vectors/gimpvectors.c | 1255 ++ app/vectors/gimpvectors.h | 184 + app/vectors/gimpvectorsmodundo.c | 141 + app/vectors/gimpvectorsmodundo.h | 52 + app/vectors/gimpvectorspropundo.c | 94 + app/vectors/gimpvectorspropundo.h | 50 + app/vectors/gimpvectorsundo.c | 213 + app/vectors/gimpvectorsundo.h | 54 + app/vectors/vectors-enums.h | 46 + app/vectors/vectors-types.h | 37 + app/widgets/Makefile.am | 514 + app/widgets/Makefile.in | 2356 +++ app/widgets/gimpaccellabel.c | 285 + app/widgets/gimpaccellabel.h | 58 + app/widgets/gimpaction-history.c | 503 + app/widgets/gimpaction-history.h | 46 + app/widgets/gimpaction.c | 392 + app/widgets/gimpaction.h | 108 + app/widgets/gimpactioneditor.c | 142 + app/widgets/gimpactioneditor.h | 55 + app/widgets/gimpactionfactory.c | 162 + app/widgets/gimpactionfactory.h | 80 + app/widgets/gimpactiongroup.c | 1068 ++ app/widgets/gimpactiongroup.h | 241 + app/widgets/gimpactionimpl.c | 400 + app/widgets/gimpactionimpl.h | 61 + app/widgets/gimpactionview.c | 905 ++ app/widgets/gimpactionview.h | 76 + app/widgets/gimpblobeditor.c | 389 + app/widgets/gimpblobeditor.h | 59 + app/widgets/gimpbrusheditor.c | 464 + app/widgets/gimpbrusheditor.h | 64 + app/widgets/gimpbrushfactoryview.c | 253 + app/widgets/gimpbrushfactoryview.h | 65 + app/widgets/gimpbrushselect.c | 346 + app/widgets/gimpbrushselect.h | 62 + app/widgets/gimpbuffersourcebox.c | 357 + app/widgets/gimpbuffersourcebox.h | 58 + app/widgets/gimpbufferview.c | 308 + app/widgets/gimpbufferview.h | 68 + app/widgets/gimpcairo-wilber.c | 1010 ++ app/widgets/gimpcairo-wilber.h | 45 + app/widgets/gimpcellrendererbutton.c | 142 + app/widgets/gimpcellrendererbutton.h | 59 + app/widgets/gimpcellrendererdashes.c | 262 + app/widgets/gimpcellrendererdashes.h | 53 + app/widgets/gimpcellrendererviewable.c | 416 + app/widgets/gimpcellrendererviewable.h | 65 + app/widgets/gimpchanneltreeview.c | 368 + app/widgets/gimpchanneltreeview.h | 55 + app/widgets/gimpcircle.c | 589 + app/widgets/gimpcircle.h | 66 + app/widgets/gimpclipboard.c | 1293 ++ app/widgets/gimpclipboard.h | 50 + app/widgets/gimpcolorbar.c | 344 + app/widgets/gimpcolorbar.h | 60 + app/widgets/gimpcolordialog.c | 388 + app/widgets/gimpcolordialog.h | 79 + app/widgets/gimpcolordisplayeditor.c | 836 + app/widgets/gimpcolordisplayeditor.h | 79 + app/widgets/gimpcoloreditor.c | 695 + app/widgets/gimpcoloreditor.h | 62 + app/widgets/gimpcolorframe.c | 1127 ++ app/widgets/gimpcolorframe.h | 111 + app/widgets/gimpcolorhistory.c | 325 + app/widgets/gimpcolorhistory.h | 61 + app/widgets/gimpcolormapeditor.c | 826 + app/widgets/gimpcolormapeditor.h | 72 + app/widgets/gimpcolorpanel.c | 331 + app/widgets/gimpcolorpanel.h | 65 + app/widgets/gimpcolorselectorpalette.c | 183 + app/widgets/gimpcolorselectorpalette.h | 53 + app/widgets/gimpcombotagentry.c | 307 + app/widgets/gimpcombotagentry.h | 61 + app/widgets/gimpcomponenteditor.c | 631 + app/widgets/gimpcomponenteditor.h | 69 + app/widgets/gimpcompressioncombobox.c | 212 + app/widgets/gimpcompressioncombobox.h | 55 + app/widgets/gimpcontainerbox.c | 219 + app/widgets/gimpcontainerbox.h | 58 + app/widgets/gimpcontainercombobox.c | 420 + app/widgets/gimpcontainercombobox.h | 57 + app/widgets/gimpcontainereditor.c | 579 + app/widgets/gimpcontainereditor.h | 69 + app/widgets/gimpcontainerentry.c | 438 + app/widgets/gimpcontainerentry.h | 56 + app/widgets/gimpcontainergridview.c | 742 + app/widgets/gimpcontainergridview.h | 69 + app/widgets/gimpcontainericonview.c | 805 + app/widgets/gimpcontainericonview.h | 70 + app/widgets/gimpcontainerpopup.c | 406 + app/widgets/gimpcontainerpopup.h | 88 + app/widgets/gimpcontainertreestore.c | 612 + app/widgets/gimpcontainertreestore.h | 95 + app/widgets/gimpcontainertreeview-dnd.c | 733 + app/widgets/gimpcontainertreeview-dnd.h | 71 + app/widgets/gimpcontainertreeview-private.h | 46 + app/widgets/gimpcontainertreeview.c | 1722 +++ app/widgets/gimpcontainertreeview.h | 141 + app/widgets/gimpcontainerview-utils.c | 92 + app/widgets/gimpcontainerview-utils.h | 30 + app/widgets/gimpcontainerview.c | 1331 ++ app/widgets/gimpcontainerview.h | 168 + app/widgets/gimpcontrollereditor.c | 888 ++ app/widgets/gimpcontrollereditor.h | 64 + app/widgets/gimpcontrollerinfo.c | 532 + app/widgets/gimpcontrollerinfo.h | 82 + app/widgets/gimpcontrollerkeyboard.c | 294 + app/widgets/gimpcontrollerkeyboard.h | 56 + app/widgets/gimpcontrollerlist.c | 719 + app/widgets/gimpcontrollerlist.h | 68 + app/widgets/gimpcontrollermouse.c | 315 + app/widgets/gimpcontrollermouse.h | 57 + app/widgets/gimpcontrollers.c | 382 + app/widgets/gimpcontrollers.h | 39 + app/widgets/gimpcontrollerwheel.c | 293 + app/widgets/gimpcontrollerwheel.h | 56 + app/widgets/gimpcriticaldialog.c | 628 + app/widgets/gimpcriticaldialog.h | 76 + app/widgets/gimpcursor.c | 526 + app/widgets/gimpcursor.h | 37 + app/widgets/gimpcurveview.c | 1547 ++ app/widgets/gimpcurveview.h | 130 + app/widgets/gimpdashboard.c | 5054 ++++++ app/widgets/gimpdashboard.h | 95 + app/widgets/gimpdasheditor.c | 513 + app/widgets/gimpdasheditor.h | 70 + app/widgets/gimpdataeditor.c | 619 + app/widgets/gimpdataeditor.h | 77 + app/widgets/gimpdatafactoryview.c | 627 + app/widgets/gimpdatafactoryview.h | 73 + app/widgets/gimpdeviceeditor.c | 550 + app/widgets/gimpdeviceeditor.h | 51 + app/widgets/gimpdeviceinfo-coords.c | 287 + app/widgets/gimpdeviceinfo-coords.h | 43 + app/widgets/gimpdeviceinfo.c | 945 ++ app/widgets/gimpdeviceinfo.h | 120 + app/widgets/gimpdeviceinfoeditor.c | 770 + app/widgets/gimpdeviceinfoeditor.h | 51 + app/widgets/gimpdevicemanager.c | 547 + app/widgets/gimpdevicemanager.h | 66 + app/widgets/gimpdevices.c | 343 + app/widgets/gimpdevices.h | 44 + app/widgets/gimpdevicestatus.c | 586 + app/widgets/gimpdevicestatus.h | 65 + app/widgets/gimpdial.c | 596 + app/widgets/gimpdial.h | 61 + app/widgets/gimpdialogfactory.c | 1681 ++ app/widgets/gimpdialogfactory.h | 217 + app/widgets/gimpdnd-xds.c | 262 + app/widgets/gimpdnd-xds.h | 35 + app/widgets/gimpdnd.c | 2465 +++ app/widgets/gimpdnd.h | 260 + app/widgets/gimpdock.c | 768 + app/widgets/gimpdock.h | 120 + app/widgets/gimpdockable.c | 905 ++ app/widgets/gimpdockable.h | 110 + app/widgets/gimpdockbook.c | 1846 +++ app/widgets/gimpdockbook.h | 98 + app/widgets/gimpdockcolumns.c | 490 + app/widgets/gimpdockcolumns.h | 80 + app/widgets/gimpdockcontainer.c | 157 + app/widgets/gimpdockcontainer.h | 61 + app/widgets/gimpdocked.c | 276 + app/widgets/gimpdocked.h | 95 + app/widgets/gimpdockwindow.c | 1250 ++ app/widgets/gimpdockwindow.h | 85 + app/widgets/gimpdocumentview.c | 189 + app/widgets/gimpdocumentview.h | 63 + app/widgets/gimpdrawabletreeview.c | 393 + app/widgets/gimpdrawabletreeview.h | 52 + app/widgets/gimpdynamicseditor.c | 453 + app/widgets/gimpdynamicseditor.h | 57 + app/widgets/gimpdynamicsfactoryview.c | 81 + app/widgets/gimpdynamicsfactoryview.h | 58 + app/widgets/gimpdynamicsoutputeditor.c | 496 + app/widgets/gimpdynamicsoutputeditor.h | 51 + app/widgets/gimpeditor.c | 981 ++ app/widgets/gimpeditor.h | 95 + app/widgets/gimpenumaction.c | 166 + app/widgets/gimpenumaction.h | 63 + app/widgets/gimperrorconsole.c | 326 + app/widgets/gimperrorconsole.h | 73 + app/widgets/gimperrordialog.c | 204 + app/widgets/gimperrordialog.h | 65 + app/widgets/gimpexportdialog.c | 221 + app/widgets/gimpexportdialog.h | 58 + app/widgets/gimpfgbgeditor.c | 824 + app/widgets/gimpfgbgeditor.h | 90 + app/widgets/gimpfgbgview.c | 329 + app/widgets/gimpfgbgview.h | 58 + app/widgets/gimpfiledialog.c | 987 ++ app/widgets/gimpfiledialog.h | 104 + app/widgets/gimpfileprocview.c | 476 + app/widgets/gimpfileprocview.h | 66 + app/widgets/gimpfilleditor.c | 218 + app/widgets/gimpfilleditor.h | 55 + app/widgets/gimpfontfactoryview.c | 101 + app/widgets/gimpfontfactoryview.h | 58 + app/widgets/gimpfontselect.c | 112 + app/widgets/gimpfontselect.h | 55 + app/widgets/gimpgradienteditor.c | 2234 +++ app/widgets/gimpgradienteditor.h | 118 + app/widgets/gimpgradientselect.c | 195 + app/widgets/gimpgradientselect.h | 57 + app/widgets/gimpgrideditor.c | 334 + app/widgets/gimpgrideditor.h | 59 + app/widgets/gimphandlebar.c | 441 + app/widgets/gimphandlebar.h | 73 + app/widgets/gimphelp-ids.h | 760 + app/widgets/gimphelp.c | 897 ++ app/widgets/gimphelp.h | 51 + app/widgets/gimphighlightablebutton.c | 369 + app/widgets/gimphighlightablebutton.h | 67 + app/widgets/gimphistogrambox.c | 317 + app/widgets/gimphistogrambox.h | 62 + app/widgets/gimphistogrameditor.c | 765 + app/widgets/gimphistogrameditor.h | 68 + app/widgets/gimphistogramview.c | 826 + app/widgets/gimphistogramview.h | 88 + app/widgets/gimpiconpicker.c | 616 + app/widgets/gimpiconpicker.h | 60 + app/widgets/gimpiconsizescale.c | 538 + app/widgets/gimpiconsizescale.h | 51 + app/widgets/gimpimagecommenteditor.c | 248 + app/widgets/gimpimagecommenteditor.h | 57 + app/widgets/gimpimageeditor.c | 179 + app/widgets/gimpimageeditor.h | 60 + app/widgets/gimpimageparasiteview.c | 240 + app/widgets/gimpimageparasiteview.h | 60 + app/widgets/gimpimageprofileview.c | 114 + app/widgets/gimpimageprofileview.h | 56 + app/widgets/gimpimagepropview.c | 563 + app/widgets/gimpimagepropview.h | 69 + app/widgets/gimpimageview.c | 159 + app/widgets/gimpimageview.h | 63 + app/widgets/gimpitemtreeview.c | 1778 +++ app/widgets/gimpitemtreeview.h | 133 + app/widgets/gimplanguagecombobox.c | 138 + app/widgets/gimplanguagecombobox.h | 51 + app/widgets/gimplanguageentry.c | 255 + app/widgets/gimplanguageentry.h | 50 + app/widgets/gimplanguagestore-parser.c | 519 + app/widgets/gimplanguagestore-parser.h | 32 + app/widgets/gimplanguagestore.c | 201 + app/widgets/gimplanguagestore.h | 65 + app/widgets/gimplayermodebox.c | 303 + app/widgets/gimplayermodebox.h | 67 + app/widgets/gimplayermodecombobox.c | 470 + app/widgets/gimplayermodecombobox.h | 66 + app/widgets/gimplayertreeview.c | 1555 ++ app/widgets/gimplayertreeview.h | 55 + app/widgets/gimpmenudock.c | 103 + app/widgets/gimpmenudock.h | 57 + app/widgets/gimpmenufactory.c | 277 + app/widgets/gimpmenufactory.h | 77 + app/widgets/gimpmessagebox.c | 492 + app/widgets/gimpmessagebox.h | 72 + app/widgets/gimpmessagedialog.c | 108 + app/widgets/gimpmessagedialog.h | 63 + app/widgets/gimpmeter.c | 1328 ++ app/widgets/gimpmeter.h | 127 + app/widgets/gimpnavigationview.c | 697 + app/widgets/gimpnavigationview.h | 85 + app/widgets/gimpopendialog.c | 125 + app/widgets/gimpopendialog.h | 61 + app/widgets/gimpoverlaybox.c | 497 + app/widgets/gimpoverlaybox.h | 76 + app/widgets/gimpoverlaychild.c | 569 + app/widgets/gimpoverlaychild.h | 81 + app/widgets/gimpoverlaydialog.c | 643 + app/widgets/gimpoverlaydialog.h | 93 + app/widgets/gimpoverlayframe.c | 172 + app/widgets/gimpoverlayframe.h | 52 + app/widgets/gimppaletteeditor.c | 967 ++ app/widgets/gimppaletteeditor.h | 82 + app/widgets/gimppaletteselect.c | 116 + app/widgets/gimppaletteselect.h | 55 + app/widgets/gimppaletteview.c | 515 + app/widgets/gimppaletteview.h | 70 + app/widgets/gimppanedbox.c | 959 ++ app/widgets/gimppanedbox.h | 78 + app/widgets/gimppatternfactoryview.c | 96 + app/widgets/gimppatternfactoryview.h | 58 + app/widgets/gimppatternselect.c | 142 + app/widgets/gimppatternselect.h | 55 + app/widgets/gimppdbdialog.c | 350 + app/widgets/gimppdbdialog.h | 86 + app/widgets/gimppickablebutton.c | 343 + app/widgets/gimppickablebutton.h | 60 + app/widgets/gimppickablepopup.c | 436 + app/widgets/gimppickablepopup.h | 61 + app/widgets/gimppivotselector.c | 547 + app/widgets/gimppivotselector.h | 78 + app/widgets/gimppixbuf.c | 150 + app/widgets/gimppixbuf.h | 33 + app/widgets/gimppluginview.c | 227 + app/widgets/gimppluginview.h | 60 + app/widgets/gimppolar.c | 393 + app/widgets/gimppolar.h | 61 + app/widgets/gimppopup.c | 344 + app/widgets/gimppopup.h | 55 + app/widgets/gimpprefsbox.c | 504 + app/widgets/gimpprefsbox.h | 74 + app/widgets/gimpprocedureaction.c | 228 + app/widgets/gimpprocedureaction.h | 61 + app/widgets/gimpprogressbox.c | 245 + app/widgets/gimpprogressbox.h | 62 + app/widgets/gimpprogressdialog.c | 231 + app/widgets/gimpprogressdialog.h | 53 + app/widgets/gimppropwidgets.c | 2355 +++ app/widgets/gimppropwidgets.h | 153 + app/widgets/gimpradioaction.c | 109 + app/widgets/gimpradioaction.h | 58 + app/widgets/gimprender.c | 95 + app/widgets/gimprender.h | 29 + app/widgets/gimpsamplepointeditor.c | 623 + app/widgets/gimpsamplepointeditor.h | 69 + app/widgets/gimpsavedialog.c | 477 + app/widgets/gimpsavedialog.h | 69 + app/widgets/gimpscalebutton.c | 202 + app/widgets/gimpscalebutton.h | 53 + app/widgets/gimpsearchpopup.c | 773 + app/widgets/gimpsearchpopup.h | 74 + app/widgets/gimpselectiondata.c | 935 ++ app/widgets/gimpselectiondata.h | 112 + app/widgets/gimpselectioneditor.c | 350 + app/widgets/gimpselectioneditor.h | 60 + app/widgets/gimpsessioninfo-aux.c | 284 + app/widgets/gimpsessioninfo-aux.h | 55 + app/widgets/gimpsessioninfo-book.c | 292 + app/widgets/gimpsessioninfo-book.h | 58 + app/widgets/gimpsessioninfo-dock.c | 376 + app/widgets/gimpsessioninfo-dock.h | 65 + app/widgets/gimpsessioninfo-dockable.c | 308 + app/widgets/gimpsessioninfo-dockable.h | 59 + app/widgets/gimpsessioninfo-private.h | 54 + app/widgets/gimpsessioninfo.c | 1079 ++ app/widgets/gimpsessioninfo.h | 99 + app/widgets/gimpsessionmanaged.c | 87 + app/widgets/gimpsessionmanaged.h | 51 + app/widgets/gimpsettingsbox.c | 991 ++ app/widgets/gimpsettingsbox.h | 76 + app/widgets/gimpsettingseditor.c | 446 + app/widgets/gimpsettingseditor.h | 53 + app/widgets/gimpsizebox.c | 471 + app/widgets/gimpsizebox.h | 64 + app/widgets/gimpspinscale.c | 1546 ++ app/widgets/gimpspinscale.h | 73 + app/widgets/gimpstringaction.c | 162 + app/widgets/gimpstringaction.h | 61 + app/widgets/gimpstrokeeditor.c | 420 + app/widgets/gimpstrokeeditor.h | 58 + app/widgets/gimpsymmetryeditor.c | 282 + app/widgets/gimpsymmetryeditor.h | 57 + app/widgets/gimptagentry.c | 2207 +++ app/widgets/gimptagentry.h | 86 + app/widgets/gimptagpopup.c | 1518 ++ app/widgets/gimptagpopup.h | 83 + app/widgets/gimptemplateeditor.c | 873 ++ app/widgets/gimptemplateeditor.h | 63 + app/widgets/gimptemplateview.c | 179 + app/widgets/gimptemplateview.h | 65 + app/widgets/gimptextbuffer-serialize.c | 662 + app/widgets/gimptextbuffer-serialize.h | 55 + app/widgets/gimptextbuffer.c | 1866 +++ app/widgets/gimptextbuffer.h | 190 + app/widgets/gimptexteditor.c | 368 + app/widgets/gimptexteditor.h | 79 + app/widgets/gimptextproxy.c | 199 + app/widgets/gimptextproxy.h | 58 + app/widgets/gimptextstyleeditor.c | 1302 ++ app/widgets/gimptextstyleeditor.h | 89 + app/widgets/gimptexttag.c | 122 + app/widgets/gimptexttag.h | 45 + app/widgets/gimpthumbbox.c | 759 + app/widgets/gimpthumbbox.h | 66 + app/widgets/gimptoggleaction.c | 118 + app/widgets/gimptoggleaction.h | 61 + app/widgets/gimptoolbox-color-area.c | 355 + app/widgets/gimptoolbox-color-area.h | 27 + app/widgets/gimptoolbox-dnd.c | 277 + app/widgets/gimptoolbox-dnd.h | 26 + app/widgets/gimptoolbox-image-area.c | 145 + app/widgets/gimptoolbox-image-area.h | 27 + app/widgets/gimptoolbox-indicator-area.c | 251 + app/widgets/gimptoolbox-indicator-area.h | 25 + app/widgets/gimptoolbox.c | 811 + app/widgets/gimptoolbox.h | 59 + app/widgets/gimptoolbutton.c | 1477 ++ app/widgets/gimptoolbutton.h | 67 + app/widgets/gimptooleditor.c | 844 + app/widgets/gimptooleditor.h | 63 + app/widgets/gimptooloptionseditor.c | 553 + app/widgets/gimptooloptionseditor.h | 58 + app/widgets/gimptoolpalette.c | 542 + app/widgets/gimptoolpalette.h | 56 + app/widgets/gimptoolpreseteditor.c | 385 + app/widgets/gimptoolpreseteditor.h | 55 + app/widgets/gimptoolpresetfactoryview.c | 103 + app/widgets/gimptoolpresetfactoryview.h | 58 + app/widgets/gimptranslationstore.c | 192 + app/widgets/gimptranslationstore.h | 45 + app/widgets/gimpuimanager.c | 1369 ++ app/widgets/gimpuimanager.h | 138 + app/widgets/gimpundoeditor.c | 462 + app/widgets/gimpundoeditor.h | 63 + app/widgets/gimpvectorstreeview.c | 281 + app/widgets/gimpvectorstreeview.h | 56 + app/widgets/gimpview-popup.c | 243 + app/widgets/gimpview-popup.h | 34 + app/widgets/gimpview.c | 856 ++ app/widgets/gimpview.h | 108 + app/widgets/gimpviewablebox.c | 769 + app/widgets/gimpviewablebox.h | 111 + app/widgets/gimpviewablebutton.c | 362 + app/widgets/gimpviewablebutton.h | 84 + app/widgets/gimpviewabledialog.c | 377 + app/widgets/gimpviewabledialog.h | 75 + app/widgets/gimpviewrenderer-frame.c | 293 + app/widgets/gimpviewrenderer-frame.h | 34 + app/widgets/gimpviewrenderer-utils.c | 98 + app/widgets/gimpviewrenderer-utils.h | 28 + app/widgets/gimpviewrenderer.c | 1336 ++ app/widgets/gimpviewrenderer.h | 167 + app/widgets/gimpviewrendererbrush.c | 265 + app/widgets/gimpviewrendererbrush.h | 56 + app/widgets/gimpviewrendererbuffer.c | 117 + app/widgets/gimpviewrendererbuffer.h | 50 + app/widgets/gimpviewrendererdrawable.c | 366 + app/widgets/gimpviewrendererdrawable.h | 53 + app/widgets/gimpviewrenderergradient.c | 265 + app/widgets/gimpviewrenderergradient.h | 65 + app/widgets/gimpviewrendererimage.c | 191 + app/widgets/gimpviewrendererimage.h | 52 + app/widgets/gimpviewrendererimagefile.c | 201 + app/widgets/gimpviewrendererimagefile.h | 52 + app/widgets/gimpviewrendererlayer.c | 98 + app/widgets/gimpviewrendererlayer.h | 50 + app/widgets/gimpviewrendererpalette.c | 260 + app/widgets/gimpviewrendererpalette.h | 63 + app/widgets/gimpviewrenderervectors.c | 111 + app/widgets/gimpviewrenderervectors.h | 51 + app/widgets/gimpwidgets-constructors.c | 103 + app/widgets/gimpwidgets-constructors.h | 28 + app/widgets/gimpwidgets-utils.c | 1964 +++ app/widgets/gimpwidgets-utils.h | 138 + app/widgets/gimpwindow.c | 300 + app/widgets/gimpwindow.h | 59 + app/widgets/gimpwindowstrategy.c | 68 + app/widgets/gimpwindowstrategy.h | 57 + app/widgets/gtkhwrapbox.c | 607 + app/widgets/gtkhwrapbox.h | 69 + app/widgets/gtkwrapbox.c | 898 ++ app/widgets/gtkwrapbox.h | 134 + app/widgets/widgets-enums.c | 269 + app/widgets/widgets-enums.h | 315 + app/widgets/widgets-types.h | 321 + app/xcf/Makefile.am | 31 + app/xcf/Makefile.in | 951 ++ app/xcf/xcf-load.c | 3246 ++++ app/xcf/xcf-load.h | 27 + app/xcf/xcf-private.h | 118 + app/xcf/xcf-read.c | 274 + app/xcf/xcf-read.h | 53 + app/xcf/xcf-save.c | 2271 +++ app/xcf/xcf-save.h | 27 + app/xcf/xcf-seek.c | 53 + app/xcf/xcf-seek.h | 27 + app/xcf/xcf-utils.c | 53 + app/xcf/xcf-utils.h | 26 + app/xcf/xcf-write.c | 339 + app/xcf/xcf-write.h | 64 + app/xcf/xcf.c | 516 + app/xcf/xcf.h | 38 + 2296 files changed, 754069 insertions(+) create mode 100644 app/Makefile.am create mode 100644 app/Makefile.in create mode 100644 app/about.h create mode 100644 app/actions/Makefile.am create mode 100644 app/actions/Makefile.in create mode 100644 app/actions/actions-types.h create mode 100644 app/actions/actions.c create mode 100644 app/actions/actions.h create mode 100644 app/actions/brush-editor-actions.c create mode 100644 app/actions/brush-editor-actions.h create mode 100644 app/actions/brushes-actions.c create mode 100644 app/actions/brushes-actions.h create mode 100644 app/actions/buffers-actions.c create mode 100644 app/actions/buffers-actions.h create mode 100644 app/actions/buffers-commands.c create mode 100644 app/actions/buffers-commands.h create mode 100644 app/actions/channels-actions.c create mode 100644 app/actions/channels-actions.h create mode 100644 app/actions/channels-commands.c create mode 100644 app/actions/channels-commands.h create mode 100644 app/actions/colormap-actions.c create mode 100644 app/actions/colormap-actions.h create mode 100644 app/actions/colormap-commands.c create mode 100644 app/actions/colormap-commands.h create mode 100644 app/actions/context-actions.c create mode 100644 app/actions/context-actions.h create mode 100644 app/actions/context-commands.c create mode 100644 app/actions/context-commands.h create mode 100644 app/actions/cursor-info-actions.c create mode 100644 app/actions/cursor-info-actions.h create mode 100644 app/actions/cursor-info-commands.c create mode 100644 app/actions/cursor-info-commands.h create mode 100644 app/actions/dashboard-actions.c create mode 100644 app/actions/dashboard-actions.h create mode 100644 app/actions/dashboard-commands.c create mode 100644 app/actions/dashboard-commands.h create mode 100644 app/actions/data-commands.c create mode 100644 app/actions/data-commands.h create mode 100644 app/actions/data-editor-commands.c create mode 100644 app/actions/data-editor-commands.h create mode 100644 app/actions/debug-actions.c create mode 100644 app/actions/debug-actions.h create mode 100644 app/actions/debug-commands.c create mode 100644 app/actions/debug-commands.h create mode 100644 app/actions/dialogs-actions.c create mode 100644 app/actions/dialogs-actions.h create mode 100644 app/actions/dialogs-commands.c create mode 100644 app/actions/dialogs-commands.h create mode 100644 app/actions/dock-actions.c create mode 100644 app/actions/dock-actions.h create mode 100644 app/actions/dock-commands.c create mode 100644 app/actions/dock-commands.h create mode 100644 app/actions/dockable-actions.c create mode 100644 app/actions/dockable-actions.h create mode 100644 app/actions/dockable-commands.c create mode 100644 app/actions/dockable-commands.h create mode 100644 app/actions/documents-actions.c create mode 100644 app/actions/documents-actions.h create mode 100644 app/actions/documents-commands.c create mode 100644 app/actions/documents-commands.h create mode 100644 app/actions/drawable-actions.c create mode 100644 app/actions/drawable-actions.h create mode 100644 app/actions/drawable-commands.c create mode 100644 app/actions/drawable-commands.h create mode 100644 app/actions/dynamics-actions.c create mode 100644 app/actions/dynamics-actions.h create mode 100644 app/actions/dynamics-editor-actions.c create mode 100644 app/actions/dynamics-editor-actions.h create mode 100644 app/actions/edit-actions.c create mode 100644 app/actions/edit-actions.h create mode 100644 app/actions/edit-commands.c create mode 100644 app/actions/edit-commands.h create mode 100644 app/actions/error-console-actions.c create mode 100644 app/actions/error-console-actions.h create mode 100644 app/actions/error-console-commands.c create mode 100644 app/actions/error-console-commands.h create mode 100644 app/actions/file-actions.c create mode 100644 app/actions/file-actions.h create mode 100644 app/actions/file-commands.c create mode 100644 app/actions/file-commands.h create mode 100644 app/actions/filters-actions.c create mode 100644 app/actions/filters-actions.h create mode 100644 app/actions/filters-commands.c create mode 100644 app/actions/filters-commands.h create mode 100644 app/actions/fonts-actions.c create mode 100644 app/actions/fonts-actions.h create mode 100644 app/actions/gimpgeglprocedure.c create mode 100644 app/actions/gimpgeglprocedure.h create mode 100644 app/actions/gradient-editor-actions.c create mode 100644 app/actions/gradient-editor-actions.h create mode 100644 app/actions/gradient-editor-commands.c create mode 100644 app/actions/gradient-editor-commands.h create mode 100644 app/actions/gradients-actions.c create mode 100644 app/actions/gradients-actions.h create mode 100644 app/actions/gradients-commands.c create mode 100644 app/actions/gradients-commands.h create mode 100644 app/actions/help-actions.c create mode 100644 app/actions/help-actions.h create mode 100644 app/actions/help-commands.c create mode 100644 app/actions/help-commands.h create mode 100644 app/actions/image-actions.c create mode 100644 app/actions/image-actions.h create mode 100644 app/actions/image-commands.c create mode 100644 app/actions/image-commands.h create mode 100644 app/actions/images-actions.c create mode 100644 app/actions/images-actions.h create mode 100644 app/actions/images-commands.c create mode 100644 app/actions/images-commands.h create mode 100644 app/actions/items-actions.c create mode 100644 app/actions/items-actions.h create mode 100644 app/actions/items-commands.c create mode 100644 app/actions/items-commands.h create mode 100644 app/actions/layers-actions.c create mode 100644 app/actions/layers-actions.h create mode 100644 app/actions/layers-commands.c create mode 100644 app/actions/layers-commands.h create mode 100644 app/actions/mypaint-brushes-actions.c create mode 100644 app/actions/mypaint-brushes-actions.h create mode 100644 app/actions/palette-editor-actions.c create mode 100644 app/actions/palette-editor-actions.h create mode 100644 app/actions/palette-editor-commands.c create mode 100644 app/actions/palette-editor-commands.h create mode 100644 app/actions/palettes-actions.c create mode 100644 app/actions/palettes-actions.h create mode 100644 app/actions/palettes-commands.c create mode 100644 app/actions/palettes-commands.h create mode 100644 app/actions/patterns-actions.c create mode 100644 app/actions/patterns-actions.h create mode 100644 app/actions/plug-in-actions.c create mode 100644 app/actions/plug-in-actions.h create mode 100644 app/actions/plug-in-commands.c create mode 100644 app/actions/plug-in-commands.h create mode 100644 app/actions/procedure-commands.c create mode 100644 app/actions/procedure-commands.h create mode 100644 app/actions/quick-mask-actions.c create mode 100644 app/actions/quick-mask-actions.h create mode 100644 app/actions/quick-mask-commands.c create mode 100644 app/actions/quick-mask-commands.h create mode 100644 app/actions/sample-points-actions.c create mode 100644 app/actions/sample-points-actions.h create mode 100644 app/actions/sample-points-commands.c create mode 100644 app/actions/sample-points-commands.h create mode 100644 app/actions/select-actions.c create mode 100644 app/actions/select-actions.h create mode 100644 app/actions/select-commands.c create mode 100644 app/actions/select-commands.h create mode 100644 app/actions/templates-actions.c create mode 100644 app/actions/templates-actions.h create mode 100644 app/actions/templates-commands.c create mode 100644 app/actions/templates-commands.h create mode 100644 app/actions/text-editor-actions.c create mode 100644 app/actions/text-editor-actions.h create mode 100644 app/actions/text-editor-commands.c create mode 100644 app/actions/text-editor-commands.h create mode 100644 app/actions/text-tool-actions.c create mode 100644 app/actions/text-tool-actions.h create mode 100644 app/actions/text-tool-commands.c create mode 100644 app/actions/text-tool-commands.h create mode 100644 app/actions/tool-options-actions.c create mode 100644 app/actions/tool-options-actions.h create mode 100644 app/actions/tool-options-commands.c create mode 100644 app/actions/tool-options-commands.h create mode 100644 app/actions/tool-preset-editor-actions.c create mode 100644 app/actions/tool-preset-editor-actions.h create mode 100644 app/actions/tool-preset-editor-commands.c create mode 100644 app/actions/tool-preset-editor-commands.h create mode 100644 app/actions/tool-presets-actions.c create mode 100644 app/actions/tool-presets-actions.h create mode 100644 app/actions/tool-presets-commands.c create mode 100644 app/actions/tool-presets-commands.h create mode 100644 app/actions/tools-actions.c create mode 100644 app/actions/tools-actions.h create mode 100644 app/actions/tools-commands.c create mode 100644 app/actions/tools-commands.h create mode 100644 app/actions/vectors-actions.c create mode 100644 app/actions/vectors-actions.h create mode 100644 app/actions/vectors-commands.c create mode 100644 app/actions/vectors-commands.h create mode 100644 app/actions/view-actions.c create mode 100644 app/actions/view-actions.h create mode 100644 app/actions/view-commands.c create mode 100644 app/actions/view-commands.h create mode 100644 app/actions/window-actions.c create mode 100644 app/actions/window-actions.h create mode 100644 app/actions/window-commands.c create mode 100644 app/actions/window-commands.h create mode 100644 app/actions/windows-actions.c create mode 100644 app/actions/windows-actions.h create mode 100644 app/actions/windows-commands.c create mode 100644 app/actions/windows-commands.h create mode 100644 app/app.c create mode 100644 app/app.h create mode 100644 app/config/Makefile.am create mode 100644 app/config/Makefile.in create mode 100644 app/config/config-enums.c create mode 100644 app/config/config-enums.h create mode 100644 app/config/config-types.h create mode 100644 app/config/gimpconfig-dump.c create mode 100644 app/config/gimpconfig-dump.h create mode 100644 app/config/gimpconfig-file.c create mode 100644 app/config/gimpconfig-file.h create mode 100644 app/config/gimpconfig-utils.c create mode 100644 app/config/gimpconfig-utils.h create mode 100644 app/config/gimpcoreconfig.c create mode 100644 app/config/gimpcoreconfig.h create mode 100644 app/config/gimpdialogconfig.c create mode 100644 app/config/gimpdialogconfig.h create mode 100644 app/config/gimpdisplayconfig.c create mode 100644 app/config/gimpdisplayconfig.h create mode 100644 app/config/gimpdisplayoptions.c create mode 100644 app/config/gimpdisplayoptions.h create mode 100644 app/config/gimpgeglconfig.c create mode 100644 app/config/gimpgeglconfig.h create mode 100644 app/config/gimpguiconfig.c create mode 100644 app/config/gimpguiconfig.h create mode 100644 app/config/gimplangrc.c create mode 100644 app/config/gimplangrc.h create mode 100644 app/config/gimppluginconfig.c create mode 100644 app/config/gimppluginconfig.h create mode 100644 app/config/gimprc-blurbs.h create mode 100644 app/config/gimprc-deserialize.c create mode 100644 app/config/gimprc-deserialize.h create mode 100644 app/config/gimprc-serialize.c create mode 100644 app/config/gimprc-serialize.h create mode 100644 app/config/gimprc-unknown.c create mode 100644 app/config/gimprc-unknown.h create mode 100644 app/config/gimprc.c create mode 100644 app/config/gimprc.h create mode 100644 app/config/gimpxmlparser.c create mode 100644 app/config/gimpxmlparser.h create mode 100644 app/config/test-config.c create mode 100644 app/core/Makefile.am create mode 100644 app/core/Makefile.in create mode 100644 app/core/core-enums.c create mode 100644 app/core/core-enums.h create mode 100644 app/core/core-types.h create mode 100644 app/core/gimp-atomic.c create mode 100644 app/core/gimp-atomic.h create mode 100644 app/core/gimp-batch.c create mode 100644 app/core/gimp-batch.h create mode 100644 app/core/gimp-cairo.c create mode 100644 app/core/gimp-cairo.h create mode 100644 app/core/gimp-contexts.c create mode 100644 app/core/gimp-contexts.h create mode 100644 app/core/gimp-data-factories.c create mode 100644 app/core/gimp-data-factories.h create mode 100644 app/core/gimp-edit.c create mode 100644 app/core/gimp-edit.h create mode 100644 app/core/gimp-filter-history.c create mode 100644 app/core/gimp-filter-history.h create mode 100644 app/core/gimp-gradients.c create mode 100644 app/core/gimp-gradients.h create mode 100644 app/core/gimp-gui.c create mode 100644 app/core/gimp-gui.h create mode 100644 app/core/gimp-internal-data.c create mode 100644 app/core/gimp-internal-data.h create mode 100644 app/core/gimp-memsize.c create mode 100644 app/core/gimp-memsize.h create mode 100644 app/core/gimp-modules.c create mode 100644 app/core/gimp-modules.h create mode 100644 app/core/gimp-palettes.c create mode 100644 app/core/gimp-palettes.h create mode 100644 app/core/gimp-parallel.cc create mode 100644 app/core/gimp-parallel.h create mode 100644 app/core/gimp-parasites.c create mode 100644 app/core/gimp-parasites.h create mode 100644 app/core/gimp-spawn.c create mode 100644 app/core/gimp-spawn.h create mode 100644 app/core/gimp-tags.c create mode 100644 app/core/gimp-tags.h create mode 100644 app/core/gimp-templates.c create mode 100644 app/core/gimp-templates.h create mode 100644 app/core/gimp-transform-3d-utils.c create mode 100644 app/core/gimp-transform-3d-utils.h create mode 100644 app/core/gimp-transform-resize.c create mode 100644 app/core/gimp-transform-resize.h create mode 100644 app/core/gimp-transform-utils.c create mode 100644 app/core/gimp-transform-utils.h create mode 100644 app/core/gimp-units.c create mode 100644 app/core/gimp-units.h create mode 100644 app/core/gimp-user-install.c create mode 100644 app/core/gimp-user-install.h create mode 100644 app/core/gimp-utils.c create mode 100644 app/core/gimp-utils.h create mode 100644 app/core/gimp.c create mode 100644 app/core/gimp.h create mode 100644 app/core/gimpasync.c create mode 100644 app/core/gimpasync.h create mode 100644 app/core/gimpasyncset.c create mode 100644 app/core/gimpasyncset.h create mode 100644 app/core/gimpauxitem.c create mode 100644 app/core/gimpauxitem.h create mode 100644 app/core/gimpauxitemundo.c create mode 100644 app/core/gimpauxitemundo.h create mode 100644 app/core/gimpbacktrace-backend.h create mode 100644 app/core/gimpbacktrace-linux.c create mode 100644 app/core/gimpbacktrace-none.c create mode 100644 app/core/gimpbacktrace-windows.c create mode 100644 app/core/gimpbacktrace.h create mode 100644 app/core/gimpbezierdesc.c create mode 100644 app/core/gimpbezierdesc.h create mode 100644 app/core/gimpboundary.c create mode 100644 app/core/gimpboundary.h create mode 100644 app/core/gimpbrush-boundary.c create mode 100644 app/core/gimpbrush-boundary.h create mode 100644 app/core/gimpbrush-header.h create mode 100644 app/core/gimpbrush-load.c create mode 100644 app/core/gimpbrush-load.h create mode 100644 app/core/gimpbrush-mipmap.cc create mode 100644 app/core/gimpbrush-mipmap.h create mode 100644 app/core/gimpbrush-private.h create mode 100644 app/core/gimpbrush-save.c create mode 100644 app/core/gimpbrush-save.h create mode 100644 app/core/gimpbrush-transform.cc create mode 100644 app/core/gimpbrush-transform.h create mode 100644 app/core/gimpbrush.c create mode 100644 app/core/gimpbrush.h create mode 100644 app/core/gimpbrushcache.c create mode 100644 app/core/gimpbrushcache.h create mode 100644 app/core/gimpbrushclipboard.c create mode 100644 app/core/gimpbrushclipboard.h create mode 100644 app/core/gimpbrushgenerated-load.c create mode 100644 app/core/gimpbrushgenerated-load.h create mode 100644 app/core/gimpbrushgenerated-save.c create mode 100644 app/core/gimpbrushgenerated-save.h create mode 100644 app/core/gimpbrushgenerated.c create mode 100644 app/core/gimpbrushgenerated.h create mode 100644 app/core/gimpbrushpipe-load.c create mode 100644 app/core/gimpbrushpipe-load.h create mode 100644 app/core/gimpbrushpipe-save.c create mode 100644 app/core/gimpbrushpipe-save.h create mode 100644 app/core/gimpbrushpipe.c create mode 100644 app/core/gimpbrushpipe.h create mode 100644 app/core/gimpbuffer.c create mode 100644 app/core/gimpbuffer.h create mode 100644 app/core/gimpcancelable.c create mode 100644 app/core/gimpcancelable.h create mode 100644 app/core/gimpchannel-combine.c create mode 100644 app/core/gimpchannel-combine.h create mode 100644 app/core/gimpchannel-select.c create mode 100644 app/core/gimpchannel-select.h create mode 100644 app/core/gimpchannel.c create mode 100644 app/core/gimpchannel.h create mode 100644 app/core/gimpchannelpropundo.c create mode 100644 app/core/gimpchannelpropundo.h create mode 100644 app/core/gimpchannelundo.c create mode 100644 app/core/gimpchannelundo.h create mode 100644 app/core/gimpchunkiterator.c create mode 100644 app/core/gimpchunkiterator.h create mode 100644 app/core/gimpcontainer-filter.c create mode 100644 app/core/gimpcontainer-filter.h create mode 100644 app/core/gimpcontainer.c create mode 100644 app/core/gimpcontainer.h create mode 100644 app/core/gimpcontext.c create mode 100644 app/core/gimpcontext.h create mode 100644 app/core/gimpcoords-interpolate.c create mode 100644 app/core/gimpcoords-interpolate.h create mode 100644 app/core/gimpcoords.c create mode 100644 app/core/gimpcoords.h create mode 100644 app/core/gimpcurve-load.c create mode 100644 app/core/gimpcurve-load.h create mode 100644 app/core/gimpcurve-map.c create mode 100644 app/core/gimpcurve-map.h create mode 100644 app/core/gimpcurve-save.c create mode 100644 app/core/gimpcurve-save.h create mode 100644 app/core/gimpcurve.c create mode 100644 app/core/gimpcurve.h create mode 100644 app/core/gimpdashpattern.c create mode 100644 app/core/gimpdashpattern.h create mode 100644 app/core/gimpdata.c create mode 100644 app/core/gimpdata.h create mode 100644 app/core/gimpdatafactory.c create mode 100644 app/core/gimpdatafactory.h create mode 100644 app/core/gimpdataloaderfactory.c create mode 100644 app/core/gimpdataloaderfactory.h create mode 100644 app/core/gimpdocumentlist.c create mode 100644 app/core/gimpdocumentlist.h create mode 100644 app/core/gimpdrawable-bucket-fill.c create mode 100644 app/core/gimpdrawable-bucket-fill.h create mode 100644 app/core/gimpdrawable-combine.c create mode 100644 app/core/gimpdrawable-combine.h create mode 100644 app/core/gimpdrawable-edit.c create mode 100644 app/core/gimpdrawable-edit.h create mode 100644 app/core/gimpdrawable-equalize.c create mode 100644 app/core/gimpdrawable-equalize.h create mode 100644 app/core/gimpdrawable-fill.c create mode 100644 app/core/gimpdrawable-fill.h create mode 100644 app/core/gimpdrawable-filters.c create mode 100644 app/core/gimpdrawable-filters.h create mode 100644 app/core/gimpdrawable-floating-selection.c create mode 100644 app/core/gimpdrawable-floating-selection.h create mode 100644 app/core/gimpdrawable-foreground-extract.c create mode 100644 app/core/gimpdrawable-foreground-extract.h create mode 100644 app/core/gimpdrawable-gradient.c create mode 100644 app/core/gimpdrawable-gradient.h create mode 100644 app/core/gimpdrawable-histogram.c create mode 100644 app/core/gimpdrawable-histogram.h create mode 100644 app/core/gimpdrawable-levels.c create mode 100644 app/core/gimpdrawable-levels.h create mode 100644 app/core/gimpdrawable-offset.c create mode 100644 app/core/gimpdrawable-offset.h create mode 100644 app/core/gimpdrawable-operation.c create mode 100644 app/core/gimpdrawable-operation.h create mode 100644 app/core/gimpdrawable-preview.c create mode 100644 app/core/gimpdrawable-preview.h create mode 100644 app/core/gimpdrawable-private.h create mode 100644 app/core/gimpdrawable-shadow.c create mode 100644 app/core/gimpdrawable-shadow.h create mode 100644 app/core/gimpdrawable-stroke.c create mode 100644 app/core/gimpdrawable-stroke.h create mode 100644 app/core/gimpdrawable-transform.c create mode 100644 app/core/gimpdrawable-transform.h create mode 100644 app/core/gimpdrawable.c create mode 100644 app/core/gimpdrawable.h create mode 100644 app/core/gimpdrawablefilter.c create mode 100644 app/core/gimpdrawablefilter.h create mode 100644 app/core/gimpdrawablemodundo.c create mode 100644 app/core/gimpdrawablemodundo.h create mode 100644 app/core/gimpdrawablestack.c create mode 100644 app/core/gimpdrawablestack.h create mode 100644 app/core/gimpdrawableundo.c create mode 100644 app/core/gimpdrawableundo.h create mode 100644 app/core/gimpdynamics-load.c create mode 100644 app/core/gimpdynamics-load.h create mode 100644 app/core/gimpdynamics-save.c create mode 100644 app/core/gimpdynamics-save.h create mode 100644 app/core/gimpdynamics.c create mode 100644 app/core/gimpdynamics.h create mode 100644 app/core/gimpdynamicsoutput.c create mode 100644 app/core/gimpdynamicsoutput.h create mode 100644 app/core/gimperror.c create mode 100644 app/core/gimperror.h create mode 100644 app/core/gimpfilloptions.c create mode 100644 app/core/gimpfilloptions.h create mode 100644 app/core/gimpfilter.c create mode 100644 app/core/gimpfilter.h create mode 100644 app/core/gimpfilteredcontainer.c create mode 100644 app/core/gimpfilteredcontainer.h create mode 100644 app/core/gimpfilterstack.c create mode 100644 app/core/gimpfilterstack.h create mode 100644 app/core/gimpfloatingselectionundo.c create mode 100644 app/core/gimpfloatingselectionundo.h create mode 100644 app/core/gimpgradient-load.c create mode 100644 app/core/gimpgradient-load.h create mode 100644 app/core/gimpgradient-save.c create mode 100644 app/core/gimpgradient-save.h create mode 100644 app/core/gimpgradient.c create mode 100644 app/core/gimpgradient.h create mode 100644 app/core/gimpgrid.c create mode 100644 app/core/gimpgrid.h create mode 100644 app/core/gimpgrouplayer.c create mode 100644 app/core/gimpgrouplayer.h create mode 100644 app/core/gimpgrouplayerundo.c create mode 100644 app/core/gimpgrouplayerundo.h create mode 100644 app/core/gimpguide.c create mode 100644 app/core/gimpguide.h create mode 100644 app/core/gimpguideundo.c create mode 100644 app/core/gimpguideundo.h create mode 100644 app/core/gimphistogram.c create mode 100644 app/core/gimphistogram.h create mode 100644 app/core/gimpidtable.c create mode 100644 app/core/gimpidtable.h create mode 100644 app/core/gimpimage-arrange.c create mode 100644 app/core/gimpimage-arrange.h create mode 100644 app/core/gimpimage-color-profile.c create mode 100644 app/core/gimpimage-color-profile.h create mode 100644 app/core/gimpimage-colormap.c create mode 100644 app/core/gimpimage-colormap.h create mode 100644 app/core/gimpimage-convert-data.h create mode 100644 app/core/gimpimage-convert-fsdither.h create mode 100644 app/core/gimpimage-convert-indexed.c create mode 100644 app/core/gimpimage-convert-indexed.h create mode 100644 app/core/gimpimage-convert-precision.c create mode 100644 app/core/gimpimage-convert-precision.h create mode 100644 app/core/gimpimage-convert-type.c create mode 100644 app/core/gimpimage-convert-type.h create mode 100644 app/core/gimpimage-crop.c create mode 100644 app/core/gimpimage-crop.h create mode 100644 app/core/gimpimage-duplicate.c create mode 100644 app/core/gimpimage-duplicate.h create mode 100644 app/core/gimpimage-flip.c create mode 100644 app/core/gimpimage-flip.h create mode 100644 app/core/gimpimage-grid.c create mode 100644 app/core/gimpimage-grid.h create mode 100644 app/core/gimpimage-guides.c create mode 100644 app/core/gimpimage-guides.h create mode 100644 app/core/gimpimage-item-list.c create mode 100644 app/core/gimpimage-item-list.h create mode 100644 app/core/gimpimage-merge.c create mode 100644 app/core/gimpimage-merge.h create mode 100644 app/core/gimpimage-metadata.c create mode 100644 app/core/gimpimage-metadata.h create mode 100644 app/core/gimpimage-new.c create mode 100644 app/core/gimpimage-new.h create mode 100644 app/core/gimpimage-pick-color.c create mode 100644 app/core/gimpimage-pick-color.h create mode 100644 app/core/gimpimage-pick-item.c create mode 100644 app/core/gimpimage-pick-item.h create mode 100644 app/core/gimpimage-preview.c create mode 100644 app/core/gimpimage-preview.h create mode 100644 app/core/gimpimage-private.h create mode 100644 app/core/gimpimage-quick-mask.c create mode 100644 app/core/gimpimage-quick-mask.h create mode 100644 app/core/gimpimage-resize.c create mode 100644 app/core/gimpimage-resize.h create mode 100644 app/core/gimpimage-rotate.c create mode 100644 app/core/gimpimage-rotate.h create mode 100644 app/core/gimpimage-sample-points.c create mode 100644 app/core/gimpimage-sample-points.h create mode 100644 app/core/gimpimage-scale.c create mode 100644 app/core/gimpimage-scale.h create mode 100644 app/core/gimpimage-snap.c create mode 100644 app/core/gimpimage-snap.h create mode 100644 app/core/gimpimage-symmetry.c create mode 100644 app/core/gimpimage-symmetry.h create mode 100644 app/core/gimpimage-transform.c create mode 100644 app/core/gimpimage-transform.h create mode 100644 app/core/gimpimage-undo-push.c create mode 100644 app/core/gimpimage-undo-push.h create mode 100644 app/core/gimpimage-undo.c create mode 100644 app/core/gimpimage-undo.h create mode 100644 app/core/gimpimage.c create mode 100644 app/core/gimpimage.h create mode 100644 app/core/gimpimagefile.c create mode 100644 app/core/gimpimagefile.h create mode 100644 app/core/gimpimageproxy.c create mode 100644 app/core/gimpimageproxy.h create mode 100644 app/core/gimpimageundo.c create mode 100644 app/core/gimpimageundo.h create mode 100644 app/core/gimpitem-exclusive.c create mode 100644 app/core/gimpitem-exclusive.h create mode 100644 app/core/gimpitem-linked.c create mode 100644 app/core/gimpitem-linked.h create mode 100644 app/core/gimpitem-preview.c create mode 100644 app/core/gimpitem-preview.h create mode 100644 app/core/gimpitem.c create mode 100644 app/core/gimpitem.h create mode 100644 app/core/gimpitempropundo.c create mode 100644 app/core/gimpitempropundo.h create mode 100644 app/core/gimpitemstack.c create mode 100644 app/core/gimpitemstack.h create mode 100644 app/core/gimpitemtree.c create mode 100644 app/core/gimpitemtree.h create mode 100644 app/core/gimpitemundo.c create mode 100644 app/core/gimpitemundo.h create mode 100644 app/core/gimplayer-floating-selection.c create mode 100644 app/core/gimplayer-floating-selection.h create mode 100644 app/core/gimplayer-new.c create mode 100644 app/core/gimplayer-new.h create mode 100644 app/core/gimplayer.c create mode 100644 app/core/gimplayer.h create mode 100644 app/core/gimplayermask.c create mode 100644 app/core/gimplayermask.h create mode 100644 app/core/gimplayermaskpropundo.c create mode 100644 app/core/gimplayermaskpropundo.h create mode 100644 app/core/gimplayermaskundo.c create mode 100644 app/core/gimplayermaskundo.h create mode 100644 app/core/gimplayerpropundo.c create mode 100644 app/core/gimplayerpropundo.h create mode 100644 app/core/gimplayerstack.c create mode 100644 app/core/gimplayerstack.h create mode 100644 app/core/gimplayerundo.c create mode 100644 app/core/gimplayerundo.h create mode 100644 app/core/gimplineart.c create mode 100644 app/core/gimplineart.h create mode 100644 app/core/gimplist.c create mode 100644 app/core/gimplist.h create mode 100644 app/core/gimpmarshal.c create mode 100644 app/core/gimpmarshal.h create mode 100644 app/core/gimpmarshal.list create mode 100644 app/core/gimpmaskundo.c create mode 100644 app/core/gimpmaskundo.h create mode 100644 app/core/gimpmybrush-load.c create mode 100644 app/core/gimpmybrush-load.h create mode 100644 app/core/gimpmybrush-private.h create mode 100644 app/core/gimpmybrush.c create mode 100644 app/core/gimpmybrush.h create mode 100644 app/core/gimpobject.c create mode 100644 app/core/gimpobject.h create mode 100644 app/core/gimpobjectqueue.c create mode 100644 app/core/gimpobjectqueue.h create mode 100644 app/core/gimppaintinfo.c create mode 100644 app/core/gimppaintinfo.h create mode 100644 app/core/gimppalette-import.c create mode 100644 app/core/gimppalette-import.h create mode 100644 app/core/gimppalette-load.c create mode 100644 app/core/gimppalette-load.h create mode 100644 app/core/gimppalette-save.c create mode 100644 app/core/gimppalette-save.h create mode 100644 app/core/gimppalette.c create mode 100644 app/core/gimppalette.h create mode 100644 app/core/gimppalettemru.c create mode 100644 app/core/gimppalettemru.h create mode 100644 app/core/gimpparamspecs-desc.c create mode 100644 app/core/gimpparamspecs-desc.h create mode 100644 app/core/gimpparamspecs-duplicate.c create mode 100644 app/core/gimpparamspecs-duplicate.h create mode 100644 app/core/gimpparamspecs.c create mode 100644 app/core/gimpparamspecs.h create mode 100644 app/core/gimpparasitelist.c create mode 100644 app/core/gimpparasitelist.h create mode 100644 app/core/gimppattern-header.h create mode 100644 app/core/gimppattern-load.c create mode 100644 app/core/gimppattern-load.h create mode 100644 app/core/gimppattern-save.c create mode 100644 app/core/gimppattern-save.h create mode 100644 app/core/gimppattern.c create mode 100644 app/core/gimppattern.h create mode 100644 app/core/gimppatternclipboard.c create mode 100644 app/core/gimppatternclipboard.h create mode 100644 app/core/gimppdbprogress.c create mode 100644 app/core/gimppdbprogress.h create mode 100644 app/core/gimppickable-auto-shrink.c create mode 100644 app/core/gimppickable-auto-shrink.h create mode 100644 app/core/gimppickable-contiguous-region.cc create mode 100644 app/core/gimppickable-contiguous-region.h create mode 100644 app/core/gimppickable.c create mode 100644 app/core/gimppickable.h create mode 100644 app/core/gimpprogress.c create mode 100644 app/core/gimpprogress.h create mode 100644 app/core/gimpprojectable.c create mode 100644 app/core/gimpprojectable.h create mode 100644 app/core/gimpprojection.c create mode 100644 app/core/gimpprojection.h create mode 100644 app/core/gimpsamplepoint.c create mode 100644 app/core/gimpsamplepoint.h create mode 100644 app/core/gimpsamplepointundo.c create mode 100644 app/core/gimpsamplepointundo.h create mode 100644 app/core/gimpscanconvert.c create mode 100644 app/core/gimpscanconvert.h create mode 100644 app/core/gimpselection.c create mode 100644 app/core/gimpselection.h create mode 100644 app/core/gimpsettings.c create mode 100644 app/core/gimpsettings.h create mode 100644 app/core/gimpstrokeoptions.c create mode 100644 app/core/gimpstrokeoptions.h create mode 100644 app/core/gimpsubprogress.c create mode 100644 app/core/gimpsubprogress.h create mode 100644 app/core/gimpsymmetry-mandala.c create mode 100644 app/core/gimpsymmetry-mandala.h create mode 100644 app/core/gimpsymmetry-mirror.c create mode 100644 app/core/gimpsymmetry-mirror.h create mode 100644 app/core/gimpsymmetry-tiling.c create mode 100644 app/core/gimpsymmetry-tiling.h create mode 100644 app/core/gimpsymmetry.c create mode 100644 app/core/gimpsymmetry.h create mode 100644 app/core/gimptag.c create mode 100644 app/core/gimptag.h create mode 100644 app/core/gimptagcache.c create mode 100644 app/core/gimptagcache.h create mode 100644 app/core/gimptagged.c create mode 100644 app/core/gimptagged.h create mode 100644 app/core/gimptaggedcontainer.c create mode 100644 app/core/gimptaggedcontainer.h create mode 100644 app/core/gimptempbuf.c create mode 100644 app/core/gimptempbuf.h create mode 100644 app/core/gimptemplate.c create mode 100644 app/core/gimptemplate.h create mode 100644 app/core/gimptilehandlerprojectable.c create mode 100644 app/core/gimptilehandlerprojectable.h create mode 100644 app/core/gimptoolgroup.c create mode 100644 app/core/gimptoolgroup.h create mode 100644 app/core/gimptoolinfo.c create mode 100644 app/core/gimptoolinfo.h create mode 100644 app/core/gimptoolitem.c create mode 100644 app/core/gimptoolitem.h create mode 100644 app/core/gimptooloptions.c create mode 100644 app/core/gimptooloptions.h create mode 100644 app/core/gimptoolpreset-load.c create mode 100644 app/core/gimptoolpreset-load.h create mode 100644 app/core/gimptoolpreset-save.c create mode 100644 app/core/gimptoolpreset-save.h create mode 100644 app/core/gimptoolpreset.c create mode 100644 app/core/gimptoolpreset.h create mode 100644 app/core/gimptreehandler.c create mode 100644 app/core/gimptreehandler.h create mode 100644 app/core/gimptreeproxy.c create mode 100644 app/core/gimptreeproxy.h create mode 100644 app/core/gimptriviallycancelablewaitable.c create mode 100644 app/core/gimptriviallycancelablewaitable.h create mode 100644 app/core/gimpuncancelablewaitable.c create mode 100644 app/core/gimpuncancelablewaitable.h create mode 100644 app/core/gimpundo.c create mode 100644 app/core/gimpundo.h create mode 100644 app/core/gimpundostack.c create mode 100644 app/core/gimpundostack.h create mode 100644 app/core/gimpunit.c create mode 100644 app/core/gimpunit.h create mode 100644 app/core/gimpviewable.c create mode 100644 app/core/gimpviewable.h create mode 100644 app/core/gimpwaitable.c create mode 100644 app/core/gimpwaitable.h create mode 100644 app/dialogs/Makefile.am create mode 100644 app/dialogs/Makefile.in create mode 100644 app/dialogs/about-dialog.c create mode 100644 app/dialogs/about-dialog.h create mode 100644 app/dialogs/action-search-dialog.c create mode 100644 app/dialogs/action-search-dialog.h create mode 100644 app/dialogs/authors.h create mode 100644 app/dialogs/authors.xsl create mode 100644 app/dialogs/channel-options-dialog.c create mode 100644 app/dialogs/channel-options-dialog.h create mode 100644 app/dialogs/color-profile-dialog.c create mode 100644 app/dialogs/color-profile-dialog.h create mode 100644 app/dialogs/color-profile-import-dialog.c create mode 100644 app/dialogs/color-profile-import-dialog.h create mode 100644 app/dialogs/convert-indexed-dialog.c create mode 100644 app/dialogs/convert-indexed-dialog.h create mode 100644 app/dialogs/convert-precision-dialog.c create mode 100644 app/dialogs/convert-precision-dialog.h create mode 100644 app/dialogs/data-delete-dialog.c create mode 100644 app/dialogs/data-delete-dialog.h create mode 100644 app/dialogs/dialogs-constructors.c create mode 100644 app/dialogs/dialogs-constructors.h create mode 100644 app/dialogs/dialogs-types.h create mode 100644 app/dialogs/dialogs.c create mode 100644 app/dialogs/dialogs.h create mode 100644 app/dialogs/file-open-dialog.c create mode 100644 app/dialogs/file-open-dialog.h create mode 100644 app/dialogs/file-open-location-dialog.c create mode 100644 app/dialogs/file-open-location-dialog.h create mode 100644 app/dialogs/file-save-dialog.c create mode 100644 app/dialogs/file-save-dialog.h create mode 100644 app/dialogs/fill-dialog.c create mode 100644 app/dialogs/fill-dialog.h create mode 100644 app/dialogs/grid-dialog.c create mode 100644 app/dialogs/grid-dialog.h create mode 100644 app/dialogs/image-merge-layers-dialog.c create mode 100644 app/dialogs/image-merge-layers-dialog.h create mode 100644 app/dialogs/image-new-dialog.c create mode 100644 app/dialogs/image-new-dialog.h create mode 100644 app/dialogs/image-properties-dialog.c create mode 100644 app/dialogs/image-properties-dialog.h create mode 100644 app/dialogs/image-scale-dialog.c create mode 100644 app/dialogs/image-scale-dialog.h create mode 100644 app/dialogs/input-devices-dialog.c create mode 100644 app/dialogs/input-devices-dialog.h create mode 100644 app/dialogs/item-options-dialog.c create mode 100644 app/dialogs/item-options-dialog.h create mode 100644 app/dialogs/keyboard-shortcuts-dialog.c create mode 100644 app/dialogs/keyboard-shortcuts-dialog.h create mode 100644 app/dialogs/layer-add-mask-dialog.c create mode 100644 app/dialogs/layer-add-mask-dialog.h create mode 100644 app/dialogs/layer-options-dialog.c create mode 100644 app/dialogs/layer-options-dialog.h create mode 100644 app/dialogs/lebl-dialog.c create mode 100644 app/dialogs/lebl-dialog.h create mode 100644 app/dialogs/module-dialog.c create mode 100644 app/dialogs/module-dialog.h create mode 100644 app/dialogs/palette-import-dialog.c create mode 100644 app/dialogs/palette-import-dialog.h create mode 100644 app/dialogs/preferences-dialog-utils.c create mode 100644 app/dialogs/preferences-dialog-utils.h create mode 100644 app/dialogs/preferences-dialog.c create mode 100644 app/dialogs/preferences-dialog.h create mode 100644 app/dialogs/print-size-dialog.c create mode 100644 app/dialogs/print-size-dialog.h create mode 100644 app/dialogs/quit-dialog.c create mode 100644 app/dialogs/quit-dialog.h create mode 100644 app/dialogs/resize-dialog.c create mode 100644 app/dialogs/resize-dialog.h create mode 100644 app/dialogs/resolution-calibrate-dialog.c create mode 100644 app/dialogs/resolution-calibrate-dialog.h create mode 100644 app/dialogs/scale-dialog.c create mode 100644 app/dialogs/scale-dialog.h create mode 100644 app/dialogs/stroke-dialog.c create mode 100644 app/dialogs/stroke-dialog.h create mode 100644 app/dialogs/template-options-dialog.c create mode 100644 app/dialogs/template-options-dialog.h create mode 100644 app/dialogs/tips-dialog.c create mode 100644 app/dialogs/tips-dialog.h create mode 100644 app/dialogs/tips-parser.c create mode 100644 app/dialogs/tips-parser.h create mode 100644 app/dialogs/user-install-dialog.c create mode 100644 app/dialogs/user-install-dialog.h create mode 100644 app/dialogs/vectors-export-dialog.c create mode 100644 app/dialogs/vectors-export-dialog.h create mode 100644 app/dialogs/vectors-import-dialog.c create mode 100644 app/dialogs/vectors-import-dialog.h create mode 100644 app/dialogs/vectors-options-dialog.c create mode 100644 app/dialogs/vectors-options-dialog.h create mode 100644 app/display/Makefile.am create mode 100644 app/display/Makefile.in create mode 100644 app/display/display-enums.c create mode 100644 app/display/display-enums.h create mode 100644 app/display/display-types.h create mode 100644 app/display/gimpcanvas-style.c create mode 100644 app/display/gimpcanvas-style.h create mode 100644 app/display/gimpcanvas.c create mode 100644 app/display/gimpcanvas.h create mode 100644 app/display/gimpcanvasarc.c create mode 100644 app/display/gimpcanvasarc.h create mode 100644 app/display/gimpcanvasboundary.c create mode 100644 app/display/gimpcanvasboundary.h create mode 100644 app/display/gimpcanvasbufferpreview.c create mode 100644 app/display/gimpcanvasbufferpreview.h create mode 100644 app/display/gimpcanvascanvasboundary.c create mode 100644 app/display/gimpcanvascanvasboundary.h create mode 100644 app/display/gimpcanvascorner.c create mode 100644 app/display/gimpcanvascorner.h create mode 100644 app/display/gimpcanvascursor.c create mode 100644 app/display/gimpcanvascursor.h create mode 100644 app/display/gimpcanvasgrid.c create mode 100644 app/display/gimpcanvasgrid.h create mode 100644 app/display/gimpcanvasgroup.c create mode 100644 app/display/gimpcanvasgroup.h create mode 100644 app/display/gimpcanvasguide.c create mode 100644 app/display/gimpcanvasguide.h create mode 100644 app/display/gimpcanvashandle.c create mode 100644 app/display/gimpcanvashandle.h create mode 100644 app/display/gimpcanvasitem-utils.c create mode 100644 app/display/gimpcanvasitem-utils.h create mode 100644 app/display/gimpcanvasitem.c create mode 100644 app/display/gimpcanvasitem.h create mode 100644 app/display/gimpcanvaslayerboundary.c create mode 100644 app/display/gimpcanvaslayerboundary.h create mode 100644 app/display/gimpcanvaslimit.c create mode 100644 app/display/gimpcanvaslimit.h create mode 100644 app/display/gimpcanvasline.c create mode 100644 app/display/gimpcanvasline.h create mode 100644 app/display/gimpcanvaspassepartout.c create mode 100644 app/display/gimpcanvaspassepartout.h create mode 100644 app/display/gimpcanvaspath.c create mode 100644 app/display/gimpcanvaspath.h create mode 100644 app/display/gimpcanvaspen.c create mode 100644 app/display/gimpcanvaspen.h create mode 100644 app/display/gimpcanvaspolygon.c create mode 100644 app/display/gimpcanvaspolygon.h create mode 100644 app/display/gimpcanvasprogress.c create mode 100644 app/display/gimpcanvasprogress.h create mode 100644 app/display/gimpcanvasproxygroup.c create mode 100644 app/display/gimpcanvasproxygroup.h create mode 100644 app/display/gimpcanvasrectangle.c create mode 100644 app/display/gimpcanvasrectangle.h create mode 100644 app/display/gimpcanvasrectangleguides.c create mode 100644 app/display/gimpcanvasrectangleguides.h create mode 100644 app/display/gimpcanvassamplepoint.c create mode 100644 app/display/gimpcanvassamplepoint.h create mode 100644 app/display/gimpcanvastextcursor.c create mode 100644 app/display/gimpcanvastextcursor.h create mode 100644 app/display/gimpcanvastransformguides.c create mode 100644 app/display/gimpcanvastransformguides.h create mode 100644 app/display/gimpcanvastransformpreview.c create mode 100644 app/display/gimpcanvastransformpreview.h create mode 100644 app/display/gimpcursorview.c create mode 100644 app/display/gimpcursorview.h create mode 100644 app/display/gimpdisplay-foreach.c create mode 100644 app/display/gimpdisplay-foreach.h create mode 100644 app/display/gimpdisplay-handlers.c create mode 100644 app/display/gimpdisplay-handlers.h create mode 100644 app/display/gimpdisplay.c create mode 100644 app/display/gimpdisplay.h create mode 100644 app/display/gimpdisplayshell-actions.c create mode 100644 app/display/gimpdisplayshell-actions.h create mode 100644 app/display/gimpdisplayshell-appearance.c create mode 100644 app/display/gimpdisplayshell-appearance.h create mode 100644 app/display/gimpdisplayshell-autoscroll.c create mode 100644 app/display/gimpdisplayshell-autoscroll.h create mode 100644 app/display/gimpdisplayshell-callbacks.c create mode 100644 app/display/gimpdisplayshell-callbacks.h create mode 100644 app/display/gimpdisplayshell-close.c create mode 100644 app/display/gimpdisplayshell-close.h create mode 100644 app/display/gimpdisplayshell-cursor.c create mode 100644 app/display/gimpdisplayshell-cursor.h create mode 100644 app/display/gimpdisplayshell-dnd.c create mode 100644 app/display/gimpdisplayshell-dnd.h create mode 100644 app/display/gimpdisplayshell-draw.c create mode 100644 app/display/gimpdisplayshell-draw.h create mode 100644 app/display/gimpdisplayshell-expose.c create mode 100644 app/display/gimpdisplayshell-expose.h create mode 100644 app/display/gimpdisplayshell-filter-dialog.c create mode 100644 app/display/gimpdisplayshell-filter-dialog.h create mode 100644 app/display/gimpdisplayshell-filter.c create mode 100644 app/display/gimpdisplayshell-filter.h create mode 100644 app/display/gimpdisplayshell-grab.c create mode 100644 app/display/gimpdisplayshell-grab.h create mode 100644 app/display/gimpdisplayshell-handlers.c create mode 100644 app/display/gimpdisplayshell-handlers.h create mode 100644 app/display/gimpdisplayshell-icon.c create mode 100644 app/display/gimpdisplayshell-icon.h create mode 100644 app/display/gimpdisplayshell-items.c create mode 100644 app/display/gimpdisplayshell-items.h create mode 100644 app/display/gimpdisplayshell-layer-select.c create mode 100644 app/display/gimpdisplayshell-layer-select.h create mode 100644 app/display/gimpdisplayshell-profile.c create mode 100644 app/display/gimpdisplayshell-profile.h create mode 100644 app/display/gimpdisplayshell-progress.c create mode 100644 app/display/gimpdisplayshell-progress.h create mode 100644 app/display/gimpdisplayshell-render.c create mode 100644 app/display/gimpdisplayshell-render.h create mode 100644 app/display/gimpdisplayshell-rotate-dialog.c create mode 100644 app/display/gimpdisplayshell-rotate-dialog.h create mode 100644 app/display/gimpdisplayshell-rotate.c create mode 100644 app/display/gimpdisplayshell-rotate.h create mode 100644 app/display/gimpdisplayshell-rulers.c create mode 100644 app/display/gimpdisplayshell-rulers.h create mode 100644 app/display/gimpdisplayshell-scale-dialog.c create mode 100644 app/display/gimpdisplayshell-scale-dialog.h create mode 100644 app/display/gimpdisplayshell-scale.c create mode 100644 app/display/gimpdisplayshell-scale.h create mode 100644 app/display/gimpdisplayshell-scroll.c create mode 100644 app/display/gimpdisplayshell-scroll.h create mode 100644 app/display/gimpdisplayshell-scrollbars.c create mode 100644 app/display/gimpdisplayshell-scrollbars.h create mode 100644 app/display/gimpdisplayshell-selection.c create mode 100644 app/display/gimpdisplayshell-selection.h create mode 100644 app/display/gimpdisplayshell-title.c create mode 100644 app/display/gimpdisplayshell-title.h create mode 100644 app/display/gimpdisplayshell-tool-events.c create mode 100644 app/display/gimpdisplayshell-tool-events.h create mode 100644 app/display/gimpdisplayshell-transform.c create mode 100644 app/display/gimpdisplayshell-transform.h create mode 100644 app/display/gimpdisplayshell-utils.c create mode 100644 app/display/gimpdisplayshell-utils.h create mode 100644 app/display/gimpdisplayshell.c create mode 100644 app/display/gimpdisplayshell.h create mode 100644 app/display/gimpdisplayxfer.c create mode 100644 app/display/gimpdisplayxfer.h create mode 100644 app/display/gimpimagewindow.c create mode 100644 app/display/gimpimagewindow.h create mode 100644 app/display/gimpmotionbuffer.c create mode 100644 app/display/gimpmotionbuffer.h create mode 100644 app/display/gimpmultiwindowstrategy.c create mode 100644 app/display/gimpmultiwindowstrategy.h create mode 100644 app/display/gimpnavigationeditor.c create mode 100644 app/display/gimpnavigationeditor.h create mode 100644 app/display/gimpscalecombobox.c create mode 100644 app/display/gimpscalecombobox.h create mode 100644 app/display/gimpsinglewindowstrategy.c create mode 100644 app/display/gimpsinglewindowstrategy.h create mode 100644 app/display/gimpstatusbar.c create mode 100644 app/display/gimpstatusbar.h create mode 100644 app/display/gimptoolcompass.c create mode 100644 app/display/gimptoolcompass.h create mode 100644 app/display/gimptooldialog.c create mode 100644 app/display/gimptooldialog.h create mode 100644 app/display/gimptoolfocus.c create mode 100644 app/display/gimptoolfocus.h create mode 100644 app/display/gimptoolgui.c create mode 100644 app/display/gimptoolgui.h create mode 100644 app/display/gimptoolgyroscope.c create mode 100644 app/display/gimptoolgyroscope.h create mode 100644 app/display/gimptoolhandlegrid.c create mode 100644 app/display/gimptoolhandlegrid.h create mode 100644 app/display/gimptoolline.c create mode 100644 app/display/gimptoolline.h create mode 100644 app/display/gimptoolpath.c create mode 100644 app/display/gimptoolpath.h create mode 100644 app/display/gimptoolpolygon.c create mode 100644 app/display/gimptoolpolygon.h create mode 100644 app/display/gimptoolrectangle.c create mode 100644 app/display/gimptoolrectangle.h create mode 100644 app/display/gimptoolrotategrid.c create mode 100644 app/display/gimptoolrotategrid.h create mode 100644 app/display/gimptoolsheargrid.c create mode 100644 app/display/gimptoolsheargrid.h create mode 100644 app/display/gimptooltransform3dgrid.c create mode 100644 app/display/gimptooltransform3dgrid.h create mode 100644 app/display/gimptooltransformgrid.c create mode 100644 app/display/gimptooltransformgrid.h create mode 100644 app/display/gimptoolwidget.c create mode 100644 app/display/gimptoolwidget.h create mode 100644 app/display/gimptoolwidgetgroup.c create mode 100644 app/display/gimptoolwidgetgroup.h create mode 100644 app/errors.c create mode 100644 app/errors.h create mode 100644 app/file-data/Makefile.am create mode 100644 app/file-data/Makefile.in create mode 100644 app/file-data/file-data-gbr.c create mode 100644 app/file-data/file-data-gbr.h create mode 100644 app/file-data/file-data-gih.c create mode 100644 app/file-data/file-data-gih.h create mode 100644 app/file-data/file-data-pat.c create mode 100644 app/file-data/file-data-pat.h create mode 100644 app/file-data/file-data.c create mode 100644 app/file-data/file-data.h create mode 100644 app/file/Makefile.am create mode 100644 app/file/Makefile.in create mode 100644 app/file/file-import.c create mode 100644 app/file/file-import.h create mode 100644 app/file/file-open.c create mode 100644 app/file/file-open.h create mode 100644 app/file/file-remote.c create mode 100644 app/file/file-remote.h create mode 100644 app/file/file-save.c create mode 100644 app/file/file-save.h create mode 100644 app/file/file-utils.c create mode 100644 app/file/file-utils.h create mode 100644 app/file/gimp-file.h create mode 100644 app/gegl/Makefile.am create mode 100644 app/gegl/Makefile.in create mode 100644 app/gegl/gimp-babl-compat.c create mode 100644 app/gegl/gimp-babl-compat.h create mode 100644 app/gegl/gimp-babl.c create mode 100644 app/gegl/gimp-babl.h create mode 100644 app/gegl/gimp-gegl-apply-operation.c create mode 100644 app/gegl/gimp-gegl-apply-operation.h create mode 100644 app/gegl/gimp-gegl-enums.c create mode 100644 app/gegl/gimp-gegl-enums.h create mode 100644 app/gegl/gimp-gegl-loops-sse2.c create mode 100644 app/gegl/gimp-gegl-loops-sse2.h create mode 100644 app/gegl/gimp-gegl-loops.cc create mode 100644 app/gegl/gimp-gegl-loops.h create mode 100644 app/gegl/gimp-gegl-mask-combine.cc create mode 100644 app/gegl/gimp-gegl-mask-combine.h create mode 100644 app/gegl/gimp-gegl-mask.c create mode 100644 app/gegl/gimp-gegl-mask.h create mode 100644 app/gegl/gimp-gegl-nodes.c create mode 100644 app/gegl/gimp-gegl-nodes.h create mode 100644 app/gegl/gimp-gegl-tile-compat.c create mode 100644 app/gegl/gimp-gegl-tile-compat.h create mode 100644 app/gegl/gimp-gegl-types.h create mode 100644 app/gegl/gimp-gegl-utils.c create mode 100644 app/gegl/gimp-gegl-utils.h create mode 100644 app/gegl/gimp-gegl.c create mode 100644 app/gegl/gimp-gegl.h create mode 100644 app/gegl/gimpapplicator.c create mode 100644 app/gegl/gimpapplicator.h create mode 100644 app/gegl/gimptilehandlervalidate.c create mode 100644 app/gegl/gimptilehandlervalidate.h create mode 100644 app/gimp-debug.c create mode 100644 app/gimp-debug.h create mode 100644 app/gimp-intl.h create mode 100644 app/gimp-log.c create mode 100644 app/gimp-log.h create mode 100644 app/gimp-priorities.h create mode 100644 app/gimp-update.c create mode 100644 app/gimp-update.h create mode 100644 app/gimp-version.c create mode 100644 app/gimp-version.h create mode 100644 app/gui/Makefile.am create mode 100644 app/gui/Makefile.in create mode 100644 app/gui/dbus-service.xml create mode 100644 app/gui/gimpdbusservice-generated.c create mode 100644 app/gui/gimpdbusservice-generated.h create mode 100644 app/gui/gimpdbusservice.c create mode 100644 app/gui/gimpdbusservice.h create mode 100644 app/gui/gimpuiconfigurer.c create mode 100644 app/gui/gimpuiconfigurer.h create mode 100644 app/gui/gui-message.c create mode 100644 app/gui/gui-message.h create mode 100644 app/gui/gui-types.h create mode 100644 app/gui/gui-unique.c create mode 100644 app/gui/gui-unique.h create mode 100644 app/gui/gui-vtable.c create mode 100644 app/gui/gui-vtable.h create mode 100644 app/gui/gui.c create mode 100644 app/gui/gui.h create mode 100644 app/gui/icon-themes.c create mode 100644 app/gui/icon-themes.h create mode 100644 app/gui/session.c create mode 100644 app/gui/session.h create mode 100644 app/gui/splash.c create mode 100644 app/gui/splash.h create mode 100644 app/gui/themes.c create mode 100644 app/gui/themes.h create mode 100644 app/language.c create mode 100644 app/language.h create mode 100644 app/main.c create mode 100644 app/menus/Makefile.am create mode 100644 app/menus/Makefile.in create mode 100644 app/menus/dockable-menu.c create mode 100644 app/menus/dockable-menu.h create mode 100644 app/menus/file-menu.c create mode 100644 app/menus/file-menu.h create mode 100644 app/menus/filters-menu.c create mode 100644 app/menus/filters-menu.h create mode 100644 app/menus/image-menu.c create mode 100644 app/menus/image-menu.h create mode 100644 app/menus/menus-types.h create mode 100644 app/menus/menus.c create mode 100644 app/menus/menus.h create mode 100644 app/menus/plug-in-menus.c create mode 100644 app/menus/plug-in-menus.h create mode 100644 app/menus/tool-options-menu.c create mode 100644 app/menus/tool-options-menu.h create mode 100644 app/menus/window-menu.c create mode 100644 app/menus/window-menu.h create mode 100644 app/menus/windows-menu.c create mode 100644 app/menus/windows-menu.h create mode 100644 app/operations/Makefile.am create mode 100644 app/operations/Makefile.in create mode 100644 app/operations/gimp-operation-config.c create mode 100644 app/operations/gimp-operation-config.h create mode 100644 app/operations/gimp-operations.c create mode 100644 app/operations/gimp-operations.h create mode 100644 app/operations/gimpbrightnesscontrastconfig.c create mode 100644 app/operations/gimpbrightnesscontrastconfig.h create mode 100644 app/operations/gimpcageconfig.c create mode 100644 app/operations/gimpcageconfig.h create mode 100644 app/operations/gimpcolorbalanceconfig.c create mode 100644 app/operations/gimpcolorbalanceconfig.h create mode 100644 app/operations/gimpcurvesconfig.c create mode 100644 app/operations/gimpcurvesconfig.h create mode 100644 app/operations/gimphuesaturationconfig.c create mode 100644 app/operations/gimphuesaturationconfig.h create mode 100644 app/operations/gimplevelsconfig.c create mode 100644 app/operations/gimplevelsconfig.h create mode 100644 app/operations/gimpoperationborder.c create mode 100644 app/operations/gimpoperationborder.h create mode 100644 app/operations/gimpoperationbrightnesscontrast.c create mode 100644 app/operations/gimpoperationbrightnesscontrast.h create mode 100644 app/operations/gimpoperationbuffersourcevalidate.c create mode 100644 app/operations/gimpoperationbuffersourcevalidate.h create mode 100644 app/operations/gimpoperationcagecoefcalc.c create mode 100644 app/operations/gimpoperationcagecoefcalc.h create mode 100644 app/operations/gimpoperationcagetransform.c create mode 100644 app/operations/gimpoperationcagetransform.h create mode 100644 app/operations/gimpoperationcolorbalance.c create mode 100644 app/operations/gimpoperationcolorbalance.h create mode 100644 app/operations/gimpoperationcolorize.c create mode 100644 app/operations/gimpoperationcolorize.h create mode 100644 app/operations/gimpoperationcomposecrop.c create mode 100644 app/operations/gimpoperationcomposecrop.h create mode 100644 app/operations/gimpoperationcurves.c create mode 100644 app/operations/gimpoperationcurves.h create mode 100644 app/operations/gimpoperationdesaturate.c create mode 100644 app/operations/gimpoperationdesaturate.h create mode 100644 app/operations/gimpoperationequalize.c create mode 100644 app/operations/gimpoperationequalize.h create mode 100644 app/operations/gimpoperationfillsource.c create mode 100644 app/operations/gimpoperationfillsource.h create mode 100644 app/operations/gimpoperationflood.c create mode 100644 app/operations/gimpoperationflood.h create mode 100644 app/operations/gimpoperationgradient.c create mode 100644 app/operations/gimpoperationgradient.h create mode 100644 app/operations/gimpoperationgrow.c create mode 100644 app/operations/gimpoperationgrow.h create mode 100644 app/operations/gimpoperationhistogramsink.c create mode 100644 app/operations/gimpoperationhistogramsink.h create mode 100644 app/operations/gimpoperationhuesaturation.c create mode 100644 app/operations/gimpoperationhuesaturation.h create mode 100644 app/operations/gimpoperationlevels.c create mode 100644 app/operations/gimpoperationlevels.h create mode 100644 app/operations/gimpoperationmaskcomponents.cc create mode 100644 app/operations/gimpoperationmaskcomponents.h create mode 100644 app/operations/gimpoperationoffset.c create mode 100644 app/operations/gimpoperationoffset.h create mode 100644 app/operations/gimpoperationpointfilter.c create mode 100644 app/operations/gimpoperationpointfilter.h create mode 100644 app/operations/gimpoperationposterize.c create mode 100644 app/operations/gimpoperationposterize.h create mode 100644 app/operations/gimpoperationprofiletransform.c create mode 100644 app/operations/gimpoperationprofiletransform.h create mode 100644 app/operations/gimpoperationscalarmultiply.c create mode 100644 app/operations/gimpoperationscalarmultiply.h create mode 100644 app/operations/gimpoperationsemiflatten.c create mode 100644 app/operations/gimpoperationsemiflatten.h create mode 100644 app/operations/gimpoperationsetalpha.c create mode 100644 app/operations/gimpoperationsetalpha.h create mode 100644 app/operations/gimpoperationsettings.c create mode 100644 app/operations/gimpoperationsettings.h create mode 100644 app/operations/gimpoperationshrink.c create mode 100644 app/operations/gimpoperationshrink.h create mode 100644 app/operations/gimpoperationthreshold.c create mode 100644 app/operations/gimpoperationthreshold.h create mode 100644 app/operations/gimpoperationthresholdalpha.c create mode 100644 app/operations/gimpoperationthresholdalpha.h create mode 100644 app/operations/layer-modes-legacy/Makefile.am create mode 100644 app/operations/layer-modes-legacy/Makefile.in create mode 100644 app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationburnlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationburnlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationdividelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationdividelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h create mode 100644 app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c create mode 100644 app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h create mode 100644 app/operations/layer-modes/Makefile.am create mode 100644 app/operations/layer-modes/Makefile.in create mode 100644 app/operations/layer-modes/gimp-layer-modes.c create mode 100644 app/operations/layer-modes/gimp-layer-modes.h create mode 100644 app/operations/layer-modes/gimpoperationantierase.c create mode 100644 app/operations/layer-modes/gimpoperationantierase.h create mode 100644 app/operations/layer-modes/gimpoperationbehind.c create mode 100644 app/operations/layer-modes/gimpoperationbehind.h create mode 100644 app/operations/layer-modes/gimpoperationdissolve.c create mode 100644 app/operations/layer-modes/gimpoperationdissolve.h create mode 100644 app/operations/layer-modes/gimpoperationerase.c create mode 100644 app/operations/layer-modes/gimpoperationerase.h create mode 100644 app/operations/layer-modes/gimpoperationlayermode-blend.c create mode 100644 app/operations/layer-modes/gimpoperationlayermode-blend.h create mode 100644 app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c create mode 100644 app/operations/layer-modes/gimpoperationlayermode-composite.c create mode 100644 app/operations/layer-modes/gimpoperationlayermode-composite.h create mode 100644 app/operations/layer-modes/gimpoperationlayermode.c create mode 100644 app/operations/layer-modes/gimpoperationlayermode.h create mode 100644 app/operations/layer-modes/gimpoperationmerge.c create mode 100644 app/operations/layer-modes/gimpoperationmerge.h create mode 100644 app/operations/layer-modes/gimpoperationnormal-sse2.c create mode 100644 app/operations/layer-modes/gimpoperationnormal-sse4.c create mode 100644 app/operations/layer-modes/gimpoperationnormal.c create mode 100644 app/operations/layer-modes/gimpoperationnormal.h create mode 100644 app/operations/layer-modes/gimpoperationpassthrough.c create mode 100644 app/operations/layer-modes/gimpoperationpassthrough.h create mode 100644 app/operations/layer-modes/gimpoperationreplace.c create mode 100644 app/operations/layer-modes/gimpoperationreplace.h create mode 100644 app/operations/layer-modes/gimpoperationsplit.c create mode 100644 app/operations/layer-modes/gimpoperationsplit.h create mode 100644 app/operations/operations-enums.c create mode 100644 app/operations/operations-enums.h create mode 100644 app/operations/operations-types.h create mode 100644 app/operations/tests/Makefile.am create mode 100644 app/operations/tests/Makefile.in create mode 100644 app/paint/Makefile.am create mode 100644 app/paint/Makefile.in create mode 100644 app/paint/gimp-paint.c create mode 100644 app/paint/gimp-paint.h create mode 100644 app/paint/gimpairbrush.c create mode 100644 app/paint/gimpairbrush.h create mode 100644 app/paint/gimpairbrushoptions.c create mode 100644 app/paint/gimpairbrushoptions.h create mode 100644 app/paint/gimpbrushcore-kernels.h create mode 100644 app/paint/gimpbrushcore-loops.cc create mode 100644 app/paint/gimpbrushcore-loops.h create mode 100644 app/paint/gimpbrushcore.c create mode 100644 app/paint/gimpbrushcore.h create mode 100644 app/paint/gimpclone.c create mode 100644 app/paint/gimpclone.h create mode 100644 app/paint/gimpcloneoptions.c create mode 100644 app/paint/gimpcloneoptions.h create mode 100644 app/paint/gimpconvolve.c create mode 100644 app/paint/gimpconvolve.h create mode 100644 app/paint/gimpconvolveoptions.c create mode 100644 app/paint/gimpconvolveoptions.h create mode 100644 app/paint/gimpdodgeburn.c create mode 100644 app/paint/gimpdodgeburn.h create mode 100644 app/paint/gimpdodgeburnoptions.c create mode 100644 app/paint/gimpdodgeburnoptions.h create mode 100644 app/paint/gimperaser.c create mode 100644 app/paint/gimperaser.h create mode 100644 app/paint/gimperaseroptions.c create mode 100644 app/paint/gimperaseroptions.h create mode 100644 app/paint/gimpheal.c create mode 100644 app/paint/gimpheal.h create mode 100644 app/paint/gimpink-blob.c create mode 100644 app/paint/gimpink-blob.h create mode 100644 app/paint/gimpink.c create mode 100644 app/paint/gimpink.h create mode 100644 app/paint/gimpinkoptions.c create mode 100644 app/paint/gimpinkoptions.h create mode 100644 app/paint/gimpinkundo.c create mode 100644 app/paint/gimpinkundo.h create mode 100644 app/paint/gimpmybrushcore.c create mode 100644 app/paint/gimpmybrushcore.h create mode 100644 app/paint/gimpmybrushoptions.c create mode 100644 app/paint/gimpmybrushoptions.h create mode 100644 app/paint/gimpmybrushsurface.c create mode 100644 app/paint/gimpmybrushsurface.h create mode 100644 app/paint/gimppaintbrush.c create mode 100644 app/paint/gimppaintbrush.h create mode 100644 app/paint/gimppaintcore-loops.cc create mode 100644 app/paint/gimppaintcore-loops.h create mode 100644 app/paint/gimppaintcore-stroke.c create mode 100644 app/paint/gimppaintcore-stroke.h create mode 100644 app/paint/gimppaintcore.c create mode 100644 app/paint/gimppaintcore.h create mode 100644 app/paint/gimppaintcoreundo.c create mode 100644 app/paint/gimppaintcoreundo.h create mode 100644 app/paint/gimppaintoptions.c create mode 100644 app/paint/gimppaintoptions.h create mode 100644 app/paint/gimppencil.c create mode 100644 app/paint/gimppencil.h create mode 100644 app/paint/gimppenciloptions.c create mode 100644 app/paint/gimppenciloptions.h create mode 100644 app/paint/gimpperspectiveclone.c create mode 100644 app/paint/gimpperspectiveclone.h create mode 100644 app/paint/gimpperspectivecloneoptions.c create mode 100644 app/paint/gimpperspectivecloneoptions.h create mode 100644 app/paint/gimpsmudge.c create mode 100644 app/paint/gimpsmudge.h create mode 100644 app/paint/gimpsmudgeoptions.c create mode 100644 app/paint/gimpsmudgeoptions.h create mode 100644 app/paint/gimpsourcecore.c create mode 100644 app/paint/gimpsourcecore.h create mode 100644 app/paint/gimpsourceoptions.c create mode 100644 app/paint/gimpsourceoptions.h create mode 100644 app/paint/paint-enums.c create mode 100644 app/paint/paint-enums.h create mode 100644 app/paint/paint-types.h create mode 100644 app/pdb/Makefile.am create mode 100644 app/pdb/Makefile.in create mode 100644 app/pdb/README create mode 100644 app/pdb/brush-cmds.c create mode 100644 app/pdb/brush-select-cmds.c create mode 100644 app/pdb/brushes-cmds.c create mode 100644 app/pdb/buffer-cmds.c create mode 100644 app/pdb/channel-cmds.c create mode 100644 app/pdb/color-cmds.c create mode 100644 app/pdb/context-cmds.c create mode 100644 app/pdb/debug-cmds.c create mode 100644 app/pdb/display-cmds.c create mode 100644 app/pdb/drawable-cmds.c create mode 100644 app/pdb/drawable-color-cmds.c create mode 100644 app/pdb/drawable-edit-cmds.c create mode 100644 app/pdb/drawable-transform-cmds.c create mode 100644 app/pdb/dynamics-cmds.c create mode 100644 app/pdb/edit-cmds.c create mode 100644 app/pdb/fileops-cmds.c create mode 100644 app/pdb/floating-sel-cmds.c create mode 100644 app/pdb/font-select-cmds.c create mode 100644 app/pdb/fonts-cmds.c create mode 100644 app/pdb/gimp-cmds.c create mode 100644 app/pdb/gimp-pdb-compat.c create mode 100644 app/pdb/gimp-pdb-compat.h create mode 100644 app/pdb/gimppdb-query.c create mode 100644 app/pdb/gimppdb-query.h create mode 100644 app/pdb/gimppdb-utils.c create mode 100644 app/pdb/gimppdb-utils.h create mode 100644 app/pdb/gimppdb.c create mode 100644 app/pdb/gimppdb.h create mode 100644 app/pdb/gimppdbcontext.c create mode 100644 app/pdb/gimppdbcontext.h create mode 100644 app/pdb/gimppdberror.c create mode 100644 app/pdb/gimppdberror.h create mode 100644 app/pdb/gimpprocedure.c create mode 100644 app/pdb/gimpprocedure.h create mode 100644 app/pdb/gimprc-cmds.c create mode 100644 app/pdb/gradient-cmds.c create mode 100644 app/pdb/gradient-select-cmds.c create mode 100644 app/pdb/gradients-cmds.c create mode 100644 app/pdb/help-cmds.c create mode 100644 app/pdb/image-cmds.c create mode 100644 app/pdb/image-color-profile-cmds.c create mode 100644 app/pdb/image-convert-cmds.c create mode 100644 app/pdb/image-grid-cmds.c create mode 100644 app/pdb/image-guides-cmds.c create mode 100644 app/pdb/image-sample-points-cmds.c create mode 100644 app/pdb/image-select-cmds.c create mode 100644 app/pdb/image-transform-cmds.c create mode 100644 app/pdb/image-undo-cmds.c create mode 100644 app/pdb/internal-procs.c create mode 100644 app/pdb/internal-procs.h create mode 100644 app/pdb/item-cmds.c create mode 100644 app/pdb/item-transform-cmds.c create mode 100644 app/pdb/layer-cmds.c create mode 100644 app/pdb/message-cmds.c create mode 100644 app/pdb/paint-tools-cmds.c create mode 100644 app/pdb/palette-cmds.c create mode 100644 app/pdb/palette-select-cmds.c create mode 100644 app/pdb/palettes-cmds.c create mode 100644 app/pdb/paths-cmds.c create mode 100644 app/pdb/pattern-cmds.c create mode 100644 app/pdb/pattern-select-cmds.c create mode 100644 app/pdb/patterns-cmds.c create mode 100644 app/pdb/pdb-types.h create mode 100644 app/pdb/plug-in-cmds.c create mode 100644 app/pdb/plug-in-compat-cmds.c create mode 100644 app/pdb/procedural-db-cmds.c create mode 100644 app/pdb/progress-cmds.c create mode 100644 app/pdb/selection-cmds.c create mode 100644 app/pdb/selection-tools-cmds.c create mode 100644 app/pdb/text-layer-cmds.c create mode 100644 app/pdb/text-tool-cmds.c create mode 100644 app/pdb/transform-tools-cmds.c create mode 100644 app/pdb/unit-cmds.c create mode 100644 app/pdb/vectors-cmds.c create mode 100644 app/plug-in/Makefile.am create mode 100644 app/plug-in/Makefile.in create mode 100644 app/plug-in/gimpenvirontable.c create mode 100644 app/plug-in/gimpenvirontable.h create mode 100644 app/plug-in/gimpinterpreterdb.c create mode 100644 app/plug-in/gimpinterpreterdb.h create mode 100644 app/plug-in/gimpplugin-cleanup.c create mode 100644 app/plug-in/gimpplugin-cleanup.h create mode 100644 app/plug-in/gimpplugin-context.c create mode 100644 app/plug-in/gimpplugin-context.h create mode 100644 app/plug-in/gimpplugin-message.c create mode 100644 app/plug-in/gimpplugin-message.h create mode 100644 app/plug-in/gimpplugin-progress.c create mode 100644 app/plug-in/gimpplugin-progress.h create mode 100644 app/plug-in/gimpplugin.c create mode 100644 app/plug-in/gimpplugin.h create mode 100644 app/plug-in/gimpplugindebug.c create mode 100644 app/plug-in/gimpplugindebug.h create mode 100644 app/plug-in/gimpplugindef.c create mode 100644 app/plug-in/gimpplugindef.h create mode 100644 app/plug-in/gimppluginerror.c create mode 100644 app/plug-in/gimppluginerror.h create mode 100644 app/plug-in/gimppluginmanager-call.c create mode 100644 app/plug-in/gimppluginmanager-call.h create mode 100644 app/plug-in/gimppluginmanager-data.c create mode 100644 app/plug-in/gimppluginmanager-data.h create mode 100644 app/plug-in/gimppluginmanager-file-procedure.c create mode 100644 app/plug-in/gimppluginmanager-file-procedure.h create mode 100644 app/plug-in/gimppluginmanager-file.c create mode 100644 app/plug-in/gimppluginmanager-file.h create mode 100644 app/plug-in/gimppluginmanager-help-domain.c create mode 100644 app/plug-in/gimppluginmanager-help-domain.h create mode 100644 app/plug-in/gimppluginmanager-locale-domain.c create mode 100644 app/plug-in/gimppluginmanager-locale-domain.h create mode 100644 app/plug-in/gimppluginmanager-menu-branch.c create mode 100644 app/plug-in/gimppluginmanager-menu-branch.h create mode 100644 app/plug-in/gimppluginmanager-query.c create mode 100644 app/plug-in/gimppluginmanager-query.h create mode 100644 app/plug-in/gimppluginmanager-restore.c create mode 100644 app/plug-in/gimppluginmanager-restore.h create mode 100644 app/plug-in/gimppluginmanager.c create mode 100644 app/plug-in/gimppluginmanager.h create mode 100644 app/plug-in/gimppluginprocedure.c create mode 100644 app/plug-in/gimppluginprocedure.h create mode 100644 app/plug-in/gimppluginprocframe.c create mode 100644 app/plug-in/gimppluginprocframe.h create mode 100644 app/plug-in/gimppluginshm.c create mode 100644 app/plug-in/gimppluginshm.h create mode 100644 app/plug-in/gimptemporaryprocedure.c create mode 100644 app/plug-in/gimptemporaryprocedure.h create mode 100644 app/plug-in/plug-in-enums.c create mode 100644 app/plug-in/plug-in-enums.h create mode 100644 app/plug-in/plug-in-menu-path.c create mode 100644 app/plug-in/plug-in-menu-path.h create mode 100644 app/plug-in/plug-in-params.c create mode 100644 app/plug-in/plug-in-params.h create mode 100644 app/plug-in/plug-in-rc.c create mode 100644 app/plug-in/plug-in-rc.h create mode 100644 app/plug-in/plug-in-types.h create mode 100644 app/propgui/Makefile.am create mode 100644 app/propgui/Makefile.in create mode 100644 app/propgui/gimppropgui-channel-mixer.c create mode 100644 app/propgui/gimppropgui-channel-mixer.h create mode 100644 app/propgui/gimppropgui-color-balance.c create mode 100644 app/propgui/gimppropgui-color-balance.h create mode 100644 app/propgui/gimppropgui-color-rotate.c create mode 100644 app/propgui/gimppropgui-color-rotate.h create mode 100644 app/propgui/gimppropgui-color-to-alpha.c create mode 100644 app/propgui/gimppropgui-color-to-alpha.h create mode 100644 app/propgui/gimppropgui-convolution-matrix.c create mode 100644 app/propgui/gimppropgui-convolution-matrix.h create mode 100644 app/propgui/gimppropgui-diffraction-patterns.c create mode 100644 app/propgui/gimppropgui-diffraction-patterns.h create mode 100644 app/propgui/gimppropgui-eval.c create mode 100644 app/propgui/gimppropgui-eval.h create mode 100644 app/propgui/gimppropgui-focus-blur.c create mode 100644 app/propgui/gimppropgui-focus-blur.h create mode 100644 app/propgui/gimppropgui-generic.c create mode 100644 app/propgui/gimppropgui-generic.h create mode 100644 app/propgui/gimppropgui-hue-saturation.c create mode 100644 app/propgui/gimppropgui-hue-saturation.h create mode 100644 app/propgui/gimppropgui-motion-blur-circular.c create mode 100644 app/propgui/gimppropgui-motion-blur-circular.h create mode 100644 app/propgui/gimppropgui-motion-blur-linear.c create mode 100644 app/propgui/gimppropgui-motion-blur-linear.h create mode 100644 app/propgui/gimppropgui-motion-blur-zoom.c create mode 100644 app/propgui/gimppropgui-motion-blur-zoom.h create mode 100644 app/propgui/gimppropgui-newsprint.c create mode 100644 app/propgui/gimppropgui-newsprint.h create mode 100644 app/propgui/gimppropgui-panorama-projection.c create mode 100644 app/propgui/gimppropgui-panorama-projection.h create mode 100644 app/propgui/gimppropgui-recursive-transform.c create mode 100644 app/propgui/gimppropgui-recursive-transform.h create mode 100644 app/propgui/gimppropgui-shadows-highlights.c create mode 100644 app/propgui/gimppropgui-shadows-highlights.h create mode 100644 app/propgui/gimppropgui-spiral.c create mode 100644 app/propgui/gimppropgui-spiral.h create mode 100644 app/propgui/gimppropgui-supernova.c create mode 100644 app/propgui/gimppropgui-supernova.h create mode 100644 app/propgui/gimppropgui-utils.c create mode 100644 app/propgui/gimppropgui-utils.h create mode 100644 app/propgui/gimppropgui-vignette.c create mode 100644 app/propgui/gimppropgui-vignette.h create mode 100644 app/propgui/gimppropgui.c create mode 100644 app/propgui/gimppropgui.h create mode 100644 app/propgui/propgui-types.h create mode 100644 app/sanity.c create mode 100644 app/sanity.h create mode 100644 app/signals.c create mode 100644 app/signals.h create mode 100644 app/tests.c create mode 100644 app/tests.h create mode 100644 app/tests/Makefile.am create mode 100644 app/tests/Makefile.in create mode 100644 app/tests/files/Makefile.am create mode 100644 app/tests/files/Makefile.in create mode 100644 app/tests/files/gimp-2-6-file.xcf create mode 100644 app/tests/gimp-app-test-utils.c create mode 100644 app/tests/gimp-app-test-utils.h create mode 100644 app/tests/gimp-test-session-utils.c create mode 100644 app/tests/gimp-test-session-utils.h create mode 100644 app/tests/gimpdir-empty/Makefile.am create mode 100644 app/tests/gimpdir-empty/Makefile.in create mode 100644 app/tests/gimpdir-empty/brushes/Makefile.am create mode 100644 app/tests/gimpdir-empty/brushes/Makefile.in create mode 100644 app/tests/gimpdir-empty/gradients/Makefile.am create mode 100644 app/tests/gimpdir-empty/gradients/Makefile.in create mode 100644 app/tests/gimpdir-empty/patterns/Makefile.am create mode 100644 app/tests/gimpdir-empty/patterns/Makefile.in create mode 100644 app/tests/gimpdir-empty/tags.xml create mode 100644 app/tests/gimpdir/Makefile.am create mode 100644 app/tests/gimpdir/Makefile.in create mode 100644 app/tests/gimpdir/brushes/Makefile.am create mode 100644 app/tests/gimpdir/brushes/Makefile.in create mode 100644 app/tests/gimpdir/dockrc create mode 100644 app/tests/gimpdir/dockrc-2-8 create mode 100644 app/tests/gimpdir/dockrc-expected create mode 100644 app/tests/gimpdir/gradients/Makefile.am create mode 100644 app/tests/gimpdir/gradients/Makefile.in create mode 100644 app/tests/gimpdir/patterns/Makefile.am create mode 100644 app/tests/gimpdir/patterns/Makefile.in create mode 100644 app/tests/gimpdir/sessionrc create mode 100644 app/tests/gimpdir/sessionrc-2-8-multi-window create mode 100644 app/tests/gimpdir/sessionrc-2-8-single-window create mode 100644 app/tests/gimpdir/sessionrc-expected-multi-window create mode 100644 app/tests/gimpdir/sessionrc-expected-single-window create mode 100644 app/tests/gimpdir/tags.xml create mode 100644 app/tests/test-core.c create mode 100644 app/tests/test-gimpidtable.c create mode 100644 app/tests/test-save-and-export.c create mode 100644 app/tests/test-session-2-8-compatibility-multi-window.c create mode 100644 app/tests/test-session-2-8-compatibility-single-window.c create mode 100644 app/tests/test-single-window-mode.c create mode 100644 app/tests/test-tools.c create mode 100644 app/tests/test-ui.c create mode 100644 app/tests/test-xcf.c create mode 100644 app/text/Makefile.am create mode 100644 app/text/Makefile.in create mode 100644 app/text/gimpfont.c create mode 100644 app/text/gimpfont.h create mode 100644 app/text/gimpfontfactory.c create mode 100644 app/text/gimpfontfactory.h create mode 100644 app/text/gimptext-compat.c create mode 100644 app/text/gimptext-compat.h create mode 100644 app/text/gimptext-parasite.c create mode 100644 app/text/gimptext-parasite.h create mode 100644 app/text/gimptext-vectors.c create mode 100644 app/text/gimptext-vectors.h create mode 100644 app/text/gimptext-xlfd.c create mode 100644 app/text/gimptext-xlfd.h create mode 100644 app/text/gimptext.c create mode 100644 app/text/gimptext.h create mode 100644 app/text/gimptextlayer-transform.c create mode 100644 app/text/gimptextlayer-transform.h create mode 100644 app/text/gimptextlayer-xcf.c create mode 100644 app/text/gimptextlayer-xcf.h create mode 100644 app/text/gimptextlayer.c create mode 100644 app/text/gimptextlayer.h create mode 100644 app/text/gimptextlayout-render.c create mode 100644 app/text/gimptextlayout-render.h create mode 100644 app/text/gimptextlayout.c create mode 100644 app/text/gimptextlayout.h create mode 100644 app/text/gimptextundo.c create mode 100644 app/text/gimptextundo.h create mode 100644 app/text/text-enums.c create mode 100644 app/text/text-enums.h create mode 100644 app/text/text-types.h create mode 100644 app/tools/Makefile.am create mode 100644 app/tools/Makefile.in create mode 100644 app/tools/gimp-tool-options-manager.c create mode 100644 app/tools/gimp-tool-options-manager.h create mode 100644 app/tools/gimp-tools.c create mode 100644 app/tools/gimp-tools.h create mode 100644 app/tools/gimpairbrushtool.c create mode 100644 app/tools/gimpairbrushtool.h create mode 100644 app/tools/gimpalignoptions.c create mode 100644 app/tools/gimpalignoptions.h create mode 100644 app/tools/gimpaligntool.c create mode 100644 app/tools/gimpaligntool.h create mode 100644 app/tools/gimpbrightnesscontrasttool.c create mode 100644 app/tools/gimpbrightnesscontrasttool.h create mode 100644 app/tools/gimpbrushtool.c create mode 100644 app/tools/gimpbrushtool.h create mode 100644 app/tools/gimpbucketfilloptions.c create mode 100644 app/tools/gimpbucketfilloptions.h create mode 100644 app/tools/gimpbucketfilltool.c create mode 100644 app/tools/gimpbucketfilltool.h create mode 100644 app/tools/gimpbycolorselecttool.c create mode 100644 app/tools/gimpbycolorselecttool.h create mode 100644 app/tools/gimpcageoptions.c create mode 100644 app/tools/gimpcageoptions.h create mode 100644 app/tools/gimpcagetool.c create mode 100644 app/tools/gimpcagetool.h create mode 100644 app/tools/gimpcloneoptions-gui.c create mode 100644 app/tools/gimpcloneoptions-gui.h create mode 100644 app/tools/gimpclonetool.c create mode 100644 app/tools/gimpclonetool.h create mode 100644 app/tools/gimpcoloroptions.c create mode 100644 app/tools/gimpcoloroptions.h create mode 100644 app/tools/gimpcolorpickeroptions.c create mode 100644 app/tools/gimpcolorpickeroptions.h create mode 100644 app/tools/gimpcolorpickertool.c create mode 100644 app/tools/gimpcolorpickertool.h create mode 100644 app/tools/gimpcolortool.c create mode 100644 app/tools/gimpcolortool.h create mode 100644 app/tools/gimpconvolvetool.c create mode 100644 app/tools/gimpconvolvetool.h create mode 100644 app/tools/gimpcropoptions.c create mode 100644 app/tools/gimpcropoptions.h create mode 100644 app/tools/gimpcroptool.c create mode 100644 app/tools/gimpcroptool.h create mode 100644 app/tools/gimpcurvestool.c create mode 100644 app/tools/gimpcurvestool.h create mode 100644 app/tools/gimpdodgeburntool.c create mode 100644 app/tools/gimpdodgeburntool.h create mode 100644 app/tools/gimpdrawtool.c create mode 100644 app/tools/gimpdrawtool.h create mode 100644 app/tools/gimpeditselectiontool.c create mode 100644 app/tools/gimpeditselectiontool.h create mode 100644 app/tools/gimpellipseselecttool.c create mode 100644 app/tools/gimpellipseselecttool.h create mode 100644 app/tools/gimperasertool.c create mode 100644 app/tools/gimperasertool.h create mode 100644 app/tools/gimpfilteroptions.c create mode 100644 app/tools/gimpfilteroptions.h create mode 100644 app/tools/gimpfiltertool-settings.c create mode 100644 app/tools/gimpfiltertool-settings.h create mode 100644 app/tools/gimpfiltertool-widgets.c create mode 100644 app/tools/gimpfiltertool-widgets.h create mode 100644 app/tools/gimpfiltertool.c create mode 100644 app/tools/gimpfiltertool.h create mode 100644 app/tools/gimpflipoptions.c create mode 100644 app/tools/gimpflipoptions.h create mode 100644 app/tools/gimpfliptool.c create mode 100644 app/tools/gimpfliptool.h create mode 100644 app/tools/gimpforegroundselectoptions.c create mode 100644 app/tools/gimpforegroundselectoptions.h create mode 100644 app/tools/gimpforegroundselecttool.c create mode 100644 app/tools/gimpforegroundselecttool.h create mode 100644 app/tools/gimpforegroundselecttoolundo.c create mode 100644 app/tools/gimpforegroundselecttoolundo.h create mode 100644 app/tools/gimpfreeselecttool.c create mode 100644 app/tools/gimpfreeselecttool.h create mode 100644 app/tools/gimpfuzzyselecttool.c create mode 100644 app/tools/gimpfuzzyselecttool.h create mode 100644 app/tools/gimpgegltool.c create mode 100644 app/tools/gimpgegltool.h create mode 100644 app/tools/gimpgenerictransformtool.c create mode 100644 app/tools/gimpgenerictransformtool.h create mode 100644 app/tools/gimpgradientoptions.c create mode 100644 app/tools/gimpgradientoptions.h create mode 100644 app/tools/gimpgradienttool-editor.c create mode 100644 app/tools/gimpgradienttool-editor.h create mode 100644 app/tools/gimpgradienttool.c create mode 100644 app/tools/gimpgradienttool.h create mode 100644 app/tools/gimpguidetool.c create mode 100644 app/tools/gimpguidetool.h create mode 100644 app/tools/gimphandletransformoptions.c create mode 100644 app/tools/gimphandletransformoptions.h create mode 100644 app/tools/gimphandletransformtool.c create mode 100644 app/tools/gimphandletransformtool.h create mode 100644 app/tools/gimphealtool.c create mode 100644 app/tools/gimphealtool.h create mode 100644 app/tools/gimphistogramoptions.c create mode 100644 app/tools/gimphistogramoptions.h create mode 100644 app/tools/gimpinkoptions-gui.c create mode 100644 app/tools/gimpinkoptions-gui.h create mode 100644 app/tools/gimpinktool.c create mode 100644 app/tools/gimpinktool.h create mode 100644 app/tools/gimpiscissorsoptions.c create mode 100644 app/tools/gimpiscissorsoptions.h create mode 100644 app/tools/gimpiscissorstool.c create mode 100644 app/tools/gimpiscissorstool.h create mode 100644 app/tools/gimplevelstool.c create mode 100644 app/tools/gimplevelstool.h create mode 100644 app/tools/gimpmagnifyoptions.c create mode 100644 app/tools/gimpmagnifyoptions.h create mode 100644 app/tools/gimpmagnifytool.c create mode 100644 app/tools/gimpmagnifytool.h create mode 100644 app/tools/gimpmeasureoptions.c create mode 100644 app/tools/gimpmeasureoptions.h create mode 100644 app/tools/gimpmeasuretool.c create mode 100644 app/tools/gimpmeasuretool.h create mode 100644 app/tools/gimpmoveoptions.c create mode 100644 app/tools/gimpmoveoptions.h create mode 100644 app/tools/gimpmovetool.c create mode 100644 app/tools/gimpmovetool.h create mode 100644 app/tools/gimpmybrushoptions-gui.c create mode 100644 app/tools/gimpmybrushoptions-gui.h create mode 100644 app/tools/gimpmybrushtool.c create mode 100644 app/tools/gimpmybrushtool.h create mode 100644 app/tools/gimpnpointdeformationoptions.c create mode 100644 app/tools/gimpnpointdeformationoptions.h create mode 100644 app/tools/gimpnpointdeformationtool.c create mode 100644 app/tools/gimpnpointdeformationtool.h create mode 100644 app/tools/gimpoffsettool.c create mode 100644 app/tools/gimpoffsettool.h create mode 100644 app/tools/gimpoperationtool.c create mode 100644 app/tools/gimpoperationtool.h create mode 100644 app/tools/gimppaintbrushtool.c create mode 100644 app/tools/gimppaintbrushtool.h create mode 100644 app/tools/gimppaintoptions-gui.c create mode 100644 app/tools/gimppaintoptions-gui.h create mode 100644 app/tools/gimppainttool-paint.c create mode 100644 app/tools/gimppainttool-paint.h create mode 100644 app/tools/gimppainttool.c create mode 100644 app/tools/gimppainttool.h create mode 100644 app/tools/gimppenciltool.c create mode 100644 app/tools/gimppenciltool.h create mode 100644 app/tools/gimpperspectiveclonetool.c create mode 100644 app/tools/gimpperspectiveclonetool.h create mode 100644 app/tools/gimpperspectivetool.c create mode 100644 app/tools/gimpperspectivetool.h create mode 100644 app/tools/gimppolygonselecttool.c create mode 100644 app/tools/gimppolygonselecttool.h create mode 100644 app/tools/gimprectangleoptions.c create mode 100644 app/tools/gimprectangleoptions.h create mode 100644 app/tools/gimprectangleselectoptions.c create mode 100644 app/tools/gimprectangleselectoptions.h create mode 100644 app/tools/gimprectangleselecttool.c create mode 100644 app/tools/gimprectangleselecttool.h create mode 100644 app/tools/gimpregionselectoptions.c create mode 100644 app/tools/gimpregionselectoptions.h create mode 100644 app/tools/gimpregionselecttool.c create mode 100644 app/tools/gimpregionselecttool.h create mode 100644 app/tools/gimprotatetool.c create mode 100644 app/tools/gimprotatetool.h create mode 100644 app/tools/gimpsamplepointtool.c create mode 100644 app/tools/gimpsamplepointtool.h create mode 100644 app/tools/gimpscaletool.c create mode 100644 app/tools/gimpscaletool.h create mode 100644 app/tools/gimpseamlesscloneoptions.c create mode 100644 app/tools/gimpseamlesscloneoptions.h create mode 100644 app/tools/gimpseamlessclonetool.c create mode 100644 app/tools/gimpseamlessclonetool.h create mode 100644 app/tools/gimpselectionoptions.c create mode 100644 app/tools/gimpselectionoptions.h create mode 100644 app/tools/gimpselectiontool.c create mode 100644 app/tools/gimpselectiontool.h create mode 100644 app/tools/gimpsheartool.c create mode 100644 app/tools/gimpsheartool.h create mode 100644 app/tools/gimpsmudgetool.c create mode 100644 app/tools/gimpsmudgetool.h create mode 100644 app/tools/gimpsourcetool.c create mode 100644 app/tools/gimpsourcetool.h create mode 100644 app/tools/gimptextoptions.c create mode 100644 app/tools/gimptextoptions.h create mode 100644 app/tools/gimptexttool-editor.c create mode 100644 app/tools/gimptexttool-editor.h create mode 100644 app/tools/gimptexttool.c create mode 100644 app/tools/gimptexttool.h create mode 100644 app/tools/gimpthresholdtool.c create mode 100644 app/tools/gimpthresholdtool.h create mode 100644 app/tools/gimptilehandleriscissors.c create mode 100644 app/tools/gimptilehandleriscissors.h create mode 100644 app/tools/gimptool-progress.c create mode 100644 app/tools/gimptool-progress.h create mode 100644 app/tools/gimptool.c create mode 100644 app/tools/gimptool.h create mode 100644 app/tools/gimptoolcontrol.c create mode 100644 app/tools/gimptoolcontrol.h create mode 100644 app/tools/gimptooloptions-gui.c create mode 100644 app/tools/gimptooloptions-gui.h create mode 100644 app/tools/gimptools-utils.c create mode 100644 app/tools/gimptools-utils.h create mode 100644 app/tools/gimptransform3doptions.c create mode 100644 app/tools/gimptransform3doptions.h create mode 100644 app/tools/gimptransform3dtool.c create mode 100644 app/tools/gimptransform3dtool.h create mode 100644 app/tools/gimptransformgridoptions.c create mode 100644 app/tools/gimptransformgridoptions.h create mode 100644 app/tools/gimptransformgridtool.c create mode 100644 app/tools/gimptransformgridtool.h create mode 100644 app/tools/gimptransformgridtoolundo.c create mode 100644 app/tools/gimptransformgridtoolundo.h create mode 100644 app/tools/gimptransformoptions.c create mode 100644 app/tools/gimptransformoptions.h create mode 100644 app/tools/gimptransformtool.c create mode 100644 app/tools/gimptransformtool.h create mode 100644 app/tools/gimpunifiedtransformtool.c create mode 100644 app/tools/gimpunifiedtransformtool.h create mode 100644 app/tools/gimpvectoroptions.c create mode 100644 app/tools/gimpvectoroptions.h create mode 100644 app/tools/gimpvectortool.c create mode 100644 app/tools/gimpvectortool.h create mode 100644 app/tools/gimpwarpoptions.c create mode 100644 app/tools/gimpwarpoptions.h create mode 100644 app/tools/gimpwarptool.c create mode 100644 app/tools/gimpwarptool.h create mode 100644 app/tools/tool_manager.c create mode 100644 app/tools/tool_manager.h create mode 100644 app/tools/tools-enums.c create mode 100644 app/tools/tools-enums.h create mode 100644 app/tools/tools-types.h create mode 100644 app/unique.c create mode 100644 app/unique.h create mode 100644 app/vectors/Makefile.am create mode 100644 app/vectors/Makefile.in create mode 100644 app/vectors/gimpanchor.c create mode 100644 app/vectors/gimpanchor.h create mode 100644 app/vectors/gimpbezierstroke.c create mode 100644 app/vectors/gimpbezierstroke.h create mode 100644 app/vectors/gimpstroke-new.c create mode 100644 app/vectors/gimpstroke-new.h create mode 100644 app/vectors/gimpstroke.c create mode 100644 app/vectors/gimpstroke.h create mode 100644 app/vectors/gimpvectors-compat.c create mode 100644 app/vectors/gimpvectors-compat.h create mode 100644 app/vectors/gimpvectors-export.c create mode 100644 app/vectors/gimpvectors-export.h create mode 100644 app/vectors/gimpvectors-import.c create mode 100644 app/vectors/gimpvectors-import.h create mode 100644 app/vectors/gimpvectors-preview.c create mode 100644 app/vectors/gimpvectors-preview.h create mode 100644 app/vectors/gimpvectors-warp.c create mode 100644 app/vectors/gimpvectors-warp.h create mode 100644 app/vectors/gimpvectors.c create mode 100644 app/vectors/gimpvectors.h create mode 100644 app/vectors/gimpvectorsmodundo.c create mode 100644 app/vectors/gimpvectorsmodundo.h create mode 100644 app/vectors/gimpvectorspropundo.c create mode 100644 app/vectors/gimpvectorspropundo.h create mode 100644 app/vectors/gimpvectorsundo.c create mode 100644 app/vectors/gimpvectorsundo.h create mode 100644 app/vectors/vectors-enums.h create mode 100644 app/vectors/vectors-types.h create mode 100644 app/widgets/Makefile.am create mode 100644 app/widgets/Makefile.in create mode 100644 app/widgets/gimpaccellabel.c create mode 100644 app/widgets/gimpaccellabel.h create mode 100644 app/widgets/gimpaction-history.c create mode 100644 app/widgets/gimpaction-history.h create mode 100644 app/widgets/gimpaction.c create mode 100644 app/widgets/gimpaction.h create mode 100644 app/widgets/gimpactioneditor.c create mode 100644 app/widgets/gimpactioneditor.h create mode 100644 app/widgets/gimpactionfactory.c create mode 100644 app/widgets/gimpactionfactory.h create mode 100644 app/widgets/gimpactiongroup.c create mode 100644 app/widgets/gimpactiongroup.h create mode 100644 app/widgets/gimpactionimpl.c create mode 100644 app/widgets/gimpactionimpl.h create mode 100644 app/widgets/gimpactionview.c create mode 100644 app/widgets/gimpactionview.h create mode 100644 app/widgets/gimpblobeditor.c create mode 100644 app/widgets/gimpblobeditor.h create mode 100644 app/widgets/gimpbrusheditor.c create mode 100644 app/widgets/gimpbrusheditor.h create mode 100644 app/widgets/gimpbrushfactoryview.c create mode 100644 app/widgets/gimpbrushfactoryview.h create mode 100644 app/widgets/gimpbrushselect.c create mode 100644 app/widgets/gimpbrushselect.h create mode 100644 app/widgets/gimpbuffersourcebox.c create mode 100644 app/widgets/gimpbuffersourcebox.h create mode 100644 app/widgets/gimpbufferview.c create mode 100644 app/widgets/gimpbufferview.h create mode 100644 app/widgets/gimpcairo-wilber.c create mode 100644 app/widgets/gimpcairo-wilber.h create mode 100644 app/widgets/gimpcellrendererbutton.c create mode 100644 app/widgets/gimpcellrendererbutton.h create mode 100644 app/widgets/gimpcellrendererdashes.c create mode 100644 app/widgets/gimpcellrendererdashes.h create mode 100644 app/widgets/gimpcellrendererviewable.c create mode 100644 app/widgets/gimpcellrendererviewable.h create mode 100644 app/widgets/gimpchanneltreeview.c create mode 100644 app/widgets/gimpchanneltreeview.h create mode 100644 app/widgets/gimpcircle.c create mode 100644 app/widgets/gimpcircle.h create mode 100644 app/widgets/gimpclipboard.c create mode 100644 app/widgets/gimpclipboard.h create mode 100644 app/widgets/gimpcolorbar.c create mode 100644 app/widgets/gimpcolorbar.h create mode 100644 app/widgets/gimpcolordialog.c create mode 100644 app/widgets/gimpcolordialog.h create mode 100644 app/widgets/gimpcolordisplayeditor.c create mode 100644 app/widgets/gimpcolordisplayeditor.h create mode 100644 app/widgets/gimpcoloreditor.c create mode 100644 app/widgets/gimpcoloreditor.h create mode 100644 app/widgets/gimpcolorframe.c create mode 100644 app/widgets/gimpcolorframe.h create mode 100644 app/widgets/gimpcolorhistory.c create mode 100644 app/widgets/gimpcolorhistory.h create mode 100644 app/widgets/gimpcolormapeditor.c create mode 100644 app/widgets/gimpcolormapeditor.h create mode 100644 app/widgets/gimpcolorpanel.c create mode 100644 app/widgets/gimpcolorpanel.h create mode 100644 app/widgets/gimpcolorselectorpalette.c create mode 100644 app/widgets/gimpcolorselectorpalette.h create mode 100644 app/widgets/gimpcombotagentry.c create mode 100644 app/widgets/gimpcombotagentry.h create mode 100644 app/widgets/gimpcomponenteditor.c create mode 100644 app/widgets/gimpcomponenteditor.h create mode 100644 app/widgets/gimpcompressioncombobox.c create mode 100644 app/widgets/gimpcompressioncombobox.h create mode 100644 app/widgets/gimpcontainerbox.c create mode 100644 app/widgets/gimpcontainerbox.h create mode 100644 app/widgets/gimpcontainercombobox.c create mode 100644 app/widgets/gimpcontainercombobox.h create mode 100644 app/widgets/gimpcontainereditor.c create mode 100644 app/widgets/gimpcontainereditor.h create mode 100644 app/widgets/gimpcontainerentry.c create mode 100644 app/widgets/gimpcontainerentry.h create mode 100644 app/widgets/gimpcontainergridview.c create mode 100644 app/widgets/gimpcontainergridview.h create mode 100644 app/widgets/gimpcontainericonview.c create mode 100644 app/widgets/gimpcontainericonview.h create mode 100644 app/widgets/gimpcontainerpopup.c create mode 100644 app/widgets/gimpcontainerpopup.h create mode 100644 app/widgets/gimpcontainertreestore.c create mode 100644 app/widgets/gimpcontainertreestore.h create mode 100644 app/widgets/gimpcontainertreeview-dnd.c create mode 100644 app/widgets/gimpcontainertreeview-dnd.h create mode 100644 app/widgets/gimpcontainertreeview-private.h create mode 100644 app/widgets/gimpcontainertreeview.c create mode 100644 app/widgets/gimpcontainertreeview.h create mode 100644 app/widgets/gimpcontainerview-utils.c create mode 100644 app/widgets/gimpcontainerview-utils.h create mode 100644 app/widgets/gimpcontainerview.c create mode 100644 app/widgets/gimpcontainerview.h create mode 100644 app/widgets/gimpcontrollereditor.c create mode 100644 app/widgets/gimpcontrollereditor.h create mode 100644 app/widgets/gimpcontrollerinfo.c create mode 100644 app/widgets/gimpcontrollerinfo.h create mode 100644 app/widgets/gimpcontrollerkeyboard.c create mode 100644 app/widgets/gimpcontrollerkeyboard.h create mode 100644 app/widgets/gimpcontrollerlist.c create mode 100644 app/widgets/gimpcontrollerlist.h create mode 100644 app/widgets/gimpcontrollermouse.c create mode 100644 app/widgets/gimpcontrollermouse.h create mode 100644 app/widgets/gimpcontrollers.c create mode 100644 app/widgets/gimpcontrollers.h create mode 100644 app/widgets/gimpcontrollerwheel.c create mode 100644 app/widgets/gimpcontrollerwheel.h create mode 100644 app/widgets/gimpcriticaldialog.c create mode 100644 app/widgets/gimpcriticaldialog.h create mode 100644 app/widgets/gimpcursor.c create mode 100644 app/widgets/gimpcursor.h create mode 100644 app/widgets/gimpcurveview.c create mode 100644 app/widgets/gimpcurveview.h create mode 100644 app/widgets/gimpdashboard.c create mode 100644 app/widgets/gimpdashboard.h create mode 100644 app/widgets/gimpdasheditor.c create mode 100644 app/widgets/gimpdasheditor.h create mode 100644 app/widgets/gimpdataeditor.c create mode 100644 app/widgets/gimpdataeditor.h create mode 100644 app/widgets/gimpdatafactoryview.c create mode 100644 app/widgets/gimpdatafactoryview.h create mode 100644 app/widgets/gimpdeviceeditor.c create mode 100644 app/widgets/gimpdeviceeditor.h create mode 100644 app/widgets/gimpdeviceinfo-coords.c create mode 100644 app/widgets/gimpdeviceinfo-coords.h create mode 100644 app/widgets/gimpdeviceinfo.c create mode 100644 app/widgets/gimpdeviceinfo.h create mode 100644 app/widgets/gimpdeviceinfoeditor.c create mode 100644 app/widgets/gimpdeviceinfoeditor.h create mode 100644 app/widgets/gimpdevicemanager.c create mode 100644 app/widgets/gimpdevicemanager.h create mode 100644 app/widgets/gimpdevices.c create mode 100644 app/widgets/gimpdevices.h create mode 100644 app/widgets/gimpdevicestatus.c create mode 100644 app/widgets/gimpdevicestatus.h create mode 100644 app/widgets/gimpdial.c create mode 100644 app/widgets/gimpdial.h create mode 100644 app/widgets/gimpdialogfactory.c create mode 100644 app/widgets/gimpdialogfactory.h create mode 100644 app/widgets/gimpdnd-xds.c create mode 100644 app/widgets/gimpdnd-xds.h create mode 100644 app/widgets/gimpdnd.c create mode 100644 app/widgets/gimpdnd.h create mode 100644 app/widgets/gimpdock.c create mode 100644 app/widgets/gimpdock.h create mode 100644 app/widgets/gimpdockable.c create mode 100644 app/widgets/gimpdockable.h create mode 100644 app/widgets/gimpdockbook.c create mode 100644 app/widgets/gimpdockbook.h create mode 100644 app/widgets/gimpdockcolumns.c create mode 100644 app/widgets/gimpdockcolumns.h create mode 100644 app/widgets/gimpdockcontainer.c create mode 100644 app/widgets/gimpdockcontainer.h create mode 100644 app/widgets/gimpdocked.c create mode 100644 app/widgets/gimpdocked.h create mode 100644 app/widgets/gimpdockwindow.c create mode 100644 app/widgets/gimpdockwindow.h create mode 100644 app/widgets/gimpdocumentview.c create mode 100644 app/widgets/gimpdocumentview.h create mode 100644 app/widgets/gimpdrawabletreeview.c create mode 100644 app/widgets/gimpdrawabletreeview.h create mode 100644 app/widgets/gimpdynamicseditor.c create mode 100644 app/widgets/gimpdynamicseditor.h create mode 100644 app/widgets/gimpdynamicsfactoryview.c create mode 100644 app/widgets/gimpdynamicsfactoryview.h create mode 100644 app/widgets/gimpdynamicsoutputeditor.c create mode 100644 app/widgets/gimpdynamicsoutputeditor.h create mode 100644 app/widgets/gimpeditor.c create mode 100644 app/widgets/gimpeditor.h create mode 100644 app/widgets/gimpenumaction.c create mode 100644 app/widgets/gimpenumaction.h create mode 100644 app/widgets/gimperrorconsole.c create mode 100644 app/widgets/gimperrorconsole.h create mode 100644 app/widgets/gimperrordialog.c create mode 100644 app/widgets/gimperrordialog.h create mode 100644 app/widgets/gimpexportdialog.c create mode 100644 app/widgets/gimpexportdialog.h create mode 100644 app/widgets/gimpfgbgeditor.c create mode 100644 app/widgets/gimpfgbgeditor.h create mode 100644 app/widgets/gimpfgbgview.c create mode 100644 app/widgets/gimpfgbgview.h create mode 100644 app/widgets/gimpfiledialog.c create mode 100644 app/widgets/gimpfiledialog.h create mode 100644 app/widgets/gimpfileprocview.c create mode 100644 app/widgets/gimpfileprocview.h create mode 100644 app/widgets/gimpfilleditor.c create mode 100644 app/widgets/gimpfilleditor.h create mode 100644 app/widgets/gimpfontfactoryview.c create mode 100644 app/widgets/gimpfontfactoryview.h create mode 100644 app/widgets/gimpfontselect.c create mode 100644 app/widgets/gimpfontselect.h create mode 100644 app/widgets/gimpgradienteditor.c create mode 100644 app/widgets/gimpgradienteditor.h create mode 100644 app/widgets/gimpgradientselect.c create mode 100644 app/widgets/gimpgradientselect.h create mode 100644 app/widgets/gimpgrideditor.c create mode 100644 app/widgets/gimpgrideditor.h create mode 100644 app/widgets/gimphandlebar.c create mode 100644 app/widgets/gimphandlebar.h create mode 100644 app/widgets/gimphelp-ids.h create mode 100644 app/widgets/gimphelp.c create mode 100644 app/widgets/gimphelp.h create mode 100644 app/widgets/gimphighlightablebutton.c create mode 100644 app/widgets/gimphighlightablebutton.h create mode 100644 app/widgets/gimphistogrambox.c create mode 100644 app/widgets/gimphistogrambox.h create mode 100644 app/widgets/gimphistogrameditor.c create mode 100644 app/widgets/gimphistogrameditor.h create mode 100644 app/widgets/gimphistogramview.c create mode 100644 app/widgets/gimphistogramview.h create mode 100644 app/widgets/gimpiconpicker.c create mode 100644 app/widgets/gimpiconpicker.h create mode 100644 app/widgets/gimpiconsizescale.c create mode 100644 app/widgets/gimpiconsizescale.h create mode 100644 app/widgets/gimpimagecommenteditor.c create mode 100644 app/widgets/gimpimagecommenteditor.h create mode 100644 app/widgets/gimpimageeditor.c create mode 100644 app/widgets/gimpimageeditor.h create mode 100644 app/widgets/gimpimageparasiteview.c create mode 100644 app/widgets/gimpimageparasiteview.h create mode 100644 app/widgets/gimpimageprofileview.c create mode 100644 app/widgets/gimpimageprofileview.h create mode 100644 app/widgets/gimpimagepropview.c create mode 100644 app/widgets/gimpimagepropview.h create mode 100644 app/widgets/gimpimageview.c create mode 100644 app/widgets/gimpimageview.h create mode 100644 app/widgets/gimpitemtreeview.c create mode 100644 app/widgets/gimpitemtreeview.h create mode 100644 app/widgets/gimplanguagecombobox.c create mode 100644 app/widgets/gimplanguagecombobox.h create mode 100644 app/widgets/gimplanguageentry.c create mode 100644 app/widgets/gimplanguageentry.h create mode 100644 app/widgets/gimplanguagestore-parser.c create mode 100644 app/widgets/gimplanguagestore-parser.h create mode 100644 app/widgets/gimplanguagestore.c create mode 100644 app/widgets/gimplanguagestore.h create mode 100644 app/widgets/gimplayermodebox.c create mode 100644 app/widgets/gimplayermodebox.h create mode 100644 app/widgets/gimplayermodecombobox.c create mode 100644 app/widgets/gimplayermodecombobox.h create mode 100644 app/widgets/gimplayertreeview.c create mode 100644 app/widgets/gimplayertreeview.h create mode 100644 app/widgets/gimpmenudock.c create mode 100644 app/widgets/gimpmenudock.h create mode 100644 app/widgets/gimpmenufactory.c create mode 100644 app/widgets/gimpmenufactory.h create mode 100644 app/widgets/gimpmessagebox.c create mode 100644 app/widgets/gimpmessagebox.h create mode 100644 app/widgets/gimpmessagedialog.c create mode 100644 app/widgets/gimpmessagedialog.h create mode 100644 app/widgets/gimpmeter.c create mode 100644 app/widgets/gimpmeter.h create mode 100644 app/widgets/gimpnavigationview.c create mode 100644 app/widgets/gimpnavigationview.h create mode 100644 app/widgets/gimpopendialog.c create mode 100644 app/widgets/gimpopendialog.h create mode 100644 app/widgets/gimpoverlaybox.c create mode 100644 app/widgets/gimpoverlaybox.h create mode 100644 app/widgets/gimpoverlaychild.c create mode 100644 app/widgets/gimpoverlaychild.h create mode 100644 app/widgets/gimpoverlaydialog.c create mode 100644 app/widgets/gimpoverlaydialog.h create mode 100644 app/widgets/gimpoverlayframe.c create mode 100644 app/widgets/gimpoverlayframe.h create mode 100644 app/widgets/gimppaletteeditor.c create mode 100644 app/widgets/gimppaletteeditor.h create mode 100644 app/widgets/gimppaletteselect.c create mode 100644 app/widgets/gimppaletteselect.h create mode 100644 app/widgets/gimppaletteview.c create mode 100644 app/widgets/gimppaletteview.h create mode 100644 app/widgets/gimppanedbox.c create mode 100644 app/widgets/gimppanedbox.h create mode 100644 app/widgets/gimppatternfactoryview.c create mode 100644 app/widgets/gimppatternfactoryview.h create mode 100644 app/widgets/gimppatternselect.c create mode 100644 app/widgets/gimppatternselect.h create mode 100644 app/widgets/gimppdbdialog.c create mode 100644 app/widgets/gimppdbdialog.h create mode 100644 app/widgets/gimppickablebutton.c create mode 100644 app/widgets/gimppickablebutton.h create mode 100644 app/widgets/gimppickablepopup.c create mode 100644 app/widgets/gimppickablepopup.h create mode 100644 app/widgets/gimppivotselector.c create mode 100644 app/widgets/gimppivotselector.h create mode 100644 app/widgets/gimppixbuf.c create mode 100644 app/widgets/gimppixbuf.h create mode 100644 app/widgets/gimppluginview.c create mode 100644 app/widgets/gimppluginview.h create mode 100644 app/widgets/gimppolar.c create mode 100644 app/widgets/gimppolar.h create mode 100644 app/widgets/gimppopup.c create mode 100644 app/widgets/gimppopup.h create mode 100644 app/widgets/gimpprefsbox.c create mode 100644 app/widgets/gimpprefsbox.h create mode 100644 app/widgets/gimpprocedureaction.c create mode 100644 app/widgets/gimpprocedureaction.h create mode 100644 app/widgets/gimpprogressbox.c create mode 100644 app/widgets/gimpprogressbox.h create mode 100644 app/widgets/gimpprogressdialog.c create mode 100644 app/widgets/gimpprogressdialog.h create mode 100644 app/widgets/gimppropwidgets.c create mode 100644 app/widgets/gimppropwidgets.h create mode 100644 app/widgets/gimpradioaction.c create mode 100644 app/widgets/gimpradioaction.h create mode 100644 app/widgets/gimprender.c create mode 100644 app/widgets/gimprender.h create mode 100644 app/widgets/gimpsamplepointeditor.c create mode 100644 app/widgets/gimpsamplepointeditor.h create mode 100644 app/widgets/gimpsavedialog.c create mode 100644 app/widgets/gimpsavedialog.h create mode 100644 app/widgets/gimpscalebutton.c create mode 100644 app/widgets/gimpscalebutton.h create mode 100644 app/widgets/gimpsearchpopup.c create mode 100644 app/widgets/gimpsearchpopup.h create mode 100644 app/widgets/gimpselectiondata.c create mode 100644 app/widgets/gimpselectiondata.h create mode 100644 app/widgets/gimpselectioneditor.c create mode 100644 app/widgets/gimpselectioneditor.h create mode 100644 app/widgets/gimpsessioninfo-aux.c create mode 100644 app/widgets/gimpsessioninfo-aux.h create mode 100644 app/widgets/gimpsessioninfo-book.c create mode 100644 app/widgets/gimpsessioninfo-book.h create mode 100644 app/widgets/gimpsessioninfo-dock.c create mode 100644 app/widgets/gimpsessioninfo-dock.h create mode 100644 app/widgets/gimpsessioninfo-dockable.c create mode 100644 app/widgets/gimpsessioninfo-dockable.h create mode 100644 app/widgets/gimpsessioninfo-private.h create mode 100644 app/widgets/gimpsessioninfo.c create mode 100644 app/widgets/gimpsessioninfo.h create mode 100644 app/widgets/gimpsessionmanaged.c create mode 100644 app/widgets/gimpsessionmanaged.h create mode 100644 app/widgets/gimpsettingsbox.c create mode 100644 app/widgets/gimpsettingsbox.h create mode 100644 app/widgets/gimpsettingseditor.c create mode 100644 app/widgets/gimpsettingseditor.h create mode 100644 app/widgets/gimpsizebox.c create mode 100644 app/widgets/gimpsizebox.h create mode 100644 app/widgets/gimpspinscale.c create mode 100644 app/widgets/gimpspinscale.h create mode 100644 app/widgets/gimpstringaction.c create mode 100644 app/widgets/gimpstringaction.h create mode 100644 app/widgets/gimpstrokeeditor.c create mode 100644 app/widgets/gimpstrokeeditor.h create mode 100644 app/widgets/gimpsymmetryeditor.c create mode 100644 app/widgets/gimpsymmetryeditor.h create mode 100644 app/widgets/gimptagentry.c create mode 100644 app/widgets/gimptagentry.h create mode 100644 app/widgets/gimptagpopup.c create mode 100644 app/widgets/gimptagpopup.h create mode 100644 app/widgets/gimptemplateeditor.c create mode 100644 app/widgets/gimptemplateeditor.h create mode 100644 app/widgets/gimptemplateview.c create mode 100644 app/widgets/gimptemplateview.h create mode 100644 app/widgets/gimptextbuffer-serialize.c create mode 100644 app/widgets/gimptextbuffer-serialize.h create mode 100644 app/widgets/gimptextbuffer.c create mode 100644 app/widgets/gimptextbuffer.h create mode 100644 app/widgets/gimptexteditor.c create mode 100644 app/widgets/gimptexteditor.h create mode 100644 app/widgets/gimptextproxy.c create mode 100644 app/widgets/gimptextproxy.h create mode 100644 app/widgets/gimptextstyleeditor.c create mode 100644 app/widgets/gimptextstyleeditor.h create mode 100644 app/widgets/gimptexttag.c create mode 100644 app/widgets/gimptexttag.h create mode 100644 app/widgets/gimpthumbbox.c create mode 100644 app/widgets/gimpthumbbox.h create mode 100644 app/widgets/gimptoggleaction.c create mode 100644 app/widgets/gimptoggleaction.h create mode 100644 app/widgets/gimptoolbox-color-area.c create mode 100644 app/widgets/gimptoolbox-color-area.h create mode 100644 app/widgets/gimptoolbox-dnd.c create mode 100644 app/widgets/gimptoolbox-dnd.h create mode 100644 app/widgets/gimptoolbox-image-area.c create mode 100644 app/widgets/gimptoolbox-image-area.h create mode 100644 app/widgets/gimptoolbox-indicator-area.c create mode 100644 app/widgets/gimptoolbox-indicator-area.h create mode 100644 app/widgets/gimptoolbox.c create mode 100644 app/widgets/gimptoolbox.h create mode 100644 app/widgets/gimptoolbutton.c create mode 100644 app/widgets/gimptoolbutton.h create mode 100644 app/widgets/gimptooleditor.c create mode 100644 app/widgets/gimptooleditor.h create mode 100644 app/widgets/gimptooloptionseditor.c create mode 100644 app/widgets/gimptooloptionseditor.h create mode 100644 app/widgets/gimptoolpalette.c create mode 100644 app/widgets/gimptoolpalette.h create mode 100644 app/widgets/gimptoolpreseteditor.c create mode 100644 app/widgets/gimptoolpreseteditor.h create mode 100644 app/widgets/gimptoolpresetfactoryview.c create mode 100644 app/widgets/gimptoolpresetfactoryview.h create mode 100644 app/widgets/gimptranslationstore.c create mode 100644 app/widgets/gimptranslationstore.h create mode 100644 app/widgets/gimpuimanager.c create mode 100644 app/widgets/gimpuimanager.h create mode 100644 app/widgets/gimpundoeditor.c create mode 100644 app/widgets/gimpundoeditor.h create mode 100644 app/widgets/gimpvectorstreeview.c create mode 100644 app/widgets/gimpvectorstreeview.h create mode 100644 app/widgets/gimpview-popup.c create mode 100644 app/widgets/gimpview-popup.h create mode 100644 app/widgets/gimpview.c create mode 100644 app/widgets/gimpview.h create mode 100644 app/widgets/gimpviewablebox.c create mode 100644 app/widgets/gimpviewablebox.h create mode 100644 app/widgets/gimpviewablebutton.c create mode 100644 app/widgets/gimpviewablebutton.h create mode 100644 app/widgets/gimpviewabledialog.c create mode 100644 app/widgets/gimpviewabledialog.h create mode 100644 app/widgets/gimpviewrenderer-frame.c create mode 100644 app/widgets/gimpviewrenderer-frame.h create mode 100644 app/widgets/gimpviewrenderer-utils.c create mode 100644 app/widgets/gimpviewrenderer-utils.h create mode 100644 app/widgets/gimpviewrenderer.c create mode 100644 app/widgets/gimpviewrenderer.h create mode 100644 app/widgets/gimpviewrendererbrush.c create mode 100644 app/widgets/gimpviewrendererbrush.h create mode 100644 app/widgets/gimpviewrendererbuffer.c create mode 100644 app/widgets/gimpviewrendererbuffer.h create mode 100644 app/widgets/gimpviewrendererdrawable.c create mode 100644 app/widgets/gimpviewrendererdrawable.h create mode 100644 app/widgets/gimpviewrenderergradient.c create mode 100644 app/widgets/gimpviewrenderergradient.h create mode 100644 app/widgets/gimpviewrendererimage.c create mode 100644 app/widgets/gimpviewrendererimage.h create mode 100644 app/widgets/gimpviewrendererimagefile.c create mode 100644 app/widgets/gimpviewrendererimagefile.h create mode 100644 app/widgets/gimpviewrendererlayer.c create mode 100644 app/widgets/gimpviewrendererlayer.h create mode 100644 app/widgets/gimpviewrendererpalette.c create mode 100644 app/widgets/gimpviewrendererpalette.h create mode 100644 app/widgets/gimpviewrenderervectors.c create mode 100644 app/widgets/gimpviewrenderervectors.h create mode 100644 app/widgets/gimpwidgets-constructors.c create mode 100644 app/widgets/gimpwidgets-constructors.h create mode 100644 app/widgets/gimpwidgets-utils.c create mode 100644 app/widgets/gimpwidgets-utils.h create mode 100644 app/widgets/gimpwindow.c create mode 100644 app/widgets/gimpwindow.h create mode 100644 app/widgets/gimpwindowstrategy.c create mode 100644 app/widgets/gimpwindowstrategy.h create mode 100644 app/widgets/gtkhwrapbox.c create mode 100644 app/widgets/gtkhwrapbox.h create mode 100644 app/widgets/gtkwrapbox.c create mode 100644 app/widgets/gtkwrapbox.h create mode 100644 app/widgets/widgets-enums.c create mode 100644 app/widgets/widgets-enums.h create mode 100644 app/widgets/widgets-types.h create mode 100644 app/xcf/Makefile.am create mode 100644 app/xcf/Makefile.in create mode 100644 app/xcf/xcf-load.c create mode 100644 app/xcf/xcf-load.h create mode 100644 app/xcf/xcf-private.h create mode 100644 app/xcf/xcf-read.c create mode 100644 app/xcf/xcf-read.h create mode 100644 app/xcf/xcf-save.c create mode 100644 app/xcf/xcf-save.h create mode 100644 app/xcf/xcf-seek.c create mode 100644 app/xcf/xcf-seek.h create mode 100644 app/xcf/xcf-utils.c create mode 100644 app/xcf/xcf-utils.h create mode 100644 app/xcf/xcf-write.c create mode 100644 app/xcf/xcf-write.h create mode 100644 app/xcf/xcf.c create mode 100644 app/xcf/xcf.h (limited to 'app') diff --git a/app/Makefile.am b/app/Makefile.am new file mode 100644 index 0000000..45d948d --- /dev/null +++ b/app/Makefile.am @@ -0,0 +1,290 @@ +## Process this file with automake to produce Makefile.in + +if PLATFORM_OSX +xobjective_c = "-xobjective-c" +xobjective_cxx = "-xobjective-c++" +xnone = "-xnone" +endif + +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la + +# Sort this by architectural dependencies, lowest level at the top, +# so that when e.g. changing a header-file the subdirs are built in +# the right order +SUBDIRS = \ + config \ + core \ + operations \ + gegl \ + text \ + vectors \ + paint \ + plug-in \ + xcf \ + file \ + file-data \ + pdb \ + widgets \ + propgui \ + display \ + tools \ + dialogs \ + actions \ + menus \ + gui \ + . \ + tests + +# Put the GIMP core in a lib so we can conveniently link against that +# in test cases +noinst_LIBRARIES = libapp.a + +if ENABLE_GIMP_CONSOLE +bin_PROGRAMS = gimp-@GIMP_APP_VERSION@ gimp-console-@GIMP_APP_VERSION@ +else +bin_PROGRAMS = gimp-@GIMP_APP_VERSION@ +endif + +libapp_sources = \ + about.h \ + app.c \ + app.h \ + errors.c \ + errors.h \ + language.c \ + language.h \ + sanity.c \ + sanity.h \ + signals.c \ + signals.h \ + tests.c \ + tests.h \ + unique.c \ + unique.h \ + gimp-debug.c \ + gimp-debug.h \ + gimp-intl.h \ + gimp-log.c \ + gimp-log.h \ + gimp-priorities.h \ + gimp-update.c \ + gimp-update.h \ + gimp-version.c \ + gimp-version.h + +libapp_a_SOURCES = $(libapp_sources) + +gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c + + +if PLATFORM_LINUX +libdl = -ldl +endif + +if PLATFORM_OSX +framework_cocoa = -framework Cocoa +endif + +if OS_WIN32 +win32_ldflags = -mwindows -Wl,--tsaware $(WIN32_LARGE_ADDRESS_AWARE) + +# for GimpDashboard and GimpBacktrace +psapi_cflags = -DPSAPI_VERSION=1 +libpsapi = -lpsapi + +# for GimpBacktrace +libdbghelp = -ldbghelp + +# for I_RpcExceptionFilter() +librpcrt4 = -lrpcrt4 + +if HAVE_EXCHNDL +exchndl = -lexchndl +endif + +else +libm = -lm +endif + +if ENABLE_RELOCATABLE_RESOURCES +munix = -Wl,-rpath '-Wl,$$ORIGIN/../lib' +endif + +if HAVE_WINDRES +include $(top_srcdir)/build/windows/gimprc.rule +GIMPRC = gimp-$(GIMP_APP_VERSION).rc.o +GIMPCONSOLERC = gimp-console-$(GIMP_APP_VERSION).rc.o +endif + +AM_CPPFLAGS = \ + -DGIMPDIR=\""$(gimpdir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \ + -DGIMP_TOOL_VERSION=\"$(GIMP_TOOL_VERSION)\" \ + -DG_LOG_DOMAIN=\"Gimp\" \ + -DGIMP_APP_GLUE_COMPILATION \ + -DCC_VERSION=\""$(CC_VERSION)"\" \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(PANGOCAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(LCMS_CFLAGS) \ + $(GEXIV2_CFLAGS) \ + $(psapi_cflags) \ + $(xobjective_c) \ + -I$(includedir) \ + -I$(builddir)/gui + +# We need this due to circular dependencies +AM_LDFLAGS = \ + $(munix) \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_param_spec_duplicate \ + -Wl,-u,$(SYMPREFIX)gimp_operations_init \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy \ + -Wl,-u,$(SYMPREFIX)gimp_parallel_init \ + -Wl,-u,$(SYMPREFIX)gimp_async_set_new \ + -Wl,-u,$(SYMPREFIX)gimp_uncancelable_waitable_new + +gimpconsoleldadd = \ + xcf/libappxcf.a \ + pdb/libappinternal-procs.a \ + pdb/libapppdb.a \ + plug-in/libappplug-in.a \ + vectors/libappvectors.a \ + core/libappcore.a \ + file/libappfile.a \ + file-data/libappfile-data.a \ + text/libapptext.a \ + paint/libapppaint.a \ + operations/libappoperations.a \ + operations/layer-modes/libapplayermodes.a \ + operations/layer-modes-legacy/libapplayermodeslegacy.a \ + gegl/libappgegl.a \ + config/libappconfig.a \ + $(libgimpconfig) \ + $(libgimpmath) \ + $(libgimpthumb) \ + $(libgimpcolor) \ + $(libgimpmodule) \ + $(libgimpbase) \ + $(GDK_PIXBUF_LIBS) \ + $(FREETYPE_LIBS) \ + $(FONTCONFIG_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(HARFBUZZ_LIBS) \ + $(CAIRO_LIBS) \ + $(GIO_UNIX_LIBS) \ + $(GIO_WINDOWS_LIBS) \ + $(GEGL_LIBS) \ + $(GLIB_LIBS) \ + $(LCMS_LIBS) \ + $(GEXIV2_LIBS) \ + $(Z_LIBS) \ + $(JSON_C_LIBS) \ + $(LIBMYPAINT_LIBS) \ + $(LIBBACKTRACE_LIBS) \ + $(LIBUNWIND_LIBS) \ + $(INTLLIBS) \ + $(RT_LIBS) \ + $(libm) \ + $(libdl) \ + $(libpsapi) \ + $(libdbghelp) \ + $(librpcrt4) + +gimp_@GIMP_APP_VERSION@_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(win32_ldflags) \ + $(framework_cocoa) \ + -Wl,-u,$(SYMPREFIX)gimp_lebl_dialog + +gimp_@GIMP_APP_VERSION@_LDADD = \ + gui/libappgui.a \ + menus/libappmenus.a \ + actions/libappactions.a \ + dialogs/libappdialogs.a \ + tools/libapptools.a \ + display/libappdisplay.a \ + propgui/libapppropgui.a \ + widgets/libappwidgets.a \ + $(libgimpwidgets) \ + $(GTK_LIBS) \ + $(GTK_MAC_INTEGRATION_LIBS) \ + $(gimpconsoleldadd) \ + $(exchndl) \ + $(GIMPRC) + + +if ENABLE_GIMP_CONSOLE + +gimp_console_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c + +gimp_console_@GIMP_APP_VERSION@_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DGIMP_CONSOLE_COMPILATION + +gimp_console_@GIMP_APP_VERSION@_LDADD = \ + $(gimpconsoleldadd) \ + $(GIMPCONSOLERC) + +endif + + +install-exec-hook: +if DEFAULT_BINARY + cd $(DESTDIR)$(bindir) \ + && rm -f gimp$(EXEEXT) \ + && $(LN_S) gimp-$(GIMP_APP_VERSION)$(EXEEXT) gimp$(EXEEXT) +if ENABLE_GIMP_CONSOLE + cd $(DESTDIR)$(bindir) \ + && rm -f gimp-console$(EXEEXT) \ + && $(LN_S) gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) gimp-console$(EXEEXT) +endif +endif + +uninstall-local: +if DEFAULT_BINARY + rm -f $(DESTDIR)$(bindir)/gimp$(EXEEXT) +if ENABLE_GIMP_CONSOLE + rm -f $(DESTDIR)$(bindir)/gimp-console$(EXEEXT) +endif +endif + + +# require gimp-console when making dist +# +if ENABLE_GIMP_CONSOLE +dist-check-gimp-console: +else +dist-check-gimp-console: + @echo "*** gimp-console must be enabled in order to make dist" + @false +endif + + +# hook to assure that the system gimprc and the gimprc manpage are +# uptodate when a release is made +# +dist-dump-gimprc: gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) + ./$< --dump-gimprc-system > gimprc.tmp \ + && (cmp -s gimprc.tmp $(top_srcdir)/etc/gimprc.in || \ + cp gimprc.tmp $(top_srcdir)/etc/gimprc.in) \ + && rm gimprc.tmp + ./$< --dump-gimprc-manpage > gimprc.tmp \ + && (cmp -s gimprc.tmp $(top_srcdir)/docs/gimprc.5.in ||\ + cp gimprc.tmp $(top_srcdir)/docs/gimprc.5.in) \ + && rm gimprc.tmp + +dist-hook: dist-check-gimp-console dist-dump-gimprc diff --git a/app/Makefile.in b/app/Makefile.in new file mode 100644 index 0000000..e34e997 --- /dev/null +++ b/app/Makefile.in @@ -0,0 +1,1664 @@ +# 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@ +@ENABLE_GIMP_CONSOLE_FALSE@bin_PROGRAMS = \ +@ENABLE_GIMP_CONSOLE_FALSE@ gimp-@GIMP_APP_VERSION@$(EXEEXT) +@ENABLE_GIMP_CONSOLE_TRUE@bin_PROGRAMS = \ +@ENABLE_GIMP_CONSOLE_TRUE@ gimp-@GIMP_APP_VERSION@$(EXEEXT) \ +@ENABLE_GIMP_CONSOLE_TRUE@ gimp-console-@GIMP_APP_VERSION@$(EXEEXT) +subdir = app +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)$(bindir)" +PROGRAMS = $(bin_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 = +libapp_a_AR = $(AR) $(ARFLAGS) +libapp_a_LIBADD = +am__objects_1 = app.$(OBJEXT) errors.$(OBJEXT) language.$(OBJEXT) \ + sanity.$(OBJEXT) signals.$(OBJEXT) tests.$(OBJEXT) \ + unique.$(OBJEXT) gimp-debug.$(OBJEXT) gimp-log.$(OBJEXT) \ + gimp-update.$(OBJEXT) gimp-version.$(OBJEXT) +am_libapp_a_OBJECTS = $(am__objects_1) +libapp_a_OBJECTS = $(am_libapp_a_OBJECTS) +am_gimp_@GIMP_APP_VERSION@_OBJECTS = $(am__objects_1) main.$(OBJEXT) +gimp_@GIMP_APP_VERSION@_OBJECTS = \ + $(am_gimp_@GIMP_APP_VERSION@_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = xcf/libappxcf.a pdb/libappinternal-procs.a \ + pdb/libapppdb.a plug-in/libappplug-in.a \ + vectors/libappvectors.a core/libappcore.a file/libappfile.a \ + file-data/libappfile-data.a text/libapptext.a \ + paint/libapppaint.a operations/libappoperations.a \ + operations/layer-modes/libapplayermodes.a \ + operations/layer-modes-legacy/libapplayermodeslegacy.a \ + gegl/libappgegl.a config/libappconfig.a $(libgimpconfig) \ + $(libgimpmath) $(libgimpthumb) $(libgimpcolor) \ + $(libgimpmodule) $(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) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +gimp_@GIMP_APP_VERSION@_DEPENDENCIES = gui/libappgui.a \ + menus/libappmenus.a actions/libappactions.a \ + dialogs/libappdialogs.a tools/libapptools.a \ + display/libappdisplay.a propgui/libapppropgui.a \ + widgets/libappwidgets.a $(libgimpwidgets) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) $(GIMPRC) +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 = +gimp_@GIMP_APP_VERSION@_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(gimp_@GIMP_APP_VERSION@_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__gimp_console_@GIMP_APP_VERSION@_SOURCES_DIST = about.h app.c app.h \ + errors.c errors.h language.c language.h sanity.c sanity.h \ + signals.c signals.h tests.c tests.h unique.c unique.h \ + gimp-debug.c gimp-debug.h gimp-intl.h gimp-log.c gimp-log.h \ + gimp-priorities.h gimp-update.c gimp-update.h gimp-version.c \ + gimp-version.h main.c +am__objects_2 = gimp_console_@GIMP_APP_VERSION@-app.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-errors.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-language.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-sanity.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-signals.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-tests.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-unique.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-gimp-debug.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-gimp-log.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-gimp-update.$(OBJEXT) \ + gimp_console_@GIMP_APP_VERSION@-gimp-version.$(OBJEXT) +@ENABLE_GIMP_CONSOLE_TRUE@am_gimp_console_@GIMP_APP_VERSION@_OBJECTS = \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(am__objects_2) \ +@ENABLE_GIMP_CONSOLE_TRUE@ gimp_console_@GIMP_APP_VERSION@-main.$(OBJEXT) +gimp_console_@GIMP_APP_VERSION@_OBJECTS = \ + $(am_gimp_console_@GIMP_APP_VERSION@_OBJECTS) +@ENABLE_GIMP_CONSOLE_TRUE@gimp_console_@GIMP_APP_VERSION@_DEPENDENCIES = \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(am__DEPENDENCIES_2) \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(GIMPCONSOLERC) +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)/app.Po ./$(DEPDIR)/errors.Po \ + ./$(DEPDIR)/gimp-debug.Po ./$(DEPDIR)/gimp-log.Po \ + ./$(DEPDIR)/gimp-update.Po ./$(DEPDIR)/gimp-version.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po \ + ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po \ + ./$(DEPDIR)/language.Po ./$(DEPDIR)/main.Po \ + ./$(DEPDIR)/sanity.Po ./$(DEPDIR)/signals.Po \ + ./$(DEPDIR)/tests.Po ./$(DEPDIR)/unique.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 = $(libapp_a_SOURCES) $(gimp_@GIMP_APP_VERSION@_SOURCES) \ + $(gimp_console_@GIMP_APP_VERSION@_SOURCES) +DIST_SOURCES = $(libapp_a_SOURCES) $(gimp_@GIMP_APP_VERSION@_SOURCES) \ + $(am__gimp_console_@GIMP_APP_VERSION@_SOURCES_DIST) +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.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 = @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@ +@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c" +@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++" +@PLATFORM_OSX_TRUE@xnone = "-xnone" +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la + +# Sort this by architectural dependencies, lowest level at the top, +# so that when e.g. changing a header-file the subdirs are built in +# the right order +SUBDIRS = \ + config \ + core \ + operations \ + gegl \ + text \ + vectors \ + paint \ + plug-in \ + xcf \ + file \ + file-data \ + pdb \ + widgets \ + propgui \ + display \ + tools \ + dialogs \ + actions \ + menus \ + gui \ + . \ + tests + + +# Put the GIMP core in a lib so we can conveniently link against that +# in test cases +noinst_LIBRARIES = libapp.a +libapp_sources = \ + about.h \ + app.c \ + app.h \ + errors.c \ + errors.h \ + language.c \ + language.h \ + sanity.c \ + sanity.h \ + signals.c \ + signals.h \ + tests.c \ + tests.h \ + unique.c \ + unique.h \ + gimp-debug.c \ + gimp-debug.h \ + gimp-intl.h \ + gimp-log.c \ + gimp-log.h \ + gimp-priorities.h \ + gimp-update.c \ + gimp-update.h \ + gimp-version.c \ + gimp-version.h + +libapp_a_SOURCES = $(libapp_sources) +gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c +@PLATFORM_LINUX_TRUE@libdl = -ldl +@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa +@OS_WIN32_TRUE@win32_ldflags = -mwindows -Wl,--tsaware $(WIN32_LARGE_ADDRESS_AWARE) + +# for GimpDashboard and GimpBacktrace +@OS_WIN32_TRUE@psapi_cflags = -DPSAPI_VERSION=1 +@OS_WIN32_TRUE@libpsapi = -lpsapi + +# for GimpBacktrace +@OS_WIN32_TRUE@libdbghelp = -ldbghelp + +# for I_RpcExceptionFilter() +@OS_WIN32_TRUE@librpcrt4 = -lrpcrt4 +@HAVE_EXCHNDL_TRUE@@OS_WIN32_TRUE@exchndl = -lexchndl +@OS_WIN32_FALSE@libm = -lm +@ENABLE_RELOCATABLE_RESOURCES_TRUE@munix = -Wl,-rpath '-Wl,$$ORIGIN/../lib' +@HAVE_WINDRES_TRUE@GIMPAPPRC = $(top_builddir)/build/windows/gimp.rc +@HAVE_WINDRES_TRUE@GIMPRC = gimp-$(GIMP_APP_VERSION).rc.o +@HAVE_WINDRES_TRUE@GIMPCONSOLERC = gimp-console-$(GIMP_APP_VERSION).rc.o +AM_CPPFLAGS = \ + -DGIMPDIR=\""$(gimpdir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \ + -DGIMP_TOOL_VERSION=\"$(GIMP_TOOL_VERSION)\" \ + -DG_LOG_DOMAIN=\"Gimp\" \ + -DGIMP_APP_GLUE_COMPILATION \ + -DCC_VERSION=\""$(CC_VERSION)"\" \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(PANGOCAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(LCMS_CFLAGS) \ + $(GEXIV2_CFLAGS) \ + $(psapi_cflags) \ + $(xobjective_c) \ + -I$(includedir) \ + -I$(builddir)/gui + + +# We need this due to circular dependencies +AM_LDFLAGS = \ + $(munix) \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_param_spec_duplicate \ + -Wl,-u,$(SYMPREFIX)gimp_operations_init \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy \ + -Wl,-u,$(SYMPREFIX)gimp_parallel_init \ + -Wl,-u,$(SYMPREFIX)gimp_async_set_new \ + -Wl,-u,$(SYMPREFIX)gimp_uncancelable_waitable_new + +gimpconsoleldadd = \ + xcf/libappxcf.a \ + pdb/libappinternal-procs.a \ + pdb/libapppdb.a \ + plug-in/libappplug-in.a \ + vectors/libappvectors.a \ + core/libappcore.a \ + file/libappfile.a \ + file-data/libappfile-data.a \ + text/libapptext.a \ + paint/libapppaint.a \ + operations/libappoperations.a \ + operations/layer-modes/libapplayermodes.a \ + operations/layer-modes-legacy/libapplayermodeslegacy.a \ + gegl/libappgegl.a \ + config/libappconfig.a \ + $(libgimpconfig) \ + $(libgimpmath) \ + $(libgimpthumb) \ + $(libgimpcolor) \ + $(libgimpmodule) \ + $(libgimpbase) \ + $(GDK_PIXBUF_LIBS) \ + $(FREETYPE_LIBS) \ + $(FONTCONFIG_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(HARFBUZZ_LIBS) \ + $(CAIRO_LIBS) \ + $(GIO_UNIX_LIBS) \ + $(GIO_WINDOWS_LIBS) \ + $(GEGL_LIBS) \ + $(GLIB_LIBS) \ + $(LCMS_LIBS) \ + $(GEXIV2_LIBS) \ + $(Z_LIBS) \ + $(JSON_C_LIBS) \ + $(LIBMYPAINT_LIBS) \ + $(LIBBACKTRACE_LIBS) \ + $(LIBUNWIND_LIBS) \ + $(INTLLIBS) \ + $(RT_LIBS) \ + $(libm) \ + $(libdl) \ + $(libpsapi) \ + $(libdbghelp) \ + $(librpcrt4) + +gimp_@GIMP_APP_VERSION@_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(win32_ldflags) \ + $(framework_cocoa) \ + -Wl,-u,$(SYMPREFIX)gimp_lebl_dialog + +gimp_@GIMP_APP_VERSION@_LDADD = \ + gui/libappgui.a \ + menus/libappmenus.a \ + actions/libappactions.a \ + dialogs/libappdialogs.a \ + tools/libapptools.a \ + display/libappdisplay.a \ + propgui/libapppropgui.a \ + widgets/libappwidgets.a \ + $(libgimpwidgets) \ + $(GTK_LIBS) \ + $(GTK_MAC_INTEGRATION_LIBS) \ + $(gimpconsoleldadd) \ + $(exchndl) \ + $(GIMPRC) + +@ENABLE_GIMP_CONSOLE_TRUE@gimp_console_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c +@ENABLE_GIMP_CONSOLE_TRUE@gimp_console_@GIMP_APP_VERSION@_CPPFLAGS = \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(AM_CPPFLAGS) \ +@ENABLE_GIMP_CONSOLE_TRUE@ -DGIMP_CONSOLE_COMPILATION + +@ENABLE_GIMP_CONSOLE_TRUE@gimp_console_@GIMP_APP_VERSION@_LDADD = \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(gimpconsoleldadd) \ +@ENABLE_GIMP_CONSOLE_TRUE@ $(GIMPCONSOLERC) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc.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 app/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/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.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-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || 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)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || 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)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_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) + +libapp.a: $(libapp_a_OBJECTS) $(libapp_a_DEPENDENCIES) $(EXTRA_libapp_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapp.a + $(AM_V_AR)$(libapp_a_AR) libapp.a $(libapp_a_OBJECTS) $(libapp_a_LIBADD) + $(AM_V_at)$(RANLIB) libapp.a + +gimp-@GIMP_APP_VERSION@$(EXEEXT): $(gimp_@GIMP_APP_VERSION@_OBJECTS) $(gimp_@GIMP_APP_VERSION@_DEPENDENCIES) $(EXTRA_gimp_@GIMP_APP_VERSION@_DEPENDENCIES) + @rm -f gimp-@GIMP_APP_VERSION@$(EXEEXT) + $(AM_V_CCLD)$(gimp_@GIMP_APP_VERSION@_LINK) $(gimp_@GIMP_APP_VERSION@_OBJECTS) $(gimp_@GIMP_APP_VERSION@_LDADD) $(LIBS) + +gimp-console-@GIMP_APP_VERSION@$(EXEEXT): $(gimp_console_@GIMP_APP_VERSION@_OBJECTS) $(gimp_console_@GIMP_APP_VERSION@_DEPENDENCIES) $(EXTRA_gimp_console_@GIMP_APP_VERSION@_DEPENDENCIES) + @rm -f gimp-console-@GIMP_APP_VERSION@$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gimp_console_@GIMP_APP_VERSION@_OBJECTS) $(gimp_console_@GIMP_APP_VERSION@_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-debug.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-update.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-version.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanity.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signals.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unique.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 $@ $< + +gimp_console_@GIMP_APP_VERSION@-app.o: app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-app.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-app.o `test -f 'app.c' || echo '$(srcdir)/'`app.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='app.c' object='gimp_console_@GIMP_APP_VERSION@-app.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-app.o `test -f 'app.c' || echo '$(srcdir)/'`app.c + +gimp_console_@GIMP_APP_VERSION@-app.obj: app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-app.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-app.obj `if test -f 'app.c'; then $(CYGPATH_W) 'app.c'; else $(CYGPATH_W) '$(srcdir)/app.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='app.c' object='gimp_console_@GIMP_APP_VERSION@-app.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-app.obj `if test -f 'app.c'; then $(CYGPATH_W) 'app.c'; else $(CYGPATH_W) '$(srcdir)/app.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-errors.o: errors.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-errors.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-errors.o `test -f 'errors.c' || echo '$(srcdir)/'`errors.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='errors.c' object='gimp_console_@GIMP_APP_VERSION@-errors.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-errors.o `test -f 'errors.c' || echo '$(srcdir)/'`errors.c + +gimp_console_@GIMP_APP_VERSION@-errors.obj: errors.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-errors.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-errors.obj `if test -f 'errors.c'; then $(CYGPATH_W) 'errors.c'; else $(CYGPATH_W) '$(srcdir)/errors.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='errors.c' object='gimp_console_@GIMP_APP_VERSION@-errors.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-errors.obj `if test -f 'errors.c'; then $(CYGPATH_W) 'errors.c'; else $(CYGPATH_W) '$(srcdir)/errors.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-language.o: language.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-language.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-language.o `test -f 'language.c' || echo '$(srcdir)/'`language.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='language.c' object='gimp_console_@GIMP_APP_VERSION@-language.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-language.o `test -f 'language.c' || echo '$(srcdir)/'`language.c + +gimp_console_@GIMP_APP_VERSION@-language.obj: language.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-language.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-language.obj `if test -f 'language.c'; then $(CYGPATH_W) 'language.c'; else $(CYGPATH_W) '$(srcdir)/language.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='language.c' object='gimp_console_@GIMP_APP_VERSION@-language.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-language.obj `if test -f 'language.c'; then $(CYGPATH_W) 'language.c'; else $(CYGPATH_W) '$(srcdir)/language.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-sanity.o: sanity.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-sanity.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-sanity.o `test -f 'sanity.c' || echo '$(srcdir)/'`sanity.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sanity.c' object='gimp_console_@GIMP_APP_VERSION@-sanity.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-sanity.o `test -f 'sanity.c' || echo '$(srcdir)/'`sanity.c + +gimp_console_@GIMP_APP_VERSION@-sanity.obj: sanity.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-sanity.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-sanity.obj `if test -f 'sanity.c'; then $(CYGPATH_W) 'sanity.c'; else $(CYGPATH_W) '$(srcdir)/sanity.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sanity.c' object='gimp_console_@GIMP_APP_VERSION@-sanity.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-sanity.obj `if test -f 'sanity.c'; then $(CYGPATH_W) 'sanity.c'; else $(CYGPATH_W) '$(srcdir)/sanity.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-signals.o: signals.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-signals.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-signals.o `test -f 'signals.c' || echo '$(srcdir)/'`signals.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signals.c' object='gimp_console_@GIMP_APP_VERSION@-signals.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-signals.o `test -f 'signals.c' || echo '$(srcdir)/'`signals.c + +gimp_console_@GIMP_APP_VERSION@-signals.obj: signals.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-signals.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-signals.obj `if test -f 'signals.c'; then $(CYGPATH_W) 'signals.c'; else $(CYGPATH_W) '$(srcdir)/signals.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signals.c' object='gimp_console_@GIMP_APP_VERSION@-signals.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-signals.obj `if test -f 'signals.c'; then $(CYGPATH_W) 'signals.c'; else $(CYGPATH_W) '$(srcdir)/signals.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-tests.o: tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-tests.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-tests.o `test -f 'tests.c' || echo '$(srcdir)/'`tests.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tests.c' object='gimp_console_@GIMP_APP_VERSION@-tests.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-tests.o `test -f 'tests.c' || echo '$(srcdir)/'`tests.c + +gimp_console_@GIMP_APP_VERSION@-tests.obj: tests.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-tests.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-tests.obj `if test -f 'tests.c'; then $(CYGPATH_W) 'tests.c'; else $(CYGPATH_W) '$(srcdir)/tests.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tests.c' object='gimp_console_@GIMP_APP_VERSION@-tests.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-tests.obj `if test -f 'tests.c'; then $(CYGPATH_W) 'tests.c'; else $(CYGPATH_W) '$(srcdir)/tests.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-unique.o: unique.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-unique.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-unique.o `test -f 'unique.c' || echo '$(srcdir)/'`unique.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unique.c' object='gimp_console_@GIMP_APP_VERSION@-unique.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-unique.o `test -f 'unique.c' || echo '$(srcdir)/'`unique.c + +gimp_console_@GIMP_APP_VERSION@-unique.obj: unique.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-unique.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-unique.obj `if test -f 'unique.c'; then $(CYGPATH_W) 'unique.c'; else $(CYGPATH_W) '$(srcdir)/unique.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unique.c' object='gimp_console_@GIMP_APP_VERSION@-unique.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-unique.obj `if test -f 'unique.c'; then $(CYGPATH_W) 'unique.c'; else $(CYGPATH_W) '$(srcdir)/unique.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-gimp-debug.o: gimp-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-debug.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-debug.o `test -f 'gimp-debug.c' || echo '$(srcdir)/'`gimp-debug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-debug.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-debug.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-debug.o `test -f 'gimp-debug.c' || echo '$(srcdir)/'`gimp-debug.c + +gimp_console_@GIMP_APP_VERSION@-gimp-debug.obj: gimp-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-debug.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-debug.obj `if test -f 'gimp-debug.c'; then $(CYGPATH_W) 'gimp-debug.c'; else $(CYGPATH_W) '$(srcdir)/gimp-debug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-debug.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-debug.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-debug.obj `if test -f 'gimp-debug.c'; then $(CYGPATH_W) 'gimp-debug.c'; else $(CYGPATH_W) '$(srcdir)/gimp-debug.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-gimp-log.o: gimp-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-log.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-log.o `test -f 'gimp-log.c' || echo '$(srcdir)/'`gimp-log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-log.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-log.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-log.o `test -f 'gimp-log.c' || echo '$(srcdir)/'`gimp-log.c + +gimp_console_@GIMP_APP_VERSION@-gimp-log.obj: gimp-log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-log.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-log.obj `if test -f 'gimp-log.c'; then $(CYGPATH_W) 'gimp-log.c'; else $(CYGPATH_W) '$(srcdir)/gimp-log.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-log.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-log.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-log.obj `if test -f 'gimp-log.c'; then $(CYGPATH_W) 'gimp-log.c'; else $(CYGPATH_W) '$(srcdir)/gimp-log.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-gimp-update.o: gimp-update.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-update.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-update.o `test -f 'gimp-update.c' || echo '$(srcdir)/'`gimp-update.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-update.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-update.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-update.o `test -f 'gimp-update.c' || echo '$(srcdir)/'`gimp-update.c + +gimp_console_@GIMP_APP_VERSION@-gimp-update.obj: gimp-update.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-update.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-update.obj `if test -f 'gimp-update.c'; then $(CYGPATH_W) 'gimp-update.c'; else $(CYGPATH_W) '$(srcdir)/gimp-update.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-update.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-update.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-update.obj `if test -f 'gimp-update.c'; then $(CYGPATH_W) 'gimp-update.c'; else $(CYGPATH_W) '$(srcdir)/gimp-update.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-gimp-version.o: gimp-version.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-version.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-version.o `test -f 'gimp-version.c' || echo '$(srcdir)/'`gimp-version.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-version.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-version.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-version.o `test -f 'gimp-version.c' || echo '$(srcdir)/'`gimp-version.c + +gimp_console_@GIMP_APP_VERSION@-gimp-version.obj: gimp-version.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-gimp-version.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-gimp-version.obj `if test -f 'gimp-version.c'; then $(CYGPATH_W) 'gimp-version.c'; else $(CYGPATH_W) '$(srcdir)/gimp-version.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-version.c' object='gimp_console_@GIMP_APP_VERSION@-gimp-version.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-gimp-version.obj `if test -f 'gimp-version.c'; then $(CYGPATH_W) 'gimp-version.c'; else $(CYGPATH_W) '$(srcdir)/gimp-version.c'; fi` + +gimp_console_@GIMP_APP_VERSION@-main.o: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-main.o -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='gimp_console_@GIMP_APP_VERSION@-main.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c + +gimp_console_@GIMP_APP_VERSION@-main.obj: main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gimp_console_@GIMP_APP_VERSION@-main.obj -MD -MP -MF $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Tpo -c -o gimp_console_@GIMP_APP_VERSION@-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Tpo $(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='gimp_console_@GIMP_APP_VERSION@-main.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) $(gimp_console_@GIMP_APP_VERSION@_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gimp_console_@GIMP_APP_VERSION@-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` + +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 + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; 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-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/app.Po + -rm -f ./$(DEPDIR)/errors.Po + -rm -f ./$(DEPDIR)/gimp-debug.Po + -rm -f ./$(DEPDIR)/gimp-log.Po + -rm -f ./$(DEPDIR)/gimp-update.Po + -rm -f ./$(DEPDIR)/gimp-version.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po + -rm -f ./$(DEPDIR)/language.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/sanity.Po + -rm -f ./$(DEPDIR)/signals.Po + -rm -f ./$(DEPDIR)/tests.Po + -rm -f ./$(DEPDIR)/unique.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-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +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)/app.Po + -rm -f ./$(DEPDIR)/errors.Po + -rm -f ./$(DEPDIR)/gimp-debug.Po + -rm -f ./$(DEPDIR)/gimp-log.Po + -rm -f ./$(DEPDIR)/gimp-update.Po + -rm -f ./$(DEPDIR)/gimp-version.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-app.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-errors.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-debug.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-log.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-update.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-gimp-version.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-language.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-main.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-sanity.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-signals.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-tests.Po + -rm -f ./$(DEPDIR)/gimp_console_@GIMP_APP_VERSION@-unique.Po + -rm -f ./$(DEPDIR)/language.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/sanity.Po + -rm -f ./$(DEPDIR)/signals.Po + -rm -f ./$(DEPDIR)/tests.Po + -rm -f ./$(DEPDIR)/unique.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-binPROGRAMS uninstall-local + +.MAKE: $(am__recursive_targets) install-am install-exec-am \ + install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am dist-hook distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-hook 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-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-local + +.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@ $(GIMPAPPRC) $@ + +install-exec-hook: +@DEFAULT_BINARY_TRUE@ cd $(DESTDIR)$(bindir) \ +@DEFAULT_BINARY_TRUE@ && rm -f gimp$(EXEEXT) \ +@DEFAULT_BINARY_TRUE@ && $(LN_S) gimp-$(GIMP_APP_VERSION)$(EXEEXT) gimp$(EXEEXT) +@DEFAULT_BINARY_TRUE@@ENABLE_GIMP_CONSOLE_TRUE@ cd $(DESTDIR)$(bindir) \ +@DEFAULT_BINARY_TRUE@@ENABLE_GIMP_CONSOLE_TRUE@ && rm -f gimp-console$(EXEEXT) \ +@DEFAULT_BINARY_TRUE@@ENABLE_GIMP_CONSOLE_TRUE@ && $(LN_S) gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) gimp-console$(EXEEXT) + +uninstall-local: +@DEFAULT_BINARY_TRUE@ rm -f $(DESTDIR)$(bindir)/gimp$(EXEEXT) +@DEFAULT_BINARY_TRUE@@ENABLE_GIMP_CONSOLE_TRUE@ rm -f $(DESTDIR)$(bindir)/gimp-console$(EXEEXT) + +# require gimp-console when making dist +# +@ENABLE_GIMP_CONSOLE_TRUE@dist-check-gimp-console: +@ENABLE_GIMP_CONSOLE_FALSE@dist-check-gimp-console: +@ENABLE_GIMP_CONSOLE_FALSE@ @echo "*** gimp-console must be enabled in order to make dist" +@ENABLE_GIMP_CONSOLE_FALSE@ @false + +# hook to assure that the system gimprc and the gimprc manpage are +# uptodate when a release is made +# +dist-dump-gimprc: gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) + ./$< --dump-gimprc-system > gimprc.tmp \ + && (cmp -s gimprc.tmp $(top_srcdir)/etc/gimprc.in || \ + cp gimprc.tmp $(top_srcdir)/etc/gimprc.in) \ + && rm gimprc.tmp + ./$< --dump-gimprc-manpage > gimprc.tmp \ + && (cmp -s gimprc.tmp $(top_srcdir)/docs/gimprc.5.in ||\ + cp gimprc.tmp $(top_srcdir)/docs/gimprc.5.in) \ + && rm gimprc.tmp + +dist-hook: dist-check-gimp-console dist-dump-gimprc + +# 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/app/about.h b/app/about.h new file mode 100644 index 0000000..c0dac07 --- /dev/null +++ b/app/about.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __ABOUT_H__ +#define __ABOUT_H__ + + +#define GIMP_ACRONYM \ + _("GIMP") + +#define GIMP_NAME \ + _("GNU Image Manipulation Program") + +/* The year of the last commit (UTC) will be inserted into this string. */ +#define GIMP_COPYRIGHT \ + _("Copyright © 1995-%s\n" \ + "Spencer Kimball, Peter Mattis and the GIMP Development Team") + +/* TRANSLATORS: do not end the license URL with a dot, because 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. + */ +#define GIMP_LICENSE \ + _("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." \ + "\n\n" \ + "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." \ + "\n\n" \ + "You should have received a copy of the GNU General Public License " \ + "along with GIMP. If not, see: https://www.gnu.org/licenses/") + + +#endif /* __ABOUT_H__ */ diff --git a/app/actions/Makefile.am b/app/actions/Makefile.am new file mode 100644 index 0000000..fd80ce2 --- /dev/null +++ b/app/actions/Makefile.am @@ -0,0 +1,202 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Actions\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappactions.a + +libappactions_a_SOURCES = \ + actions-types.h \ + actions.c \ + actions.h \ + \ + gimpgeglprocedure.c \ + gimpgeglprocedure.h \ + \ + brush-editor-actions.c \ + brush-editor-actions.h \ + brushes-actions.c \ + brushes-actions.h \ + buffers-actions.c \ + buffers-actions.h \ + buffers-commands.c \ + buffers-commands.h \ + channels-actions.c \ + channels-actions.h \ + channels-commands.c \ + channels-commands.h \ + colormap-actions.c \ + colormap-actions.h \ + colormap-commands.c \ + colormap-commands.h \ + context-actions.c \ + context-actions.h \ + context-commands.c \ + context-commands.h \ + cursor-info-actions.c \ + cursor-info-actions.h \ + cursor-info-commands.c \ + cursor-info-commands.h \ + dashboard-actions.c \ + dashboard-actions.h \ + dashboard-commands.c \ + dashboard-commands.h \ + data-commands.c \ + data-commands.h \ + data-editor-commands.c \ + data-editor-commands.h \ + debug-actions.c \ + debug-actions.h \ + debug-commands.c \ + debug-commands.h \ + dialogs-actions.c \ + dialogs-actions.h \ + dialogs-commands.c \ + dialogs-commands.h \ + dock-actions.c \ + dock-actions.h \ + dock-commands.c \ + dock-commands.h \ + dockable-actions.c \ + dockable-actions.h \ + dockable-commands.c \ + dockable-commands.h \ + documents-actions.c \ + documents-actions.h \ + documents-commands.c \ + documents-commands.h \ + drawable-actions.c \ + drawable-actions.h \ + drawable-commands.c \ + drawable-commands.h \ + dynamics-actions.c \ + dynamics-actions.h \ + dynamics-editor-actions.c \ + dynamics-editor-actions.h \ + edit-actions.c \ + edit-actions.h \ + edit-commands.c \ + edit-commands.h \ + error-console-actions.c \ + error-console-actions.h \ + error-console-commands.c \ + error-console-commands.h \ + file-actions.c \ + file-actions.h \ + file-commands.c \ + file-commands.h \ + filters-actions.c \ + filters-actions.h \ + filters-commands.c \ + filters-commands.h \ + fonts-actions.c \ + fonts-actions.h \ + gradient-editor-actions.c \ + gradient-editor-actions.h \ + gradient-editor-commands.c \ + gradient-editor-commands.h \ + gradients-actions.c \ + gradients-actions.h \ + gradients-commands.c \ + gradients-commands.h \ + help-actions.c \ + help-actions.h \ + help-commands.c \ + help-commands.h \ + image-actions.c \ + image-actions.h \ + image-commands.c \ + image-commands.h \ + images-actions.c \ + images-actions.h \ + images-commands.c \ + images-commands.h \ + items-commands.c \ + items-commands.h \ + items-actions.c \ + items-actions.h \ + layers-actions.c \ + layers-actions.h \ + layers-commands.c \ + layers-commands.h \ + mypaint-brushes-actions.c \ + mypaint-brushes-actions.h \ + palette-editor-actions.c \ + palette-editor-actions.h \ + palette-editor-commands.c \ + palette-editor-commands.h \ + palettes-actions.c \ + palettes-actions.h \ + palettes-commands.c \ + palettes-commands.h \ + patterns-actions.c \ + patterns-actions.h \ + plug-in-actions.c \ + plug-in-actions.h \ + plug-in-commands.c \ + plug-in-commands.h \ + procedure-commands.c \ + procedure-commands.h \ + quick-mask-actions.c \ + quick-mask-actions.h \ + quick-mask-commands.c \ + quick-mask-commands.h \ + sample-points-actions.c \ + sample-points-actions.h \ + sample-points-commands.c \ + sample-points-commands.h \ + select-actions.c \ + select-actions.h \ + select-commands.c \ + select-commands.h \ + templates-actions.c \ + templates-actions.h \ + templates-commands.c \ + templates-commands.h \ + text-editor-actions.c \ + text-editor-actions.h \ + text-editor-commands.c \ + text-editor-commands.h \ + text-tool-actions.c \ + text-tool-actions.h \ + text-tool-commands.c \ + text-tool-commands.h \ + tool-options-actions.c \ + tool-options-actions.h \ + tool-options-commands.c \ + tool-options-commands.h \ + tool-presets-actions.c \ + tool-presets-actions.h \ + tool-presets-commands.c \ + tool-presets-commands.h \ + tool-preset-editor-actions.c \ + tool-preset-editor-actions.h \ + tool-preset-editor-commands.c \ + tool-preset-editor-commands.h \ + tools-actions.c \ + tools-actions.h \ + tools-commands.c \ + tools-commands.h \ + vectors-actions.c \ + vectors-actions.h \ + vectors-commands.c \ + vectors-commands.h \ + view-actions.c \ + view-actions.h \ + view-commands.c \ + view-commands.h \ + window-actions.c \ + window-actions.h \ + window-commands.c \ + window-commands.h \ + windows-actions.c \ + windows-actions.h \ + windows-commands.c \ + windows-commands.h diff --git a/app/actions/Makefile.in b/app/actions/Makefile.in new file mode 100644 index 0000000..0ca776d --- /dev/null +++ b/app/actions/Makefile.in @@ -0,0 +1,1489 @@ +# 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 = app/actions +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 = +libappactions_a_AR = $(AR) $(ARFLAGS) +libappactions_a_LIBADD = +am_libappactions_a_OBJECTS = actions.$(OBJEXT) \ + gimpgeglprocedure.$(OBJEXT) brush-editor-actions.$(OBJEXT) \ + brushes-actions.$(OBJEXT) buffers-actions.$(OBJEXT) \ + buffers-commands.$(OBJEXT) channels-actions.$(OBJEXT) \ + channels-commands.$(OBJEXT) colormap-actions.$(OBJEXT) \ + colormap-commands.$(OBJEXT) context-actions.$(OBJEXT) \ + context-commands.$(OBJEXT) cursor-info-actions.$(OBJEXT) \ + cursor-info-commands.$(OBJEXT) dashboard-actions.$(OBJEXT) \ + dashboard-commands.$(OBJEXT) data-commands.$(OBJEXT) \ + data-editor-commands.$(OBJEXT) debug-actions.$(OBJEXT) \ + debug-commands.$(OBJEXT) dialogs-actions.$(OBJEXT) \ + dialogs-commands.$(OBJEXT) dock-actions.$(OBJEXT) \ + dock-commands.$(OBJEXT) dockable-actions.$(OBJEXT) \ + dockable-commands.$(OBJEXT) documents-actions.$(OBJEXT) \ + documents-commands.$(OBJEXT) drawable-actions.$(OBJEXT) \ + drawable-commands.$(OBJEXT) dynamics-actions.$(OBJEXT) \ + dynamics-editor-actions.$(OBJEXT) edit-actions.$(OBJEXT) \ + edit-commands.$(OBJEXT) error-console-actions.$(OBJEXT) \ + error-console-commands.$(OBJEXT) file-actions.$(OBJEXT) \ + file-commands.$(OBJEXT) filters-actions.$(OBJEXT) \ + filters-commands.$(OBJEXT) fonts-actions.$(OBJEXT) \ + gradient-editor-actions.$(OBJEXT) \ + gradient-editor-commands.$(OBJEXT) gradients-actions.$(OBJEXT) \ + gradients-commands.$(OBJEXT) help-actions.$(OBJEXT) \ + help-commands.$(OBJEXT) image-actions.$(OBJEXT) \ + image-commands.$(OBJEXT) images-actions.$(OBJEXT) \ + images-commands.$(OBJEXT) items-commands.$(OBJEXT) \ + items-actions.$(OBJEXT) layers-actions.$(OBJEXT) \ + layers-commands.$(OBJEXT) mypaint-brushes-actions.$(OBJEXT) \ + palette-editor-actions.$(OBJEXT) \ + palette-editor-commands.$(OBJEXT) palettes-actions.$(OBJEXT) \ + palettes-commands.$(OBJEXT) patterns-actions.$(OBJEXT) \ + plug-in-actions.$(OBJEXT) plug-in-commands.$(OBJEXT) \ + procedure-commands.$(OBJEXT) quick-mask-actions.$(OBJEXT) \ + quick-mask-commands.$(OBJEXT) sample-points-actions.$(OBJEXT) \ + sample-points-commands.$(OBJEXT) select-actions.$(OBJEXT) \ + select-commands.$(OBJEXT) templates-actions.$(OBJEXT) \ + templates-commands.$(OBJEXT) text-editor-actions.$(OBJEXT) \ + text-editor-commands.$(OBJEXT) text-tool-actions.$(OBJEXT) \ + text-tool-commands.$(OBJEXT) tool-options-actions.$(OBJEXT) \ + tool-options-commands.$(OBJEXT) tool-presets-actions.$(OBJEXT) \ + tool-presets-commands.$(OBJEXT) \ + tool-preset-editor-actions.$(OBJEXT) \ + tool-preset-editor-commands.$(OBJEXT) tools-actions.$(OBJEXT) \ + tools-commands.$(OBJEXT) vectors-actions.$(OBJEXT) \ + vectors-commands.$(OBJEXT) view-actions.$(OBJEXT) \ + view-commands.$(OBJEXT) window-actions.$(OBJEXT) \ + window-commands.$(OBJEXT) windows-actions.$(OBJEXT) \ + windows-commands.$(OBJEXT) +libappactions_a_OBJECTS = $(am_libappactions_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)/actions.Po \ + ./$(DEPDIR)/brush-editor-actions.Po \ + ./$(DEPDIR)/brushes-actions.Po ./$(DEPDIR)/buffers-actions.Po \ + ./$(DEPDIR)/buffers-commands.Po \ + ./$(DEPDIR)/channels-actions.Po \ + ./$(DEPDIR)/channels-commands.Po \ + ./$(DEPDIR)/colormap-actions.Po \ + ./$(DEPDIR)/colormap-commands.Po \ + ./$(DEPDIR)/context-actions.Po ./$(DEPDIR)/context-commands.Po \ + ./$(DEPDIR)/cursor-info-actions.Po \ + ./$(DEPDIR)/cursor-info-commands.Po \ + ./$(DEPDIR)/dashboard-actions.Po \ + ./$(DEPDIR)/dashboard-commands.Po ./$(DEPDIR)/data-commands.Po \ + ./$(DEPDIR)/data-editor-commands.Po \ + ./$(DEPDIR)/debug-actions.Po ./$(DEPDIR)/debug-commands.Po \ + ./$(DEPDIR)/dialogs-actions.Po ./$(DEPDIR)/dialogs-commands.Po \ + ./$(DEPDIR)/dock-actions.Po ./$(DEPDIR)/dock-commands.Po \ + ./$(DEPDIR)/dockable-actions.Po \ + ./$(DEPDIR)/dockable-commands.Po \ + ./$(DEPDIR)/documents-actions.Po \ + ./$(DEPDIR)/documents-commands.Po \ + ./$(DEPDIR)/drawable-actions.Po \ + ./$(DEPDIR)/drawable-commands.Po \ + ./$(DEPDIR)/dynamics-actions.Po \ + ./$(DEPDIR)/dynamics-editor-actions.Po \ + ./$(DEPDIR)/edit-actions.Po ./$(DEPDIR)/edit-commands.Po \ + ./$(DEPDIR)/error-console-actions.Po \ + ./$(DEPDIR)/error-console-commands.Po \ + ./$(DEPDIR)/file-actions.Po ./$(DEPDIR)/file-commands.Po \ + ./$(DEPDIR)/filters-actions.Po ./$(DEPDIR)/filters-commands.Po \ + ./$(DEPDIR)/fonts-actions.Po ./$(DEPDIR)/gimpgeglprocedure.Po \ + ./$(DEPDIR)/gradient-editor-actions.Po \ + ./$(DEPDIR)/gradient-editor-commands.Po \ + ./$(DEPDIR)/gradients-actions.Po \ + ./$(DEPDIR)/gradients-commands.Po ./$(DEPDIR)/help-actions.Po \ + ./$(DEPDIR)/help-commands.Po ./$(DEPDIR)/image-actions.Po \ + ./$(DEPDIR)/image-commands.Po ./$(DEPDIR)/images-actions.Po \ + ./$(DEPDIR)/images-commands.Po ./$(DEPDIR)/items-actions.Po \ + ./$(DEPDIR)/items-commands.Po ./$(DEPDIR)/layers-actions.Po \ + ./$(DEPDIR)/layers-commands.Po \ + ./$(DEPDIR)/mypaint-brushes-actions.Po \ + ./$(DEPDIR)/palette-editor-actions.Po \ + ./$(DEPDIR)/palette-editor-commands.Po \ + ./$(DEPDIR)/palettes-actions.Po \ + ./$(DEPDIR)/palettes-commands.Po \ + ./$(DEPDIR)/patterns-actions.Po ./$(DEPDIR)/plug-in-actions.Po \ + ./$(DEPDIR)/plug-in-commands.Po \ + ./$(DEPDIR)/procedure-commands.Po \ + ./$(DEPDIR)/quick-mask-actions.Po \ + ./$(DEPDIR)/quick-mask-commands.Po \ + ./$(DEPDIR)/sample-points-actions.Po \ + ./$(DEPDIR)/sample-points-commands.Po \ + ./$(DEPDIR)/select-actions.Po ./$(DEPDIR)/select-commands.Po \ + ./$(DEPDIR)/templates-actions.Po \ + ./$(DEPDIR)/templates-commands.Po \ + ./$(DEPDIR)/text-editor-actions.Po \ + ./$(DEPDIR)/text-editor-commands.Po \ + ./$(DEPDIR)/text-tool-actions.Po \ + ./$(DEPDIR)/text-tool-commands.Po \ + ./$(DEPDIR)/tool-options-actions.Po \ + ./$(DEPDIR)/tool-options-commands.Po \ + ./$(DEPDIR)/tool-preset-editor-actions.Po \ + ./$(DEPDIR)/tool-preset-editor-commands.Po \ + ./$(DEPDIR)/tool-presets-actions.Po \ + ./$(DEPDIR)/tool-presets-commands.Po \ + ./$(DEPDIR)/tools-actions.Po ./$(DEPDIR)/tools-commands.Po \ + ./$(DEPDIR)/vectors-actions.Po ./$(DEPDIR)/vectors-commands.Po \ + ./$(DEPDIR)/view-actions.Po ./$(DEPDIR)/view-commands.Po \ + ./$(DEPDIR)/window-actions.Po ./$(DEPDIR)/window-commands.Po \ + ./$(DEPDIR)/windows-actions.Po ./$(DEPDIR)/windows-commands.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 = $(libappactions_a_SOURCES) +DIST_SOURCES = $(libappactions_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Actions\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappactions.a +libappactions_a_SOURCES = \ + actions-types.h \ + actions.c \ + actions.h \ + \ + gimpgeglprocedure.c \ + gimpgeglprocedure.h \ + \ + brush-editor-actions.c \ + brush-editor-actions.h \ + brushes-actions.c \ + brushes-actions.h \ + buffers-actions.c \ + buffers-actions.h \ + buffers-commands.c \ + buffers-commands.h \ + channels-actions.c \ + channels-actions.h \ + channels-commands.c \ + channels-commands.h \ + colormap-actions.c \ + colormap-actions.h \ + colormap-commands.c \ + colormap-commands.h \ + context-actions.c \ + context-actions.h \ + context-commands.c \ + context-commands.h \ + cursor-info-actions.c \ + cursor-info-actions.h \ + cursor-info-commands.c \ + cursor-info-commands.h \ + dashboard-actions.c \ + dashboard-actions.h \ + dashboard-commands.c \ + dashboard-commands.h \ + data-commands.c \ + data-commands.h \ + data-editor-commands.c \ + data-editor-commands.h \ + debug-actions.c \ + debug-actions.h \ + debug-commands.c \ + debug-commands.h \ + dialogs-actions.c \ + dialogs-actions.h \ + dialogs-commands.c \ + dialogs-commands.h \ + dock-actions.c \ + dock-actions.h \ + dock-commands.c \ + dock-commands.h \ + dockable-actions.c \ + dockable-actions.h \ + dockable-commands.c \ + dockable-commands.h \ + documents-actions.c \ + documents-actions.h \ + documents-commands.c \ + documents-commands.h \ + drawable-actions.c \ + drawable-actions.h \ + drawable-commands.c \ + drawable-commands.h \ + dynamics-actions.c \ + dynamics-actions.h \ + dynamics-editor-actions.c \ + dynamics-editor-actions.h \ + edit-actions.c \ + edit-actions.h \ + edit-commands.c \ + edit-commands.h \ + error-console-actions.c \ + error-console-actions.h \ + error-console-commands.c \ + error-console-commands.h \ + file-actions.c \ + file-actions.h \ + file-commands.c \ + file-commands.h \ + filters-actions.c \ + filters-actions.h \ + filters-commands.c \ + filters-commands.h \ + fonts-actions.c \ + fonts-actions.h \ + gradient-editor-actions.c \ + gradient-editor-actions.h \ + gradient-editor-commands.c \ + gradient-editor-commands.h \ + gradients-actions.c \ + gradients-actions.h \ + gradients-commands.c \ + gradients-commands.h \ + help-actions.c \ + help-actions.h \ + help-commands.c \ + help-commands.h \ + image-actions.c \ + image-actions.h \ + image-commands.c \ + image-commands.h \ + images-actions.c \ + images-actions.h \ + images-commands.c \ + images-commands.h \ + items-commands.c \ + items-commands.h \ + items-actions.c \ + items-actions.h \ + layers-actions.c \ + layers-actions.h \ + layers-commands.c \ + layers-commands.h \ + mypaint-brushes-actions.c \ + mypaint-brushes-actions.h \ + palette-editor-actions.c \ + palette-editor-actions.h \ + palette-editor-commands.c \ + palette-editor-commands.h \ + palettes-actions.c \ + palettes-actions.h \ + palettes-commands.c \ + palettes-commands.h \ + patterns-actions.c \ + patterns-actions.h \ + plug-in-actions.c \ + plug-in-actions.h \ + plug-in-commands.c \ + plug-in-commands.h \ + procedure-commands.c \ + procedure-commands.h \ + quick-mask-actions.c \ + quick-mask-actions.h \ + quick-mask-commands.c \ + quick-mask-commands.h \ + sample-points-actions.c \ + sample-points-actions.h \ + sample-points-commands.c \ + sample-points-commands.h \ + select-actions.c \ + select-actions.h \ + select-commands.c \ + select-commands.h \ + templates-actions.c \ + templates-actions.h \ + templates-commands.c \ + templates-commands.h \ + text-editor-actions.c \ + text-editor-actions.h \ + text-editor-commands.c \ + text-editor-commands.h \ + text-tool-actions.c \ + text-tool-actions.h \ + text-tool-commands.c \ + text-tool-commands.h \ + tool-options-actions.c \ + tool-options-actions.h \ + tool-options-commands.c \ + tool-options-commands.h \ + tool-presets-actions.c \ + tool-presets-actions.h \ + tool-presets-commands.c \ + tool-presets-commands.h \ + tool-preset-editor-actions.c \ + tool-preset-editor-actions.h \ + tool-preset-editor-commands.c \ + tool-preset-editor-commands.h \ + tools-actions.c \ + tools-actions.h \ + tools-commands.c \ + tools-commands.h \ + vectors-actions.c \ + vectors-actions.h \ + vectors-commands.c \ + vectors-commands.h \ + view-actions.c \ + view-actions.h \ + view-commands.c \ + view-commands.h \ + window-actions.c \ + window-actions.h \ + window-commands.c \ + window-commands.h \ + windows-actions.c \ + windows-actions.h \ + windows-commands.c \ + windows-commands.h + +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 app/actions/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/actions/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) + +libappactions.a: $(libappactions_a_OBJECTS) $(libappactions_a_DEPENDENCIES) $(EXTRA_libappactions_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappactions.a + $(AM_V_AR)$(libappactions_a_AR) libappactions.a $(libappactions_a_OBJECTS) $(libappactions_a_LIBADD) + $(AM_V_at)$(RANLIB) libappactions.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/brush-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/brushes-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffers-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffers-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channels-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channels-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colormap-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colormap-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cursor-info-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cursor-info-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dashboard-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dashboard-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-editor-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialogs-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialogs-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dock-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dock-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dockable-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dockable-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/documents-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/documents-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawable-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawable-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamics-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamics-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edit-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edit-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error-console-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error-console-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filters-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filters-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fonts-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgeglprocedure.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradient-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradient-editor-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradients-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradients-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/images-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/images-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/items-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/items-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layers-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layers-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mypaint-brushes-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palette-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palette-editor-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palettes-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palettes-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/patterns-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procedure-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quick-mask-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quick-mask-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sample-points-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sample-points-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/templates-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/templates-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text-editor-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text-tool-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text-tool-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-options-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-options-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-preset-editor-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-preset-editor-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-presets-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-presets-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tools-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tools-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vectors-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vectors-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-commands.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windows-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windows-commands.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)/actions.Po + -rm -f ./$(DEPDIR)/brush-editor-actions.Po + -rm -f ./$(DEPDIR)/brushes-actions.Po + -rm -f ./$(DEPDIR)/buffers-actions.Po + -rm -f ./$(DEPDIR)/buffers-commands.Po + -rm -f ./$(DEPDIR)/channels-actions.Po + -rm -f ./$(DEPDIR)/channels-commands.Po + -rm -f ./$(DEPDIR)/colormap-actions.Po + -rm -f ./$(DEPDIR)/colormap-commands.Po + -rm -f ./$(DEPDIR)/context-actions.Po + -rm -f ./$(DEPDIR)/context-commands.Po + -rm -f ./$(DEPDIR)/cursor-info-actions.Po + -rm -f ./$(DEPDIR)/cursor-info-commands.Po + -rm -f ./$(DEPDIR)/dashboard-actions.Po + -rm -f ./$(DEPDIR)/dashboard-commands.Po + -rm -f ./$(DEPDIR)/data-commands.Po + -rm -f ./$(DEPDIR)/data-editor-commands.Po + -rm -f ./$(DEPDIR)/debug-actions.Po + -rm -f ./$(DEPDIR)/debug-commands.Po + -rm -f ./$(DEPDIR)/dialogs-actions.Po + -rm -f ./$(DEPDIR)/dialogs-commands.Po + -rm -f ./$(DEPDIR)/dock-actions.Po + -rm -f ./$(DEPDIR)/dock-commands.Po + -rm -f ./$(DEPDIR)/dockable-actions.Po + -rm -f ./$(DEPDIR)/dockable-commands.Po + -rm -f ./$(DEPDIR)/documents-actions.Po + -rm -f ./$(DEPDIR)/documents-commands.Po + -rm -f ./$(DEPDIR)/drawable-actions.Po + -rm -f ./$(DEPDIR)/drawable-commands.Po + -rm -f ./$(DEPDIR)/dynamics-actions.Po + -rm -f ./$(DEPDIR)/dynamics-editor-actions.Po + -rm -f ./$(DEPDIR)/edit-actions.Po + -rm -f ./$(DEPDIR)/edit-commands.Po + -rm -f ./$(DEPDIR)/error-console-actions.Po + -rm -f ./$(DEPDIR)/error-console-commands.Po + -rm -f ./$(DEPDIR)/file-actions.Po + -rm -f ./$(DEPDIR)/file-commands.Po + -rm -f ./$(DEPDIR)/filters-actions.Po + -rm -f ./$(DEPDIR)/filters-commands.Po + -rm -f ./$(DEPDIR)/fonts-actions.Po + -rm -f ./$(DEPDIR)/gimpgeglprocedure.Po + -rm -f ./$(DEPDIR)/gradient-editor-actions.Po + -rm -f ./$(DEPDIR)/gradient-editor-commands.Po + -rm -f ./$(DEPDIR)/gradients-actions.Po + -rm -f ./$(DEPDIR)/gradients-commands.Po + -rm -f ./$(DEPDIR)/help-actions.Po + -rm -f ./$(DEPDIR)/help-commands.Po + -rm -f ./$(DEPDIR)/image-actions.Po + -rm -f ./$(DEPDIR)/image-commands.Po + -rm -f ./$(DEPDIR)/images-actions.Po + -rm -f ./$(DEPDIR)/images-commands.Po + -rm -f ./$(DEPDIR)/items-actions.Po + -rm -f ./$(DEPDIR)/items-commands.Po + -rm -f ./$(DEPDIR)/layers-actions.Po + -rm -f ./$(DEPDIR)/layers-commands.Po + -rm -f ./$(DEPDIR)/mypaint-brushes-actions.Po + -rm -f ./$(DEPDIR)/palette-editor-actions.Po + -rm -f ./$(DEPDIR)/palette-editor-commands.Po + -rm -f ./$(DEPDIR)/palettes-actions.Po + -rm -f ./$(DEPDIR)/palettes-commands.Po + -rm -f ./$(DEPDIR)/patterns-actions.Po + -rm -f ./$(DEPDIR)/plug-in-actions.Po + -rm -f ./$(DEPDIR)/plug-in-commands.Po + -rm -f ./$(DEPDIR)/procedure-commands.Po + -rm -f ./$(DEPDIR)/quick-mask-actions.Po + -rm -f ./$(DEPDIR)/quick-mask-commands.Po + -rm -f ./$(DEPDIR)/sample-points-actions.Po + -rm -f ./$(DEPDIR)/sample-points-commands.Po + -rm -f ./$(DEPDIR)/select-actions.Po + -rm -f ./$(DEPDIR)/select-commands.Po + -rm -f ./$(DEPDIR)/templates-actions.Po + -rm -f ./$(DEPDIR)/templates-commands.Po + -rm -f ./$(DEPDIR)/text-editor-actions.Po + -rm -f ./$(DEPDIR)/text-editor-commands.Po + -rm -f ./$(DEPDIR)/text-tool-actions.Po + -rm -f ./$(DEPDIR)/text-tool-commands.Po + -rm -f ./$(DEPDIR)/tool-options-actions.Po + -rm -f ./$(DEPDIR)/tool-options-commands.Po + -rm -f ./$(DEPDIR)/tool-preset-editor-actions.Po + -rm -f ./$(DEPDIR)/tool-preset-editor-commands.Po + -rm -f ./$(DEPDIR)/tool-presets-actions.Po + -rm -f ./$(DEPDIR)/tool-presets-commands.Po + -rm -f ./$(DEPDIR)/tools-actions.Po + -rm -f ./$(DEPDIR)/tools-commands.Po + -rm -f ./$(DEPDIR)/vectors-actions.Po + -rm -f ./$(DEPDIR)/vectors-commands.Po + -rm -f ./$(DEPDIR)/view-actions.Po + -rm -f ./$(DEPDIR)/view-commands.Po + -rm -f ./$(DEPDIR)/window-actions.Po + -rm -f ./$(DEPDIR)/window-commands.Po + -rm -f ./$(DEPDIR)/windows-actions.Po + -rm -f ./$(DEPDIR)/windows-commands.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)/actions.Po + -rm -f ./$(DEPDIR)/brush-editor-actions.Po + -rm -f ./$(DEPDIR)/brushes-actions.Po + -rm -f ./$(DEPDIR)/buffers-actions.Po + -rm -f ./$(DEPDIR)/buffers-commands.Po + -rm -f ./$(DEPDIR)/channels-actions.Po + -rm -f ./$(DEPDIR)/channels-commands.Po + -rm -f ./$(DEPDIR)/colormap-actions.Po + -rm -f ./$(DEPDIR)/colormap-commands.Po + -rm -f ./$(DEPDIR)/context-actions.Po + -rm -f ./$(DEPDIR)/context-commands.Po + -rm -f ./$(DEPDIR)/cursor-info-actions.Po + -rm -f ./$(DEPDIR)/cursor-info-commands.Po + -rm -f ./$(DEPDIR)/dashboard-actions.Po + -rm -f ./$(DEPDIR)/dashboard-commands.Po + -rm -f ./$(DEPDIR)/data-commands.Po + -rm -f ./$(DEPDIR)/data-editor-commands.Po + -rm -f ./$(DEPDIR)/debug-actions.Po + -rm -f ./$(DEPDIR)/debug-commands.Po + -rm -f ./$(DEPDIR)/dialogs-actions.Po + -rm -f ./$(DEPDIR)/dialogs-commands.Po + -rm -f ./$(DEPDIR)/dock-actions.Po + -rm -f ./$(DEPDIR)/dock-commands.Po + -rm -f ./$(DEPDIR)/dockable-actions.Po + -rm -f ./$(DEPDIR)/dockable-commands.Po + -rm -f ./$(DEPDIR)/documents-actions.Po + -rm -f ./$(DEPDIR)/documents-commands.Po + -rm -f ./$(DEPDIR)/drawable-actions.Po + -rm -f ./$(DEPDIR)/drawable-commands.Po + -rm -f ./$(DEPDIR)/dynamics-actions.Po + -rm -f ./$(DEPDIR)/dynamics-editor-actions.Po + -rm -f ./$(DEPDIR)/edit-actions.Po + -rm -f ./$(DEPDIR)/edit-commands.Po + -rm -f ./$(DEPDIR)/error-console-actions.Po + -rm -f ./$(DEPDIR)/error-console-commands.Po + -rm -f ./$(DEPDIR)/file-actions.Po + -rm -f ./$(DEPDIR)/file-commands.Po + -rm -f ./$(DEPDIR)/filters-actions.Po + -rm -f ./$(DEPDIR)/filters-commands.Po + -rm -f ./$(DEPDIR)/fonts-actions.Po + -rm -f ./$(DEPDIR)/gimpgeglprocedure.Po + -rm -f ./$(DEPDIR)/gradient-editor-actions.Po + -rm -f ./$(DEPDIR)/gradient-editor-commands.Po + -rm -f ./$(DEPDIR)/gradients-actions.Po + -rm -f ./$(DEPDIR)/gradients-commands.Po + -rm -f ./$(DEPDIR)/help-actions.Po + -rm -f ./$(DEPDIR)/help-commands.Po + -rm -f ./$(DEPDIR)/image-actions.Po + -rm -f ./$(DEPDIR)/image-commands.Po + -rm -f ./$(DEPDIR)/images-actions.Po + -rm -f ./$(DEPDIR)/images-commands.Po + -rm -f ./$(DEPDIR)/items-actions.Po + -rm -f ./$(DEPDIR)/items-commands.Po + -rm -f ./$(DEPDIR)/layers-actions.Po + -rm -f ./$(DEPDIR)/layers-commands.Po + -rm -f ./$(DEPDIR)/mypaint-brushes-actions.Po + -rm -f ./$(DEPDIR)/palette-editor-actions.Po + -rm -f ./$(DEPDIR)/palette-editor-commands.Po + -rm -f ./$(DEPDIR)/palettes-actions.Po + -rm -f ./$(DEPDIR)/palettes-commands.Po + -rm -f ./$(DEPDIR)/patterns-actions.Po + -rm -f ./$(DEPDIR)/plug-in-actions.Po + -rm -f ./$(DEPDIR)/plug-in-commands.Po + -rm -f ./$(DEPDIR)/procedure-commands.Po + -rm -f ./$(DEPDIR)/quick-mask-actions.Po + -rm -f ./$(DEPDIR)/quick-mask-commands.Po + -rm -f ./$(DEPDIR)/sample-points-actions.Po + -rm -f ./$(DEPDIR)/sample-points-commands.Po + -rm -f ./$(DEPDIR)/select-actions.Po + -rm -f ./$(DEPDIR)/select-commands.Po + -rm -f ./$(DEPDIR)/templates-actions.Po + -rm -f ./$(DEPDIR)/templates-commands.Po + -rm -f ./$(DEPDIR)/text-editor-actions.Po + -rm -f ./$(DEPDIR)/text-editor-commands.Po + -rm -f ./$(DEPDIR)/text-tool-actions.Po + -rm -f ./$(DEPDIR)/text-tool-commands.Po + -rm -f ./$(DEPDIR)/tool-options-actions.Po + -rm -f ./$(DEPDIR)/tool-options-commands.Po + -rm -f ./$(DEPDIR)/tool-preset-editor-actions.Po + -rm -f ./$(DEPDIR)/tool-preset-editor-commands.Po + -rm -f ./$(DEPDIR)/tool-presets-actions.Po + -rm -f ./$(DEPDIR)/tool-presets-commands.Po + -rm -f ./$(DEPDIR)/tools-actions.Po + -rm -f ./$(DEPDIR)/tools-commands.Po + -rm -f ./$(DEPDIR)/vectors-actions.Po + -rm -f ./$(DEPDIR)/vectors-commands.Po + -rm -f ./$(DEPDIR)/view-actions.Po + -rm -f ./$(DEPDIR)/view-commands.Po + -rm -f ./$(DEPDIR)/window-actions.Po + -rm -f ./$(DEPDIR)/window-commands.Po + -rm -f ./$(DEPDIR)/windows-actions.Po + -rm -f ./$(DEPDIR)/windows-commands.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/app/actions/actions-types.h b/app/actions/actions-types.h new file mode 100644 index 0000000..383aada --- /dev/null +++ b/app/actions/actions-types.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 . + */ + +#ifndef __ACTIONS_TYPES_H__ +#define __ACTIONS_TYPES_H__ + + +#include "dialogs/dialogs-types.h" +#include "tools/tools-types.h" + + +typedef enum +{ + GIMP_ACTION_SELECT_SET = 0, + GIMP_ACTION_SELECT_SET_TO_DEFAULT = -1, + GIMP_ACTION_SELECT_FIRST = -2, + GIMP_ACTION_SELECT_LAST = -3, + GIMP_ACTION_SELECT_SMALL_PREVIOUS = -4, + GIMP_ACTION_SELECT_SMALL_NEXT = -5, + GIMP_ACTION_SELECT_PREVIOUS = -6, + GIMP_ACTION_SELECT_NEXT = -7, + GIMP_ACTION_SELECT_SKIP_PREVIOUS = -8, + GIMP_ACTION_SELECT_SKIP_NEXT = -9, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS = -10, + GIMP_ACTION_SELECT_PERCENT_NEXT = -11 +} GimpActionSelectType; + +typedef enum +{ + GIMP_SAVE_MODE_SAVE, + GIMP_SAVE_MODE_SAVE_AS, + GIMP_SAVE_MODE_SAVE_A_COPY, + GIMP_SAVE_MODE_SAVE_AND_CLOSE, + GIMP_SAVE_MODE_EXPORT, + GIMP_SAVE_MODE_EXPORT_AS, + GIMP_SAVE_MODE_OVERWRITE +} GimpSaveMode; + + +#endif /* __ACTIONS_TYPES_H__ */ diff --git a/app/actions/actions.c b/app/actions/actions.c new file mode 100644 index 0000000..cf8d6dc --- /dev/null +++ b/app/actions/actions.c @@ -0,0 +1,737 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimptooloptions.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimpactionfactory.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpcontainereditor.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockable.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimpimageeditor.h" +#include "widgets/gimpitemtreeview.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpimagewindow.h" +#include "display/gimpnavigationeditor.h" +#include "display/gimpstatusbar.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "brush-editor-actions.h" +#include "brushes-actions.h" +#include "buffers-actions.h" +#include "channels-actions.h" +#include "colormap-actions.h" +#include "context-actions.h" +#include "cursor-info-actions.h" +#include "dashboard-actions.h" +#include "debug-actions.h" +#include "dialogs-actions.h" +#include "dock-actions.h" +#include "dockable-actions.h" +#include "documents-actions.h" +#include "drawable-actions.h" +#include "dynamics-actions.h" +#include "dynamics-editor-actions.h" +#include "edit-actions.h" +#include "error-console-actions.h" +#include "file-actions.h" +#include "filters-actions.h" +#include "fonts-actions.h" +#include "gradient-editor-actions.h" +#include "gradients-actions.h" +#include "help-actions.h" +#include "image-actions.h" +#include "images-actions.h" +#include "layers-actions.h" +#include "mypaint-brushes-actions.h" +#include "palette-editor-actions.h" +#include "palettes-actions.h" +#include "patterns-actions.h" +#include "plug-in-actions.h" +#include "quick-mask-actions.h" +#include "sample-points-actions.h" +#include "select-actions.h" +#include "templates-actions.h" +#include "text-editor-actions.h" +#include "text-tool-actions.h" +#include "tool-options-actions.h" +#include "tool-presets-actions.h" +#include "tool-preset-editor-actions.h" +#include "tools-actions.h" +#include "vectors-actions.h" +#include "view-actions.h" +#include "windows-actions.h" + +#include "gimp-intl.h" + + +/* global variables */ + +GimpActionFactory *global_action_factory = NULL; + + +/* private variables */ + +static const GimpActionFactoryEntry action_groups[] = +{ + { "brush-editor", N_("Brush Editor"), GIMP_ICON_BRUSH, + brush_editor_actions_setup, + brush_editor_actions_update }, + { "brushes", N_("Brushes"), GIMP_ICON_BRUSH, + brushes_actions_setup, + brushes_actions_update }, + { "buffers", N_("Buffers"), GIMP_ICON_BUFFER, + buffers_actions_setup, + buffers_actions_update }, + { "channels", N_("Channels"), GIMP_ICON_CHANNEL, + channels_actions_setup, + channels_actions_update }, + { "colormap", N_("Colormap"), GIMP_ICON_COLORMAP, + colormap_actions_setup, + colormap_actions_update }, + { "context", N_("Context"), GIMP_ICON_DIALOG_TOOL_OPTIONS /* well... */, + context_actions_setup, + context_actions_update }, + { "cursor-info", N_("Pointer Information"), NULL, + cursor_info_actions_setup, + cursor_info_actions_update }, + { "dashboard", N_("Dashboard"), GIMP_ICON_DIALOG_DASHBOARD, + dashboard_actions_setup, + dashboard_actions_update }, + { "debug", N_("Debug"), NULL, + debug_actions_setup, + debug_actions_update }, + { "dialogs", N_("Dialogs"), NULL, + dialogs_actions_setup, + dialogs_actions_update }, + { "dock", N_("Dock"), NULL, + dock_actions_setup, + dock_actions_update }, + { "dockable", N_("Dockable"), NULL, + dockable_actions_setup, + dockable_actions_update }, + { "documents", N_("Document History"), NULL, + documents_actions_setup, + documents_actions_update }, + { "drawable", N_("Drawable"), GIMP_ICON_LAYER, + drawable_actions_setup, + drawable_actions_update }, + { "dynamics", N_("Paint Dynamics"), GIMP_ICON_DYNAMICS, + dynamics_actions_setup, + dynamics_actions_update }, + { "dynamics-editor", N_("Paint Dynamics Editor"), GIMP_ICON_DYNAMICS, + dynamics_editor_actions_setup, + dynamics_editor_actions_update }, + { "edit", N_("Edit"), GIMP_ICON_EDIT, + edit_actions_setup, + edit_actions_update }, + { "error-console", N_("Error Console"), GIMP_ICON_DIALOG_WARNING, + error_console_actions_setup, + error_console_actions_update }, + { "file", N_("File"), "text-x-generic", + file_actions_setup, + file_actions_update }, + { "filters", N_("Filters"), GIMP_ICON_GEGL, + filters_actions_setup, + filters_actions_update }, + { "fonts", N_("Fonts"), GIMP_ICON_FONT, + fonts_actions_setup, + fonts_actions_update }, + { "gradient-editor", N_("Gradient Editor"), GIMP_ICON_GRADIENT, + gradient_editor_actions_setup, + gradient_editor_actions_update }, + { "gradients", N_("Gradients"), GIMP_ICON_GRADIENT, + gradients_actions_setup, + gradients_actions_update }, + { "tool-presets", N_("Tool Presets"), GIMP_ICON_TOOL_PRESET, + tool_presets_actions_setup, + tool_presets_actions_update }, + { "tool-preset-editor", N_("Tool Preset Editor"), GIMP_ICON_TOOL_PRESET, + tool_preset_editor_actions_setup, + tool_preset_editor_actions_update }, + { "help", N_("Help"), "help-browser", + help_actions_setup, + help_actions_update }, + { "image", N_("Image"), GIMP_ICON_IMAGE, + image_actions_setup, + image_actions_update }, + { "images", N_("Images"), GIMP_ICON_IMAGE, + images_actions_setup, + images_actions_update }, + { "layers", N_("Layers"), GIMP_ICON_LAYER, + layers_actions_setup, + layers_actions_update }, + { "mypaint-brushes", N_("MyPaint Brushes"), GIMP_ICON_MYPAINT_BRUSH, + mypaint_brushes_actions_setup, + mypaint_brushes_actions_update }, + { "palette-editor", N_("Palette Editor"), GIMP_ICON_PALETTE, + palette_editor_actions_setup, + palette_editor_actions_update }, + { "palettes", N_("Palettes"), GIMP_ICON_PALETTE, + palettes_actions_setup, + palettes_actions_update }, + { "patterns", N_("Patterns"), GIMP_ICON_PATTERN, + patterns_actions_setup, + patterns_actions_update }, + { "plug-in", N_("Plug-ins"), GIMP_ICON_PLUGIN, + plug_in_actions_setup, + plug_in_actions_update }, + { "quick-mask", N_("Quick Mask"), GIMP_ICON_QUICK_MASK_ON, + quick_mask_actions_setup, + quick_mask_actions_update }, + { "sample-points", N_("Sample Points"), GIMP_ICON_SAMPLE_POINT, + sample_points_actions_setup, + sample_points_actions_update }, + { "select", N_("Select"), GIMP_ICON_SELECTION, + select_actions_setup, + select_actions_update }, + { "templates", N_("Templates"), GIMP_ICON_TEMPLATE, + templates_actions_setup, + templates_actions_update }, + { "text-tool", N_("Text Tool"), GIMP_ICON_EDIT, + text_tool_actions_setup, + text_tool_actions_update }, + { "text-editor", N_("Text Editor"), GIMP_ICON_EDIT, + text_editor_actions_setup, + text_editor_actions_update }, + { "tool-options", N_("Tool Options"), GIMP_ICON_DIALOG_TOOL_OPTIONS, + tool_options_actions_setup, + tool_options_actions_update }, + { "tools", N_("Tools"), GIMP_ICON_DIALOG_TOOLS, + tools_actions_setup, + tools_actions_update }, + { "vectors", N_("Paths"), GIMP_ICON_PATH, + vectors_actions_setup, + vectors_actions_update }, + { "view", N_("View"), GIMP_ICON_VISIBLE, + view_actions_setup, + view_actions_update }, + { "windows", N_("Windows"), NULL, + windows_actions_setup, + windows_actions_update } +}; + + +/* public functions */ + +void +actions_init (Gimp *gimp) +{ + gint i; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (global_action_factory == NULL); + + global_action_factory = gimp_action_factory_new (gimp); + + for (i = 0; i < G_N_ELEMENTS (action_groups); i++) + gimp_action_factory_group_register (global_action_factory, + action_groups[i].identifier, + gettext (action_groups[i].label), + action_groups[i].icon_name, + action_groups[i].setup_func, + action_groups[i].update_func); +} + +void +actions_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (global_action_factory != NULL); + g_return_if_fail (global_action_factory->gimp == gimp); + + g_clear_object (&global_action_factory); +} + +Gimp * +action_data_get_gimp (gpointer data) +{ + Gimp *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (GIMP_IS_GIMP (data)) + result = data; + + if (! result) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + result = display->gimp; + } + + if (! result) + { + GimpContext *context = action_data_get_context (data); + + if (context) + result = context->gimp; + } + + recursion = FALSE; + + return result; +} + +GimpContext * +action_data_get_context (gpointer data) +{ + GimpContext *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (GIMP_IS_DOCK (data)) + result = gimp_dock_get_context ((GimpDock *) data); + else if (GIMP_IS_DOCK_WINDOW (data)) + result = gimp_dock_window_get_context (((GimpDockWindow *) data)); + else if (GIMP_IS_CONTAINER_VIEW (data)) + result = gimp_container_view_get_context ((GimpContainerView *) data); + else if (GIMP_IS_CONTAINER_EDITOR (data)) + result = gimp_container_view_get_context (((GimpContainerEditor *) data)->view); + else if (GIMP_IS_IMAGE_EDITOR (data)) + result = ((GimpImageEditor *) data)->context; + else if (GIMP_IS_NAVIGATION_EDITOR (data)) + result = ((GimpNavigationEditor *) data)->context; + + if (! result) + { + Gimp *gimp = action_data_get_gimp (data); + + if (gimp) + result = gimp_get_user_context (gimp); + } + + recursion = FALSE; + + return result; +} + +GimpImage * +action_data_get_image (gpointer data) +{ + GimpImage *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (GIMP_IS_ITEM_TREE_VIEW (data)) + result = gimp_item_tree_view_get_image ((GimpItemTreeView *) data); + else if (GIMP_IS_IMAGE_EDITOR (data)) + result = ((GimpImageEditor *) data)->image; + + if (! result) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + result = gimp_display_get_image (display); + } + + if (! result) + { + GimpContext *context = action_data_get_context (data); + + if (context) + result = gimp_context_get_image (context); + } + + recursion = FALSE; + + return result; +} + +GimpDisplay * +action_data_get_display (gpointer data) +{ + GimpDisplay *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (GIMP_IS_DISPLAY (data)) + result = data; + else if (GIMP_IS_IMAGE_WINDOW (data)) + { + GimpDisplayShell *shell = gimp_image_window_get_active_shell (data); + result = shell ? shell->display : NULL; + } + + if (! result) + { + GimpContext *context = action_data_get_context (data); + + if (context) + result = gimp_context_get_display (context); + } + + recursion = FALSE; + + return result; +} + +GimpDisplayShell * +action_data_get_shell (gpointer data) +{ + GimpDisplayShell *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (! result) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + result = gimp_display_get_shell (display); + } + + recursion = FALSE; + + return result; +} + +GtkWidget * +action_data_get_widget (gpointer data) +{ + GtkWidget *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) + return NULL; + + recursion = TRUE; + + if (GTK_IS_WIDGET (data)) + result = data; + + if (! result) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + result = GTK_WIDGET (gimp_display_get_shell (display)); + } + + if (! result) + result = dialogs_get_toolbox (); + + recursion = FALSE; + + return result; +} + +gint +action_data_sel_count (gpointer data) +{ + if (GIMP_IS_CONTAINER_EDITOR (data)) + { + GimpContainerEditor *editor; + + editor = GIMP_CONTAINER_EDITOR (data); + return gimp_container_view_get_selected (editor->view, NULL); + } + else + { + return 0; + } +} + +gdouble +action_select_value (GimpActionSelectType select_type, + gdouble value, + gdouble min, + gdouble max, + gdouble def, + gdouble small_inc, + gdouble inc, + gdouble skip_inc, + gdouble delta_factor, + gboolean wrap) +{ + switch (select_type) + { + case GIMP_ACTION_SELECT_SET_TO_DEFAULT: + value = def; + break; + + case GIMP_ACTION_SELECT_FIRST: + value = min; + break; + + case GIMP_ACTION_SELECT_LAST: + value = max; + break; + + case GIMP_ACTION_SELECT_SMALL_PREVIOUS: + value -= small_inc; + break; + + case GIMP_ACTION_SELECT_SMALL_NEXT: + value += small_inc; + break; + + case GIMP_ACTION_SELECT_PREVIOUS: + value -= inc; + break; + + case GIMP_ACTION_SELECT_NEXT: + value += inc; + break; + + case GIMP_ACTION_SELECT_SKIP_PREVIOUS: + value -= skip_inc; + break; + + case GIMP_ACTION_SELECT_SKIP_NEXT: + value += skip_inc; + break; + + case GIMP_ACTION_SELECT_PERCENT_PREVIOUS: + g_return_val_if_fail (delta_factor >= 0.0, value); + value /= (1.0 + delta_factor); + break; + + case GIMP_ACTION_SELECT_PERCENT_NEXT: + g_return_val_if_fail (delta_factor >= 0.0, value); + value *= (1.0 + delta_factor); + break; + + default: + if ((gint) select_type >= 0) + value = (gdouble) select_type * (max - min) / 1000.0 + min; + else + g_return_val_if_reached (value); + break; + } + + if (wrap) + { + while (value < min) + value = max - (min - value); + + while (value > max) + value = min + (value - max); + } + else + { + value = CLAMP (value, min, max); + } + + return value; +} + +void +action_select_property (GimpActionSelectType select_type, + GimpDisplay *display, + GObject *object, + const gchar *property_name, + gdouble small_inc, + gdouble inc, + gdouble skip_inc, + gdouble delta_factor, + gboolean wrap) +{ + GParamSpec *pspec; + + g_return_if_fail (display == NULL || GIMP_IS_DISPLAY (display)); + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (property_name != NULL); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), + property_name); + + if (G_IS_PARAM_SPEC_DOUBLE (pspec)) + { + gdouble value; + + g_object_get (object, property_name, &value, NULL); + + value = action_select_value (select_type, + value, + G_PARAM_SPEC_DOUBLE (pspec)->minimum, + G_PARAM_SPEC_DOUBLE (pspec)->maximum, + G_PARAM_SPEC_DOUBLE (pspec)->default_value, + small_inc, inc, skip_inc, delta_factor, wrap); + + g_object_set (object, property_name, value, NULL); + + if (display) + { + const gchar *blurb = g_param_spec_get_blurb (pspec); + + if (blurb) + { + /* value description and new value shown in the status bar */ + action_message (display, object, _("%s: %.2f"), blurb, value); + } + } + } + else if (G_IS_PARAM_SPEC_INT (pspec)) + { + gint value; + + g_object_get (object, property_name, &value, NULL); + + value = action_select_value (select_type, + value, + G_PARAM_SPEC_INT (pspec)->minimum, + G_PARAM_SPEC_INT (pspec)->maximum, + G_PARAM_SPEC_INT (pspec)->default_value, + small_inc, inc, skip_inc, delta_factor, wrap); + + g_object_set (object, property_name, value, NULL); + + if (display) + { + const gchar *blurb = g_param_spec_get_blurb (pspec); + + if (blurb) + { + /* value description and new value shown in the status bar */ + action_message (display, object, _("%s: %d"), blurb, value); + } + } + } + else + { + g_return_if_reached (); + } +} + +GimpObject * +action_select_object (GimpActionSelectType select_type, + GimpContainer *container, + GimpObject *current) +{ + gint select_index; + gint n_children; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (current == NULL || GIMP_IS_OBJECT (current), NULL); + + if (! current) + return NULL; + + n_children = gimp_container_get_n_children (container); + + if (n_children == 0) + return NULL; + + switch (select_type) + { + case GIMP_ACTION_SELECT_FIRST: + select_index = 0; + break; + + case GIMP_ACTION_SELECT_LAST: + select_index = n_children - 1; + break; + + case GIMP_ACTION_SELECT_PREVIOUS: + select_index = gimp_container_get_child_index (container, current) - 1; + break; + + case GIMP_ACTION_SELECT_NEXT: + select_index = gimp_container_get_child_index (container, current) + 1; + break; + + case GIMP_ACTION_SELECT_SKIP_PREVIOUS: + select_index = gimp_container_get_child_index (container, current) - 10; + break; + + case GIMP_ACTION_SELECT_SKIP_NEXT: + select_index = gimp_container_get_child_index (container, current) + 10; + break; + + default: + if ((gint) select_type >= 0) + select_index = (gint) select_type; + else + g_return_val_if_reached (current); + break; + } + + select_index = CLAMP (select_index, 0, n_children - 1); + + return gimp_container_get_child_by_index (container, select_index); +} + +void +action_message (GimpDisplay *display, + GObject *object, + const gchar *format, + ...) +{ + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + const gchar *icon_name = NULL; + va_list args; + + if (GIMP_IS_TOOL_OPTIONS (object)) + { + GimpToolInfo *tool_info = GIMP_TOOL_OPTIONS (object)->tool_info; + + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + } + else if (GIMP_IS_VIEWABLE (object)) + { + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (object)); + } + + va_start (args, format); + gimp_statusbar_push_temp_valist (statusbar, GIMP_MESSAGE_INFO, + icon_name, format, args); + va_end (args); +} diff --git a/app/actions/actions.h b/app/actions/actions.h new file mode 100644 index 0000000..10e1e22 --- /dev/null +++ b/app/actions/actions.h @@ -0,0 +1,120 @@ +/* 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 . + */ + +#ifndef __ACTIONS_H__ +#define __ACTIONS_H__ + + +extern GimpActionFactory *global_action_factory; + + +void actions_init (Gimp *gimp); +void actions_exit (Gimp *gimp); + +Gimp * action_data_get_gimp (gpointer data); +GimpContext * action_data_get_context (gpointer data); +GimpImage * action_data_get_image (gpointer data); +GimpDisplay * action_data_get_display (gpointer data); +GimpDisplayShell * action_data_get_shell (gpointer data); +GtkWidget * action_data_get_widget (gpointer data); +gint action_data_sel_count (gpointer data); + +gdouble action_select_value (GimpActionSelectType select_type, + gdouble value, + gdouble min, + gdouble max, + gdouble def, + gdouble small_inc, + gdouble inc, + gdouble skip_inc, + gdouble delta_factor, + gboolean wrap); +void action_select_property (GimpActionSelectType select_type, + GimpDisplay *display, + GObject *object, + const gchar *property_name, + gdouble small_inc, + gdouble inc, + gdouble skip_inc, + gdouble delta_factor, + gboolean wrap); +GimpObject * action_select_object (GimpActionSelectType select_type, + GimpContainer *container, + GimpObject *current); +void action_message (GimpDisplay *display, + GObject *object, + const gchar *format, + ...) G_GNUC_PRINTF(3,4); + + +#define return_if_no_gimp(gimp,data) \ + gimp = action_data_get_gimp (data); \ + if (! gimp) \ + return + +#define return_if_no_context(context,data) \ + context = action_data_get_context (data); \ + if (! context) \ + return + +#define return_if_no_image(image,data) \ + image = action_data_get_image (data); \ + if (! image) \ + return + +#define return_if_no_display(display,data) \ + display = action_data_get_display (data); \ + if (! display) \ + return + +#define return_if_no_shell(shell,data) \ + shell = action_data_get_shell (data); \ + if (! shell) \ + return + +#define return_if_no_widget(widget,data) \ + widget = action_data_get_widget (data); \ + if (! widget) \ + return + + +#define return_if_no_drawable(image,drawable,data) \ + return_if_no_image (image,data); \ + drawable = gimp_image_get_active_drawable (image); \ + if (! drawable) \ + return + +#define return_if_no_layer(image,layer,data) \ + return_if_no_image (image,data); \ + layer = gimp_image_get_active_layer (image); \ + if (! layer) \ + return + +#define return_if_no_channel(image,channel,data) \ + return_if_no_image (image,data); \ + channel = gimp_image_get_active_channel (image); \ + if (! channel) \ + return + +#define return_if_no_vectors(image,vectors,data) \ + return_if_no_image (image,data); \ + vectors = gimp_image_get_active_vectors (image); \ + if (! vectors) \ + return + + +#endif /* __ACTIONS_H__ */ diff --git a/app/actions/brush-editor-actions.c b/app/actions/brush-editor-actions.c new file mode 100644 index 0000000..f90de3a --- /dev/null +++ b/app/actions/brush-editor-actions.c @@ -0,0 +1,87 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpbrusheditor.h" + +#include "brush-editor-actions.h" +#include "data-editor-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry brush_editor_actions[] = +{ + { "brush-editor-popup", GIMP_ICON_BRUSH, + NC_("brush-editor-action", "Brush Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_BRUSH_EDITOR_DIALOG } +}; + +static const GimpToggleActionEntry brush_editor_toggle_actions[] = +{ + { "brush-editor-edit-active", GIMP_ICON_LINKED, + NC_("brush-editor-action", "Edit Active Brush"), NULL, NULL, + data_editor_edit_active_cmd_callback, + FALSE, + GIMP_HELP_BRUSH_EDITOR_EDIT_ACTIVE } +}; + + +void +brush_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "brush-editor-action", + brush_editor_actions, + G_N_ELEMENTS (brush_editor_actions)); + + gimp_action_group_add_toggle_actions (group, "brush-editor-action", + brush_editor_toggle_actions, + G_N_ELEMENTS (brush_editor_toggle_actions)); +} + +void +brush_editor_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (user_data); + gboolean edit_active = FALSE; + + edit_active = gimp_data_editor_get_edit_active (data_editor); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_ACTIVE ("brush-editor-edit-active", edit_active); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +} diff --git a/app/actions/brush-editor-actions.h b/app/actions/brush-editor-actions.h new file mode 100644 index 0000000..9b4eb6f --- /dev/null +++ b/app/actions/brush-editor-actions.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 . + */ + +#ifndef __BRUSH_EDITOR_ACTIONS_H__ +#define __BRUSH_EDITOR_ACTIONS_H__ + + +void brush_editor_actions_setup (GimpActionGroup *group); +void brush_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __BRUSH_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/brushes-actions.c b/app/actions/brushes-actions.c new file mode 100644 index 0000000..87daed6 --- /dev/null +++ b/app/actions/brushes-actions.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpbrushgenerated.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "brushes-actions.h" +#include "data-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry brushes_actions[] = +{ + { "brushes-popup", GIMP_ICON_BRUSH, + NC_("brushes-action", "Brushes Menu"), NULL, NULL, NULL, + GIMP_HELP_BRUSH_DIALOG }, + + { "brushes-open-as-image", GIMP_ICON_DOCUMENT_OPEN, + NC_("brushes-action", "_Open Brush as Image"), NULL, + NC_("brushes-action", "Open brush as image"), + data_open_as_image_cmd_callback, + GIMP_HELP_BRUSH_OPEN_AS_IMAGE }, + + { "brushes-new", GIMP_ICON_DOCUMENT_NEW, + NC_("brushes-action", "_New Brush"), NULL, + NC_("brushes-action", "Create a new brush"), + data_new_cmd_callback, + GIMP_HELP_BRUSH_NEW }, + + { "brushes-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("brushes-action", "D_uplicate Brush"), NULL, + NC_("brushes-action", "Duplicate this brush"), + data_duplicate_cmd_callback, + GIMP_HELP_BRUSH_DUPLICATE }, + + { "brushes-copy-location", GIMP_ICON_EDIT_COPY, + NC_("brushes-action", "Copy Brush _Location"), NULL, + NC_("brushes-action", "Copy brush file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_BRUSH_COPY_LOCATION }, + + { "brushes-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("brushes-action", "Show in _File Manager"), NULL, + NC_("brushes-action", "Show brush file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_BRUSH_SHOW_IN_FILE_MANAGER }, + + { "brushes-delete", GIMP_ICON_EDIT_DELETE, + NC_("brushes-action", "_Delete Brush"), NULL, + NC_("brushes-action", "Delete this brush"), + data_delete_cmd_callback, + GIMP_HELP_BRUSH_DELETE }, + + { "brushes-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("brushes-action", "_Refresh Brushes"), NULL, + NC_("brushes-action", "Refresh brushes"), + data_refresh_cmd_callback, + GIMP_HELP_BRUSH_REFRESH } +}; + +static const GimpStringActionEntry brushes_edit_actions[] = +{ + { "brushes-edit", GIMP_ICON_EDIT, + NC_("brushes-action", "_Edit Brush..."), NULL, + NC_("brushes-action", "Edit this brush"), + "gimp-brush-editor", + GIMP_HELP_BRUSH_EDIT } +}; + + +void +brushes_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "brushes-action", + brushes_actions, + G_N_ELEMENTS (brushes_actions)); + + gimp_action_group_add_string_actions (group, "brushes-action", + brushes_edit_actions, + G_N_ELEMENTS (brushes_edit_actions), + data_edit_cmd_callback); +} + +void +brushes_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpBrush *brush = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + brush = gimp_context_get_brush (context); + + if (action_data_sel_count (user_data) > 1) + { + brush = NULL; + } + + if (brush) + { + data = GIMP_DATA (brush); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("brushes-edit", brush); + SET_SENSITIVE ("brushes-open-as-image", file && ! GIMP_IS_BRUSH_GENERATED (brush)); + SET_SENSITIVE ("brushes-duplicate", brush && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("brushes-copy-location", file); + SET_SENSITIVE ("brushes-show-in-file-manager", file); + SET_SENSITIVE ("brushes-delete", brush && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/brushes-actions.h b/app/actions/brushes-actions.h new file mode 100644 index 0000000..1d292a2 --- /dev/null +++ b/app/actions/brushes-actions.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 . + */ + +#ifndef __BRUSHES_ACTIONS_H__ +#define __BRUSHES_ACTIONS_H__ + + +void brushes_actions_setup (GimpActionGroup *group); +void brushes_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __BRUSHES_ACTIONS_H__ */ diff --git a/app/actions/buffers-actions.c b/app/actions/buffers-actions.c new file mode 100644 index 0000000..6fbd2c1 --- /dev/null +++ b/app/actions/buffers-actions.c @@ -0,0 +1,136 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "buffers-actions.h" +#include "buffers-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry buffers_actions[] = +{ + { "buffers-popup", GIMP_ICON_BUFFER, + NC_("buffers-action", "Buffers Menu"), NULL, NULL, NULL, + GIMP_HELP_BUFFER_DIALOG }, + + { "buffers-paste-as-new-image", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("buffers-action", "Paste Buffer as _New Image"), NULL, + NC_("buffers-action", "Paste the selected buffer as a new image"), + buffers_paste_as_new_image_cmd_callback, + GIMP_HELP_BUFFER_PASTE_AS_NEW_IMAGE }, + + { "buffers-delete", GIMP_ICON_EDIT_DELETE, + NC_("buffers-action", "_Delete Buffer"), NULL, + NC_("buffers-action", "Delete the selected buffer"), + buffers_delete_cmd_callback, + GIMP_HELP_BUFFER_DELETE } +}; + +static const GimpEnumActionEntry buffers_paste_actions[] = +{ + { "buffers-paste", GIMP_ICON_EDIT_PASTE, + NC_("buffers-action", "_Paste Buffer"), NULL, + NC_("buffers-action", "Paste the selected buffer"), + GIMP_PASTE_TYPE_FLOATING, FALSE, + GIMP_HELP_BUFFER_PASTE }, + + { "buffers-paste-in-place", GIMP_ICON_EDIT_PASTE, + NC_("buffers-action", "Paste Buffer In Pl_ace"), NULL, + NC_("buffers-action", "Paste the selected buffer at its original position"), + GIMP_PASTE_TYPE_FLOATING_IN_PLACE, FALSE, + GIMP_HELP_BUFFER_PASTE_IN_PLACE }, + + { "buffers-paste-into", GIMP_ICON_EDIT_PASTE_INTO, + NC_("buffers-action", "Paste Buffer _Into The Selection"), NULL, + NC_("buffers-action", "Paste the selected buffer into the selection"), + GIMP_PASTE_TYPE_FLOATING_INTO, FALSE, + GIMP_HELP_BUFFER_PASTE_INTO }, + + { "buffers-paste-into-in-place", GIMP_ICON_EDIT_PASTE_INTO, + NC_("buffers-action", "Paste Buffer Into The Selection In Place"), NULL, + NC_("buffers-action", + "Paste the selected buffer into the selection at its original position"), + GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE, FALSE, + GIMP_HELP_BUFFER_PASTE_INTO_IN_PLACE }, + + { "buffers-paste-as-new-layer", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("buffers-action", "Paste Buffer as New _Layer"), NULL, + NC_("buffers-action", "Paste the selected buffer as a new layer"), + GIMP_PASTE_TYPE_NEW_LAYER, FALSE, + GIMP_HELP_BUFFER_PASTE_AS_NEW_LAYER }, + + { "buffers-paste-as-new-layer-in-place", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("buffers-action", "Paste Buffer as New Layer in Place"), NULL, + NC_("buffers-action", + "Paste the selected buffer as a new layer at its original position"), + GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, FALSE, + GIMP_HELP_BUFFER_PASTE_AS_NEW_LAYER_IN_PLACE }, +}; + + +void +buffers_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "buffers-action", + buffers_actions, + G_N_ELEMENTS (buffers_actions)); + + gimp_action_group_add_enum_actions (group, "buffers-action", + buffers_paste_actions, + G_N_ELEMENTS (buffers_paste_actions), + buffers_paste_cmd_callback); +} + +void +buffers_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpContext *context = action_data_get_context (data); + GimpBuffer *buffer = NULL; + + if (context) + buffer = gimp_context_get_buffer (context); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("buffers-paste", buffer); + SET_SENSITIVE ("buffers-paste-in-place", buffer); + SET_SENSITIVE ("buffers-paste-into", buffer); + SET_SENSITIVE ("buffers-paste-into-in-place", buffer); + SET_SENSITIVE ("buffers-paste-as-new-layer", buffer); + SET_SENSITIVE ("buffers-paste-as-new-layer-in-place", buffer); + SET_SENSITIVE ("buffers-paste-as-new-image", buffer); + SET_SENSITIVE ("buffers-delete", buffer); + +#undef SET_SENSITIVE +} diff --git a/app/actions/buffers-actions.h b/app/actions/buffers-actions.h new file mode 100644 index 0000000..6a02344 --- /dev/null +++ b/app/actions/buffers-actions.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 . + */ + +#ifndef __BUFFERS_ACTIONS_H__ +#define __BUFFERS_ACTIONS_H__ + + +void buffers_actions_setup (GimpActionGroup *group); +void buffers_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __BUFFERS_ACTIONS_H__ */ diff --git a/app/actions/buffers-commands.c b/app/actions/buffers-commands.c new file mode 100644 index 0000000..d3398df --- /dev/null +++ b/app/actions/buffers-commands.c @@ -0,0 +1,138 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimp-edit.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimpcontainereditor.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpcontainerview-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-transform.h" + +#include "buffers-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +buffers_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpBuffer *buffer; + GimpPasteType paste_type = (GimpPasteType) g_variant_get_int32 (value); + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + buffer = gimp_context_get_buffer (context); + + if (buffer && gimp_container_have (container, GIMP_OBJECT (buffer))) + { + GimpDisplay *display = gimp_context_get_display (context); + GimpImage *image = NULL; + gint x = -1; + gint y = -1; + gint width = -1; + gint height = -1; + + if (display) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + + gimp_display_shell_untransform_viewport ( + shell, + ! gimp_display_shell_get_infinite_canvas (shell), + &x, &y, &width, &height); + + image = gimp_display_get_image (display); + } + else + { + image = gimp_context_get_image (context); + } + + if (image) + { + gimp_edit_paste (image, gimp_image_get_active_drawable (image), + GIMP_OBJECT (buffer), paste_type, + x, y, width, height); + + gimp_image_flush (image); + } + } +} + +void +buffers_paste_as_new_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpBuffer *buffer; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + buffer = gimp_context_get_buffer (context); + + if (buffer && gimp_container_have (container, GIMP_OBJECT (buffer))) + { + GtkWidget *widget = GTK_WIDGET (editor); + GimpImage *new_image; + + new_image = gimp_edit_paste_as_new_image (context->gimp, + GIMP_OBJECT (buffer)); + gimp_create_display (context->gimp, new_image, + GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (new_image); + } +} + +void +buffers_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + + gimp_container_view_remove_active (editor->view); +} diff --git a/app/actions/buffers-commands.h b/app/actions/buffers-commands.h new file mode 100644 index 0000000..a5e7339 --- /dev/null +++ b/app/actions/buffers-commands.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 . + */ + +#ifndef __BUFFERS_COMMANDS_H__ +#define __BUFFERS_COMMANDS_H__ + + +void buffers_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void buffers_paste_as_new_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void buffers_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __BUFFERS_COMMANDS_H__ */ diff --git a/app/actions/channels-actions.c b/app/actions/channels-actions.c new file mode 100644 index 0000000..533e794 --- /dev/null +++ b/app/actions/channels-actions.c @@ -0,0 +1,348 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpimage.h" +#include "core/gimpitem.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpcomponenteditor.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "channels-actions.h" +#include "channels-commands.h" +#include "items-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry channels_actions[] = +{ + { "channels-popup", GIMP_ICON_DIALOG_CHANNELS, + NC_("channels-action", "Channels Menu"), NULL, NULL, NULL, + GIMP_HELP_CHANNEL_DIALOG }, + + { "channels-color-tag-menu", NULL, + NC_("channels-action", "Color Tag"), NULL, NULL, NULL, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-edit-attributes", GIMP_ICON_EDIT, + NC_("channels-action", "_Edit Channel Attributes..."), NULL, + NC_("channels-action", "Edit the channel's name, color and opacity"), + channels_edit_attributes_cmd_callback, + GIMP_HELP_CHANNEL_EDIT }, + + { "channels-new", GIMP_ICON_DOCUMENT_NEW, + NC_("channels-action", "_New Channel..."), NULL, + NC_("channels-action", "Create a new channel"), + channels_new_cmd_callback, + GIMP_HELP_CHANNEL_NEW }, + + { "channels-new-last-values", GIMP_ICON_DOCUMENT_NEW, + NC_("channels-action", "_New Channel"), NULL, + NC_("channels-action", "Create a new channel with last used values"), + channels_new_last_vals_cmd_callback, + GIMP_HELP_CHANNEL_NEW }, + + { "channels-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("channels-action", "D_uplicate Channel"), NULL, + NC_("channels-action", + "Create a duplicate of this channel and add it to the image"), + channels_duplicate_cmd_callback, + GIMP_HELP_CHANNEL_DUPLICATE }, + + { "channels-delete", GIMP_ICON_EDIT_DELETE, + NC_("channels-action", "_Delete Channel"), NULL, + NC_("channels-action", "Delete this channel"), + channels_delete_cmd_callback, + GIMP_HELP_CHANNEL_DELETE }, + + { "channels-raise", GIMP_ICON_GO_UP, + NC_("channels-action", "_Raise Channel"), NULL, + NC_("channels-action", "Raise this channel one step in the channel stack"), + channels_raise_cmd_callback, + GIMP_HELP_CHANNEL_RAISE }, + + { "channels-raise-to-top", GIMP_ICON_GO_TOP, + NC_("channels-action", "Raise Channel to _Top"), NULL, + NC_("channels-action", + "Raise this channel to the top of the channel stack"), + channels_raise_to_top_cmd_callback, + GIMP_HELP_CHANNEL_RAISE_TO_TOP }, + + { "channels-lower", GIMP_ICON_GO_DOWN, + NC_("channels-action", "_Lower Channel"), NULL, + NC_("channels-action", "Lower this channel one step in the channel stack"), + channels_lower_cmd_callback, + GIMP_HELP_CHANNEL_LOWER }, + + { "channels-lower-to-bottom", GIMP_ICON_GO_BOTTOM, + NC_("channels-action", "Lower Channel to _Bottom"), NULL, + NC_("channels-action", + "Lower this channel to the bottom of the channel stack"), + channels_lower_to_bottom_cmd_callback, + GIMP_HELP_CHANNEL_LOWER_TO_BOTTOM } +}; + +static const GimpToggleActionEntry channels_toggle_actions[] = +{ + { "channels-visible", GIMP_ICON_VISIBLE, + NC_("channels-action", "Toggle Channel _Visibility"), NULL, NULL, + channels_visible_cmd_callback, + FALSE, + GIMP_HELP_CHANNEL_VISIBLE }, + + { "channels-linked", GIMP_ICON_LINKED, + NC_("channels-action", "Toggle Channel _Linked State"), NULL, NULL, + channels_linked_cmd_callback, + FALSE, + GIMP_HELP_CHANNEL_LINKED }, + + { "channels-lock-content", NULL /* GIMP_ICON_LOCK */, + NC_("channels-action", "L_ock Pixels of Channel"), NULL, NULL, + channels_lock_content_cmd_callback, + FALSE, + GIMP_HELP_CHANNEL_LOCK_PIXELS }, + + { "channels-lock-position", GIMP_ICON_TOOL_MOVE, + NC_("channels-action", "L_ock Position of Channel"), NULL, NULL, + channels_lock_position_cmd_callback, + FALSE, + GIMP_HELP_CHANNEL_LOCK_POSITION } +}; + +static const GimpEnumActionEntry channels_color_tag_actions[] = +{ + { "channels-color-tag-none", GIMP_ICON_EDIT_CLEAR, + NC_("channels-action", "None"), NULL, + NC_("channels-action", "Channel Color Tag: Clear"), + GIMP_COLOR_TAG_NONE, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-blue", NULL, + NC_("channels-action", "Blue"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Blue"), + GIMP_COLOR_TAG_BLUE, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-green", NULL, + NC_("channels-action", "Green"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Green"), + GIMP_COLOR_TAG_GREEN, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-yellow", NULL, + NC_("channels-action", "Yellow"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Yellow"), + GIMP_COLOR_TAG_YELLOW, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-orange", NULL, + NC_("channels-action", "Orange"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Orange"), + GIMP_COLOR_TAG_ORANGE, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-brown", NULL, + NC_("channels-action", "Brown"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Brown"), + GIMP_COLOR_TAG_BROWN, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-red", NULL, + NC_("channels-action", "Red"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Red"), + GIMP_COLOR_TAG_RED, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-violet", NULL, + NC_("channels-action", "Violet"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Violet"), + GIMP_COLOR_TAG_VIOLET, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG }, + + { "channels-color-tag-gray", NULL, + NC_("channels-action", "Gray"), NULL, + NC_("channels-action", "Channel Color Tag: Set to Gray"), + GIMP_COLOR_TAG_GRAY, FALSE, + GIMP_HELP_CHANNEL_COLOR_TAG } +}; + +static const GimpEnumActionEntry channels_to_selection_actions[] = +{ + { "channels-selection-replace", GIMP_ICON_SELECTION_REPLACE, + NC_("channels-action", "Channel to Sele_ction"), NULL, + NC_("channels-action", "Replace the selection with this channel"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_CHANNEL_SELECTION_REPLACE }, + + { "channels-selection-add", GIMP_ICON_SELECTION_ADD, + NC_("channels-action", "_Add to Selection"), NULL, + NC_("channels-action", "Add this channel to the current selection"), + GIMP_CHANNEL_OP_ADD, FALSE, + GIMP_HELP_CHANNEL_SELECTION_ADD }, + + { "channels-selection-subtract", GIMP_ICON_SELECTION_SUBTRACT, + NC_("channels-action", "_Subtract from Selection"), NULL, + NC_("channels-action", "Subtract this channel from the current selection"), + GIMP_CHANNEL_OP_SUBTRACT, FALSE, + GIMP_HELP_CHANNEL_SELECTION_SUBTRACT }, + + { "channels-selection-intersect", GIMP_ICON_SELECTION_INTERSECT, + NC_("channels-action", "_Intersect with Selection"), NULL, + NC_("channels-action", "Intersect this channel with the current selection"), + GIMP_CHANNEL_OP_INTERSECT, FALSE, + GIMP_HELP_CHANNEL_SELECTION_INTERSECT } +}; + +static const GimpEnumActionEntry channels_select_actions[] = +{ + { "channels-select-top", NULL, + NC_("channels-action", "Select _Top Channel"), NULL, + NC_("channels-action", "Select the topmost channel"), + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_CHANNEL_TOP }, + + { "channels-select-bottom", NULL, + NC_("channels-action", "Select _Bottom Channel"), NULL, + NC_("channels-action", "Select the bottommost channel"), + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_CHANNEL_BOTTOM }, + + { "channels-select-previous", NULL, + NC_("channels-action", "Select _Previous Channel"), NULL, + NC_("channels-action", "Select the channel above the current channel"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_CHANNEL_PREVIOUS }, + + { "channels-select-next", NULL, + NC_("channels-action", "Select _Next Channel"), NULL, + NC_("channels-action", "Select the channel below the current channel"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_CHANNEL_NEXT } +}; + + +void +channels_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "channels-action", + channels_actions, + G_N_ELEMENTS (channels_actions)); + + gimp_action_group_add_toggle_actions (group, "channels-action", + channels_toggle_actions, + G_N_ELEMENTS (channels_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "channels-action", + channels_color_tag_actions, + G_N_ELEMENTS (channels_color_tag_actions), + channels_color_tag_cmd_callback); + + gimp_action_group_add_enum_actions (group, "channels-action", + channels_to_selection_actions, + G_N_ELEMENTS (channels_to_selection_actions), + channels_to_selection_cmd_callback); + + gimp_action_group_add_enum_actions (group, "channels-action", + channels_select_actions, + G_N_ELEMENTS (channels_select_actions), + channels_select_cmd_callback); + + items_actions_setup (group, "channels"); +} + +void +channels_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpChannel *channel = NULL; + gboolean fs = FALSE; + gboolean component = FALSE; + GList *next = NULL; + GList *prev = NULL; + + if (image) + { + fs = (gimp_image_get_floating_selection (image) != NULL); + + if (GIMP_IS_COMPONENT_EDITOR (data)) + { + if (GIMP_COMPONENT_EDITOR (data)->clicked_component != -1) + component = TRUE; + } + else + { + channel = gimp_image_get_active_channel (image); + + if (channel) + { + GList *channel_list; + GList *list; + + channel_list = gimp_item_get_container_iter (GIMP_ITEM (channel)); + + list = g_list_find (channel_list, channel); + + if (list) + { + prev = g_list_previous (list); + next = g_list_next (list); + } + } + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("channels-edit-attributes", !fs && channel); + + SET_SENSITIVE ("channels-new", !fs && image); + SET_SENSITIVE ("channels-new-last-values", !fs && image); + SET_SENSITIVE ("channels-duplicate", !fs && (channel || component)); + SET_SENSITIVE ("channels-delete", !fs && channel); + + SET_SENSITIVE ("channels-raise", !fs && channel && prev); + SET_SENSITIVE ("channels-raise-to-top", !fs && channel && prev); + SET_SENSITIVE ("channels-lower", !fs && channel && next); + SET_SENSITIVE ("channels-lower-to-bottom", !fs && channel && next); + + SET_SENSITIVE ("channels-selection-replace", !fs && (channel || component)); + SET_SENSITIVE ("channels-selection-add", !fs && (channel || component)); + SET_SENSITIVE ("channels-selection-subtract", !fs && (channel || component)); + SET_SENSITIVE ("channels-selection-intersect", !fs && (channel || component)); + + SET_SENSITIVE ("channels-select-top", !fs && channel && prev); + SET_SENSITIVE ("channels-select-bottom", !fs && channel && next); + SET_SENSITIVE ("channels-select-previous", !fs && channel && prev); + SET_SENSITIVE ("channels-select-next", !fs && channel && next); + +#undef SET_SENSITIVE + + items_actions_update (group, "channels", GIMP_ITEM (channel)); +} diff --git a/app/actions/channels-actions.h b/app/actions/channels-actions.h new file mode 100644 index 0000000..382eae1 --- /dev/null +++ b/app/actions/channels-actions.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 . + */ + +#ifndef __CHANNELS_ACTIONS_H__ +#define __CHANNELS_ACTIONS_H__ + + +void channels_actions_setup (GimpActionGroup *group); +void channels_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __CHANNELS_ACTIONS_H__ */ diff --git a/app/actions/channels-commands.c b/app/actions/channels-commands.c new file mode 100644 index 0000000..6701242 --- /dev/null +++ b/app/actions/channels-commands.c @@ -0,0 +1,567 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpchannel-select.h" +#include "core/gimpcontext.h" +#include "core/gimpdrawable-fill.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpcolorpanel.h" +#include "widgets/gimpcomponenteditor.h" +#include "widgets/gimpdock.h" +#include "widgets/gimphelp-ids.h" + +#include "dialogs/dialogs.h" +#include "dialogs/channel-options-dialog.h" + +#include "actions.h" +#include "channels-commands.h" +#include "items-commands.h" + +#include "gimp-intl.h" + + +#define RGBA_EPSILON 1e-6 + + +/* local function prototypes */ + +static void channels_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data); +static void channels_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data); + + +/* public functions */ + +void +channels_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_channel (image, channel, data); + return_if_no_widget (widget, data); + +#define EDIT_DIALOG_KEY "gimp-channel-edit-attributes-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (channel), EDIT_DIALOG_KEY); + + if (! dialog) + { + GimpItem *item = GIMP_ITEM (channel); + + dialog = channel_options_dialog_new (image, channel, + action_data_get_context (data), + widget, + _("Channel Attributes"), + "gimp-channel-edit", + GIMP_ICON_EDIT, + _("Edit Channel Attributes"), + GIMP_HELP_CHANNEL_EDIT, + _("Edit Channel Color"), + _("_Fill opacity:"), + FALSE, + gimp_object_get_name (channel), + &channel->color, + gimp_item_get_visible (item), + gimp_item_get_linked (item), + gimp_item_get_color_tag (item), + gimp_item_get_lock_content (item), + gimp_item_get_lock_position (item), + channels_edit_attributes_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (channel), EDIT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +channels_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + +#define NEW_DIALOG_KEY "gimp-channel-new-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = channel_options_dialog_new (image, NULL, + action_data_get_context (data), + widget, + _("New Channel"), + "gimp-channel-new", + GIMP_ICON_CHANNEL, + _("Create a New Channel"), + GIMP_HELP_CHANNEL_NEW, + _("New Channel Color"), + _("_Fill opacity:"), + TRUE, + config->channel_new_name, + &config->channel_new_color, + TRUE, + FALSE, + GIMP_COLOR_TAG_NONE, + FALSE, + FALSE, + channels_new_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +channels_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + GimpDialogConfig *config; + return_if_no_image (image, data); + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + channel = gimp_channel_new (image, + gimp_image_get_width (image), + gimp_image_get_height (image), + config->channel_new_name, + &config->channel_new_color); + + gimp_drawable_fill (GIMP_DRAWABLE (channel), + action_data_get_context (data), + GIMP_FILL_TRANSPARENT); + + gimp_image_add_channel (image, channel, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); +} + +void +channels_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_image_raise_item (image, GIMP_ITEM (channel), NULL); + gimp_image_flush (image); +} + +void +channels_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_image_raise_item_to_top (image, GIMP_ITEM (channel)); + gimp_image_flush (image); +} + +void +channels_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_image_lower_item (image, GIMP_ITEM (channel), NULL); + gimp_image_flush (image); +} + +void +channels_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_image_lower_item_to_bottom (image, GIMP_ITEM (channel)); + gimp_image_flush (image); +} + +void +channels_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *new_channel; + GimpChannel *parent = GIMP_IMAGE_ACTIVE_PARENT; + + if (GIMP_IS_COMPONENT_EDITOR (data)) + { + GimpChannelType component; + const gchar *desc; + gchar *name; + return_if_no_image (image, data); + + component = GIMP_COMPONENT_EDITOR (data)->clicked_component; + + gimp_enum_get_value (GIMP_TYPE_CHANNEL_TYPE, component, + NULL, NULL, &desc, NULL); + + name = g_strdup_printf (_("%s Channel Copy"), desc); + + new_channel = gimp_channel_new_from_component (image, component, + name, NULL); + + /* copied components are invisible by default so subsequent copies + * of components don't affect each other + */ + gimp_item_set_visible (GIMP_ITEM (new_channel), FALSE, FALSE); + + g_free (name); + } + else + { + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + new_channel = + GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (channel), + G_TYPE_FROM_INSTANCE (channel))); + + /* use the actual parent here, not GIMP_IMAGE_ACTIVE_PARENT because + * the latter would add a duplicated group inside itself instead of + * above it + */ + parent = gimp_channel_get_parent (channel); + } + + gimp_image_add_channel (image, new_channel, parent, -1, TRUE); + gimp_image_flush (image); +} + +void +channels_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_image_remove_channel (image, channel, TRUE, NULL); + gimp_image_flush (image); +} + +void +channels_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpChannelOps op; + GimpImage *image; + + op = (GimpChannelOps) g_variant_get_int32 (value); + + if (GIMP_IS_COMPONENT_EDITOR (data)) + { + GimpChannelType component; + return_if_no_image (image, data); + + component = GIMP_COMPONENT_EDITOR (data)->clicked_component; + + gimp_channel_select_component (gimp_image_get_mask (image), component, + op, FALSE, 0.0, 0.0); + } + else + { + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + gimp_item_to_selection (GIMP_ITEM (channel), + op, TRUE, FALSE, 0.0, 0.0); + } + + gimp_image_flush (image); +} + +void +channels_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + items_visible_cmd_callback (action, value, image, GIMP_ITEM (channel)); +} + +void +channels_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + items_linked_cmd_callback (action, value, image, GIMP_ITEM (channel)); +} + +void +channels_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + items_lock_content_cmd_callback (action, value, image, GIMP_ITEM (channel)); +} + +void +channels_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + return_if_no_channel (image, channel, data); + + items_lock_position_cmd_callback (action, value, image, GIMP_ITEM (channel)); +} + +void +channels_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + GimpColorTag color_tag; + return_if_no_channel (image, channel, data); + + color_tag = (GimpColorTag) g_variant_get_int32 (value); + + items_color_tag_cmd_callback (action, image, GIMP_ITEM (channel), + color_tag); +} + +void +channels_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + GimpChannel *channel2; + GimpContainer *container; + GimpActionSelectType select_type; + return_if_no_channel (image, channel, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + container = gimp_image_get_channels (image); + channel2 = (GimpChannel *) action_select_object (select_type, container, + (GimpObject *) channel); + + if (channel2 && channel2 != channel) + { + gimp_image_set_active_channel (image, channel2); + gimp_image_flush (image); + } +} + +/* private functions */ + +static void +channels_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + g_object_set (config, + "channel-new-name", channel_name, + "channel-new-color", channel_color, + NULL); + + if (save_selection) + { + GimpChannel *selection = gimp_image_get_mask (image); + + channel = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (selection), + GIMP_TYPE_CHANNEL)); + + gimp_object_set_name (GIMP_OBJECT (channel), + config->channel_new_name); + gimp_channel_set_color (channel, &config->channel_new_color, FALSE); + } + else + { + channel = gimp_channel_new (image, + gimp_image_get_width (image), + gimp_image_get_height (image), + config->channel_new_name, + &config->channel_new_color); + + gimp_drawable_fill (GIMP_DRAWABLE (channel), context, + GIMP_FILL_TRANSPARENT); + } + + gimp_item_set_visible (GIMP_ITEM (channel), channel_visible, FALSE); + gimp_item_set_linked (GIMP_ITEM (channel), channel_linked, FALSE); + gimp_item_set_color_tag (GIMP_ITEM (channel), channel_color_tag, FALSE); + gimp_item_set_lock_content (GIMP_ITEM (channel), channel_lock_content, FALSE); + gimp_item_set_lock_position (GIMP_ITEM (channel), channel_lock_position, FALSE); + + gimp_image_add_channel (image, channel, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +channels_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data) +{ + GimpItem *item = GIMP_ITEM (channel); + + if (strcmp (channel_name, gimp_object_get_name (channel)) || + gimp_rgba_distance (channel_color, &channel->color) > RGBA_EPSILON || + channel_visible != gimp_item_get_visible (item) || + channel_linked != gimp_item_get_linked (item) || + channel_color_tag != gimp_item_get_color_tag (item) || + channel_lock_content != gimp_item_get_lock_content (item) || + channel_lock_position != gimp_item_get_lock_position (item)) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_PROPERTIES, + _("Channel Attributes")); + + if (strcmp (channel_name, gimp_object_get_name (channel))) + gimp_item_rename (GIMP_ITEM (channel), channel_name, NULL); + + if (gimp_rgba_distance (channel_color, &channel->color) > RGBA_EPSILON) + gimp_channel_set_color (channel, channel_color, TRUE); + + if (channel_visible != gimp_item_get_visible (item)) + gimp_item_set_visible (item, channel_visible, TRUE); + + if (channel_linked != gimp_item_get_linked (item)) + gimp_item_set_linked (item, channel_linked, TRUE); + + if (channel_color_tag != gimp_item_get_color_tag (item)) + gimp_item_set_color_tag (item, channel_color_tag, TRUE); + + if (channel_lock_content != gimp_item_get_lock_content (item)) + gimp_item_set_lock_content (item, channel_lock_content, TRUE); + + if (channel_lock_position != gimp_item_get_lock_position (item)) + gimp_item_set_lock_position (item, channel_lock_position, TRUE); + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + } + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/channels-commands.h b/app/actions/channels-commands.h new file mode 100644 index 0000000..6dd7d79 --- /dev/null +++ b/app/actions/channels-commands.h @@ -0,0 +1,77 @@ +/* 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 . + */ + +#ifndef __CHANNELS_COMMANDS_H__ +#define __CHANNELS_COMMANDS_H__ + + +void channels_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void channels_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void channels_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void channels_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void channels_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void channels_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void channels_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __CHANNELS_COMMANDS_H__ */ diff --git a/app/actions/colormap-actions.c b/app/actions/colormap-actions.c new file mode 100644 index 0000000..1c5d59f --- /dev/null +++ b/app/actions/colormap-actions.c @@ -0,0 +1,173 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimpimage-colormap.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "colormap-actions.h" +#include "colormap-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry colormap_actions[] = +{ + { "colormap-popup", GIMP_ICON_COLORMAP, + NC_("colormap-action", "Colormap Menu"), NULL, NULL, NULL, + GIMP_HELP_INDEXED_PALETTE_DIALOG }, + + { "colormap-edit-color", GIMP_ICON_EDIT, + NC_("colormap-action", "_Edit Color..."), NULL, + NC_("colormap-action", "Edit this color"), + colormap_edit_color_cmd_callback, + GIMP_HELP_INDEXED_PALETTE_EDIT } +}; + +static const GimpEnumActionEntry colormap_add_color_actions[] = +{ + { "colormap-add-color-from-fg", GIMP_ICON_LIST_ADD, + NC_("colormap-action", "_Add Color from FG"), "", + NC_("colormap-action", "Add current foreground color"), + FALSE, FALSE, + GIMP_HELP_INDEXED_PALETTE_ADD }, + + { "colormap-add-color-from-bg", GIMP_ICON_LIST_ADD, + NC_("colormap-action", "_Add Color from BG"), "", + NC_("colormap-action", "Add current background color"), + TRUE, FALSE, + GIMP_HELP_INDEXED_PALETTE_ADD } +}; + +static const GimpEnumActionEntry colormap_to_selection_actions[] = +{ + { "colormap-selection-replace", GIMP_ICON_SELECTION_REPLACE, + NC_("colormap-action", "_Select this Color"), NULL, + NC_("colormap-action", "Select all pixels with this color"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_INDEXED_PALETTE_SELECTION_REPLACE }, + + { "colormap-selection-add", GIMP_ICON_SELECTION_ADD, + NC_("colormap-action", "_Add to Selection"), NULL, + NC_("colormap-action", "Add all pixels with this color to the current selection"), + GIMP_CHANNEL_OP_ADD, FALSE, + GIMP_HELP_INDEXED_PALETTE_SELECTION_ADD }, + + { "colormap-selection-subtract", GIMP_ICON_SELECTION_SUBTRACT, + NC_("colormap-action", "_Subtract from Selection"), NULL, + NC_("colormap-action", "Subtract all pixels with this color from the current selection"), + GIMP_CHANNEL_OP_SUBTRACT, FALSE, + GIMP_HELP_INDEXED_PALETTE_SELECTION_SUBTRACT }, + + { "colormap-selection-intersect", GIMP_ICON_SELECTION_INTERSECT, + NC_("colormap-action", "_Intersect with Selection"), NULL, + NC_("colormap-action", "Intersect all pixels with this color with the current selection"), + GIMP_CHANNEL_OP_INTERSECT, FALSE, + GIMP_HELP_INDEXED_PALETTE_SELECTION_INTERSECT } +}; + +void +colormap_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "colormap-action", + colormap_actions, + G_N_ELEMENTS (colormap_actions)); + + gimp_action_group_add_enum_actions (group, "colormap-action", + colormap_add_color_actions, + G_N_ELEMENTS (colormap_add_color_actions), + colormap_add_color_cmd_callback); + + gimp_action_group_add_enum_actions (group, "colormap-action", + colormap_to_selection_actions, + G_N_ELEMENTS (colormap_to_selection_actions), + colormap_to_selection_cmd_callback); +} + +void +colormap_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpContext *context = action_data_get_context (data); + gboolean indexed = FALSE; + gboolean drawable_indexed = FALSE; + gint num_colors = 0; + GimpRGB fg; + GimpRGB bg; + + if (image) + { + indexed = (gimp_image_get_base_type (image) == GIMP_INDEXED); + + if (indexed) + { + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + + num_colors = gimp_image_get_colormap_size (image); + drawable_indexed = gimp_drawable_is_indexed (drawable); + } + } + + if (context) + { + gimp_context_get_foreground (context, &fg); + gimp_context_get_background (context, &bg); + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_COLOR(action,color) \ + gimp_action_group_set_action_color (group, action, color, FALSE); + + SET_SENSITIVE ("colormap-edit-color", + indexed && num_colors > 0); + + SET_SENSITIVE ("colormap-add-color-from-fg", + indexed && num_colors < 256); + SET_SENSITIVE ("colormap-add-color-from-bg", + indexed && num_colors < 256); + + SET_COLOR ("colormap-add-color-from-fg", context ? &fg : NULL); + SET_COLOR ("colormap-add-color-from-bg", context ? &bg : NULL); + + SET_SENSITIVE ("colormap-selection-replace", + drawable_indexed && num_colors > 0); + SET_SENSITIVE ("colormap-selection-add", + drawable_indexed && num_colors > 0); + SET_SENSITIVE ("colormap-selection-subtract", + drawable_indexed && num_colors > 0); + SET_SENSITIVE ("colormap-selection-intersect", + drawable_indexed && num_colors > 0); + +#undef SET_SENSITIVE +#undef SET_COLOR +} diff --git a/app/actions/colormap-actions.h b/app/actions/colormap-actions.h new file mode 100644 index 0000000..0451632 --- /dev/null +++ b/app/actions/colormap-actions.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 . + */ + +#ifndef __COLORMAP_ACTIONS_H__ +#define __COLORMAP_ACTIONS_H__ + + +void colormap_actions_setup (GimpActionGroup *group); +void colormap_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __COLORMAP_ACTIONS_H__ */ diff --git a/app/actions/colormap-commands.c b/app/actions/colormap-commands.c new file mode 100644 index 0000000..226197f --- /dev/null +++ b/app/actions/colormap-commands.c @@ -0,0 +1,96 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "actions-types.h" + +#include "core/gimpchannel-select.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-colormap.h" + +#include "widgets/gimpcolormapeditor.h" + +#include "actions.h" +#include "colormap-commands.h" + + +/* public functions */ + +void +colormap_edit_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpColormapEditor *editor = GIMP_COLORMAP_EDITOR (data); + + gimp_colormap_editor_edit_color (editor); +} + +void +colormap_add_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpImage *image; + gboolean background; + return_if_no_context (context, data); + return_if_no_image (image, data); + + background = (gboolean) g_variant_get_int32 (value); + + if (gimp_image_get_colormap_size (image) < 256) + { + GimpRGB color; + + if (background) + gimp_context_get_background (context, &color); + else + gimp_context_get_foreground (context, &color); + + gimp_image_add_colormap_entry (image, &color); + gimp_image_flush (image); + } +} + +void +colormap_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpColormapEditor *editor; + GimpImage *image; + GimpChannelOps op; + return_if_no_image (image, data); + + editor = GIMP_COLORMAP_EDITOR (data); + + op = (GimpChannelOps) g_variant_get_int32 (value); + + gimp_channel_select_by_index (gimp_image_get_mask (image), + gimp_image_get_active_drawable (image), + editor->col_index, + op, + FALSE, 0.0, 0.0); + + gimp_image_flush (image); +} diff --git a/app/actions/colormap-commands.h b/app/actions/colormap-commands.h new file mode 100644 index 0000000..8aa4e52 --- /dev/null +++ b/app/actions/colormap-commands.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 . + */ + +#ifndef __COLORMAP_COMMANDS_H__ +#define __COLORMAP_COMMANDS_H__ + + +void colormap_edit_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void colormap_add_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void colormap_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __COLORMAP_COMMANDS_H__ */ diff --git a/app/actions/context-actions.c b/app/actions/context-actions.c new file mode 100644 index 0000000..aef16ab --- /dev/null +++ b/app/actions/context-actions.c @@ -0,0 +1,1294 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpbrushgenerated.h" +#include "core/gimpcontext.h" +#include "core/gimplist.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "context-actions.h" +#include "context-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static const GimpActionEntry context_actions[] = +{ + { "context-menu", NULL, NC_("context-action", + "_Context") }, + { "context-colors-menu", GIMP_ICON_COLORS_DEFAULT, NC_("context-action", + "_Colors") }, + { "context-opacity-menu", GIMP_ICON_TRANSPARENCY, NC_("context-action", + "_Opacity") }, + { "context-paint-mode-menu", GIMP_ICON_TOOL_PENCIL, NC_("context-action", + "Paint _Mode") }, + { "context-tool-menu", GIMP_ICON_DIALOG_TOOLS, NC_("context-action", + "_Tool") }, + { "context-brush-menu", GIMP_ICON_BRUSH, NC_("context-action", + "_Brush") }, + { "context-pattern-menu", GIMP_ICON_PATTERN, NC_("context-action", + "_Pattern") }, + { "context-palette-menu", GIMP_ICON_PALETTE, NC_("context-action", + "_Palette") }, + { "context-gradient-menu", GIMP_ICON_GRADIENT, NC_("context-action", + "_Gradient") }, + { "context-font-menu", GIMP_ICON_FONT, NC_("context-action", + "_Font") }, + + { "context-brush-shape-menu", NULL, NC_("context-action", + "_Shape") }, + { "context-brush-radius-menu", NULL, NC_("context-action", + "_Radius") }, + { "context-brush-spikes-menu", NULL, NC_("context-action", + "S_pikes") }, + { "context-brush-hardness-menu", NULL, NC_("context-action", + "_Hardness") }, + { "context-brush-aspect-menu", NULL, NC_("context-action", + "_Aspect Ratio")}, + { "context-brush-angle-menu", NULL, NC_("context-action", + "A_ngle") }, + + { "context-colors-default", GIMP_ICON_COLORS_DEFAULT, + NC_("context-action", "_Default Colors"), "D", + NC_("context-action", + "Set foreground color to black, background color to white"), + context_colors_default_cmd_callback, + GIMP_HELP_TOOLBOX_DEFAULT_COLORS }, + + { "context-colors-swap", GIMP_ICON_COLORS_SWAP, + NC_("context-action", "S_wap Colors"), "X", + NC_("context-action", "Exchange foreground and background colors"), + context_colors_swap_cmd_callback, + GIMP_HELP_TOOLBOX_SWAP_COLORS } +}; + +static GimpEnumActionEntry context_palette_foreground_actions[] = +{ + { "context-palette-foreground-set", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Set Color From Palette"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-palette-foreground-first", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use First Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-palette-foreground-last", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Last Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-palette-foreground-previous", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Previous Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-palette-foreground-next", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Next Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-palette-foreground-previous-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Skip Back Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-palette-foreground-next-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Skip Forward Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static GimpEnumActionEntry context_palette_background_actions[] = +{ + { "context-palette-background-set", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Set Color From Palette"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-palette-background-first", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use First Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-palette-background-last", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Last Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-palette-background-previous", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Previous Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-palette-background-next", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Next Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-palette-background-previous-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Skip Back Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-palette-background-next-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Skip Forward Palette Color"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static GimpEnumActionEntry context_colormap_foreground_actions[] = +{ + { "context-colormap-foreground-set", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Set Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-colormap-foreground-first", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Use First Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-colormap-foreground-last", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Use Last Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-colormap-foreground-previous", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Use Previous Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-colormap-foreground-next", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Use Next Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-colormap-foreground-previous-skip", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Skip Back Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-colormap-foreground-next-skip", GIMP_ICON_COLORMAP, + NC_("context-action", "Foreground: Skip Forward Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static GimpEnumActionEntry context_colormap_background_actions[] = +{ + { "context-colormap-background-set", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Set Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-colormap-background-first", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Use First Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-colormap-background-last", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Use Last Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-colormap-background-previous", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Use Previous Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-colormap-background-next", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Use Next Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-colormap-background-previous-skip", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Skip Back Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-colormap-background-next-skip", GIMP_ICON_COLORMAP, + NC_("context-action", "Background: Skip Forward Color From Colormap"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static GimpEnumActionEntry context_swatch_foreground_actions[] = +{ + { "context-swatch-foreground-set", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Set Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-swatch-foreground-first", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use First Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-swatch-foreground-last", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Last Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-swatch-foreground-previous", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Previous Color From Swatch"), "9", NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-swatch-foreground-next", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Use Next Color From Swatch"), "0", NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-swatch-foreground-previous-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Skip Back Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-swatch-foreground-next-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Foreground: Skip Forward Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static GimpEnumActionEntry context_swatch_background_actions[] = +{ + { "context-swatch-background-set", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Set Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SET, FALSE, + NULL }, + { "context-swatch-background-first", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use First Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-swatch-background-last", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Last Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-swatch-background-previous", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Previous Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-swatch-background-next", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Use Next Color From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-swatch-background-previous-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Skip Color Back From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-swatch-background-next-skip", GIMP_ICON_PALETTE, + NC_("context-action", "Background: Skip Color Forward From Swatch"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_red_actions[] = +{ + { "context-foreground-red-set", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-red-minimum", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-red-maximum", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-red-decrease", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-red-increase", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-red-decrease-skip", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-red-increase-skip", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Foreground Red: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_green_actions[] = +{ + { "context-foreground-green-set", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-green-minimum", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-green-maximum", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-green-decrease", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-green-increase", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-green-decrease-skip", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-green-increase-skip", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Foreground Green: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_blue_actions[] = +{ + { "context-foreground-blue-set", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-blue-minimum", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-blue-maximum", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-blue-decrease", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-blue-increase", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-blue-decrease-skip", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-blue-increase-skip", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Foreground Blue: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_red_actions[] = +{ + { "context-background-red-set", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-red-minimum", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-red-maximum", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-red-decrease", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-red-increase", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-red-decrease-skip", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-red-increase-skip", GIMP_ICON_CHANNEL_RED, + NC_("context-action", "Background Red: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_green_actions[] = +{ + { "context-background-green-set", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-green-minimum", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-green-maximum", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-green-decrease", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-green-increase", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-green-decrease-skip", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-green-increase-skip", GIMP_ICON_CHANNEL_GREEN, + NC_("context-action", "Background Green: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_blue_actions[] = +{ + { "context-background-blue-set", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-blue-minimum", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-blue-maximum", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-blue-decrease", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-blue-increase", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-blue-decrease-skip", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-blue-increase-skip", GIMP_ICON_CHANNEL_BLUE, + NC_("context-action", "Background Blue: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_hue_actions[] = +{ + { "context-foreground-hue-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-hue-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-hue-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-hue-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-hue-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-hue-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-hue-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Hue: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_saturation_actions[] = +{ + { "context-foreground-saturation-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-saturation-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-saturation-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-saturation-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-saturation-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-saturation-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-saturation-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Saturation: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_foreground_value_actions[] = +{ + { "context-foreground-value-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-foreground-value-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-foreground-value-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-foreground-value-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-foreground-value-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-foreground-value-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-foreground-value-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Foreground Value: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_hue_actions[] = +{ + { "context-background-hue-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-hue-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-hue-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-hue-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-hue-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-hue-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-hue-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Hue: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_saturation_actions[] = +{ + { "context-background-saturation-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-saturation-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-saturation-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-saturation-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-saturation-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-saturation-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-saturation-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Saturation: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_background_value_actions[] = +{ + { "context-background-value-set", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-background-value-minimum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-background-value-maximum", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-background-value-decrease", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Decrease by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-background-value-increase", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Increase by 1%"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-background-value-decrease-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Decrease by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-background-value-increase-skip", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("context-action", "Background Value: Increase by 10%"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_opacity_actions[] = +{ + { "context-opacity-set", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Set Transparency"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-opacity-transparent", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make Completely Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-opacity-opaque", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make Completely Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-opacity-decrease", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make 1% More Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-opacity-increase", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make 1% More Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-opacity-decrease-skip", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make 10% More Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-opacity-increase-skip", GIMP_ICON_TRANSPARENCY, + NC_("context-action", "Tool Opacity: Make 10% More Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_paint_mode_actions[] = +{ + { "context-paint-mode-first", GIMP_ICON_TOOL_PENCIL, + NC_("context-action", "Tool Paint Mode: Select First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-paint-mode-last", GIMP_ICON_TOOL_PENCIL, + NC_("context-action", "Tool Paint Mode: Select Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-paint-mode-previous", GIMP_ICON_TOOL_PENCIL, + NC_("context-action", "Tool Paint Mode: Select Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-paint-mode-next", GIMP_ICON_TOOL_PENCIL, + NC_("context-action", "Tool Paint Mode: Select Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_tool_select_actions[] = +{ + { "context-tool-select-set", GIMP_ICON_DIALOG_TOOLS, + NC_("context-action", "Tool Selection: Choose by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-tool-select-first", GIMP_ICON_DIALOG_TOOLS, + NC_("context-action", "Tool Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-tool-select-last", GIMP_ICON_DIALOG_TOOLS, + NC_("context-action", "Tool Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-tool-select-previous", GIMP_ICON_DIALOG_TOOLS, + NC_("context-action", "Tool Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-tool-select-next", GIMP_ICON_DIALOG_TOOLS, + NC_("context-action", "Tool Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_select_actions[] = +{ + { "context-brush-select-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Selection: Select by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-select-first", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-select-last", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-select-previous", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-select-next", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_pattern_select_actions[] = +{ + { "context-pattern-select-set", GIMP_ICON_PATTERN, + NC_("context-action", "Pattern Selection: Select by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-pattern-select-first", GIMP_ICON_PATTERN, + NC_("context-action", "Pattern Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-pattern-select-last", GIMP_ICON_PATTERN, + NC_("context-action", "Pattern Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-pattern-select-previous", GIMP_ICON_PATTERN, + NC_("context-action", "Pattern Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-pattern-select-next", GIMP_ICON_PATTERN, + NC_("context-action", "Pattern Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_palette_select_actions[] = +{ + { "context-palette-select-set", GIMP_ICON_PALETTE, + NC_("context-action", "Palette Selection: Select by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-palette-select-first", GIMP_ICON_PALETTE, + NC_("context-action", "Palette Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-palette-select-last", GIMP_ICON_PALETTE, + NC_("context-action", "Palette Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-palette-select-previous", GIMP_ICON_PALETTE, + NC_("context-action", "Palette Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-palette-select-next", GIMP_ICON_PALETTE, + NC_("context-action", "Palette Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_gradient_select_actions[] = +{ + { "context-gradient-select-set", GIMP_ICON_GRADIENT, + NC_("context-action", "Gradient Selection: Select by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-gradient-select-first", GIMP_ICON_GRADIENT, + NC_("context-action", "Gradient Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-gradient-select-last", GIMP_ICON_GRADIENT, + NC_("context-action", "Gradient Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-gradient-select-previous", GIMP_ICON_GRADIENT, + NC_("context-action", "Gradient Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-gradient-select-next", GIMP_ICON_GRADIENT, + NC_("context-action", "Gradient Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_font_select_actions[] = +{ + { "context-font-select-set", GIMP_ICON_FONT, + NC_("context-action", "Font Selection: Select by Index"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-font-select-first", GIMP_ICON_FONT, + NC_("context-action", "Font Selection: Switch to First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-font-select-last", GIMP_ICON_FONT, + NC_("context-action", "Font Selection: Switch to Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-font-select-previous", GIMP_ICON_FONT, + NC_("context-action", "Font Selection: Switch to Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-font-select-next", GIMP_ICON_FONT, + NC_("context-action", "Font Selection: Switch to Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_spacing_actions[] = +{ + { "context-brush-spacing-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-spacing-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-spacing-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-spacing-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-spacing-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-spacing-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-spacing-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spacing (Editor): Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_shape_actions[] = +{ + { "context-brush-shape-circle", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Shape (Editor): Use Circular"), NULL, NULL, + GIMP_BRUSH_GENERATED_CIRCLE, FALSE, + NULL }, + { "context-brush-shape-square", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Shape (Editor): Use Square"), NULL, NULL, + GIMP_BRUSH_GENERATED_SQUARE, FALSE, + NULL }, + { "context-brush-shape-diamond", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Shape (Editor): Use Diamond"), NULL, NULL, + GIMP_BRUSH_GENERATED_DIAMOND, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_radius_actions[] = +{ + { "context-brush-radius-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-radius-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-radius-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-radius-decrease-less", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Decrease by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_SMALL_PREVIOUS, FALSE, + NULL }, + { "context-brush-radius-increase-less", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Increase by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_SMALL_NEXT, FALSE, + NULL }, + { "context-brush-radius-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-radius-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-radius-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-radius-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "context-brush-radius-decrease-percent", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "context-brush-radius-increase-percent", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Radius (Editor): Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_spikes_actions[] = +{ + { "context-brush-spikes-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-spikes-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-spikes-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-spikes-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-spikes-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-spikes-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Decrease by 4"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-spikes-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Spikes (Editor): Increase by 4"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_hardness_actions[] = +{ + { "context-brush-hardness-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-hardness-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-hardness-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-hardness-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Decrease by 0.01"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-hardness-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Increase by 0.01"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-hardness-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Decrease by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-hardness-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Hardness (Editor): Increase by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_aspect_actions[] = +{ + { "context-brush-aspect-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-aspect-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-aspect-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-aspect-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Decrease by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-aspect-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Increase by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-aspect-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-aspect-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Aspect Ratio (Editor): Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry context_brush_angle_actions[] = +{ + { "context-brush-angle-set", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "context-brush-angle-minimum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Make Horizontal"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "context-brush-angle-maximum", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Make Vertical"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "context-brush-angle-decrease", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Rotate Right by 1°"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "context-brush-angle-increase", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Rotate Left by 1°"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "context-brush-angle-decrease-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Rotate Right by 15°"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "context-brush-angle-increase-skip", GIMP_ICON_BRUSH, + NC_("context-action", "Brush Angle (Editor): Rotate Left by 15°"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + + +void +context_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "context-action", + context_actions, + G_N_ELEMENTS (context_actions)); + + gimp_action_group_add_enum_actions (group, "context-action", + context_palette_foreground_actions, + G_N_ELEMENTS (context_palette_foreground_actions), + context_palette_foreground_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_palette_background_actions, + G_N_ELEMENTS (context_palette_background_actions), + context_palette_background_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_colormap_foreground_actions, + G_N_ELEMENTS (context_colormap_foreground_actions), + context_colormap_foreground_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_colormap_background_actions, + G_N_ELEMENTS (context_colormap_background_actions), + context_colormap_background_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_swatch_foreground_actions, + G_N_ELEMENTS (context_swatch_foreground_actions), + context_swatch_foreground_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_swatch_background_actions, + G_N_ELEMENTS (context_swatch_background_actions), + context_swatch_background_cmd_callback); + + + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_red_actions, + G_N_ELEMENTS (context_foreground_red_actions), + context_foreground_red_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_green_actions, + G_N_ELEMENTS (context_foreground_green_actions), + context_foreground_green_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_blue_actions, + G_N_ELEMENTS (context_foreground_blue_actions), + context_foreground_blue_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_hue_actions, + G_N_ELEMENTS (context_foreground_hue_actions), + context_foreground_hue_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_saturation_actions, + G_N_ELEMENTS (context_foreground_saturation_actions), + context_foreground_saturation_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_foreground_value_actions, + G_N_ELEMENTS (context_foreground_value_actions), + context_foreground_value_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_background_red_actions, + G_N_ELEMENTS (context_background_red_actions), + context_background_red_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_background_green_actions, + G_N_ELEMENTS (context_background_green_actions), + context_background_green_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_background_blue_actions, + G_N_ELEMENTS (context_background_blue_actions), + context_background_blue_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_background_hue_actions, + G_N_ELEMENTS (context_background_hue_actions), + context_background_hue_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_background_saturation_actions, + G_N_ELEMENTS (context_background_saturation_actions), + context_background_saturation_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_background_value_actions, + G_N_ELEMENTS (context_background_value_actions), + context_background_value_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_opacity_actions, + G_N_ELEMENTS (context_opacity_actions), + context_opacity_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_paint_mode_actions, + G_N_ELEMENTS (context_paint_mode_actions), + context_paint_mode_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_tool_select_actions, + G_N_ELEMENTS (context_tool_select_actions), + context_tool_select_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_select_actions, + G_N_ELEMENTS (context_brush_select_actions), + context_brush_select_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_pattern_select_actions, + G_N_ELEMENTS (context_pattern_select_actions), + context_pattern_select_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_palette_select_actions, + G_N_ELEMENTS (context_palette_select_actions), + context_palette_select_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_gradient_select_actions, + G_N_ELEMENTS (context_gradient_select_actions), + context_gradient_select_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_font_select_actions, + G_N_ELEMENTS (context_font_select_actions), + context_font_select_cmd_callback); + + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_spacing_actions, + G_N_ELEMENTS (context_brush_spacing_actions), + context_brush_spacing_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_shape_actions, + G_N_ELEMENTS (context_brush_shape_actions), + context_brush_shape_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_radius_actions, + G_N_ELEMENTS (context_brush_radius_actions), + context_brush_radius_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_spikes_actions, + G_N_ELEMENTS (context_brush_spikes_actions), + context_brush_spikes_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_hardness_actions, + G_N_ELEMENTS (context_brush_hardness_actions), + context_brush_hardness_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_aspect_actions, + G_N_ELEMENTS (context_brush_aspect_actions), + context_brush_aspect_cmd_callback); + gimp_action_group_add_enum_actions (group, "context-action", + context_brush_angle_actions, + G_N_ELEMENTS (context_brush_angle_actions), + context_brush_angle_cmd_callback); +} + +void +context_actions_update (GimpActionGroup *group, + gpointer data) +{ +#if 0 + GimpContext *context = action_data_get_context (data); + gboolean generated = FALSE; + gdouble radius = 0.0; + gint spikes = 0; + gdouble hardness = 0.0; + gdouble aspect = 0.0; + gdouble angle = 0.0; + + if (context) + { + GimpBrush *brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush)) + { + GimpBrushGenerated *gen = GIMP_BRUSH_GENERATED (brush); + + generated = TRUE; + + radius = gimp_brush_generated_get_radius (gen); + spikes = gimp_brush_generated_get_spikes (gen); + hardness = gimp_brush_generated_get_hardness (gen); + aspect = gimp_brush_generated_get_aspect_ratio (gen); + angle = gimp_brush_generated_get_angle (gen); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, "context-" action, (condition) != 0) + + SET_SENSITIVE ("brush-radius-minimum", generated && radius > 1.0); + SET_SENSITIVE ("brush-radius-decrease", generated && radius > 1.0); + SET_SENSITIVE ("brush-radius-decrease-skip", generated && radius > 1.0); + + SET_SENSITIVE ("brush-radius-maximum", generated && radius < 4000.0); + SET_SENSITIVE ("brush-radius-increase", generated && radius < 4000.0); + SET_SENSITIVE ("brush-radius-increase-skip", generated && radius < 4000.0); + + SET_SENSITIVE ("brush-angle-minimum", generated); + SET_SENSITIVE ("brush-angle-decrease", generated); + SET_SENSITIVE ("brush-angle-decrease-skip", generated); + + SET_SENSITIVE ("brush-angle-maximum", generated); + SET_SENSITIVE ("brush-angle-increase", generated); + SET_SENSITIVE ("brush-angle-increase-skip", generated); +#undef SET_SENSITIVE + +#endif +} diff --git a/app/actions/context-actions.h b/app/actions/context-actions.h new file mode 100644 index 0000000..f2ad085 --- /dev/null +++ b/app/actions/context-actions.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 . + */ + +#ifndef __CONTEXT_ACTIONS_H__ +#define __CONTEXT_ACTIONS_H__ + + +void context_actions_setup (GimpActionGroup *group); +void context_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __CONTEXT_ACTIONS_H__ */ diff --git a/app/actions/context-commands.c b/app/actions/context-commands.c new file mode 100644 index 0000000..61d9e90 --- /dev/null +++ b/app/actions/context-commands.c @@ -0,0 +1,975 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "core/gimp.h" +#include "core/gimpbrushgenerated.h" +#include "core/gimpcontext.h" +#include "core/gimpdatafactory.h" +#include "core/gimplist.h" +#include "core/gimptoolinfo.h" + +#include "paint/gimppaintoptions.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimppaletteeditor.h" +#include "widgets/gimpcolormapeditor.h" + +#include "actions.h" +#include "context-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void context_select_object (GimpActionSelectType select_type, + GimpContext *context, + GimpContainer *container); +static gint context_paint_mode_index (GimpLayerMode paint_mode, + const GimpLayerMode *modes, + gint n_modes); + +static void context_select_color (GimpActionSelectType select_type, + GimpRGB *color, + gboolean use_colormap, + gboolean use_palette); + +static gint context_get_color_index (gboolean use_colormap, + gboolean use_palette, + const GimpRGB *color); +static gint context_max_color_index (gboolean use_colormap, + gboolean use_palette); +static gboolean context_set_color_index (gint index, + gboolean use_colormap, + gboolean use_palette, + GimpRGB *color); + +static GimpPaletteEditor * context_get_palette_editor (void); +static GimpColormapEditor * context_get_colormap_editor (void); + + +/* public functions */ + +void +context_colors_default_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + return_if_no_context (context, data); + + gimp_context_set_default_colors (context); +} + +void +context_colors_swap_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + return_if_no_context (context, data); + + gimp_context_swap_colors (context); +} + +#define SELECT_COLOR_CMD_CALLBACK(name, fgbg, use_colormap, use_palette) \ +void \ +context_##name##_##fgbg##ground_cmd_callback (GimpAction *action, \ + GVariant *value, \ + gpointer data) \ +{ \ + GimpContext *context; \ + GimpRGB color; \ + GimpActionSelectType select_type; \ + return_if_no_context (context, data); \ + \ + select_type = (GimpActionSelectType) g_variant_get_int32 (value); \ + \ + gimp_context_get_##fgbg##ground (context, &color); \ + context_select_color (select_type, &color, \ + use_colormap, use_palette); \ + gimp_context_set_##fgbg##ground (context, &color); \ +} + +SELECT_COLOR_CMD_CALLBACK (palette, fore, FALSE, TRUE) +SELECT_COLOR_CMD_CALLBACK (palette, back, FALSE, TRUE) +SELECT_COLOR_CMD_CALLBACK (colormap, fore, TRUE, FALSE) +SELECT_COLOR_CMD_CALLBACK (colormap, back, TRUE, FALSE) +SELECT_COLOR_CMD_CALLBACK (swatch, fore, TRUE, TRUE) +SELECT_COLOR_CMD_CALLBACK (swatch, back, TRUE, TRUE) + +void +context_foreground_red_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + color.r = action_select_value (select_type, + color.r, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_foreground (context, &color); +} + +void +context_foreground_green_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + color.g = action_select_value (select_type, + color.g, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_foreground (context, &color); +} + +void +context_foreground_blue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + color.b = action_select_value (select_type, + color.b, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_foreground (context, &color); +} + +void +context_background_red_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + color.r = action_select_value (select_type, + color.r, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_background (context, &color); +} + +void +context_background_green_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + color.g = action_select_value (select_type, + color.g, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_background (context, &color); +} + +void +context_background_blue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + color.b = action_select_value (select_type, + color.b, + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_context_set_background (context, &color); +} + +void +context_foreground_hue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.h = action_select_value (select_type, + hsv.h, + 0.0, 1.0, 1.0, + 1.0 / 360.0, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_foreground (context, &color); +} + +void +context_foreground_saturation_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.s = action_select_value (select_type, + hsv.s, + 0.0, 1.0, 1.0, + 0.01, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_foreground (context, &color); +} + +void +context_foreground_value_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_foreground (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.v = action_select_value (select_type, + hsv.v, + 0.0, 1.0, 1.0, + 0.01, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_foreground (context, &color); +} + +void +context_background_hue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.h = action_select_value (select_type, + hsv.h, + 0.0, 1.0, 1.0, + 1.0 / 360.0, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_background (context, &color); +} + +void +context_background_saturation_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.s = action_select_value (select_type, + hsv.s, + 0.0, 1.0, 1.0, + 0.01, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_background (context, &color); +} + +void +context_background_value_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpRGB color; + GimpHSV hsv; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + gimp_context_get_background (context, &color); + gimp_rgb_to_hsv (&color, &hsv); + hsv.v = action_select_value (select_type, + hsv.v, + 0.0, 1.0, 1.0, + 0.01, 0.01, 0.1, 0.0, FALSE); + gimp_hsv_to_rgb (&hsv, &color); + gimp_context_set_background (context, &color); +} + +void +context_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_TOOL_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "opacity", + 1.0 / 255.0, 0.01, 0.1, 0.1, FALSE); + } +} + +void +context_paint_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpLayerMode *modes; + gint n_modes; + GimpLayerMode paint_mode; + gint index; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + paint_mode = gimp_context_get_paint_mode (context); + + modes = gimp_layer_mode_get_context_array (paint_mode, + GIMP_LAYER_MODE_CONTEXT_PAINT, + &n_modes); + index = context_paint_mode_index (paint_mode, modes, n_modes); + index = action_select_value (select_type, + index, 0, n_modes - 1, 0, + 0.0, 1.0, 1.0, 0.0, FALSE); + paint_mode = modes[index]; + g_free (modes); + + gimp_context_set_paint_mode (context, paint_mode); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_TOOL_OPTIONS (tool_info->tool_options)) + { + GimpDisplay *display; + const char *value_desc; + + gimp_enum_get_value (GIMP_TYPE_LAYER_MODE, paint_mode, + NULL, NULL, &value_desc, NULL); + + display = action_data_get_display (data); + + if (value_desc && display) + { + action_message (display, G_OBJECT (tool_info->tool_options), + _("Paint Mode: %s"), value_desc); + } + } +} + +void +context_tool_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, context->gimp->tool_info_list); +} + +void +context_brush_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, + gimp_data_factory_get_container (context->gimp->brush_factory)); +} + +void +context_pattern_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, + gimp_data_factory_get_container (context->gimp->pattern_factory)); +} + +void +context_palette_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, + gimp_data_factory_get_container (context->gimp->palette_factory)); +} + +void +context_gradient_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, + gimp_data_factory_get_container (context->gimp->gradient_factory)); +} + +void +context_font_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + context_select_object (select_type, + context, + gimp_data_factory_get_container (context->gimp->font_factory)); +} + +void +context_brush_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH (brush) && gimp_data_is_writable (GIMP_DATA (brush))) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (brush), + "spacing", + 1.0, 5.0, 20.0, 0.1, FALSE); + } +} + +void +context_brush_shape_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpBrushGeneratedShape shape; + return_if_no_context (context, data); + + shape = (GimpBrushGeneratedShape) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + GimpBrushGenerated *generated = GIMP_BRUSH_GENERATED (brush); + GimpDisplay *display; + const char *value_desc; + + gimp_brush_generated_set_shape (generated, shape); + + gimp_enum_get_value (GIMP_TYPE_BRUSH_GENERATED_SHAPE, shape, + NULL, NULL, &value_desc, NULL); + display = action_data_get_display (data); + + if (value_desc && display) + { + action_message (display, G_OBJECT (brush), + _("Brush Shape: %s"), value_desc); + } + } +} + +void +context_brush_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + GimpBrushGenerated *generated = GIMP_BRUSH_GENERATED (brush); + GimpDisplay *display; + gdouble radius; + gdouble min_radius; + + radius = gimp_brush_generated_get_radius (generated); + + /* If the user uses a high precision radius adjustment command + * then we allow a minimum radius of 0.1 px, otherwise we set the + * minimum radius to 1.0 px and adjust the radius to 1.0 px if it + * is less than 1.0 px. This prevents irritating 0.1, 1.1, 2.1 etc + * radius sequences when 1.0 px steps are used. + */ + switch (select_type) + { + case GIMP_ACTION_SELECT_SMALL_PREVIOUS: + case GIMP_ACTION_SELECT_SMALL_NEXT: + case GIMP_ACTION_SELECT_PERCENT_PREVIOUS: + case GIMP_ACTION_SELECT_PERCENT_NEXT: + min_radius = 0.1; + break; + + default: + min_radius = 1.0; + + if (radius < 1.0) + radius = 1.0; + break; + } + + radius = action_select_value (select_type, + radius, + min_radius, 4000.0, min_radius, + 0.1, 1.0, 10.0, 0.05, FALSE); + gimp_brush_generated_set_radius (generated, radius); + + display = action_data_get_display (data); + + if (display) + { + action_message (action_data_get_display (data), G_OBJECT (brush), + _("Brush Radius: %2.2f"), radius); + } + } +} + +void +context_brush_spikes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (brush), + "spikes", + 0.0, 1.0, 4.0, 0.1, FALSE); + } +} + +void +context_brush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (brush), + "hardness", + 0.001, 0.01, 0.1, 0.1, FALSE); + } +} + +void +context_brush_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (brush), + "aspect-ratio", + 0.1, 1.0, 4.0, 0.1, FALSE); + } +} + +void +context_brush_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpBrush *brush; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + brush = gimp_context_get_brush (context); + + if (GIMP_IS_BRUSH_GENERATED (brush) && + gimp_data_is_writable (GIMP_DATA (brush))) + { + GimpBrushGenerated *generated = GIMP_BRUSH_GENERATED (brush); + GimpDisplay *display; + gdouble angle; + + angle = gimp_brush_generated_get_angle (generated); + + if (select_type == GIMP_ACTION_SELECT_FIRST) + angle = 0.0; + else if (select_type == GIMP_ACTION_SELECT_LAST) + angle = 90.0; + else + angle = action_select_value (select_type, + angle, + 0.0, 180.0, 0.0, + 0.1, 1.0, 15.0, 0.1, TRUE); + + gimp_brush_generated_set_angle (generated, angle); + + display = action_data_get_display (data); + + if (display) + { + action_message (action_data_get_display (data), G_OBJECT (brush), + _("Brush Angle: %2.2f"), angle); + } + } +} + + +/* private functions */ + +static void +context_select_object (GimpActionSelectType select_type, + GimpContext *context, + GimpContainer *container) +{ + GimpObject *current; + + current = + gimp_context_get_by_type (context, + gimp_container_get_children_type (container)); + + current = action_select_object (select_type, container, current); + + if (current) + gimp_context_set_by_type (context, + gimp_container_get_children_type (container), + current); +} + +static gint +context_paint_mode_index (GimpLayerMode paint_mode, + const GimpLayerMode *modes, + gint n_modes) +{ + gint i = 0; + + while (i < (n_modes - 1) && modes[i] != paint_mode) + i++; + + return i; +} + +static void +context_select_color (GimpActionSelectType select_type, + GimpRGB *color, + gboolean use_colormap, + gboolean use_palette) +{ + gint index; + gint max; + + index = context_get_color_index (use_colormap, use_palette, color); + max = context_max_color_index (use_colormap, use_palette); + + index = action_select_value (select_type, + index, + 0, max, 0, + 0, 1, 4, 0, FALSE); + + context_set_color_index (index, use_colormap, use_palette, color); +} + +static gint +context_get_color_index (gboolean use_colormap, + gboolean use_palette, + const GimpRGB *color) +{ + if (use_colormap) + { + GimpColormapEditor *editor = context_get_colormap_editor (); + + if (editor) + { + gint index = gimp_colormap_editor_get_index (editor, color); + + if (index != -1) + return index; + } + } + + if (use_palette) + { + GimpPaletteEditor *editor = context_get_palette_editor (); + + if (editor) + { + gint index = gimp_palette_editor_get_index (editor, color); + + if (index != -1) + return index; + } + } + + return 0; +} + +static gint +context_max_color_index (gboolean use_colormap, + gboolean use_palette) +{ + if (use_colormap) + { + GimpColormapEditor *editor = context_get_colormap_editor (); + + if (editor) + { + gint index = gimp_colormap_editor_max_index (editor); + + if (index != -1) + return index; + } + } + + if (use_palette) + { + GimpPaletteEditor *editor = context_get_palette_editor (); + + if (editor) + { + gint index = gimp_palette_editor_max_index (editor); + + if (index != -1) + return index; + } + } + + return 0; +} + +static gboolean +context_set_color_index (gint index, + gboolean use_colormap, + gboolean use_palette, + GimpRGB *color) +{ + if (use_colormap) + { + GimpColormapEditor *editor = context_get_colormap_editor (); + + if (editor && gimp_colormap_editor_set_index (editor, index, color)) + return TRUE; + } + + if (use_palette) + { + GimpPaletteEditor *editor = context_get_palette_editor (); + + if (editor && gimp_palette_editor_set_index (editor, index, color)) + return TRUE; + } + + return FALSE; +} + +static GimpPaletteEditor * +context_get_palette_editor (void) +{ + GtkWidget *widget; + + g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (gimp_dialog_factory_get_singleton ()), NULL); + + widget = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (), + "gimp-palette-editor"); + if (widget) + return GIMP_PALETTE_EDITOR (gtk_bin_get_child (GTK_BIN (widget))); + + return NULL; +} + +static GimpColormapEditor * +context_get_colormap_editor (void) +{ + GtkWidget *widget; + + g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (gimp_dialog_factory_get_singleton ()), NULL); + + widget = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (), + "gimp-indexed-palette"); + if (widget) + return GIMP_COLORMAP_EDITOR (gtk_bin_get_child (GTK_BIN (widget))); + + return NULL; +} diff --git a/app/actions/context-commands.h b/app/actions/context-commands.h new file mode 100644 index 0000000..76fe716 --- /dev/null +++ b/app/actions/context-commands.h @@ -0,0 +1,140 @@ +/* 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 . + */ + +#ifndef __CONTEXT_COMMANDS_H__ +#define __CONTEXT_COMMANDS_H__ + + + +void context_colors_default_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_colors_swap_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_palette_foreground_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_palette_background_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_colormap_foreground_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_colormap_background_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_swatch_foreground_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_swatch_background_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_foreground_red_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_foreground_green_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_foreground_blue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_background_red_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_background_green_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_background_blue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_foreground_hue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_foreground_saturation_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_foreground_value_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_background_hue_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_background_saturation_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_background_value_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_paint_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_tool_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_pattern_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_palette_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_gradient_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_font_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void context_brush_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_shape_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_spikes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void context_brush_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __CONTEXT_COMMANDS_H__ */ diff --git a/app/actions/cursor-info-actions.c b/app/actions/cursor-info-actions.c new file mode 100644 index 0000000..bef1106 --- /dev/null +++ b/app/actions/cursor-info-actions.c @@ -0,0 +1,81 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "display/gimpcursorview.h" + +#include "cursor-info-actions.h" +#include "cursor-info-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry cursor_info_actions[] = +{ + { "cursor-info-popup", GIMP_ICON_CURSOR, + NC_("cursor-info-action", "Pointer Information Menu"), NULL, NULL, NULL, + GIMP_HELP_POINTER_INFO_DIALOG } +}; + +static const GimpToggleActionEntry cursor_info_toggle_actions[] = +{ + { "cursor-info-sample-merged", NULL, + NC_("cursor-info-action", "_Sample Merged"), "", + NC_("cursor-info-action", "Use the composite color of all visible layers"), + cursor_info_sample_merged_cmd_callback, + TRUE, + GIMP_HELP_POINTER_INFO_SAMPLE_MERGED } +}; + + +void +cursor_info_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "cursor-info-action", + cursor_info_actions, + G_N_ELEMENTS (cursor_info_actions)); + + gimp_action_group_add_toggle_actions (group, "cursor-info-action", + cursor_info_toggle_actions, + G_N_ELEMENTS (cursor_info_toggle_actions)); +} + +void +cursor_info_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (data); + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_ACTIVE ("cursor-info-sample-merged", + gimp_cursor_view_get_sample_merged (view)); + +#undef SET_ACTIVE +} diff --git a/app/actions/cursor-info-actions.h b/app/actions/cursor-info-actions.h new file mode 100644 index 0000000..58b3f71 --- /dev/null +++ b/app/actions/cursor-info-actions.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 . + */ + +#ifndef __CURSOR_INFO_ACIONS_H__ +#define __CURSOR_INFO_ACIONS_H__ + + +void cursor_info_actions_setup (GimpActionGroup *group); +void cursor_info_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __CURSOR_INFO_ACTIONS_H__ */ diff --git a/app/actions/cursor-info-commands.c b/app/actions/cursor-info-commands.c new file mode 100644 index 0000000..b5f760b --- /dev/null +++ b/app/actions/cursor-info-commands.c @@ -0,0 +1,41 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "actions-types.h" + +#include "display/gimpcursorview.h" + +#include "cursor-info-commands.h" + + +/* public functions */ + +void +cursor_info_sample_merged_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (data); + gboolean active = g_variant_get_boolean (value); + + gimp_cursor_view_set_sample_merged (view, active); +} diff --git a/app/actions/cursor-info-commands.h b/app/actions/cursor-info-commands.h new file mode 100644 index 0000000..3562e15 --- /dev/null +++ b/app/actions/cursor-info-commands.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 . + */ + +#ifndef __CURSOR_INFO_COMMANDS_H__ +#define __CURSOR_INFO_COMMANDS_H__ + + +void cursor_info_sample_merged_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __CURSOR_INFO_COMMANDS_H__ */ diff --git a/app/actions/dashboard-actions.c b/app/actions/dashboard-actions.c new file mode 100644 index 0000000..e471307 --- /dev/null +++ b/app/actions/dashboard-actions.c @@ -0,0 +1,230 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdashboard.h" +#include "widgets/gimphelp-ids.h" + +#include "dashboard-actions.h" +#include "dashboard-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry dashboard_actions[] = +{ + { "dashboard-popup", GIMP_ICON_DIALOG_DASHBOARD, + NC_("dashboard-action", "Dashboard Menu"), NULL, NULL, NULL, + GIMP_HELP_DASHBOARD_DIALOG }, + + { "dashboard-groups", NULL, + NC_("dashboard-action", "_Groups") }, + { "dashboard-update-interval", NULL, + NC_("dashboard-action", "_Update Interval") }, + { "dashboard-history-duration", NULL, + NC_("dashboard-action", "_History Duration") }, + + { "dashboard-log-record", GIMP_ICON_RECORD, + NC_("dashboard-action", "_Start/Stop Recording..."), NULL, + NC_("dashboard-action", "Start/stop recording performance log"), + dashboard_log_record_cmd_callback, + GIMP_HELP_DASHBOARD_LOG_RECORD }, + { "dashboard-log-add-marker", GIMP_ICON_MARKER, + NC_("dashboard-action", "_Add Marker..."), NULL, + NC_("dashboard-action", "Add an event marker " + "to the performance log"), + dashboard_log_add_marker_cmd_callback, + GIMP_HELP_DASHBOARD_LOG_ADD_MARKER }, + { "dashboard-log-add-empty-marker", GIMP_ICON_MARKER, + NC_("dashboard-action", "Add _Empty Marker"), NULL, + NC_("dashboard-action", "Add an empty event marker " + "to the performance log"), + dashboard_log_add_empty_marker_cmd_callback, + GIMP_HELP_DASHBOARD_LOG_ADD_EMPTY_MARKER }, + + { "dashboard-reset", GIMP_ICON_RESET, + NC_("dashboard-action", "_Reset"), NULL, + NC_("dashboard-action", "Reset cumulative data"), + dashboard_reset_cmd_callback, + GIMP_HELP_DASHBOARD_RESET }, +}; + +static const GimpToggleActionEntry dashboard_toggle_actions[] = +{ + { "dashboard-low-swap-space-warning", NULL, + NC_("dashboard-action", "_Low Swap Space Warning"), NULL, + NC_("dashboard-action", "Raise the dashboard when " + "the swap size approaches its limit"), + dashboard_low_swap_space_warning_cmd_callback, + FALSE, + GIMP_HELP_DASHBOARD_LOW_SWAP_SPACE_WARNING } +}; + +static const GimpRadioActionEntry dashboard_update_interval_actions[] = +{ + { "dashboard-update-interval-0-25-sec", NULL, + NC_("dashboard-update-interval", "0.25 Seconds"), NULL, NULL, + GIMP_DASHBOARD_UPDATE_INTERVAL_0_25_SEC, + GIMP_HELP_DASHBOARD_UPDATE_INTERVAL }, + + { "dashboard-update-interval-0-5-sec", NULL, + NC_("dashboard-update-interval", "0.5 Seconds"), NULL, NULL, + GIMP_DASHBOARD_UPDATE_INTERVAL_0_5_SEC, + GIMP_HELP_DASHBOARD_UPDATE_INTERVAL }, + + { "dashboard-update-interval-1-sec", NULL, + NC_("dashboard-update-interval", "1 Second"), NULL, NULL, + GIMP_DASHBOARD_UPDATE_INTERVAL_1_SEC, + GIMP_HELP_DASHBOARD_UPDATE_INTERVAL }, + + { "dashboard-update-interval-2-sec", NULL, + NC_("dashboard-update-interval", "2 Seconds"), NULL, NULL, + GIMP_DASHBOARD_UPDATE_INTERVAL_2_SEC, + GIMP_HELP_DASHBOARD_UPDATE_INTERVAL }, + + { "dashboard-update-interval-4-sec", NULL, + NC_("dashboard-update-interval", "4 Seconds"), NULL, NULL, + GIMP_DASHBOARD_UPDATE_INTERVAL_4_SEC, + GIMP_HELP_DASHBOARD_UPDATE_INTERVAL } +}; + +static const GimpRadioActionEntry dashboard_history_duration_actions[] = +{ + { "dashboard-history-duration-15-sec", NULL, + NC_("dashboard-history-duration", "15 Seconds"), NULL, NULL, + GIMP_DASHBOARD_HISTORY_DURATION_15_SEC, + GIMP_HELP_DASHBOARD_HISTORY_DURATION }, + + { "dashboard-history-duration-30-sec", NULL, + NC_("dashboard-history-duration", "30 Seconds"), NULL, NULL, + GIMP_DASHBOARD_HISTORY_DURATION_30_SEC, + GIMP_HELP_DASHBOARD_HISTORY_DURATION }, + + { "dashboard-history-duration-60-sec", NULL, + NC_("dashboard-history-duration", "60 Seconds"), NULL, NULL, + GIMP_DASHBOARD_HISTORY_DURATION_60_SEC, + GIMP_HELP_DASHBOARD_HISTORY_DURATION }, + + { "dashboard-history-duration-120-sec", NULL, + NC_("dashboard-history-duration", "120 Seconds"), NULL, NULL, + GIMP_DASHBOARD_HISTORY_DURATION_120_SEC, + GIMP_HELP_DASHBOARD_HISTORY_DURATION }, + + { "dashboard-history-duration-240-sec", NULL, + NC_("dashboard-history-duration", "240 Seconds"), NULL, NULL, + GIMP_DASHBOARD_HISTORY_DURATION_240_SEC, + GIMP_HELP_DASHBOARD_HISTORY_DURATION } +}; + + +void +dashboard_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "dashboard-action", + dashboard_actions, + G_N_ELEMENTS (dashboard_actions)); + + gimp_action_group_add_toggle_actions (group, "dashboard-action", + dashboard_toggle_actions, + G_N_ELEMENTS (dashboard_toggle_actions)); + + gimp_action_group_add_radio_actions (group, "dashboard-update-interval", + dashboard_update_interval_actions, + G_N_ELEMENTS (dashboard_update_interval_actions), + NULL, + 0, + dashboard_update_interval_cmd_callback); + + gimp_action_group_add_radio_actions (group, "dashboard-history-duration", + dashboard_history_duration_actions, + G_N_ELEMENTS (dashboard_history_duration_actions), + NULL, + 0, + dashboard_history_duration_cmd_callback); +} + +void +dashboard_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + gboolean recording; + + recording = gimp_dashboard_log_is_recording (dashboard); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + switch (gimp_dashboard_get_update_interval (dashboard)) + { + case GIMP_DASHBOARD_UPDATE_INTERVAL_0_25_SEC: + SET_ACTIVE ("dashboard-update-interval-0-25-sec", TRUE); + break; + case GIMP_DASHBOARD_UPDATE_INTERVAL_0_5_SEC: + SET_ACTIVE ("dashboard-update-interval-0-5-sec", TRUE); + break; + case GIMP_DASHBOARD_UPDATE_INTERVAL_1_SEC: + SET_ACTIVE ("dashboard-update-interval-1-sec", TRUE); + break; + case GIMP_DASHBOARD_UPDATE_INTERVAL_2_SEC: + SET_ACTIVE ("dashboard-update-interval-2-sec", TRUE); + break; + case GIMP_DASHBOARD_UPDATE_INTERVAL_4_SEC: + SET_ACTIVE ("dashboard-update-interval-4-sec", TRUE); + break; + } + + switch (gimp_dashboard_get_history_duration (dashboard)) + { + case GIMP_DASHBOARD_HISTORY_DURATION_15_SEC: + SET_ACTIVE ("dashboard-history-duration-15-sec", TRUE); + break; + case GIMP_DASHBOARD_HISTORY_DURATION_30_SEC: + SET_ACTIVE ("dashboard-history-duration-30-sec", TRUE); + break; + case GIMP_DASHBOARD_HISTORY_DURATION_60_SEC: + SET_ACTIVE ("dashboard-history-duration-60-sec", TRUE); + break; + case GIMP_DASHBOARD_HISTORY_DURATION_120_SEC: + SET_ACTIVE ("dashboard-history-duration-120-sec", TRUE); + break; + case GIMP_DASHBOARD_HISTORY_DURATION_240_SEC: + SET_ACTIVE ("dashboard-history-duration-240-sec", TRUE); + break; + } + + SET_SENSITIVE ("dashboard-log-add-marker", recording); + SET_SENSITIVE ("dashboard-log-add-empty-marker", recording); + SET_SENSITIVE ("dashboard-reset", !recording); + + SET_ACTIVE ("dashboard-low-swap-space-warning", + gimp_dashboard_get_low_swap_space_warning (dashboard)); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +} diff --git a/app/actions/dashboard-actions.h b/app/actions/dashboard-actions.h new file mode 100644 index 0000000..b2f8342 --- /dev/null +++ b/app/actions/dashboard-actions.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 . + */ + +#ifndef __DASHBOARD_ACTIONS_H__ +#define __DASHBOARD_ACTIONS_H__ + + +void dashboard_actions_setup (GimpActionGroup *group); +void dashboard_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DASHBOARD_ACTIONS_H__ */ diff --git a/app/actions/dashboard-commands.c b/app/actions/dashboard-commands.c new file mode 100644 index 0000000..c1112aa --- /dev/null +++ b/app/actions/dashboard-commands.c @@ -0,0 +1,406 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpdashboard.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" + +#include "dialogs/dialogs.h" + +#include "dashboard-commands.h" + +#include "gimp-intl.h" + + +typedef struct +{ + GFile *folder; + GimpDashboardLogParams params; +} DashboardLogDialogInfo; + + +/* local function prototypes */ + +static void dashboard_log_record_response (GtkWidget *dialog, + int response_id, + GimpDashboard *dashboard); + +static void dashboard_log_add_marker_response (GtkWidget *dialog, + const gchar *description, + GimpDashboard *dashboard); + +static DashboardLogDialogInfo * dashboard_log_dialog_info_new (GimpDashboard *dashboard); +static void dashboard_log_dialog_info_free (DashboardLogDialogInfo *info); + + +/* public functions */ + + +void +dashboard_update_interval_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + GimpDashboardUpdateInteval update_interval; + + update_interval = g_variant_get_int32 (value); + + gimp_dashboard_set_update_interval (dashboard, update_interval); +} + +void +dashboard_history_duration_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + GimpDashboardHistoryDuration history_duration; + + history_duration = g_variant_get_int32 (value); + + gimp_dashboard_set_history_duration (dashboard, history_duration); +} + +void +dashboard_log_record_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + + if (! gimp_dashboard_log_is_recording (dashboard)) + { + GtkWidget *dialog; + + #define LOG_RECORD_KEY "gimp-dashboard-log-record-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (dashboard), LOG_RECORD_KEY); + + if (! dialog) + { + GtkFileFilter *filter; + DashboardLogDialogInfo *info; + GtkWidget *hbox; + GtkWidget *hbox2; + GtkWidget *label; + GtkWidget *spinbutton; + GtkWidget *toggle; + + dialog = gtk_file_chooser_dialog_new ( + "Record Performance Log", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Record"), 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_window_set_screen ( + GTK_WINDOW (dialog), + gtk_widget_get_screen (GTK_WIDGET (dashboard))); + gtk_window_set_role (GTK_WINDOW (dialog), + "gimp-dashboard-log-record"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + + gtk_file_chooser_set_do_overwrite_confirmation ( + GTK_FILE_CHOOSER (dialog), TRUE); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Log Files (*.log)")); + gtk_file_filter_add_pattern (filter, "*.log"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter); + + info = g_object_get_data (G_OBJECT (dashboard), + "gimp-dashboard-log-dialog-info"); + + if (! info) + { + info = dashboard_log_dialog_info_new (dashboard); + + g_object_set_data_full ( + G_OBJECT (dashboard), + "gimp-dashboard-log-dialog-info", info, + (GDestroyNotify) dashboard_log_dialog_info_free); + } + + if (info->folder) + { + gtk_file_chooser_set_current_folder_file ( + GTK_FILE_CHOOSER (dialog), info->folder, NULL); + } + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), + "gimp-performance.log"); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox); + gtk_widget_show (hbox); + + hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gimp_help_set_help_data (hbox2, _("Log samples per second"), NULL); + gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0); + gtk_widget_show (hbox2); + + label = gtk_label_new_with_mnemonic (_("Sample fre_quency:")); + gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + spinbutton = gimp_spin_button_new_with_range (1, 1000, 1); + gtk_box_pack_start (GTK_BOX (hbox2), spinbutton, FALSE, FALSE, 0); + gtk_widget_show (spinbutton); + + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinbutton), + info->params.sample_frequency); + + g_signal_connect (gtk_spin_button_get_adjustment ( + GTK_SPIN_BUTTON (spinbutton)), + "value-changed", + G_CALLBACK (gimp_int_adjustment_update), + &info->params.sample_frequency); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton); + + toggle = gtk_check_button_new_with_mnemonic (_("_Backtrace")); + gimp_help_set_help_data (toggle, _("Include backtraces in log"), + NULL); + gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + info->params.backtrace); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &info->params.backtrace); + + toggle = gtk_check_button_new_with_mnemonic (_("_Messages")); + gimp_help_set_help_data (toggle, + _("Include diagnostic messages in log"), + NULL); + gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + info->params.messages); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &info->params.messages); + + toggle = gtk_check_button_new_with_mnemonic (_("Progressi_ve")); + gimp_help_set_help_data (toggle, + _("Produce complete log " + "even if not properly terminated"), + NULL); + gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + info->params.progressive); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &info->params.progressive); + + g_signal_connect (dialog, "response", + G_CALLBACK (dashboard_log_record_response), + dashboard); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + gimp_help_connect (dialog, gimp_standard_help_func, + GIMP_HELP_DASHBOARD_LOG_RECORD, NULL); + + dialogs_attach_dialog (G_OBJECT (dashboard), LOG_RECORD_KEY, dialog); + + g_signal_connect_object (dashboard, "destroy", + G_CALLBACK (gtk_widget_destroy), + dialog, + G_CONNECT_SWAPPED); + + #undef LOG_RECORD_KEY + } + + gtk_window_present (GTK_WINDOW (dialog)); + } + else + { + GError *error = NULL; + + if (! gimp_dashboard_log_stop_recording (dashboard, &error)) + { + gimp_message_literal ( + gimp_editor_get_ui_manager (GIMP_EDITOR (dashboard))->gimp, + NULL, GIMP_MESSAGE_ERROR, error->message); + } + } +} + +void +dashboard_log_add_marker_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + GtkWidget *dialog; + + #define LOG_ADD_MARKER_KEY "gimp-dashboard-log-add-marker-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (dashboard), LOG_ADD_MARKER_KEY); + + if (! dialog) + { + dialog = gimp_query_string_box ( + _("Add Marker"), GTK_WIDGET (dashboard), + gimp_standard_help_func, GIMP_HELP_DASHBOARD_LOG_ADD_MARKER, + _("Enter a description for the marker"), + NULL, + G_OBJECT (dashboard), "destroy", + (GimpQueryStringCallback) dashboard_log_add_marker_response, + dashboard); + + dialogs_attach_dialog (G_OBJECT (dashboard), LOG_ADD_MARKER_KEY, dialog); + + #undef LOG_ADD_MARKER_KEY + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +dashboard_log_add_empty_marker_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + + gimp_dashboard_log_add_marker (dashboard, NULL); +} + +void +dashboard_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + + gimp_dashboard_reset (dashboard); +} + +void +dashboard_low_swap_space_warning_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDashboard *dashboard = GIMP_DASHBOARD (data); + gboolean low_swap_space_warning = g_variant_get_boolean (value); + + gimp_dashboard_set_low_swap_space_warning (dashboard, low_swap_space_warning); +} + + +/* private functions */ + +static void +dashboard_log_record_response (GtkWidget *dialog, + int response_id, + GimpDashboard *dashboard) +{ + if (response_id == GTK_RESPONSE_OK) + { + GFile *file; + DashboardLogDialogInfo *info; + GError *error = NULL; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + + info = g_object_get_data (G_OBJECT (dashboard), + "gimp-dashboard-log-dialog-info"); + + g_return_if_fail (info != NULL); + + g_set_object (&info->folder, g_file_get_parent (file)); + + if (! gimp_dashboard_log_start_recording (dashboard, + file, &info->params, + &error)) + { + gimp_message_literal ( + gimp_editor_get_ui_manager (GIMP_EDITOR (dashboard))->gimp, + NULL, GIMP_MESSAGE_ERROR, error->message); + + g_clear_error (&error); + } + + g_object_unref (file); + } + + gtk_widget_destroy (dialog); +} + +static void +dashboard_log_add_marker_response (GtkWidget *dialog, + const gchar *description, + GimpDashboard *dashboard) +{ + gimp_dashboard_log_add_marker (dashboard, description); +} + +static DashboardLogDialogInfo * +dashboard_log_dialog_info_new (GimpDashboard *dashboard) +{ + DashboardLogDialogInfo *info = g_slice_new (DashboardLogDialogInfo); + + info->folder = NULL; + info->params = *gimp_dashboard_log_get_default_params (dashboard); + + return info; +} + +static void +dashboard_log_dialog_info_free (DashboardLogDialogInfo *info) +{ + g_clear_object (&info->folder); + + g_slice_free (DashboardLogDialogInfo, info); +} diff --git a/app/actions/dashboard-commands.h b/app/actions/dashboard-commands.h new file mode 100644 index 0000000..a97b7f1 --- /dev/null +++ b/app/actions/dashboard-commands.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __DASHBOARD_COMMANDS_H__ +#define __DASHBOARD_COMMANDS_H__ + + +void dashboard_update_interval_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dashboard_history_duration_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void dashboard_log_record_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dashboard_log_add_marker_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dashboard_log_add_empty_marker_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void dashboard_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void dashboard_low_swap_space_warning_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DASHBOARD_COMMANDS_H__ */ diff --git a/app/actions/data-commands.c b/app/actions/data-commands.c new file mode 100644 index 0000000..9d71bda --- /dev/null +++ b/app/actions/data-commands.c @@ -0,0 +1,304 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpdata.h" +#include "core/gimpdatafactory.h" + +#include "file/file-open.h" + +#include "widgets/gimpclipboard.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdataeditor.h" +#include "widgets/gimpdatafactoryview.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" +#include "widgets/gimpwidgets-utils.h" + +#include "dialogs/data-delete-dialog.h" + +#include "actions.h" +#include "data-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +data_open_as_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = + gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data && gimp_data_get_file (data)) + { + GFile *file = gimp_data_get_file (data); + GtkWidget *widget = GTK_WIDGET (view); + GimpImage *image; + GimpPDBStatusType status; + GError *error = NULL; + + image = file_open_with_display (context->gimp, context, NULL, + file, FALSE, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget), + &status, &error); + + if (! image && status != GIMP_PDB_CANCEL) + { + gimp_message (context->gimp, G_OBJECT (view), + GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + } +} + +void +data_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + + if (gimp_data_factory_view_has_data_new_func (view)) + { + GimpDataFactory *factory; + GimpContext *context; + GimpData *data; + + factory = gimp_data_factory_view_get_data_factory (view); + + context = + gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = gimp_data_factory_data_new (factory, context, _("Untitled")); + + if (data) + { + gimp_context_set_by_type (context, + gimp_data_factory_view_get_children_type (view), + GIMP_OBJECT (data)); + + gtk_button_clicked (GTK_BUTTON (gimp_data_factory_view_get_edit_button (view))); + } + } +} + +void +data_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data && gimp_data_factory_view_have (view, GIMP_OBJECT (data))) + { + GimpData *new_data; + + new_data = gimp_data_factory_data_duplicate (gimp_data_factory_view_get_data_factory (view), data); + + if (new_data) + { + gimp_context_set_by_type (context, + gimp_data_factory_view_get_children_type (view), + GIMP_OBJECT (new_data)); + + gtk_button_clicked (GTK_BUTTON (gimp_data_factory_view_get_edit_button (view))); + } + } +} + +void +data_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data) + { + GFile *file = gimp_data_get_file (data); + + if (file) + { + gchar *uri = g_file_get_uri (file); + + gimp_clipboard_set_text (context->gimp, uri); + g_free (uri); + } + } +} + +void +data_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data) + { + GFile *file = gimp_data_get_file (data); + + if (file) + { + GError *error = NULL; + + if (! gimp_file_show_in_file_manager (file, &error)) + { + gimp_message (context->gimp, G_OBJECT (view), + GIMP_MESSAGE_ERROR, + _("Can't show file in file manager: %s"), + error->message); + g_clear_error (&error); + } + } + } +} + +void +data_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = + gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data && + gimp_data_is_deletable (data) && + gimp_data_factory_view_have (view, GIMP_OBJECT (data))) + { + GimpDataFactory *factory; + GtkWidget *dialog; + + factory = gimp_data_factory_view_get_data_factory (view); + + dialog = data_delete_dialog_new (factory, data, context, + GTK_WIDGET (view)); + gtk_widget_show (dialog); + } +} + +void +data_refresh_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + Gimp *gimp; + return_if_no_gimp (gimp, user_data); + + gimp_set_busy (gimp); + gimp_data_factory_data_refresh (gimp_data_factory_view_get_data_factory (view), + action_data_get_context (user_data)); + gimp_unset_busy (gimp); +} + +void +data_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpDataFactoryView *view = GIMP_DATA_FACTORY_VIEW (user_data); + GimpContext *context; + GimpData *data; + + context = gimp_container_view_get_context (GIMP_CONTAINER_EDITOR (view)->view); + + data = (GimpData *) + gimp_context_get_by_type (context, + gimp_data_factory_view_get_children_type (view)); + + if (data && gimp_data_factory_view_have (view, GIMP_OBJECT (data))) + { + GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (view)); + gint monitor = gimp_widget_get_monitor (GTK_WIDGET (view)); + GtkWidget *dockable; + + dockable = + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (context->gimp)), + context->gimp, + gimp_dialog_factory_get_singleton (), + screen, + monitor, + g_variant_get_string (value, + NULL)); + + gimp_data_editor_set_data (GIMP_DATA_EDITOR (gtk_bin_get_child (GTK_BIN (dockable))), + data); + } +} diff --git a/app/actions/data-commands.h b/app/actions/data-commands.h new file mode 100644 index 0000000..a9bb4af --- /dev/null +++ b/app/actions/data-commands.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __DATA_COMMANDS_H__ +#define __DATA_COMMANDS_H__ + + +void data_open_as_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_refresh_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void data_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DATA_COMMANDS_H__ */ diff --git a/app/actions/data-editor-commands.c b/app/actions/data-editor-commands.c new file mode 100644 index 0000000..0c8383c --- /dev/null +++ b/app/actions/data-editor-commands.c @@ -0,0 +1,43 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "actions-types.h" + +#include "widgets/gimpdataeditor.h" + +#include "data-editor-commands.h" + + +/* public functions */ + +void +data_editor_edit_active_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDataEditor *editor = GIMP_DATA_EDITOR (data); + gboolean edit_active; + + edit_active = g_variant_get_boolean (value); + + gimp_data_editor_set_edit_active (editor, edit_active); +} diff --git a/app/actions/data-editor-commands.h b/app/actions/data-editor-commands.h new file mode 100644 index 0000000..c70743d --- /dev/null +++ b/app/actions/data-editor-commands.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 . + */ + +#ifndef __DATA_EDITOR_COMMANDS_H__ +#define __DATA_EDITOR_COMMANDS_H__ + + +void data_editor_edit_active_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DATA_EDITOR_COMMANDS_H__ */ diff --git a/app/actions/debug-actions.c b/app/actions/debug-actions.c new file mode 100644 index 0000000..70ceb0d --- /dev/null +++ b/app/actions/debug-actions.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpactiongroup.h" + +#include "debug-actions.h" +#include "debug-commands.h" + + +static const GimpActionEntry debug_actions[] = +{ + { "debug-menu", NULL, "_Debug" }, + + { "debug-mem-profile", NULL, + "_Memory Profile", NULL, NULL, + debug_mem_profile_cmd_callback, + NULL }, + + { "debug-benchmark-projection", NULL, + "Benchmark _Projection", NULL, + "Invalidates the entire projection, measures the time it takes to " + "validate (render) the part that is visible in the active display, " + "and print the result to stdout.", + debug_benchmark_projection_cmd_callback, + NULL }, + + { "debug-show-image-graph", NULL, + "Show Image _Graph", NULL, + "Creates a new image showing the GEGL graph of this image", + debug_show_image_graph_cmd_callback, + NULL }, + + { "debug-dump-items", NULL, + "_Dump Items", NULL, NULL, + debug_dump_menus_cmd_callback, + NULL }, + + { "debug-dump-managers", NULL, + "Dump _UI Managers", NULL, NULL, + debug_dump_managers_cmd_callback, + NULL }, + + { "debug-dump-keyboard-shortcuts", NULL, + "Dump _Keyboard Shortcuts", NULL, NULL, + debug_dump_keyboard_shortcuts_cmd_callback, + NULL }, + + { "debug-dump-attached-data", NULL, + "Dump Attached Data", NULL, NULL, + debug_dump_attached_data_cmd_callback, + NULL } +}; + +void +debug_actions_setup (GimpActionGroup *group) +{ + gint i; + + gimp_action_group_add_actions (group, NULL, + debug_actions, + G_N_ELEMENTS (debug_actions)); + +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) + + for (i = 0; i < G_N_ELEMENTS (debug_actions); i++) + SET_VISIBLE (debug_actions[i].name, group->gimp->show_debug_menu); + +#undef SET_VISIBLE +} + +void +debug_actions_update (GimpActionGroup *group, + gpointer data) +{ +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("debug-show-image-graph", gegl_has_operation ("gegl:introspect")); + +#undef SET_SENSITIVE +} diff --git a/app/actions/debug-actions.h b/app/actions/debug-actions.h new file mode 100644 index 0000000..c00780c --- /dev/null +++ b/app/actions/debug-actions.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 . + */ + +#ifndef __DEBUG_ACTIONS_H__ +#define __DEBUG_ACTIONS_H__ + + +void debug_actions_setup (GimpActionGroup *group); +void debug_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DEBUG_ACTIONS_H__ */ diff --git a/app/actions/debug-commands.c b/app/actions/debug-commands.c new file mode 100644 index 0000000..c00c1fe --- /dev/null +++ b/app/actions/debug-commands.c @@ -0,0 +1,430 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimp-utils.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpprojectable.h" +#include "core/gimpprojection.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpuimanager.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpimagewindow.h" + +#include "menus/menus.h" + +#include "actions.h" +#include "debug-commands.h" + + +/* local function prototypes */ + +static gboolean debug_benchmark_projection (GimpDisplay *display); +static gboolean debug_show_image_graph (GimpImage *source_image); + +static void debug_dump_menus_recurse_menu (GtkWidget *menu, + gint depth, + gchar *path); + +static void debug_print_qdata (GimpObject *object); +static void debug_print_qdata_foreach (GQuark key_id, + gpointer data, + gpointer user_data); + +static gboolean debug_accel_find_func (GtkAccelKey *key, + GClosure *closure, + gpointer data); + + +/* public functions */ + +void +debug_mem_profile_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + extern gboolean gimp_debug_memsize; + Gimp *gimp; + return_if_no_gimp (gimp, data); + + gimp_debug_memsize = TRUE; + + gimp_object_get_memsize (GIMP_OBJECT (gimp), NULL); + + gimp_debug_memsize = FALSE; +} + +void +debug_benchmark_projection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + return_if_no_display (display, data); + + g_idle_add ((GSourceFunc) debug_benchmark_projection, g_object_ref (display)); +} + +void +debug_show_image_graph_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *source_image = NULL; + return_if_no_image (source_image, data); + + g_idle_add ((GSourceFunc) debug_show_image_graph, g_object_ref (source_image)); +} + +void +debug_dump_menus_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GList *list; + + for (list = gimp_menu_factory_get_registered_menus (global_menu_factory); + list; + list = g_list_next (list)) + { + GimpMenuFactoryEntry *entry = list->data; + GList *managers; + + managers = gimp_ui_managers_from_name (entry->identifier); + + if (managers) + { + GimpUIManager *manager = managers->data; + GList *list; + + for (list = manager->registered_uis; list; list = g_list_next (list)) + { + GimpUIManagerUIEntry *ui_entry = list->data; + + if (GTK_IS_MENU_SHELL (ui_entry->widget)) + { + g_print ("\n\n" + "========================================\n" + "Menu: %s%s\n" + "========================================\n\n", + entry->identifier, ui_entry->ui_path); + + debug_dump_menus_recurse_menu (ui_entry->widget, 1, + entry->identifier); + g_print ("\n"); + } + } + } + } +} + +void +debug_dump_managers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GList *list; + + for (list = gimp_menu_factory_get_registered_menus (global_menu_factory); + list; + list = g_list_next (list)) + { + GimpMenuFactoryEntry *entry = list->data; + GList *managers; + + managers = gimp_ui_managers_from_name (entry->identifier); + + if (managers) + { + g_print ("\n\n" + "========================================\n" + "UI Manager: %s\n" + "========================================\n\n", + entry->identifier); + + g_print ("%s\n", gimp_ui_manager_get_ui (managers->data)); + } + } +} + +void +debug_dump_keyboard_shortcuts_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImageWindow *window; + GimpUIManager *manager; + GtkAccelGroup *accel_group; + GList *group_it; + GList *strings = NULL; + return_if_no_display (display, data); + + window = gimp_display_shell_get_window (gimp_display_get_shell (display)); + manager = gimp_image_window_get_ui_manager (window); + + accel_group = gimp_ui_manager_get_accel_group (manager); + + /* Gather formatted strings of keyboard shortcuts */ + for (group_it = gimp_ui_manager_get_action_groups (manager); + group_it; + group_it = g_list_next (group_it)) + { + GimpActionGroup *group = group_it->data; + GList *actions = NULL; + GList *action_it = NULL; + + actions = gimp_action_group_list_actions (group); + actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare); + + for (action_it = actions; action_it; action_it = g_list_next (action_it)) + { + GimpAction *action = action_it->data; + const gchar *name = gimp_action_get_name (action); + GClosure *accel_closure = NULL; + + if (strstr (name, "-menu") || + strstr (name, "-popup") || + name[0] == '<') + continue; + + accel_closure = gimp_action_get_accel_closure (action); + + if (accel_closure) + { + GtkAccelKey *key = gtk_accel_group_find (accel_group, + debug_accel_find_func, + accel_closure); + if (key && + key->accel_key && + key->accel_flags & GTK_ACCEL_VISIBLE) + { + const gchar *label_tmp; + gchar *label; + gchar *key_string; + + label_tmp = gimp_action_get_label (action); + label = gimp_strip_uline (label_tmp); + key_string = gtk_accelerator_get_label (key->accel_key, + key->accel_mods); + + strings = g_list_prepend (strings, + g_strdup_printf ("%-20s %s", + key_string, label)); + + g_free (key_string); + g_free (label); + } + } + } + + g_list_free (actions); + } + + /* Sort and prints the strings */ + { + GList *string_it = NULL; + + strings = g_list_sort (strings, (GCompareFunc) strcmp); + + for (string_it = strings; string_it; string_it = g_list_next (string_it)) + { + g_print ("%s\n", (gchar *) string_it->data); + g_free (string_it->data); + } + + g_list_free (strings); + } +} + +void +debug_dump_attached_data_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp = action_data_get_gimp (data); + GimpContext *user_context = gimp_get_user_context (gimp); + + debug_print_qdata (GIMP_OBJECT (gimp)); + debug_print_qdata (GIMP_OBJECT (user_context)); +} + + +/* private functions */ + +static gboolean +debug_benchmark_projection (GimpDisplay *display) +{ + GimpImage *image = gimp_display_get_image (display); + + if (image) + { + GimpProjection *projection = gimp_image_get_projection (image); + + gimp_projection_stop_rendering (projection); + + GIMP_TIMER_START (); + + gimp_image_invalidate_all (image); + gimp_projection_flush_now (projection, TRUE); + + GIMP_TIMER_END ("Validation of the entire projection"); + + g_object_unref (display); + } + + return FALSE; +} + +static gboolean +debug_show_image_graph (GimpImage *source_image) +{ + GeglNode *image_graph; + GeglNode *output_node; + GimpImage *new_image; + GeglNode *introspect; + GeglNode *sink; + GeglBuffer *buffer; + gchar *new_name; + + image_graph = gimp_projectable_get_graph (GIMP_PROJECTABLE (source_image)); + + output_node = gegl_node_get_output_proxy (image_graph, "output"); + + introspect = gegl_node_new_child (NULL, + "operation", "gegl:introspect", + "node", output_node, + NULL); + sink = gegl_node_new_child (NULL, + "operation", "gegl:buffer-sink", + "buffer", &buffer, + NULL); + + gegl_node_link_many (introspect, sink, NULL); + gegl_node_process (sink); + + new_name = g_strdup_printf ("%s GEGL graph", + gimp_image_get_display_name (source_image)); + + new_image = gimp_create_image_from_buffer (source_image->gimp, + buffer, new_name); + gimp_image_set_file (new_image, g_file_new_for_uri (new_name)); + + g_free (new_name); + + g_object_unref (buffer); + + g_object_unref (sink); + g_object_unref (introspect); + + g_object_unref (source_image); + + return FALSE; +} + +static void +debug_dump_menus_recurse_menu (GtkWidget *menu, + gint depth, + gchar *path) +{ + GList *children; + GList *list; + + children = gtk_container_get_children (GTK_CONTAINER (menu)); + + for (list = children; list; list = g_list_next (list)) + { + GtkWidget *menu_item = GTK_WIDGET (list->data); + GtkWidget *child = gtk_bin_get_child (GTK_BIN (menu_item)); + + if (GTK_IS_LABEL (child)) + { + GtkWidget *submenu; + const gchar *label; + gchar *full_path; + gchar *help_page; + gchar *format_str; + + label = gtk_label_get_text (GTK_LABEL (child)); + full_path = g_strconcat (path, "/", label, NULL); + + help_page = g_object_get_data (G_OBJECT (menu_item), "gimp-help-id"); + help_page = g_strdup (help_page); + + format_str = g_strdup_printf ("%%%ds%%%ds %%-20s %%s\n", + depth * 2, depth * 2 - 40); + g_print (format_str, + "", label, "", help_page ? help_page : ""); + g_free (format_str); + g_free (help_page); + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item)); + + if (submenu) + debug_dump_menus_recurse_menu (submenu, depth + 1, full_path); + + g_free (full_path); + } + } + + g_list_free (children); +} + +static void +debug_print_qdata (GimpObject *object) +{ + g_print ("\nData attached to '%s':\n\n", gimp_object_get_name (object)); + g_datalist_foreach (&G_OBJECT (object)->qdata, + debug_print_qdata_foreach, + NULL); + g_print ("\n"); +} + +static void +debug_print_qdata_foreach (GQuark key_id, + gpointer data, + gpointer user_data) +{ + g_print ("%s: %p\n", g_quark_to_string (key_id), data); +} + +static gboolean +debug_accel_find_func (GtkAccelKey *key, + GClosure *closure, + gpointer data) +{ + return (GClosure *) data == closure; +} diff --git a/app/actions/debug-commands.h b/app/actions/debug-commands.h new file mode 100644 index 0000000..12c7dd2 --- /dev/null +++ b/app/actions/debug-commands.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#ifndef __DEBUG_COMMANDS_H__ +#define __DEBUG_COMMANDS_H__ + + +void debug_mem_profile_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_benchmark_projection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_show_image_graph_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_dump_menus_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_dump_managers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_dump_keyboard_shortcuts_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void debug_dump_attached_data_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + + +#endif /* __DEBUG_COMMANDS_H__ */ diff --git a/app/actions/dialogs-actions.c b/app/actions/dialogs-actions.c new file mode 100644 index 0000000..188752b --- /dev/null +++ b/app/actions/dialogs-actions.c @@ -0,0 +1,368 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimptoolbox.h" + +#include "display/gimpimagewindow.h" + +#include "actions.h" +#include "dialogs-actions.h" +#include "dialogs-commands.h" + +#include "gimp-intl.h" + + +const GimpStringActionEntry dialogs_dockable_actions[] = +{ + { "dialogs-toolbox", NULL, + NC_("windows-action", "Tool_box"), "B", + NULL /* set in dialogs_actions_update() */, + "gimp-toolbox", + GIMP_HELP_TOOLBOX }, + + { "dialogs-tool-options", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("dialogs-action", "Tool _Options"), NULL, + NC_("dialogs-action", "Open the tool options dialog"), + "gimp-tool-options", + GIMP_HELP_TOOL_OPTIONS_DIALOG }, + + { "dialogs-device-status", GIMP_ICON_DIALOG_DEVICE_STATUS, + NC_("dialogs-action", "_Device Status"), NULL, + NC_("dialogs-action", "Open the device status dialog"), + "gimp-device-status", + GIMP_HELP_DEVICE_STATUS_DIALOG }, + + { "dialogs-symmetry", GIMP_ICON_SYMMETRY, + NC_("dialogs-action", "_Symmetry Painting"), NULL, + NC_("dialogs-action", "Open the symmetry dialog"), + "gimp-symmetry-editor", + GIMP_HELP_SYMMETRY_DIALOG }, + + { "dialogs-layers", GIMP_ICON_DIALOG_LAYERS, + NC_("dialogs-action", "_Layers"), "L", + NC_("dialogs-action", "Open the layers dialog"), + "gimp-layer-list", + GIMP_HELP_LAYER_DIALOG }, + + { "dialogs-channels", GIMP_ICON_DIALOG_CHANNELS, + NC_("dialogs-action", "_Channels"), NULL, + NC_("dialogs-action", "Open the channels dialog"), + "gimp-channel-list", + GIMP_HELP_CHANNEL_DIALOG }, + + { "dialogs-vectors", GIMP_ICON_DIALOG_PATHS, + NC_("dialogs-action", "_Paths"), NULL, + NC_("dialogs-action", "Open the paths dialog"), + "gimp-vectors-list", + GIMP_HELP_PATH_DIALOG }, + + { "dialogs-indexed-palette", GIMP_ICON_COLORMAP, + NC_("dialogs-action", "Color_map"), NULL, + NC_("dialogs-action", "Open the colormap dialog"), + "gimp-indexed-palette", + GIMP_HELP_INDEXED_PALETTE_DIALOG }, + + { "dialogs-histogram", GIMP_ICON_HISTOGRAM, + NC_("dialogs-action", "Histogra_m"), NULL, + NC_("dialogs-action", "Open the histogram dialog"), + "gimp-histogram-editor", + GIMP_HELP_HISTOGRAM_DIALOG }, + + { "dialogs-selection-editor", GIMP_ICON_SELECTION, + NC_("dialogs-action", "_Selection Editor"), NULL, + NC_("dialogs-action", "Open the selection editor"), + "gimp-selection-editor", + GIMP_HELP_SELECTION_DIALOG }, + + { "dialogs-navigation", GIMP_ICON_DIALOG_NAVIGATION, + NC_("dialogs-action", "Na_vigation"), NULL, + NC_("dialogs-action", "Open the display navigation dialog"), + "gimp-navigation-view", + GIMP_HELP_NAVIGATION_DIALOG }, + + { "dialogs-undo-history", GIMP_ICON_DIALOG_UNDO_HISTORY, + NC_("dialogs-action", "Undo _History"), NULL, + NC_("dialogs-action", "Open the undo history dialog"), + "gimp-undo-history", + GIMP_HELP_UNDO_DIALOG }, + + { "dialogs-cursor", GIMP_ICON_CURSOR, + NC_("dialogs-action", "_Pointer"), NULL, + NC_("dialogs-action", "Open the pointer information dialog"), + "gimp-cursor-view", + GIMP_HELP_POINTER_INFO_DIALOG }, + + { "dialogs-sample-points", GIMP_ICON_SAMPLE_POINT, + NC_("dialogs-action", "_Sample Points"), NULL, + NC_("dialogs-action", "Open the sample points dialog"), + "gimp-sample-point-editor", + GIMP_HELP_SAMPLE_POINT_DIALOG }, + + { "dialogs-colors", GIMP_ICON_COLORS_DEFAULT, + NC_("dialogs-action", "Colo_rs"), NULL, + NC_("dialogs-action", "Open the FG/BG color dialog"), + "gimp-color-editor", + GIMP_HELP_COLOR_DIALOG }, + + { "dialogs-brushes", GIMP_ICON_BRUSH, + NC_("dialogs-action", "_Brushes"), "B", + NC_("dialogs-action", "Open the brushes dialog"), + "gimp-brush-grid|gimp-brush-list", + GIMP_HELP_BRUSH_DIALOG }, + + { "dialogs-brush-editor", GIMP_ICON_BRUSH, + NC_("dialogs-action", "Brush Editor"), NULL, + NC_("dialogs-action", "Open the brush editor"), + "gimp-brush-editor", + GIMP_HELP_BRUSH_EDIT }, + + { "dialogs-dynamics", GIMP_ICON_DYNAMICS, + NC_("dialogs-action", "Paint D_ynamics"), NULL, + NC_("dialogs-action", "Open paint dynamics dialog"), + "gimp-dynamics-list|gimp-dynamics-grid", + GIMP_HELP_DYNAMICS_DIALOG }, + + { "dialogs-dynamics-editor", GIMP_ICON_DYNAMICS, + NC_("dialogs-action", "Paint Dynamics Editor"), NULL, + NC_("dialogs-action", "Open the paint dynamics editor"), + "gimp-dynamics-editor", + GIMP_HELP_DYNAMICS_EDITOR_DIALOG }, + + { "dialogs-mypaint-brushes", GIMP_ICON_MYPAINT_BRUSH, + NC_("dialogs-action", "_MyPaint Brushes"), NULL, + NC_("dialogs-action", "Open the mypaint brushes dialog"), + "gimp-mypaint-brush-grid|gimp-mapyint-brush-list", + GIMP_HELP_MYPAINT_BRUSH_DIALOG }, + + { "dialogs-patterns", GIMP_ICON_PATTERN, + NC_("dialogs-action", "P_atterns"), "P", + NC_("dialogs-action", "Open the patterns dialog"), + "gimp-pattern-grid|gimp-pattern-list", + GIMP_HELP_PATTERN_DIALOG }, + + { "dialogs-gradients", GIMP_ICON_GRADIENT, + NC_("dialogs-action", "_Gradients"), "G", + NC_("dialogs-action", "Open the gradients dialog"), + "gimp-gradient-list|gimp-gradient-grid", + GIMP_HELP_GRADIENT_DIALOG }, + + { "dialogs-gradient-editor", GIMP_ICON_GRADIENT, + NC_("dialogs-action", "Gradient Editor"), NULL, + NC_("dialogs-action", "Open the gradient editor"), + "gimp-gradient-editor", + GIMP_HELP_GRADIENT_EDIT }, + + { "dialogs-palettes", GIMP_ICON_PALETTE, + NC_("dialogs-action", "Pal_ettes"), NULL, + NC_("dialogs-action", "Open the palettes dialog"), + "gimp-palette-list|gimp-palette-grid", + GIMP_HELP_PALETTE_DIALOG }, + + { "dialogs-palette-editor", GIMP_ICON_PALETTE, + NC_("dialogs-action", "Palette _Editor"), NULL, + NC_("dialogs-action", "Open the palette editor"), + "gimp-palette-editor", + GIMP_HELP_PALETTE_EDIT }, + + { "dialogs-tool-presets", GIMP_ICON_TOOL_PRESET, + NC_("dialogs-action", "Tool Pre_sets"), NULL, + NC_("dialogs-action", "Open tool presets dialog"), + "gimp-tool-preset-list|gimp-tool-preset-grid", + GIMP_HELP_TOOL_PRESET_DIALOG }, + + { "dialogs-fonts", GIMP_ICON_FONT, + NC_("dialogs-action", "_Fonts"), NULL, + NC_("dialogs-action", "Open the fonts dialog"), + "gimp-font-list|gimp-font-grid", + GIMP_HELP_FONT_DIALOG }, + + { "dialogs-buffers", GIMP_ICON_BUFFER, + NC_("dialogs-action", "B_uffers"), "", + NC_("dialogs-action", "Open the named buffers dialog"), + "gimp-buffer-list|gimp-buffer-grid", + GIMP_HELP_BUFFER_DIALOG }, + + { "dialogs-images", GIMP_ICON_DIALOG_IMAGES, + NC_("dialogs-action", "_Images"), NULL, + NC_("dialogs-action", "Open the images dialog"), + "gimp-image-list|gimp-image-grid", + GIMP_HELP_IMAGE_DIALOG }, + + { "dialogs-document-history", GIMP_ICON_DOCUMENT_OPEN_RECENT, + NC_("dialogs-action", "Document Histor_y"), "", + NC_("dialogs-action", "Open the document history dialog"), + "gimp-document-list|gimp-document-grid", + GIMP_HELP_DOCUMENT_DIALOG }, + + { "dialogs-templates", GIMP_ICON_TEMPLATE, + NC_("dialogs-action", "_Templates"), "", + NC_("dialogs-action", "Open the image templates dialog"), + "gimp-template-list|gimp-template-grid", + GIMP_HELP_TEMPLATE_DIALOG }, + + { "dialogs-error-console", GIMP_ICON_DIALOG_WARNING, + NC_("dialogs-action", "Error Co_nsole"), NULL, + NC_("dialogs-action", "Open the error console"), + "gimp-error-console", + GIMP_HELP_ERRORS_DIALOG }, + + { "dialogs-dashboard", GIMP_ICON_DIALOG_DASHBOARD, + NC_("dialogs-action", "_Dashboard"), NULL, + NC_("dialogs-action", "Open the dashboard"), + "gimp-dashboard", + GIMP_HELP_ERRORS_DIALOG } +}; + +gint n_dialogs_dockable_actions = G_N_ELEMENTS (dialogs_dockable_actions); + +static const GimpStringActionEntry dialogs_toplevel_actions[] = +{ + { "dialogs-preferences", GIMP_ICON_PREFERENCES_SYSTEM, + NC_("dialogs-action", "_Preferences"), NULL, + NC_("dialogs-action", "Open the preferences dialog"), + "gimp-preferences-dialog", + GIMP_HELP_PREFS_DIALOG }, + + { "dialogs-input-devices", GIMP_ICON_INPUT_DEVICE, + NC_("dialogs-action", "_Input Devices"), NULL, + NC_("dialogs-action", "Open the input devices editor"), + "gimp-input-devices-dialog", + GIMP_HELP_INPUT_DEVICES }, + + { "dialogs-keyboard-shortcuts", GIMP_ICON_CHAR_PICKER, + NC_("dialogs-action", "_Keyboard Shortcuts"), NULL, + NC_("dialogs-action", "Open the keyboard shortcuts editor"), + "gimp-keyboard-shortcuts-dialog", + GIMP_HELP_KEYBOARD_SHORTCUTS }, + + { "dialogs-module-dialog", GIMP_ICON_SYSTEM_RUN, + NC_("dialogs-action", "_Modules"), NULL, + NC_("dialogs-action", "Open the module manager dialog"), + "gimp-module-dialog", + GIMP_HELP_MODULE_DIALOG }, + + { "dialogs-tips", GIMP_ICON_DIALOG_INFORMATION, + NC_("dialogs-action", "_Tip of the Day"), NULL, + NC_("dialogs-action", "Show some helpful tips on using GIMP"), + "gimp-tips-dialog", + GIMP_HELP_TIPS_DIALOG }, + + { "dialogs-about", GIMP_ICON_HELP_ABOUT, +#if defined(G_OS_WIN32) + NC_("dialogs-action", "About GIMP"), +#elif defined(PLATFORM_OSX) + NC_("dialogs-action", "About"), +#else /* UNIX: use GNOME HIG */ + NC_("dialogs-action", "_About"), +#endif + NULL, + NC_("dialogs-action", "About GIMP"), + "gimp-about-dialog", + GIMP_HELP_ABOUT_DIALOG }, + + { "dialogs-action-search", GIMP_ICON_TOOL_ZOOM, + NC_("dialogs-action", "_Search and Run a Command"), "slash", + NC_("dialogs-action", "Search commands by keyword, and run them"), + "gimp-action-search-dialog", + GIMP_HELP_ACTION_SEARCH_DIALOG } +}; + + +gboolean +dialogs_actions_toolbox_exists (Gimp *gimp) +{ + GimpDialogFactory *factory = gimp_dialog_factory_get_singleton (); + gboolean toolbox_found = FALSE; + GList *iter; + + /* First look in session managed windows */ + toolbox_found = + gimp_dialog_factory_find_widget (factory, "gimp-toolbox-window") != NULL; + + /* Then in image windows */ + if (! toolbox_found) + { + GList *windows = gimp ? gimp_get_image_windows (gimp) : NULL; + + for (iter = windows; iter; iter = g_list_next (iter)) + { + GimpImageWindow *window = GIMP_IMAGE_WINDOW (windows->data); + + if (gimp_image_window_has_toolbox (window)) + { + toolbox_found = TRUE; + break; + } + } + + g_list_free (windows); + } + + return toolbox_found; +} + +void +dialogs_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_string_actions (group, "dialogs-action", + dialogs_dockable_actions, + G_N_ELEMENTS (dialogs_dockable_actions), + dialogs_create_dockable_cmd_callback); + + gimp_action_group_add_string_actions (group, "dialogs-action", + dialogs_toplevel_actions, + G_N_ELEMENTS (dialogs_toplevel_actions), + dialogs_create_toplevel_cmd_callback); +} + +void +dialogs_actions_update (GimpActionGroup *group, + gpointer data) +{ + Gimp *gimp = action_data_get_gimp (data); + const gchar *toolbox_label = NULL; + const gchar *toolbox_tooltip = NULL; + + if (dialogs_actions_toolbox_exists (gimp)) + { + toolbox_label = _("Tool_box"); + toolbox_tooltip = _("Raise the toolbox"); + } + else + { + toolbox_label = _("New Tool_box"); + toolbox_tooltip = _("Create a new toolbox"); + } + + gimp_action_group_set_action_label (group, "dialogs-toolbox", toolbox_label); + gimp_action_group_set_action_tooltip (group, "dialogs-toolbox", toolbox_tooltip); +} diff --git a/app/actions/dialogs-actions.h b/app/actions/dialogs-actions.h new file mode 100644 index 0000000..b424bdd --- /dev/null +++ b/app/actions/dialogs-actions.h @@ -0,0 +1,38 @@ +/* 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 . + */ + +#ifndef __DIALOGS_ACTIONS_H__ +#define __DIALOGS_ACTIONS_H__ + + +/* this check is needed for the extern declaration below to be correct */ +#ifndef __GIMP_ACTION_GROUP_H__ +#error "widgets/gimpactiongroup.h must be included prior to dialogs-actions.h" +#endif + +extern const GimpStringActionEntry dialogs_dockable_actions[]; +extern gint n_dialogs_dockable_actions; + + +void dialogs_actions_setup (GimpActionGroup *group); +void dialogs_actions_update (GimpActionGroup *group, + gpointer data); + +gboolean dialogs_actions_toolbox_exists (Gimp *gimp); + + +#endif /* __DIALOGS_ACTIONS_H__ */ diff --git a/app/actions/dialogs-commands.c b/app/actions/dialogs-commands.c new file mode 100644 index 0000000..5804bcc --- /dev/null +++ b/app/actions/dialogs-commands.c @@ -0,0 +1,78 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "actions.h" +#include "dialogs-commands.h" + + +/* public functions */ + +void +dialogs_create_toplevel_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + const gchar *identifier; + return_if_no_widget (widget, data); + + identifier = g_variant_get_string (value, NULL); + + if (identifier) + gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + NULL /*ui_manager*/, + identifier, -1, TRUE); +} + +void +dialogs_create_dockable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + const gchar *identifier; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + identifier = g_variant_get_string (value, NULL); + + if (identifier) + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)), + gimp, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + identifier); +} diff --git a/app/actions/dialogs-commands.h b/app/actions/dialogs-commands.h new file mode 100644 index 0000000..22abafc --- /dev/null +++ b/app/actions/dialogs-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __DIALOGS_COMMANDS_H__ +#define __DIALOGS_COMMANDS_H__ + + +void dialogs_create_toplevel_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dialogs_create_dockable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DIALOGS_COMMANDS_H__ */ diff --git a/app/actions/dock-actions.c b/app/actions/dock-actions.c new file mode 100644 index 0000000..1f7dc38 --- /dev/null +++ b/app/actions/dock-actions.c @@ -0,0 +1,141 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenudock.h" + +#include "display/gimpimagewindow.h" + +#include "actions.h" +#include "dock-actions.h" +#include "dock-commands.h" +#include "window-actions.h" +#include "window-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry dock_actions[] = +{ + { "dock-move-to-screen-menu", GIMP_ICON_WINDOW_MOVE_TO_SCREEN, + NC_("dock-action", "M_ove to Screen"), NULL, NULL, NULL, + GIMP_HELP_DOCK_CHANGE_SCREEN }, + + { "dock-close", GIMP_ICON_WINDOW_CLOSE, + NC_("dock-action", "Close Dock"), "", NULL, + window_close_cmd_callback, + GIMP_HELP_DOCK_CLOSE }, + + { "dock-open-display", NULL, + NC_("dock-action", "_Open Display..."), NULL, + NC_("dock-action", "Connect to another display"), + window_open_display_cmd_callback, + NULL } +}; + +static const GimpToggleActionEntry dock_toggle_actions[] = +{ + { "dock-show-image-menu", NULL, + NC_("dock-action", "_Show Image Selection"), NULL, NULL, + dock_toggle_image_menu_cmd_callback, + TRUE, + GIMP_HELP_DOCK_IMAGE_MENU }, + + { "dock-auto-follow-active", NULL, + NC_("dock-action", "Auto _Follow Active Image"), NULL, NULL, + dock_toggle_auto_cmd_callback, + TRUE, + GIMP_HELP_DOCK_AUTO_BUTTON } +}; + + +void +dock_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "dock-action", + dock_actions, + G_N_ELEMENTS (dock_actions)); + + gimp_action_group_add_toggle_actions (group, "dock-action", + dock_toggle_actions, + G_N_ELEMENTS (dock_toggle_actions)); + + window_actions_setup (group, GIMP_HELP_DOCK_CHANGE_SCREEN); +} + +void +dock_actions_update (GimpActionGroup *group, + gpointer data) +{ + GtkWidget *widget = action_data_get_widget (data); + GtkWidget *toplevel = NULL; + + if (widget) + toplevel = gtk_widget_get_toplevel (widget); + +#define SET_ACTIVE(action,active) \ + gimp_action_group_set_action_active (group, action, (active) != 0) +#define SET_VISIBLE(action,active) \ + gimp_action_group_set_action_visible (group, action, (active) != 0) + + if (GIMP_IS_DOCK_WINDOW (toplevel)) + { + GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (toplevel); + gboolean show_image_menu = ! gimp_dock_window_has_toolbox (dock_window); + + if (show_image_menu) + { + SET_VISIBLE ("dock-show-image-menu", TRUE); + SET_VISIBLE ("dock-auto-follow-active", TRUE); + + SET_ACTIVE ("dock-show-image-menu", + gimp_dock_window_get_show_image_menu (dock_window)); + SET_ACTIVE ("dock-auto-follow-active", + gimp_dock_window_get_auto_follow_active (dock_window)); + } + else + { + SET_VISIBLE ("dock-show-image-menu", FALSE); + SET_VISIBLE ("dock-auto-follow-active", FALSE); + } + + /* update the window actions only in the context of their + * own window (not in the context of some display or NULL) + * (see view-actions.c) + */ + window_actions_update (group, toplevel); + } + else if (GIMP_IS_IMAGE_WINDOW (toplevel)) + { + SET_VISIBLE ("dock-show-image-menu", FALSE); + SET_VISIBLE ("dock-auto-follow-active", FALSE); + } + +#undef SET_ACTIVE +#undef SET_VISIBLE +} diff --git a/app/actions/dock-actions.h b/app/actions/dock-actions.h new file mode 100644 index 0000000..9e26c36 --- /dev/null +++ b/app/actions/dock-actions.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 . + */ + +#ifndef __DOCK_ACTIONS_H__ +#define __DOCK_ACTIONS_H__ + + +void dock_actions_setup (GimpActionGroup *group); +void dock_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DOCK_ACTIONS_H__ */ diff --git a/app/actions/dock-commands.c b/app/actions/dock-commands.c new file mode 100644 index 0000000..35508d5 --- /dev/null +++ b/app/actions/dock-commands.c @@ -0,0 +1,85 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpdockwindow.h" +#include "widgets/gimpdockwindow.h" + +#include "actions.h" +#include "dock-commands.h" + + +static GimpDockWindow * +dock_commands_get_dock_window_from_widget (GtkWidget *widget) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + GimpDockWindow *dock_window = NULL; + + if (GIMP_IS_DOCK_WINDOW (toplevel)) + dock_window = GIMP_DOCK_WINDOW (toplevel); + + return dock_window; +} + + +/* public functions */ + +void +dock_toggle_image_menu_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget = NULL; + GimpDockWindow *dock_window = NULL; + return_if_no_widget (widget, data); + + dock_window = dock_commands_get_dock_window_from_widget (widget); + + if (dock_window) + { + gboolean active = g_variant_get_boolean (value); + + gimp_dock_window_set_show_image_menu (dock_window, active); + } +} + +void +dock_toggle_auto_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget = NULL; + GimpDockWindow *dock_window = NULL; + return_if_no_widget (widget, data); + + dock_window = dock_commands_get_dock_window_from_widget (widget); + + if (dock_window) + { + gboolean active = g_variant_get_boolean (value); + + gimp_dock_window_set_auto_follow_active (dock_window, active); + } +} diff --git a/app/actions/dock-commands.h b/app/actions/dock-commands.h new file mode 100644 index 0000000..73120de --- /dev/null +++ b/app/actions/dock-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __DOCK_COMMANDS_H__ +#define __DOCK_COMMANDS_H__ + + +void dock_toggle_image_menu_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dock_toggle_auto_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DOCK_COMMANDS_H__ */ diff --git a/app/actions/dockable-actions.c b/app/actions/dockable-actions.c new file mode 100644 index 0000000..d80ca33 --- /dev/null +++ b/app/actions/dockable-actions.c @@ -0,0 +1,375 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpcontainerview-utils.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockable.h" +#include "widgets/gimpdockbook.h" +#include "widgets/gimpdocked.h" +#include "widgets/gimphelp-ids.h" + +#include "dialogs-actions.h" +#include "dockable-actions.h" +#include "dockable-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry dockable_actions[] = +{ + { "dockable-popup", NULL, + NC_("dockable-action", "Dialogs Menu"), NULL, NULL, NULL, + GIMP_HELP_DOCK }, + + { "dockable-menu", "image-missing", "" }, + { "dockable-add-tab-menu", NULL, NC_("dockable-action", + "_Add Tab") }, + { "dockable-preview-size-menu", NULL, NC_("dockable-action", + "_Preview Size") }, + { "dockable-tab-style-menu", NULL, NC_("dockable-action", + "_Tab Style") }, + + { "dockable-close-tab", "window-close", + NC_("dockable-action", "_Close Tab"), "", NULL, + dockable_close_tab_cmd_callback, + GIMP_HELP_DOCK_TAB_CLOSE }, + + { "dockable-detach-tab", GIMP_ICON_DETACH, + NC_("dockable-action", "_Detach Tab"), "", NULL, + dockable_detach_tab_cmd_callback, + GIMP_HELP_DOCK_TAB_DETACH } +}; + +#define VIEW_SIZE(action,label,size) \ + { "dockable-preview-size-" action, NULL, \ + (label), NULL, NULL, \ + (size), \ + GIMP_HELP_DOCK_PREVIEW_SIZE } +#define TAB_STYLE(action,label,style) \ + { "dockable-tab-style-" action, NULL, \ + (label), NULL, NULL, \ + (style), \ + GIMP_HELP_DOCK_TAB_STYLE } + +static const GimpRadioActionEntry dockable_view_size_actions[] = +{ + VIEW_SIZE ("tiny", + NC_("preview-size", "_Tiny"), GIMP_VIEW_SIZE_TINY), + VIEW_SIZE ("extra-small", + NC_("preview-size", "E_xtra Small"), GIMP_VIEW_SIZE_EXTRA_SMALL), + VIEW_SIZE ("small", + NC_("preview-size", "_Small"), GIMP_VIEW_SIZE_SMALL), + VIEW_SIZE ("medium", + NC_("preview-size", "_Medium"), GIMP_VIEW_SIZE_MEDIUM), + VIEW_SIZE ("large", + NC_("preview-size", "_Large"), GIMP_VIEW_SIZE_LARGE), + VIEW_SIZE ("extra-large", + NC_("preview-size", "Ex_tra Large"), GIMP_VIEW_SIZE_EXTRA_LARGE), + VIEW_SIZE ("huge", + NC_("preview-size", "_Huge"), GIMP_VIEW_SIZE_HUGE), + VIEW_SIZE ("enormous", + NC_("preview-size", "_Enormous"), GIMP_VIEW_SIZE_ENORMOUS), + VIEW_SIZE ("gigantic", + NC_("preview-size", "_Gigantic"), GIMP_VIEW_SIZE_GIGANTIC) +}; + +static const GimpRadioActionEntry dockable_tab_style_actions[] = +{ + TAB_STYLE ("icon", + NC_("tab-style", "_Icon"), GIMP_TAB_STYLE_ICON), + TAB_STYLE ("preview", + NC_("tab-style", "Current _Status"), GIMP_TAB_STYLE_PREVIEW), + TAB_STYLE ("name", + NC_("tab-style", "_Text"), GIMP_TAB_STYLE_NAME), + TAB_STYLE ("icon-name", + NC_("tab-style", "I_con & Text"), GIMP_TAB_STYLE_ICON_NAME), + TAB_STYLE ("preview-name", + NC_("tab-style", "St_atus & Text"), GIMP_TAB_STYLE_PREVIEW_NAME), + TAB_STYLE ("automatic", + NC_("tab-style", "Automatic"), GIMP_TAB_STYLE_AUTOMATIC) +}; + +#undef VIEW_SIZE +#undef TAB_STYLE + + +static const GimpToggleActionEntry dockable_toggle_actions[] = +{ + { "dockable-lock-tab", NULL, + NC_("dockable-action", "Loc_k Tab to Dock"), NULL, + NC_("dockable-action", + "Protect this tab from being dragged with the mouse pointer"), + dockable_lock_tab_cmd_callback, + FALSE, + GIMP_HELP_DOCK_TAB_LOCK }, + + { "dockable-show-button-bar", NULL, + NC_("dockable-action", "Show _Button Bar"), NULL, NULL, + dockable_show_button_bar_cmd_callback, + TRUE, + GIMP_HELP_DOCK_SHOW_BUTTON_BAR } +}; + +static const GimpRadioActionEntry dockable_view_type_actions[] = +{ + { "dockable-view-type-list", NULL, + NC_("dockable-action", "View as _List"), NULL, NULL, + GIMP_VIEW_TYPE_LIST, + GIMP_HELP_DOCK_VIEW_AS_LIST }, + + { "dockable-view-type-grid", NULL, + NC_("dockable-action", "View as _Grid"), NULL, NULL, + GIMP_VIEW_TYPE_GRID, + GIMP_HELP_DOCK_VIEW_AS_GRID } +}; + + +void +dockable_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "dockable-action", + dockable_actions, + G_N_ELEMENTS (dockable_actions)); + + gimp_action_group_add_toggle_actions (group, "dockable-action", + dockable_toggle_actions, + G_N_ELEMENTS (dockable_toggle_actions)); + + gimp_action_group_add_string_actions (group, "dialogs-action", + dialogs_dockable_actions, + n_dialogs_dockable_actions, + dockable_add_tab_cmd_callback); + + gimp_action_group_add_radio_actions (group, "preview-size", + dockable_view_size_actions, + G_N_ELEMENTS (dockable_view_size_actions), + NULL, + GIMP_VIEW_SIZE_MEDIUM, + dockable_view_size_cmd_callback); + + gimp_action_group_add_radio_actions (group, "tab-style", + dockable_tab_style_actions, + G_N_ELEMENTS (dockable_tab_style_actions), + NULL, + GIMP_TAB_STYLE_AUTOMATIC, + dockable_tab_style_cmd_callback); + + gimp_action_group_add_radio_actions (group, "dockable-action", + dockable_view_type_actions, + G_N_ELEMENTS (dockable_view_type_actions), + NULL, + GIMP_VIEW_TYPE_LIST, + dockable_toggle_view_cmd_callback); +} + +void +dockable_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpDockable *dockable; + GimpDockbook *dockbook; + GimpDocked *docked; + GimpDock *dock; + GimpDialogFactoryEntry *entry; + GimpContainerView *view; + GimpViewType view_type = -1; + gboolean list_view_available = FALSE; + gboolean grid_view_available = FALSE; + gboolean locked = FALSE; + GimpViewSize view_size = -1; + GimpTabStyle tab_style = -1; + gint n_pages = 0; + gint n_books = 0; + GimpDockedInterface *docked_iface = NULL; + + if (GIMP_IS_DOCKBOOK (data)) + { + gint page_num; + + dockbook = GIMP_DOCKBOOK (data); + + page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); + + dockable = (GimpDockable *) + gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), page_num); + } + else if (GIMP_IS_DOCKABLE (data)) + { + dockable = GIMP_DOCKABLE (data); + dockbook = gimp_dockable_get_dockbook (dockable); + } + else + { + return; + } + + docked = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (dockable))); + dock = gimp_dockbook_get_dock (dockbook); + + + gimp_dialog_factory_from_widget (GTK_WIDGET (dockable), &entry); + + if (entry) + { + gchar *identifier; + gchar *substring = NULL; + + identifier = g_strdup (entry->identifier); + + if ((substring = strstr (identifier, "grid"))) + view_type = GIMP_VIEW_TYPE_GRID; + else if ((substring = strstr (identifier, "list"))) + view_type = GIMP_VIEW_TYPE_LIST; + + if (substring) + { + memcpy (substring, "list", 4); + if (gimp_dialog_factory_find_entry (gimp_dock_get_dialog_factory (dock), + identifier)) + list_view_available = TRUE; + + memcpy (substring, "grid", 4); + if (gimp_dialog_factory_find_entry (gimp_dock_get_dialog_factory (dock), + identifier)) + grid_view_available = TRUE; + } + + g_free (identifier); + } + + view = gimp_container_view_get_by_dockable (dockable); + + if (view) + view_size = gimp_container_view_get_view_size (view, NULL); + + tab_style = gimp_dockable_get_tab_style (dockable); + + n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (dockbook)); + n_books = g_list_length (gimp_dock_get_dockbooks (dock)); + +#define SET_ACTIVE(action,active) \ + gimp_action_group_set_action_active (group, action, (active) != 0) +#define SET_VISIBLE(action,active) \ + gimp_action_group_set_action_visible (group, action, (active) != 0) +#define SET_SENSITIVE(action,sensitive) \ + gimp_action_group_set_action_sensitive (group, action, (sensitive) != 0) + + + locked = gimp_dockable_is_locked (dockable); + + SET_SENSITIVE ("dockable-detach-tab", (! locked && + (n_pages > 1 || n_books > 1))); + + SET_ACTIVE ("dockable-lock-tab", locked); + + SET_VISIBLE ("dockable-preview-size-menu", view_size != -1); + + if (view_size != -1) + { + if (view_size >= GIMP_VIEW_SIZE_GIGANTIC) + { + SET_ACTIVE ("dockable-preview-size-gigantic", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_ENORMOUS) + { + SET_ACTIVE ("dockable-preview-size-enormous", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_HUGE) + { + SET_ACTIVE ("dockable-preview-size-huge", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_EXTRA_LARGE) + { + SET_ACTIVE ("dockable-preview-size-extra-large", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_LARGE) + { + SET_ACTIVE ("dockable-preview-size-large", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_MEDIUM) + { + SET_ACTIVE ("dockable-preview-size-medium", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_SMALL) + { + SET_ACTIVE ("dockable-preview-size-small", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_EXTRA_SMALL) + { + SET_ACTIVE ("dockable-preview-size-extra-small", TRUE); + } + else if (view_size >= GIMP_VIEW_SIZE_TINY) + { + SET_ACTIVE ("dockable-preview-size-tiny", TRUE); + } + } + + if (tab_style == GIMP_TAB_STYLE_ICON) + SET_ACTIVE ("dockable-tab-style-icon", TRUE); + else if (tab_style == GIMP_TAB_STYLE_PREVIEW) + SET_ACTIVE ("dockable-tab-style-preview", TRUE); + else if (tab_style == GIMP_TAB_STYLE_NAME) + SET_ACTIVE ("dockable-tab-style-name", TRUE); + else if (tab_style == GIMP_TAB_STYLE_ICON_NAME) + SET_ACTIVE ("dockable-tab-style-icon-name", TRUE); + else if (tab_style == GIMP_TAB_STYLE_PREVIEW_NAME) + SET_ACTIVE ("dockable-tab-style-preview-name", TRUE); + else if (tab_style == GIMP_TAB_STYLE_AUTOMATIC) + SET_ACTIVE ("dockable-tab-style-automatic", TRUE); + + docked_iface = GIMP_DOCKED_GET_INTERFACE (docked); + SET_SENSITIVE ("dockable-tab-style-preview", + docked_iface->get_preview); + SET_SENSITIVE ("dockable-tab-style-preview-name", + docked_iface->get_preview); + + SET_VISIBLE ("dockable-view-type-grid", view_type != -1); + SET_VISIBLE ("dockable-view-type-list", view_type != -1); + + if (view_type != -1) + { + if (view_type == GIMP_VIEW_TYPE_LIST) + SET_ACTIVE ("dockable-view-type-list", TRUE); + else + SET_ACTIVE ("dockable-view-type-grid", TRUE); + + SET_SENSITIVE ("dockable-view-type-grid", grid_view_available); + SET_SENSITIVE ("dockable-view-type-list", list_view_available); + } + + SET_VISIBLE ("dockable-show-button-bar", gimp_docked_has_button_bar (docked)); + SET_ACTIVE ("dockable-show-button-bar", + gimp_docked_get_show_button_bar (docked)); + +#undef SET_ACTIVE +#undef SET_VISIBLE +#undef SET_SENSITIVE +} diff --git a/app/actions/dockable-actions.h b/app/actions/dockable-actions.h new file mode 100644 index 0000000..d70872d --- /dev/null +++ b/app/actions/dockable-actions.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 . + */ + +#ifndef __DOCKABLE_ACTIONS_H__ +#define __DOCKABLE_ACTIONS_H__ + + +void dockable_actions_setup (GimpActionGroup *group); +void dockable_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DOCKABLE_ACTIONS_H__ */ diff --git a/app/actions/dockable-commands.c b/app/actions/dockable-commands.c new file mode 100644 index 0000000..60229e3 --- /dev/null +++ b/app/actions/dockable-commands.c @@ -0,0 +1,298 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpcontainerview-utils.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockable.h" +#include "widgets/gimpdockbook.h" +#include "widgets/gimpdocked.h" +#include "widgets/gimpsessioninfo.h" + +#include "dockable-commands.h" + + +static GimpDockable * dockable_get_current (GimpDockbook *dockbook); + + +/* public functions */ + +void +dockable_add_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + + gimp_dockbook_add_from_dialog_factory (dockbook, + g_variant_get_string (value, NULL), + -1); +} + +void +dockable_close_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + + if (dockable) + { + g_object_ref (dockable); + gimp_dockbook_remove (dockbook, dockable); + gtk_widget_destroy (GTK_WIDGET (dockable)); + g_object_unref (dockable); + } +} + +void +dockable_detach_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + + if (dockable) + gimp_dockable_detach (dockable); +} + +void +dockable_lock_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + + if (dockable) + { + gboolean lock = g_variant_get_boolean (value); + + gimp_dockable_set_locked (dockable, lock); + } +} + +void +dockable_toggle_view_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable; + GimpViewType view_type; + gint page_num; + + view_type = (GimpViewType) g_variant_get_int32 (value); + + page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); + + dockable = (GimpDockable *) + gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), page_num); + + if (dockable) + { + GimpDialogFactoryEntry *entry; + + gimp_dialog_factory_from_widget (GTK_WIDGET (dockable), &entry); + + if (entry) + { + gchar *identifier; + gchar *substring = NULL; + + identifier = g_strdup (entry->identifier); + + substring = strstr (identifier, "grid"); + + if (substring && view_type == GIMP_VIEW_TYPE_GRID) + { + g_free (identifier); + return; + } + + if (! substring) + { + substring = strstr (identifier, "list"); + + if (substring && view_type == GIMP_VIEW_TYPE_LIST) + { + g_free (identifier); + return; + } + } + + if (substring) + { + GimpContainerView *old_view; + GtkWidget *new_dockable; + GimpDock *dock; + gint view_size = -1; + + if (view_type == GIMP_VIEW_TYPE_LIST) + memcpy (substring, "list", 4); + else if (view_type == GIMP_VIEW_TYPE_GRID) + memcpy (substring, "grid", 4); + + old_view = gimp_container_view_get_by_dockable (dockable); + + if (old_view) + view_size = gimp_container_view_get_view_size (old_view, NULL); + + dock = gimp_dockbook_get_dock (dockbook); + new_dockable = gimp_dialog_factory_dockable_new (gimp_dock_get_dialog_factory (dock), + dock, + identifier, + view_size); + + if (new_dockable) + { + GimpDocked *old; + GimpDocked *new; + gboolean show; + + gimp_dockable_set_locked (GIMP_DOCKABLE (new_dockable), + gimp_dockable_is_locked (dockable)); + + old = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (dockable))); + new = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (new_dockable))); + + show = gimp_docked_get_show_button_bar (old); + gimp_docked_set_show_button_bar (new, show); + + /* Maybe gimp_dialog_factory_dockable_new() returned + * an already existing singleton dockable, so check + * if it already is attached to a dockbook. + */ + if (! gimp_dockable_get_dockbook (GIMP_DOCKABLE (new_dockable))) + { + gimp_dockbook_add (dockbook, GIMP_DOCKABLE (new_dockable), + page_num); + + g_object_ref (dockable); + gimp_dockbook_remove (dockbook, dockable); + gtk_widget_destroy (GTK_WIDGET (dockable)); + g_object_unref (dockable); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (dockbook), + page_num); + } + } + } + + g_free (identifier); + } + } +} + +void +dockable_view_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + gint view_size; + + view_size = g_variant_get_int32 (value); + + if (dockable) + { + GimpContainerView *view = gimp_container_view_get_by_dockable (dockable); + + if (view) + { + gint old_size; + gint border_width; + + old_size = gimp_container_view_get_view_size (view, &border_width); + + if (old_size != view_size) + gimp_container_view_set_view_size (view, view_size, border_width); + } + } +} + +void +dockable_tab_style_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + GimpTabStyle tab_style; + + tab_style = (GimpTabStyle) g_variant_get_int32 (value); + + if (dockable && gimp_dockable_get_tab_style (dockable) != tab_style) + { + GtkWidget *tab_widget; + + gimp_dockable_set_tab_style (dockable, tab_style); + + tab_widget = gimp_dockbook_create_tab_widget (dockbook, dockable); + + gtk_notebook_set_tab_label (GTK_NOTEBOOK (dockbook), + GTK_WIDGET (dockable), + tab_widget); + } +} + +void +dockable_show_button_bar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (data); + GimpDockable *dockable = dockable_get_current (dockbook); + + if (dockable) + { + GimpDocked *docked; + gboolean show; + + docked = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (dockable))); + show = g_variant_get_boolean (value); + + gimp_docked_set_show_button_bar (docked, show); + } +} + + +/* private functions */ + +static GimpDockable * +dockable_get_current (GimpDockbook *dockbook) +{ + gint page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); + + return (GimpDockable *) gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), + page_num); +} diff --git a/app/actions/dockable-commands.h b/app/actions/dockable-commands.h new file mode 100644 index 0000000..3ffb848 --- /dev/null +++ b/app/actions/dockable-commands.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 . + */ + +#ifndef __DOCKABLE_COMMANDS_H__ +#define __DOCKABLE_COMMANDS_H__ + + +void dockable_add_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_close_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_detach_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_lock_tab_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void dockable_toggle_view_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_view_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_tab_style_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void dockable_show_button_bar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DOCKABLE_COMMANDS_H__ */ diff --git a/app/actions/documents-actions.c b/app/actions/documents-actions.c new file mode 100644 index 0000000..80c0371 --- /dev/null +++ b/app/actions/documents-actions.c @@ -0,0 +1,143 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "documents-actions.h" +#include "documents-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry documents_actions[] = +{ + { "documents-popup", GIMP_ICON_DOCUMENT_OPEN_RECENT, + NC_("documents-action", "Documents Menu"), NULL, NULL, NULL, + GIMP_HELP_DOCUMENT_DIALOG }, + + { "documents-open", GIMP_ICON_DOCUMENT_OPEN, + NC_("documents-action", "_Open Image"), NULL, + NC_("documents-action", "Open the selected entry"), + documents_open_cmd_callback, + GIMP_HELP_DOCUMENT_OPEN }, + + { "documents-raise-or-open", NULL, + NC_("documents-action", "_Raise or Open Image"), NULL, + NC_("documents-action", "Raise window if already open"), + documents_raise_or_open_cmd_callback, + GIMP_HELP_DOCUMENT_OPEN }, + + { "documents-file-open-dialog", NULL, + NC_("documents-action", "File Open _Dialog"), NULL, + NC_("documents-action", "Open image dialog"), + documents_file_open_dialog_cmd_callback, + GIMP_HELP_DOCUMENT_OPEN }, + + { "documents-copy-location", GIMP_ICON_EDIT_COPY, + NC_("documents-action", "Copy Image _Location"), NULL, + NC_("documents-action", "Copy image location to clipboard"), + documents_copy_location_cmd_callback, + GIMP_HELP_DOCUMENT_COPY_LOCATION }, + + { "documents-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("documents-action", "Show in _File Manager"), NULL, + NC_("documents-action", "Show image location in the file manager"), + documents_show_in_file_manager_cmd_callback, + GIMP_HELP_DOCUMENT_SHOW_IN_FILE_MANAGER }, + + { "documents-remove", GIMP_ICON_LIST_REMOVE, + NC_("documents-action", "Remove _Entry"), NULL, + NC_("documents-action", "Remove the selected entry"), + documents_remove_cmd_callback, + GIMP_HELP_DOCUMENT_REMOVE }, + + { "documents-clear", GIMP_ICON_SHRED, + NC_("documents-action", "_Clear History"), NULL, + NC_("documents-action", "Clear the entire document history"), + documents_clear_cmd_callback, + GIMP_HELP_DOCUMENT_CLEAR }, + + { "documents-recreate-preview", GIMP_ICON_VIEW_REFRESH, + NC_("documents-action", "Recreate _Preview"), NULL, + NC_("documents-action", "Recreate preview"), + documents_recreate_preview_cmd_callback, + GIMP_HELP_DOCUMENT_REFRESH }, + + { "documents-reload-previews", NULL, + NC_("documents-action", "Reload _all Previews"), NULL, + NC_("documents-action", "Reload all previews"), + documents_reload_previews_cmd_callback, + GIMP_HELP_DOCUMENT_REFRESH }, + + { "documents-remove-dangling", NULL, + NC_("documents-action", "Remove Dangling E_ntries"), NULL, + NC_("documents-action", + "Remove entries for which the corresponding file is not available"), + documents_remove_dangling_cmd_callback, + GIMP_HELP_DOCUMENT_REFRESH } +}; + + +void +documents_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "documents-action", + documents_actions, + G_N_ELEMENTS (documents_actions)); +} + +void +documents_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpContext *context; + GimpImagefile *imagefile = NULL; + + context = action_data_get_context (data); + + if (context) + imagefile = gimp_context_get_imagefile (context); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("documents-open", imagefile); + SET_SENSITIVE ("documents-raise-or-open", imagefile); + SET_SENSITIVE ("documents-file-open-dialog", TRUE); + SET_SENSITIVE ("documents-copy-location", imagefile); + SET_SENSITIVE ("documents-show-in-file-manager", imagefile); + SET_SENSITIVE ("documents-remove", imagefile); + SET_SENSITIVE ("documents-clear", TRUE); + SET_SENSITIVE ("documents-recreate-preview", imagefile); + SET_SENSITIVE ("documents-reload-previews", imagefile); + SET_SENSITIVE ("documents-remove-dangling", imagefile); + +#undef SET_SENSITIVE +} diff --git a/app/actions/documents-actions.h b/app/actions/documents-actions.h new file mode 100644 index 0000000..3d31f27 --- /dev/null +++ b/app/actions/documents-actions.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 . + */ + +#ifndef __DOCUMENTS_ACTIONS_H__ +#define __DOCUMENTS_ACTIONS_H__ + + +void documents_actions_setup (GimpActionGroup *group); +void documents_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DOCUMENTS_ACTIONS_H__ */ diff --git a/app/actions/documents-commands.c b/app/actions/documents-commands.c new file mode 100644 index 0000000..b85d065 --- /dev/null +++ b/app/actions/documents-commands.c @@ -0,0 +1,410 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpthumb/gimpthumb.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimagefile.h" + +#include "file/file-open.h" + +#include "widgets/gimpclipboard.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpcontainerview-utils.h" +#include "widgets/gimpdocumentview.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" +#include "display/gimpdisplayshell.h" + +#include "documents-commands.h" +#include "file-commands.h" + +#include "gimp-intl.h" + + +typedef struct +{ + const gchar *name; + gboolean found; +} RaiseClosure; + + +/* local function prototypes */ + +static void documents_open_image (GtkWidget *editor, + GimpContext *context, + GimpImagefile *imagefile); +static void documents_raise_display (GimpDisplay *display, + RaiseClosure *closure); + + + +/* public functions */ + +void +documents_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpContainer *container; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + container = gimp_container_view_get_container (editor->view); + + imagefile = gimp_context_get_imagefile (context); + + if (imagefile && gimp_container_have (container, GIMP_OBJECT (imagefile))) + { + documents_open_image (GTK_WIDGET (editor), context, imagefile); + } + else + { + file_file_open_dialog (context->gimp, NULL, GTK_WIDGET (editor)); + } +} + +void +documents_raise_or_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpContainer *container; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + container = gimp_container_view_get_container (editor->view); + + imagefile = gimp_context_get_imagefile (context); + + if (imagefile && gimp_container_have (container, GIMP_OBJECT (imagefile))) + { + RaiseClosure closure; + + closure.name = gimp_object_get_name (imagefile); + closure.found = FALSE; + + gimp_container_foreach (context->gimp->displays, + (GFunc) documents_raise_display, + &closure); + + if (! closure.found) + documents_open_image (GTK_WIDGET (editor), context, imagefile); + } +} + +void +documents_file_open_dialog_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpContainer *container; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + container = gimp_container_view_get_container (editor->view); + + imagefile = gimp_context_get_imagefile (context); + + if (imagefile && gimp_container_have (container, GIMP_OBJECT (imagefile))) + { + file_file_open_dialog (context->gimp, + gimp_imagefile_get_file (imagefile), + GTK_WIDGET (editor)); + } +} + +void +documents_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + imagefile = gimp_context_get_imagefile (context); + + if (imagefile) + gimp_clipboard_set_text (context->gimp, + gimp_object_get_name (imagefile)); +} + +void +documents_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + imagefile = gimp_context_get_imagefile (context); + + if (imagefile) + { + GFile *file = g_file_new_for_uri (gimp_object_get_name (imagefile)); + GError *error = NULL; + + if (! gimp_file_show_in_file_manager (file, &error)) + { + gimp_message (context->gimp, G_OBJECT (editor), + GIMP_MESSAGE_ERROR, + _("Can't show file in file manager: %s"), + error->message); + g_clear_error (&error); + } + + g_object_unref (file); + } +} + +void +documents_remove_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context = gimp_container_view_get_context (editor->view); + GimpImagefile *imagefile = gimp_context_get_imagefile (context); + const gchar *uri; + + uri = gimp_object_get_name (imagefile); + + gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, NULL); + + gimp_container_view_remove_active (editor->view); +} + +void +documents_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context = gimp_container_view_get_context (editor->view); + Gimp *gimp = context->gimp; + GtkWidget *dialog; + + dialog = gimp_message_dialog_new (_("Clear Document History"), + GIMP_ICON_SHRED, + GTK_WIDGET (editor), + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("Cl_ear"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (gtk_widget_get_toplevel (GTK_WIDGET (editor)), + "unmap", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Clear the Recent Documents list?")); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Clearing the document history will " + "permanently remove all images from " + "the recent documents list.")); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + GtkRecentManager *manager = gtk_recent_manager_get_default (); + GList *items; + GList *list; + + items = gtk_recent_manager_get_items (manager); + + for (list = items; list; list = list->next) + { + GtkRecentInfo *info = list->data; + + if (gtk_recent_info_has_application (info, + "GNU Image Manipulation Program")) + { + gtk_recent_manager_remove_item (manager, + gtk_recent_info_get_uri (info), + NULL); + } + + gtk_recent_info_unref (info); + } + + g_list_free (items); + + gimp_container_clear (gimp->documents); + } + + gtk_widget_destroy (dialog); +} + +void +documents_recreate_preview_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpContainer *container; + GimpImagefile *imagefile; + + context = gimp_container_view_get_context (editor->view); + container = gimp_container_view_get_container (editor->view); + + imagefile = gimp_context_get_imagefile (context); + + if (imagefile && gimp_container_have (container, GIMP_OBJECT (imagefile))) + { + GError *error = NULL; + + if (! gimp_imagefile_create_thumbnail (imagefile, + context, NULL, + context->gimp->config->thumbnail_size, + FALSE, &error)) + { + gimp_message_literal (context->gimp, + NULL , GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + } + } +} + +void +documents_reload_previews_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + + container = gimp_container_view_get_container (editor->view); + + gimp_container_foreach (container, + (GFunc) gimp_imagefile_update, + editor->view); +} + +static void +documents_remove_dangling_foreach (GimpImagefile *imagefile, + GimpContainer *container) +{ + GimpThumbnail *thumbnail = gimp_imagefile_get_thumbnail (imagefile); + + if (gimp_thumbnail_peek_image (thumbnail) == GIMP_THUMB_STATE_NOT_FOUND) + { + const gchar *uri = gimp_object_get_name (imagefile); + + gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, + NULL); + + gimp_container_remove (container, GIMP_OBJECT (imagefile)); + } +} + +void +documents_remove_dangling_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + + container = gimp_container_view_get_container (editor->view); + + gimp_container_foreach (container, + (GFunc) documents_remove_dangling_foreach, + container); +} + + +/* private functions */ + +static void +documents_open_image (GtkWidget *editor, + GimpContext *context, + GimpImagefile *imagefile) +{ + GFile *file; + GimpImage *image; + GimpPDBStatusType status; + GError *error = NULL; + + file = gimp_imagefile_get_file (imagefile); + + image = file_open_with_display (context->gimp, context, NULL, file, FALSE, + G_OBJECT (gtk_widget_get_screen (editor)), + gimp_widget_get_monitor (editor), + &status, &error); + + if (! image && status != GIMP_PDB_CANCEL) + { + gimp_message (context->gimp, G_OBJECT (editor), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } +} + +static void +documents_raise_display (GimpDisplay *display, + RaiseClosure *closure) +{ + const gchar *uri = gimp_object_get_name (gimp_display_get_image (display)); + + if (! g_strcmp0 (closure->name, uri)) + { + closure->found = TRUE; + gimp_display_shell_present (gimp_display_get_shell (display)); + } +} diff --git a/app/actions/documents-commands.h b/app/actions/documents-commands.h new file mode 100644 index 0000000..6db47c5 --- /dev/null +++ b/app/actions/documents-commands.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 . + */ + +#ifndef __DOCUMENTS_COMMANDS_H__ +#define __DOCUMENTS_COMMANDS_H__ + + +void documents_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_raise_or_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_file_open_dialog_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_remove_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_recreate_preview_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_reload_previews_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void documents_remove_dangling_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DOCUMENTS_COMMANDS_H__ */ diff --git a/app/actions/drawable-actions.c b/app/actions/drawable-actions.c new file mode 100644 index 0000000..a4b6233 --- /dev/null +++ b/app/actions/drawable-actions.c @@ -0,0 +1,231 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimplayermask.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "drawable-actions.h" +#include "drawable-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry drawable_actions[] = +{ + { "drawable-equalize", NULL, + NC_("drawable-action", "_Equalize"), NULL, + NC_("drawable-action", "Automatic contrast enhancement"), + drawable_equalize_cmd_callback, + GIMP_HELP_LAYER_EQUALIZE }, + + { "drawable-levels-stretch", NULL, + NC_("drawable-action", "_White Balance"), NULL, + NC_("drawable-action", "Automatic white balance correction"), + drawable_levels_stretch_cmd_callback, + GIMP_HELP_LAYER_WHITE_BALANCE } +}; + +static const GimpToggleActionEntry drawable_toggle_actions[] = +{ + { "drawable-visible", GIMP_ICON_VISIBLE, + NC_("drawable-action", "Toggle Drawable _Visibility"), NULL, NULL, + drawable_visible_cmd_callback, + FALSE, + GIMP_HELP_LAYER_VISIBLE }, + + { "drawable-linked", GIMP_ICON_LINKED, + NC_("drawable-action", "Toggle Drawable _Linked State"), NULL, NULL, + drawable_linked_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LINKED }, + + { "drawable-lock-content", NULL /* GIMP_ICON_LOCK */, + NC_("drawable-action", "L_ock Pixels of Drawable"), NULL, + NC_("drawable-action", + "Keep the pixels on this drawable from being modified"), + drawable_lock_content_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LOCK_PIXELS }, + + { "drawable-lock-position", GIMP_ICON_TOOL_MOVE, + NC_("drawable-action", "L_ock Position of Drawable"), NULL, + NC_("drawable-action", + "Keep the position on this drawable from being modified"), + drawable_lock_position_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LOCK_POSITION }, +}; + +static const GimpEnumActionEntry drawable_flip_actions[] = +{ + { "drawable-flip-horizontal", GIMP_ICON_OBJECT_FLIP_HORIZONTAL, + NC_("drawable-action", "Flip _Horizontally"), NULL, + NC_("drawable-action", "Flip drawable horizontally"), + GIMP_ORIENTATION_HORIZONTAL, FALSE, + GIMP_HELP_LAYER_FLIP_HORIZONTAL }, + + { "drawable-flip-vertical", GIMP_ICON_OBJECT_FLIP_VERTICAL, + NC_("drawable-action", "Flip _Vertically"), NULL, + NC_("drawable-action", "Flip drawable vertically"), + GIMP_ORIENTATION_VERTICAL, FALSE, + GIMP_HELP_LAYER_FLIP_VERTICAL } +}; + +static const GimpEnumActionEntry drawable_rotate_actions[] = +{ + { "drawable-rotate-90", GIMP_ICON_OBJECT_ROTATE_90, + NC_("drawable-action", "Rotate 90° _clockwise"), NULL, + NC_("drawable-action", "Rotate drawable 90 degrees to the right"), + GIMP_ROTATE_90, FALSE, + GIMP_HELP_LAYER_ROTATE_90 }, + + { "drawable-rotate-180", GIMP_ICON_OBJECT_ROTATE_180, + NC_("drawable-action", "Rotate _180°"), NULL, + NC_("drawable-action", "Turn drawable upside-down"), + GIMP_ROTATE_180, FALSE, + GIMP_HELP_LAYER_ROTATE_180 }, + + { "drawable-rotate-270", GIMP_ICON_OBJECT_ROTATE_270, + NC_("drawable-action", "Rotate 90° counter-clock_wise"), NULL, + NC_("drawable-action", "Rotate drawable 90 degrees to the left"), + GIMP_ROTATE_270, FALSE, + GIMP_HELP_LAYER_ROTATE_270 } +}; + + +void +drawable_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "drawable-action", + drawable_actions, + G_N_ELEMENTS (drawable_actions)); + + gimp_action_group_add_toggle_actions (group, "drawable-action", + drawable_toggle_actions, + G_N_ELEMENTS (drawable_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "drawable-action", + drawable_flip_actions, + G_N_ELEMENTS (drawable_flip_actions), + drawable_flip_cmd_callback); + + gimp_action_group_add_enum_actions (group, "drawable-action", + drawable_rotate_actions, + G_N_ELEMENTS (drawable_rotate_actions), + drawable_rotate_cmd_callback); + +#define SET_ALWAYS_SHOW_IMAGE(action,show) \ + gimp_action_group_set_action_always_show_image (group, action, show) + + SET_ALWAYS_SHOW_IMAGE ("drawable-rotate-90", TRUE); + SET_ALWAYS_SHOW_IMAGE ("drawable-rotate-180", TRUE); + SET_ALWAYS_SHOW_IMAGE ("drawable-rotate-270", TRUE); + +#undef SET_ALWAYS_SHOW_IMAGE +} + +void +drawable_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable = NULL; + gboolean is_rgb = FALSE; + gboolean visible = FALSE; + gboolean linked = FALSE; + gboolean locked = FALSE; + gboolean can_lock = FALSE; + gboolean locked_pos = FALSE; + gboolean can_lock_pos = FALSE; + gboolean writable = FALSE; + gboolean movable = FALSE; + gboolean children = FALSE; + + image = action_data_get_image (data); + + if (image) + { + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + GimpItem *item; + + is_rgb = gimp_drawable_is_rgb (drawable); + + if (GIMP_IS_LAYER_MASK (drawable)) + item = GIMP_ITEM (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + else + item = GIMP_ITEM (drawable); + + visible = gimp_item_get_visible (item); + linked = gimp_item_get_linked (item); + locked = gimp_item_get_lock_content (item); + can_lock = gimp_item_can_lock_content (item); + writable = ! gimp_item_is_content_locked (item); + locked_pos = gimp_item_get_lock_position (item); + can_lock_pos = gimp_item_can_lock_position (item); + movable = ! gimp_item_is_position_locked (item); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + children = TRUE; + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_SENSITIVE ("drawable-equalize", writable && !children); + SET_SENSITIVE ("drawable-levels-stretch", writable && !children && is_rgb); + + SET_SENSITIVE ("drawable-visible", drawable); + SET_SENSITIVE ("drawable-linked", drawable); + SET_SENSITIVE ("drawable-lock-content", can_lock); + SET_SENSITIVE ("drawable-lock-position", can_lock_pos); + + SET_ACTIVE ("drawable-visible", visible); + SET_ACTIVE ("drawable-linked", linked); + SET_ACTIVE ("drawable-lock-content", locked); + SET_ACTIVE ("drawable-lock-position", locked_pos); + + SET_SENSITIVE ("drawable-flip-horizontal", writable && movable); + SET_SENSITIVE ("drawable-flip-vertical", writable && movable); + + SET_SENSITIVE ("drawable-rotate-90", writable && movable); + SET_SENSITIVE ("drawable-rotate-180", writable && movable); + SET_SENSITIVE ("drawable-rotate-270", writable && movable); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +} diff --git a/app/actions/drawable-actions.h b/app/actions/drawable-actions.h new file mode 100644 index 0000000..1b6e38c --- /dev/null +++ b/app/actions/drawable-actions.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 . + */ + +#ifndef __DRAWABLE_ACTIONS_H__ +#define __DRAWABLE_ACTIONS_H__ + + +void drawable_actions_setup (GimpActionGroup *group); +void drawable_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DRAWABLE_ACTIONS_H__ */ diff --git a/app/actions/drawable-commands.c b/app/actions/drawable-commands.c new file mode 100644 index 0000000..7ea70e8 --- /dev/null +++ b/app/actions/drawable-commands.c @@ -0,0 +1,304 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpdrawable-equalize.h" +#include "core/gimpdrawable-levels.h" +#include "core/gimpdrawable-operation.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" +#include "core/gimpitem-linked.h" +#include "core/gimpitemundo.h" +#include "core/gimplayermask.h" +#include "core/gimpprogress.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "drawable-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +drawable_equalize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + return_if_no_drawable (image, drawable, data); + + gimp_drawable_equalize (drawable, TRUE); + gimp_image_flush (image); +} + +void +drawable_levels_stretch_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpDisplay *display; + GtkWidget *widget; + return_if_no_drawable (image, drawable, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + if (! gimp_drawable_is_rgb (drawable)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("White Balance operates only on RGB color " + "layers.")); + return; + } + + gimp_drawable_levels_stretch (drawable, GIMP_PROGRESS (display)); + gimp_image_flush (image); +} + +void +drawable_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + gboolean linked; + return_if_no_drawable (image, drawable, data); + + linked = g_variant_get_boolean (value); + + if (GIMP_IS_LAYER_MASK (drawable)) + drawable = + GIMP_DRAWABLE (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + + if (linked != gimp_item_get_linked (GIMP_ITEM (drawable))) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_LINKED); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (drawable)) + push_undo = FALSE; + + gimp_item_set_linked (GIMP_ITEM (drawable), linked, push_undo); + gimp_image_flush (image); + } +} + +void +drawable_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + gboolean visible; + return_if_no_drawable (image, drawable, data); + + visible = g_variant_get_boolean (value); + + if (GIMP_IS_LAYER_MASK (drawable)) + drawable = + GIMP_DRAWABLE (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + + if (visible != gimp_item_get_visible (GIMP_ITEM (drawable))) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_VISIBILITY); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (drawable)) + push_undo = FALSE; + + gimp_item_set_visible (GIMP_ITEM (drawable), visible, push_undo); + gimp_image_flush (image); + } +} + +void +drawable_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + gboolean locked; + return_if_no_drawable (image, drawable, data); + + locked = g_variant_get_boolean (value); + + if (GIMP_IS_LAYER_MASK (drawable)) + drawable = + GIMP_DRAWABLE (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + + if (locked != gimp_item_get_lock_content (GIMP_ITEM (drawable))) + { +#if 0 + GimpUndo *undo; +#endif + gboolean push_undo = TRUE; + +#if 0 + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_VISIBILITY); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (drawable)) + push_undo = FALSE; +#endif + + gimp_item_set_lock_content (GIMP_ITEM (drawable), locked, push_undo); + gimp_image_flush (image); + } +} + +void +drawable_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + gboolean locked; + return_if_no_drawable (image, drawable, data); + + locked = g_variant_get_boolean (value); + + if (GIMP_IS_LAYER_MASK (drawable)) + drawable = + GIMP_DRAWABLE (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + + if (locked != gimp_item_get_lock_position (GIMP_ITEM (drawable))) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_LOCK_POSITION); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (drawable)) + push_undo = FALSE; + + gimp_item_set_lock_position (GIMP_ITEM (drawable), locked, push_undo); + gimp_image_flush (image); + } +} + +void +drawable_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpItem *item; + GimpContext *context; + gint off_x, off_y; + gdouble axis = 0.0; + GimpOrientationType orientation; + return_if_no_drawable (image, drawable, data); + return_if_no_context (context, data); + + orientation = (GimpOrientationType) g_variant_get_int32 (value); + + item = GIMP_ITEM (drawable); + + gimp_item_get_offset (item, &off_x, &off_y); + + switch (orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + axis = ((gdouble) off_x + (gdouble) gimp_item_get_width (item) / 2.0); + break; + + case GIMP_ORIENTATION_VERTICAL: + axis = ((gdouble) off_y + (gdouble) gimp_item_get_height (item) / 2.0); + break; + + default: + break; + } + + if (gimp_item_get_linked (item)) + { + gimp_item_linked_flip (item, context, orientation, axis, FALSE); + } + else + { + gimp_item_flip (item, context, orientation, axis, + gimp_item_get_clip (item, FALSE)); + } + + gimp_image_flush (image); +} + +void +drawable_rotate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpContext *context; + GimpItem *item; + gint off_x, off_y; + gdouble center_x, center_y; + GimpRotationType rotation_type; + return_if_no_drawable (image, drawable, data); + return_if_no_context (context, data); + + rotation_type = (GimpRotationType) g_variant_get_int32 (value); + + item = GIMP_ITEM (drawable); + + gimp_item_get_offset (item, &off_x, &off_y); + + center_x = ((gdouble) off_x + (gdouble) gimp_item_get_width (item) / 2.0); + center_y = ((gdouble) off_y + (gdouble) gimp_item_get_height (item) / 2.0); + + if (gimp_item_get_linked (item)) + { + gimp_item_linked_rotate (item, context, rotation_type, + center_x, center_y, FALSE); + } + else + { + gimp_item_rotate (item, context, + rotation_type, center_x, center_y, + gimp_item_get_clip (item, FALSE)); + } + + gimp_image_flush (image); +} diff --git a/app/actions/drawable-commands.h b/app/actions/drawable-commands.h new file mode 100644 index 0000000..1f8ae79 --- /dev/null +++ b/app/actions/drawable-commands.h @@ -0,0 +1,50 @@ +/* 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 . + */ + +#ifndef __DRAWABLE_COMMANDS_H__ +#define __DRAWABLE_COMMANDS_H__ + + +void drawable_equalize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void drawable_levels_stretch_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void drawable_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void drawable_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void drawable_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void drawable_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void drawable_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void drawable_rotate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __DRAWABLE_COMMANDS_H__ */ diff --git a/app/actions/dynamics-actions.c b/app/actions/dynamics-actions.c new file mode 100644 index 0000000..ce6b005 --- /dev/null +++ b/app/actions/dynamics-actions.c @@ -0,0 +1,137 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdata.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "dynamics-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry dynamics_actions[] = +{ + { "dynamics-popup", GIMP_ICON_DYNAMICS, + NC_("dynamics-action", "Paint Dynamics Menu"), NULL, NULL, NULL, + GIMP_HELP_DYNAMICS_DIALOG }, + + { "dynamics-new", GIMP_ICON_DOCUMENT_NEW, + NC_("dynamics-action", "_New Dynamics"), NULL, + NC_("dynamics-action", "Create a new dynamics"), + data_new_cmd_callback, + GIMP_HELP_DYNAMICS_NEW }, + + { "dynamics-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("dynamics-action", "D_uplicate Dynamics"), NULL, + NC_("dynamics-action", "Duplicate this dynamics"), + data_duplicate_cmd_callback, + GIMP_HELP_DYNAMICS_DUPLICATE }, + + { "dynamics-copy-location", GIMP_ICON_EDIT_COPY, + NC_("dynamics-action", "Copy Dynamics _Location"), NULL, + NC_("dynamics-action", "Copy dynamics file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_DYNAMICS_COPY_LOCATION }, + + { "dynamics-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("dynamics-action", "Show in _File Manager"), NULL, + NC_("dynamics-action", "Show dynamics file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_DYNAMICS_SHOW_IN_FILE_MANAGER }, + + { "dynamics-delete", GIMP_ICON_EDIT_DELETE, + NC_("dynamics-action", "_Delete Dynamics"), NULL, + NC_("dynamics-action", "Delete this dynamics"), + data_delete_cmd_callback, + GIMP_HELP_DYNAMICS_DELETE }, + + { "dynamics-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("dynamics-action", "_Refresh Dynamics"), NULL, + NC_("dynamics-action", "Refresh dynamics"), + data_refresh_cmd_callback, + GIMP_HELP_DYNAMICS_REFRESH } +}; + +static const GimpStringActionEntry dynamics_edit_actions[] = +{ + { "dynamics-edit", GIMP_ICON_EDIT, + NC_("dynamics-action", "_Edit Dynamics..."), NULL, + NC_("dynamics-action", "Edit this dynamics"), + "gimp-dynamics-editor", + GIMP_HELP_DYNAMICS_EDIT } +}; + + +void +dynamics_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "dynamics-action", + dynamics_actions, + G_N_ELEMENTS (dynamics_actions)); + + gimp_action_group_add_string_actions (group, "dynamics-action", + dynamics_edit_actions, + G_N_ELEMENTS (dynamics_edit_actions), + data_edit_cmd_callback); +} + +void +dynamics_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpDynamics *dynamics = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + dynamics = gimp_context_get_dynamics (context); + + if (dynamics) + { + data = GIMP_DATA (dynamics); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("dynamics-edit", dynamics); + SET_SENSITIVE ("dynamics-duplicate", dynamics && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("dynamics-copy-location", file); + SET_SENSITIVE ("dynamics-show-in-file-manager", file); + SET_SENSITIVE ("dynamics-delete", dynamics && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/dynamics-actions.h b/app/actions/dynamics-actions.h new file mode 100644 index 0000000..515f6b3 --- /dev/null +++ b/app/actions/dynamics-actions.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 . + */ + +#ifndef __DYNAMICS_ACTIONS_H__ +#define __DYNAMICS_ACTIONS_H__ + + +void dynamics_actions_setup (GimpActionGroup *group); +void dynamics_actions_update (GimpActionGroup *group, + gpointer user_data); + + +#endif /* __DYNAMICS_ACTIONS_H__ */ diff --git a/app/actions/dynamics-editor-actions.c b/app/actions/dynamics-editor-actions.c new file mode 100644 index 0000000..0b38d3c --- /dev/null +++ b/app/actions/dynamics-editor-actions.c @@ -0,0 +1,89 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdataeditor.h" +#include "widgets/gimphelp-ids.h" + +#include "data-editor-commands.h" +#include "dynamics-editor-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry dynamics_editor_actions[] = +{ + { "dynamics-editor-popup", GIMP_ICON_DYNAMICS, + NC_("dynamics-editor-action", "Paint Dynamics Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_BRUSH_EDITOR_DIALOG } +}; + + +static const GimpToggleActionEntry dynamics_editor_toggle_actions[] = +{ + { "dynamics-editor-edit-active", GIMP_ICON_LINKED, + NC_("dynamics-editor-action", "Edit Active Dynamics"), NULL, NULL, + data_editor_edit_active_cmd_callback, + FALSE, + GIMP_HELP_BRUSH_EDITOR_EDIT_ACTIVE } +}; + + +void +dynamics_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "dynamics-editor-action", + dynamics_editor_actions, + G_N_ELEMENTS (dynamics_editor_actions)); + + gimp_action_group_add_toggle_actions (group, "dynamics-editor-action", + dynamics_editor_toggle_actions, + G_N_ELEMENTS (dynamics_editor_toggle_actions)); + +} + +void +dynamics_editor_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (user_data); + gboolean edit_active = FALSE; + + edit_active = gimp_data_editor_get_edit_active (data_editor); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_ACTIVE ("dynamics-editor-edit-active", edit_active); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +} diff --git a/app/actions/dynamics-editor-actions.h b/app/actions/dynamics-editor-actions.h new file mode 100644 index 0000000..2f79c6d --- /dev/null +++ b/app/actions/dynamics-editor-actions.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 . + */ + +#ifndef __DYNAMICS_EDITOR_ACTIONS_H__ +#define __DYNAMICS_EDITOR_ACTIONS_H__ + + +void dynamics_editor_actions_setup (GimpActionGroup *group); +void dynamics_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __DYNAMICS_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/edit-actions.c b/app/actions/edit-actions.c new file mode 100644 index 0000000..caf28b7 --- /dev/null +++ b/app/actions/edit-actions.c @@ -0,0 +1,417 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" +#include "core/gimplayer.h" +#include "core/gimplist.h" +#include "core/gimptoolinfo.h" +#include "core/gimpundostack.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "tools/tool_manager.h" + +#include "actions.h" +#include "edit-actions.h" +#include "edit-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void edit_actions_foreground_changed (GimpContext *context, + const GimpRGB *color, + GimpActionGroup *group); +static void edit_actions_background_changed (GimpContext *context, + const GimpRGB *color, + GimpActionGroup *group); +static void edit_actions_pattern_changed (GimpContext *context, + GimpPattern *pattern, + GimpActionGroup *group); + + +static const GimpActionEntry edit_actions[] = +{ + { "edit-menu", NULL, NC_("edit-action", "_Edit") }, + { "edit-paste-as-menu", NULL, NC_("edit-action", "Paste _as") }, + { "edit-buffer-menu", NULL, NC_("edit-action", "_Buffer") }, + + { "undo-popup", + "edit-undo", NC_("edit-action", "Undo History Menu"), NULL, NULL, NULL, + GIMP_HELP_UNDO_DIALOG }, + + { "edit-undo", GIMP_ICON_EDIT_UNDO, + NC_("edit-action", "_Undo"), "Z", + NC_("edit-action", "Undo the last operation"), + edit_undo_cmd_callback, + GIMP_HELP_EDIT_UNDO }, + + { "edit-redo", GIMP_ICON_EDIT_REDO, + NC_("edit-action", "_Redo"), "Y", + NC_("edit-action", "Redo the last operation that was undone"), + edit_redo_cmd_callback, + GIMP_HELP_EDIT_REDO }, + + { "edit-strong-undo", GIMP_ICON_EDIT_UNDO, + NC_("edit-action", "Strong Undo"), "Z", + NC_("edit-action", "Undo the last operation, skipping visibility changes"), + edit_strong_undo_cmd_callback, + GIMP_HELP_EDIT_STRONG_UNDO }, + + { "edit-strong-redo", GIMP_ICON_EDIT_REDO, + NC_("edit-action", "Strong Redo"), "Y", + NC_("edit-action", + "Redo the last operation that was undone, skipping visibility changes"), + edit_strong_redo_cmd_callback, + GIMP_HELP_EDIT_STRONG_REDO }, + + { "edit-undo-clear", GIMP_ICON_SHRED, + NC_("edit-action", "_Clear Undo History"), NULL, + NC_("edit-action", "Remove all operations from the undo history"), + edit_undo_clear_cmd_callback, + GIMP_HELP_EDIT_UNDO_CLEAR }, + + { "edit-cut", GIMP_ICON_EDIT_CUT, + NC_("edit-action", "Cu_t"), "X", + NC_("edit-action", "Move the selected pixels to the clipboard"), + edit_cut_cmd_callback, + GIMP_HELP_EDIT_CUT }, + + { "edit-copy", GIMP_ICON_EDIT_COPY, + NC_("edit-action", "_Copy"), "C", + NC_("edit-action", "Copy the selected pixels to the clipboard"), + edit_copy_cmd_callback, + GIMP_HELP_EDIT_COPY }, + + { "edit-copy-visible", NULL, /* GIMP_ICON_COPY_VISIBLE, */ + NC_("edit-action", "Copy _Visible"), "C", + NC_("edit-action", "Copy what is visible in the selected region"), + edit_copy_visible_cmd_callback, + GIMP_HELP_EDIT_COPY_VISIBLE }, + + { "edit-paste-as-new-image", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("edit-action", "From _Clipboard"), "V", + NC_("edit-action", "Create a new image from the content of the clipboard"), + edit_paste_as_new_image_cmd_callback, + GIMP_HELP_EDIT_PASTE_AS_NEW_IMAGE }, + + { "edit-paste-as-new-image-short", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("edit-action", "_New Image"), NULL, + NC_("edit-action", "Create a new image from the content of the clipboard"), + edit_paste_as_new_image_cmd_callback, + GIMP_HELP_EDIT_PASTE_AS_NEW_IMAGE }, + + { "edit-named-cut", GIMP_ICON_EDIT_CUT, + NC_("edit-action", "Cu_t Named..."), NULL, + NC_("edit-action", "Move the selected pixels to a named buffer"), + edit_named_cut_cmd_callback, + GIMP_HELP_BUFFER_CUT }, + + { "edit-named-copy", GIMP_ICON_EDIT_COPY, + NC_("edit-action", "_Copy Named..."), NULL, + NC_("edit-action", "Copy the selected pixels to a named buffer"), + edit_named_copy_cmd_callback, + GIMP_HELP_BUFFER_COPY }, + + { "edit-named-copy-visible", NULL, /* GIMP_ICON_COPY_VISIBLE, */ + NC_("edit-action", "Copy _Visible Named..."), "", + NC_("edit-action", + "Copy what is visible in the selected region to a named buffer"), + edit_named_copy_visible_cmd_callback, + GIMP_HELP_BUFFER_COPY }, + + { "edit-named-paste", GIMP_ICON_EDIT_PASTE, + NC_("edit-action", "_Paste Named..."), NULL, + NC_("edit-action", "Paste the content of a named buffer"), + edit_named_paste_cmd_callback, + GIMP_HELP_BUFFER_PASTE }, + + { "edit-clear", GIMP_ICON_EDIT_CLEAR, + NC_("edit-action", "Cl_ear"), "Delete", + NC_("edit-action", "Clear the selected pixels"), + edit_clear_cmd_callback, + GIMP_HELP_EDIT_CLEAR } +}; + +static const GimpEnumActionEntry edit_paste_actions[] = +{ + { "edit-paste", GIMP_ICON_EDIT_PASTE, + NC_("edit-action", "_Paste"), "V", + NC_("edit-action", "Paste the content of the clipboard"), + GIMP_PASTE_TYPE_FLOATING, FALSE, + GIMP_HELP_EDIT_PASTE }, + + { "edit-paste-in-place", GIMP_ICON_EDIT_PASTE, + NC_("edit-action", "Paste In P_lace"), "V", + NC_("edit-action", + "Paste the content of the clipboard at its original position"), + GIMP_PASTE_TYPE_FLOATING_IN_PLACE, FALSE, + GIMP_HELP_EDIT_PASTE_IN_PLACE }, + + { "edit-paste-into", GIMP_ICON_EDIT_PASTE_INTO, + NC_("edit-action", "Paste _Into Selection"), NULL, + NC_("edit-action", + "Paste the content of the clipboard into the current selection"), + GIMP_PASTE_TYPE_FLOATING_INTO, FALSE, + GIMP_HELP_EDIT_PASTE_INTO }, + + { "edit-paste-into-in-place", GIMP_ICON_EDIT_PASTE_INTO, + NC_("edit-action", "Paste Int_o Selection In Place"), NULL, + NC_("edit-action", + "Paste the content of the clipboard into the current selection " + "at its original position"), + GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE, FALSE, + GIMP_HELP_EDIT_PASTE_INTO_IN_PLACE }, + + { "edit-paste-as-new-layer", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("edit-action", "New _Layer"), NULL, + NC_("edit-action", "Create a new layer from the content of the clipboard"), + GIMP_PASTE_TYPE_NEW_LAYER, FALSE, + GIMP_HELP_EDIT_PASTE_AS_NEW_LAYER }, + + { "edit-paste-as-new-layer-in-place", GIMP_ICON_EDIT_PASTE_AS_NEW, + NC_("edit-action", "New Layer In _Place"), NULL, + NC_("edit-action", + "Create a new layer from the content of the clipboard " + "and place it at its original position"), + GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, FALSE, + GIMP_HELP_EDIT_PASTE_AS_NEW_LAYER_IN_PLACE } +}; + +static const GimpEnumActionEntry edit_fill_actions[] = +{ + { "edit-fill-fg", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("edit-action", "Fill with _FG Color"), "comma", + NC_("edit-action", "Fill the selection using the foreground color"), + GIMP_FILL_FOREGROUND, FALSE, + GIMP_HELP_EDIT_FILL_FG }, + + { "edit-fill-bg", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("edit-action", "Fill with B_G Color"), "period", + NC_("edit-action", "Fill the selection using the background color"), + GIMP_FILL_BACKGROUND, FALSE, + GIMP_HELP_EDIT_FILL_BG }, + + { "edit-fill-pattern", GIMP_ICON_PATTERN, + NC_("edit-action", "Fill _with Pattern"), "semicolon", + NC_("edit-action", "Fill the selection using the active pattern"), + GIMP_FILL_PATTERN, FALSE, + GIMP_HELP_EDIT_FILL_PATTERN } +}; + + +void +edit_actions_setup (GimpActionGroup *group) +{ + GimpContext *context = gimp_get_user_context (group->gimp); + GimpRGB color; + GimpPattern *pattern; + GimpAction *action; + + gimp_action_group_add_actions (group, "edit-action", + edit_actions, + G_N_ELEMENTS (edit_actions)); + + gimp_action_group_add_enum_actions (group, "edit-action", + edit_paste_actions, + G_N_ELEMENTS (edit_paste_actions), + edit_paste_cmd_callback); + + gimp_action_group_add_enum_actions (group, "edit-action", + edit_fill_actions, + G_N_ELEMENTS (edit_fill_actions), + edit_fill_cmd_callback); + + action = gimp_action_group_get_action (group, + "edit-paste-as-new-image-short"); + gimp_action_set_accel_path (action, + "/edit/edit-paste-as-new-image"); + + gimp_action_group_set_action_context (group, "edit-fill-fg", context); + gimp_action_group_set_action_context (group, "edit-fill-bg", context); + gimp_action_group_set_action_context (group, "edit-fill-pattern", context); + + g_signal_connect_object (context, "foreground-changed", + G_CALLBACK (edit_actions_foreground_changed), + group, 0); + g_signal_connect_object (context, "background-changed", + G_CALLBACK (edit_actions_background_changed), + group, 0); + g_signal_connect_object (context, "pattern-changed", + G_CALLBACK (edit_actions_pattern_changed), + group, 0); + + gimp_context_get_foreground (context, &color); + edit_actions_foreground_changed (context, &color, group); + + gimp_context_get_background (context, &color); + edit_actions_background_changed (context, &color, group); + + pattern = gimp_context_get_pattern (context); + edit_actions_pattern_changed (context, pattern, group); + +#define SET_ALWAYS_SHOW_IMAGE(action,show) \ + gimp_action_group_set_action_always_show_image (group, action, show) + + SET_ALWAYS_SHOW_IMAGE ("edit-fill-fg", TRUE); + SET_ALWAYS_SHOW_IMAGE ("edit-fill-bg", TRUE); + SET_ALWAYS_SHOW_IMAGE ("edit-fill-pattern", TRUE); + +#undef SET_ALWAYS_SHOW_IMAGE +} + +void +edit_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpDisplay *display = action_data_get_display (data); + GimpDrawable *drawable = NULL; + gchar *undo_name = NULL; + gchar *redo_name = NULL; + gboolean writable = FALSE; + gboolean children = FALSE; + gboolean undo_enabled = FALSE; + + if (image) + { + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + writable = ! gimp_item_is_content_locked (GIMP_ITEM (drawable)); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + children = TRUE; + } + + undo_enabled = gimp_image_undo_is_enabled (image); + + if (undo_enabled) + { + GimpUndoStack *undo_stack = gimp_image_get_undo_stack (image); + GimpUndoStack *redo_stack = gimp_image_get_redo_stack (image); + GimpUndo *undo = gimp_undo_stack_peek (undo_stack); + GimpUndo *redo = gimp_undo_stack_peek (redo_stack); + const gchar *tool_undo = NULL; + const gchar *tool_redo = NULL; + + if (display) + { + tool_undo = tool_manager_can_undo_active (image->gimp, display); + tool_redo = tool_manager_can_redo_active (image->gimp, display); + } + + if (tool_undo) + undo_name = g_strdup_printf (_("_Undo %s"), tool_undo); + else if (undo) + undo_name = g_strdup_printf (_("_Undo %s"), + gimp_object_get_name (undo)); + + if (tool_redo) + redo_name = g_strdup_printf (_("_Redo %s"), tool_redo); + else if (redo) + redo_name = g_strdup_printf (_("_Redo %s"), + gimp_object_get_name (redo)); + } + } + + +#define SET_LABEL(action,label) \ + gimp_action_group_set_action_label (group, action, (label)) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_LABEL ("edit-undo", undo_name ? undo_name : _("_Undo")); + SET_LABEL ("edit-redo", redo_name ? redo_name : _("_Redo")); + + SET_SENSITIVE ("edit-undo", undo_enabled && undo_name); + SET_SENSITIVE ("edit-redo", undo_enabled && redo_name); + SET_SENSITIVE ("edit-strong-undo", undo_enabled && undo_name); + SET_SENSITIVE ("edit-strong-redo", undo_enabled && redo_name); + SET_SENSITIVE ("edit-undo-clear", undo_enabled && (undo_name || redo_name)); + + g_free (undo_name); + g_free (redo_name); + + SET_SENSITIVE ("edit-cut", writable && !children); + SET_SENSITIVE ("edit-copy", drawable); + SET_SENSITIVE ("edit-copy-visible", image); + /* "edit-paste" is always active */ + SET_SENSITIVE ("edit-paste-in-place", image); + SET_SENSITIVE ("edit-paste-into", image); + SET_SENSITIVE ("edit-paste-into-in-place", image); + SET_SENSITIVE ("edit-paste-as-new-layer", image); + SET_SENSITIVE ("edit-paste-as-new-layer-in-place", image); + + SET_SENSITIVE ("edit-named-cut", writable && !children); + SET_SENSITIVE ("edit-named-copy", drawable); + SET_SENSITIVE ("edit-named-copy-visible", drawable); + /* "edit-named-paste" is always active */ + + SET_SENSITIVE ("edit-clear", writable && !children); + SET_SENSITIVE ("edit-fill-fg", writable && !children); + SET_SENSITIVE ("edit-fill-bg", writable && !children); + SET_SENSITIVE ("edit-fill-pattern", writable && !children); + +#undef SET_LABEL +#undef SET_SENSITIVE +} + + +/* private functions */ + +static void +edit_actions_foreground_changed (GimpContext *context, + const GimpRGB *color, + GimpActionGroup *group) +{ + gimp_action_group_set_action_color (group, "edit-fill-fg", color, FALSE); +} + +static void +edit_actions_background_changed (GimpContext *context, + const GimpRGB *color, + GimpActionGroup *group) +{ + gimp_action_group_set_action_color (group, "edit-fill-bg", color, FALSE); +} + +static void +edit_actions_pattern_changed (GimpContext *context, + GimpPattern *pattern, + GimpActionGroup *group) +{ + gimp_action_group_set_action_viewable (group, "edit-fill-pattern", + GIMP_VIEWABLE (pattern)); +} diff --git a/app/actions/edit-actions.h b/app/actions/edit-actions.h new file mode 100644 index 0000000..89978b3 --- /dev/null +++ b/app/actions/edit-actions.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 . + */ + +#ifndef __EDIT_ACTIONS_H__ +#define __EDIT_ACTIONS_H__ + + +void edit_actions_setup (GimpActionGroup *group); +void edit_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __EDIT_ACTIONS_H__ */ diff --git a/app/actions/edit-commands.c b/app/actions/edit-commands.c new file mode 100644 index 0000000..6042f86 --- /dev/null +++ b/app/actions/edit-commands.c @@ -0,0 +1,721 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimp-edit.h" +#include "core/gimpbuffer.h" +#include "core/gimpcontainer.h" +#include "core/gimpdrawable.h" +#include "core/gimpdrawable-edit.h" +#include "core/gimpfilloptions.h" +#include "core/gimplayer.h" +#include "core/gimplayer-new.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" + +#include "vectors/gimpvectors-import.h" + +#include "widgets/gimpclipboard.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-transform.h" + +#include "tools/gimptools-utils.h" +#include "tools/tool_manager.h" + +#include "actions.h" +#include "edit-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static gboolean check_drawable_alpha (GimpDrawable *drawable, + gpointer data); +static void edit_paste (GimpDisplay *display, + GimpPasteType paste_type, + gboolean try_svg); +static void cut_named_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data); +static void copy_named_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data); +static void copy_named_visible_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data); + + +/* public functions */ + +void +edit_undo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + return_if_no_image (image, data); + return_if_no_display (display, data); + + if (tool_manager_undo_active (image->gimp, display) || + gimp_image_undo (image)) + { + gimp_image_flush (image); + } +} + +void +edit_redo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + return_if_no_image (image, data); + return_if_no_display (display, data); + + if (tool_manager_redo_active (image->gimp, display) || + gimp_image_redo (image)) + { + gimp_image_flush (image); + } +} + +void +edit_strong_undo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + if (gimp_image_strong_undo (image)) + gimp_image_flush (image); +} + +void +edit_strong_redo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + if (gimp_image_strong_redo (image)) + gimp_image_flush (image); +} + +void +edit_undo_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpUndoStack *undo_stack; + GimpUndoStack *redo_stack; + GtkWidget *widget; + GtkWidget *dialog; + gchar *size; + gint64 memsize; + gint64 guisize; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + dialog = gimp_message_dialog_new (_("Clear Undo History"), + GIMP_ICON_DIALOG_WARNING, + widget, + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, + GIMP_HELP_EDIT_UNDO_CLEAR, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("Cl_ear"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (gtk_widget_get_toplevel (widget), "unmap", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect_object (image, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Really clear image's undo history?")); + + undo_stack = gimp_image_get_undo_stack (image); + redo_stack = gimp_image_get_redo_stack (image); + + memsize = gimp_object_get_memsize (GIMP_OBJECT (undo_stack), &guisize); + memsize += guisize; + memsize += gimp_object_get_memsize (GIMP_OBJECT (redo_stack), &guisize); + memsize += guisize; + + size = g_format_size (memsize); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Clearing the undo history of this " + "image will gain %s of memory."), size); + g_free (size); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + gimp_image_undo_disable (image); + gimp_image_undo_enable (image); + gimp_image_flush (image); + } + + gtk_widget_destroy (dialog); +} + +void +edit_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpObject *cut; + GError *error = NULL; + return_if_no_drawable (image, drawable, data); + + if (! check_drawable_alpha (drawable, data)) + return; + + cut = gimp_edit_cut (image, drawable, action_data_get_context (data), + &error); + + if (cut) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + gimp_message_literal (image->gimp, + G_OBJECT (display), GIMP_MESSAGE_INFO, + GIMP_IS_IMAGE (cut) ? + _("Cut layer to the clipboard.") : + _("Cut pixels to the clipboard.")); + + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, + G_OBJECT (action_data_get_display (data)), + GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +void +edit_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpObject *copy; + GError *error = NULL; + return_if_no_drawable (image, drawable, data); + + copy = gimp_edit_copy (image, drawable, action_data_get_context (data), + &error); + + if (copy) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + gimp_message_literal (image->gimp, + G_OBJECT (display), GIMP_MESSAGE_INFO, + GIMP_IS_IMAGE (copy) ? + _("Copied layer to the clipboard.") : + _("Copied pixels to the clipboard.")); + + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, + G_OBJECT (action_data_get_display (data)), + GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +void +edit_copy_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GError *error = NULL; + return_if_no_image (image, data); + + if (gimp_edit_copy_visible (image, action_data_get_context (data), &error)) + { + GimpDisplay *display = action_data_get_display (data); + + if (display) + gimp_message_literal (image->gimp, + G_OBJECT (display), GIMP_MESSAGE_INFO, + _("Copied pixels to the clipboard.")); + + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, + G_OBJECT (action_data_get_display (data)), + GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +void +edit_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display = action_data_get_display (data); + GimpPasteType paste_type = (GimpPasteType) g_variant_get_int32 (value); + + if (paste_type == GIMP_PASTE_TYPE_FLOATING) + { + if (! display || ! gimp_display_get_image (display)) + { + edit_paste_as_new_image_cmd_callback (action, value, data); + return; + } + } + + if (! display) + return; + + switch (paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + edit_paste (display, paste_type, TRUE); + break; + + case GIMP_PASTE_TYPE_NEW_LAYER: + case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: + edit_paste (display, paste_type, FALSE); + break; + } +} + +void +edit_paste_as_new_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + GimpObject *paste; + GimpImage *image = NULL; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + paste = gimp_clipboard_get_object (gimp); + + if (paste) + { + image = gimp_edit_paste_as_new_image (gimp, paste); + g_object_unref (paste); + } + + if (image) + { + gimp_create_display (gimp, image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (image); + } + else + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_WARNING, + _("There is no image data in the clipboard " + "to paste.")); + } +} + +void +edit_named_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + dialog = gimp_query_string_box (_("Cut Named"), widget, + gimp_standard_help_func, + GIMP_HELP_BUFFER_CUT, + _("Enter a name for this buffer"), + NULL, + G_OBJECT (image), "disconnect", + cut_named_buffer_callback, image); + gtk_widget_show (dialog); +} + +void +edit_named_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + dialog = gimp_query_string_box (_("Copy Named"), widget, + gimp_standard_help_func, + GIMP_HELP_BUFFER_COPY, + _("Enter a name for this buffer"), + NULL, + G_OBJECT (image), "disconnect", + copy_named_buffer_callback, image); + gtk_widget_show (dialog); +} + +void +edit_named_copy_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + dialog = gimp_query_string_box (_("Copy Visible Named"), widget, + gimp_standard_help_func, + GIMP_HELP_BUFFER_COPY, + _("Enter a name for this buffer"), + NULL, + G_OBJECT (image), "disconnect", + copy_named_visible_buffer_callback, image); + gtk_widget_show (dialog); +} + +void +edit_named_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)), + gimp, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + "gimp-buffer-list|gimp-buffer-grid"); +} + +void +edit_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + return_if_no_drawable (image, drawable, data); + + if (! check_drawable_alpha (drawable, data)) + return; + + gimp_drawable_edit_clear (drawable, action_data_get_context (data)); + gimp_image_flush (image); +} + +void +edit_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpFillType fill_type; + GimpFillOptions *options; + GError *error = NULL; + return_if_no_drawable (image, drawable, data); + + fill_type = (GimpFillType) g_variant_get_int32 (value); + + options = gimp_fill_options_new (action_data_get_gimp (data), NULL, FALSE); + + if (gimp_fill_options_set_by_fill_type (options, + action_data_get_context (data), + fill_type, &error)) + { + gimp_drawable_edit_fill (drawable, options, NULL); + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } + + g_object_unref (options); +} + + +/* private functions */ + +static gboolean +check_drawable_alpha (GimpDrawable *drawable, + gpointer data) +{ + if (gimp_drawable_has_alpha (drawable) && + GIMP_IS_LAYER (drawable) && + gimp_layer_get_lock_alpha (GIMP_LAYER (drawable))) + { + Gimp *gimp = action_data_get_gimp (data); + GimpDisplay *display = action_data_get_display (data); + + if (gimp && display) + { + gimp_message_literal ( + gimp, G_OBJECT (display), GIMP_MESSAGE_WARNING, + _("The active layer's alpha channel is locked.")); + + gimp_tools_blink_lock_box (gimp, GIMP_ITEM (drawable)); + } + + return FALSE; + } + + return TRUE; +} + +static void +edit_paste (GimpDisplay *display, + GimpPasteType paste_type, + gboolean try_svg) +{ + GimpImage *image = gimp_display_get_image (display); + GimpObject *paste; + + if (try_svg) + { + gchar *svg; + gsize svg_size; + + svg = gimp_clipboard_get_svg (display->gimp, &svg_size); + + if (svg) + { + if (gimp_vectors_import_buffer (image, svg, svg_size, + TRUE, FALSE, + GIMP_IMAGE_ACTIVE_PARENT, -1, + NULL, NULL)) + { + gimp_image_flush (image); + } + + g_free (svg); + + return; + } + } + + paste = gimp_clipboard_get_object (display->gimp); + + if (paste) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + gint x, y; + gint width, height; + + if (drawable && + paste_type != GIMP_PASTE_TYPE_NEW_LAYER && + paste_type != GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE) + { + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + { + gimp_message_literal (display->gimp, G_OBJECT (display), + GIMP_MESSAGE_INFO, + _("Pasted as new layer because the " + "target is a layer group.")); + } + else if (gimp_item_is_content_locked (GIMP_ITEM (drawable))) + { + gimp_message_literal (display->gimp, G_OBJECT (display), + GIMP_MESSAGE_INFO, + _("Pasted as new layer because the " + "target's pixels are locked.")); + } + + /* the actual paste-type conversion happens in gimp_edit_paste() */ + } + + gimp_display_shell_untransform_viewport ( + shell, + ! gimp_display_shell_get_infinite_canvas (shell), + &x, &y, &width, &height); + + if (gimp_edit_paste (image, drawable, paste, + paste_type, x, y, width, height)) + { + gimp_image_flush (image); + } + + g_object_unref (paste); + } + else + { + gimp_message_literal (display->gimp, G_OBJECT (display), + GIMP_MESSAGE_WARNING, + _("There is no image data in the clipboard " + "to paste.")); + } +} + +static void +cut_named_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + GError *error = NULL; + + if (! drawable) + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to cut from.")); + return; + } + + if (! (name && strlen (name))) + name = _("(Unnamed Buffer)"); + + if (gimp_edit_named_cut (image, name, drawable, + gimp_get_user_context (image->gimp), &error)) + { + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +static void +copy_named_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + GError *error = NULL; + + if (! drawable) + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to copy from.")); + return; + } + + if (! (name && strlen (name))) + name = _("(Unnamed Buffer)"); + + if (gimp_edit_named_copy (image, name, drawable, + gimp_get_user_context (image->gimp), &error)) + { + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +static void +copy_named_visible_buffer_callback (GtkWidget *widget, + const gchar *name, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GError *error = NULL; + + if (! (name && strlen (name))) + name = _("(Unnamed Buffer)"); + + if (gimp_edit_named_copy_visible (image, name, + gimp_get_user_context (image->gimp), + &error)) + { + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} diff --git a/app/actions/edit-commands.h b/app/actions/edit-commands.h new file mode 100644 index 0000000..2185854 --- /dev/null +++ b/app/actions/edit-commands.h @@ -0,0 +1,76 @@ +/* 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 . + */ + +#ifndef __EDIT_COMMANDS_H__ +#define __EDIT_COMMANDS_H__ + + +void edit_undo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_redo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_strong_undo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_strong_redo_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_undo_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void edit_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_copy_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void edit_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_paste_as_new_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void edit_named_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_named_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_named_copy_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_named_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void edit_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void edit_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __EDIT_COMMANDS_H__ */ diff --git a/app/actions/error-console-actions.c b/app/actions/error-console-actions.c new file mode 100644 index 0000000..e82b55a --- /dev/null +++ b/app/actions/error-console-actions.c @@ -0,0 +1,152 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimperrorconsole.h" +#include "widgets/gimphelp-ids.h" + +#include "error-console-actions.h" +#include "error-console-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry error_console_actions[] = +{ + { "error-console-popup", GIMP_ICON_DIALOG_WARNING, + NC_("error-console-action", "Error Console Menu"), NULL, NULL, NULL, + GIMP_HELP_ERRORS_DIALOG }, + + { "error-console-clear", GIMP_ICON_EDIT_CLEAR, + NC_("error-console-action", "_Clear"), NULL, + NC_("error-console-action", "Clear error console"), + error_console_clear_cmd_callback, + GIMP_HELP_ERRORS_CLEAR }, + + { "error-console-select-all", NULL, + NC_("error-console-action", "Select _All"), "", + NC_("error-console-action", "Select all error messages"), + error_console_select_all_cmd_callback, + GIMP_HELP_ERRORS_SELECT_ALL }, + + { "error-console-highlight", NULL, + NC_("error-console-action", "_Highlight"), NULL, NULL, NULL, + GIMP_HELP_ERRORS_HIGHLIGHT } +}; + +static const GimpEnumActionEntry error_console_save_actions[] = +{ + { "error-console-save-all", GIMP_ICON_DOCUMENT_SAVE_AS, + NC_("error-console-action", "_Save Error Log to File..."), NULL, + NC_("error-console-action", "Write all error messages to a file"), + FALSE, FALSE, + GIMP_HELP_ERRORS_SAVE }, + + { "error-console-save-selection", GIMP_ICON_DOCUMENT_SAVE_AS, + NC_("error-console-action", "Save S_election to File..."), NULL, + NC_("error-console-action", "Write the selected error messages to a file"), + TRUE, FALSE, + GIMP_HELP_ERRORS_SAVE } +}; + +static const GimpToggleActionEntry error_console_highlight_actions[] = +{ + { "error-console-highlight-error", NULL, + NC_("error-console-action", "_Errors"), NULL, + NC_("error-console-action", "Highlight error console on errors"), + error_console_highlight_error_cmd_callback, + FALSE, + GIMP_HELP_ERRORS_HIGHLIGHT }, + + { "error-console-highlight-warning", NULL, + NC_("error-console-action", "_Warnings"), NULL, + NC_("error-console-action", "Highlight error console on warnings"), + error_console_highlight_warning_cmd_callback, + FALSE, + GIMP_HELP_ERRORS_HIGHLIGHT }, + + { "error-console-highlight-info", NULL, + NC_("error-console-action", "_Messages"), NULL, + NC_("error-console-action", "Highlight error console on messages"), + error_console_highlight_info_cmd_callback, + FALSE, + GIMP_HELP_ERRORS_HIGHLIGHT } +}; + + +void +error_console_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "error-console-action", + error_console_actions, + G_N_ELEMENTS (error_console_actions)); + + gimp_action_group_add_enum_actions (group, "error-console-action", + error_console_save_actions, + G_N_ELEMENTS (error_console_save_actions), + error_console_save_cmd_callback); + + gimp_action_group_add_toggle_actions (group, "error-console-action", + error_console_highlight_actions, + G_N_ELEMENTS (error_console_highlight_actions)); +} + +void +error_console_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + gboolean selection; + + selection = gtk_text_buffer_get_selection_bounds (console->text_buffer, + NULL, NULL); + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("error-console-clear", TRUE); + SET_SENSITIVE ("error-console-select-all", TRUE); + SET_SENSITIVE ("error-console-save-all", TRUE); + SET_SENSITIVE ("error-console-save-selection", selection); + SET_SENSITIVE ("error-console-highlight", TRUE); + + SET_SENSITIVE ("error-console-highlight-error", TRUE); + SET_ACTIVE ("error-console-highlight-error", + console->highlight[GIMP_MESSAGE_ERROR]); + + SET_SENSITIVE ("error-console-highlight-warning", TRUE); + SET_ACTIVE ("error-console-highlight-warning", + console->highlight[GIMP_MESSAGE_WARNING]); + + SET_SENSITIVE ("error-console-highlight-info", TRUE); + SET_ACTIVE ("error-console-highlight-info", + console->highlight[GIMP_MESSAGE_INFO]); + +#undef SET_ACTIVE +#undef SET_SENSITIVE +} diff --git a/app/actions/error-console-actions.h b/app/actions/error-console-actions.h new file mode 100644 index 0000000..c7a2dde --- /dev/null +++ b/app/actions/error-console-actions.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 . + */ + +#ifndef __ERROR_CONSOLE_ACIONS_H__ +#define __ERROR_CONSOLE_ACIONS_H__ + + +void error_console_actions_setup (GimpActionGroup *group); +void error_console_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __ERROR_CONSOLE_ACTIONS_H__ */ diff --git a/app/actions/error-console-commands.c b/app/actions/error-console-commands.c new file mode 100644 index 0000000..4d70981 --- /dev/null +++ b/app/actions/error-console-commands.c @@ -0,0 +1,201 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimperrorconsole.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimptextbuffer.h" + +#include "error-console-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void error_console_save_response (GtkWidget *dialog, + gint response_id, + GimpErrorConsole *console); + + +/* public functions */ + +void +error_console_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + GtkTextIter start_iter; + GtkTextIter end_iter; + + gtk_text_buffer_get_bounds (console->text_buffer, &start_iter, &end_iter); + gtk_text_buffer_delete (console->text_buffer, &start_iter, &end_iter); +} + +void +error_console_select_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + GtkTextIter start_iter; + GtkTextIter end_iter; + + gtk_text_buffer_get_bounds (console->text_buffer, &start_iter, &end_iter); + gtk_text_buffer_select_range (console->text_buffer, &start_iter, &end_iter); +} + +void +error_console_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + gboolean selection = (gboolean) g_variant_get_int32 (value); + + if (selection && + ! gtk_text_buffer_get_selection_bounds (console->text_buffer, + NULL, NULL)) + { + gimp_message_literal (console->gimp, + G_OBJECT (console), GIMP_MESSAGE_WARNING, + _("Cannot save. Nothing is selected.")); + return; + } + + if (! console->file_dialog) + { + GtkWidget *dialog; + + dialog = console->file_dialog = + gtk_file_chooser_dialog_new (_("Save Error Log to File"), 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); + + console->save_selection = selection; + + g_object_add_weak_pointer (G_OBJECT (dialog), + (gpointer) &console->file_dialog); + + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (GTK_WIDGET (console))); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_role (GTK_WINDOW (dialog), "gimp-save-errors"); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), + TRUE); + + g_signal_connect (dialog, "response", + G_CALLBACK (error_console_save_response), + console); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + gimp_help_connect (dialog, gimp_standard_help_func, + GIMP_HELP_ERRORS_DIALOG, NULL); + } + + gtk_window_present (GTK_WINDOW (console->file_dialog)); +} + +void +error_console_highlight_error_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + gboolean active = g_variant_get_boolean (value); + + console->highlight[GIMP_MESSAGE_ERROR] = active; +} + +void +error_console_highlight_warning_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + gboolean active = g_variant_get_boolean (value); + + console->highlight[GIMP_MESSAGE_WARNING] = active; +} + +void +error_console_highlight_info_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpErrorConsole *console = GIMP_ERROR_CONSOLE (data); + gboolean active = g_variant_get_boolean (value); + + console->highlight[GIMP_MESSAGE_INFO] = active; +} + + +/* private functions */ + +static void +error_console_save_response (GtkWidget *dialog, + gint response_id, + GimpErrorConsole *console) +{ + if (response_id == GTK_RESPONSE_OK) + { + GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + GError *error = NULL; + + if (! gimp_text_buffer_save (GIMP_TEXT_BUFFER (console->text_buffer), + file, + console->save_selection, &error)) + { + gimp_message (console->gimp, G_OBJECT (dialog), GIMP_MESSAGE_ERROR, + _("Error writing file '%s':\n%s"), + gimp_file_get_utf8_name (file), + error->message); + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_object_unref (file); + } + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/error-console-commands.h b/app/actions/error-console-commands.h new file mode 100644 index 0000000..6913a9b --- /dev/null +++ b/app/actions/error-console-commands.h @@ -0,0 +1,43 @@ +/* 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 . + */ + +#ifndef __ERROR_CONSOLE_COMMANDS_H__ +#define __ERROR_CONSOLE_COMMANDS_H__ + + +void error_console_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void error_console_select_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void error_console_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void error_console_highlight_error_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void error_console_highlight_warning_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void error_console_highlight_info_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __ERROR_CONSOLE_COMMANDS_H__ */ diff --git a/app/actions/file-actions.c b/app/actions/file-actions.c new file mode 100644 index 0000000..b767750 --- /dev/null +++ b/app/actions/file-actions.c @@ -0,0 +1,452 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" +#include "core/gimpviewable.h" + +#include "file/gimp-file.h" + +#include "plug-in/gimppluginmanager-file.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpactionimpl.h" +#include "widgets/gimphelp-ids.h" + +#include "display/gimpdisplay.h" + +#include "actions.h" +#include "file-actions.h" +#include "file-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void file_actions_last_opened_update (GimpContainer *container, + GimpImagefile *unused, + GimpActionGroup *group); +static void file_actions_last_opened_reorder (GimpContainer *container, + GimpImagefile *unused1, + gint unused2, + GimpActionGroup *group); +static void file_actions_close_all_update (GimpContainer *images, + GimpObject *unused, + GimpActionGroup *group); +static gchar * file_actions_create_label (const gchar *format, + GFile *file); + + +static const GimpActionEntry file_actions[] = +{ + { "file-menu", NULL, NC_("file-action", "_File") }, + { "file-create-menu", NULL, NC_("file-action", "Crea_te") }, + { "file-open-recent-menu", NULL, NC_("file-action", "Open _Recent") }, + + { "file-open", GIMP_ICON_IMAGE_OPEN, + NC_("file-action", "_Open..."), "O", + NC_("file-action", "Open an image file"), + file_open_cmd_callback, + GIMP_HELP_FILE_OPEN }, + + { "file-open-as-layers", GIMP_ICON_LAYER, + NC_("file-action", "Op_en as Layers..."), "O", + NC_("file-action", "Open an image file as layers"), + file_open_as_layers_cmd_callback, + GIMP_HELP_FILE_OPEN_AS_LAYER }, + + { "file-open-location", GIMP_ICON_WEB, + NC_("file-action", "Open _Location..."), NULL, + NC_("file-action", "Open an image file from a specified location"), + file_open_location_cmd_callback, + GIMP_HELP_FILE_OPEN_LOCATION }, + + { "file-create-template", NULL, + NC_("file-action", "Create _Template..."), NULL, + NC_("file-action", "Create a new template from this image"), + file_create_template_cmd_callback, + GIMP_HELP_FILE_CREATE_TEMPLATE }, + + { "file-revert", GIMP_ICON_IMAGE_RELOAD, + NC_("file-action", "Re_vert"), NULL, + NC_("file-action", "Reload the image file from disk"), + file_revert_cmd_callback, + GIMP_HELP_FILE_REVERT }, + + { "file-close-all", GIMP_ICON_CLOSE_ALL, + NC_("file-action", "C_lose All"), "W", + NC_("file-action", "Close all opened images"), + file_close_all_cmd_callback, + GIMP_HELP_FILE_CLOSE_ALL }, + + { "file-copy-location", GIMP_ICON_EDIT_COPY, + NC_("file-action", "Copy _Image Location"), NULL, + NC_("file-action", "Copy image file location to clipboard"), + file_copy_location_cmd_callback, + GIMP_HELP_FILE_COPY_LOCATION }, + + { "file-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("file-action", "Show in _File Manager"), "F", + NC_("file-action", "Show image file location in the file manager"), + file_show_in_file_manager_cmd_callback, + GIMP_HELP_FILE_SHOW_IN_FILE_MANAGER }, + + { "file-quit", GIMP_ICON_APPLICATION_EXIT, + NC_("file-action", "_Quit"), "Q", + NC_("file-action", "Quit the GNU Image Manipulation Program"), + file_quit_cmd_callback, + GIMP_HELP_FILE_QUIT } +}; + +static const GimpEnumActionEntry file_save_actions[] = +{ + { "file-save", GIMP_ICON_DOCUMENT_SAVE, + NC_("file-action", "_Save"), "S", + NC_("file-action", "Save this image"), + GIMP_SAVE_MODE_SAVE, FALSE, + GIMP_HELP_FILE_SAVE }, + + { "file-save-as", GIMP_ICON_DOCUMENT_SAVE_AS, + NC_("file-action", "Save _As..."), "S", + NC_("file-action", "Save this image with a different name"), + GIMP_SAVE_MODE_SAVE_AS, FALSE, + GIMP_HELP_FILE_SAVE_AS }, + + { "file-save-a-copy", NULL, + NC_("file-action", "Save a Cop_y..."), NULL, + NC_("file-action", + "Save a copy of this image, without affecting the source file " + "(if any) or the current state of the image"), + GIMP_SAVE_MODE_SAVE_A_COPY, FALSE, + GIMP_HELP_FILE_SAVE_A_COPY }, + + { "file-save-and-close", NULL, + NC_("file-action", "Save and Close..."), NULL, + NC_("file-action", "Save this image and close its window"), + GIMP_SAVE_MODE_SAVE_AND_CLOSE, FALSE, + GIMP_HELP_FILE_SAVE }, + + { "file-export", NULL, + NC_("file-action", "E_xport..."), "E", + NC_("file-action", "Export the image"), + GIMP_SAVE_MODE_EXPORT, FALSE, + GIMP_HELP_FILE_EXPORT }, + + { "file-overwrite", NULL, + NC_("file-action", "Over_write"), "", + NC_("file-action", "Export the image back to the imported file in the import format"), + GIMP_SAVE_MODE_OVERWRITE, FALSE, + GIMP_HELP_FILE_OVERWRITE }, + + { "file-export-as", NULL, + NC_("file-action", "E_xport As..."), "E", + NC_("file-action", "Export the image to various file formats such as PNG or JPEG"), + GIMP_SAVE_MODE_EXPORT_AS, FALSE, + GIMP_HELP_FILE_EXPORT_AS } +}; + +void +file_actions_setup (GimpActionGroup *group) +{ + GimpEnumActionEntry *entries; + gint n_entries; + gint i; + + gimp_action_group_add_actions (group, "file-action", + file_actions, + G_N_ELEMENTS (file_actions)); + + gimp_action_group_add_enum_actions (group, "file-action", + file_save_actions, + G_N_ELEMENTS (file_save_actions), + file_save_cmd_callback); + + n_entries = GIMP_GUI_CONFIG (group->gimp->config)->last_opened_size; + + entries = g_new0 (GimpEnumActionEntry, n_entries); + + for (i = 0; i < n_entries; i++) + { + entries[i].name = g_strdup_printf ("file-open-recent-%02d", + i + 1); + entries[i].icon_name = GIMP_ICON_DOCUMENT_OPEN, + entries[i].label = entries[i].name; + entries[i].tooltip = NULL; + entries[i].value = i; + entries[i].value_variable = FALSE; + + if (i < 9) + entries[i].accelerator = g_strdup_printf ("%d", i + 1); + else if (i == 9) + entries[i].accelerator = g_strdup ("0"); + else + entries[i].accelerator = NULL; + } + + gimp_action_group_add_enum_actions (group, NULL, entries, n_entries, + file_open_recent_cmd_callback); + + for (i = 0; i < n_entries; i++) + { + gimp_action_group_set_action_visible (group, entries[i].name, FALSE); + gimp_action_group_set_action_always_show_image (group, entries[i].name, + TRUE); + gimp_action_group_set_action_context (group, entries[i].name, + gimp_get_user_context (group->gimp)); + + g_free ((gchar *) entries[i].name); + if (entries[i].accelerator) + g_free ((gchar *) entries[i].accelerator); + } + + g_free (entries); + + g_signal_connect_object (group->gimp->documents, "add", + G_CALLBACK (file_actions_last_opened_update), + group, 0); + g_signal_connect_object (group->gimp->documents, "remove", + G_CALLBACK (file_actions_last_opened_update), + group, 0); + g_signal_connect_object (group->gimp->documents, "reorder", + G_CALLBACK (file_actions_last_opened_reorder), + group, 0); + + file_actions_last_opened_update (group->gimp->documents, NULL, group); + + /* also listen to image adding/removal so we catch the case where + * the last image is closed but its display stays open. + */ + g_signal_connect_object (group->gimp->images, "add", + G_CALLBACK (file_actions_close_all_update), + group, 0); + g_signal_connect_object (group->gimp->images, "remove", + G_CALLBACK (file_actions_close_all_update), + group, 0); + + file_actions_close_all_update (group->gimp->displays, NULL, group); +} + +void +file_actions_update (GimpActionGroup *group, + gpointer data) +{ + Gimp *gimp = action_data_get_gimp (data); + GimpImage *image = action_data_get_image (data); + GimpDrawable *drawable = NULL; + GFile *file = NULL; + GFile *source = NULL; + GFile *export = NULL; + gboolean show_overwrite = FALSE; + + if (image) + { + drawable = gimp_image_get_active_drawable (image); + + file = gimp_image_get_file (image); + source = gimp_image_get_imported_file (image); + export = gimp_image_get_exported_file (image); + } + + show_overwrite = + (source && + gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_EXPORT, + source, NULL)); + +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("file-save", drawable); + SET_SENSITIVE ("file-save-as", drawable); + SET_SENSITIVE ("file-save-a-copy", drawable); + SET_SENSITIVE ("file-save-and-close", drawable); + SET_SENSITIVE ("file-revert", file || source); + SET_SENSITIVE ("file-export", drawable); + SET_VISIBLE ("file-export", ! show_overwrite); + SET_SENSITIVE ("file-overwrite", show_overwrite); + SET_VISIBLE ("file-overwrite", show_overwrite); + SET_SENSITIVE ("file-export-as", drawable); + SET_SENSITIVE ("file-create-template", image); + SET_SENSITIVE ("file-copy-location", file || source || export); + SET_SENSITIVE ("file-show-in-file-manager", file || source || export); + + if (file) + { + gimp_action_group_set_action_label (group, + "file-save", + C_("file-action", "_Save")); + } + else + { + gimp_action_group_set_action_label (group, + "file-save", + C_("file-action", "_Save...")); + } + + if (export) + { + gchar *label = file_actions_create_label (_("Export to %s"), export); + gimp_action_group_set_action_label (group, "file-export", label); + g_free (label); + } + else if (show_overwrite) + { + gchar *label = file_actions_create_label (_("Over_write %s"), source); + gimp_action_group_set_action_label (group, "file-overwrite", label); + g_free (label); + } + else + { + gimp_action_group_set_action_label (group, + "file-export", + C_("file-action", "E_xport...")); + } + + /* needed for the empty display */ + SET_SENSITIVE ("file-close-all", image); + +#undef SET_SENSITIVE +} + + +/* private functions */ + +static void +file_actions_last_opened_update (GimpContainer *container, + GimpImagefile *unused, + GimpActionGroup *group) +{ + gint num_documents; + gint i; + gint n = GIMP_GUI_CONFIG (group->gimp->config)->last_opened_size; + + num_documents = gimp_container_get_n_children (container); + + for (i = 0; i < n; i++) + { + GimpAction *action; + gchar *name = g_strdup_printf ("file-open-recent-%02d", i + 1); + + action = gimp_action_group_get_action (group, name); + + if (i < num_documents) + { + GimpImagefile *imagefile = (GimpImagefile *) + gimp_container_get_child_by_index (container, i); + + if (GIMP_ACTION_IMPL (action)->viewable != (GimpViewable *) imagefile) + { + GFile *file; + const gchar *name; + gchar *basename; + gchar *escaped; + + file = gimp_imagefile_get_file (imagefile); + + name = gimp_file_get_utf8_name (file); + basename = g_path_get_basename (name); + + escaped = gimp_escape_uline (basename); + + g_free (basename); + + g_object_set (action, + "label", escaped, + "tooltip", name, + "visible", TRUE, + "viewable", imagefile, + NULL); + + g_free (escaped); + } + } + else + { + g_object_set (action, + "label", name, + "tooltip", NULL, + "visible", FALSE, + "viewable", NULL, + NULL); + } + + g_free (name); + } +} + +static void +file_actions_last_opened_reorder (GimpContainer *container, + GimpImagefile *unused1, + gint unused2, + GimpActionGroup *group) +{ + file_actions_last_opened_update (container, unused1, group); +} + +static void +file_actions_close_all_update (GimpContainer *images, + GimpObject *unused, + GimpActionGroup *group) +{ + GimpContainer *container = group->gimp->displays; + gint n_displays = gimp_container_get_n_children (container); + gboolean sensitive = (n_displays > 0); + + if (n_displays == 1) + { + GimpDisplay *display; + + display = GIMP_DISPLAY (gimp_container_get_first_child (container)); + + if (! gimp_display_get_image (display)) + sensitive = FALSE; + } + + gimp_action_group_set_action_sensitive (group, "file-close-all", sensitive); +} + +static gchar * +file_actions_create_label (const gchar *format, + GFile *file) +{ + gchar *basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + gchar *escaped_basename = gimp_escape_uline (basename); + gchar *label = g_strdup_printf (format, escaped_basename); + + g_free (escaped_basename); + g_free (basename); + + return label; +} diff --git a/app/actions/file-actions.h b/app/actions/file-actions.h new file mode 100644 index 0000000..7029a9a --- /dev/null +++ b/app/actions/file-actions.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 . + */ + +#ifndef __FILE_ACTIONS_H__ +#define __FILE_ACTIONS_H__ + + +void file_actions_setup (GimpActionGroup *group); +void file_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __FILE_ACTIONS_H__ */ diff --git a/app/actions/file-commands.c b/app/actions/file-commands.c new file mode 100644 index 0000000..9e72f3e --- /dev/null +++ b/app/actions/file-commands.c @@ -0,0 +1,837 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" +#include "core/gimpprogress.h" +#include "core/gimptemplate.h" + +#include "plug-in/gimppluginmanager-file.h" + +#include "file/file-open.h" +#include "file/file-save.h" +#include "file/gimp-file.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpclipboard.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpexportdialog.h" +#include "widgets/gimpfiledialog.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpopendialog.h" +#include "widgets/gimpsavedialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" + +#include "dialogs/dialogs.h" +#include "dialogs/file-save-dialog.h" + +#include "actions.h" +#include "file-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void file_open_dialog_show (Gimp *gimp, + GtkWidget *parent, + const gchar *title, + GimpImage *image, + GFile *file, + gboolean open_as_layers); +static GtkWidget * file_save_dialog_show (Gimp *gimp, + GimpImage *image, + GtkWidget *parent, + const gchar *title, + gboolean save_a_copy, + gboolean close_after_saving, + GimpDisplay *display); +static GtkWidget * file_export_dialog_show (Gimp *gimp, + GimpImage *image, + GtkWidget *parent); +static void file_save_dialog_response (GtkWidget *dialog, + gint response_id, + gpointer data); +static void file_export_dialog_response (GtkWidget *dialog, + gint response_id, + gpointer data); +static void file_new_template_callback (GtkWidget *widget, + const gchar *name, + gpointer data); +static void file_revert_confirm_response (GtkWidget *dialog, + gint response_id, + GimpDisplay *display); + + + +/* public functions */ + + +void +file_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + GimpImage *image; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + image = action_data_get_image (data); + + file_open_dialog_show (gimp, widget, + _("Open Image"), + image, NULL, FALSE); +} + +void +file_open_as_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *widget; + GimpDisplay *display; + GimpImage *image = NULL; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + display = action_data_get_display (data); + + if (display) + image = gimp_display_get_image (display); + + file_open_dialog_show (gimp, widget, + _("Open Image as Layers"), + image, NULL, TRUE); +} + +void +file_open_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + return_if_no_widget (widget, data); + + gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + NULL /*ui_manager*/, + "gimp-file-open-location-dialog", -1, TRUE); +} + +void +file_open_recent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpImagefile *imagefile; + gint index; + gint num_entries; + return_if_no_gimp (gimp, data); + + index = g_variant_get_int32 (value); + + num_entries = gimp_container_get_n_children (gimp->documents); + + if (index >= num_entries) + return; + + imagefile = (GimpImagefile *) + gimp_container_get_child_by_index (gimp->documents, index); + + if (imagefile) + { + GFile *file; + GimpDisplay *display; + GtkWidget *widget; + GimpProgress *progress; + GimpImage *image; + GimpPDBStatusType status; + GError *error = NULL; + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + g_object_ref (display); + g_object_ref (imagefile); + + file = gimp_imagefile_get_file (imagefile); + + progress = gimp_display_get_image (display) ? + NULL : GIMP_PROGRESS (display); + + image = file_open_with_display (gimp, action_data_get_context (data), + progress, + file, FALSE, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget), + &status, &error); + + if (! image && status != GIMP_PDB_CANCEL) + { + gimp_message (gimp, G_OBJECT (display), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + g_object_unref (imagefile); + g_object_unref (display); + } +} + +void +file_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplay *display; + GimpImage *image; + GtkWidget *widget; + GimpSaveMode save_mode; + GFile *file = NULL; + gboolean saved = FALSE; + return_if_no_gimp (gimp, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + image = gimp_display_get_image (display); + + save_mode = (GimpSaveMode) g_variant_get_int32 (value); + + if (! gimp_image_get_active_drawable (image)) + return; + + file = gimp_image_get_file (image); + + switch (save_mode) + { + case GIMP_SAVE_MODE_SAVE: + case GIMP_SAVE_MODE_SAVE_AND_CLOSE: + /* Only save if the image has been modified, or if it is new. */ + if ((gimp_image_is_dirty (image) || + ! GIMP_GUI_CONFIG (image->gimp->config)->trust_dirty_flag) || + file == NULL) + { + GimpPlugInProcedure *save_proc = gimp_image_get_save_proc (image); + + if (file && ! save_proc) + { + save_proc = + gimp_plug_in_manager_file_procedure_find (image->gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_SAVE, + file, NULL); + } + + if (file && save_proc) + { + saved = file_save_dialog_save_image (GIMP_PROGRESS (display), + gimp, image, file, + save_proc, + GIMP_RUN_WITH_LAST_VALS, + TRUE, FALSE, FALSE, + gimp_image_get_xcf_compression (image), + TRUE); + break; + } + + /* fall thru */ + } + else + { + gimp_message_literal (image->gimp, + G_OBJECT (display), GIMP_MESSAGE_INFO, + _("No changes need to be saved")); + saved = TRUE; + break; + } + + case GIMP_SAVE_MODE_SAVE_AS: + file_save_dialog_show (gimp, image, widget, + _("Save Image"), FALSE, + save_mode == GIMP_SAVE_MODE_SAVE_AND_CLOSE, display); + break; + + case GIMP_SAVE_MODE_SAVE_A_COPY: + file_save_dialog_show (gimp, image, widget, + _("Save a Copy of the Image"), TRUE, + FALSE, display); + break; + + case GIMP_SAVE_MODE_EXPORT_AS: + file_export_dialog_show (gimp, image, widget); + break; + + case GIMP_SAVE_MODE_EXPORT: + case GIMP_SAVE_MODE_OVERWRITE: + { + GFile *file = NULL; + GimpPlugInProcedure *export_proc = NULL; + gboolean overwrite = FALSE; + + if (save_mode == GIMP_SAVE_MODE_EXPORT) + { + file = gimp_image_get_exported_file (image); + export_proc = gimp_image_get_export_proc (image); + + if (! file) + { + /* Behave as if Export As... was invoked */ + file_export_dialog_show (gimp, image, widget); + break; + } + + overwrite = FALSE; + } + else if (save_mode == GIMP_SAVE_MODE_OVERWRITE) + { + file = gimp_image_get_imported_file (image); + + overwrite = TRUE; + } + + if (file && ! export_proc) + { + export_proc = + gimp_plug_in_manager_file_procedure_find (image->gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_EXPORT, + file, NULL); + } + + if (file && export_proc) + { + saved = file_save_dialog_save_image (GIMP_PROGRESS (display), + gimp, image, file, + export_proc, + GIMP_RUN_WITH_LAST_VALS, + FALSE, + overwrite, ! overwrite, + FALSE, TRUE); + } + } + break; + } + + if (save_mode == GIMP_SAVE_MODE_SAVE_AND_CLOSE && + saved && + ! gimp_image_is_dirty (image)) + { + gimp_display_close (display); + } +} + +void +file_create_template_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + dialog = gimp_query_string_box (_("Create New Template"), + GTK_WIDGET (gimp_display_get_shell (display)), + gimp_standard_help_func, + GIMP_HELP_FILE_CREATE_TEMPLATE, + _("Enter a name for this template"), + NULL, + G_OBJECT (image), "disconnect", + file_new_template_callback, image); + gtk_widget_show (dialog); +} + +void +file_revert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + GFile *file; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + file = gimp_image_get_file (image); + + if (! file) + file = gimp_image_get_imported_file (image); + + if (! file) + { + gimp_message_literal (image->gimp, + G_OBJECT (display), GIMP_MESSAGE_ERROR, + _("Revert failed. " + "No file name associated with this image.")); + return; + } + +#define REVERT_DIALOG_KEY "gimp-revert-confirm-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), REVERT_DIALOG_KEY); + + if (! dialog) + { + dialog = + gimp_message_dialog_new (_("Revert Image"), GIMP_ICON_DOCUMENT_REVERT, + GTK_WIDGET (gimp_display_get_shell (display)), + 0, + gimp_standard_help_func, GIMP_HELP_FILE_REVERT, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Revert"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (display, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_revert_confirm_response), + display); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Revert '%s' to '%s'?"), + gimp_image_get_display_name (image), + gimp_file_get_utf8_name (file)); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("By reverting the image to the state saved " + "on disk, you will lose all changes, " + "including all undo information.")); + + dialogs_attach_dialog (G_OBJECT (image), REVERT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +file_close_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + return_if_no_gimp (gimp, data); + + if (! gimp_displays_dirty (gimp)) + { + gimp_displays_close (gimp); + } + else + { + GtkWidget *widget; + return_if_no_widget (widget, data); + + gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + "gimp-close-all-dialog", -1); + } +} + +void +file_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplay *display; + GimpImage *image; + GFile *file; + return_if_no_gimp (gimp, data); + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + file = gimp_image_get_any_file (image); + + if (file) + { + gchar *uri = g_file_get_uri (file); + + gimp_clipboard_set_text (gimp, uri); + g_free (uri); + } +} + +void +file_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplay *display; + GimpImage *image; + GFile *file; + return_if_no_gimp (gimp, data); + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + file = gimp_image_get_any_file (image); + + if (file) + { + GError *error = NULL; + + if (! gimp_file_show_in_file_manager (file, &error)) + { + gimp_message (gimp, G_OBJECT (display), GIMP_MESSAGE_ERROR, + _("Can't show file in file manager: %s"), + error->message); + g_clear_error (&error); + } + } +} + +void +file_quit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + return_if_no_gimp (gimp, data); + + gimp_exit (gimp, FALSE); +} + +void +file_file_open_dialog (Gimp *gimp, + GFile *file, + GtkWidget *parent) +{ + file_open_dialog_show (gimp, parent, + _("Open Image"), + NULL, file, FALSE); +} + + +/* private functions */ + +static void +file_open_dialog_show (Gimp *gimp, + GtkWidget *parent, + const gchar *title, + GimpImage *image, + GFile *file, + gboolean open_as_layers) +{ + GtkWidget *dialog; + + dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (parent), + gimp_widget_get_monitor (parent), + NULL /*ui_manager*/, + "gimp-file-open-dialog", -1, FALSE); + + if (dialog) + { + if (! file && image) + file = gimp_image_get_file (image); + + if (! file) + file = g_object_get_data (G_OBJECT (gimp), + GIMP_FILE_OPEN_LAST_FILE_KEY); + + if (file) + { + gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, NULL); + } + else if (gimp->default_folder) + { + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), + gimp->default_folder, NULL); + } + + gtk_window_set_title (GTK_WINDOW (dialog), title); + + gimp_open_dialog_set_image (GIMP_OPEN_DIALOG (dialog), + image, open_as_layers); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (gtk_widget_get_toplevel (parent))); + + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +static GtkWidget * +file_save_dialog_show (Gimp *gimp, + GimpImage *image, + GtkWidget *parent, + const gchar *title, + gboolean save_a_copy, + gboolean close_after_saving, + GimpDisplay *display) +{ + GtkWidget *dialog; + +#define SAVE_DIALOG_KEY "gimp-file-save-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), SAVE_DIALOG_KEY); + + if (! dialog) + { + dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (parent), + gimp_widget_get_monitor (parent), + NULL /*ui_manager*/, + "gimp-file-save-dialog", + -1, FALSE); + + if (dialog) + { + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (gtk_widget_get_toplevel (parent))); + + dialogs_attach_dialog (G_OBJECT (image), SAVE_DIALOG_KEY, dialog); + g_signal_connect_object (image, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_save_dialog_response), + image); + } + } + + if (dialog) + { + gtk_window_set_title (GTK_WINDOW (dialog), title); + + gimp_save_dialog_set_image (GIMP_SAVE_DIALOG (dialog), + image, save_a_copy, + close_after_saving, GIMP_OBJECT (display)); + + gtk_window_present (GTK_WINDOW (dialog)); + } + + return dialog; +} + +static void +file_save_dialog_response (GtkWidget *dialog, + gint response_id, + gpointer data) +{ + if (response_id == FILE_SAVE_RESPONSE_OTHER_DIALOG) + { + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + GtkWindow *parent; + GtkWidget *other; + GFile *file; + gchar *folder; + gchar *basename; + + parent = gtk_window_get_transient_for (GTK_WINDOW (dialog)); + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + folder = g_path_get_dirname (gimp_file_get_utf8_name (file)); + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + g_object_unref (file); + + other = file_export_dialog_show (GIMP_FILE_DIALOG (file_dialog)->image->gimp, + GIMP_FILE_DIALOG (file_dialog)->image, + GTK_WIDGET (parent)); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (other), folder); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (other), basename); + + g_free (folder); + g_free (basename); + } +} + +static GtkWidget * +file_export_dialog_show (Gimp *gimp, + GimpImage *image, + GtkWidget *parent) +{ + GtkWidget *dialog; + +#define EXPORT_DIALOG_KEY "gimp-file-export-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY); + + if (! dialog) + { + dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (parent), + gimp_widget_get_monitor (parent), + NULL /*ui_manager*/, + "gimp-file-export-dialog", + -1, FALSE); + + if (dialog) + { + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (gtk_widget_get_toplevel (parent))); + + dialogs_attach_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY, dialog); + g_signal_connect_object (image, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_export_dialog_response), + image); + } + } + + if (dialog) + { + gimp_export_dialog_set_image (GIMP_EXPORT_DIALOG (dialog), image); + + gtk_window_present (GTK_WINDOW (dialog)); + } + + return dialog; +} + +static void +file_export_dialog_response (GtkWidget *dialog, + gint response_id, + gpointer data) +{ + if (response_id == FILE_SAVE_RESPONSE_OTHER_DIALOG) + { + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + GtkWindow *parent; + GtkWidget *other; + GFile *file; + gchar *folder; + gchar *basename; + + parent = gtk_window_get_transient_for (GTK_WINDOW (dialog)); + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + folder = g_path_get_dirname (gimp_file_get_utf8_name (file)); + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + g_object_unref (file); + + other = file_save_dialog_show (GIMP_FILE_DIALOG (file_dialog)->image->gimp, + GIMP_FILE_DIALOG (file_dialog)->image, + GTK_WIDGET (parent), + _("Save Image"), + FALSE, FALSE, NULL); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (other), folder); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (other), basename); + + g_free (folder); + g_free (basename); + } +} + +static void +file_new_template_callback (GtkWidget *widget, + const gchar *name, + gpointer data) +{ + GimpTemplate *template; + GimpImage *image; + + image = (GimpImage *) data; + + if (! (name && strlen (name))) + name = _("(Unnamed Template)"); + + template = gimp_template_new (name); + gimp_template_set_from_image (template, image); + gimp_container_add (image->gimp->templates, GIMP_OBJECT (template)); + g_object_unref (template); +} + +static void +file_revert_confirm_response (GtkWidget *dialog, + gint response_id, + GimpDisplay *display) +{ + GimpImage *old_image = gimp_display_get_image (display); + + gtk_widget_destroy (dialog); + + if (response_id == GTK_RESPONSE_OK) + { + Gimp *gimp = old_image->gimp; + GimpImage *new_image; + GFile *file; + GimpPDBStatusType status; + GError *error = NULL; + + file = gimp_image_get_file (old_image); + + if (! file) + file = gimp_image_get_imported_file (old_image); + + new_image = file_open_image (gimp, gimp_get_user_context (gimp), + GIMP_PROGRESS (display), + file, file, FALSE, NULL, + GIMP_RUN_INTERACTIVE, + &status, NULL, &error); + + if (new_image) + { + gimp_displays_reconnect (gimp, old_image, new_image); + gimp_image_flush (new_image); + + /* the displays own the image now */ + g_object_unref (new_image); + } + else if (status != GIMP_PDB_CANCEL) + { + gimp_message (gimp, G_OBJECT (display), GIMP_MESSAGE_ERROR, + _("Reverting to '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + } +} diff --git a/app/actions/file-commands.h b/app/actions/file-commands.h new file mode 100644 index 0000000..47c8750 --- /dev/null +++ b/app/actions/file-commands.h @@ -0,0 +1,63 @@ +/* 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 . + */ + +#ifndef __FILE_COMMANDS_H__ +#define __FILE_COMMANDS_H__ + + +void file_open_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_open_as_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_open_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_open_recent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void file_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_create_template_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void file_revert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_close_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_copy_location_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_show_in_file_manager_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void file_quit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void file_file_open_dialog (Gimp *gimp, + GFile *file, + GtkWidget *parent); + + +#endif /* __FILE_COMMANDS_H__ */ diff --git a/app/actions/filters-actions.c b/app/actions/filters-actions.c new file mode 100644 index 0000000..b20c433 --- /dev/null +++ b/app/actions/filters-actions.c @@ -0,0 +1,1229 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp-filter-history.h" +#include "core/gimpimage.h" +#include "core/gimplayermask.h" + +#include "pdb/gimpprocedure.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" + +#include "actions.h" +#include "filters-actions.h" +#include "filters-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void filters_actions_set_tooltips (GimpActionGroup *group, + const GimpStringActionEntry *entries, + gint n_entries); +static void filters_actions_history_changed (Gimp *gimp, + GimpActionGroup *group); + + +/* private variables */ + +static const GimpActionEntry filters_menu_actions[] = +{ + { "filters-menu", NULL, NC_("filters-action", + "Filte_rs") }, + { "filters-recent-menu", NULL, NC_("filters-action", + "Recently _Used") }, + { "filters-blur-menu", NULL, NC_("filters-action", + "_Blur") }, + { "filters-noise-menu", NULL, NC_("filters-action", + "_Noise") }, + { "filters-edge-detect-menu", NULL, NC_("filters-action", + "Edge-De_tect") }, + { "filters-enhance-menu", NULL, NC_("filters-action", + "En_hance") }, + { "filters-combine-menu", NULL, NC_("filters-action", + "C_ombine") }, + { "filters-generic-menu", NULL, NC_("filters-action", + "_Generic") }, + { "filters-light-shadow-menu", NULL, NC_("filters-action", + "_Light and Shadow") }, + { "filters-distorts-menu", NULL, NC_("filters-action", + "_Distorts") }, + { "filters-artistic-menu", NULL, NC_("filters-action", + "_Artistic") }, + { "filters-decor-menu", NULL, NC_("filters-action", + "_Decor") }, + { "filters-map-menu", NULL, NC_("filters-action", + "_Map") }, + { "filters-render-menu", NULL, NC_("filters-action", + "_Render") }, + { "filters-render-clouds-menu", NULL, NC_("filters-action", + "_Clouds") }, + { "filters-render-fractals-menu", NULL, NC_("filters-action", + "_Fractals") }, + { "filters-render-nature-menu", NULL, NC_("filters-action", + "_Nature") }, + { "filters-render-noise-menu", NULL, NC_("filters-action", + "N_oise") }, + { "filters-render-pattern-menu", NULL, NC_("filters-action", + "_Pattern") }, + { "filters-web-menu", NULL, NC_("filters-action", + "_Web") }, + { "filters-animation-menu", NULL, NC_("filters-action", + "An_imation") } +}; + +static const GimpStringActionEntry filters_actions[] = +{ + { "filters-antialias", GIMP_ICON_GEGL, + NC_("filters-action", "_Antialias"), NULL, NULL, + "gegl:antialias", + GIMP_HELP_FILTER_ANTIALIAS }, + + { "filters-color-enhance", GIMP_ICON_GEGL, + NC_("filters-action", "_Color Enhance"), NULL, NULL, + "gegl:color-enhance", + GIMP_HELP_FILTER_COLOR_ENHANCE }, + + { "filters-invert-linear", GIMP_ICON_INVERT, + NC_("filters-action", "L_inear Invert"), NULL, NULL, + "gegl:invert-linear", + GIMP_HELP_FILTER_INVERT_LINEAR }, + + { "filters-invert-perceptual", GIMP_ICON_INVERT, + NC_("filters-action", "In_vert"), NULL, NULL, + "gegl:invert-gamma", + GIMP_HELP_FILTER_INVERT_PERCEPTUAL }, + + { "filters-invert-value", GIMP_ICON_INVERT, + NC_("filters-action", "_Value Invert"), NULL, NULL, + "gegl:value-invert", + GIMP_HELP_FILTER_INVERT_VALUE }, + + { "filters-stretch-contrast-hsv", GIMP_ICON_GEGL, + NC_("filters-action", "_Stretch Contrast HSV"), NULL, NULL, + "gegl:stretch-contrast-hsv", + GIMP_HELP_FILTER_STRETCH_CONTRAST_HSV } +}; + +static const GimpStringActionEntry filters_settings_actions[] = +{ + { "filters-dilate", GIMP_ICON_GEGL, + NC_("filters-action", "_Dilate"), NULL, + NC_("filters-action", "Grow lighter areas of the image"), + "gegl:value-propagate\n" + "(mode white)" + "(lower-threshold 0.000000)" + "(upper-threshold 1.000000)" + "(rate 1.000000)" + "(top yes)" + "(left yes)" + "(right yes)" + "(bottom yes)" + "(value yes)" + "(alpha no)", + GIMP_HELP_FILTER_DILATE }, + + { "filters-erode", GIMP_ICON_GEGL, + NC_("filters-action", "_Erode"), NULL, + NC_("filters-action", "Grow darker areas of the image"), + "gegl:value-propagate\n" + "(mode black)" + "(lower-threshold 0.000000)" + "(upper-threshold 1.000000)" + "(rate 1.000000)" + "(top yes)" + "(left yes)" + "(right yes)" + "(bottom yes)" + "(value yes)" + "(alpha no)", + GIMP_HELP_FILTER_ERODE } +}; + +static const GimpStringActionEntry filters_interactive_actions[] = +{ + { "filters-alien-map", GIMP_ICON_GEGL, + NC_("filters-action", "_Alien Map..."), NULL, NULL, + "gegl:alien-map", + GIMP_HELP_FILTER_ALIEN_MAP }, + + { "filters-apply-canvas", GIMP_ICON_GEGL, + NC_("filters-action", "_Apply Canvas..."), NULL, NULL, + "gegl:texturize-canvas", + GIMP_HELP_FILTER_APPLY_CANVAS }, + + { "filters-apply-lens", GIMP_ICON_GEGL, + NC_("filters-action", "Apply _Lens..."), NULL, NULL, + "gegl:apply-lens", + GIMP_HELP_FILTER_APPLY_LENS }, + + { "filters-bayer-matrix", GIMP_ICON_GEGL, + NC_("filters-action", "_Bayer Matrix..."), NULL, NULL, + "gegl:bayer-matrix", + GIMP_HELP_FILTER_BAYER_MATRIX }, + + { "filters-bloom", GIMP_ICON_GEGL, + NC_("filters-action", "_Bloom..."), NULL, NULL, + "gegl:bloom", + GIMP_HELP_FILTER_BLOOM }, + + { "filters-brightness-contrast", GIMP_ICON_TOOL_BRIGHTNESS_CONTRAST, + NC_("filters-action", "B_rightness-Contrast..."), NULL, NULL, + "gimp:brightness-contrast", + GIMP_HELP_TOOL_BRIGHTNESS_CONTRAST }, + + { "filters-bump-map", GIMP_ICON_GEGL, + NC_("filters-action", "_Bump Map..."), NULL, NULL, + "gegl:bump-map", + GIMP_HELP_FILTER_BUMP_MAP }, + + { "filters-c2g", GIMP_ICON_GEGL, + NC_("filters-action", "_Color to Gray..."), NULL, NULL, + "gegl:c2g", + GIMP_HELP_FILTER_C2G }, + + { "filters-cartoon", GIMP_ICON_GEGL, + NC_("filters-action", "Ca_rtoon..."), NULL, NULL, + "gegl:cartoon", + GIMP_HELP_FILTER_CARTOON }, + + { "filters-channel-mixer", GIMP_ICON_GEGL, + NC_("filters-action", "_Channel Mixer..."), NULL, NULL, + "gegl:channel-mixer", + GIMP_HELP_FILTER_CHANNEL_MIXER }, + + { "filters-checkerboard", GIMP_ICON_GEGL, + NC_("filters-action", "_Checkerboard..."), NULL, NULL, + "gegl:checkerboard", + GIMP_HELP_FILTER_CHECKERBOARD }, + + { "filters-color-balance", GIMP_ICON_TOOL_COLOR_BALANCE, + NC_("filters-action", "Color _Balance..."), NULL, NULL, + "gimp:color-balance", + GIMP_HELP_TOOL_COLOR_BALANCE }, + + { "filters-color-exchange", GIMP_ICON_GEGL, + NC_("filters-action", "_Color Exchange..."), NULL, NULL, + "gegl:color-exchange", + GIMP_HELP_FILTER_COLOR_EXCHANGE }, + + { "filters-colorize", GIMP_ICON_TOOL_COLORIZE, + NC_("filters-action", "Colori_ze..."), NULL, NULL, + "gimp:colorize", + GIMP_HELP_TOOL_COLORIZE }, + + { "filters-dither", GIMP_ICON_GEGL, + NC_("filters-action", "Dithe_r..."), NULL, NULL, + "gegl:dither", + GIMP_HELP_FILTER_DITHER }, + + { "filters-color-rotate", GIMP_ICON_GEGL, + NC_("filters-action", "_Rotate Colors..."), NULL, NULL, + "gegl:color-rotate", + GIMP_HELP_FILTER_COLOR_ROTATE }, + + { "filters-color-temperature", GIMP_ICON_TOOL_COLOR_TEMPERATURE, + NC_("filters-action", "Color T_emperature..."), NULL, NULL, + "gegl:color-temperature", + GIMP_HELP_FILTER_COLOR_TEMPERATURE }, + + { "filters-color-to-alpha", GIMP_ICON_GEGL, + NC_("filters-action", "Color to _Alpha..."), NULL, NULL, + "gegl:color-to-alpha", + GIMP_HELP_FILTER_COLOR_TO_ALPHA }, + + { "filters-component-extract", GIMP_ICON_GEGL, + NC_("filters-action", "_Extract Component..."), NULL, NULL, + "gegl:component-extract", + GIMP_HELP_FILTER_COMPONENT_EXTRACT }, + + { "filters-convolution-matrix", GIMP_ICON_GEGL, + NC_("filters-action", "_Convolution Matrix..."), NULL, NULL, + "gegl:convolution-matrix", + GIMP_HELP_FILTER_CONVOLUTION_MATRIX }, + + { "filters-cubism", GIMP_ICON_GEGL, + NC_("filters-action", "_Cubism..."), NULL, NULL, + "gegl:cubism", + GIMP_HELP_FILTER_CUBISM }, + + { "filters-curves", GIMP_ICON_TOOL_CURVES, + NC_("filters-action", "_Curves..."), NULL, NULL, + "gimp:curves", + GIMP_HELP_TOOL_CURVES }, + + { "filters-deinterlace", GIMP_ICON_GEGL, + NC_("filters-action", "_Deinterlace..."), NULL, NULL, + "gegl:deinterlace", + GIMP_HELP_FILTER_DEINTERLACE }, + + { "filters-desaturate", GIMP_ICON_TOOL_DESATURATE, + NC_("filters-action", "_Desaturate..."), NULL, NULL, + "gimp:desaturate", + GIMP_HELP_FILTER_DESATURATE }, + + { "filters-difference-of-gaussians", GIMP_ICON_GEGL, + NC_("filters-action", "Difference of _Gaussians..."), NULL, NULL, + "gegl:difference-of-gaussians", + GIMP_HELP_FILTER_DIFFERENCE_OF_GAUSSIANS }, + + { "filters-diffraction-patterns", GIMP_ICON_GEGL, + NC_("filters-action", "D_iffraction Patterns..."), NULL, NULL, + "gegl:diffraction-patterns", + GIMP_HELP_FILTER_DIFFRACTION_PATTERNS }, + + { "filters-displace", GIMP_ICON_GEGL, + NC_("filters-action", "_Displace..."), NULL, NULL, + "gegl:displace", + GIMP_HELP_FILTER_DISPLACE }, + + { "filters-distance-map", GIMP_ICON_GEGL, + NC_("filters-action", "Distance _Map..."), NULL, NULL, + "gegl:distance-transform", + GIMP_HELP_FILTER_DISTANCE_MAP }, + + { "filters-dropshadow", GIMP_ICON_GEGL, + NC_("filters-action", "_Drop Shadow..."), NULL, NULL, + "gegl:dropshadow", + GIMP_HELP_FILTER_DROPSHADOW }, + + { "filters-edge", GIMP_ICON_GEGL, + NC_("filters-action", "_Edge..."), NULL, NULL, + "gegl:edge", + GIMP_HELP_FILTER_EDGE }, + + { "filters-edge-laplace", GIMP_ICON_GEGL, + NC_("filters-action", "_Laplace"), NULL, NULL, + "gegl:edge-laplace", + GIMP_HELP_FILTER_EDGE_LAPLACE }, + + { "filters-edge-neon", GIMP_ICON_GEGL, + NC_("filters-action", "_Neon..."), NULL, NULL, + "gegl:edge-neon", + GIMP_HELP_FILTER_EDGE_NEON }, + + { "filters-edge-sobel", GIMP_ICON_GEGL, + NC_("filters-action", "_Sobel..."), NULL, NULL, + "gegl:edge-sobel", + GIMP_HELP_FILTER_EDGE_SOBEL }, + + { "filters-emboss", GIMP_ICON_GEGL, + NC_("filters-action", "_Emboss..."), NULL, NULL, + "gegl:emboss", + GIMP_HELP_FILTER_EMBOSS }, + + { "filters-engrave", GIMP_ICON_GEGL, + NC_("filters-action", "En_grave..."), NULL, NULL, + "gegl:engrave", + GIMP_HELP_FILTER_ENGRAVE }, + + { "filters-exposure", GIMP_ICON_TOOL_EXPOSURE, + NC_("filters-action", "E_xposure..."), NULL, NULL, + "gegl:exposure", + GIMP_HELP_FILTER_EXPOSURE }, + + { "filters-fattal-2002", GIMP_ICON_GEGL, + NC_("filters-action", "_Fattal et al. 2002..."), NULL, NULL, + "gegl:fattal02", + GIMP_HELP_FILTER_FATTAL_2002 }, + + { "filters-focus-blur", GIMP_ICON_GEGL, + NC_("filters-action", "_Focus Blur..."), NULL, NULL, + "gegl:focus-blur", + GIMP_HELP_FILTER_FOCUS_BLUR }, + + { "filters-fractal-trace", GIMP_ICON_GEGL, + NC_("filters-action", "_Fractal Trace..."), NULL, NULL, + "gegl:fractal-trace", + GIMP_HELP_FILTER_FRACTAL_TRACE }, + + { "filters-gaussian-blur", GIMP_ICON_GEGL, + NC_("filters-action", "_Gaussian Blur..."), NULL, NULL, + "gegl:gaussian-blur", + GIMP_HELP_FILTER_GAUSSIAN_BLUR }, + + { "filters-gaussian-blur-selective", GIMP_ICON_GEGL, + NC_("filters-action", "_Selective Gaussian Blur..."), NULL, NULL, + "gegl:gaussian-blur-selective", + GIMP_HELP_FILTER_GAUSSIAN_BLUR_SELECTIVE }, + + { "filters-gegl-graph", GIMP_ICON_GEGL, + NC_("filters-action", "_GEGL graph..."), NULL, NULL, + "gegl:gegl", + GIMP_HELP_FILTER_GEGL_GRAPH }, + + { "filters-grid", GIMP_ICON_GRID, + NC_("filters-action", "_Grid..."), NULL, NULL, + "gegl:grid", + GIMP_HELP_FILTER_GRID }, + + { "filters-high-pass", GIMP_ICON_GEGL, + NC_("filters-action", "_High Pass..."), NULL, NULL, + "gegl:high-pass", + GIMP_HELP_FILTER_HIGH_PASS }, + + { "filters-hue-chroma", GIMP_ICON_GEGL, + NC_("filters-action", "Hue-_Chroma..."), NULL, NULL, + "gegl:hue-chroma", + GIMP_HELP_FILTER_HUE_CHROMA }, + + { "filters-hue-saturation", GIMP_ICON_TOOL_HUE_SATURATION, + NC_("filters-action", "Hue-_Saturation..."), NULL, NULL, + "gimp:hue-saturation", + GIMP_HELP_TOOL_HUE_SATURATION }, + + { "filters-illusion", GIMP_ICON_GEGL, + NC_("filters-action", "_Illusion..."), NULL, NULL, + "gegl:illusion", + GIMP_HELP_FILTER_ILLUSION }, + + { "filters-image-gradient", GIMP_ICON_GEGL, + NC_("filters-action", "_Image Gradient..."), NULL, NULL, + "gegl:image-gradient", + GIMP_HELP_FILTER_IMAGE_GRADIENT }, + + { "filters-kaleidoscope", GIMP_ICON_GEGL, + NC_("filters-action", "_Kaleidoscope..."), NULL, NULL, + "gegl:mirrors", + GIMP_HELP_FILTER_KALEIDOSCOPE }, + + { "filters-lens-blur", GIMP_ICON_GEGL, + NC_("filters-action", "Le_ns Blur..."), NULL, NULL, + "gegl:lens-blur", + GIMP_HELP_FILTER_LENS_BLUR }, + + { "filters-lens-distortion", GIMP_ICON_GEGL, + NC_("filters-action", "Le_ns Distortion..."), NULL, NULL, + "gegl:lens-distortion", + GIMP_HELP_FILTER_LENS_DISTORTION }, + + { "filters-lens-flare", GIMP_ICON_GEGL, + NC_("filters-action", "Lens _Flare..."), NULL, NULL, + "gegl:lens-flare", + GIMP_HELP_FILTER_LENS_FLARE }, + + { "filters-levels", GIMP_ICON_TOOL_LEVELS, + NC_("filters-action", "_Levels..."), NULL, NULL, + "gimp:levels", + GIMP_HELP_TOOL_LEVELS }, + + { "filters-linear-sinusoid", GIMP_ICON_TOOL_LEVELS, + NC_("filters-action", "_Linear Sinusoid..."), NULL, NULL, + "gegl:linear-sinusoid", + GIMP_HELP_FILTER_LINEAR_SINUSOID }, + + { "filters-little-planet", GIMP_ICON_GEGL, + NC_("filters-action", "_Little Planet..."), NULL, NULL, + "gegl:stereographic-projection", + GIMP_HELP_FILTER_LITTLE_PLANET }, + + { "filters-long-shadow", GIMP_ICON_GEGL, + NC_("filters-action", "_Long Shadow..."), NULL, NULL, + "gegl:long-shadow", + GIMP_HELP_FILTER_LONG_SHADOW }, + + { "filters-mantiuk-2006", GIMP_ICON_GEGL, + NC_("filters-action", "_Mantiuk 2006..."), NULL, NULL, + "gegl:mantiuk06", + GIMP_HELP_FILTER_MANTIUK_2006 }, + + { "filters-maze", GIMP_ICON_GEGL, + NC_("filters-action", "_Maze..."), NULL, NULL, + "gegl:maze", + GIMP_HELP_FILTER_MAZE }, + + { "filters-mean-curvature-blur", GIMP_ICON_GEGL, + NC_("filters-action", "Mean C_urvature Blur..."), NULL, NULL, + "gegl:mean-curvature-blur", + GIMP_HELP_FILTER_MEAN_CURVATURE_BLUR }, + + { "filters-median-blur", GIMP_ICON_GEGL, + NC_("filters-action", "_Median Blur..."), NULL, NULL, + "gegl:median-blur", + GIMP_HELP_FILTER_MEDIAN_BLUR }, + + { "filters-mono-mixer", GIMP_ICON_GEGL, + NC_("filters-action", "_Mono Mixer..."), NULL, NULL, + "gegl:mono-mixer", + GIMP_HELP_FILTER_MONO_MIXER }, + + { "filters-mosaic", GIMP_ICON_GEGL, + NC_("filters-action", "_Mosaic..."), NULL, NULL, + "gegl:mosaic", + GIMP_HELP_FILTER_MOSAIC }, + + { "filters-motion-blur-circular", GIMP_ICON_GEGL, + NC_("filters-action", "_Circular Motion Blur..."), NULL, NULL, + "gegl:motion-blur-circular", + GIMP_HELP_FILTER_MOTION_BLUR_CIRCULAR }, + + { "filters-motion-blur-linear", GIMP_ICON_GEGL, + NC_("filters-action", "_Linear Motion Blur..."), NULL, NULL, + "gegl:motion-blur-linear", + GIMP_HELP_FILTER_MOTION_BLUR_LINEAR }, + + { "filters-motion-blur-zoom", GIMP_ICON_GEGL, + NC_("filters-action", "_Zoom Motion Blur..."), NULL, NULL, + "gegl:motion-blur-zoom", + GIMP_HELP_FILTER_MOTION_BLUR_ZOOM }, + + { "filters-noise-cell", GIMP_ICON_GEGL, + NC_("filters-action", "_Cell Noise..."), NULL, NULL, + "gegl:cell-noise", + GIMP_HELP_FILTER_NOISE_CELL }, + + { "filters-newsprint", GIMP_ICON_GEGL, + NC_("filters-action", "_Newsprint..."), NULL, NULL, + "gegl:newsprint", + GIMP_HELP_FILTER_NEWSPRINT }, + + { "filters-noise-cie-lch", GIMP_ICON_GEGL, + NC_("filters-action", "_CIE lch Noise..."), NULL, NULL, + "gegl:noise-cie-lch", + GIMP_HELP_FILTER_NOISE_CIE_LCH }, + + { "filters-noise-hsv", GIMP_ICON_GEGL, + NC_("filters-action", "HS_V Noise..."), NULL, NULL, + "gegl:noise-hsv", + GIMP_HELP_FILTER_NOISE_HSV }, + + { "filters-noise-hurl", GIMP_ICON_GEGL, + NC_("filters-action", "_Hurl..."), NULL, NULL, + "gegl:noise-hurl", + GIMP_HELP_FILTER_NOISE_HURL }, + + { "filters-noise-perlin", GIMP_ICON_GEGL, + NC_("filters-action", "Perlin _Noise..."), NULL, NULL, + "gegl:perlin-noise", + GIMP_HELP_FILTER_NOISE_PERLIN }, + + { "filters-noise-pick", GIMP_ICON_GEGL, + NC_("filters-action", "_Pick..."), NULL, NULL, + "gegl:noise-pick", + GIMP_HELP_FILTER_NOISE_PICK }, + + { "filters-noise-rgb", GIMP_ICON_GEGL, + NC_("filters-action", "_RGB Noise..."), NULL, NULL, + "gegl:noise-rgb", + GIMP_HELP_FILTER_NOISE_RGB }, + + { "filters-noise-reduction", GIMP_ICON_GEGL, + NC_("filters-action", "Noise R_eduction..."), NULL, NULL, + "gegl:noise-reduction", + GIMP_HELP_FILTER_NOISE_REDUCTION }, + + { "filters-noise-simplex", GIMP_ICON_GEGL, + NC_("filters-action", "_Simplex Noise..."), NULL, NULL, + "gegl:simplex-noise", + GIMP_HELP_FILTER_NOISE_SIMPLEX }, + + { "filters-noise-slur", GIMP_ICON_GEGL, + NC_("filters-action", "_Slur..."), NULL, NULL, + "gegl:noise-slur", + GIMP_HELP_FILTER_NOISE_SLUR }, + + { "filters-noise-solid", GIMP_ICON_GEGL, + NC_("filters-action", "_Solid Noise..."), NULL, NULL, + "gegl:noise-solid", + GIMP_HELP_FILTER_NOISE_SOLID }, + + { "filters-noise-spread", GIMP_ICON_GEGL, + NC_("filters-action", "Sp_read..."), NULL, NULL, + "gegl:noise-spread", + GIMP_HELP_FILTER_NOISE_SPREAD }, + + { "filters-normal-map", GIMP_ICON_GEGL, + NC_("filters-action", "_Normal Map..."), NULL, NULL, + "gegl:normal-map", + GIMP_HELP_FILTER_NORMAL_MAP }, + + { "filters-offset", GIMP_ICON_TOOL_OFFSET, + NC_("filters-action", "_Offset..."), "O", NULL, + "gimp:offset", + GIMP_HELP_TOOL_OFFSET }, + + { "filters-oilify", GIMP_ICON_GEGL, + NC_("filters-action", "Oili_fy..."), NULL, NULL, + "gegl:oilify", + GIMP_HELP_FILTER_OILIFY }, + + { "filters-panorama-projection", GIMP_ICON_GEGL, + NC_("filters-action", "_Panorama Projection..."), NULL, NULL, + "gegl:panorama-projection", + GIMP_HELP_FILTER_PANORAMA_PROJECTION }, + + { "filters-photocopy", GIMP_ICON_GEGL, + NC_("filters-action", "_Photocopy..."), NULL, NULL, + "gegl:photocopy", + GIMP_HELP_FILTER_PHOTOCOPY }, + + { "filters-pixelize", GIMP_ICON_GEGL, + NC_("filters-action", "_Pixelize..."), NULL, NULL, + "gegl:pixelize", + GIMP_HELP_FILTER_PIXELIZE }, + + { "filters-plasma", GIMP_ICON_GEGL, + NC_("filters-action", "_Plasma..."), NULL, NULL, + "gegl:plasma", + GIMP_HELP_FILTER_PLASMA }, + + { "filters-polar-coordinates", GIMP_ICON_GEGL, + NC_("filters-action", "P_olar Coordinates..."), NULL, NULL, + "gegl:polar-coordinates", + GIMP_HELP_FILTER_POLAR_COORDINATES }, + + { "filters-posterize", GIMP_ICON_TOOL_POSTERIZE, + NC_("filters-action", "_Posterize..."), NULL, NULL, + "gimp:posterize", + GIMP_HELP_FILTER_POSTERIZE }, + + { "filters-recursive-transform", GIMP_ICON_GEGL, + NC_("filters-action", "_Recursive Transform..."), NULL, NULL, + "gegl:recursive-transform", + GIMP_HELP_FILTER_RECURSIVE_TRANSFORM }, + + { "filters-red-eye-removal", GIMP_ICON_GEGL, + NC_("filters-action", "_Red Eye Removal..."), NULL, NULL, + "gegl:red-eye-removal", + GIMP_HELP_FILTER_RED_EYE_REMOVAL }, + + { "filters-reinhard-2005", GIMP_ICON_GEGL, + NC_("filters-action", "_Reinhard 2005..."), NULL, NULL, + "gegl:reinhard05", + GIMP_HELP_FILTER_REINHARD_2005 }, + + { "filters-rgb-clip", GIMP_ICON_GEGL, + NC_("filters-action", "RGB _Clip..."), NULL, NULL, + "gegl:rgb-clip", + GIMP_HELP_FILTER_RGB_CLIP }, + + { "filters-ripple", GIMP_ICON_GEGL, + NC_("filters-action", "_Ripple..."), NULL, NULL, + "gegl:ripple", + GIMP_HELP_FILTER_RIPPLE }, + + { "filters-saturation", GIMP_ICON_GEGL, + NC_("filters-action", "Sat_uration..."), NULL, NULL, + "gegl:saturation", + GIMP_HELP_FILTER_SATURATION }, + + { "filters-semi-flatten", GIMP_ICON_GEGL, + NC_("filters-action", "_Semi-Flatten..."), NULL, NULL, + "gimp:semi-flatten", + GIMP_HELP_FILTER_SEMI_FLATTEN }, + + { "filters-sepia", GIMP_ICON_GEGL, + NC_("filters-action", "_Sepia..."), NULL, NULL, + "gegl:sepia", + GIMP_HELP_FILTER_SEPIA }, + + { "filters-shadows-highlights", GIMP_ICON_TOOL_SHADOWS_HIGHLIGHTS, + NC_("filters-action", "S_hadows-Highlights..."), NULL, NULL, + "gegl:shadows-highlights", + GIMP_HELP_FILTER_SHADOWS_HIGHLIGHTS }, + + { "filters-shift", GIMP_ICON_GEGL, + NC_("filters-action", "_Shift..."), NULL, NULL, + "gegl:shift", + GIMP_HELP_FILTER_SHIFT }, + + { "filters-sinus", GIMP_ICON_GEGL, + NC_("filters-action", "_Sinus..."), NULL, NULL, + "gegl:sinus", + GIMP_HELP_FILTER_SINUS }, + + { "filters-slic", GIMP_ICON_GEGL, + NC_("filters-action", "_Simple Linear Iterative Clustering..."), NULL, NULL, + "gegl:slic", + GIMP_HELP_FILTER_SLIC }, + + { "filters-snn-mean", GIMP_ICON_GEGL, + NC_("filters-action", "_Symmetric Nearest Neighbor..."), NULL, NULL, + "gegl:snn-mean", + GIMP_HELP_FILTER_SNN_MEAN }, + + { "filters-softglow", GIMP_ICON_GEGL, + NC_("filters-action", "_Softglow..."), NULL, NULL, + "gegl:softglow", + GIMP_HELP_FILTER_SOFTGLOW }, + + { "filters-spherize", GIMP_ICON_GEGL, + NC_("filters-action", "Spheri_ze..."), NULL, NULL, + "gegl:spherize", + GIMP_HELP_FILTER_SPHERIZE }, + + { "filters-spiral", GIMP_ICON_GEGL, + NC_("filters-action", "S_piral..."), NULL, NULL, + "gegl:spiral", + GIMP_HELP_FILTER_SPIRAL }, + + { "filters-stretch-contrast", GIMP_ICON_GEGL, + NC_("filters-action", "_Stretch Contrast..."), NULL, NULL, + "gegl:stretch-contrast", + GIMP_HELP_FILTER_STRETCH_CONTRAST }, + + { "filters-stress", GIMP_ICON_GEGL, + NC_("filters-action", "_Stress..."), NULL, NULL, + "gegl:stress", + GIMP_HELP_FILTER_STRESS }, + + { "filters-supernova", GIMP_ICON_GEGL, + NC_("filters-action", "Super_nova..."), NULL, NULL, + "gegl:supernova", + GIMP_HELP_FILTER_SUPERNOVA }, + + { "filters-threshold", GIMP_ICON_TOOL_THRESHOLD, + NC_("filters-action", "_Threshold..."), NULL, NULL, + "gimp:threshold", + GIMP_HELP_TOOL_THRESHOLD }, + + { "filters-threshold-alpha", GIMP_ICON_GEGL, + NC_("filters-action", "_Threshold Alpha..."), NULL, NULL, + "gimp:threshold-alpha", + GIMP_HELP_FILTER_THRESHOLD_ALPHA }, + + { "filters-tile-glass", GIMP_ICON_GEGL, + NC_("filters-action", "_Glass Tile..."), NULL, NULL, + "gegl:tile-glass", + GIMP_HELP_FILTER_TILE_GLASS }, + + { "filters-tile-paper", GIMP_ICON_GEGL, + NC_("filters-action", "_Paper Tile..."), NULL, NULL, + "gegl:tile-paper", + GIMP_HELP_FILTER_TILE_PAPER }, + + { "filters-tile-seamless", GIMP_ICON_GEGL, + NC_("filters-action", "_Tile Seamless..."), NULL, NULL, + "gegl:tile-seamless", + GIMP_HELP_FILTER_TILE_SEAMLESS }, + + { "filters-unsharp-mask", GIMP_ICON_GEGL, + NC_("filters-action", "Sharpen (_Unsharp Mask)..."), NULL, NULL, + "gegl:unsharp-mask", + GIMP_HELP_FILTER_UNSHARP_MASK }, + + { "filters-value-propagate", GIMP_ICON_GEGL, + NC_("filters-action", "_Value Propagate..."), NULL, NULL, + "gegl:value-propagate", + GIMP_HELP_FILTER_VALUE_PROPAGATE }, + + { "filters-variable-blur", GIMP_ICON_GEGL, + NC_("filters-action", "_Variable Blur..."), NULL, NULL, + "gegl:variable-blur", + GIMP_HELP_FILTER_VARIABLE_BLUR }, + + { "filters-video-degradation", GIMP_ICON_GEGL, + NC_("filters-action", "Vi_deo Degradation..."), NULL, NULL, + "gegl:video-degradation", + GIMP_HELP_FILTER_VIDEO_DEGRADATION }, + + { "filters-vignette", GIMP_ICON_GEGL, + NC_("filters-action", "_Vignette..."), NULL, NULL, + "gegl:vignette", + GIMP_HELP_FILTER_VIGNETTE }, + + { "filters-waterpixels", GIMP_ICON_GEGL, + NC_("filters-action", "_Waterpixels..."), NULL, NULL, + "gegl:waterpixels", + GIMP_HELP_FILTER_WATERPIXELS }, + + { "filters-waves", GIMP_ICON_GEGL, + NC_("filters-action", "_Waves..."), NULL, NULL, + "gegl:waves", + GIMP_HELP_FILTER_WAVES }, + + { "filters-whirl-pinch", GIMP_ICON_GEGL, + NC_("filters-action", "W_hirl and Pinch..."), NULL, NULL, + "gegl:whirl-pinch", + GIMP_HELP_FILTER_WHIRL_PINCH }, + + { "filters-wind", GIMP_ICON_GEGL, + NC_("filters-action", "W_ind..."), NULL, NULL, + "gegl:wind", + GIMP_HELP_FILTER_WIND } +}; + +static const GimpEnumActionEntry filters_repeat_actions[] = +{ + { "filters-repeat", GIMP_ICON_SYSTEM_RUN, + NC_("filters-action", "Re_peat Last"), "F", + NC_("filters-action", + "Rerun the last used filter using the same settings"), + GIMP_RUN_WITH_LAST_VALS, FALSE, + GIMP_HELP_FILTER_REPEAT }, + + { "filters-reshow", GIMP_ICON_DIALOG_RESHOW_FILTER, + NC_("filters-action", "R_e-Show Last"), "F", + NC_("filters-action", "Show the last used filter dialog again"), + GIMP_RUN_INTERACTIVE, FALSE, + GIMP_HELP_FILTER_RESHOW } +}; + + +void +filters_actions_setup (GimpActionGroup *group) +{ + GimpProcedureActionEntry *entries; + gint n_entries; + gint i; + + gimp_action_group_add_actions (group, "filters-action", + filters_menu_actions, + G_N_ELEMENTS (filters_menu_actions)); + + gimp_action_group_add_string_actions (group, "filters-action", + filters_actions, + G_N_ELEMENTS (filters_actions), + filters_apply_cmd_callback); + filters_actions_set_tooltips (group, filters_actions, + G_N_ELEMENTS (filters_actions)); + + gimp_action_group_add_string_actions (group, "filters-action", + filters_settings_actions, + G_N_ELEMENTS (filters_settings_actions), + filters_apply_cmd_callback); + filters_actions_set_tooltips (group, filters_settings_actions, + G_N_ELEMENTS (filters_settings_actions)); + + gimp_action_group_add_string_actions (group, "filters-action", + filters_interactive_actions, + G_N_ELEMENTS (filters_interactive_actions), + filters_apply_interactive_cmd_callback); + filters_actions_set_tooltips (group, filters_interactive_actions, + G_N_ELEMENTS (filters_interactive_actions)); + + gimp_action_group_add_enum_actions (group, "filters-action", + filters_repeat_actions, + G_N_ELEMENTS (filters_repeat_actions), + filters_repeat_cmd_callback); + + n_entries = gimp_filter_history_size (group->gimp); + + entries = g_new0 (GimpProcedureActionEntry, n_entries); + + for (i = 0; i < n_entries; i++) + { + entries[i].name = g_strdup_printf ("filters-recent-%02d", i + 1); + entries[i].icon_name = NULL; + entries[i].label = ""; + entries[i].accelerator = ""; + entries[i].tooltip = NULL; + entries[i].procedure = NULL; + entries[i].help_id = GIMP_HELP_FILTER_RESHOW; + } + + gimp_action_group_add_procedure_actions (group, entries, n_entries, + filters_history_cmd_callback); + + for (i = 0; i < n_entries; i++) + { + gimp_action_group_set_action_visible (group, entries[i].name, FALSE); + g_free ((gchar *) entries[i].name); + } + + g_free (entries); + + g_signal_connect_object (group->gimp, "filter-history-changed", + G_CALLBACK (filters_actions_history_changed), + group, 0); + + filters_actions_history_changed (group->gimp, group); +} + +void +filters_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable = NULL; + gboolean writable = FALSE; + gboolean gray = FALSE; + gboolean alpha = FALSE; + gboolean supports_alpha = FALSE; + + image = action_data_get_image (data); + + if (image) + { + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + GimpItem *item; + + gray = gimp_drawable_is_gray (drawable); + alpha = gimp_drawable_has_alpha (drawable); + supports_alpha = gimp_drawable_supports_alpha (drawable); + + if (GIMP_IS_LAYER_MASK (drawable)) + item = GIMP_ITEM (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + else + item = GIMP_ITEM (drawable); + + writable = ! gimp_item_is_content_locked (item); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + writable = FALSE; + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("filters-alien-map", writable); + SET_SENSITIVE ("filters-antialias", writable); + SET_SENSITIVE ("filters-apply-canvas", writable); + SET_SENSITIVE ("filters-apply-lens", writable); + SET_SENSITIVE ("filters-bayer-matrix", writable); + SET_SENSITIVE ("filters-bloom", writable); + SET_SENSITIVE ("filters-brightness-contrast", writable); + SET_SENSITIVE ("filters-bump-map", writable); + SET_SENSITIVE ("filters-c2g", writable && !gray); + SET_SENSITIVE ("filters-cartoon", writable); + SET_SENSITIVE ("filters-channel-mixer", writable); + SET_SENSITIVE ("filters-checkerboard", writable); + SET_SENSITIVE ("filters-color-balance", writable && !gray); + SET_SENSITIVE ("filters-color-enhance", writable && !gray); + SET_SENSITIVE ("filters-color-exchange", writable); + SET_SENSITIVE ("filters-colorize", writable && !gray); + SET_SENSITIVE ("filters-dither", writable); + SET_SENSITIVE ("filters-color-rotate", writable); + SET_SENSITIVE ("filters-color-temperature", writable && !gray); + SET_SENSITIVE ("filters-color-to-alpha", writable && supports_alpha); + SET_SENSITIVE ("filters-component-extract", writable); + SET_SENSITIVE ("filters-convolution-matrix", writable); + SET_SENSITIVE ("filters-cubism", writable); + SET_SENSITIVE ("filters-curves", writable); + SET_SENSITIVE ("filters-deinterlace", writable); + SET_SENSITIVE ("filters-desaturate", writable && !gray); + SET_SENSITIVE ("filters-difference-of-gaussians", writable); + SET_SENSITIVE ("filters-diffraction-patterns", writable); + SET_SENSITIVE ("filters-dilate", writable); + SET_SENSITIVE ("filters-displace", writable); + SET_SENSITIVE ("filters-distance-map", writable); + SET_SENSITIVE ("filters-dropshadow", writable && alpha); + SET_SENSITIVE ("filters-edge", writable); + SET_SENSITIVE ("filters-edge-laplace", writable); + SET_SENSITIVE ("filters-edge-neon", writable); + SET_SENSITIVE ("filters-edge-sobel", writable); + SET_SENSITIVE ("filters-emboss", writable); + SET_SENSITIVE ("filters-engrave", writable); + SET_SENSITIVE ("filters-erode", writable); + SET_SENSITIVE ("filters-exposure", writable); + SET_SENSITIVE ("filters-fattal-2002", writable); + SET_SENSITIVE ("filters-focus-blur", writable); + SET_SENSITIVE ("filters-fractal-trace", writable); + SET_SENSITIVE ("filters-gaussian-blur", writable); + SET_SENSITIVE ("filters-gaussian-blur-selective", writable); + SET_SENSITIVE ("filters-gegl-graph", writable); + SET_SENSITIVE ("filters-grid", writable); + SET_SENSITIVE ("filters-high-pass", writable); + SET_SENSITIVE ("filters-hue-chroma", writable); + SET_SENSITIVE ("filters-hue-saturation", writable && !gray); + SET_SENSITIVE ("filters-illusion", writable); + SET_SENSITIVE ("filters-invert-linear", writable); + SET_SENSITIVE ("filters-invert-perceptual", writable); + SET_SENSITIVE ("filters-invert-value", writable); + SET_SENSITIVE ("filters-image-gradient", writable); + SET_SENSITIVE ("filters-kaleidoscope", writable); + SET_SENSITIVE ("filters-lens-blur", writable); + SET_SENSITIVE ("filters-lens-distortion", writable); + SET_SENSITIVE ("filters-lens-flare", writable); + SET_SENSITIVE ("filters-levels", writable); + SET_SENSITIVE ("filters-linear-sinusoid", writable); + SET_SENSITIVE ("filters-little-planet", writable); + SET_SENSITIVE ("filters-long-shadow", writable && alpha); + SET_SENSITIVE ("filters-mantiuk-2006", writable); + SET_SENSITIVE ("filters-maze", writable); + SET_SENSITIVE ("filters-mean-curvature-blur", writable); + SET_SENSITIVE ("filters-median-blur", writable); + SET_SENSITIVE ("filters-mono-mixer", writable && !gray); + SET_SENSITIVE ("filters-mosaic", writable); + SET_SENSITIVE ("filters-motion-blur-circular", writable); + SET_SENSITIVE ("filters-motion-blur-linear", writable); + SET_SENSITIVE ("filters-motion-blur-zoom", writable); + SET_SENSITIVE ("filters-newsprint", writable); + SET_SENSITIVE ("filters-noise-cell", writable); + SET_SENSITIVE ("filters-noise-cie-lch", writable); + SET_SENSITIVE ("filters-noise-hsv", writable && !gray); + SET_SENSITIVE ("filters-noise-hurl", writable); + SET_SENSITIVE ("filters-noise-perlin", writable); + SET_SENSITIVE ("filters-noise-pick", writable); + SET_SENSITIVE ("filters-noise-reduction", writable); + SET_SENSITIVE ("filters-noise-rgb", writable); + SET_SENSITIVE ("filters-noise-simplex", writable); + SET_SENSITIVE ("filters-noise-slur", writable); + SET_SENSITIVE ("filters-noise-solid", writable); + SET_SENSITIVE ("filters-noise-spread", writable); + SET_SENSITIVE ("filters-normal-map", writable); + SET_SENSITIVE ("filters-offset", writable); + SET_SENSITIVE ("filters-oilify", writable); + SET_SENSITIVE ("filters-panorama-projection", writable); + SET_SENSITIVE ("filters-photocopy", writable); + SET_SENSITIVE ("filters-pixelize", writable); + SET_SENSITIVE ("filters-plasma", writable); + SET_SENSITIVE ("filters-polar-coordinates", writable); + SET_SENSITIVE ("filters-posterize", writable); + SET_SENSITIVE ("filters-recursive-transform", writable); + SET_SENSITIVE ("filters-red-eye-removal", writable && !gray); + SET_SENSITIVE ("filters-reinhard-2005", writable); + SET_SENSITIVE ("filters-rgb-clip", writable); + SET_SENSITIVE ("filters-ripple", writable); + SET_SENSITIVE ("filters-saturation", writable && !gray); + SET_SENSITIVE ("filters-semi-flatten", writable && alpha); + SET_SENSITIVE ("filters-sepia", writable && !gray); + SET_SENSITIVE ("filters-shadows-highlights", writable); + SET_SENSITIVE ("filters-shift", writable); + SET_SENSITIVE ("filters-sinus", writable); + SET_SENSITIVE ("filters-slic", writable); + SET_SENSITIVE ("filters-snn-mean", writable); + SET_SENSITIVE ("filters-softglow", writable); + SET_SENSITIVE ("filters-spherize", writable); + SET_SENSITIVE ("filters-spiral", writable); + SET_SENSITIVE ("filters-stretch-contrast", writable); + SET_SENSITIVE ("filters-stretch-contrast-hsv", writable); + SET_SENSITIVE ("filters-stress", writable); + SET_SENSITIVE ("filters-supernova", writable); + SET_SENSITIVE ("filters-threshold", writable); + SET_SENSITIVE ("filters-threshold-alpha", writable && alpha); + SET_SENSITIVE ("filters-tile-glass", writable); + SET_SENSITIVE ("filters-tile-paper", writable); + SET_SENSITIVE ("filters-tile-seamless", writable); + SET_SENSITIVE ("filters-unsharp-mask", writable); + SET_SENSITIVE ("filters-value-propagate", writable); + SET_SENSITIVE ("filters-variable-blur", writable); + SET_SENSITIVE ("filters-video-degradation", writable); + SET_SENSITIVE ("filters-vignette", writable); + SET_SENSITIVE ("filters-waterpixels", writable); + SET_SENSITIVE ("filters-waves", writable); + SET_SENSITIVE ("filters-whirl-pinch", writable); + SET_SENSITIVE ("filters-wind", writable); + +#undef SET_SENSITIVE + + { + GimpProcedure *proc = gimp_filter_history_nth (group->gimp, 0); + gint i; + + if (proc && + gimp_procedure_get_sensitive (proc, GIMP_OBJECT (drawable), NULL)) + { + gimp_action_group_set_action_sensitive (group, "filters-repeat", TRUE); + gimp_action_group_set_action_sensitive (group, "filters-reshow", TRUE); + } + else + { + gimp_action_group_set_action_sensitive (group, "filters-repeat", FALSE); + gimp_action_group_set_action_sensitive (group, "filters-reshow", FALSE); + } + + for (i = 0; i < gimp_filter_history_length (group->gimp); i++) + { + gchar *name = g_strdup_printf ("filters-recent-%02d", i + 1); + gboolean sensitive; + + proc = gimp_filter_history_nth (group->gimp, i); + + sensitive = gimp_procedure_get_sensitive (proc, GIMP_OBJECT (drawable), + NULL); + + gimp_action_group_set_action_sensitive (group, name, sensitive); + + g_free (name); + } + } +} + +static void +filters_actions_set_tooltips (GimpActionGroup *group, + const GimpStringActionEntry *entries, + gint n_entries) +{ + gint i; + + for (i = 0; i < n_entries; i++) + { + const GimpStringActionEntry *entry = entries + i; + const gchar *description; + + description = gegl_operation_get_key (entry->value, "description"); + + if (description) + gimp_action_group_set_action_tooltip (group, entry->name, + description); + } +} + +static GimpActionGroup * +filters_actions_get_plug_in_group (GimpActionGroup *group) +{ + GList *list; + + for (list = gimp_ui_managers_from_name (""); + list; + list = g_list_next (list)) + { + GimpUIManager *manager = list->data; + + /* if this is our UI manager */ + if (gimp_ui_manager_get_action_group (manager, "filters") == group) + return gimp_ui_manager_get_action_group (manager, "plug-in"); + } + + /* this happens during initial UI manager construction */ + return NULL; +} + +static void +filters_actions_history_changed (Gimp *gimp, + GimpActionGroup *group) +{ + GimpProcedure *proc; + GimpActionGroup *plug_in_group; + gint i; + + plug_in_group = filters_actions_get_plug_in_group (group); + + proc = gimp_filter_history_nth (gimp, 0); + + if (proc) + { + GimpAction *actual_action = NULL; + const gchar *label; + gchar *repeat; + gchar *reshow; + gboolean sensitive = FALSE; + + label = gimp_procedure_get_label (proc); + + repeat = g_strdup_printf (_("Re_peat \"%s\""), label); + reshow = g_strdup_printf (_("R_e-Show \"%s\""), label); + + gimp_action_group_set_action_label (group, "filters-repeat", repeat); + gimp_action_group_set_action_label (group, "filters-reshow", reshow); + + g_free (repeat); + g_free (reshow); + + if (g_str_has_prefix (gimp_object_get_name (proc), "filters-")) + { + actual_action = + gimp_action_group_get_action (group, + gimp_object_get_name (proc)); + } + else if (plug_in_group) + { + /* copy the sensitivity of the plug-in procedure's actual + * action instead of calling filters_actions_update() + * because doing the latter would set the sensitivity of + * this image's action on all images' actions. See bug + * #517683. + */ + actual_action = + gimp_action_group_get_action (plug_in_group, + gimp_object_get_name (proc)); + } + + if (actual_action) + sensitive = gimp_action_get_sensitive (actual_action); + + gimp_action_group_set_action_sensitive (group, "filters-repeat", + sensitive); + gimp_action_group_set_action_sensitive (group, "filters-reshow", + sensitive); + } + else + { + gimp_action_group_set_action_label (group, "filters-repeat", + _("Repeat Last")); + gimp_action_group_set_action_label (group, "filters-reshow", + _("Re-Show Last")); + + gimp_action_group_set_action_sensitive (group, "filters-repeat", FALSE); + gimp_action_group_set_action_sensitive (group, "filters-reshow", FALSE); + } + + for (i = 0; i < gimp_filter_history_length (gimp); i++) + { + GimpAction *action; + GimpAction *actual_action = NULL; + const gchar *label; + gchar *name; + gboolean sensitive = FALSE; + + name = g_strdup_printf ("filters-recent-%02d", i + 1); + action = gimp_action_group_get_action (group, name); + g_free (name); + + proc = gimp_filter_history_nth (gimp, i); + + label = gimp_procedure_get_menu_label (proc); + + if (g_str_has_prefix (gimp_object_get_name (proc), "filters-")) + { + actual_action = + gimp_action_group_get_action (group, + gimp_object_get_name (proc)); + } + else if (plug_in_group) + { + /* see comment above */ + actual_action = + gimp_action_group_get_action (plug_in_group, + gimp_object_get_name (proc)); + } + + if (actual_action) + sensitive = gimp_action_get_sensitive (actual_action); + + g_object_set (action, + "visible", TRUE, + "sensitive", sensitive, + "procedure", proc, + "label", label, + "icon-name", gimp_viewable_get_icon_name (GIMP_VIEWABLE (proc)), + "tooltip", gimp_procedure_get_blurb (proc), + NULL); + } + + for (; i < gimp_filter_history_size (gimp); i++) + { + GimpAction *action; + gchar *name = g_strdup_printf ("filters-recent-%02d", i + 1); + + action = gimp_action_group_get_action (group, name); + g_free (name); + + g_object_set (action, + "visible", FALSE, + "procedure", NULL, + NULL); + } +} diff --git a/app/actions/filters-actions.h b/app/actions/filters-actions.h new file mode 100644 index 0000000..f197b86 --- /dev/null +++ b/app/actions/filters-actions.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 . + */ + +#ifndef __FILTERS_ACTIONS_H__ +#define __FILTERS_ACTIONS_H__ + + +void filters_actions_setup (GimpActionGroup *group); +void filters_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __FILTERS_ACTIONS_H__ */ diff --git a/app/actions/filters-commands.c b/app/actions/filters-commands.c new file mode 100644 index 0000000..6ca0529 --- /dev/null +++ b/app/actions/filters-commands.c @@ -0,0 +1,265 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "operations/gimp-operation-config.h" +#include "operations/gimpoperationsettings.h" + +#include "core/gimp.h" +#include "core/gimp-filter-history.h" +#include "core/gimpimage.h" +#include "core/gimpprogress.h" + +#include "widgets/gimpaction.h" + +#include "actions.h" +#include "filters-commands.h" +#include "gimpgeglprocedure.h" +#include "procedure-commands.h" + + +/* local function prototypes */ + +static gchar * filters_parse_operation (Gimp *gimp, + const gchar *operation_str, + const gchar *icon_name, + GimpObject **settings); + +static void filters_run_procedure (Gimp *gimp, + GimpDisplay *display, + GimpProcedure *procedure, + GimpRunMode run_mode); + + +/* public functions */ + +void +filters_apply_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + gchar *operation; + GimpObject *settings; + GimpProcedure *procedure; + return_if_no_drawable (image, drawable, data); + + operation = filters_parse_operation (image->gimp, + g_variant_get_string (value, NULL), + gimp_action_get_icon_name (action), + &settings); + + procedure = gimp_gegl_procedure_new (image->gimp, + GIMP_RUN_NONINTERACTIVE, settings, + operation, + gimp_action_get_name (action), + gimp_action_get_label (action), + gimp_action_get_tooltip (action), + gimp_action_get_icon_name (action), + gimp_action_get_help_id (action)); + + g_free (operation); + + if (settings) + g_object_unref (settings); + + gimp_filter_history_add (image->gimp, procedure); + filters_history_cmd_callback (NULL, + g_variant_new_uint64 (GPOINTER_TO_SIZE (procedure)), + data); + + g_object_unref (procedure); +} + +void +filters_apply_interactive_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpProcedure *procedure; + return_if_no_drawable (image, drawable, data); + + procedure = gimp_gegl_procedure_new (image->gimp, + GIMP_RUN_INTERACTIVE, NULL, + g_variant_get_string (value, NULL), + gimp_action_get_name (action), + gimp_action_get_label (action), + gimp_action_get_tooltip (action), + gimp_action_get_icon_name (action), + gimp_action_get_help_id (action)); + + gimp_filter_history_add (image->gimp, procedure); + filters_history_cmd_callback (NULL, + g_variant_new_uint64 (GPOINTER_TO_SIZE (procedure)), + data); + + g_object_unref (procedure); +} + +void +filters_repeat_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpDisplay *display; + GimpProcedure *procedure; + GimpRunMode run_mode; + return_if_no_drawable (image, drawable, data); + return_if_no_display (display, data); + + run_mode = (GimpRunMode) g_variant_get_int32 (value); + + procedure = gimp_filter_history_nth (image->gimp, 0); + + if (procedure) + filters_run_procedure (image->gimp, display, procedure, run_mode); +} + +void +filters_history_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplay *display; + GimpProcedure *procedure; + gsize hack; + return_if_no_gimp (gimp, data); + return_if_no_display (display, data); + + hack = g_variant_get_uint64 (value); + + procedure = GSIZE_TO_POINTER (hack); + + filters_run_procedure (gimp, display, procedure, GIMP_RUN_INTERACTIVE); +} + + +/* private functions */ + +static gchar * +filters_parse_operation (Gimp *gimp, + const gchar *operation_str, + const gchar *icon_name, + GimpObject **settings) +{ + const gchar *newline = strchr (operation_str, '\n'); + + *settings = NULL; + + if (newline) + { + gchar *operation; + const gchar *serialized; + + operation = g_strndup (operation_str, newline - operation_str); + serialized = newline + 1; + + if (*serialized) + { + GError *error = NULL; + + *settings = + g_object_new (gimp_operation_config_get_type (gimp, operation, + icon_name, + GIMP_TYPE_OPERATION_SETTINGS), + NULL); + + if (! gimp_config_deserialize_string (GIMP_CONFIG (*settings), + serialized, -1, NULL, + &error)) + { + g_warning ("filters_parse_operation: deserializing hardcoded " + "operation settings failed: %s", + error->message); + g_clear_error (&error); + + g_object_unref (*settings); + *settings = NULL; + } + } + + return operation; + } + + return g_strdup (operation_str); +} + +static void +filters_run_procedure (Gimp *gimp, + GimpDisplay *display, + GimpProcedure *procedure, + GimpRunMode run_mode) +{ + GimpObject *settings = NULL; + GimpValueArray *args; + + if (GIMP_IS_GEGL_PROCEDURE (procedure)) + { + GimpGeglProcedure *gegl_procedure = GIMP_GEGL_PROCEDURE (procedure); + + if (gegl_procedure->default_run_mode == GIMP_RUN_NONINTERACTIVE) + run_mode = GIMP_RUN_NONINTERACTIVE; + + settings = gegl_procedure->default_settings; + } + + args = procedure_commands_get_display_args (procedure, display, settings); + + if (args) + { + gboolean success = FALSE; + + if (run_mode == GIMP_RUN_NONINTERACTIVE) + { + success = + procedure_commands_run_procedure (procedure, gimp, + GIMP_PROGRESS (display), + args); + } + else + { + success = + procedure_commands_run_procedure_async (procedure, gimp, + GIMP_PROGRESS (display), + run_mode, args, + display); + } + + if (success) + gimp_filter_history_add (gimp, procedure); + + gimp_value_array_unref (args); + } +} diff --git a/app/actions/filters-commands.h b/app/actions/filters-commands.h new file mode 100644 index 0000000..68cb2ea --- /dev/null +++ b/app/actions/filters-commands.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __FILTERS_COMMANDS_H__ +#define __FILTERS_COMMANDS_H__ + + +void filters_apply_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void filters_apply_interactive_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void filters_repeat_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void filters_history_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __FILTERS_COMMANDS_H__ */ diff --git a/app/actions/fonts-actions.c b/app/actions/fonts-actions.c new file mode 100644 index 0000000..5191dcc --- /dev/null +++ b/app/actions/fonts-actions.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" + +#include "text/gimpfont.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "fonts-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry fonts_actions[] = +{ + { "fonts-popup", GIMP_ICON_FONT, + NC_("fonts-action", "Fonts Menu"), NULL, NULL, NULL, + GIMP_HELP_FONT_DIALOG }, + + { "fonts-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("fonts-action", "_Rescan Font List"), NULL, + NC_("fonts-action", "Rescan the installed fonts"), + data_refresh_cmd_callback, + GIMP_HELP_FONT_REFRESH } +}; + + +void +fonts_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "fonts-action", + fonts_actions, + G_N_ELEMENTS (fonts_actions)); +} + +void +fonts_actions_update (GimpActionGroup *group, + gpointer data) +{ +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("fonts-refresh", TRUE); + +#undef SET_SENSITIVE +} diff --git a/app/actions/fonts-actions.h b/app/actions/fonts-actions.h new file mode 100644 index 0000000..f4651d5 --- /dev/null +++ b/app/actions/fonts-actions.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 . + */ + +#ifndef __FONTS_ACTIONS_H__ +#define __FONTS_ACTIONS_H__ + + +void fonts_actions_setup (GimpActionGroup *group); +void fonts_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __FONTS_ACTIONS_H__ */ diff --git a/app/actions/gimpgeglprocedure.c b/app/actions/gimpgeglprocedure.c new file mode 100644 index 0000000..b9f243f --- /dev/null +++ b/app/actions/gimpgeglprocedure.c @@ -0,0 +1,476 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpgeglprocedure.c + * Copyright (C) 2016-2018 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "actions-types.h" + +#include "config/gimpguiconfig.h" + +#include "operations/gimp-operation-config.h" +#include "operations/gimpoperationsettings.h" + +#include "core/gimp.h" +#include "core/gimp-memsize.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpdrawable-operation.h" +#include "core/gimpimage.h" +#include "core/gimplayermask.h" +#include "core/gimpparamspecs.h" +#include "core/gimpsettings.h" +#include "core/gimptoolinfo.h" + +#include "display/gimpdisplay.h" + +#include "tools/gimpoperationtool.h" +#include "tools/tool_manager.h" + +#include "gimpgeglprocedure.h" + +#include "gimp-intl.h" + + +static void gimp_gegl_procedure_finalize (GObject *object); + +static gint64 gimp_gegl_procedure_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gchar * gimp_gegl_procedure_get_description (GimpViewable *viewable, + gchar **tooltip); + +static const gchar * gimp_gegl_procedure_get_label (GimpProcedure *procedure); +static const gchar * gimp_gegl_procedure_get_menu_label (GimpProcedure *procedure); +static const gchar * gimp_gegl_procedure_get_help_id (GimpProcedure *procedure); +static gboolean gimp_gegl_procedure_get_sensitive (GimpProcedure *procedure, + GimpObject *object, + const gchar **tooltip); +static GimpValueArray * gimp_gegl_procedure_execute (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpValueArray *args, + GError **error); +static void gimp_gegl_procedure_execute_async (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpValueArray *args, + GimpObject *display); + + +G_DEFINE_TYPE (GimpGeglProcedure, gimp_gegl_procedure, + GIMP_TYPE_PROCEDURE) + +#define parent_class gimp_gegl_procedure_parent_class + + +static void +gimp_gegl_procedure_class_init (GimpGeglProcedureClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpProcedureClass *proc_class = GIMP_PROCEDURE_CLASS (klass); + + object_class->finalize = gimp_gegl_procedure_finalize; + + gimp_object_class->get_memsize = gimp_gegl_procedure_get_memsize; + + viewable_class->default_icon_name = "gimp-gegl"; + viewable_class->get_description = gimp_gegl_procedure_get_description; + + proc_class->get_label = gimp_gegl_procedure_get_label; + proc_class->get_menu_label = gimp_gegl_procedure_get_menu_label; + proc_class->get_help_id = gimp_gegl_procedure_get_help_id; + proc_class->get_sensitive = gimp_gegl_procedure_get_sensitive; + proc_class->execute = gimp_gegl_procedure_execute; + proc_class->execute_async = gimp_gegl_procedure_execute_async; +} + +static void +gimp_gegl_procedure_init (GimpGeglProcedure *proc) +{ +} + +static void +gimp_gegl_procedure_finalize (GObject *object) +{ + GimpGeglProcedure *proc = GIMP_GEGL_PROCEDURE (object); + + g_clear_object (&proc->default_settings); + + g_clear_pointer (&proc->menu_label, g_free); + g_clear_pointer (&proc->label, g_free); + g_clear_pointer (&proc->help_id, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_gegl_procedure_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpGeglProcedure *proc = GIMP_GEGL_PROCEDURE (object); + gint64 memsize = 0; + + memsize += gimp_string_get_memsize (proc->menu_label); + memsize += gimp_string_get_memsize (proc->label); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gchar * +gimp_gegl_procedure_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpProcedure *procedure = GIMP_PROCEDURE (viewable); + + if (tooltip) + *tooltip = g_strdup (gimp_procedure_get_blurb (procedure)); + + return g_strdup (gimp_procedure_get_label (procedure)); +} + +static const gchar * +gimp_gegl_procedure_get_label (GimpProcedure *procedure) +{ + GimpGeglProcedure *proc = GIMP_GEGL_PROCEDURE (procedure); + gchar *ellipsis; + gchar *label; + + if (proc->label) + return proc->label; + + label = gimp_strip_uline (gimp_procedure_get_menu_label (procedure)); + + ellipsis = strstr (label, "..."); + + if (! ellipsis) + ellipsis = strstr (label, "\342\200\246" /* U+2026 HORIZONTAL ELLIPSIS */); + + if (ellipsis && ellipsis == (label + strlen (label) - 3)) + *ellipsis = '\0'; + + proc->label = label; + + return proc->label; +} + +static const gchar * +gimp_gegl_procedure_get_menu_label (GimpProcedure *procedure) +{ + GimpGeglProcedure *proc = GIMP_GEGL_PROCEDURE (procedure); + + if (proc->menu_label) + return proc->menu_label; + + return GIMP_PROCEDURE_CLASS (parent_class)->get_menu_label (procedure); +} + +static const gchar * +gimp_gegl_procedure_get_help_id (GimpProcedure *procedure) +{ + GimpGeglProcedure *proc = GIMP_GEGL_PROCEDURE (procedure); + + return proc->help_id; +} + +static gboolean +gimp_gegl_procedure_get_sensitive (GimpProcedure *procedure, + GimpObject *object, + const gchar **tooltip) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (object); + gboolean sensitive = FALSE; + + if (drawable) + { + GimpItem *item; + + if (GIMP_IS_LAYER_MASK (drawable)) + item = GIMP_ITEM (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable))); + else + item = GIMP_ITEM (drawable); + + sensitive = ! gimp_item_is_content_locked (item); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + sensitive = FALSE; + } + + return sensitive; +} + +static GimpValueArray * +gimp_gegl_procedure_execute (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpValueArray *args, + GError **error) +{ + GimpImage *image; + GimpDrawable *drawable; + GObject *config; + GeglNode *node; + + image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); + drawable = gimp_value_get_drawable (gimp_value_array_index (args, 2), gimp); + config = g_value_get_object (gimp_value_array_index (args, 3)); + + node = gegl_node_new_child (NULL, + "operation", procedure->original_name, + NULL); + + gimp_drawable_apply_operation_with_config ( + drawable, + progress, gimp_procedure_get_label (procedure), + node, config); + + g_object_unref (node); + + gimp_image_flush (image); + + return gimp_procedure_get_return_values (procedure, TRUE, NULL); +} + +static void +gimp_gegl_procedure_execute_async (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpValueArray *args, + GimpObject *display) +{ + GimpRunMode run_mode; + GimpObject *settings; + GimpTool *active_tool; + const gchar *tool_name; + + run_mode = g_value_get_int (gimp_value_array_index (args, 0)); + settings = g_value_get_object (gimp_value_array_index (args, 3)); + + if (! settings && + (run_mode != GIMP_RUN_INTERACTIVE || + GIMP_GUI_CONFIG (gimp->config)->filter_tool_use_last_settings)) + { + /* if we didn't get settings passed, get the last used settings */ + + GType config_type; + GimpContainer *container; + + config_type = G_VALUE_TYPE (gimp_value_array_index (args, 3)); + + container = gimp_operation_config_get_container (gimp, config_type, + (GCompareFunc) + gimp_settings_compare); + + settings = gimp_container_get_child_by_index (container, 0); + + /* only use the settings if they are automatically created + * "last used" values, not if they were saved explicitly and + * have a zero timestamp; and if they are not a separator. + */ + if (settings && + (GIMP_SETTINGS (settings)->time == 0 || + ! gimp_object_get_name (settings))) + { + settings = NULL; + } + } + + if (run_mode == GIMP_RUN_NONINTERACTIVE || + run_mode == GIMP_RUN_WITH_LAST_VALS) + { + if (settings || run_mode == GIMP_RUN_NONINTERACTIVE) + { + g_value_set_object (gimp_value_array_index (args, 3), settings); + gimp_procedure_execute (procedure, gimp, context, progress, + args, NULL); + return; + } + + gimp_message (gimp, + G_OBJECT (progress), GIMP_MESSAGE_WARNING, + _("There are no last settings for '%s', " + "showing the filter dialog instead."), + gimp_procedure_get_label (procedure)); + } + + if (! strcmp (procedure->original_name, "gimp:brightness-contrast")) + { + tool_name = "gimp-brightness-contrast-tool"; + } + else if (! strcmp (procedure->original_name, "gimp:curves")) + { + tool_name = "gimp-curves-tool"; + } + else if (! strcmp (procedure->original_name, "gimp:levels")) + { + tool_name = "gimp-levels-tool"; + } + else if (! strcmp (procedure->original_name, "gimp:threshold")) + { + tool_name = "gimp-threshold-tool"; + } + else if (! strcmp (procedure->original_name, "gimp:offset")) + { + tool_name = "gimp-offset-tool"; + } + else + { + tool_name = "gimp-operation-tool"; + } + + active_tool = tool_manager_get_active (gimp); + + /* do not use the passed context because we need to set the active + * tool on the global user context + */ + context = gimp_get_user_context (gimp); + + if (strcmp (gimp_object_get_name (active_tool->tool_info), tool_name)) + { + GimpToolInfo *tool_info = gimp_get_tool_info (gimp, tool_name); + + if (GIMP_IS_TOOL_INFO (tool_info)) + gimp_context_set_tool (context, tool_info); + } + else + { + gimp_context_tool_changed (context); + } + + active_tool = tool_manager_get_active (gimp); + + if (! strcmp (gimp_object_get_name (active_tool->tool_info), tool_name)) + { + /* Remember the procedure that created this tool, because + * we can't just switch to an operation tool using + * gimp_context_set_tool(), we also have to go through the + * initialization code below, otherwise we end up with a + * dummy tool that does nothing. See bug #776370. + */ + g_object_set_data_full (G_OBJECT (active_tool), "gimp-gegl-procedure", + g_object_ref (procedure), + (GDestroyNotify) g_object_unref); + + if (! strcmp (tool_name, "gimp-operation-tool")) + { + gimp_operation_tool_set_operation (GIMP_OPERATION_TOOL (active_tool), + procedure->original_name, + gimp_procedure_get_label (procedure), + gimp_procedure_get_label (procedure), + gimp_procedure_get_label (procedure), + gimp_viewable_get_icon_name (GIMP_VIEWABLE (procedure)), + gimp_procedure_get_help_id (procedure)); + } + + tool_manager_initialize_active (gimp, GIMP_DISPLAY (display)); + + if (settings) + gimp_filter_tool_set_config (GIMP_FILTER_TOOL (active_tool), + GIMP_CONFIG (settings)); + } +} + + +/* public functions */ + +GimpProcedure * +gimp_gegl_procedure_new (Gimp *gimp, + GimpRunMode default_run_mode, + GimpObject *default_settings, + const gchar *operation, + const gchar *name, + const gchar *menu_label, + const gchar *tooltip, + const gchar *icon_name, + const gchar *help_id) +{ + GimpProcedure *procedure; + GimpGeglProcedure *gegl_procedure; + GType config_type; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (operation != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (menu_label != NULL, NULL); + + config_type = gimp_operation_config_get_type (gimp, operation, icon_name, + GIMP_TYPE_OPERATION_SETTINGS); + + procedure = g_object_new (GIMP_TYPE_GEGL_PROCEDURE, NULL); + + gegl_procedure = GIMP_GEGL_PROCEDURE (procedure); + + gegl_procedure->default_run_mode = default_run_mode; + gegl_procedure->menu_label = g_strdup (menu_label); + gegl_procedure->help_id = g_strdup (help_id); + + if (default_settings) + gegl_procedure->default_settings = g_object_ref (default_settings); + + gimp_object_set_name (GIMP_OBJECT (procedure), name); + gimp_viewable_set_icon_name (GIMP_VIEWABLE (procedure), icon_name); + gimp_procedure_set_strings (procedure, + operation, + tooltip, + tooltip, + "author", "copyright", "date", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("run-mode", + "Run mode", + "Run mode", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_drawable_id ("drawable", + "Drawable", + "Input drawable", + gimp, TRUE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + g_param_spec_object ("settings", + "Settings", + "Settings", + config_type, + GIMP_PARAM_READWRITE)); + + return procedure; +} diff --git a/app/actions/gimpgeglprocedure.h b/app/actions/gimpgeglprocedure.h new file mode 100644 index 0000000..07fc24c --- /dev/null +++ b/app/actions/gimpgeglprocedure.h @@ -0,0 +1,70 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpgeglprocedure.h + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_PROCEDURE_H__ +#define __GIMP_GEGL_PROCEDURE_H__ + + +#include "pdb/gimpprocedure.h" + + +#define GIMP_TYPE_GEGL_PROCEDURE (gimp_gegl_procedure_get_type ()) +#define GIMP_GEGL_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GEGL_PROCEDURE, GimpGeglProcedure)) +#define GIMP_GEGL_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GEGL_PROCEDURE, GimpGeglProcedureClass)) +#define GIMP_IS_GEGL_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GEGL_PROCEDURE)) +#define GIMP_IS_GEGL_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GEGL_PROCEDURE)) +#define GIMP_GEGL_PROCEDURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GEGL_PROCEDURE, GimpGeglProcedureClass)) + + +typedef struct _GimpGeglProcedure GimpGeglProcedure; +typedef struct _GimpGeglProcedureClass GimpGeglProcedureClass; + +struct _GimpGeglProcedure +{ + GimpProcedure parent_instance; + + GimpRunMode default_run_mode; + GimpObject *default_settings; + + gchar *menu_label; + gchar *label; + gchar *help_id; +}; + +struct _GimpGeglProcedureClass +{ + GimpProcedureClass parent_class; +}; + + +GType gimp_gegl_procedure_get_type (void) G_GNUC_CONST; + +GimpProcedure * gimp_gegl_procedure_new (Gimp *gimp, + GimpRunMode default_run_mode, + GimpObject *default_settings, + const gchar *operation, + const gchar *name, + const gchar *menu_label, + const gchar *tooltip, + const gchar *icon_name, + const gchar *help_id); + + +#endif /* __GIMP_GEGL_PROCEDURE_H__ */ diff --git a/app/actions/gradient-editor-actions.c b/app/actions/gradient-editor-actions.c new file mode 100644 index 0000000..ca730e6 --- /dev/null +++ b/app/actions/gradient-editor-actions.c @@ -0,0 +1,906 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpdatafactory.h" +#include "core/gimpgradient.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpgradienteditor.h" +#include "widgets/gimphelp-ids.h" + +#include "data-editor-commands.h" +#include "gradient-editor-actions.h" +#include "gradient-editor-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry gradient_editor_actions[] = +{ + { "gradient-editor-popup", GIMP_ICON_GRADIENT, + NC_("gradient-editor-action", "Gradient Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_GRADIENT_EDITOR_DIALOG }, + + { "gradient-editor-left-color-type", NULL, + NC_("gradient-editor-action", "Left Color Type") }, + { "gradient-editor-load-left-color", GIMP_ICON_DOCUMENT_REVERT, + NC_("gradient-editor-action", "_Load Left Color From") }, + { "gradient-editor-save-left-color", GIMP_ICON_DOCUMENT_SAVE, + NC_("gradient-editor-action", "_Save Left Color To") }, + + { "gradient-editor-right-color-type", NULL, + NC_("gradient-editor-action", "Right Color Type") }, + { "gradient-editor-load-right-color", GIMP_ICON_DOCUMENT_REVERT, + NC_("gradient-editor-action", "Load Right Color Fr_om") }, + { "gradient-editor-save-right-color", GIMP_ICON_DOCUMENT_SAVE, + NC_("gradient-editor-action", "Sa_ve Right Color To") }, + + { "gradient-editor-blending-func", NULL, "blending-function" }, + { "gradient-editor-coloring-type", NULL, "coloring-type" }, + + { "gradient-editor-left-color", NULL, + NC_("gradient-editor-action", "L_eft Endpoint's Color..."), NULL, NULL, + gradient_editor_left_color_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR }, + + { "gradient-editor-right-color", NULL, + NC_("gradient-editor-action", "R_ight Endpoint's Color..."), NULL, NULL, + gradient_editor_right_color_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR }, + + { "gradient-editor-flip", GIMP_ICON_OBJECT_FLIP_HORIZONTAL, + "flip", NULL, NULL, + gradient_editor_flip_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_FLIP }, + + { "gradient-editor-replicate", GIMP_ICON_OBJECT_DUPLICATE, + "replicate", NULL, NULL, + gradient_editor_replicate_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_FLIP }, + + { "gradient-editor-split-midpoint", NULL, + "splitmidpoint", NULL, NULL, + gradient_editor_split_midpoint_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_SPLIT_MIDPOINT }, + + { "gradient-editor-split-uniform", NULL, + "splituniform", NULL, NULL, + gradient_editor_split_uniformly_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_SPLIT_UNIFORM }, + + { "gradient-editor-delete", GIMP_ICON_EDIT_DELETE, + "delete", "", NULL, + gradient_editor_delete_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_DELETE }, + + { "gradient-editor-recenter", NULL, + "recenter", NULL, NULL, + gradient_editor_recenter_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_RECENTER }, + + { "gradient-editor-redistribute", NULL, + "redistribute", NULL, NULL, + gradient_editor_redistribute_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_REDISTRIBUTE }, + + { "gradient-editor-blend-color", NULL, + NC_("gradient-editor-action", "Ble_nd Endpoints' Colors"), NULL, NULL, + gradient_editor_blend_color_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_BLEND_COLOR }, + + { "gradient-editor-blend-opacity", NULL, + NC_("gradient-editor-action", "Blend Endpoints' Opacit_y"), NULL, NULL, + gradient_editor_blend_opacity_cmd_callback, + GIMP_HELP_GRADIENT_EDITOR_BLEND_OPACITY } +}; + +static const GimpToggleActionEntry gradient_editor_toggle_actions[] = +{ + { "gradient-editor-edit-active", GIMP_ICON_LINKED, + NC_("gradient-editor-action", "Edit Active Gradient"), NULL, NULL, + data_editor_edit_active_cmd_callback, + FALSE, + GIMP_HELP_GRADIENT_EDITOR_EDIT_ACTIVE } +}; + + +#define LOAD_LEFT_FROM(num,magic) \ + { "gradient-editor-load-left-" num, NULL, \ + num, NULL, NULL, \ + (magic), FALSE, \ + GIMP_HELP_GRADIENT_EDITOR_LEFT_LOAD } +#define SAVE_LEFT_TO(num,magic) \ + { "gradient-editor-save-left-" num, NULL, \ + num, NULL, NULL, \ + (magic), FALSE, \ + GIMP_HELP_GRADIENT_EDITOR_LEFT_SAVE } +#define LOAD_RIGHT_FROM(num,magic) \ + { "gradient-editor-load-right-" num, NULL, \ + num, NULL, NULL, \ + (magic), FALSE, \ + GIMP_HELP_GRADIENT_EDITOR_RIGHT_LOAD } +#define SAVE_RIGHT_TO(num,magic) \ + { "gradient-editor-save-right-" num, NULL, \ + num, NULL, NULL, \ + (magic), FALSE, \ + GIMP_HELP_GRADIENT_EDITOR_RIGHT_SAVE } + +static const GimpEnumActionEntry gradient_editor_load_left_actions[] = +{ + { "gradient-editor-load-left-left-neighbor", NULL, + NC_("gradient-editor-action", "_Left Neighbor's Right Endpoint"), NULL, NULL, + GRADIENT_EDITOR_COLOR_NEIGHBOR_ENDPOINT, FALSE, + GIMP_HELP_GRADIENT_EDITOR_LEFT_LOAD }, + + { "gradient-editor-load-left-right-endpoint", NULL, + NC_("gradient-editor-action", "_Right Endpoint"), NULL, NULL, + GRADIENT_EDITOR_COLOR_OTHER_ENDPOINT, FALSE, + GIMP_HELP_GRADIENT_EDITOR_LEFT_LOAD }, + + { "gradient-editor-load-left-fg", NULL, + NC_("gradient-editor-action", "_Foreground Color"), NULL, NULL, + GRADIENT_EDITOR_COLOR_FOREGROUND, FALSE, + GIMP_HELP_GRADIENT_EDITOR_LEFT_LOAD }, + + { "gradient-editor-load-left-bg", NULL, + NC_("gradient-editor-action", "_Background Color"), NULL, NULL, + GRADIENT_EDITOR_COLOR_BACKGROUND, FALSE, + GIMP_HELP_GRADIENT_EDITOR_LEFT_LOAD }, + + LOAD_LEFT_FROM ("01", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 0), + LOAD_LEFT_FROM ("02", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 1), + LOAD_LEFT_FROM ("03", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 2), + LOAD_LEFT_FROM ("04", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 3), + LOAD_LEFT_FROM ("05", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 4), + LOAD_LEFT_FROM ("06", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 5), + LOAD_LEFT_FROM ("07", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 6), + LOAD_LEFT_FROM ("08", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 7), + LOAD_LEFT_FROM ("09", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 8), + LOAD_LEFT_FROM ("10", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 9) +}; + +static const GimpEnumActionEntry gradient_editor_save_left_actions[] = +{ + SAVE_LEFT_TO ("01", 0), + SAVE_LEFT_TO ("02", 1), + SAVE_LEFT_TO ("03", 2), + SAVE_LEFT_TO ("04", 3), + SAVE_LEFT_TO ("05", 4), + SAVE_LEFT_TO ("06", 5), + SAVE_LEFT_TO ("07", 6), + SAVE_LEFT_TO ("08", 7), + SAVE_LEFT_TO ("09", 8), + SAVE_LEFT_TO ("10", 9) +}; + +static const GimpEnumActionEntry gradient_editor_load_right_actions[] = +{ + { "gradient-editor-load-right-right-neighbor", NULL, + NC_("gradient-editor-action", "_Right Neighbor's Left Endpoint"), NULL, NULL, + GRADIENT_EDITOR_COLOR_NEIGHBOR_ENDPOINT, FALSE, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_LOAD }, + + { "gradient-editor-load-right-left-endpoint", NULL, + NC_("gradient-editor-action", "_Left Endpoint"), NULL, NULL, + GRADIENT_EDITOR_COLOR_OTHER_ENDPOINT, FALSE, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_LOAD }, + + { "gradient-editor-load-right-fg", NULL, + NC_("gradient-editor-action", "_Foreground Color"), NULL, NULL, + GRADIENT_EDITOR_COLOR_FOREGROUND, FALSE, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_LOAD }, + + { "gradient-editor-load-right-bg", NULL, + NC_("gradient-editor-action", "_Background Color"), NULL, NULL, + GRADIENT_EDITOR_COLOR_BACKGROUND, FALSE, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_LOAD }, + + LOAD_RIGHT_FROM ("01", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 0), + LOAD_RIGHT_FROM ("02", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 1), + LOAD_RIGHT_FROM ("03", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 2), + LOAD_RIGHT_FROM ("04", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 3), + LOAD_RIGHT_FROM ("05", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 4), + LOAD_RIGHT_FROM ("06", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 5), + LOAD_RIGHT_FROM ("07", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 6), + LOAD_RIGHT_FROM ("08", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 7), + LOAD_RIGHT_FROM ("09", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 8), + LOAD_RIGHT_FROM ("10", GRADIENT_EDITOR_COLOR_FIRST_CUSTOM + 9) +}; + +static const GimpEnumActionEntry gradient_editor_save_right_actions[] = +{ + SAVE_RIGHT_TO ("01", 0), + SAVE_RIGHT_TO ("02", 1), + SAVE_RIGHT_TO ("03", 2), + SAVE_RIGHT_TO ("04", 3), + SAVE_RIGHT_TO ("05", 4), + SAVE_RIGHT_TO ("06", 5), + SAVE_RIGHT_TO ("07", 6), + SAVE_RIGHT_TO ("08", 7), + SAVE_RIGHT_TO ("09", 8), + SAVE_RIGHT_TO ("10", 9) +}; + +#undef LOAD_LEFT_FROM +#undef SAVE_LEFT_TO +#undef LOAD_RIGHT_FROM +#undef SAVE_RIGHT_TO + + +static const GimpRadioActionEntry gradient_editor_left_color_type_actions[] = +{ + { "gradient-editor-left-color-fixed", NULL, + NC_("gradient-editor-color-type", "_Fixed"), NULL, NULL, + GIMP_GRADIENT_COLOR_FIXED, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR }, + + { "gradient-editor-left-color-foreground", NULL, + NC_("gradient-editor-color-type", "F_oreground Color"), NULL, NULL, + GIMP_GRADIENT_COLOR_FOREGROUND, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR }, + + { "gradient-editor-left-color-foreground-transparent", NULL, + NC_("gradient-editor-color-type", + "Fo_reground Color (Transparent)"), NULL, NULL, + GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR }, + + { "gradient-editor-left-color-background", NULL, + NC_("gradient-editor-color-type", "_Background Color"), NULL, NULL, + GIMP_GRADIENT_COLOR_BACKGROUND, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR }, + + { "gradient-editor-left-color-background-transparent", NULL, + NC_("gradient-editor-color-type", + "B_ackground Color (Transparent)"), NULL, NULL, + GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT, + GIMP_HELP_GRADIENT_EDITOR_LEFT_COLOR } +}; + +static const GimpRadioActionEntry gradient_editor_right_color_type_actions[] = +{ + { "gradient-editor-right-color-fixed", NULL, + NC_("gradient-editor-color-type", "_Fixed"), NULL, NULL, + GIMP_GRADIENT_COLOR_FIXED, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR }, + + { "gradient-editor-right-color-foreground", NULL, + NC_("gradient-editor-color-type", "F_oreground Color"), NULL, NULL, + GIMP_GRADIENT_COLOR_FOREGROUND, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR }, + + { "gradient-editor-right-color-foreground-transparent", NULL, + NC_("gradient-editor-color-type", + "Fo_reground Color (Transparent)"), NULL, NULL, + GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR }, + + { "gradient-editor-right-color-background", NULL, + NC_("gradient-editor-color-type", "_Background Color"), NULL, NULL, + GIMP_GRADIENT_COLOR_BACKGROUND, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR }, + + { "gradient-editor-right-color-background-transparent", NULL, + NC_("gradient-editor-color-type", + "B_ackground Color (Transparent)"), NULL, NULL, + GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT, + GIMP_HELP_GRADIENT_EDITOR_RIGHT_COLOR } +}; + +static const GimpRadioActionEntry gradient_editor_blending_actions[] = +{ + { "gradient-editor-blending-linear", NULL, + NC_("gradient-editor-blending", "_Linear"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_LINEAR, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-curved", NULL, + NC_("gradient-editor-blending", "_Curved"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_CURVED, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-sine", NULL, + NC_("gradient-editor-blending", "_Sinusoidal"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_SINE, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-sphere-increasing", NULL, + NC_("gradient-editor-blending", "Spherical (i_ncreasing)"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-sphere-decreasing", NULL, + NC_("gradient-editor-blending", "Spherical (_decreasing)"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-step", NULL, + NC_("gradient-editor-blending", "S_tep"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_STEP, + GIMP_HELP_GRADIENT_EDITOR_BLENDING }, + + { "gradient-editor-blending-varies", NULL, + NC_("gradient-editor-blending", "(Varies)"), NULL, NULL, + -1, + GIMP_HELP_GRADIENT_EDITOR_BLENDING } +}; + +static const GimpRadioActionEntry gradient_editor_coloring_actions[] = +{ + { "gradient-editor-coloring-rgb", NULL, + NC_("gradient-editor-coloring", "_RGB"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_RGB, + GIMP_HELP_GRADIENT_EDITOR_COLORING }, + + { "gradient-editor-coloring-hsv-ccw", NULL, + NC_("gradient-editor-coloring", "HSV (_counter-clockwise hue)"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_HSV_CCW, + GIMP_HELP_GRADIENT_EDITOR_COLORING }, + + { "gradient-editor-coloring-hsv-cw", NULL, + NC_("gradient-editor-coloring", "HSV (clockwise _hue)"), NULL, NULL, + GIMP_GRADIENT_SEGMENT_HSV_CW, + GIMP_HELP_GRADIENT_EDITOR_COLORING }, + + { "gradient-editor-coloring-varies", NULL, + NC_("gradient-editor-coloring", "(Varies)"), NULL, NULL, + -1, + GIMP_HELP_GRADIENT_EDITOR_COLORING } +}; + +static const GimpEnumActionEntry gradient_editor_zoom_actions[] = +{ + { "gradient-editor-zoom-in", GIMP_ICON_ZOOM_IN, + N_("Zoom In"), NULL, + N_("Zoom in"), + GIMP_ZOOM_IN, FALSE, + GIMP_HELP_GRADIENT_EDITOR_ZOOM_IN }, + + { "gradient-editor-zoom-out", GIMP_ICON_ZOOM_OUT, + N_("Zoom Out"), NULL, + N_("Zoom out"), + GIMP_ZOOM_OUT, FALSE, + GIMP_HELP_GRADIENT_EDITOR_ZOOM_OUT }, + + { "gradient-editor-zoom-all", GIMP_ICON_ZOOM_FIT_BEST, + N_("Zoom All"), NULL, + N_("Zoom all"), + GIMP_ZOOM_OUT_MAX, FALSE, + GIMP_HELP_GRADIENT_EDITOR_ZOOM_ALL } +}; + + +void +gradient_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "gradient-editor-action", + gradient_editor_actions, + G_N_ELEMENTS (gradient_editor_actions)); + + gimp_action_group_add_toggle_actions (group, "gradient-editor-action", + gradient_editor_toggle_actions, + G_N_ELEMENTS (gradient_editor_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "gradient-editor-action", + gradient_editor_load_left_actions, + G_N_ELEMENTS (gradient_editor_load_left_actions), + gradient_editor_load_left_cmd_callback); + + gimp_action_group_add_enum_actions (group, "gradient-editor-action", + gradient_editor_save_left_actions, + G_N_ELEMENTS (gradient_editor_save_left_actions), + gradient_editor_save_left_cmd_callback); + + gimp_action_group_add_enum_actions (group, "gradient-editor-action", + gradient_editor_load_right_actions, + G_N_ELEMENTS (gradient_editor_load_right_actions), + gradient_editor_load_right_cmd_callback); + + + gimp_action_group_add_enum_actions (group, "gradient-editor-action", + gradient_editor_save_right_actions, + G_N_ELEMENTS (gradient_editor_save_right_actions), + gradient_editor_save_right_cmd_callback); + + gimp_action_group_add_radio_actions (group, "gradient-editor-color-type", + gradient_editor_left_color_type_actions, + G_N_ELEMENTS (gradient_editor_left_color_type_actions), + NULL, + 0, + gradient_editor_left_color_type_cmd_callback); + + gimp_action_group_add_radio_actions (group, "gradient-editor-color-type", + gradient_editor_right_color_type_actions, + G_N_ELEMENTS (gradient_editor_right_color_type_actions), + NULL, + 0, + gradient_editor_right_color_type_cmd_callback); + + gimp_action_group_add_radio_actions (group, "gradient-editor-blending", + gradient_editor_blending_actions, + G_N_ELEMENTS (gradient_editor_blending_actions), + NULL, + 0, + gradient_editor_blending_func_cmd_callback); + + gimp_action_group_add_radio_actions (group, "gradient-editor-coloring", + gradient_editor_coloring_actions, + G_N_ELEMENTS (gradient_editor_coloring_actions), + NULL, + 0, + gradient_editor_coloring_type_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + gradient_editor_zoom_actions, + G_N_ELEMENTS (gradient_editor_zoom_actions), + gradient_editor_zoom_cmd_callback); +} + +void +gradient_editor_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + gboolean editable = FALSE; + GimpRGB left_color; + GimpRGB right_color; + GimpRGB left_seg_color; + GimpRGB right_seg_color; + GimpRGB fg; + GimpRGB bg; + gboolean blending_equal = TRUE; + gboolean coloring_equal = TRUE; + gboolean left_editable = TRUE; + gboolean right_editable = TRUE; + gboolean selection = FALSE; + gboolean delete = FALSE; + gboolean edit_active = FALSE; + + gradient = GIMP_GRADIENT (data_editor->data); + + if (gradient) + { + GimpGradientSegmentType type; + GimpGradientSegmentColor color; + GimpGradientSegment *left_seg; + GimpGradientSegment *right_seg; + GimpGradientSegment *seg, *aseg; + + if (data_editor->data_editable) + editable = TRUE; + + gimp_gradient_segment_get_left_flat_color (gradient, + data_editor->context, + editor->control_sel_l, + &left_color); + + if (editor->control_sel_l->prev) + left_seg = editor->control_sel_l->prev; + else + left_seg = gimp_gradient_segment_get_last (editor->control_sel_l); + + gimp_gradient_segment_get_right_flat_color (gradient, + data_editor->context, + left_seg, + &left_seg_color); + + gimp_gradient_segment_get_right_flat_color (gradient, + data_editor->context, + editor->control_sel_r, + &right_color); + + if (editor->control_sel_r->next) + right_seg = editor->control_sel_r->next; + else + right_seg = gimp_gradient_segment_get_first (editor->control_sel_r); + + gimp_gradient_segment_get_left_flat_color (gradient, + data_editor->context, + right_seg, + &right_seg_color); + + left_editable = (editor->control_sel_l->left_color_type == + GIMP_GRADIENT_COLOR_FIXED); + right_editable = (editor->control_sel_r->right_color_type == + GIMP_GRADIENT_COLOR_FIXED); + + type = editor->control_sel_l->type; + color = editor->control_sel_l->color; + + seg = editor->control_sel_l; + + do + { + blending_equal = blending_equal && (seg->type == type); + coloring_equal = coloring_equal && (seg->color == color); + + aseg = seg; + seg = seg->next; + } + while (aseg != editor->control_sel_r); + + selection = (editor->control_sel_l != editor->control_sel_r); + delete = (editor->control_sel_l->prev || editor->control_sel_r->next); + } + + if (data_editor->context) + { + gimp_context_get_foreground (data_editor->context, &fg); + gimp_context_get_background (data_editor->context, &bg); + } + + /* pretend the gradient not being editable while the dialog is + * insensitive. prevents the gradient from being modified while a + * dialog is running. bug #161411 --mitch + */ + if (! gtk_widget_is_sensitive (GTK_WIDGET (editor))) + editable = FALSE; + + if (! editable) + { + left_editable = FALSE; + right_editable = FALSE; + } + + edit_active = gimp_data_editor_get_edit_active (data_editor); + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_COLOR(action,color,set_label) \ + gimp_action_group_set_action_color (group, action, (color), (set_label)) +#define SET_LABEL(action,label) \ + gimp_action_group_set_action_label (group, action, (label)) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) + + SET_SENSITIVE ("gradient-editor-left-color-fixed", editable); + SET_SENSITIVE ("gradient-editor-left-color-foreground", editable); + SET_SENSITIVE ("gradient-editor-left-color-foreground-transparent", editable); + SET_SENSITIVE ("gradient-editor-left-color-background", editable); + SET_SENSITIVE ("gradient-editor-left-color-background-transparent", editable); + + if (gradient) + { + switch (editor->control_sel_l->left_color_type) + { + case GIMP_GRADIENT_COLOR_FIXED: + SET_ACTIVE ("gradient-editor-left-color-fixed", TRUE); + break; + case GIMP_GRADIENT_COLOR_FOREGROUND: + SET_ACTIVE ("gradient-editor-left-color-foreground", TRUE); + break; + case GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT: + SET_ACTIVE ("gradient-editor-left-color-foreground-transparent", TRUE); + break; + case GIMP_GRADIENT_COLOR_BACKGROUND: + SET_ACTIVE ("gradient-editor-left-color-background", TRUE); + break; + case GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT: + SET_ACTIVE ("gradient-editor-left-color-background-transparent", TRUE); + break; + } + } + + SET_SENSITIVE ("gradient-editor-left-color", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-left-neighbor", editable); + SET_SENSITIVE ("gradient-editor-load-left-right-endpoint", editable); + + if (gradient) + { + SET_COLOR ("gradient-editor-left-color", + &left_color, FALSE); + SET_COLOR ("gradient-editor-load-left-left-neighbor", + &left_seg_color, FALSE); + SET_COLOR ("gradient-editor-load-left-right-endpoint", + &right_color, FALSE); + } + + SET_SENSITIVE ("gradient-editor-load-left-fg", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-bg", left_editable); + + SET_COLOR ("gradient-editor-load-left-fg", + data_editor->context ? &fg : NULL, FALSE); + SET_COLOR ("gradient-editor-load-left-bg", + data_editor->context ? &bg : NULL, FALSE); + + SET_SENSITIVE ("gradient-editor-load-left-01", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-02", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-03", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-04", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-05", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-06", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-07", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-08", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-09", left_editable); + SET_SENSITIVE ("gradient-editor-load-left-10", left_editable); + + SET_COLOR ("gradient-editor-load-left-01", &editor->saved_colors[0], TRUE); + SET_COLOR ("gradient-editor-load-left-02", &editor->saved_colors[1], TRUE); + SET_COLOR ("gradient-editor-load-left-03", &editor->saved_colors[2], TRUE); + SET_COLOR ("gradient-editor-load-left-04", &editor->saved_colors[3], TRUE); + SET_COLOR ("gradient-editor-load-left-05", &editor->saved_colors[4], TRUE); + SET_COLOR ("gradient-editor-load-left-06", &editor->saved_colors[5], TRUE); + SET_COLOR ("gradient-editor-load-left-07", &editor->saved_colors[6], TRUE); + SET_COLOR ("gradient-editor-load-left-08", &editor->saved_colors[7], TRUE); + SET_COLOR ("gradient-editor-load-left-09", &editor->saved_colors[8], TRUE); + SET_COLOR ("gradient-editor-load-left-10", &editor->saved_colors[9], TRUE); + + SET_SENSITIVE ("gradient-editor-save-left-01", gradient); + SET_SENSITIVE ("gradient-editor-save-left-02", gradient); + SET_SENSITIVE ("gradient-editor-save-left-03", gradient); + SET_SENSITIVE ("gradient-editor-save-left-04", gradient); + SET_SENSITIVE ("gradient-editor-save-left-05", gradient); + SET_SENSITIVE ("gradient-editor-save-left-06", gradient); + SET_SENSITIVE ("gradient-editor-save-left-07", gradient); + SET_SENSITIVE ("gradient-editor-save-left-08", gradient); + SET_SENSITIVE ("gradient-editor-save-left-09", gradient); + SET_SENSITIVE ("gradient-editor-save-left-10", gradient); + + SET_COLOR ("gradient-editor-save-left-01", &editor->saved_colors[0], TRUE); + SET_COLOR ("gradient-editor-save-left-02", &editor->saved_colors[1], TRUE); + SET_COLOR ("gradient-editor-save-left-03", &editor->saved_colors[2], TRUE); + SET_COLOR ("gradient-editor-save-left-04", &editor->saved_colors[3], TRUE); + SET_COLOR ("gradient-editor-save-left-05", &editor->saved_colors[4], TRUE); + SET_COLOR ("gradient-editor-save-left-06", &editor->saved_colors[5], TRUE); + SET_COLOR ("gradient-editor-save-left-07", &editor->saved_colors[6], TRUE); + SET_COLOR ("gradient-editor-save-left-08", &editor->saved_colors[7], TRUE); + SET_COLOR ("gradient-editor-save-left-09", &editor->saved_colors[8], TRUE); + SET_COLOR ("gradient-editor-save-left-10", &editor->saved_colors[9], TRUE); + + SET_SENSITIVE ("gradient-editor-right-color-fixed", editable); + SET_SENSITIVE ("gradient-editor-right-color-foreground", editable); + SET_SENSITIVE ("gradient-editor-right-color-foreground-transparent", editable); + SET_SENSITIVE ("gradient-editor-right-color-background", editable); + SET_SENSITIVE ("gradient-editor-right-color-background-transparent", editable); + + if (gradient) + { + switch (editor->control_sel_r->right_color_type) + { + case GIMP_GRADIENT_COLOR_FIXED: + SET_ACTIVE ("gradient-editor-right-color-fixed", TRUE); + break; + case GIMP_GRADIENT_COLOR_FOREGROUND: + SET_ACTIVE ("gradient-editor-right-color-foreground", TRUE); + break; + case GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT: + SET_ACTIVE ("gradient-editor-right-color-foreground-transparent", TRUE); + break; + case GIMP_GRADIENT_COLOR_BACKGROUND: + SET_ACTIVE ("gradient-editor-right-color-background", TRUE); + break; + case GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT: + SET_ACTIVE ("gradient-editor-right-color-background-transparent", TRUE); + break; + } + } + + SET_SENSITIVE ("gradient-editor-right-color", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-right-neighbor", editable); + SET_SENSITIVE ("gradient-editor-load-right-left-endpoint", editable); + + if (gradient) + { + SET_COLOR ("gradient-editor-right-color", + &right_color, FALSE); + SET_COLOR ("gradient-editor-load-right-right-neighbor", + &right_seg_color, FALSE); + SET_COLOR ("gradient-editor-load-right-left-endpoint", + &left_color, FALSE); + } + + SET_SENSITIVE ("gradient-editor-load-right-fg", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-bg", right_editable); + + SET_COLOR ("gradient-editor-load-right-fg", + data_editor->context ? &fg : NULL, FALSE); + SET_COLOR ("gradient-editor-load-right-bg", + data_editor->context ? &bg : NULL, FALSE); + + SET_SENSITIVE ("gradient-editor-load-right-01", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-02", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-03", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-04", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-05", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-06", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-07", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-08", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-09", right_editable); + SET_SENSITIVE ("gradient-editor-load-right-10", right_editable); + + SET_COLOR ("gradient-editor-load-right-01", &editor->saved_colors[0], TRUE); + SET_COLOR ("gradient-editor-load-right-02", &editor->saved_colors[1], TRUE); + SET_COLOR ("gradient-editor-load-right-03", &editor->saved_colors[2], TRUE); + SET_COLOR ("gradient-editor-load-right-04", &editor->saved_colors[3], TRUE); + SET_COLOR ("gradient-editor-load-right-05", &editor->saved_colors[4], TRUE); + SET_COLOR ("gradient-editor-load-right-06", &editor->saved_colors[5], TRUE); + SET_COLOR ("gradient-editor-load-right-07", &editor->saved_colors[6], TRUE); + SET_COLOR ("gradient-editor-load-right-08", &editor->saved_colors[7], TRUE); + SET_COLOR ("gradient-editor-load-right-09", &editor->saved_colors[8], TRUE); + SET_COLOR ("gradient-editor-load-right-10", &editor->saved_colors[9], TRUE); + + SET_SENSITIVE ("gradient-editor-save-right-01", gradient); + SET_SENSITIVE ("gradient-editor-save-right-02", gradient); + SET_SENSITIVE ("gradient-editor-save-right-03", gradient); + SET_SENSITIVE ("gradient-editor-save-right-04", gradient); + SET_SENSITIVE ("gradient-editor-save-right-05", gradient); + SET_SENSITIVE ("gradient-editor-save-right-06", gradient); + SET_SENSITIVE ("gradient-editor-save-right-07", gradient); + SET_SENSITIVE ("gradient-editor-save-right-08", gradient); + SET_SENSITIVE ("gradient-editor-save-right-09", gradient); + SET_SENSITIVE ("gradient-editor-save-right-10", gradient); + + SET_COLOR ("gradient-editor-save-right-01", &editor->saved_colors[0], TRUE); + SET_COLOR ("gradient-editor-save-right-02", &editor->saved_colors[1], TRUE); + SET_COLOR ("gradient-editor-save-right-03", &editor->saved_colors[2], TRUE); + SET_COLOR ("gradient-editor-save-right-04", &editor->saved_colors[3], TRUE); + SET_COLOR ("gradient-editor-save-right-05", &editor->saved_colors[4], TRUE); + SET_COLOR ("gradient-editor-save-right-06", &editor->saved_colors[5], TRUE); + SET_COLOR ("gradient-editor-save-right-07", &editor->saved_colors[6], TRUE); + SET_COLOR ("gradient-editor-save-right-08", &editor->saved_colors[7], TRUE); + SET_COLOR ("gradient-editor-save-right-09", &editor->saved_colors[8], TRUE); + SET_COLOR ("gradient-editor-save-right-10", &editor->saved_colors[9], TRUE); + + SET_SENSITIVE ("gradient-editor-flip", editable); + SET_SENSITIVE ("gradient-editor-replicate", editable); + SET_SENSITIVE ("gradient-editor-split-midpoint", editable); + SET_SENSITIVE ("gradient-editor-split-uniform", editable); + SET_SENSITIVE ("gradient-editor-delete", editable && delete); + SET_SENSITIVE ("gradient-editor-recenter", editable); + SET_SENSITIVE ("gradient-editor-redistribute", editable); + + if (! selection) + { + SET_LABEL ("gradient-editor-blending-func", + _("_Blending Function for Segment")); + SET_LABEL ("gradient-editor-coloring-type", + _("Coloring _Type for Segment")); + + SET_LABEL ("gradient-editor-flip", + _("_Flip Segment")); + SET_LABEL ("gradient-editor-replicate", + _("_Replicate Segment...")); + SET_LABEL ("gradient-editor-split-midpoint", + _("Split Segment at _Midpoint")); + SET_LABEL ("gradient-editor-split-uniform", + _("Split Segment _Uniformly...")); + SET_LABEL ("gradient-editor-delete", + _("_Delete Segment")); + SET_LABEL ("gradient-editor-recenter", + _("Re-_center Segment's Midpoint")); + SET_LABEL ("gradient-editor-redistribute", + _("Re-distribute _Handles in Segment")); + } + else + { + SET_LABEL ("gradient-editor-blending-func", + _("_Blending Function for Selection")); + SET_LABEL ("gradient-editor-coloring-type", + _("Coloring _Type for Selection")); + + SET_LABEL ("gradient-editor-flip", + _("_Flip Selection")); + SET_LABEL ("gradient-editor-replicate", + _("_Replicate Selection...")); + SET_LABEL ("gradient-editor-split-midpoint", + _("Split Segments at _Midpoints")); + SET_LABEL ("gradient-editor-split-uniform", + _("Split Segments _Uniformly...")); + SET_LABEL ("gradient-editor-delete", + _("_Delete Selection")); + SET_LABEL ("gradient-editor-recenter", + _("Re-_center Midpoints in Selection")); + SET_LABEL ("gradient-editor-redistribute", + _("Re-distribute _Handles in Selection")); + } + + SET_SENSITIVE ("gradient-editor-blending-varies", FALSE); + SET_VISIBLE ("gradient-editor-blending-varies", ! blending_equal); + + SET_SENSITIVE ("gradient-editor-blending-linear", editable); + SET_SENSITIVE ("gradient-editor-blending-curved", editable); + SET_SENSITIVE ("gradient-editor-blending-sine", editable); + SET_SENSITIVE ("gradient-editor-blending-sphere-increasing", editable); + SET_SENSITIVE ("gradient-editor-blending-sphere-decreasing", editable); + SET_SENSITIVE ("gradient-editor-blending-step", editable); + + if (blending_equal && gradient) + { + switch (editor->control_sel_l->type) + { + case GIMP_GRADIENT_SEGMENT_LINEAR: + SET_ACTIVE ("gradient-editor-blending-linear", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_CURVED: + SET_ACTIVE ("gradient-editor-blending-curved", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_SINE: + SET_ACTIVE ("gradient-editor-blending-sine", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING: + SET_ACTIVE ("gradient-editor-blending-sphere-increasing", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING: + SET_ACTIVE ("gradient-editor-blending-sphere-decreasing", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_STEP: + SET_ACTIVE ("gradient-editor-blending-step", TRUE); + break; + } + } + else + { + SET_ACTIVE ("gradient-editor-blending-varies", TRUE); + } + + SET_SENSITIVE ("gradient-editor-coloring-varies", FALSE); + SET_VISIBLE ("gradient-editor-coloring-varies", ! coloring_equal); + + SET_SENSITIVE ("gradient-editor-coloring-rgb", editable); + SET_SENSITIVE ("gradient-editor-coloring-hsv-ccw", editable); + SET_SENSITIVE ("gradient-editor-coloring-hsv-cw", editable); + + if (coloring_equal && gradient) + { + switch (editor->control_sel_l->color) + { + case GIMP_GRADIENT_SEGMENT_RGB: + SET_ACTIVE ("gradient-editor-coloring-rgb", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_HSV_CCW: + SET_ACTIVE ("gradient-editor-coloring-hsv-ccw", TRUE); + break; + case GIMP_GRADIENT_SEGMENT_HSV_CW: + SET_ACTIVE ("gradient-editor-coloring-hsv-cw", TRUE); + break; + } + } + else + { + SET_ACTIVE ("gradient-editor-coloring-varies", TRUE); + } + + SET_SENSITIVE ("gradient-editor-blend-color", editable && selection); + SET_SENSITIVE ("gradient-editor-blend-opacity", editable && selection); + + SET_SENSITIVE ("gradient-editor-zoom-out", gradient); + SET_SENSITIVE ("gradient-editor-zoom-in", gradient); + SET_SENSITIVE ("gradient-editor-zoom-all", gradient); + + SET_ACTIVE ("gradient-editor-edit-active", edit_active); + +#undef SET_ACTIVE +#undef SET_COLOR +#undef SET_LABEL +#undef SET_SENSITIVE +#undef SET_VISIBLE +} diff --git a/app/actions/gradient-editor-actions.h b/app/actions/gradient-editor-actions.h new file mode 100644 index 0000000..5c2f999 --- /dev/null +++ b/app/actions/gradient-editor-actions.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 . + */ + +#ifndef __GRADIENT_EDITOR_ACTIONS_H__ +#define __GRADIENT_EDITOR_ACTIONS_H__ + + +void gradient_editor_actions_setup (GimpActionGroup *group); +void gradient_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __GRADIENT_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/gradient-editor-commands.c b/app/actions/gradient-editor-commands.c new file mode 100644 index 0000000..fa3709f --- /dev/null +++ b/app/actions/gradient-editor-commands.c @@ -0,0 +1,738 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpgradient.h" + +#include "widgets/gimpgradienteditor.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpviewabledialog.h" + +#include "gradient-editor-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gradient_editor_split_uniform_response (GtkWidget *widget, + gint response_id, + GimpGradientEditor *editor); +static void gradient_editor_replicate_response (GtkWidget *widget, + gint response_id, + GimpGradientEditor *editor); + + +/* public functions */ + +void +gradient_editor_left_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + + gimp_gradient_editor_edit_left_color (editor); +} + +void +gradient_editor_left_color_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientColor color_type; + + color_type = (GimpGradientColor) g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, NULL); + + if (gradient && + color_type >= 0 && + color_type != + gimp_gradient_segment_get_left_color_type (gradient, left)) + { + GimpRGB color; + + gimp_gradient_segment_get_left_flat_color (gradient, + GIMP_DATA_EDITOR (editor)->context, + left, &color); + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_set_left_color_type (gradient, left, color_type); + + if (color_type == GIMP_GRADIENT_COLOR_FIXED) + gimp_gradient_segment_set_left_color (gradient, left, &color); + + gimp_data_thaw (GIMP_DATA (gradient)); + } +} + +void +gradient_editor_load_left_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GimpGradientSegment *seg; + GimpRGB color; + GimpGradientColor color_type = GIMP_GRADIENT_COLOR_FIXED; + gint index = g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + switch (index) + { + case GRADIENT_EDITOR_COLOR_NEIGHBOR_ENDPOINT: + if (left->prev != NULL) + seg = left->prev; + else + seg = gimp_gradient_segment_get_last (left); + + color = seg->right_color; + color_type = seg->right_color_type; + break; + + case GRADIENT_EDITOR_COLOR_OTHER_ENDPOINT: + color = right->right_color; + color_type = right->right_color_type; + break; + + case GRADIENT_EDITOR_COLOR_FOREGROUND: + gimp_context_get_foreground (data_editor->context, &color); + break; + + case GRADIENT_EDITOR_COLOR_BACKGROUND: + gimp_context_get_background (data_editor->context, &color); + break; + + default: /* Load a color */ + color = editor->saved_colors[index - GRADIENT_EDITOR_COLOR_FIRST_CUSTOM]; + break; + } + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_range_blend (gradient, left, right, + &color, + &right->right_color, + TRUE, TRUE); + gimp_gradient_segment_set_left_color_type (gradient, left, color_type); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gradient_editor_save_left_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + gint index = g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, NULL); + + gimp_gradient_segment_get_left_color (gradient, left, + &editor->saved_colors[index]); +} + +void +gradient_editor_right_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + + gimp_gradient_editor_edit_right_color (editor); +} + +void +gradient_editor_right_color_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *right; + GimpGradientColor color_type; + + color_type = (GimpGradientColor) g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, NULL, &right); + + if (gradient && + color_type >= 0 && + color_type != + gimp_gradient_segment_get_right_color_type (gradient, right)) + { + GimpRGB color; + + gimp_gradient_segment_get_right_flat_color (gradient, + GIMP_DATA_EDITOR (editor)->context, + right, &color); + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_set_right_color_type (gradient, right, color_type); + + if (color_type == GIMP_GRADIENT_COLOR_FIXED) + gimp_gradient_segment_set_right_color (gradient, right, &color); + + gimp_data_thaw (GIMP_DATA (gradient)); + } +} + +void +gradient_editor_load_right_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GimpGradientSegment *seg; + GimpRGB color; + GimpGradientColor color_type = GIMP_GRADIENT_COLOR_FIXED; + gint index = g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + switch (index) + { + case GRADIENT_EDITOR_COLOR_NEIGHBOR_ENDPOINT: + if (right->next != NULL) + seg = right->next; + else + seg = gimp_gradient_segment_get_first (right); + + color = seg->left_color; + color_type = seg->left_color_type; + break; + + case GRADIENT_EDITOR_COLOR_OTHER_ENDPOINT: + color = left->left_color; + color_type = left->left_color_type; + break; + + case GRADIENT_EDITOR_COLOR_FOREGROUND: + gimp_context_get_foreground (data_editor->context, &color); + break; + + case GRADIENT_EDITOR_COLOR_BACKGROUND: + gimp_context_get_background (data_editor->context, &color); + break; + + default: /* Load a color */ + color = editor->saved_colors[index - GRADIENT_EDITOR_COLOR_FIRST_CUSTOM]; + break; + } + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_range_blend (gradient, left, right, + &left->left_color, + &color, + TRUE, TRUE); + gimp_gradient_segment_set_right_color_type (gradient, left, color_type); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gradient_editor_save_right_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *right; + gint index = g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, NULL, &right); + + gimp_gradient_segment_get_right_color (gradient, right, + &editor->saved_colors[index]); +} + +void +gradient_editor_blending_func_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GEnumClass *enum_class = NULL; + GimpGradientSegmentType type; + + type = (GimpGradientSegmentType) g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + enum_class = g_type_class_ref (GIMP_TYPE_GRADIENT_SEGMENT_TYPE); + + if (gradient && g_enum_get_value (enum_class, type)) + { + gimp_gradient_segment_range_set_blending_function (gradient, + left, right, + type); + } + + g_type_class_unref (enum_class); +} + +void +gradient_editor_coloring_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GEnumClass *enum_class = NULL; + GimpGradientSegmentColor color; + + color = (GimpGradientSegmentColor) g_variant_get_int32 (value); + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + enum_class = g_type_class_ref (GIMP_TYPE_GRADIENT_SEGMENT_COLOR); + + if (gradient && g_enum_get_value (enum_class, color)) + { + gimp_gradient_segment_range_set_coloring_type (gradient, + left, right, + color); + } + + g_type_class_unref (enum_class); +} + +void +gradient_editor_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_flip (gradient, + left, right, + &left, &right); + + gimp_gradient_editor_set_selection (editor, left, right); +} + +void +gradient_editor_replicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *scale; + GtkAdjustment *scale_data; + const gchar *title; + const gchar *desc; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + if (left == right) + { + title = _("Replicate Segment"); + desc = _("Replicate Gradient Segment"); + } + else + { + title = _("Replicate Selection"); + desc = _("Replicate Gradient Selection"); + } + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (gradient), + data_editor->context, + title, + "gimp-gradient-segment-replicate", + GIMP_ICON_GRADIENT, desc, + GTK_WIDGET (editor), + gimp_standard_help_func, + GIMP_HELP_GRADIENT_EDITOR_REPLICATE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Replicate"), 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 (gradient_editor_replicate_response), + editor); + + 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); + + /* Instructions */ + if (left == right) + label = gtk_label_new (_("Select the number of times\n" + "to replicate the selected segment.")); + else + label = gtk_label_new (_("Select the number of times\n" + "to replicate the selection.")); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + /* Scale */ + scale_data = GTK_ADJUSTMENT (gtk_adjustment_new (2.0, 2.0, 21.0, 1.0, 1.0, 1.0)); + + scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_data); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); + gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, TRUE, 4); + gtk_widget_show (scale); + + g_object_set_data (G_OBJECT (dialog), "adjustment", scale_data); + + gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE); + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); + + gtk_widget_show (dialog); +} + +void +gradient_editor_split_midpoint_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_split_midpoint (gradient, + data_editor->context, + left, right, + editor->blend_color_space, + &left, &right); + + gimp_gradient_editor_set_selection (editor, left, right); +} + +void +gradient_editor_split_uniformly_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *scale; + GtkAdjustment *scale_data; + const gchar *title; + const gchar *desc; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + if (left == right) + { + title = _("Split Segment Uniformly"); + desc = _("Split Gradient Segment Uniformly"); + } + else + { + title = _("Split Segments Uniformly"); + desc = _("Split Gradient Segments Uniformly"); + } + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (gradient), + data_editor->context, + title, + "gimp-gradient-segment-split-uniformly", + GIMP_ICON_GRADIENT, desc, + GTK_WIDGET (editor), + gimp_standard_help_func, + GIMP_HELP_GRADIENT_EDITOR_SPLIT_UNIFORM, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Split"), 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 (gradient_editor_split_uniform_response), + editor); + + /* 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 (dialog))), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + /* Instructions */ + if (left == right) + label = gtk_label_new (_("Select the number of uniform parts\n" + "in which to split the selected segment.")); + else + label = gtk_label_new (_("Select the number of uniform parts\n" + "in which to split the segments in the selection.")); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + /* Scale */ + scale_data = GTK_ADJUSTMENT (gtk_adjustment_new (2.0, 2.0, 21.0, 1.0, 1.0, 1.0)); + + scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_data); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); + gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 4); + gtk_widget_show (scale); + + g_object_set_data (G_OBJECT (dialog), "adjustment", scale_data); + + gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE); + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); + + gtk_widget_show (dialog); +} + +void +gradient_editor_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_delete (gradient, + left, right, + &left, &right); + + gimp_gradient_editor_set_selection (editor, left, right); +} + +void +gradient_editor_recenter_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_recenter_handles (gradient, left, right); +} + +void +gradient_editor_redistribute_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_redistribute_handles (gradient, left, right); +} + +void +gradient_editor_blend_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_blend (gradient, left, right, + &left->left_color, + &right->right_color, + TRUE, FALSE); +} + +void +gradient_editor_blend_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_blend (gradient, left, right, + &left->left_color, + &right->right_color, + FALSE, TRUE); +} + +void +gradient_editor_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); + GimpZoomType zoom_type = (GimpZoomType) g_variant_get_int32 (value); + + gimp_gradient_editor_zoom (editor, zoom_type); +} + + +/* private functions */ + +static void +gradient_editor_split_uniform_response (GtkWidget *widget, + gint response_id, + GimpGradientEditor *editor) +{ + GtkAdjustment *adjustment; + gint split_parts; + + adjustment = g_object_get_data (G_OBJECT (widget), "adjustment"); + + split_parts = RINT (gtk_adjustment_get_value (adjustment)); + + gtk_widget_destroy (widget); + gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE); + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); + + if (response_id == GTK_RESPONSE_OK) + { + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor); + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_split_uniform (gradient, + data_editor->context, + left, right, + split_parts, + editor->blend_color_space, + &left, &right); + + gimp_gradient_editor_set_selection (editor, left, right); + } +} + +static void +gradient_editor_replicate_response (GtkWidget *widget, + gint response_id, + GimpGradientEditor *editor) +{ + GtkAdjustment *adjustment; + gint replicate_times; + + adjustment = g_object_get_data (G_OBJECT (widget), "adjustment"); + + replicate_times = RINT (gtk_adjustment_get_value (adjustment)); + + gtk_widget_destroy (widget); + gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE); + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); + + if (response_id == GTK_RESPONSE_OK) + { + GimpGradient *gradient; + GimpGradientSegment *left; + GimpGradientSegment *right; + + gimp_gradient_editor_get_selection (editor, &gradient, &left, &right); + + gimp_gradient_segment_range_replicate (gradient, + left, right, + replicate_times, + &left, &right); + + gimp_gradient_editor_set_selection (editor, left, right); + } +} diff --git a/app/actions/gradient-editor-commands.h b/app/actions/gradient-editor-commands.h new file mode 100644 index 0000000..4f930ea --- /dev/null +++ b/app/actions/gradient-editor-commands.h @@ -0,0 +1,99 @@ +/* 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 . + */ + +#ifndef __GRADIENT_EDITOR_COMMANDS_H__ +#define __GRADIENT_EDITOR_COMMANDS_H__ + + +enum +{ + GRADIENT_EDITOR_COLOR_NEIGHBOR_ENDPOINT, + GRADIENT_EDITOR_COLOR_OTHER_ENDPOINT, + GRADIENT_EDITOR_COLOR_FOREGROUND, + GRADIENT_EDITOR_COLOR_BACKGROUND, + GRADIENT_EDITOR_COLOR_FIRST_CUSTOM +}; + + +void gradient_editor_left_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_left_color_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_load_left_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_save_left_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void gradient_editor_right_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_right_color_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_load_right_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_save_right_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void gradient_editor_blending_func_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_coloring_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void gradient_editor_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_replicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_split_midpoint_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_split_uniformly_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_recenter_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_redistribute_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void gradient_editor_blend_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void gradient_editor_blend_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void gradient_editor_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __GRADIENT_EDITOR_COMMANDS_H__ */ diff --git a/app/actions/gradients-actions.c b/app/actions/gradients-actions.c new file mode 100644 index 0000000..f4b22ef --- /dev/null +++ b/app/actions/gradients-actions.c @@ -0,0 +1,150 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdata.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "gradients-actions.h" +#include "gradients-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry gradients_actions[] = +{ + { "gradients-popup", GIMP_ICON_GRADIENT, + NC_("gradients-action", "Gradients Menu"), NULL, NULL, NULL, + GIMP_HELP_GRADIENT_DIALOG }, + + { "gradients-new", GIMP_ICON_DOCUMENT_NEW, + NC_("gradients-action", "_New Gradient"), NULL, + NC_("gradients-action", "Create a new gradient"), + data_new_cmd_callback, + GIMP_HELP_GRADIENT_NEW }, + + { "gradients-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("gradients-action", "D_uplicate Gradient"), NULL, + NC_("gradients-action", "Duplicate this gradient"), + data_duplicate_cmd_callback, + GIMP_HELP_GRADIENT_DUPLICATE }, + + { "gradients-copy-location", GIMP_ICON_EDIT_COPY, + NC_("gradients-action", "Copy Gradient _Location"), NULL, + NC_("gradients-action", "Copy gradient file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_GRADIENT_COPY_LOCATION }, + + { "gradients-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("gradients-action", "Show in _File Manager"), NULL, + NC_("gradients-action", "Show gradient file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_GRADIENT_SHOW_IN_FILE_MANAGER }, + + { "gradients-save-as-pov", GIMP_ICON_DOCUMENT_SAVE_AS, + NC_("gradients-action", "Save as _POV-Ray..."), NULL, + NC_("gradients-action", "Save gradient as POV-Ray"), + gradients_save_as_pov_ray_cmd_callback, + GIMP_HELP_GRADIENT_SAVE_AS_POV }, + + { "gradients-delete", GIMP_ICON_EDIT_DELETE, + NC_("gradients-action", "_Delete Gradient"), NULL, + NC_("gradients-action", "Delete this gradient"), + data_delete_cmd_callback, + GIMP_HELP_GRADIENT_DELETE }, + + { "gradients-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("gradients-action", "_Refresh Gradients"), NULL, + NC_("gradients-action", "Refresh gradients"), + data_refresh_cmd_callback, + GIMP_HELP_GRADIENT_REFRESH } +}; + +static const GimpStringActionEntry gradients_edit_actions[] = +{ + { "gradients-edit", GIMP_ICON_EDIT, + NC_("gradients-action", "_Edit Gradient..."), NULL, + NC_("gradients-action", "Edit this gradient"), + "gimp-gradient-editor", + GIMP_HELP_GRADIENT_EDIT } +}; + + +void +gradients_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "gradients-action", + gradients_actions, + G_N_ELEMENTS (gradients_actions)); + + gimp_action_group_add_string_actions (group, "gradients-action", + gradients_edit_actions, + G_N_ELEMENTS (gradients_edit_actions), + data_edit_cmd_callback); +} + +void +gradients_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpGradient *gradient = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + gradient = gimp_context_get_gradient (context); + + if (action_data_sel_count (user_data) > 1) + { + gradient = NULL; + } + + if (gradient) + { + data = GIMP_DATA (gradient); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("gradients-edit", gradient); + SET_SENSITIVE ("gradients-duplicate", gradient); + SET_SENSITIVE ("gradients-save-as-pov", gradient); + SET_SENSITIVE ("gradients-copy-location", file); + SET_SENSITIVE ("gradients-show-in-file-manager", file); + SET_SENSITIVE ("gradients-delete", gradient && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/gradients-actions.h b/app/actions/gradients-actions.h new file mode 100644 index 0000000..c8ee1ce --- /dev/null +++ b/app/actions/gradients-actions.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 . + */ + +#ifndef __GRADIENTS_ACTIONS_H__ +#define __GRADIENTS_ACTIONS_H__ + + +void gradients_actions_setup (GimpActionGroup *group); +void gradients_actions_update (GimpActionGroup *group, + gpointer user_data); + + +#endif /* __GRADIENT_ACTIONS_H__ */ diff --git a/app/actions/gradients-commands.c b/app/actions/gradients-commands.c new file mode 100644 index 0000000..40b71fd --- /dev/null +++ b/app/actions/gradients-commands.c @@ -0,0 +1,151 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpgradient-save.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpcontainereditor.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimphelp-ids.h" + +#include "dialogs/dialogs.h" + +#include "gradients-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gradients_save_as_pov_ray_response (GtkWidget *dialog, + gint response_id, + GimpGradient *gradient); + + +/* public functions */ + +void +gradients_save_as_pov_ray_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpGradient *gradient; + GtkWidget *dialog; + + context = gimp_container_view_get_context (editor->view); + gradient = gimp_context_get_gradient (context); + + if (! gradient) + return; + +#define SAVE_AS_POV_DIALOG_KEY "gimp-save-as-pov-ray-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (gradient), SAVE_AS_POV_DIALOG_KEY); + + if (! dialog) + { + gchar *title = g_strdup_printf (_("Save '%s' as POV-Ray"), + gimp_object_get_name (gradient)); + + dialog = gtk_file_chooser_dialog_new (title, NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_OK, + + NULL); + + g_free (title); + + 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_object_set_data (G_OBJECT (dialog), "gimp", context->gimp); + + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (GTK_WIDGET (editor))); + gtk_window_set_role (GTK_WINDOW (dialog), "gimp-gradient-save-pov"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), + TRUE); + + g_signal_connect (dialog, "response", + G_CALLBACK (gradients_save_as_pov_ray_response), + gradient); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + g_signal_connect_object (dialog, "destroy", + G_CALLBACK (g_object_unref), + g_object_ref (gradient), + G_CONNECT_SWAPPED); + + gimp_help_connect (dialog, gimp_standard_help_func, + GIMP_HELP_GRADIENT_SAVE_AS_POV, NULL); + + dialogs_attach_dialog (G_OBJECT (gradient), + SAVE_AS_POV_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + + +/* private functions */ + +static void +gradients_save_as_pov_ray_response (GtkWidget *dialog, + gint response_id, + GimpGradient *gradient) +{ + if (response_id == GTK_RESPONSE_OK) + { + GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + GError *error = NULL; + + if (! gimp_gradient_save_pov (gradient, file, &error)) + { + gimp_message_literal (g_object_get_data (G_OBJECT (dialog), "gimp"), + G_OBJECT (dialog), GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_object_unref (file); + } + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/gradients-commands.h b/app/actions/gradients-commands.h new file mode 100644 index 0000000..938f3ff --- /dev/null +++ b/app/actions/gradients-commands.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 . + */ + +#ifndef __GRADIENTS_COMMANDS_H__ +#define __GRADIENTS_COMMANDS_H__ + + +void gradients_save_as_pov_ray_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __GRADIENTS_COMMANDS_H__ */ diff --git a/app/actions/help-actions.c b/app/actions/help-actions.c new file mode 100644 index 0000000..6b1502a --- /dev/null +++ b/app/actions/help-actions.c @@ -0,0 +1,66 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "help-actions.h" +#include "help-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry help_actions[] = +{ + { "help-menu", NULL, NC_("help-action", "_Help") }, + + { "help-help", "gimp-prefs-help-system", + NC_("help-action", "_Help"), "F1", + NC_("help-action", "Open the GIMP user manual"), + help_help_cmd_callback, + GIMP_HELP_HELP }, + + { "help-context-help", "gimp-prefs-help-system", + NC_("help-action", "_Context Help"), "F1", + NC_("help-action", "Show the help for a specific user interface item"), + help_context_help_cmd_callback, + GIMP_HELP_HELP_CONTEXT } +}; + + +void +help_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "help-action", + help_actions, + G_N_ELEMENTS (help_actions)); +} + +void +help_actions_update (GimpActionGroup *group, + gpointer data) +{ +} diff --git a/app/actions/help-actions.h b/app/actions/help-actions.h new file mode 100644 index 0000000..970dc97 --- /dev/null +++ b/app/actions/help-actions.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 . + */ + +#ifndef __HELP_ACTIONS_H__ +#define __HELP_ACTIONS_H__ + + +void help_actions_setup (GimpActionGroup *group); +void help_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __HELP_ACTIONS_H__ */ diff --git a/app/actions/help-commands.c b/app/actions/help-commands.c new file mode 100644 index 0000000..9e9693a --- /dev/null +++ b/app/actions/help-commands.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpprogress.h" + +#include "widgets/gimphelp.h" + +#include "actions.h" +#include "help-commands.h" + + +void +help_help_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplay *display; + return_if_no_gimp (gimp, data); + return_if_no_display (display, data); + + gimp_help_show (gimp, GIMP_PROGRESS (display), NULL, NULL); +} + +void +help_context_help_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + return_if_no_widget (widget, data); + + gimp_context_help (widget); +} diff --git a/app/actions/help-commands.h b/app/actions/help-commands.h new file mode 100644 index 0000000..61ce201 --- /dev/null +++ b/app/actions/help-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __HELP_COMMANDS_H__ +#define __HELP_COMMANDS_H__ + + +void help_help_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void help_context_help_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __HELP_COMMANDS_H__ */ diff --git a/app/actions/image-actions.c b/app/actions/image-actions.c new file mode 100644 index 0000000..213933f --- /dev/null +++ b/app/actions/image-actions.c @@ -0,0 +1,497 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpguiconfig.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-color-profile.h" +#include "core/gimpitemstack.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "image-actions.h" +#include "image-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry image_actions[] = +{ + { "image-menubar", NULL, + NC_("image-action", "Image Menu"), NULL, NULL, NULL, + GIMP_HELP_IMAGE_WINDOW }, + + { "image-popup", NULL, + NC_("image-action", "Image Menu"), NULL, NULL, NULL, + GIMP_HELP_IMAGE_WINDOW }, + + { "image-menu", NULL, NC_("image-action", "_Image") }, + { "image-mode-menu", NULL, NC_("image-action", "_Mode") }, + { "image-precision-menu", NULL, NC_("image-action", "Pr_ecision") }, +#if PENDING_TRANSLATION + { "image-precision-menu", NULL, NC_("image-action", "_Encoding") }, +#endif + { "image-color-management-menu", NULL, NC_("image-action", + "Color Ma_nagement") }, + { "image-transform-menu", NULL, NC_("image-action", "_Transform") }, + { "image-guides-menu", NULL, NC_("image-action", "_Guides") }, + { "image-metadata-menu", NULL, NC_("image-action", "Meta_data") }, + + { "colors-menu", NULL, NC_("image-action", "_Colors") }, + { "colors-info-menu", NULL, NC_("image-action", "I_nfo") }, + { "colors-auto-menu", NULL, NC_("image-action", "_Auto") }, + { "colors-map-menu", NULL, NC_("image-action", "_Map") }, + { "colors-tone-mapping-menu", NULL, NC_("image-action", "_Tone Mapping") }, + { "colors-components-menu", NULL, NC_("image-action", "C_omponents") }, + { "colors-desaturate-menu", NULL, NC_("image-action", "D_esaturate") }, + + { "image-new", GIMP_ICON_DOCUMENT_NEW, + NC_("image-action", "_New..."), "N", + NC_("image-action", "Create a new image"), + image_new_cmd_callback, + GIMP_HELP_FILE_NEW }, + + { "image-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("image-action", "_Duplicate"), "D", + NC_("image-action", "Create a duplicate of this image"), + image_duplicate_cmd_callback, + GIMP_HELP_IMAGE_DUPLICATE }, + + { "image-color-profile-assign", NULL, + NC_("image-action", "_Assign Color Profile..."), NULL, + NC_("image-action", "Set a color profile on the image"), + image_color_profile_assign_cmd_callback, + GIMP_HELP_IMAGE_COLOR_PROFILE_ASSIGN }, + + { "image-color-profile-convert", NULL, + NC_("image-action", "_Convert to Color Profile..."), NULL, + NC_("image-action", "Apply a color profile to the image"), + image_color_profile_convert_cmd_callback, + GIMP_HELP_IMAGE_COLOR_PROFILE_CONVERT }, + + { "image-color-profile-discard", NULL, + NC_("image-action", "_Discard Color Profile"), NULL, + NC_("image-action", "Remove the image's color profile"), + image_color_profile_discard_cmd_callback, + GIMP_HELP_IMAGE_COLOR_PROFILE_DISCARD }, + + { "image-color-profile-save", NULL, + NC_("image-action", "_Save Color Profile to File..."), NULL, + NC_("image-action", "Save the image's color profile to an ICC file"), + image_color_profile_save_cmd_callback, + GIMP_HELP_IMAGE_COLOR_PROFILE_SAVE }, + + { "image-resize", GIMP_ICON_OBJECT_RESIZE, + NC_("image-action", "Can_vas Size..."), NULL, + NC_("image-action", "Adjust the image dimensions"), + image_resize_cmd_callback, + GIMP_HELP_IMAGE_RESIZE }, + + { "image-resize-to-layers", NULL, + NC_("image-action", "Fit Canvas to L_ayers"), NULL, + NC_("image-action", "Resize the image to enclose all layers"), + image_resize_to_layers_cmd_callback, + GIMP_HELP_IMAGE_RESIZE_TO_LAYERS }, + + { "image-resize-to-selection", NULL, + NC_("image-action", "F_it Canvas to Selection"), NULL, + NC_("image-action", "Resize the image to the extents of the selection"), + image_resize_to_selection_cmd_callback, + GIMP_HELP_IMAGE_RESIZE_TO_SELECTION }, + + { "image-print-size", GIMP_ICON_DOCUMENT_PRINT_RESOLUTION, + NC_("image-action", "_Print Size..."), NULL, + NC_("image-action", "Adjust the print resolution"), + image_print_size_cmd_callback, + GIMP_HELP_IMAGE_PRINT_SIZE }, + + { "image-scale", GIMP_ICON_OBJECT_SCALE, + NC_("image-action", "_Scale Image..."), NULL, + NC_("image-action", "Change the size of the image content"), + image_scale_cmd_callback, + GIMP_HELP_IMAGE_SCALE }, + + { "image-crop-to-selection", GIMP_ICON_TOOL_CROP, + NC_("image-action", "_Crop to Selection"), NULL, + NC_("image-action", "Crop the image to the extents of the selection"), + image_crop_to_selection_cmd_callback, + GIMP_HELP_IMAGE_CROP }, + + { "image-crop-to-content", GIMP_ICON_TOOL_CROP, + NC_("image-action", "Crop to C_ontent"), NULL, + NC_("image-action", "Crop the image to the extents of its content (remove empty borders from the image)"), + image_crop_to_content_cmd_callback, + GIMP_HELP_IMAGE_CROP }, + + { "image-merge-layers", NULL, + NC_("image-action", "Merge Visible _Layers..."), "M", + NC_("image-action", "Merge all visible layers into one layer"), + image_merge_layers_cmd_callback, + GIMP_HELP_IMAGE_MERGE_LAYERS }, + + { "image-flatten", NULL, + NC_("image-action", "_Flatten Image"), NULL, + NC_("image-action", "Merge all layers into one and remove transparency"), + image_flatten_image_cmd_callback, + GIMP_HELP_IMAGE_FLATTEN }, + + { "image-configure-grid", GIMP_ICON_GRID, + NC_("image-action", "Configure G_rid..."), NULL, + NC_("image-action", "Configure the grid for this image"), + image_configure_grid_cmd_callback, + GIMP_HELP_IMAGE_GRID }, + + { "image-properties", "dialog-information", + NC_("image-action", "Image Pr_operties"), "Return", + NC_("image-action", "Display information about this image"), + image_properties_cmd_callback, + GIMP_HELP_IMAGE_PROPERTIES } +}; + +static const GimpToggleActionEntry image_toggle_actions[] = +{ + { "image-color-management-enabled", NULL, + NC_("image-action", "_Enable Color Management"), NULL, + NC_("image-action", "Whether the image is color managed. Disabling " + "color management is equivalent to assigning a built-in sRGB " + "color profile. Better leave color management enabled."), + image_color_management_enabled_cmd_callback, + TRUE, + GIMP_HELP_IMAGE_COLOR_MANAGEMENT_ENABLED } +}; + +static const GimpRadioActionEntry image_convert_base_type_actions[] = +{ + { "image-convert-rgb", GIMP_ICON_CONVERT_RGB, + NC_("image-convert-action", "_RGB"), NULL, + NC_("image-convert-action", "Convert the image to the RGB colorspace"), + GIMP_RGB, GIMP_HELP_IMAGE_CONVERT_RGB }, + + { "image-convert-grayscale", GIMP_ICON_CONVERT_GRAYSCALE, + NC_("image-convert-action", "_Grayscale"), NULL, + NC_("image-convert-action", "Convert the image to grayscale"), + GIMP_GRAY, GIMP_HELP_IMAGE_CONVERT_GRAYSCALE }, + + { "image-convert-indexed", GIMP_ICON_CONVERT_INDEXED, + NC_("image-convert-action", "_Indexed..."), NULL, + NC_("image-convert-action", "Convert the image to indexed colors"), + GIMP_INDEXED, GIMP_HELP_IMAGE_CONVERT_INDEXED } +}; + +static const GimpRadioActionEntry image_convert_precision_actions[] = +{ + { "image-convert-u8", NULL, + NC_("image-convert-action", "8 bit integer"), NULL, + NC_("image-convert-action", + "Convert the image to 8 bit integer"), + GIMP_COMPONENT_TYPE_U8, GIMP_HELP_IMAGE_CONVERT_U8 }, + + { "image-convert-u16", NULL, + NC_("image-convert-action", "16 bit integer"), NULL, + NC_("image-convert-action", + "Convert the image to 16 bit integer"), + GIMP_COMPONENT_TYPE_U16, GIMP_HELP_IMAGE_CONVERT_U16 }, + + { "image-convert-u32", NULL, + NC_("image-convert-action", "32 bit integer"), NULL, + NC_("image-convert-action", + "Convert the image to 32 bit integer"), + GIMP_COMPONENT_TYPE_U32, GIMP_HELP_IMAGE_CONVERT_U32 }, + + { "image-convert-half", NULL, + NC_("image-convert-action", "16 bit floating point"), NULL, + NC_("image-convert-action", + "Convert the image to 16 bit floating point"), + GIMP_COMPONENT_TYPE_HALF, GIMP_HELP_IMAGE_CONVERT_HALF }, + + { "image-convert-float", NULL, + NC_("image-convert-action", "32 bit floating point"), NULL, + NC_("image-convert-action", + "Convert the image to 32 bit floating point"), + GIMP_COMPONENT_TYPE_FLOAT, GIMP_HELP_IMAGE_CONVERT_FLOAT }, + + { "image-convert-double", NULL, + NC_("image-convert-action", "64 bit floating point"), NULL, + NC_("image-convert-action", + "Convert the image to 64 bit floating point"), + GIMP_COMPONENT_TYPE_DOUBLE, GIMP_HELP_IMAGE_CONVERT_DOUBLE } +}; + +static const GimpRadioActionEntry image_convert_gamma_actions[] = +{ + { "image-convert-gamma", NULL, + NC_("image-convert-action", "Perceptual gamma (sRGB)"), NULL, + NC_("image-convert-action", + "Convert the image to perceptual (sRGB) gamma"), + FALSE, GIMP_HELP_IMAGE_CONVERT_GAMMA }, + + { "image-convert-linear", NULL, + NC_("image-convert-action", "Linear light"), NULL, + NC_("image-convert-action", + "Convert the image to linear light"), + TRUE, GIMP_HELP_IMAGE_CONVERT_GAMMA } +}; + +static const GimpEnumActionEntry image_flip_actions[] = +{ + { "image-flip-horizontal", GIMP_ICON_OBJECT_FLIP_HORIZONTAL, + NC_("image-action", "Flip _Horizontally"), NULL, + NC_("image-action", "Flip image horizontally"), + GIMP_ORIENTATION_HORIZONTAL, FALSE, + GIMP_HELP_IMAGE_FLIP_HORIZONTAL }, + + { "image-flip-vertical", GIMP_ICON_OBJECT_FLIP_VERTICAL, + NC_("image-action", "Flip _Vertically"), NULL, + NC_("image-action", "Flip image vertically"), + GIMP_ORIENTATION_VERTICAL, FALSE, + GIMP_HELP_IMAGE_FLIP_VERTICAL } +}; + +static const GimpEnumActionEntry image_rotate_actions[] = +{ + { "image-rotate-90", GIMP_ICON_OBJECT_ROTATE_90, + NC_("image-action", "Rotate 90° _clockwise"), NULL, + NC_("image-action", "Rotate the image 90 degrees to the right"), + GIMP_ROTATE_90, FALSE, + GIMP_HELP_IMAGE_ROTATE_90 }, + + { "image-rotate-180", GIMP_ICON_OBJECT_ROTATE_180, + NC_("image-action", "Rotate _180°"), NULL, + NC_("image-action", "Turn the image upside-down"), + GIMP_ROTATE_180, FALSE, + GIMP_HELP_IMAGE_ROTATE_180 }, + + { "image-rotate-270", GIMP_ICON_OBJECT_ROTATE_270, + NC_("image-action", "Rotate 90° counter-clock_wise"), NULL, + NC_("image-action", "Rotate the image 90 degrees to the left"), + GIMP_ROTATE_270, FALSE, + GIMP_HELP_IMAGE_ROTATE_270 } +}; + + +void +image_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "image-action", + image_actions, + G_N_ELEMENTS (image_actions)); + + gimp_action_group_add_toggle_actions (group, "image-action", + image_toggle_actions, + G_N_ELEMENTS (image_toggle_actions)); + + gimp_action_group_add_radio_actions (group, "image-convert-action", + image_convert_base_type_actions, + G_N_ELEMENTS (image_convert_base_type_actions), + NULL, 0, + image_convert_base_type_cmd_callback); + + gimp_action_group_add_radio_actions (group, "image-convert-action", + image_convert_precision_actions, + G_N_ELEMENTS (image_convert_precision_actions), + NULL, 0, + image_convert_precision_cmd_callback); + + gimp_action_group_add_radio_actions (group, "image-convert-action", + image_convert_gamma_actions, + G_N_ELEMENTS (image_convert_gamma_actions), + NULL, 0, + image_convert_gamma_cmd_callback); + + gimp_action_group_add_enum_actions (group, "image-action", + image_flip_actions, + G_N_ELEMENTS (image_flip_actions), + image_flip_cmd_callback); + + gimp_action_group_add_enum_actions (group, "image-action", + image_rotate_actions, + G_N_ELEMENTS (image_rotate_actions), + image_rotate_cmd_callback); + +#define SET_ALWAYS_SHOW_IMAGE(action,show) \ + gimp_action_group_set_action_always_show_image (group, action, show) + + SET_ALWAYS_SHOW_IMAGE ("image-rotate-90", TRUE); + SET_ALWAYS_SHOW_IMAGE ("image-rotate-180", TRUE); + SET_ALWAYS_SHOW_IMAGE ("image-rotate-270", TRUE); + +#undef SET_ALWAYS_SHOW_IMAGE +} + +void +image_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + gboolean is_indexed = FALSE; + gboolean is_u8_gamma = FALSE; + gboolean is_double = FALSE; + gboolean aux = FALSE; + gboolean lp = FALSE; + gboolean sel = FALSE; + gboolean groups = FALSE; + gboolean color_managed = FALSE; + gboolean profile = FALSE; + + if (image) + { + GimpContainer *layers; + const gchar *action = NULL; + GimpImageBaseType base_type; + GimpPrecision precision; + GimpComponentType component_type; + + base_type = gimp_image_get_base_type (image); + precision = gimp_image_get_precision (image); + component_type = gimp_image_get_component_type (image); + + switch (base_type) + { + case GIMP_RGB: action = "image-convert-rgb"; break; + case GIMP_GRAY: action = "image-convert-grayscale"; break; + case GIMP_INDEXED: action = "image-convert-indexed"; break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + + switch (component_type) + { + case GIMP_COMPONENT_TYPE_U8: action = "image-convert-u8"; break; + case GIMP_COMPONENT_TYPE_U16: action = "image-convert-u16"; break; + case GIMP_COMPONENT_TYPE_U32: action = "image-convert-u32"; break; + case GIMP_COMPONENT_TYPE_HALF: action = "image-convert-half"; break; + case GIMP_COMPONENT_TYPE_FLOAT: action = "image-convert-float"; break; + case GIMP_COMPONENT_TYPE_DOUBLE: action = "image-convert-double"; break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + + if (gimp_babl_format_get_linear (gimp_image_get_layer_format (image, + FALSE))) + { + gimp_action_group_set_action_active (group, "image-convert-linear", + TRUE); + } + else + { + gimp_action_group_set_action_active (group, "image-convert-gamma", + TRUE); + } + + is_indexed = (base_type == GIMP_INDEXED); + is_u8_gamma = (precision == GIMP_PRECISION_U8_GAMMA); + is_double = (component_type == GIMP_COMPONENT_TYPE_DOUBLE); + aux = (gimp_image_get_active_channel (image) != NULL); + lp = ! gimp_image_is_empty (image); + sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); + + layers = gimp_image_get_layers (image); + + groups = ! gimp_item_stack_is_flat (GIMP_ITEM_STACK (layers)); + + color_managed = gimp_image_get_is_color_managed (image); + profile = (gimp_image_get_color_profile (image) != NULL); + } + +#define SET_LABEL(action,label) \ + gimp_action_group_set_action_label (group, action, (label)) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) + + SET_SENSITIVE ("image-duplicate", image); + + if (profile) + { + SET_LABEL ("image-convert-rgb", + C_("image-convert-action", "_RGB...")); + SET_LABEL ("image-convert-grayscale", + C_("image-convert-action", "_Grayscale...")); + } + else + { + SET_LABEL ("image-convert-rgb", + C_("image-convert-action", "_RGB")); + SET_LABEL ("image-convert-grayscale", + C_("image-convert-action", "_Grayscale")); + } + + SET_SENSITIVE ("image-convert-rgb", image); + SET_SENSITIVE ("image-convert-grayscale", image); + SET_SENSITIVE ("image-convert-indexed", image && !groups && is_u8_gamma); + + SET_SENSITIVE ("image-convert-u8", image); + SET_SENSITIVE ("image-convert-u16", image && !is_indexed); + SET_SENSITIVE ("image-convert-u32", image && !is_indexed); + SET_SENSITIVE ("image-convert-half", image && !is_indexed); + SET_SENSITIVE ("image-convert-float", image && !is_indexed); + SET_SENSITIVE ("image-convert-double", image && !is_indexed); + SET_VISIBLE ("image-convert-double", is_double); + + SET_SENSITIVE ("image-convert-gamma", image); + SET_SENSITIVE ("image-convert-linear", image && !is_indexed); + + SET_SENSITIVE ("image-color-management-enabled", image); + SET_ACTIVE ("image-color-management-enabled", image && color_managed); + + SET_SENSITIVE ("image-color-profile-assign", image); + SET_SENSITIVE ("image-color-profile-convert", image); + SET_SENSITIVE ("image-color-profile-discard", image && profile); + SET_SENSITIVE ("image-color-profile-save", image); + + SET_SENSITIVE ("image-flip-horizontal", image); + SET_SENSITIVE ("image-flip-vertical", image); + SET_SENSITIVE ("image-rotate-90", image); + SET_SENSITIVE ("image-rotate-180", image); + SET_SENSITIVE ("image-rotate-270", image); + + SET_SENSITIVE ("image-resize", image); + SET_SENSITIVE ("image-resize-to-layers", image); + SET_SENSITIVE ("image-resize-to-selection", image && sel); + SET_SENSITIVE ("image-print-size", image); + SET_SENSITIVE ("image-scale", image); + SET_SENSITIVE ("image-crop-to-selection", image && sel); + SET_SENSITIVE ("image-crop-to-content", image); + SET_SENSITIVE ("image-merge-layers", image && !aux && lp); + SET_SENSITIVE ("image-flatten", image && !aux && lp); + SET_SENSITIVE ("image-configure-grid", image); + SET_SENSITIVE ("image-properties", image); + +#undef SET_LABEL +#undef SET_SENSITIVE +#undef SET_ACTIVE +#undef SET_VISIBLE +} diff --git a/app/actions/image-actions.h b/app/actions/image-actions.h new file mode 100644 index 0000000..802f7af --- /dev/null +++ b/app/actions/image-actions.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 . + */ + +#ifndef __IMAGE_ACTIONS_H__ +#define __IMAGE_ACTIONS_H__ + + +void image_actions_setup (GimpActionGroup *group); +void image_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __IMAGE_ACTIONS_H__ */ diff --git a/app/actions/image-commands.c b/app/actions/image-commands.c new file mode 100644 index 0000000..536ee6f --- /dev/null +++ b/app/actions/image-commands.c @@ -0,0 +1,1581 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "gegl/gimp-babl.h" + +#include "core/core-enums.h" +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-color-profile.h" +#include "core/gimpimage-convert-indexed.h" +#include "core/gimpimage-convert-precision.h" +#include "core/gimpimage-convert-type.h" +#include "core/gimpimage-crop.h" +#include "core/gimpimage-duplicate.h" +#include "core/gimpimage-flip.h" +#include "core/gimpimage-merge.h" +#include "core/gimpimage-resize.h" +#include "core/gimpimage-rotate.h" +#include "core/gimpimage-scale.h" +#include "core/gimpimage-undo.h" +#include "core/gimpitem.h" +#include "core/gimppickable.h" +#include "core/gimppickable-auto-shrink.h" +#include "core/gimpprogress.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "dialogs/dialogs.h" +#include "dialogs/color-profile-dialog.h" +#include "dialogs/convert-indexed-dialog.h" +#include "dialogs/convert-precision-dialog.h" +#include "dialogs/grid-dialog.h" +#include "dialogs/image-merge-layers-dialog.h" +#include "dialogs/image-new-dialog.h" +#include "dialogs/image-properties-dialog.h" +#include "dialogs/image-scale-dialog.h" +#include "dialogs/print-size-dialog.h" +#include "dialogs/resize-dialog.h" + +#include "actions.h" +#include "image-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void image_convert_rgb_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); + +static void image_convert_gray_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); + +static void image_convert_indexed_callback (GtkWidget *dialog, + GimpImage *image, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + gpointer user_data); + +static void image_convert_precision_callback (GtkWidget *dialog, + GimpImage *image, + GimpPrecision precision, + GeglDitherMethod layer_dither_method, + GeglDitherMethod text_layer_dither_method, + GeglDitherMethod mask_dither_method, + gpointer user_data); + +static void image_profile_assign_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); + +static void image_profile_convert_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); + +static void image_resize_callback (GtkWidget *dialog, + GimpViewable *viewable, + GimpContext *context, + gint width, + gint height, + GimpUnit unit, + gint offset_x, + gint offset_y, + gdouble xres, + gdouble yres, + GimpUnit res_unit, + GimpFillType fill_type, + GimpItemSet layer_set, + gboolean resize_text_layers, + gpointer user_data); + +static void image_print_size_callback (GtkWidget *dialog, + GimpImage *image, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data); + +static void image_scale_callback (GtkWidget *dialog, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data); + +static void image_merge_layers_callback (GtkWidget *dialog, + GimpImage *image, + GimpContext *context, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + gpointer user_data); + + +/* private variables */ + +static GimpUnit image_resize_unit = GIMP_UNIT_PIXEL; +static GimpUnit image_scale_unit = GIMP_UNIT_PIXEL; +static GimpInterpolationType image_scale_interp = -1; +static GimpPalette *image_convert_indexed_custom_palette = NULL; + + +/* public functions */ + +void +image_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_widget (widget, data); + + dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + NULL /*ui_manager*/, + "gimp-image-new-dialog", -1, FALSE); + + if (dialog) + { + GimpImage *image = action_data_get_image (data); + + image_new_dialog_set (dialog, image, NULL); + + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +void +image_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GimpDisplayShell *shell; + GimpImage *new_image; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + shell = gimp_display_get_shell (display); + + new_image = gimp_image_duplicate (image); + + gimp_create_display (new_image->gimp, new_image, shell->unit, + gimp_zoom_model_get_factor (shell->zoom), + G_OBJECT (gtk_widget_get_screen (GTK_WIDGET (shell))), + gimp_widget_get_monitor (GTK_WIDGET (shell))); + + g_object_unref (new_image); +} + +void +image_convert_base_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GimpDialogConfig *config; + GtkWidget *dialog; + GimpImageBaseType base_type; + GError *error = NULL; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + base_type = (GimpImageBaseType) g_variant_get_int32 (value); + + if (base_type == gimp_image_get_base_type (image)) + return; + +#define CONVERT_TYPE_DIALOG_KEY "gimp-convert-type-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), CONVERT_TYPE_DIALOG_KEY); + + if (dialog) + { + gtk_widget_destroy (dialog); + dialog = NULL; + } + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + switch (base_type) + { + case GIMP_RGB: + case GIMP_GRAY: + if (gimp_image_get_color_profile (image)) + { + ColorProfileDialogType dialog_type; + GimpColorProfileCallback callback; + GimpColorProfile *current_profile; + GimpColorProfile *default_profile; + const Babl *format; + + current_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + if (base_type == GIMP_RGB) + { + dialog_type = COLOR_PROFILE_DIALOG_CONVERT_TO_RGB; + callback = image_convert_rgb_callback; + + format = gimp_babl_format (GIMP_RGB, + gimp_image_get_precision (image), + TRUE); + default_profile = gimp_babl_format_get_color_profile (format); + } + else + { + dialog_type = COLOR_PROFILE_DIALOG_CONVERT_TO_GRAY; + callback = image_convert_gray_callback; + + format = gimp_babl_format (GIMP_GRAY, + gimp_image_get_precision (image), + TRUE); + default_profile = gimp_babl_format_get_color_profile (format); + } + + dialog = color_profile_dialog_new (dialog_type, + image, + action_data_get_context (data), + widget, + current_profile, + default_profile, + 0, 0, + callback, + display); + } + else if (! gimp_image_convert_type (image, base_type, NULL, NULL, &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } + break; + + case GIMP_INDEXED: + dialog = convert_indexed_dialog_new (image, + action_data_get_context (data), + widget, + config->image_convert_indexed_palette_type, + config->image_convert_indexed_max_colors, + config->image_convert_indexed_remove_duplicates, + config->image_convert_indexed_dither_type, + config->image_convert_indexed_dither_alpha, + config->image_convert_indexed_dither_text_layers, + image_convert_indexed_custom_palette, + image_convert_indexed_callback, + display); + break; + } + + if (dialog) + { + dialogs_attach_dialog (G_OBJECT (image), + CONVERT_TYPE_DIALOG_KEY, dialog); + gtk_window_present (GTK_WINDOW (dialog)); + } + + /* always flush, also when only the indexed dialog was shown, so + * the menu items get updated back to the current image type + */ + gimp_image_flush (image); +} + +void +image_convert_precision_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GimpDialogConfig *config; + GtkWidget *dialog; + GimpComponentType component_type; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + component_type = (GimpComponentType) g_variant_get_int32 (value); + + if (component_type == gimp_image_get_component_type (image)) + return; + +#define CONVERT_PRECISION_DIALOG_KEY "gimp-convert-precision-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), CONVERT_PRECISION_DIALOG_KEY); + + if (dialog) + { + gtk_widget_destroy (dialog); + dialog = NULL; + } + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = convert_precision_dialog_new (image, + action_data_get_context (data), + widget, + component_type, + config->image_convert_precision_layer_dither_method, + config->image_convert_precision_text_layer_dither_method, + config->image_convert_precision_channel_dither_method, + image_convert_precision_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), + CONVERT_PRECISION_DIALOG_KEY, dialog); + + gtk_window_present (GTK_WINDOW (dialog)); + + /* see comment above */ + gimp_image_flush (image); +} + +void +image_convert_gamma_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + gboolean linear; + GimpPrecision precision; + return_if_no_image (image, data); + return_if_no_display (display, data); + + linear = (gboolean) g_variant_get_int32 (value); + + if (linear == gimp_babl_format_get_linear (gimp_image_get_layer_format (image, + FALSE))) + return; + + precision = gimp_babl_precision (gimp_image_get_component_type (image), + linear); + + gimp_image_convert_precision (image, precision, + GEGL_DITHER_NONE, + GEGL_DITHER_NONE, + GEGL_DITHER_NONE, + GIMP_PROGRESS (display)); + gimp_image_flush (image); +} + +void +image_color_management_enabled_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + gboolean enabled; + return_if_no_image (image, data); + + enabled = g_variant_get_boolean (value); + + if (enabled != gimp_image_get_is_color_managed (image)) + { + gimp_image_set_is_color_managed (image, enabled, TRUE); + gimp_image_flush (image); + } +} + +void +image_color_profile_assign_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + +#define PROFILE_ASSIGN_DIALOG_KEY "gimp-profile-assign-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), PROFILE_ASSIGN_DIALOG_KEY); + + if (! dialog) + { + GimpColorProfile *current_profile; + GimpColorProfile *default_profile; + + current_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + default_profile = gimp_image_get_builtin_color_profile (image); + + dialog = color_profile_dialog_new (COLOR_PROFILE_DIALOG_ASSIGN_PROFILE, + image, + action_data_get_context (data), + widget, + current_profile, + default_profile, + 0, 0, + image_profile_assign_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), + PROFILE_ASSIGN_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_color_profile_convert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + +#define PROFILE_CONVERT_DIALOG_KEY "gimp-profile-convert-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), PROFILE_CONVERT_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpColorProfile *current_profile; + GimpColorProfile *default_profile; + + current_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + default_profile = gimp_image_get_builtin_color_profile (image); + + dialog = color_profile_dialog_new (COLOR_PROFILE_DIALOG_CONVERT_TO_PROFILE, + image, + action_data_get_context (data), + widget, + current_profile, + default_profile, + config->image_convert_profile_intent, + config->image_convert_profile_bpc, + image_profile_convert_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), + PROFILE_CONVERT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_color_profile_discard_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_image_set_color_profile (image, NULL, NULL); + gimp_image_flush (image); +} + +static void +image_profile_save_dialog_response (GtkWidget *dialog, + gint response_id, + GimpImage *image) +{ + if (response_id == GTK_RESPONSE_ACCEPT) + { + GimpColorProfile *profile; + GFile *file; + GError *error = NULL; + + profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + + if (! file) + return; + + if (! gimp_color_profile_save_to_file (profile, file, &error)) + { + gimp_message (image->gimp, NULL, + GIMP_MESSAGE_WARNING, + _("Saving color profile failed: %s"), + error->message); + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_object_unref (file); + } + + gtk_widget_destroy (dialog); +} + +void +image_color_profile_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + +#define PROFILE_SAVE_DIALOG_KEY "gimp-profile-save-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), PROFILE_SAVE_DIALOG_KEY); + + if (! dialog) + { + GtkWindow *toplevel; + GimpColorProfile *profile; + gchar *basename; + + toplevel = GTK_WINDOW (gtk_widget_get_toplevel (widget)); + profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + dialog = + gimp_color_profile_chooser_dialog_new (_("Save Color Profile"), + toplevel, + GTK_FILE_CHOOSER_ACTION_SAVE); + + gimp_color_profile_chooser_dialog_connect_path (dialog, + G_OBJECT (image->gimp->config), + "color-profile-path"); + + basename = g_strconcat (gimp_color_profile_get_label (profile), + ".icc", NULL); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename); + g_free (basename); + + g_signal_connect (dialog, "response", + G_CALLBACK (image_profile_save_dialog_response), + image); + + dialogs_attach_dialog (G_OBJECT (image), PROFILE_SAVE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_resize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GimpDisplay *display; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + return_if_no_display (display, data); + +#define RESIZE_DIALOG_KEY "gimp-resize-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), RESIZE_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + if (image_resize_unit != GIMP_UNIT_PERCENT) + image_resize_unit = gimp_display_get_shell (display)->unit; + + dialog = resize_dialog_new (GIMP_VIEWABLE (image), + action_data_get_context (data), + _("Set Image Canvas Size"), + "gimp-image-resize", + widget, + gimp_standard_help_func, + GIMP_HELP_IMAGE_RESIZE, + image_resize_unit, + config->image_resize_fill_type, + config->image_resize_layer_set, + config->image_resize_resize_text_layers, + image_resize_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), RESIZE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_resize_to_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GimpProgress *progress; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Resizing")); + + gimp_image_resize_to_layers (image, + action_data_get_context (data), + NULL, NULL, NULL, NULL, progress); + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); +} + +void +image_resize_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GimpProgress *progress; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Resizing")); + + gimp_image_resize_to_selection (image, + action_data_get_context (data), + progress); + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); +} + +void +image_print_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + image = gimp_display_get_image (display); + +#define PRINT_SIZE_DIALOG_KEY "gimp-print-size-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), PRINT_SIZE_DIALOG_KEY); + + if (! dialog) + { + dialog = print_size_dialog_new (image, + action_data_get_context (data), + _("Set Image Print Resolution"), + "gimp-image-print-size", + widget, + gimp_standard_help_func, + GIMP_HELP_IMAGE_PRINT_SIZE, + image_print_size_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (image), PRINT_SIZE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_scale_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + image = gimp_display_get_image (display); + +#define SCALE_DIALOG_KEY "gimp-scale-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), SCALE_DIALOG_KEY); + + if (! dialog) + { + if (image_scale_unit != GIMP_UNIT_PERCENT) + image_scale_unit = gimp_display_get_shell (display)->unit; + + if (image_scale_interp == -1) + image_scale_interp = display->gimp->config->interpolation_type; + + dialog = image_scale_dialog_new (image, + action_data_get_context (data), + widget, + image_scale_unit, + image_scale_interp, + image_scale_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), SCALE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GimpProgress *progress; + GimpOrientationType orientation; + return_if_no_display (display, data); + + orientation = (GimpOrientationType) g_variant_get_int32 (value); + + image = gimp_display_get_image (display); + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Flipping")); + + gimp_image_flip (image, action_data_get_context (data), + orientation, progress); + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); +} + +void +image_rotate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GimpProgress *progress; + GimpRotationType rotation; + return_if_no_display (display, data); + + rotation = (GimpRotationType) g_variant_get_int32 (value); + + image = gimp_display_get_image (display); + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Rotating")); + + gimp_image_rotate (image, action_data_get_context (data), + rotation, progress); + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); +} + +void +image_crop_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + gint x, y; + gint width, height; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("Cannot crop because the current selection " + "is empty.")); + return; + } + + gimp_image_crop (image, + action_data_get_context (data), GIMP_FILL_TRANSPARENT, + x, y, width, height, TRUE); + gimp_image_flush (image); +} + +void +image_crop_to_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + gint x, y; + gint width, height; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (image), + 0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image), + &x, &y, &width, &height)) + { + case GIMP_AUTO_SHRINK_SHRINK: + gimp_image_crop (image, + action_data_get_context (data), GIMP_FILL_TRANSPARENT, + x, y, width, height, TRUE); + gimp_image_flush (image); + break; + + case GIMP_AUTO_SHRINK_EMPTY: + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_INFO, + _("Cannot crop because the image has no content.")); + break; + + case GIMP_AUTO_SHRINK_UNSHRINKABLE: + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_INFO, + _("Cannot crop because the image is already " + "cropped to its content.")); + break; + } +} + +void +image_merge_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *dialog; + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + +#define MERGE_LAYERS_DIALOG_KEY "gimp-merge-layers-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), MERGE_LAYERS_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = image_merge_layers_dialog_new (image, + action_data_get_context (data), + widget, + config->layer_merge_type, + config->layer_merge_active_group_only, + config->layer_merge_discard_invisible, + image_merge_layers_callback, + display); + + dialogs_attach_dialog (G_OBJECT (image), MERGE_LAYERS_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_merge_layers_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GimpDialogConfig *config; + return_if_no_image (image, data); + return_if_no_display (display, data); + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + image_merge_layers_callback (NULL, + image, + action_data_get_context (data), + config->layer_merge_type, + config->layer_merge_active_group_only, + config->layer_merge_discard_invisible, + display); +} + +void +image_flatten_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GError *error = NULL; + return_if_no_image (image, data); + return_if_no_display (display, data); + return_if_no_widget (widget, data); + + if (! gimp_image_flatten (image, action_data_get_context (data), + GIMP_PROGRESS (display), &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + return; + } + + gimp_image_flush (image); +} + +void +image_configure_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define GRID_DIALOG_KEY "gimp-grid-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), GRID_DIALOG_KEY); + + if (! dialog) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + + dialog = grid_dialog_new (image, + action_data_get_context (data), + gtk_widget_get_toplevel (GTK_WIDGET (shell))); + + dialogs_attach_dialog (G_OBJECT (image), GRID_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +image_properties_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define PROPERTIES_DIALOG_KEY "gimp-image-properties-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), PROPERTIES_DIALOG_KEY); + + if (! dialog) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + + dialog = image_properties_dialog_new (image, + action_data_get_context (data), + gtk_widget_get_toplevel (GTK_WIDGET (shell))); + + dialogs_attach_dialog (G_OBJECT (image), PROPERTIES_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + + +/* private functions */ + +static void +image_convert_rgb_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data) +{ + GimpProgress *progress = user_data; + GError *error = NULL; + + progress = gimp_progress_start (progress, FALSE, + _("Converting to RGB (%s)"), + gimp_color_profile_get_label (new_profile)); + + if (! gimp_image_convert_type (image, GIMP_RGB, new_profile, + progress, &error)) + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + + if (progress) + gimp_progress_end (progress); + + return; + } + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_convert_gray_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data) +{ + GimpProgress *progress = user_data; + GError *error = NULL; + + progress = gimp_progress_start (progress, FALSE, + _("Converting to grayscale (%s)"), + gimp_color_profile_get_label (new_profile)); + + if (! gimp_image_convert_type (image, GIMP_GRAY, new_profile, + progress, &error)) + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + + if (progress) + gimp_progress_end (progress); + + return; + } + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_convert_indexed_callback (GtkWidget *dialog, + GimpImage *image, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDisplay *display = user_data; + GimpProgress *progress; + GError *error = NULL; + + g_object_set (config, + "image-convert-indexed-palette-type", palette_type, + "image-convert-indexed-max-colors", max_colors, + "image-convert-indexed-remove-duplicates", remove_duplicates, + "image-convert-indexed-dither-type", dither_type, + "image-convert-indexed-dither-alpha", dither_alpha, + "image-convert-indexed-dither-text-layers", dither_text_layers, + NULL); + + if (image_convert_indexed_custom_palette) + g_object_remove_weak_pointer (G_OBJECT (image_convert_indexed_custom_palette), + (gpointer) &image_convert_indexed_custom_palette); + + image_convert_indexed_custom_palette = custom_palette; + + if (image_convert_indexed_custom_palette) + g_object_add_weak_pointer (G_OBJECT (image_convert_indexed_custom_palette), + (gpointer) &image_convert_indexed_custom_palette); + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Converting to indexed colors")); + + if (! gimp_image_convert_indexed (image, + config->image_convert_indexed_palette_type, + config->image_convert_indexed_max_colors, + config->image_convert_indexed_remove_duplicates, + config->image_convert_indexed_dither_type, + config->image_convert_indexed_dither_alpha, + config->image_convert_indexed_dither_text_layers, + image_convert_indexed_custom_palette, + progress, + &error)) + { + gimp_message_literal (image->gimp, G_OBJECT (display), + GIMP_MESSAGE_WARNING, error->message); + g_clear_error (&error); + + if (progress) + gimp_progress_end (progress); + + return; + } + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_convert_precision_callback (GtkWidget *dialog, + GimpImage *image, + GimpPrecision precision, + GeglDitherMethod layer_dither_method, + GeglDitherMethod text_layer_dither_method, + GeglDitherMethod channel_dither_method, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpProgress *progress = user_data; + const gchar *enum_desc; + const Babl *old_format; + const Babl *new_format; + gint old_bits; + gint new_bits; + + g_object_set (config, + "image-convert-precision-layer-dither-method", + layer_dither_method, + "image-convert-precision-text-layer-dither-method", + text_layer_dither_method, + "image-convert-precision-channel-dither-method", + channel_dither_method, + NULL); + + /* we do the same dither method checks here *and* in the dialog, + * because the dialog leaves the passed dither methods untouched if + * dithering is disabled and passes the original values to the + * callback, in order not to change the values saved in + * GimpDialogConfig. + */ + + /* random formats with the right precision */ + old_format = gimp_image_get_layer_format (image, FALSE); + new_format = gimp_babl_format (GIMP_RGB, precision, FALSE); + + old_bits = (babl_format_get_bytes_per_pixel (old_format) * 8 / + babl_format_get_n_components (old_format)); + new_bits = (babl_format_get_bytes_per_pixel (new_format) * 8 / + babl_format_get_n_components (new_format)); + + if (new_bits >= old_bits || + new_bits > CONVERT_PRECISION_DIALOG_MAX_DITHER_BITS) + { + /* don't dither if we are converting to a higher bit depth, + * or to more than MAX_DITHER_BITS. + */ + layer_dither_method = GEGL_DITHER_NONE; + text_layer_dither_method = GEGL_DITHER_NONE; + channel_dither_method = GEGL_DITHER_NONE; + } + + gimp_enum_get_value (GIMP_TYPE_PRECISION, precision, + NULL, NULL, &enum_desc, NULL); + + progress = gimp_progress_start (progress, FALSE, + _("Converting image to %s"), + enum_desc); + + gimp_image_convert_precision (image, + precision, + layer_dither_method, + text_layer_dither_method, + channel_dither_method, + progress); + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_profile_assign_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data) +{ + GError *error = NULL; + + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_PARASITE_ATTACH, + _("Assign color profile")); + + if (! gimp_image_set_color_profile (image, new_profile, &error)) + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + + gimp_image_undo_group_end (image); + gimp_image_undo (image); + + return; + } + + gimp_image_set_is_color_managed (image, TRUE, TRUE); + + /* omg... */ + gimp_image_parasite_detach (image, "icc-profile-name", TRUE); + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_profile_convert_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpProgress *progress = user_data; + GError *error = NULL; + + g_object_set (config, + "image-convert-profile-intent", intent, + "image-convert-profile-black-point-compensation", bpc, + NULL); + + progress = gimp_progress_start (progress, FALSE, + _("Converting to '%s'"), + gimp_color_profile_get_label (new_profile)); + + if (! gimp_image_convert_color_profile (image, new_profile, + config->image_convert_profile_intent, + config->image_convert_profile_bpc, + progress, &error)) + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + + if (progress) + gimp_progress_end (progress); + + return; + } + + if (progress) + gimp_progress_end (progress); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +image_resize_callback (GtkWidget *dialog, + GimpViewable *viewable, + GimpContext *context, + gint width, + gint height, + GimpUnit unit, + gint offset_x, + gint offset_y, + gdouble xres, + gdouble yres, + GimpUnit res_unit, + GimpFillType fill_type, + GimpItemSet layer_set, + gboolean resize_text_layers, + gpointer user_data) +{ + GimpDisplay *display = user_data; + + image_resize_unit = unit; + + if (width > 0 && height > 0) + { + GimpImage *image = GIMP_IMAGE (viewable); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpProgress *progress; + gdouble old_xres; + gdouble old_yres; + GimpUnit old_res_unit; + gboolean update_resolution; + + g_object_set (config, + "image-resize-fill-type", fill_type, + "image-resize-layer-set", layer_set, + "image-resize-resize-text-layers", resize_text_layers, + NULL); + + gtk_widget_destroy (dialog); + + if (width == gimp_image_get_width (image) && + height == gimp_image_get_height (image)) + return; + + progress = gimp_progress_start (GIMP_PROGRESS (display), FALSE, + _("Resizing")); + + gimp_image_get_resolution (image, &old_xres, &old_yres); + old_res_unit = gimp_image_get_unit (image); + + update_resolution = xres != old_xres || + yres != old_yres || + res_unit != old_res_unit; + + if (update_resolution) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_IMAGE_SCALE, + _("Change Canvas Size")); + gimp_image_set_resolution (image, xres, yres); + gimp_image_set_unit (image, res_unit); + } + + gimp_image_resize_with_layers (image, + context, fill_type, + width, height, offset_x, offset_y, + layer_set, + resize_text_layers, + progress); + + if (progress) + gimp_progress_end (progress); + + if (update_resolution) + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + } + else + { + g_warning ("Resize Error: " + "Both width and height must be greater than zero."); + } +} + +static void +image_print_size_callback (GtkWidget *dialog, + GimpImage *image, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer data) +{ + gdouble xres; + gdouble yres; + + gtk_widget_destroy (dialog); + + gimp_image_get_resolution (image, &xres, &yres); + + if (xresolution == xres && + yresolution == yres && + resolution_unit == gimp_image_get_unit (image)) + return; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_SCALE, + _("Change Print Size")); + + gimp_image_set_resolution (image, xresolution, yresolution); + gimp_image_set_unit (image, resolution_unit); + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); +} + +static void +image_scale_callback (GtkWidget *dialog, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data) +{ + GimpProgress *progress = user_data; + GimpImage *image = GIMP_IMAGE (viewable); + gdouble xres; + gdouble yres; + + image_scale_unit = unit; + image_scale_interp = interpolation; + + gimp_image_get_resolution (image, &xres, &yres); + + if (width > 0 && height > 0) + { + gtk_widget_destroy (dialog); + + if (width == gimp_image_get_width (image) && + height == gimp_image_get_height (image) && + xresolution == xres && + yresolution == yres && + resolution_unit == gimp_image_get_unit (image)) + return; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_SCALE, + _("Scale Image")); + + gimp_image_set_resolution (image, xresolution, yresolution); + gimp_image_set_unit (image, resolution_unit); + + if (width != gimp_image_get_width (image) || + height != gimp_image_get_height (image)) + { + progress = gimp_progress_start (progress, FALSE, + _("Scaling")); + + gimp_image_scale (image, width, height, interpolation, progress); + + if (progress) + gimp_progress_end (progress); + } + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + } + else + { + g_warning ("Scale Error: " + "Both width and height must be greater than zero."); + } +} + +static void +image_merge_layers_callback (GtkWidget *dialog, + GimpImage *image, + GimpContext *context, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDisplay *display = user_data; + + g_object_set (config, + "layer-merge-type", merge_type, + "layer-merge-active-group-only", merge_active_group, + "layer-merge-discard-invisible", discard_invisible, + NULL); + + gimp_image_merge_visible_layers (image, + context, + config->layer_merge_type, + config->layer_merge_active_group_only, + config->layer_merge_discard_invisible, + GIMP_PROGRESS (display)); + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/image-commands.h b/app/actions/image-commands.h new file mode 100644 index 0000000..018dec7 --- /dev/null +++ b/app/actions/image-commands.h @@ -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 . + */ + +#ifndef __IMAGE_COMMANDS_H__ +#define __IMAGE_COMMANDS_H__ + + +void image_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void image_convert_base_type_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_convert_precision_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_convert_gamma_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void image_color_management_enabled_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_color_profile_assign_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_color_profile_convert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_color_profile_discard_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_color_profile_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void image_resize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_resize_to_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_resize_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_print_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_scale_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_flip_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_rotate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_crop_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_crop_to_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void image_merge_layers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_merge_layers_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_flatten_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void image_configure_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void image_properties_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __IMAGE_COMMANDS_H__ */ diff --git a/app/actions/images-actions.c b/app/actions/images-actions.c new file mode 100644 index 0000000..74745c1 --- /dev/null +++ b/app/actions/images-actions.c @@ -0,0 +1,98 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "images-actions.h" +#include "images-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry images_actions[] = +{ + { "images-popup", GIMP_ICON_DIALOG_IMAGES, + NC_("images-action", "Images Menu"), NULL, NULL, NULL, + GIMP_HELP_IMAGE_DIALOG }, + + { "images-raise-views", GIMP_ICON_GO_TOP, + NC_("images-action", "_Raise Views"), NULL, + NC_("images-action", "Raise this image's displays"), + images_raise_views_cmd_callback, + NULL }, + + { "images-new-view", GIMP_ICON_DOCUMENT_NEW, + NC_("images-action", "_New View"), NULL, + NC_("images-action", "Create a new display for this image"), + images_new_view_cmd_callback, + NULL }, + + { "images-delete", GIMP_ICON_EDIT_DELETE, + NC_("images-action", "_Delete Image"), NULL, + NC_("images-action", "Delete this image"), + images_delete_image_cmd_callback, + NULL } +}; + + +void +images_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "images-action", + images_actions, + G_N_ELEMENTS (images_actions)); +} + +void +images_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpContext *context = action_data_get_context (data); + GimpImage *image = NULL; + gint disp_count = 0; + + if (context) + { + image = gimp_context_get_image (context); + + if (image) + disp_count = gimp_image_get_display_count (image); + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("images-raise-views", image); + SET_SENSITIVE ("images-new-view", image); + SET_SENSITIVE ("images-delete", image && disp_count == 0); + +#undef SET_SENSITIVE +} diff --git a/app/actions/images-actions.h b/app/actions/images-actions.h new file mode 100644 index 0000000..4a304f7 --- /dev/null +++ b/app/actions/images-actions.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 . + */ + +#ifndef __IMAGES_ACTIONS_H__ +#define __IMAGES_ACTIONS_H__ + + +void images_actions_setup (GimpActionGroup *group); +void images_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __IMAGES_ACTIONS_H__ */ diff --git a/app/actions/images-commands.c b/app/actions/images-commands.c new file mode 100644 index 0000000..22ea8a8 --- /dev/null +++ b/app/actions/images-commands.c @@ -0,0 +1,117 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpimageview.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "images-commands.h" + + +/* public functions */ + +void +images_raise_views_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpImage *image; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + image = gimp_context_get_image (context); + + if (image && gimp_container_have (container, GIMP_OBJECT (image))) + { + GList *list; + + for (list = gimp_get_display_iter (image->gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + if (gimp_display_get_image (display) == image) + gimp_display_shell_present (gimp_display_get_shell (display)); + } + } +} + +void +images_new_view_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpImage *image; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + image = gimp_context_get_image (context); + + if (image && gimp_container_have (container, GIMP_OBJECT (image))) + { + gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (GTK_WIDGET (editor))), + gimp_widget_get_monitor (GTK_WIDGET (editor))); + } +} + +void +images_delete_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpImage *image; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + image = gimp_context_get_image (context); + + if (image && gimp_container_have (container, GIMP_OBJECT (image))) + { + if (gimp_image_get_display_count (image) == 0) + g_object_unref (image); + } +} diff --git a/app/actions/images-commands.h b/app/actions/images-commands.h new file mode 100644 index 0000000..f836fc4 --- /dev/null +++ b/app/actions/images-commands.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 . + */ + +#ifndef __IMAGES_COMMANDS_H__ +#define __IMAGES_COMMANDS_H__ + + +void images_raise_views_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void images_new_view_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void images_delete_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __IMAGES_COMMANDS_H__ */ diff --git a/app/actions/items-actions.c b/app/actions/items-actions.c new file mode 100644 index 0000000..5f18e1f --- /dev/null +++ b/app/actions/items-actions.c @@ -0,0 +1,142 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpitem.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpwidgets-utils.h" + +#include "items-actions.h" + + +void +items_actions_setup (GimpActionGroup *group, + const gchar *prefix) +{ + GEnumClass *enum_class; + GEnumValue *value; + + enum_class = g_type_class_ref (GIMP_TYPE_COLOR_TAG); + + for (value = enum_class->values; value->value_name; value++) + { + gchar action[32]; + + g_snprintf (action, sizeof (action), + "%s-color-tag-%s", prefix, value->value_nick); + + if (value->value == GIMP_COLOR_TAG_NONE) + { + gimp_action_group_set_action_always_show_image (group, action, TRUE); + } + else + { + GimpRGB color; + + gimp_action_group_set_action_context (group, action, + gimp_get_user_context (group->gimp)); + + gimp_get_color_tag_color (value->value, &color, FALSE); + gimp_action_group_set_action_color (group, action, &color, FALSE); + } + } + + g_type_class_unref (enum_class); +} + +void +items_actions_update (GimpActionGroup *group, + const gchar *prefix, + GimpItem *item) +{ + GEnumClass *enum_class; + GEnumValue *value; + gchar action[32]; + gboolean visible = FALSE; + gboolean linked = FALSE; + gboolean has_color_tag = FALSE; + gboolean locked = FALSE; + gboolean can_lock = FALSE; + gboolean locked_pos = FALSE; + gboolean can_lock_pos = FALSE; + GimpRGB tag_color; + + if (item) + { + visible = gimp_item_get_visible (item); + linked = gimp_item_get_linked (item); + locked = gimp_item_get_lock_content (item); + can_lock = gimp_item_can_lock_content (item); + locked_pos = gimp_item_get_lock_position (item); + can_lock_pos = gimp_item_can_lock_position (item); + + has_color_tag = gimp_get_color_tag_color (gimp_item_get_color_tag (item), + &tag_color, FALSE); + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_COLOR(action,color) \ + gimp_action_group_set_action_color (group, action, color, FALSE) + + g_snprintf (action, sizeof (action), "%s-visible", prefix); + SET_SENSITIVE (action, item); + SET_ACTIVE (action, visible); + + g_snprintf (action, sizeof (action), "%s-linked", prefix); + SET_SENSITIVE (action, item); + SET_ACTIVE (action, linked); + + g_snprintf (action, sizeof (action), "%s-lock-content", prefix); + SET_SENSITIVE (action, can_lock); + SET_ACTIVE (action, locked); + + g_snprintf (action, sizeof (action), "%s-lock-position", prefix); + SET_SENSITIVE (action, can_lock_pos); + SET_ACTIVE (action, locked_pos); + + g_snprintf (action, sizeof (action), "%s-color-tag-menu", prefix); + SET_COLOR (action, has_color_tag ? &tag_color : NULL); + + enum_class = g_type_class_ref (GIMP_TYPE_COLOR_TAG); + + for (value = enum_class->values; value->value_name; value++) + { + g_snprintf (action, sizeof (action), + "%s-color-tag-%s", prefix, value->value_nick); + + SET_SENSITIVE (action, item); + } + + g_type_class_unref (enum_class); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +#undef SET_COLOR +} diff --git a/app/actions/items-actions.h b/app/actions/items-actions.h new file mode 100644 index 0000000..90efc08 --- /dev/null +++ b/app/actions/items-actions.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 . + */ + +#ifndef __ITEMS_ACTIONS_H__ +#define __ITEMS_ACTIONS_H__ + + +void items_actions_setup (GimpActionGroup *group, + const gchar *prefix); +void items_actions_update (GimpActionGroup *group, + const gchar *prefix, + GimpItem *item); + + +#endif /* __ITEMS_ACTIONS_H__ */ diff --git a/app/actions/items-commands.c b/app/actions/items-commands.c new file mode 100644 index 0000000..43e17d9 --- /dev/null +++ b/app/actions/items-commands.c @@ -0,0 +1,419 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" +#include "core/gimpitem.h" +#include "core/gimpitemundo.h" + +#include "dialogs/dialogs.h" +#include "dialogs/fill-dialog.h" +#include "dialogs/stroke-dialog.h" + +#include "actions.h" +#include "items-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void items_fill_callback (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpFillOptions *options, + gpointer user_data); +static void items_stroke_callback (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpStrokeOptions *options, + gpointer user_data); + + +/* public functions */ + +void +items_visible_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item) +{ + gboolean visible = g_variant_get_boolean (value); + + if (visible != gimp_item_get_visible (item)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_VISIBILITY); + + if (undo && GIMP_ITEM_UNDO (undo)->item == item) + push_undo = FALSE; + + gimp_item_set_visible (item, visible, push_undo); + gimp_image_flush (image); + } +} + +void +items_linked_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item) +{ + gboolean linked = g_variant_get_boolean (value); + + if (linked != gimp_item_get_linked (item)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_LINKED); + + if (undo && GIMP_ITEM_UNDO (undo)->item == item) + push_undo = FALSE; + + gimp_item_set_linked (item, linked, push_undo); + gimp_image_flush (image); + } +} + +void +items_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item) +{ + gboolean locked = g_variant_get_boolean (value); + + if (locked != gimp_item_get_lock_content (item)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_LINKED); + + if (undo && GIMP_ITEM_UNDO (undo)->item == item) + push_undo = FALSE; + + gimp_item_set_lock_content (item, locked, push_undo); + gimp_image_flush (image); + } +} + +void +items_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item) +{ + gboolean locked = g_variant_get_boolean (value); + + if (locked != gimp_item_get_lock_position (item)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_LOCK_POSITION); + + if (undo && GIMP_ITEM_UNDO (undo)->item == item) + push_undo = FALSE; + + + gimp_item_set_lock_position (item, locked, push_undo); + gimp_image_flush (image); + } +} + +void +items_color_tag_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + GimpColorTag color_tag) +{ + if (color_tag != gimp_item_get_color_tag (item)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_ITEM_COLOR_TAG); + + if (undo && GIMP_ITEM_UNDO (undo)->item == item) + push_undo = FALSE; + + gimp_item_set_color_tag (item, color_tag, push_undo); + gimp_image_flush (image); + } +} + +void +items_fill_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + const gchar *dialog_key, + const gchar *dialog_title, + const gchar *dialog_icon_name, + const gchar *dialog_help_id, + gpointer data) +{ + GimpDrawable *drawable; + GtkWidget *dialog; + GtkWidget *widget; + return_if_no_widget (widget, data); + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to fill.")); + return; + } + + dialog = dialogs_get_dialog (G_OBJECT (item), dialog_key); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = fill_dialog_new (item, + drawable, + action_data_get_context (data), + dialog_title, + dialog_icon_name, + dialog_help_id, + widget, + config->fill_options, + items_fill_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (item), dialog_key, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +items_fill_last_vals_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + gpointer data) +{ + GimpDrawable *drawable; + GimpDialogConfig *config; + GtkWidget *widget; + GError *error = NULL; + return_if_no_widget (widget, data); + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to fill.")); + return; + } + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + if (! gimp_item_fill (item, drawable, + config->fill_options, TRUE, NULL, &error)) + { + gimp_message_literal (image->gimp, G_OBJECT (widget), + GIMP_MESSAGE_WARNING, error->message); + g_clear_error (&error); + } + else + { + gimp_image_flush (image); + } +} + +void +items_stroke_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + const gchar *dialog_key, + const gchar *dialog_title, + const gchar *dialog_icon_name, + const gchar *dialog_help_id, + gpointer data) +{ + GimpDrawable *drawable; + GtkWidget *dialog; + GtkWidget *widget; + return_if_no_widget (widget, data); + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to stroke to.")); + return; + } + + dialog = dialogs_get_dialog (G_OBJECT (item), dialog_key); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = stroke_dialog_new (item, + drawable, + action_data_get_context (data), + dialog_title, + dialog_icon_name, + dialog_help_id, + widget, + config->stroke_options, + items_stroke_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (item), dialog_key, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +items_stroke_last_vals_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + gpointer data) +{ + GimpDrawable *drawable; + GimpDialogConfig *config; + GtkWidget *widget; + GError *error = NULL; + return_if_no_widget (widget, data); + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("There is no active layer or channel to stroke to.")); + return; + } + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + if (! gimp_item_stroke (item, drawable, + action_data_get_context (data), + config->stroke_options, NULL, + TRUE, NULL, &error)) + { + gimp_message_literal (image->gimp, G_OBJECT (widget), + GIMP_MESSAGE_WARNING, error->message); + g_clear_error (&error); + } + else + { + gimp_image_flush (image); + } +} + + +/* private functions */ + +static void +items_fill_callback (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpFillOptions *options, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (context->gimp->config); + GimpImage *image = gimp_item_get_image (item); + GError *error = NULL; + + gimp_config_sync (G_OBJECT (options), + G_OBJECT (config->fill_options), 0); + + if (! gimp_item_fill (item, drawable, options, TRUE, NULL, &error)) + { + gimp_message_literal (context->gimp, + G_OBJECT (dialog), + GIMP_MESSAGE_WARNING, + error ? error->message : "NULL"); + + g_clear_error (&error); + return; + } + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +items_stroke_callback (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpStrokeOptions *options, + gpointer data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (context->gimp->config); + GimpImage *image = gimp_item_get_image (item); + GError *error = NULL; + + gimp_config_sync (G_OBJECT (options), + G_OBJECT (config->stroke_options), 0); + + if (! gimp_item_stroke (item, drawable, context, options, NULL, + TRUE, NULL, &error)) + { + gimp_message_literal (context->gimp, + G_OBJECT (dialog), + GIMP_MESSAGE_WARNING, + error ? error->message : "NULL"); + + g_clear_error (&error); + return; + } + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/items-commands.h b/app/actions/items-commands.h new file mode 100644 index 0000000..f7c87b5 --- /dev/null +++ b/app/actions/items-commands.h @@ -0,0 +1,72 @@ +/* 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 . + */ + +#ifndef __ITEMS_COMMANDS_H__ +#define __ITEMS_COMMANDS_H__ + + +void items_visible_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item); +void items_linked_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item); +void items_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item); +void items_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + GimpImage *image, + GimpItem *item); + +void items_color_tag_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + GimpColorTag color_tag); + +void items_fill_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + const gchar *dialog_key, + const gchar *dialog_title, + const gchar *dialog_icon_name, + const gchar *dialog_help_id, + gpointer data); +void items_fill_last_vals_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + gpointer data); + +void items_stroke_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + const gchar *dialog_key, + const gchar *dialog_title, + const gchar *dialog_icon_name, + const gchar *dialog_help_id, + gpointer data); +void items_stroke_last_vals_cmd_callback (GimpAction *action, + GimpImage *image, + GimpItem *item, + gpointer data); + + + +#endif /* __ITEMS_COMMANDS_H__ */ diff --git a/app/actions/layers-actions.c b/app/actions/layers-actions.c new file mode 100644 index 0000000..2100696 --- /dev/null +++ b/app/actions/layers-actions.c @@ -0,0 +1,1027 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" +#include "core/gimplayer-floating-selection.h" + +#include "text/gimptextlayer.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpwidgets-utils.h" + +#include "actions.h" +#include "image-commands.h" +#include "items-actions.h" +#include "layers-actions.h" +#include "layers-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry layers_actions[] = +{ + { "layers-popup", GIMP_ICON_DIALOG_LAYERS, + NC_("layers-action", "Layers Menu"), NULL, NULL, NULL, + GIMP_HELP_LAYER_DIALOG }, + + { "layers-blend-space-menu", NULL, + NC_("layers-action", "Blend Space"), NULL, NULL, NULL, + NULL }, + + { "layers-composite-space-menu", NULL, + NC_("layers-action", "Composite Space"), NULL, NULL, NULL, + NULL }, + + { "layers-composite-mode-menu", NULL, + NC_("layers-action", "Composite Mode"), NULL, NULL, NULL, + NULL }, + + { "layers-color-tag-menu", NULL, + NC_("layers-action", "Color Tag"), NULL, NULL, NULL, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-menu", NULL, + NC_("layers-action", "_Layer") }, + { "layers-stack-menu", NULL, + NC_("layers-action", "Stac_k") }, + { "layers-mask-menu", NULL, + NC_("layers-action", "_Mask") }, + { "layers-transparency-menu", NULL, + NC_("layers-action", "Tr_ansparency") }, + { "layers-transform-menu", NULL, + NC_("layers-action", "_Transform") }, + { "layers-properties-menu", GIMP_ICON_DOCUMENT_PROPERTIES, + NC_("layers-action", "_Properties") }, + { "layers-opacity-menu", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "_Opacity") }, + { "layers-mode-menu", GIMP_ICON_TOOL_PENCIL, + NC_("layers-action", "Layer _Mode") }, + + { "layers-edit", GIMP_ICON_EDIT, + NC_("layers-action", "Default Edit Action"), NULL, + NC_("layers-action", "Activate the default edit action for this type of layer"), + layers_edit_cmd_callback, + GIMP_HELP_LAYER_EDIT }, + + { "layers-edit-text", GIMP_ICON_EDIT, + NC_("layers-action", "Edit Te_xt on canvas"), NULL, + NC_("layers-action", "Edit this text layer content on canvas"), + layers_edit_text_cmd_callback, + GIMP_HELP_LAYER_EDIT }, + + { "layers-edit-attributes", GIMP_ICON_EDIT, + NC_("layers-action", "_Edit Layer Attributes..."), NULL, + NC_("layers-action", "Edit the layer's name"), + layers_edit_attributes_cmd_callback, + GIMP_HELP_LAYER_EDIT }, + + { "layers-new", GIMP_ICON_DOCUMENT_NEW, + NC_("layers-action", "_New Layer..."), "N", + NC_("layers-action", "Create a new layer and add it to the image"), + layers_new_cmd_callback, + GIMP_HELP_LAYER_NEW }, + + { "layers-new-last-values", GIMP_ICON_DOCUMENT_NEW, + NC_("layers-action", "_New Layer"), NULL, + NC_("layers-action", "Create a new layer with last used values"), + layers_new_last_vals_cmd_callback, + GIMP_HELP_LAYER_NEW }, + + { "layers-new-from-visible", NULL, + NC_("layers-action", "New from _Visible"), NULL, + NC_("layers-action", + "Create a new layer from what is visible in this image"), + layers_new_from_visible_cmd_callback, + GIMP_HELP_LAYER_NEW_FROM_VISIBLE }, + + { "layers-new-group", GIMP_ICON_FOLDER_NEW, + NC_("layers-action", "New Layer _Group"), NULL, + NC_("layers-action", "Create a new layer group and add it to the image"), + layers_new_group_cmd_callback, + GIMP_HELP_LAYER_NEW }, + + { "layers-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("layers-action", "D_uplicate Layer"), "D", + NC_("layers-action", + "Create a duplicate of the layer and add it to the image"), + layers_duplicate_cmd_callback, + GIMP_HELP_LAYER_DUPLICATE }, + + { "layers-delete", GIMP_ICON_EDIT_DELETE, + NC_("layers-action", "_Delete Layer"), NULL, + NC_("layers-action", "Delete this layer"), + layers_delete_cmd_callback, + GIMP_HELP_LAYER_DELETE }, + + { "layers-raise", GIMP_ICON_GO_UP, + NC_("layers-action", "_Raise Layer"), NULL, + NC_("layers-action", "Raise this layer one step in the layer stack"), + layers_raise_cmd_callback, + GIMP_HELP_LAYER_RAISE }, + + { "layers-raise-to-top", GIMP_ICON_GO_TOP, + NC_("layers-action", "Layer to _Top"), NULL, + NC_("layers-action", "Move this layer to the top of the layer stack"), + layers_raise_to_top_cmd_callback, + GIMP_HELP_LAYER_RAISE_TO_TOP }, + + { "layers-lower", GIMP_ICON_GO_DOWN, + NC_("layers-action", "_Lower Layer"), NULL, + NC_("layers-action", "Lower this layer one step in the layer stack"), + layers_lower_cmd_callback, + GIMP_HELP_LAYER_LOWER }, + + { "layers-lower-to-bottom", GIMP_ICON_GO_BOTTOM, + NC_("layers-action", "Layer to _Bottom"), NULL, + NC_("layers-action", "Move this layer to the bottom of the layer stack"), + layers_lower_to_bottom_cmd_callback, + GIMP_HELP_LAYER_LOWER_TO_BOTTOM }, + + { "layers-anchor", GIMP_ICON_LAYER_ANCHOR, + NC_("layers-action", "_Anchor Layer"), "H", + NC_("layers-action", "Anchor the floating layer"), + layers_anchor_cmd_callback, + GIMP_HELP_LAYER_ANCHOR }, + + { "layers-merge-down", GIMP_ICON_LAYER_MERGE_DOWN, + NC_("layers-action", "Merge Do_wn"), NULL, + NC_("layers-action", "Merge this layer with the first visible layer below it"), + layers_merge_down_cmd_callback, + GIMP_HELP_LAYER_MERGE_DOWN }, + + /* this is the same as layers-merge-down, except it's sensitive even if + * the layer can't be merged down + */ + { "layers-merge-down-button", GIMP_ICON_LAYER_MERGE_DOWN, + NC_("layers-action", "Merge Do_wn"), NULL, + NC_("layers-action", "Merge this layer with the first visible layer below it"), + layers_merge_down_cmd_callback, + GIMP_HELP_LAYER_MERGE_DOWN }, + + { "layers-merge-group", NULL, + NC_("layers-action", "Merge Layer Group"), NULL, + NC_("layers-action", "Merge the layer group's layers into one normal layer"), + layers_merge_group_cmd_callback, + GIMP_HELP_LAYER_MERGE_GROUP }, + + { "layers-merge-layers", NULL, + NC_("layers-action", "Merge _Visible Layers..."), NULL, + NC_("layers-action", "Merge all visible layers into one layer"), + image_merge_layers_cmd_callback, + GIMP_HELP_IMAGE_MERGE_LAYERS }, + + { "layers-merge-layers-last-values", NULL, + NC_("layers-action", "Merge _Visible Layers"), NULL, + NC_("layers-action", "Merge all visible layers with last used values"), + image_merge_layers_last_vals_cmd_callback, + GIMP_HELP_IMAGE_MERGE_LAYERS }, + + { "layers-flatten-image", NULL, + NC_("layers-action", "_Flatten Image"), NULL, + NC_("layers-action", "Merge all layers into one and remove transparency"), + image_flatten_image_cmd_callback, + GIMP_HELP_IMAGE_FLATTEN }, + + { "layers-text-discard", GIMP_ICON_TOOL_TEXT, + NC_("layers-action", "_Discard Text Information"), NULL, + NC_("layers-action", "Turn this text layer into a normal layer"), + layers_text_discard_cmd_callback, + GIMP_HELP_LAYER_TEXT_DISCARD }, + + { "layers-text-to-vectors", GIMP_ICON_TOOL_TEXT, + NC_("layers-action", "Text to _Path"), NULL, + NC_("layers-action", "Create a path from this text layer"), + layers_text_to_vectors_cmd_callback, + GIMP_HELP_LAYER_TEXT_TO_PATH }, + + { "layers-text-along-vectors", GIMP_ICON_TOOL_TEXT, + NC_("layers-action", "Text alon_g Path"), NULL, + NC_("layers-action", "Warp this layer's text along the current path"), + layers_text_along_vectors_cmd_callback, + GIMP_HELP_LAYER_TEXT_ALONG_PATH }, + + { "layers-resize", GIMP_ICON_OBJECT_RESIZE, + NC_("layers-action", "Layer B_oundary Size..."), NULL, + NC_("layers-action", "Adjust the layer dimensions"), + layers_resize_cmd_callback, + GIMP_HELP_LAYER_RESIZE }, + + { "layers-resize-to-image", GIMP_ICON_LAYER_TO_IMAGESIZE, + NC_("layers-action", "Layer to _Image Size"), NULL, + NC_("layers-action", "Resize the layer to the size of the image"), + layers_resize_to_image_cmd_callback, + GIMP_HELP_LAYER_RESIZE_TO_IMAGE }, + + { "layers-scale", GIMP_ICON_OBJECT_SCALE, + NC_("layers-action", "_Scale Layer..."), NULL, + NC_("layers-action", "Change the size of the layer content"), + layers_scale_cmd_callback, + GIMP_HELP_LAYER_SCALE }, + + { "layers-crop-to-selection", GIMP_ICON_TOOL_CROP, + NC_("layers-action", "_Crop to Selection"), NULL, + NC_("layers-action", "Crop the layer to the extents of the selection"), + layers_crop_to_selection_cmd_callback, + GIMP_HELP_LAYER_CROP }, + + { "layers-crop-to-content", GIMP_ICON_TOOL_CROP, + NC_("layers-action", "Crop to C_ontent"), NULL, + NC_("layers-action", "Crop the layer to the extents of its content (remove empty borders from the layer)"), + layers_crop_to_content_cmd_callback, + GIMP_HELP_LAYER_CROP }, + + { "layers-mask-add", GIMP_ICON_LAYER_MASK, + NC_("layers-action", "Add La_yer Mask..."), NULL, + NC_("layers-action", + "Add a mask that allows non-destructive editing of transparency"), + layers_mask_add_cmd_callback, + GIMP_HELP_LAYER_MASK_ADD }, + + /* this is the same as layers-mask-add, except it's sensitive even if + * there is a mask on the layer + */ + { "layers-mask-add-button", GIMP_ICON_LAYER_MASK, + NC_("layers-action", "Add La_yer Mask..."), NULL, + NC_("layers-action", + "Add a mask that allows non-destructive editing of transparency"), + layers_mask_add_cmd_callback, + GIMP_HELP_LAYER_MASK_ADD }, + + { "layers-mask-add-last-values", GIMP_ICON_LAYER_MASK, + NC_("layers-action", "Add La_yer Mask"), NULL, + NC_("layers-action", + "Add a mask with last used values"), + layers_mask_add_last_vals_cmd_callback, + GIMP_HELP_LAYER_MASK_ADD }, + + { "layers-alpha-add", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Add Alpha C_hannel"), NULL, + NC_("layers-action", "Add transparency information to the layer"), + layers_alpha_add_cmd_callback, + GIMP_HELP_LAYER_ALPHA_ADD }, + + { "layers-alpha-remove", NULL, + NC_("layers-action", "_Remove Alpha Channel"), NULL, + NC_("layers-action", "Remove transparency information from the layer"), + layers_alpha_remove_cmd_callback, + GIMP_HELP_LAYER_ALPHA_REMOVE } +}; + +static const GimpToggleActionEntry layers_toggle_actions[] = +{ + { "layers-mask-edit", GIMP_ICON_EDIT, + NC_("layers-action", "_Edit Layer Mask"), NULL, + NC_("layers-action", "Work on the layer mask"), + layers_mask_edit_cmd_callback, + FALSE, + GIMP_HELP_LAYER_MASK_EDIT }, + + { "layers-mask-show", GIMP_ICON_VISIBLE, + NC_("layers-action", "S_how Layer Mask"), NULL, NULL, + layers_mask_show_cmd_callback, + FALSE, + GIMP_HELP_LAYER_MASK_SHOW }, + + { "layers-mask-disable", NULL, + NC_("layers-action", "_Disable Layer Mask"), NULL, + NC_("layers-action", "Dismiss the effect of the layer mask"), + layers_mask_disable_cmd_callback, + FALSE, + GIMP_HELP_LAYER_MASK_DISABLE }, + + { "layers-visible", GIMP_ICON_VISIBLE, + NC_("layers-action", "Toggle Layer _Visibility"), NULL, NULL, + layers_visible_cmd_callback, + FALSE, + GIMP_HELP_LAYER_VISIBLE }, + + { "layers-linked", GIMP_ICON_LINKED, + NC_("layers-action", "Toggle Layer _Linked State"), NULL, NULL, + layers_linked_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LINKED }, + + { "layers-lock-content", NULL /* GIMP_ICON_LOCK */, + NC_("layers-action", "L_ock Pixels of Layer"), NULL, NULL, + layers_lock_content_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LOCK_PIXELS }, + + { "layers-lock-position", GIMP_ICON_TOOL_MOVE, + NC_("layers-action", "L_ock Position of Layer"), NULL, NULL, + layers_lock_position_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LOCK_POSITION }, + + { "layers-lock-alpha", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Lock Alph_a Channel"), NULL, + NC_("layers-action", + "Keep transparency information on this layer from being modified"), + layers_lock_alpha_cmd_callback, + FALSE, + GIMP_HELP_LAYER_LOCK_ALPHA }, +}; + +static const GimpRadioActionEntry layers_blend_space_actions[] = +{ + { "layers-blend-space-auto", NULL, + NC_("layers-action", "Auto"), NULL, + NC_("layers-action", "Layer Blend Space: Auto"), + GIMP_LAYER_COLOR_SPACE_AUTO, + NULL }, + + { "layers-blend-space-rgb-linear", NULL, + NC_("layers-action", "RGB (linear)"), NULL, + NC_("layers-action", "Layer Blend Space: RGB (linear)"), + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + NULL }, + + { "layers-blend-space-rgb-perceptual", NULL, + NC_("layers-action", "RGB (perceptual)"), NULL, + NC_("layers-action", "Layer Blend Space: RGB (perceptual)"), + GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + NULL } +}; + +static const GimpRadioActionEntry layers_composite_space_actions[] = +{ + { "layers-composite-space-auto", NULL, + NC_("layers-action", "Auto"), NULL, + NC_("layers-action", "Layer Composite Space: Auto"), + GIMP_LAYER_COLOR_SPACE_AUTO, + NULL }, + + { "layers-composite-space-rgb-linear", NULL, + NC_("layers-action", "RGB (linear)"), NULL, + NC_("layers-action", "Layer Composite Space: RGB (linear)"), + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + NULL }, + + { "layers-composite-space-rgb-perceptual", NULL, + NC_("layers-action", "RGB (perceptual)"), NULL, + NC_("layers-action", "Layer Composite Space: RGB (perceptual)"), + GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + NULL } +}; + +static const GimpRadioActionEntry layers_composite_mode_actions[] = +{ + { "layers-composite-mode-auto", NULL, + NC_("layers-action", "Auto"), NULL, + NC_("layers-action", "Layer Composite Mode: Auto"), + GIMP_LAYER_COMPOSITE_AUTO, + NULL }, + + { "layers-composite-mode-union", NULL, + NC_("layers-action", "Union"), NULL, + NC_("layers-action", "Layer Composite Mode: Union"), + GIMP_LAYER_COMPOSITE_UNION, + NULL }, + + { "layers-composite-mode-clip-to-backdrop", NULL, + NC_("layers-action", "Clip to Backdrop"), NULL, + NC_("layers-action", "Layer Composite Mode: Clip to Backdrop"), + GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + NULL }, + + { "layers-composite-mode-clip-to-layer", NULL, + NC_("layers-action", "Clip to Layer"), NULL, + NC_("layers-action", "Layer Composite Mode: Clip to Layer"), + GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, + NULL }, + + { "layers-composite-mode-intersection", NULL, + NC_("layers-action", "Intersection"), NULL, + NC_("layers-action", "Layer Composite Mode: Intersection"), + GIMP_LAYER_COMPOSITE_INTERSECTION, + NULL } +}; + +static const GimpEnumActionEntry layers_color_tag_actions[] = +{ + { "layers-color-tag-none", GIMP_ICON_EDIT_CLEAR, + NC_("layers-action", "None"), NULL, + NC_("layers-action", "Layer Color Tag: Clear"), + GIMP_COLOR_TAG_NONE, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-blue", NULL, + NC_("layers-action", "Blue"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Blue"), + GIMP_COLOR_TAG_BLUE, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-green", NULL, + NC_("layers-action", "Green"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Green"), + GIMP_COLOR_TAG_GREEN, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-yellow", NULL, + NC_("layers-action", "Yellow"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Yellow"), + GIMP_COLOR_TAG_YELLOW, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-orange", NULL, + NC_("layers-action", "Orange"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Orange"), + GIMP_COLOR_TAG_ORANGE, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-brown", NULL, + NC_("layers-action", "Brown"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Brown"), + GIMP_COLOR_TAG_BROWN, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-red", NULL, + NC_("layers-action", "Red"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Red"), + GIMP_COLOR_TAG_RED, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-violet", NULL, + NC_("layers-action", "Violet"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Violet"), + GIMP_COLOR_TAG_VIOLET, FALSE, + GIMP_HELP_LAYER_COLOR_TAG }, + + { "layers-color-tag-gray", NULL, + NC_("layers-action", "Gray"), NULL, + NC_("layers-action", "Layer Color Tag: Set to Gray"), + GIMP_COLOR_TAG_GRAY, FALSE, + GIMP_HELP_LAYER_COLOR_TAG } +}; + +static const GimpEnumActionEntry layers_mask_apply_actions[] = +{ + { "layers-mask-apply", NULL, + NC_("layers-action", "Apply Layer _Mask"), NULL, + NC_("layers-action", "Apply the effect of the layer mask and remove it"), + GIMP_MASK_APPLY, FALSE, + GIMP_HELP_LAYER_MASK_APPLY }, + + { "layers-mask-delete", GIMP_ICON_EDIT_DELETE, + NC_("layers-action", "Delete Layer Mas_k"), NULL, + NC_("layers-action", "Remove the layer mask and its effect"), + GIMP_MASK_DISCARD, FALSE, + GIMP_HELP_LAYER_MASK_DELETE } +}; + +static const GimpEnumActionEntry layers_mask_to_selection_actions[] = +{ + { "layers-mask-selection-replace", GIMP_ICON_SELECTION_REPLACE, + NC_("layers-action", "_Mask to Selection"), NULL, + NC_("layers-action", "Replace the selection with the layer mask"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_LAYER_MASK_SELECTION_REPLACE }, + + { "layers-mask-selection-add", GIMP_ICON_SELECTION_ADD, + NC_("layers-action", "_Add to Selection"), NULL, + NC_("layers-action", "Add the layer mask to the current selection"), + GIMP_CHANNEL_OP_ADD, FALSE, + GIMP_HELP_LAYER_MASK_SELECTION_ADD }, + + { "layers-mask-selection-subtract", GIMP_ICON_SELECTION_SUBTRACT, + NC_("layers-action", "_Subtract from Selection"), NULL, + NC_("layers-action", "Subtract the layer mask from the current selection"), + GIMP_CHANNEL_OP_SUBTRACT, FALSE, + GIMP_HELP_LAYER_MASK_SELECTION_SUBTRACT }, + + { "layers-mask-selection-intersect", GIMP_ICON_SELECTION_INTERSECT, + NC_("layers-action", "_Intersect with Selection"), NULL, + NC_("layers-action", "Intersect the layer mask with the current selection"), + GIMP_CHANNEL_OP_INTERSECT, FALSE, + GIMP_HELP_LAYER_MASK_SELECTION_INTERSECT } +}; + +static const GimpEnumActionEntry layers_alpha_to_selection_actions[] = +{ + { "layers-alpha-selection-replace", GIMP_ICON_SELECTION_REPLACE, + NC_("layers-action", "Al_pha to Selection"), NULL, + NC_("layers-action", + "Replace the selection with the layer's alpha channel"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_LAYER_ALPHA_SELECTION_REPLACE }, + + { "layers-alpha-selection-add", GIMP_ICON_SELECTION_ADD, + NC_("layers-action", "A_dd to Selection"), NULL, + NC_("layers-action", + "Add the layer's alpha channel to the current selection"), + GIMP_CHANNEL_OP_ADD, FALSE, + GIMP_HELP_LAYER_ALPHA_SELECTION_ADD }, + + { "layers-alpha-selection-subtract", GIMP_ICON_SELECTION_SUBTRACT, + NC_("layers-action", "_Subtract from Selection"), NULL, + NC_("layers-action", + "Subtract the layer's alpha channel from the current selection"), + GIMP_CHANNEL_OP_SUBTRACT, FALSE, + GIMP_HELP_LAYER_ALPHA_SELECTION_SUBTRACT }, + + { "layers-alpha-selection-intersect", GIMP_ICON_SELECTION_INTERSECT, + NC_("layers-action", "_Intersect with Selection"), NULL, + NC_("layers-action", + "Intersect the layer's alpha channel with the current selection"), + GIMP_CHANNEL_OP_INTERSECT, FALSE, + GIMP_HELP_LAYER_ALPHA_SELECTION_INTERSECT } +}; + +static const GimpEnumActionEntry layers_select_actions[] = +{ + { "layers-select-top", NULL, + NC_("layers-action", "Select _Top Layer"), "Home", + NC_("layers-action", "Select the topmost layer"), + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_LAYER_TOP }, + + { "layers-select-bottom", NULL, + NC_("layers-action", "Select _Bottom Layer"), "End", + NC_("layers-action", "Select the bottommost layer"), + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_LAYER_BOTTOM }, + + { "layers-select-previous", NULL, + NC_("layers-action", "Select _Previous Layer"), "Prior", + NC_("layers-action", "Select the layer above the current layer"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_LAYER_PREVIOUS }, + + { "layers-select-next", NULL, + NC_("layers-action", "Select _Next Layer"), "Next", + NC_("layers-action", "Select the layer below the current layer"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_LAYER_NEXT } +}; + +static const GimpEnumActionEntry layers_opacity_actions[] = +{ + { "layers-opacity-set", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-transparent", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make Completely Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-opaque", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make Completely Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-decrease", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make More Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-increase", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make More Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-decrease-skip", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make 10% More Transparent"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + GIMP_HELP_LAYER_OPACITY }, + { "layers-opacity-increase-skip", GIMP_ICON_TRANSPARENCY, + NC_("layers-action", "Layer Opacity: Make 10% More Opaque"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + GIMP_HELP_LAYER_OPACITY } +}; + +static const GimpEnumActionEntry layers_mode_actions[] = +{ + { "layers-mode-first", GIMP_ICON_TOOL_PENCIL, + NC_("layers-action", "Layer Mode: Select First"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_LAYER_MODE }, + { "layers-mode-last", GIMP_ICON_TOOL_PENCIL, + NC_("layers-action", "Layer Mode: Select Last"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_LAYER_MODE }, + { "layers-mode-previous", GIMP_ICON_TOOL_PENCIL, + NC_("layers-action", "Layer Mode: Select Previous"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_LAYER_MODE }, + { "layers-mode-next", GIMP_ICON_TOOL_PENCIL, + NC_("layers-action", "Layer Mode: Select Next"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_LAYER_MODE } +}; + +/** + * layers_actions_fix_tooltip: + * @group: + * @action: + * @modifiers: + * + * Make layer alpha to selection click-shortcuts discoverable, at + * least in theory. + **/ +static void +layers_actions_fix_tooltip (GimpActionGroup *group, + const gchar *action, + GdkModifierType modifiers) +{ + const gchar *old_hint; + gchar *new_hint; + + old_hint = gimp_action_group_get_action_tooltip (group, + action); + new_hint = g_strconcat (old_hint, + "\n", + /* Will be followed with e.g. "Shift-Click + on thumbnail" + */ + _("Shortcut: "), + gimp_get_mod_string (modifiers), + /* Will be prepended with a modifier key + string, e.g. "Shift" + */ + _("-Click on thumbnail in Layers dockable"), + NULL); + + gimp_action_group_set_action_tooltip (group, action, new_hint); + g_free (new_hint); +} + +void +layers_actions_setup (GimpActionGroup *group) +{ + GdkDisplay *display = gdk_display_get_default (); + GdkModifierType extend_mask; + GdkModifierType modify_mask; + + extend_mask = + gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display), + GDK_MODIFIER_INTENT_EXTEND_SELECTION); + modify_mask = + gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display), + GDK_MODIFIER_INTENT_MODIFY_SELECTION); + + gimp_action_group_add_actions (group, "layers-action", + layers_actions, + G_N_ELEMENTS (layers_actions)); + + gimp_action_group_add_toggle_actions (group, "layers-action", + layers_toggle_actions, + G_N_ELEMENTS (layers_toggle_actions)); + + gimp_action_group_add_radio_actions (group, "layers-action", + layers_blend_space_actions, + G_N_ELEMENTS (layers_blend_space_actions), + NULL, 0, + layers_blend_space_cmd_callback); + + gimp_action_group_add_radio_actions (group, "layers-action", + layers_composite_space_actions, + G_N_ELEMENTS (layers_composite_space_actions), + NULL, 0, + layers_composite_space_cmd_callback); + + gimp_action_group_add_radio_actions (group, "layers-action", + layers_composite_mode_actions, + G_N_ELEMENTS (layers_composite_mode_actions), + NULL, 0, + layers_composite_mode_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_color_tag_actions, + G_N_ELEMENTS (layers_color_tag_actions), + layers_color_tag_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_mask_apply_actions, + G_N_ELEMENTS (layers_mask_apply_actions), + layers_mask_apply_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_mask_to_selection_actions, + G_N_ELEMENTS (layers_mask_to_selection_actions), + layers_mask_to_selection_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_alpha_to_selection_actions, + G_N_ELEMENTS (layers_alpha_to_selection_actions), + layers_alpha_to_selection_cmd_callback); + + layers_actions_fix_tooltip (group, "layers-alpha-selection-replace", + GDK_MOD1_MASK); + layers_actions_fix_tooltip (group, "layers-alpha-selection-add", + extend_mask | GDK_MOD1_MASK); + layers_actions_fix_tooltip (group, "layers-alpha-selection-subtract", + modify_mask | GDK_MOD1_MASK); + layers_actions_fix_tooltip (group, "layers-alpha-selection-intersect", + extend_mask | modify_mask | GDK_MOD1_MASK); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_select_actions, + G_N_ELEMENTS (layers_select_actions), + layers_select_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_opacity_actions, + G_N_ELEMENTS (layers_opacity_actions), + layers_opacity_cmd_callback); + + gimp_action_group_add_enum_actions (group, "layers-action", + layers_mode_actions, + G_N_ELEMENTS (layers_mode_actions), + layers_mode_cmd_callback); + + items_actions_setup (group, "layers"); +} + +void +layers_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpLayer *layer = NULL; + GimpLayerMask *mask = NULL; /* layer mask */ + gboolean fs = FALSE; /* floating sel */ + gboolean ac = FALSE; /* active channel */ + gboolean sel = FALSE; + gboolean alpha = FALSE; /* alpha channel present */ + gboolean indexed = FALSE; /* is indexed */ + gboolean lock_alpha = FALSE; + gboolean can_lock_alpha = FALSE; + gboolean text_layer = FALSE; + gboolean visible = FALSE; + gboolean writable = FALSE; + gboolean movable = FALSE; + gboolean children = FALSE; + gboolean bs_mutable = FALSE; + gboolean cs_mutable = FALSE; + gboolean cm_mutable = FALSE; + GList *next = NULL; + GList *next_visible = NULL; + GList *prev = NULL; + gboolean next_mode = FALSE; + gboolean prev_mode = FALSE; + + if (image) + { + fs = (gimp_image_get_floating_selection (image) != NULL); + ac = (gimp_image_get_active_channel (image) != NULL); + sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); + indexed = (gimp_image_get_base_type (image) == GIMP_INDEXED); + + layer = gimp_image_get_active_layer (image); + + if (layer) + { + GimpLayerMode *modes; + GimpLayerMode mode = gimp_layer_get_mode (layer); + const gchar *action = NULL; + GList *layer_list; + GList *list; + gint n_modes; + gint i = 0; + + switch (gimp_layer_get_blend_space (layer)) + { + case GIMP_LAYER_COLOR_SPACE_AUTO: + action = "layers-blend-space-auto"; break; + case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR: + action = "layers-blend-space-rgb-linear"; break; + case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL: + action = "layers-blend-space-rgb-perceptual"; break; + default: + action = NULL; break; /* can't happen */ + } + + if (action) + gimp_action_group_set_action_active (group, action, TRUE); + + switch (gimp_layer_get_composite_space (layer)) + { + case GIMP_LAYER_COLOR_SPACE_AUTO: + action = "layers-composite-space-auto"; break; + case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR: + action = "layers-composite-space-rgb-linear"; break; + case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL: + action = "layers-composite-space-rgb-perceptual"; break; + default: + action = NULL; break; /* can't happen */ + } + + if (action) + gimp_action_group_set_action_active (group, action, TRUE); + + switch (gimp_layer_get_composite_mode (layer)) + { + case GIMP_LAYER_COMPOSITE_AUTO: + action = "layers-composite-mode-auto"; break; + case GIMP_LAYER_COMPOSITE_UNION: + action = "layers-composite-mode-union"; break; + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + action = "layers-composite-mode-clip-to-backdrop"; break; + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + action = "layers-composite-mode-clip-to-layer"; break; + case GIMP_LAYER_COMPOSITE_INTERSECTION: + action = "layers-composite-mode-intersection"; break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + + bs_mutable = gimp_layer_mode_is_blend_space_mutable (mode); + cs_mutable = gimp_layer_mode_is_composite_space_mutable (mode); + cm_mutable = gimp_layer_mode_is_composite_mode_mutable (mode); + + mask = gimp_layer_get_mask (layer); + lock_alpha = gimp_layer_get_lock_alpha (layer); + can_lock_alpha = gimp_layer_can_lock_alpha (layer); + alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)); + visible = gimp_item_get_visible (GIMP_ITEM (layer)); + writable = ! gimp_item_is_content_locked (GIMP_ITEM (layer)); + movable = ! gimp_item_is_position_locked (GIMP_ITEM (layer)); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + children = TRUE; + + layer_list = gimp_item_get_container_iter (GIMP_ITEM (layer)); + + list = g_list_find (layer_list, layer); + + if (list) + { + prev = g_list_previous (list); + next = g_list_next (list); + + for (next_visible = next; + next_visible; + next_visible = g_list_next (next_visible)) + { + if (gimp_item_get_visible (next_visible->data)) + { + /* "next_visible" is actually "next_visible" and + * "writable" and "not group" + */ + if (gimp_item_is_content_locked (next_visible->data) || + gimp_viewable_get_children (next_visible->data)) + next_visible = NULL; + + break; + } + } + } + + modes = gimp_layer_mode_get_context_array (mode, + GIMP_LAYER_MODE_CONTEXT_LAYER, + &n_modes); + while (i < (n_modes - 1) && modes[i] != mode) + i++; + g_free (modes); + next_mode = (i < n_modes - 1); + prev_mode = (i > 0); + + text_layer = gimp_item_is_text_layer (GIMP_ITEM (layer)); + } + } + +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_LABEL(action,label) \ + gimp_action_group_set_action_label (group, action, label) + + SET_SENSITIVE ("layers-edit", !ac && ((layer && !fs) || text_layer)); + SET_VISIBLE ("layers-edit-text", text_layer && !ac); + SET_SENSITIVE ("layers-edit-text", text_layer && !ac); + SET_SENSITIVE ("layers-edit-attributes", layer && !fs && !ac); + + if (layer && gimp_layer_is_floating_sel (layer)) + { + SET_LABEL ("layers-new", C_("layers-action", "To _New Layer")); + SET_LABEL ("layers-new-last-values", C_("layers-action", "To _New Layer")); + } + else + { + SET_LABEL ("layers-new", C_("layers-action", "_New Layer...")); + SET_LABEL ("layers-new-last-values", C_("layers-action", "_New Layer")); + } + + SET_SENSITIVE ("layers-new", image); + SET_SENSITIVE ("layers-new-last-values", image); + SET_SENSITIVE ("layers-new-from-visible", image); + SET_SENSITIVE ("layers-new-group", image && !indexed); + SET_SENSITIVE ("layers-duplicate", layer && !fs && !ac); + SET_SENSITIVE ("layers-delete", layer && !ac); + + SET_SENSITIVE ("layers-mode-first", layer && !ac && prev_mode); + SET_SENSITIVE ("layers-mode-last", layer && !ac && next_mode); + SET_SENSITIVE ("layers-mode-previous", layer && !ac && prev_mode); + SET_SENSITIVE ("layers-mode-next", layer && !ac && next_mode); + + SET_SENSITIVE ("layers-select-top", layer && !fs && !ac && prev); + SET_SENSITIVE ("layers-select-bottom", layer && !fs && !ac && next); + SET_SENSITIVE ("layers-select-previous", layer && !fs && !ac && prev); + SET_SENSITIVE ("layers-select-next", layer && !fs && !ac && next); + + SET_SENSITIVE ("layers-raise", layer && !fs && !ac && prev); + SET_SENSITIVE ("layers-raise-to-top", layer && !fs && !ac && prev); + SET_SENSITIVE ("layers-lower", layer && !fs && !ac && next); + SET_SENSITIVE ("layers-lower-to-bottom", layer && !fs && !ac && next); + + SET_VISIBLE ("layers-anchor", layer && fs && !ac); + SET_VISIBLE ("layers-merge-down", !fs); + SET_SENSITIVE ("layers-merge-down", layer && !fs && !ac && visible && next_visible); + SET_VISIBLE ("layers-merge-down-button", !fs); + SET_SENSITIVE ("layers-merge-down-button", layer && !fs && !ac); + SET_VISIBLE ("layers-merge-group", children); + SET_SENSITIVE ("layers-merge-group", layer && !fs && !ac && children); + SET_SENSITIVE ("layers-merge-layers", layer && !fs && !ac); + SET_SENSITIVE ("layers-flatten-image", layer && !fs && !ac); + + SET_VISIBLE ("layers-text-discard", text_layer && !ac); + SET_VISIBLE ("layers-text-to-vectors", text_layer && !ac); + SET_VISIBLE ("layers-text-along-vectors", text_layer && !ac); + + SET_SENSITIVE ("layers-resize", writable && movable && !ac); + SET_SENSITIVE ("layers-resize-to-image", writable && movable && !ac); + SET_SENSITIVE ("layers-scale", writable && movable && !ac); + + SET_SENSITIVE ("layers-crop-to-selection", writable && movable && sel); + SET_SENSITIVE ("layers-crop-to-content", writable && movable); + + SET_SENSITIVE ("layers-alpha-add", writable && !children && !fs && !alpha); + SET_SENSITIVE ("layers-alpha-remove", writable && !children && !fs && alpha); + + SET_SENSITIVE ("layers-lock-alpha", can_lock_alpha); + SET_ACTIVE ("layers-lock-alpha", lock_alpha); + + SET_SENSITIVE ("layers-blend-space-auto", layer && bs_mutable); + SET_SENSITIVE ("layers-blend-space-rgb-linear", layer && bs_mutable); + SET_SENSITIVE ("layers-blend-space-rgb-perceptual", layer && bs_mutable); + + SET_SENSITIVE ("layers-composite-space-auto", layer && cs_mutable); + SET_SENSITIVE ("layers-composite-space-rgb-linear", layer && cs_mutable); + SET_SENSITIVE ("layers-composite-space-rgb-perceptual", layer && cs_mutable); + + SET_SENSITIVE ("layers-composite-mode-auto", layer && cm_mutable); + SET_SENSITIVE ("layers-composite-mode-union", layer && cm_mutable); + SET_SENSITIVE ("layers-composite-mode-clip-to-backdrop", layer && cm_mutable); + SET_SENSITIVE ("layers-composite-mode-clip-to-layer", layer && cm_mutable); + SET_SENSITIVE ("layers-composite-mode-intersection", layer && cm_mutable); + + SET_SENSITIVE ("layers-mask-add", layer && !fs && !ac && !mask); + SET_SENSITIVE ("layers-mask-add-button", layer && !fs && !ac); + SET_SENSITIVE ("layers-mask-add-last-values", layer && !fs && !ac && !mask); + + SET_SENSITIVE ("layers-mask-apply", writable && !fs && !ac && mask && !children); + SET_SENSITIVE ("layers-mask-delete", layer && !fs && !ac && mask); + + SET_SENSITIVE ("layers-mask-edit", layer && !fs && !ac && mask); + SET_SENSITIVE ("layers-mask-show", layer && !fs && !ac && mask); + SET_SENSITIVE ("layers-mask-disable", layer && !fs && !ac && mask); + + SET_ACTIVE ("layers-mask-edit", mask && gimp_layer_get_edit_mask (layer)); + SET_ACTIVE ("layers-mask-show", mask && gimp_layer_get_show_mask (layer)); + SET_ACTIVE ("layers-mask-disable", mask && !gimp_layer_get_apply_mask (layer)); + + SET_SENSITIVE ("layers-mask-selection-replace", layer && !fs && !ac && mask); + SET_SENSITIVE ("layers-mask-selection-add", layer && !fs && !ac && mask); + SET_SENSITIVE ("layers-mask-selection-subtract", layer && !fs && !ac && mask); + SET_SENSITIVE ("layers-mask-selection-intersect", layer && !fs && !ac && mask); + + SET_SENSITIVE ("layers-alpha-selection-replace", layer && !fs && !ac); + SET_SENSITIVE ("layers-alpha-selection-add", layer && !fs && !ac); + SET_SENSITIVE ("layers-alpha-selection-subtract", layer && !fs && !ac); + SET_SENSITIVE ("layers-alpha-selection-intersect", layer && !fs && !ac); + +#undef SET_VISIBLE +#undef SET_SENSITIVE +#undef SET_ACTIVE +#undef SET_LABEL + + items_actions_update (group, "layers", GIMP_ITEM (layer)); +} diff --git a/app/actions/layers-actions.h b/app/actions/layers-actions.h new file mode 100644 index 0000000..0f257bf --- /dev/null +++ b/app/actions/layers-actions.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 . + */ + +#ifndef __LAYERS_ACTIONS_H__ +#define __LAYERS_ACTIONS_H__ + + +void layers_actions_setup (GimpActionGroup *group); +void layers_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __LAYERS_ACTIONS_H__ */ diff --git a/app/actions/layers-commands.c b/app/actions/layers-commands.c new file mode 100644 index 0000000..528eb4c --- /dev/null +++ b/app/actions/layers-commands.c @@ -0,0 +1,1749 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpdrawable-fill.h" +#include "core/gimpgrouplayer.h" +#include "core/gimpimage.h" +#include "core/gimpimage-merge.h" +#include "core/gimpimage-undo.h" +#include "core/gimpimage-undo-push.h" +#include "core/gimpitemundo.h" +#include "core/gimplayerpropundo.h" +#include "core/gimplayer-floating-selection.h" +#include "core/gimplayer-new.h" +#include "core/gimppickable.h" +#include "core/gimppickable-auto-shrink.h" +#include "core/gimptoolinfo.h" +#include "core/gimpundostack.h" +#include "core/gimpprogress.h" + +#include "text/gimptext.h" +#include "text/gimptext-vectors.h" +#include "text/gimptextlayer.h" + +#include "vectors/gimpstroke.h" +#include "vectors/gimpvectors.h" +#include "vectors/gimpvectors-warp.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpdock.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpprogressdialog.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpimagewindow.h" + +#include "tools/gimptexttool.h" +#include "tools/tool_manager.h" + +#include "dialogs/dialogs.h" +#include "dialogs/layer-add-mask-dialog.h" +#include "dialogs/layer-options-dialog.h" +#include "dialogs/resize-dialog.h" +#include "dialogs/scale-dialog.h" + +#include "actions.h" +#include "items-commands.h" +#include "layers-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void layers_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpLayer *layer, + GimpContext *context, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gint layer_width, + gint layer_height, + gint layer_offset_x, + gint layer_offset_y, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_pixels, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + gboolean rename_text_layer, + gpointer user_data); +static void layers_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpLayer *layer, + GimpContext *context, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gint layer_width, + gint layer_height, + gint layer_offset_x, + gint layer_offset_y, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_pixels, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + gboolean rename_text_layer, + gpointer user_data); +static void layers_add_mask_callback (GtkWidget *dialog, + GimpLayer *layer, + GimpAddMaskType add_mask_type, + GimpChannel *channel, + gboolean invert, + gpointer user_data); +static void layers_scale_callback (GtkWidget *dialog, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data); +static void layers_resize_callback (GtkWidget *dialog, + GimpViewable *viewable, + GimpContext *context, + gint width, + gint height, + GimpUnit unit, + gint offset_x, + gint offset_y, + gdouble unused0, + gdouble unused1, + GimpUnit unused2, + GimpFillType fill_type, + GimpItemSet unused3, + gboolean unused4, + gpointer data); + +static gint layers_mode_index (GimpLayerMode layer_mode, + const GimpLayerMode *modes, + gint n_modes); + + +/* private variables */ + +static GimpUnit layer_resize_unit = GIMP_UNIT_PIXEL; +static GimpUnit layer_scale_unit = GIMP_UNIT_PIXEL; +static GimpInterpolationType layer_scale_interp = -1; + + +/* public functions */ + +void +layers_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + if (gimp_item_is_text_layer (GIMP_ITEM (layer))) + { + layers_edit_text_cmd_callback (action, value, data); + } + else + { + layers_edit_attributes_cmd_callback (action, value, data); + } +} + +void +layers_edit_text_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GimpTool *active_tool; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + g_return_if_fail (gimp_item_is_text_layer (GIMP_ITEM (layer))); + + active_tool = tool_manager_get_active (image->gimp); + + if (! GIMP_IS_TEXT_TOOL (active_tool)) + { + GimpToolInfo *tool_info = gimp_get_tool_info (image->gimp, + "gimp-text-tool"); + + if (GIMP_IS_TOOL_INFO (tool_info)) + { + gimp_context_set_tool (action_data_get_context (data), tool_info); + active_tool = tool_manager_get_active (image->gimp); + } + } + + if (GIMP_IS_TEXT_TOOL (active_tool)) + { + if (gimp_text_tool_set_layer (GIMP_TEXT_TOOL (active_tool), layer)) + { + GimpDisplayShell *shell; + + shell = gimp_display_get_shell (active_tool->display); + gtk_widget_grab_focus (shell->canvas); + } + } +} + +void +layers_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + +#define EDIT_DIALOG_KEY "gimp-layer-edit-attributes-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY); + + if (! dialog) + { + GimpItem *item = GIMP_ITEM (layer); + + dialog = layer_options_dialog_new (gimp_item_get_image (GIMP_ITEM (layer)), + layer, + action_data_get_context (data), + widget, + _("Layer Attributes"), + "gimp-layer-edit", + GIMP_ICON_EDIT, + _("Edit Layer Attributes"), + GIMP_HELP_LAYER_EDIT, + gimp_object_get_name (layer), + gimp_layer_get_mode (layer), + gimp_layer_get_blend_space (layer), + gimp_layer_get_composite_space (layer), + gimp_layer_get_composite_mode (layer), + gimp_layer_get_opacity (layer), + 0 /* unused */, + gimp_item_get_visible (item), + gimp_item_get_linked (item), + gimp_item_get_color_tag (item), + gimp_item_get_lock_content (item), + gimp_item_get_lock_position (item), + gimp_layer_get_lock_alpha (layer), + layers_edit_attributes_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +layers_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GimpLayer *floating_sel; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + /* If there is a floating selection, the new command transforms + * the current fs into a new layer + */ + if ((floating_sel = gimp_image_get_floating_selection (image))) + { + GError *error = NULL; + + if (! floating_sel_to_layer (floating_sel, &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + return; + } + + gimp_image_flush (image); + return; + } + +#define NEW_DIALOG_KEY "gimp-layer-new-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpLayerMode layer_mode = config->layer_new_mode; + + if (layer_mode == GIMP_LAYER_MODE_NORMAL || + layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY) + { + layer_mode = gimp_image_get_default_new_layer_mode (image); + } + + dialog = layer_options_dialog_new (image, NULL, + action_data_get_context (data), + widget, + _("New Layer"), + "gimp-layer-new", + GIMP_ICON_LAYER, + _("Create a New Layer"), + GIMP_HELP_LAYER_NEW, + config->layer_new_name, + layer_mode, + config->layer_new_blend_space, + config->layer_new_composite_space, + config->layer_new_composite_mode, + config->layer_new_opacity, + config->layer_new_fill_type, + TRUE, + FALSE, + GIMP_COLOR_TAG_NONE, + FALSE, + FALSE, + FALSE, + layers_new_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +layers_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GimpLayer *layer; + GimpDialogConfig *config; + GimpLayerMode layer_mode; + + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + /* If there is a floating selection, the new command transforms + * the current fs into a new layer + */ + if (gimp_image_get_floating_selection (image)) + { + layers_new_cmd_callback (action, value, data); + return; + } + + layer_mode = config->layer_new_mode; + + if (layer_mode == GIMP_LAYER_MODE_NORMAL || + layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY) + { + layer_mode = gimp_image_get_default_new_layer_mode (image); + } + + layer = gimp_layer_new (image, + gimp_image_get_width (image), + gimp_image_get_height (image), + gimp_image_get_layer_format (image, TRUE), + config->layer_new_name, + config->layer_new_opacity, + layer_mode); + + gimp_drawable_fill (GIMP_DRAWABLE (layer), + action_data_get_context (data), + config->layer_new_fill_type); + gimp_layer_set_blend_space (layer, + config->layer_new_blend_space, FALSE); + gimp_layer_set_composite_space (layer, + config->layer_new_composite_space, FALSE); + gimp_layer_set_composite_mode (layer, + config->layer_new_composite_mode, FALSE); + + gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); +} + +void +layers_new_from_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplayShell *shell; + GimpLayer *layer; + GimpPickable *pickable; + GimpColorProfile *profile; + return_if_no_image (image, data); + return_if_no_shell (shell, data); + + pickable = gimp_display_shell_get_canvas_pickable (shell); + + gimp_pickable_flush (pickable); + + profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + layer = gimp_layer_new_from_gegl_buffer (gimp_pickable_get_buffer (pickable), + image, + gimp_image_get_layer_format (image, + TRUE), + _("Visible"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image), + profile); + + gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); +} + +void +layers_new_group_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_image (image, data); + + layer = gimp_group_layer_new (image); + + gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); +} + +void +layers_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpContainer *container; + GimpLayer *new_layer; + GimpActionSelectType select_type; + return_if_no_image (image, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + layer = gimp_image_get_active_layer (image); + + if (layer) + container = gimp_item_get_container (GIMP_ITEM (layer)); + else + container = gimp_image_get_layers (image); + + new_layer = (GimpLayer *) action_select_object (select_type, + container, + (GimpObject *) layer); + + if (new_layer && new_layer != layer) + { + gimp_image_set_active_layer (image, new_layer); + gimp_image_flush (image); + } +} + +void +layers_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_raise_item (image, GIMP_ITEM (layer), NULL); + gimp_image_flush (image); +} + +void +layers_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_raise_item_to_top (image, GIMP_ITEM (layer)); + gimp_image_flush (image); +} + +void +layers_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_lower_item (image, GIMP_ITEM (layer), NULL); + gimp_image_flush (image); +} + +void +layers_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_lower_item_to_bottom (image, GIMP_ITEM (layer)); + gimp_image_flush (image); +} + +void +layers_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayer *new_layer; + return_if_no_layer (image, layer, data); + + new_layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (layer), + G_TYPE_FROM_INSTANCE (layer))); + + /* use the actual parent here, not GIMP_IMAGE_ACTIVE_PARENT because + * the latter would add a duplicated group inside itself instead of + * above it + */ + gimp_image_add_layer (image, new_layer, + gimp_layer_get_parent (layer), -1, + TRUE); + gimp_image_flush (image); +} + +void +layers_anchor_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_layer_is_floating_sel (layer)) + { + floating_sel_anchor (layer); + gimp_image_flush (image); + } +} + +void +layers_merge_down_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpDisplay *display; + return_if_no_layer (image, layer, data); + return_if_no_display (display, data); + + gimp_image_merge_down (image, layer, action_data_get_context (data), + GIMP_EXPAND_AS_NECESSARY, + GIMP_PROGRESS (display), NULL); + gimp_image_flush (image); +} + +void +layers_merge_group_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_merge_group_layer (image, GIMP_GROUP_LAYER (layer)); + gimp_image_flush (image); +} + +void +layers_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_image_remove_layer (image, layer, TRUE, NULL); + gimp_image_flush (image); +} + +void +layers_text_discard_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (GIMP_IS_TEXT_LAYER (layer)) + gimp_text_layer_discard (GIMP_TEXT_LAYER (layer)); +} + +void +layers_text_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (GIMP_IS_TEXT_LAYER (layer)) + { + GimpVectors *vectors; + gint x, y; + + vectors = gimp_text_vectors_new (image, GIMP_TEXT_LAYER (layer)->text); + + gimp_item_get_offset (GIMP_ITEM (layer), &x, &y); + gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE); + + gimp_image_add_vectors (image, vectors, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + } +} + +void +layers_text_along_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpVectors *vectors; + return_if_no_layer (image, layer, data); + return_if_no_vectors (image, vectors, data); + + if (GIMP_IS_TEXT_LAYER (layer)) + { + gdouble box_width; + gdouble box_height; + GimpVectors *new_vectors; + gdouble offset; + + box_width = gimp_item_get_width (GIMP_ITEM (layer)); + box_height = gimp_item_get_height (GIMP_ITEM (layer)); + + new_vectors = gimp_text_vectors_new (image, GIMP_TEXT_LAYER (layer)->text); + + offset = 0; + switch (GIMP_TEXT_LAYER (layer)->text->base_dir) + { + case GIMP_TEXT_DIRECTION_LTR: + case GIMP_TEXT_DIRECTION_RTL: + offset = 0.5 * box_height; + break; + case GIMP_TEXT_DIRECTION_TTB_RTL: + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + case GIMP_TEXT_DIRECTION_TTB_LTR: + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + { + GimpStroke *stroke = NULL; + + while ((stroke = gimp_vectors_stroke_get_next (new_vectors, stroke))) + { + gimp_stroke_rotate (stroke, 0, 0, 270); + gimp_stroke_translate (stroke, 0, box_width); + } + } + offset = 0.5 * box_width; + break; + } + + + gimp_vectors_warp_vectors (vectors, new_vectors, offset); + + gimp_item_set_visible (GIMP_ITEM (new_vectors), TRUE, FALSE); + + gimp_image_add_vectors (image, new_vectors, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + } +} + +void +layers_resize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + +#define RESIZE_DIALOG_KEY "gimp-resize-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDisplay *display = NULL; + + if (GIMP_IS_IMAGE_WINDOW (data)) + display = action_data_get_display (data); + + if (layer_resize_unit != GIMP_UNIT_PERCENT && display) + layer_resize_unit = gimp_display_get_shell (display)->unit; + + dialog = resize_dialog_new (GIMP_VIEWABLE (layer), + action_data_get_context (data), + _("Set Layer Boundary Size"), + "gimp-layer-resize", + widget, + gimp_standard_help_func, + GIMP_HELP_LAYER_RESIZE, + layer_resize_unit, + config->layer_resize_fill_type, + GIMP_ITEM_SET_NONE, + FALSE, + layers_resize_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +layers_resize_to_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + gimp_layer_resize_to_image (layer, + action_data_get_context (data), + GIMP_FILL_TRANSPARENT); + gimp_image_flush (image); +} + +void +layers_scale_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + +#define SCALE_DIALOG_KEY "gimp-scale-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY); + + if (! dialog) + { + GimpDisplay *display = NULL; + + if (GIMP_IS_IMAGE_WINDOW (data)) + display = action_data_get_display (data); + + if (layer_scale_unit != GIMP_UNIT_PERCENT && display) + layer_scale_unit = gimp_display_get_shell (display)->unit; + + if (layer_scale_interp == -1) + layer_scale_interp = image->gimp->config->interpolation_type; + + dialog = scale_dialog_new (GIMP_VIEWABLE (layer), + action_data_get_context (data), + _("Scale Layer"), "gimp-layer-scale", + widget, + gimp_standard_help_func, GIMP_HELP_LAYER_SCALE, + layer_scale_unit, + layer_scale_interp, + layers_scale_callback, + display); + + dialogs_attach_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +layers_crop_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + gint x, y; + gint width, height; + gint off_x, off_y; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + _("Cannot crop because the current selection " + "is empty.")); + return; + } + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + off_x -= x; + off_y -= y; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, + _("Crop Layer to Selection")); + + gimp_item_resize (GIMP_ITEM (layer), + action_data_get_context (data), GIMP_FILL_TRANSPARENT, + width, height, off_x, off_y); + + gimp_image_undo_group_end (image); + gimp_image_flush (image); +} + +void +layers_crop_to_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + gint x, y; + gint width, height; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (layer), + 0, 0, + gimp_item_get_width (GIMP_ITEM (layer)), + gimp_item_get_height (GIMP_ITEM (layer)), + &x, &y, &width, &height)) + { + case GIMP_AUTO_SHRINK_SHRINK: + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, + _("Crop Layer to Content")); + + gimp_item_resize (GIMP_ITEM (layer), + action_data_get_context (data), GIMP_FILL_TRANSPARENT, + width, height, -x, -y); + + gimp_image_undo_group_end (image); + gimp_image_flush (image); + break; + + case GIMP_AUTO_SHRINK_EMPTY: + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_INFO, + _("Cannot crop because the active layer " + "has no content.")); + break; + + case GIMP_AUTO_SHRINK_UNSHRINKABLE: + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_INFO, + _("Cannot crop because the active layer " + "is already cropped to its content.")); + break; + } +} + +void +layers_mask_add_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + if (gimp_layer_get_mask (layer)) + return; + +#define ADD_MASK_DIALOG_KEY "gimp-add-mask-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (layer), ADD_MASK_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = layer_add_mask_dialog_new (layer, action_data_get_context (data), + widget, + config->layer_add_mask_type, + config->layer_add_mask_invert, + layers_add_mask_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (layer), ADD_MASK_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +layers_mask_add_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GtkWidget *widget; + GimpDialogConfig *config; + GimpChannel *channel = NULL; + GimpLayerMask *mask; + return_if_no_layer (image, layer, data); + return_if_no_widget (widget, data); + + if (gimp_layer_get_mask (layer)) + return; + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + if (config->layer_add_mask_type == GIMP_ADD_MASK_CHANNEL) + { + channel = gimp_image_get_active_channel (image); + + if (! channel) + { + GimpContainer *channels = gimp_image_get_channels (image); + + channel = GIMP_CHANNEL (gimp_container_get_first_child (channels)); + } + + if (! channel) + { + layers_mask_add_cmd_callback (action, value, data); + return; + } + } + + mask = gimp_layer_create_mask (layer, + config->layer_add_mask_type, + channel); + + if (config->layer_add_mask_invert) + gimp_channel_invert (GIMP_CHANNEL (mask), FALSE); + + gimp_layer_add_mask (layer, mask, TRUE, NULL); + gimp_image_flush (image); +} + +void +layers_mask_apply_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_layer_get_mask (layer)) + { + GimpMaskApplyMode mode = (GimpMaskApplyMode) g_variant_get_int32 (value); + + gimp_layer_apply_mask (layer, mode, TRUE); + gimp_image_flush (image); + } +} + +void +layers_mask_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_layer_get_mask (layer)) + { + gboolean active = g_variant_get_boolean (value); + + gimp_layer_set_edit_mask (layer, active); + gimp_image_flush (image); + } +} + +void +layers_mask_show_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_layer_get_mask (layer)) + { + gboolean active = g_variant_get_boolean (value); + + gimp_layer_set_show_mask (layer, active, TRUE); + gimp_image_flush (image); + } +} + +void +layers_mask_disable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_layer_get_mask (layer)) + { + gboolean active = g_variant_get_boolean (value); + + gimp_layer_set_apply_mask (layer, ! active, TRUE); + gimp_image_flush (image); + } +} + +void +layers_mask_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayerMask *mask; + return_if_no_layer (image, layer, data); + + mask = gimp_layer_get_mask (layer); + + if (mask) + { + GimpChannelOps operation = (GimpChannelOps) g_variant_get_int32 (value); + + gimp_item_to_selection (GIMP_ITEM (mask), operation, + TRUE, FALSE, 0.0, 0.0); + gimp_image_flush (image); + } +} + +void +layers_alpha_add_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + gimp_layer_add_alpha (layer); + gimp_image_flush (image); + } +} + +void +layers_alpha_remove_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + gimp_layer_remove_alpha (layer, action_data_get_context (data)); + gimp_image_flush (image); + } +} + +void +layers_alpha_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpChannelOps operation; + return_if_no_layer (image, layer, data); + + operation = (GimpChannelOps) g_variant_get_int32 (value); + + gimp_item_to_selection (GIMP_ITEM (layer), operation, + TRUE, FALSE, 0.0, 0.0); + gimp_image_flush (image); +} + +void +layers_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + gdouble opacity; + GimpUndo *undo; + GimpActionSelectType select_type; + gboolean push_undo = TRUE; + return_if_no_layer (image, layer, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_LAYER_OPACITY); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + opacity = action_select_value (select_type, + gimp_layer_get_opacity (layer), + 0.0, 1.0, 1.0, + 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); + gimp_layer_set_opacity (layer, opacity, push_undo); + gimp_image_flush (image); +} + +void +layers_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayerMode *modes; + gint n_modes; + GimpLayerMode layer_mode; + gint index; + GimpUndo *undo; + GimpActionSelectType select_type; + gboolean push_undo = TRUE; + return_if_no_layer (image, layer, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_LAYER_MODE); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + layer_mode = gimp_layer_get_mode (layer); + + modes = gimp_layer_mode_get_context_array (layer_mode, + GIMP_LAYER_MODE_CONTEXT_LAYER, + &n_modes); + index = layers_mode_index (layer_mode, modes, n_modes); + index = action_select_value (select_type, + index, 0, n_modes - 1, 0, + 0.0, 1.0, 1.0, 0.0, FALSE); + layer_mode = modes[index]; + g_free (modes); + + gimp_layer_set_mode (layer, layer_mode, push_undo); + gimp_image_flush (image); +} + +void +layers_blend_space_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayerColorSpace blend_space; + return_if_no_layer (image, layer, data); + + blend_space = (GimpLayerColorSpace) g_variant_get_int32 (value); + + if (blend_space != gimp_layer_get_blend_space (layer)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_MODE); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + gimp_layer_set_blend_space (layer, blend_space, push_undo); + gimp_image_flush (image); + } +} + +void +layers_composite_space_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayerColorSpace composite_space; + return_if_no_layer (image, layer, data); + + composite_space = (GimpLayerColorSpace) g_variant_get_int32 (value); + + if (composite_space != gimp_layer_get_composite_space (layer)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_MODE); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + gimp_layer_set_composite_space (layer, composite_space, push_undo); + gimp_image_flush (image); + } +} + +void +layers_composite_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpLayerCompositeMode composite_mode; + return_if_no_layer (image, layer, data); + + composite_mode = (GimpLayerCompositeMode) g_variant_get_int32 (value); + + if (composite_mode != gimp_layer_get_composite_mode (layer)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_MODE); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + gimp_layer_set_composite_mode (layer, composite_mode, push_undo); + gimp_image_flush (image); + } +} + +void +layers_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + items_visible_cmd_callback (action, value, image, GIMP_ITEM (layer)); +} + +void +layers_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + items_linked_cmd_callback (action, value, image, GIMP_ITEM (layer)); +} + +void +layers_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + items_lock_content_cmd_callback (action, value, image, GIMP_ITEM (layer)); +} + +void +layers_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + return_if_no_layer (image, layer, data); + + items_lock_position_cmd_callback (action, value, image, GIMP_ITEM (layer)); +} + +void +layers_lock_alpha_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + gboolean lock_alpha; + return_if_no_layer (image, layer, data); + + lock_alpha = g_variant_get_boolean (value); + + if (lock_alpha != gimp_layer_get_lock_alpha (layer)) + { + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, + GIMP_UNDO_LAYER_LOCK_ALPHA); + + if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer)) + push_undo = FALSE; + + gimp_layer_set_lock_alpha (layer, lock_alpha, push_undo); + gimp_image_flush (image); + } +} + +void +layers_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpLayer *layer; + GimpColorTag color_tag; + return_if_no_layer (image, layer, data); + + color_tag = (GimpColorTag) g_variant_get_int32 (value); + + items_color_tag_cmd_callback (action, image, GIMP_ITEM (layer), + color_tag); +} + + +/* private functions */ + +static void +layers_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpLayer *layer, + GimpContext *context, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gint layer_width, + gint layer_height, + gint layer_offset_x, + gint layer_offset_y, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_pixels, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + gboolean rename_text_layer, /* unused */ + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + g_object_set (config, + "layer-new-name", layer_name, + "layer-new-mode", layer_mode, + "layer-new-blend-space", layer_blend_space, + "layer-new-composite-space", layer_composite_space, + "layer-new-composite-mode", layer_composite_mode, + "layer-new-opacity", layer_opacity, + "layer-new-fill-type", layer_fill_type, + NULL); + + layer = gimp_layer_new (image, layer_width, layer_height, + gimp_image_get_layer_format (image, TRUE), + config->layer_new_name, + config->layer_new_opacity, + config->layer_new_mode); + + if (layer) + { + gimp_item_set_offset (GIMP_ITEM (layer), layer_offset_x, layer_offset_y); + gimp_drawable_fill (GIMP_DRAWABLE (layer), context, + config->layer_new_fill_type); + gimp_item_set_visible (GIMP_ITEM (layer), layer_visible, FALSE); + gimp_item_set_linked (GIMP_ITEM (layer), layer_linked, FALSE); + gimp_item_set_color_tag (GIMP_ITEM (layer), layer_color_tag, FALSE); + gimp_item_set_lock_content (GIMP_ITEM (layer), layer_lock_pixels, + FALSE); + gimp_item_set_lock_position (GIMP_ITEM (layer), layer_lock_position, + FALSE); + gimp_layer_set_lock_alpha (layer, layer_lock_alpha, FALSE); + gimp_layer_set_blend_space (layer, layer_blend_space, FALSE); + gimp_layer_set_composite_space (layer, layer_composite_space, FALSE); + gimp_layer_set_composite_mode (layer, layer_composite_mode, FALSE); + + gimp_image_add_layer (image, layer, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + } + else + { + g_warning ("%s: could not allocate new layer", G_STRFUNC); + } + + gtk_widget_destroy (dialog); +} + +static void +layers_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpLayer *layer, + GimpContext *context, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType unused1, + gint unused2, + gint unused3, + gint layer_offset_x, + gint layer_offset_y, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_pixels, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + gboolean rename_text_layer, + gpointer user_data) +{ + GimpItem *item = GIMP_ITEM (layer); + + if (strcmp (layer_name, gimp_object_get_name (layer)) || + layer_mode != gimp_layer_get_mode (layer) || + layer_blend_space != gimp_layer_get_blend_space (layer) || + layer_composite_space != gimp_layer_get_composite_space (layer) || + layer_composite_mode != gimp_layer_get_composite_mode (layer) || + layer_opacity != gimp_layer_get_opacity (layer) || + layer_offset_x != gimp_item_get_offset_x (item) || + layer_offset_y != gimp_item_get_offset_y (item) || + layer_visible != gimp_item_get_visible (item) || + layer_linked != gimp_item_get_linked (item) || + layer_color_tag != gimp_item_get_color_tag (item) || + layer_lock_pixels != gimp_item_get_lock_content (item) || + layer_lock_position != gimp_item_get_lock_position (item) || + layer_lock_alpha != gimp_layer_get_lock_alpha (layer)) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_PROPERTIES, + _("Layer Attributes")); + + if (strcmp (layer_name, gimp_object_get_name (layer))) + { + GError *error = NULL; + + if (! gimp_item_rename (GIMP_ITEM (layer), layer_name, &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (dialog), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } + } + + if (layer_mode != gimp_layer_get_mode (layer)) + gimp_layer_set_mode (layer, layer_mode, TRUE); + + if (layer_blend_space != gimp_layer_get_blend_space (layer)) + gimp_layer_set_blend_space (layer, layer_blend_space, TRUE); + + if (layer_composite_space != gimp_layer_get_composite_space (layer)) + gimp_layer_set_composite_space (layer, layer_composite_space, TRUE); + + if (layer_composite_mode != gimp_layer_get_composite_mode (layer)) + gimp_layer_set_composite_mode (layer, layer_composite_mode, TRUE); + + if (layer_opacity != gimp_layer_get_opacity (layer)) + gimp_layer_set_opacity (layer, layer_opacity, TRUE); + + if (layer_offset_x != gimp_item_get_offset_x (item) || + layer_offset_y != gimp_item_get_offset_y (item)) + { + gimp_item_translate (item, + layer_offset_x - gimp_item_get_offset_x (item), + layer_offset_y - gimp_item_get_offset_y (item), + TRUE); + } + + if (layer_visible != gimp_item_get_visible (item)) + gimp_item_set_visible (item, layer_visible, TRUE); + + if (layer_linked != gimp_item_get_linked (item)) + gimp_item_set_linked (item, layer_linked, TRUE); + + if (layer_color_tag != gimp_item_get_color_tag (item)) + gimp_item_set_color_tag (item, layer_color_tag, TRUE); + + if (layer_lock_pixels != gimp_item_get_lock_content (item)) + gimp_item_set_lock_content (item, layer_lock_pixels, TRUE); + + if (layer_lock_position != gimp_item_get_lock_position (item)) + gimp_item_set_lock_position (item, layer_lock_position, TRUE); + + if (layer_lock_alpha != gimp_layer_get_lock_alpha (layer)) + gimp_layer_set_lock_alpha (layer, layer_lock_alpha, TRUE); + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + } + + if (gimp_item_is_text_layer (GIMP_ITEM (layer))) + { + g_object_set (layer, + "auto-rename", rename_text_layer, + NULL); + } + + gtk_widget_destroy (dialog); +} + +static void +layers_add_mask_callback (GtkWidget *dialog, + GimpLayer *layer, + GimpAddMaskType add_mask_type, + GimpChannel *channel, + gboolean invert, + gpointer user_data) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpLayerMask *mask; + GError *error = NULL; + + g_object_set (config, + "layer-add-mask-type", add_mask_type, + "layer-add-mask-invert", invert, + NULL); + + mask = gimp_layer_create_mask (layer, + config->layer_add_mask_type, + channel); + + if (config->layer_add_mask_invert) + gimp_channel_invert (GIMP_CHANNEL (mask), FALSE); + + if (! gimp_layer_add_mask (layer, mask, TRUE, &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (dialog), GIMP_MESSAGE_WARNING, + error->message); + g_object_unref (mask); + g_clear_error (&error); + return; + } + + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +layers_scale_callback (GtkWidget *dialog, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, /* unused */ + gdouble yresolution, /* unused */ + GimpUnit resolution_unit,/* unused */ + gpointer user_data) +{ + GimpDisplay *display = GIMP_DISPLAY (user_data); + + layer_scale_unit = unit; + layer_scale_interp = interpolation; + + if (width > 0 && height > 0) + { + GimpItem *item = GIMP_ITEM (viewable); + GimpProgress *progress; + GtkWidget *progress_dialog = NULL; + + gtk_widget_destroy (dialog); + + if (width == gimp_item_get_width (item) && + height == gimp_item_get_height (item)) + return; + + if (display) + { + progress = GIMP_PROGRESS (display); + } + else + { + progress_dialog = gimp_progress_dialog_new (); + progress = GIMP_PROGRESS (progress_dialog); + } + + progress = gimp_progress_start (progress, FALSE, _("Scaling")); + + gimp_item_scale_by_origin (item, + width, height, interpolation, + progress, TRUE); + + if (progress) + gimp_progress_end (progress); + + if (progress_dialog) + gtk_widget_destroy (progress_dialog); + + gimp_image_flush (gimp_item_get_image (item)); + } + else + { + g_warning ("Scale Error: " + "Both width and height must be greater than zero."); + } +} + +static void +layers_resize_callback (GtkWidget *dialog, + GimpViewable *viewable, + GimpContext *context, + gint width, + gint height, + GimpUnit unit, + gint offset_x, + gint offset_y, + gdouble unused0, + gdouble unused1, + GimpUnit unused2, + GimpFillType fill_type, + GimpItemSet unused3, + gboolean unused4, + gpointer user_data) +{ + layer_resize_unit = unit; + + if (width > 0 && height > 0) + { + GimpItem *item = GIMP_ITEM (viewable); + GimpImage *image = gimp_item_get_image (item); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + g_object_set (config, + "layer-resize-fill-type", fill_type, + NULL); + + gtk_widget_destroy (dialog); + + if (width == gimp_item_get_width (item) && + height == gimp_item_get_height (item)) + return; + + gimp_item_resize (item, context, fill_type, + width, height, offset_x, offset_y); + gimp_image_flush (gimp_item_get_image (item)); + } + else + { + g_warning ("Resize Error: " + "Both width and height must be greater than zero."); + } +} + +static gint +layers_mode_index (GimpLayerMode layer_mode, + const GimpLayerMode *modes, + gint n_modes) +{ + gint i = 0; + + while (i < (n_modes - 1) && modes[i] != layer_mode) + i++; + + return i; +} diff --git a/app/actions/layers-commands.h b/app/actions/layers-commands.h new file mode 100644 index 0000000..7a8b181 --- /dev/null +++ b/app/actions/layers-commands.h @@ -0,0 +1,173 @@ +/* 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 . + */ + +#ifndef __LAYERS_COMMANDS_H__ +#define __LAYERS_COMMANDS_H__ + + +void layers_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_edit_text_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_new_from_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_new_group_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_anchor_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_merge_down_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_merge_group_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_text_discard_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_text_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_text_along_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_resize_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_resize_to_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_scale_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_crop_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_crop_to_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_mask_add_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_add_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_apply_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_show_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_disable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mask_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_alpha_add_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_alpha_remove_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_alpha_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_blend_space_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_composite_space_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_composite_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void layers_lock_alpha_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void layers_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __LAYERS_COMMANDS_H__ */ diff --git a/app/actions/mypaint-brushes-actions.c b/app/actions/mypaint-brushes-actions.c new file mode 100644 index 0000000..0f39df3 --- /dev/null +++ b/app/actions/mypaint-brushes-actions.c @@ -0,0 +1,142 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpmybrush.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "mypaint-brushes-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry mypaint_brushes_actions[] = +{ + { "mypaint-brushes-popup", GIMP_ICON_MYPAINT_BRUSH, + NC_("mypaint-brushes-action", "MyPaint Brushes Menu"), NULL, NULL, NULL, + GIMP_HELP_MYPAINT_BRUSH_DIALOG }, + + { "mypaint-brushes-new", GIMP_ICON_DOCUMENT_NEW, + NC_("mypaint-brushes-action", "_New MyPaint Brush"), NULL, + NC_("mypaint-brushes-action", "Create a new MyPaint brush"), + data_new_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_NEW }, + + { "mypaint-brushes-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("mypaint-brushes-action", "D_uplicate MyPaint Brush"), NULL, + NC_("mypaint-brushes-action", "Duplicate this MyPaint brush"), + data_duplicate_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_DUPLICATE }, + + { "mypaint-brushes-copy-location", GIMP_ICON_EDIT_COPY, + NC_("mypaint-brushes-action", "Copy MyPaint Brush _Location"), NULL, + NC_("mypaint-brushes-action", "Copy MyPaint brush file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_COPY_LOCATION }, + + { "mypaint-brushes-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("mypaint-brushes-action", "Show in _File Manager"), NULL, + NC_("mypaint-brushes-action", "Show MyPaint brush file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_SHOW_IN_FILE_MANAGER }, + + { "mypaint-brushes-delete", GIMP_ICON_EDIT_DELETE, + NC_("mypaint-brushes-action", "_Delete MyPaint Brush"), NULL, + NC_("mypaint-brushes-action", "Delete this MyPaint brush"), + data_delete_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_DELETE }, + + { "mypaint-brushes-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("mypaint-brushes-action", "_Refresh MyPaint Brushes"), NULL, + NC_("mypaint-brushes-action", "Refresh MyPaint brushes"), + data_refresh_cmd_callback, + GIMP_HELP_MYPAINT_BRUSH_REFRESH } +}; + +static const GimpStringActionEntry mypaint_brushes_edit_actions[] = +{ + { "mypaint-brushes-edit", GIMP_ICON_EDIT, + NC_("mypaint-brushes-action", "_Edit MyPaint Brush..."), NULL, + NC_("mypaint-brushes-action", "Edit MyPaint brush"), + "gimp-mybrush-editor", + GIMP_HELP_MYPAINT_BRUSH_EDIT } +}; + + +void +mypaint_brushes_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "mypaint-brushes-action", + mypaint_brushes_actions, + G_N_ELEMENTS (mypaint_brushes_actions)); + + gimp_action_group_add_string_actions (group, "mypaint-brushes-action", + mypaint_brushes_edit_actions, + G_N_ELEMENTS (mypaint_brushes_edit_actions), + data_edit_cmd_callback); +} + +void +mypaint_brushes_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpMybrush *brush = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + brush = gimp_context_get_mybrush (context); + + if (action_data_sel_count (user_data) > 1) + { + brush = NULL; + } + + if (brush) + { + data = GIMP_DATA (brush); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("mypaint-brushes-edit", brush && FALSE); + SET_SENSITIVE ("mypaint-brushes-duplicate", brush && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("mypaint-brushes-copy-location", file); + SET_SENSITIVE ("mypaint-brushes-show-in-file-manager", file); + SET_SENSITIVE ("mypaint-brushes-delete", brush && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/mypaint-brushes-actions.h b/app/actions/mypaint-brushes-actions.h new file mode 100644 index 0000000..63c93c6 --- /dev/null +++ b/app/actions/mypaint-brushes-actions.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 . + */ + +#ifndef __MYPAINT_BRUSHES_ACTIONS_H__ +#define __MYPAINT_BRUSHES_ACTIONS_H__ + + +void mypaint_brushes_actions_setup (GimpActionGroup *group); +void mypaint_brushes_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __MYPAINT_BRUSHES_ACTIONS_H__ */ diff --git a/app/actions/palette-editor-actions.c b/app/actions/palette-editor-actions.c new file mode 100644 index 0000000..1540126 --- /dev/null +++ b/app/actions/palette-editor-actions.c @@ -0,0 +1,183 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimppaletteeditor.h" + +#include "data-editor-commands.h" +#include "palette-editor-actions.h" +#include "palette-editor-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry palette_editor_actions[] = +{ + { "palette-editor-popup", GIMP_ICON_PALETTE, + NC_("palette-editor-action", "Palette Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_PALETTE_EDITOR_DIALOG }, + + { "palette-editor-edit-color", GIMP_ICON_EDIT, + NC_("palette-editor-action", "_Edit Color..."), NULL, + NC_("palette-editor-action", "Edit this entry"), + palette_editor_edit_color_cmd_callback, + GIMP_HELP_PALETTE_EDITOR_EDIT }, + + { "palette-editor-delete-color", GIMP_ICON_EDIT_DELETE, + NC_("palette-editor-action", "_Delete Color"), NULL, + NC_("palette-editor-action", "Delete this entry"), + palette_editor_delete_color_cmd_callback, + GIMP_HELP_PALETTE_EDITOR_DELETE } +}; + +static const GimpToggleActionEntry palette_editor_toggle_actions[] = +{ + { "palette-editor-edit-active", GIMP_ICON_LINKED, + NC_("palette-editor-action", "Edit Active Palette"), NULL, NULL, + data_editor_edit_active_cmd_callback, + FALSE, + GIMP_HELP_PALETTE_EDITOR_EDIT_ACTIVE } +}; + +static const GimpEnumActionEntry palette_editor_new_actions[] = +{ + { "palette-editor-new-color-fg", GIMP_ICON_DOCUMENT_NEW, + NC_("palette-editor-action", "New Color from _FG"), NULL, + NC_("palette-editor-action", + "Create a new entry from the foreground color"), + FALSE, FALSE, + GIMP_HELP_PALETTE_EDITOR_NEW }, + + { "palette-editor-new-color-bg", GIMP_ICON_DOCUMENT_NEW, + NC_("palette-editor-action", "New Color from _BG"), NULL, + NC_("palette-editor-action", + "Create a new entry from the background color"), + TRUE, FALSE, + GIMP_HELP_PALETTE_EDITOR_NEW } +}; + +static const GimpEnumActionEntry palette_editor_zoom_actions[] = +{ + { "palette-editor-zoom-in", GIMP_ICON_ZOOM_IN, + N_("Zoom _In"), NULL, + N_("Zoom in"), + GIMP_ZOOM_IN, FALSE, + GIMP_HELP_PALETTE_EDITOR_ZOOM_IN }, + + { "palette-editor-zoom-out", GIMP_ICON_ZOOM_OUT, + N_("Zoom _Out"), NULL, + N_("Zoom out"), + GIMP_ZOOM_OUT, FALSE, + GIMP_HELP_PALETTE_EDITOR_ZOOM_OUT }, + + { "palette-editor-zoom-all", GIMP_ICON_ZOOM_FIT_BEST, + N_("Zoom _All"), NULL, + N_("Zoom all"), + GIMP_ZOOM_OUT_MAX, FALSE, + GIMP_HELP_PALETTE_EDITOR_ZOOM_ALL } +}; + + +void +palette_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "palette-editor-action", + palette_editor_actions, + G_N_ELEMENTS (palette_editor_actions)); + + gimp_action_group_add_toggle_actions (group, "palette-editor-action", + palette_editor_toggle_actions, + G_N_ELEMENTS (palette_editor_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "palette-editor-action", + palette_editor_new_actions, + G_N_ELEMENTS (palette_editor_new_actions), + palette_editor_new_color_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + palette_editor_zoom_actions, + G_N_ELEMENTS (palette_editor_zoom_actions), + palette_editor_zoom_cmd_callback); +} + +void +palette_editor_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (user_data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (user_data); + GimpData *data; + gboolean editable = FALSE; + GimpRGB fg; + GimpRGB bg; + gboolean edit_active = FALSE; + + data = data_editor->data; + + if (data) + { + if (data_editor->data_editable) + editable = TRUE; + } + + if (data_editor->context) + { + gimp_context_get_foreground (data_editor->context, &fg); + gimp_context_get_background (data_editor->context, &bg); + } + + edit_active = gimp_data_editor_get_edit_active (data_editor); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_COLOR(action,color) \ + gimp_action_group_set_action_color (group, action, color, FALSE); + + SET_SENSITIVE ("palette-editor-edit-color", editable && editor->color); + SET_SENSITIVE ("palette-editor-delete-color", editable && editor->color); + + SET_SENSITIVE ("palette-editor-new-color-fg", editable); + SET_SENSITIVE ("palette-editor-new-color-bg", editable); + + SET_COLOR ("palette-editor-new-color-fg", data_editor->context ? &fg : NULL); + SET_COLOR ("palette-editor-new-color-bg", data_editor->context ? &bg : NULL); + + SET_SENSITIVE ("palette-editor-zoom-out", data); + SET_SENSITIVE ("palette-editor-zoom-in", data); + SET_SENSITIVE ("palette-editor-zoom-all", data); + + SET_ACTIVE ("palette-editor-edit-active", edit_active); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +#undef SET_COLOR +} diff --git a/app/actions/palette-editor-actions.h b/app/actions/palette-editor-actions.h new file mode 100644 index 0000000..287df8a --- /dev/null +++ b/app/actions/palette-editor-actions.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 . + */ + +#ifndef __PALETTE_EDITOR_ACTIONS_H__ +#define __PALETTE_EDITOR_ACTIONS_H__ + + +void palette_editor_actions_setup (GimpActionGroup *group); +void palette_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __PALETTE_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/palette-editor-commands.c b/app/actions/palette-editor-commands.c new file mode 100644 index 0000000..55a8c0e --- /dev/null +++ b/app/actions/palette-editor-commands.c @@ -0,0 +1,96 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimppalette.h" + +#include "widgets/gimppaletteeditor.h" +#include "widgets/gimppaletteview.h" + +#include "palette-editor-commands.h" + + +/* public functions */ + +void +palette_editor_edit_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (data); + + gimp_palette_editor_edit_color (editor); +} + +void +palette_editor_new_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + gboolean background = (gboolean) g_variant_get_int32 (value); + + if (data_editor->data_editable) + { + GimpPalette *palette = GIMP_PALETTE (data_editor->data); + GimpPaletteEntry *entry; + GimpRGB color; + + if (background) + gimp_context_get_background (data_editor->context, &color); + else + gimp_context_get_foreground (data_editor->context, &color); + + entry = gimp_palette_add_entry (palette, -1, NULL, &color); + gimp_palette_view_select_entry (GIMP_PALETTE_VIEW (editor->view), entry); + } +} + +void +palette_editor_delete_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (data); + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (data); + + if (data_editor->data_editable && editor->color) + { + GimpPalette *palette = GIMP_PALETTE (data_editor->data); + + gimp_palette_delete_entry (palette, editor->color); + } +} + +void +palette_editor_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (data); + GimpZoomType zoom_type = (GimpZoomType) g_variant_get_int32 (value); + + gimp_palette_editor_zoom (editor, zoom_type); +} diff --git a/app/actions/palette-editor-commands.h b/app/actions/palette-editor-commands.h new file mode 100644 index 0000000..29cd43c --- /dev/null +++ b/app/actions/palette-editor-commands.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __PALETTE_EDITOR_COMMANDS_H__ +#define __PALETTE_EDITOR_COMMANDS_H__ + + +void palette_editor_edit_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void palette_editor_new_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void palette_editor_delete_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void palette_editor_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __PALETTE_EDITOR_COMMANDS_H__ */ diff --git a/app/actions/palettes-actions.c b/app/actions/palettes-actions.c new file mode 100644 index 0000000..80dab41 --- /dev/null +++ b/app/actions/palettes-actions.c @@ -0,0 +1,159 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdata.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "palettes-actions.h" +#include "palettes-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry palettes_actions[] = +{ + { "palettes-popup", GIMP_ICON_PALETTE, + NC_("palettes-action", "Palettes Menu"), NULL, NULL, NULL, + GIMP_HELP_PALETTE_DIALOG }, + + { "palettes-new", GIMP_ICON_DOCUMENT_NEW, + NC_("palettes-action", "_New Palette"), NULL, + NC_("palettes-action", "Create a new palette"), + data_new_cmd_callback, + GIMP_HELP_PALETTE_NEW }, + + { "palettes-import", "gtk-convert", + NC_("palettes-action", "_Import Palette..."), NULL, + NC_("palettes-action", "Import palette"), + palettes_import_cmd_callback, + GIMP_HELP_PALETTE_IMPORT }, + + { "palettes-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("palettes-action", "D_uplicate Palette"), NULL, + NC_("palettes-action", "Duplicate this palette"), + data_duplicate_cmd_callback, + GIMP_HELP_PALETTE_DUPLICATE }, + + { "palettes-merge", NULL, + NC_("palettes-action", "_Merge Palettes..."), NULL, + NC_("palettes-action", "Merge palettes"), + palettes_merge_cmd_callback, + GIMP_HELP_PALETTE_MERGE }, + + { "palettes-copy-location", GIMP_ICON_EDIT_COPY, + NC_("palettes-action", "Copy Palette _Location"), NULL, + NC_("palettes-action", "Copy palette file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_PALETTE_COPY_LOCATION }, + + { "palettes-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("palettes-action", "Show in _File Manager"), NULL, + NC_("palettes-action", "Show palette file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_PALETTE_SHOW_IN_FILE_MANAGER }, + + { "palettes-delete", GIMP_ICON_EDIT_DELETE, + NC_("palettes-action", "_Delete Palette"), NULL, + NC_("palettes-action", "Delete this palette"), + data_delete_cmd_callback, + GIMP_HELP_PALETTE_DELETE }, + + { "palettes-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("palettes-action", "_Refresh Palettes"), NULL, + NC_("palettes-action", "Refresh palettes"), + data_refresh_cmd_callback, + GIMP_HELP_PALETTE_REFRESH } +}; + +static const GimpStringActionEntry palettes_edit_actions[] = +{ + { "palettes-edit", GIMP_ICON_EDIT, + NC_("palettes-action", "_Edit Palette..."), NULL, + NC_("palettes-action", "Edit this palette"), + "gimp-palette-editor", + GIMP_HELP_PALETTE_EDIT } +}; + + +void +palettes_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "palettes-action", + palettes_actions, + G_N_ELEMENTS (palettes_actions)); + + gimp_action_group_add_string_actions (group, "palettes-action", + palettes_edit_actions, + G_N_ELEMENTS (palettes_edit_actions), + data_edit_cmd_callback); +} + +void +palettes_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpPalette *palette = NULL; + GimpData *data = NULL; + GFile *file = NULL; + gint sel_count = 0; + + if (context) + { + palette = gimp_context_get_palette (context); + + sel_count = action_data_sel_count (user_data); + + if (sel_count > 1) + { + palette = NULL; + } + + if (palette) + { + data = GIMP_DATA (palette); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("palettes-edit", palette); + SET_SENSITIVE ("palettes-duplicate", palette && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("palettes-merge", sel_count > 1); + SET_SENSITIVE ("palettes-copy-location", file); + SET_SENSITIVE ("palettes-show-in-file-manager", file); + SET_SENSITIVE ("palettes-delete", palette && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/palettes-actions.h b/app/actions/palettes-actions.h new file mode 100644 index 0000000..4029532 --- /dev/null +++ b/app/actions/palettes-actions.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 . + */ + +#ifndef __PALETTES_ACTIONS_H__ +#define __PALETTES_ACTIONS_H__ + + +void palettes_actions_setup (GimpActionGroup *group); +void palettes_actions_update (GimpActionGroup *group, + gpointer user_data); + + +#endif /* __PALETTES_ACTIONS_H__ */ diff --git a/app/actions/palettes-commands.c b/app/actions/palettes-commands.c new file mode 100644 index 0000000..5240e77 --- /dev/null +++ b/app/actions/palettes-commands.c @@ -0,0 +1,152 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpdatafactory.h" +#include "core/gimppalette.h" + +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdatafactoryview.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpview.h" +#include "widgets/gimpwidgets-utils.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "palettes-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void palettes_merge_callback (GtkWidget *widget, + const gchar *palette_name, + gpointer data); + + +/* public functions */ + +void +palettes_import_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + return_if_no_widget (widget, data); + + gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + NULL /*ui_manager*/, + "gimp-palette-import-dialog", -1, TRUE); +} + +void +palettes_merge_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GtkWidget *dialog; + +#define MERGE_DIALOG_KEY "gimp-palettes-merge-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (editor), MERGE_DIALOG_KEY); + + if (! dialog) + { + dialog = gimp_query_string_box (_("Merge Palettes"), + GTK_WIDGET (editor), + gimp_standard_help_func, + GIMP_HELP_PALETTE_MERGE, + _("Enter a name for the merged palette"), + NULL, + G_OBJECT (editor), "destroy", + palettes_merge_callback, editor); + + dialogs_attach_dialog (G_OBJECT (editor), MERGE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + + +/* private functions */ + +static void +palettes_merge_callback (GtkWidget *widget, + const gchar *palette_name, + gpointer data) +{ + GimpContainerEditor *editor = data; + GimpDataFactoryView *view = data; + GimpDataFactory *factory; + GimpContext *context; + GimpPalette *new_palette; + GList *selected = NULL; + GList *list; + + context = gimp_container_view_get_context (editor->view); + factory = gimp_data_factory_view_get_data_factory (view); + + gimp_container_view_get_selected (editor->view, &selected); + + if (g_list_length (selected) < 2) + { + gimp_message_literal (context->gimp, + G_OBJECT (editor), GIMP_MESSAGE_WARNING, + _("There must be at least two palettes selected " + "to merge.")); + g_list_free (selected); + return; + } + + new_palette = GIMP_PALETTE (gimp_data_factory_data_new (factory, context, + palette_name)); + + for (list = selected; list; list = g_list_next (list)) + { + GimpPalette *palette = list->data; + GList *cols; + + for (cols = gimp_palette_get_colors (palette); + cols; + cols = g_list_next (cols)) + { + GimpPaletteEntry *entry = cols->data; + + gimp_palette_add_entry (new_palette, -1, + entry->name, + &entry->color); + } + } + + g_list_free (selected); +} diff --git a/app/actions/palettes-commands.h b/app/actions/palettes-commands.h new file mode 100644 index 0000000..ab7fb24 --- /dev/null +++ b/app/actions/palettes-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __PALETTES_COMMANDS_H__ +#define __PALETTES_COMMANDS_H__ + + +void palettes_import_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void palettes_merge_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __PALETTES_COMMANDS_H__ */ diff --git a/app/actions/patterns-actions.c b/app/actions/patterns-actions.c new file mode 100644 index 0000000..f563ef6 --- /dev/null +++ b/app/actions/patterns-actions.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdata.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "patterns-actions.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry patterns_actions[] = +{ + { "patterns-popup", GIMP_ICON_PATTERN, + NC_("patterns-action", "Patterns Menu"), NULL, NULL, NULL, + GIMP_HELP_PATTERN_DIALOG }, + + { "patterns-open-as-image", GIMP_ICON_DOCUMENT_OPEN, + NC_("patterns-action", "_Open Pattern as Image"), NULL, + NC_("patterns-action", "Open this pattern as an image"), + data_open_as_image_cmd_callback, + GIMP_HELP_PATTERN_OPEN_AS_IMAGE }, + + { "patterns-new", GIMP_ICON_DOCUMENT_NEW, + NC_("patterns-action", "_New Pattern"), NULL, + NC_("patterns-action", "Create a new pattern"), + data_new_cmd_callback, + GIMP_HELP_PATTERN_NEW }, + + { "patterns-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("patterns-action", "D_uplicate Pattern"), NULL, + NC_("patterns-action", "Duplicate this pattern"), + data_duplicate_cmd_callback, + GIMP_HELP_PATTERN_DUPLICATE }, + + { "patterns-copy-location", GIMP_ICON_EDIT_COPY, + NC_("patterns-action", "Copy Pattern _Location"), NULL, + NC_("patterns-action", "Copy pattern file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_PATTERN_COPY_LOCATION }, + + { "patterns-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("patterns-action", "Show in _File Manager"), NULL, + NC_("patterns-action", "Show pattern file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_PATTERN_SHOW_IN_FILE_MANAGER }, + + { "patterns-delete", GIMP_ICON_EDIT_DELETE, + NC_("patterns-action", "_Delete Pattern"), NULL, + NC_("patterns-action", "Delete this pattern"), + data_delete_cmd_callback, + GIMP_HELP_PATTERN_DELETE }, + + { "patterns-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("patterns-action", "_Refresh Patterns"), NULL, + NC_("patterns-action", "Refresh patterns"), + data_refresh_cmd_callback, + GIMP_HELP_PATTERN_REFRESH } +}; + +static const GimpStringActionEntry patterns_edit_actions[] = +{ + { "patterns-edit", GIMP_ICON_EDIT, + NC_("patterns-action", "_Edit Pattern..."), NULL, + NC_("patterns-action", "Edit pattern"), + "gimp-pattern-editor", + GIMP_HELP_PATTERN_EDIT } +}; + + +void +patterns_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "patterns-action", + patterns_actions, + G_N_ELEMENTS (patterns_actions)); + + gimp_action_group_add_string_actions (group, "patterns-action", + patterns_edit_actions, + G_N_ELEMENTS (patterns_edit_actions), + data_edit_cmd_callback); +} + +void +patterns_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpPattern *pattern = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + pattern = gimp_context_get_pattern (context); + + if (action_data_sel_count (user_data) > 1) + { + pattern = NULL; + } + + if (pattern) + { + data = GIMP_DATA (pattern); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("patterns-edit", pattern && FALSE); + SET_SENSITIVE ("patterns-open-as-image", file); + SET_SENSITIVE ("patterns-duplicate", pattern && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("patterns-copy-location", file); + SET_SENSITIVE ("patterns-show-in-file-manager", file); + SET_SENSITIVE ("patterns-delete", pattern && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/patterns-actions.h b/app/actions/patterns-actions.h new file mode 100644 index 0000000..a736b9d --- /dev/null +++ b/app/actions/patterns-actions.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 . + */ + +#ifndef __PATTERNS_ACTIONS_H__ +#define __PATTERNS_ACTIONS_H__ + + +void patterns_actions_setup (GimpActionGroup *group); +void patterns_actions_update (GimpActionGroup *group, + gpointer user_data); + + +#endif /* __PATTERNS_ACTIONS_H__ */ diff --git a/app/actions/plug-in-actions.c b/app/actions/plug-in-actions.c new file mode 100644 index 0000000..5a78324 --- /dev/null +++ b/app/actions/plug-in-actions.c @@ -0,0 +1,517 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" + +#include "plug-in/gimppluginmanager.h" +#include "plug-in/gimppluginmanager-help-domain.h" +#include "plug-in/gimppluginmanager-locale-domain.h" +#include "plug-in/gimppluginmanager-menu-branch.h" +#include "plug-in/gimppluginprocedure.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpactionimpl.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "plug-in-actions.h" +#include "plug-in-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void plug_in_actions_menu_branch_added (GimpPlugInManager *manager, + GFile *file, + const gchar *menu_path, + const gchar *menu_label, + GimpActionGroup *group); +static void plug_in_actions_register_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpActionGroup *group); +static void plug_in_actions_unregister_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpActionGroup *group); +static void plug_in_actions_menu_path_added (GimpPlugInProcedure *proc, + const gchar *menu_path, + GimpActionGroup *group); +static void plug_in_actions_add_proc (GimpActionGroup *group, + GimpPlugInProcedure *proc); + +static gboolean plug_in_actions_check_translation (const gchar *original, + const gchar *translated); +static void plug_in_actions_build_path (GimpActionGroup *group, + const gchar *original, + const gchar *translated); + + +/* private variables */ + +static const GimpActionEntry plug_in_actions[] = +{ + { "plug-in-reset-all", GIMP_ICON_RESET, + NC_("plug-in-action", "Reset all _Filters"), NULL, + NC_("plug-in-action", "Reset all plug-ins to their default settings"), + plug_in_reset_all_cmd_callback, + GIMP_HELP_FILTER_RESET_ALL } +}; + + +/* public functions */ + +void +plug_in_actions_setup (GimpActionGroup *group) +{ + GimpPlugInManager *manager = group->gimp->plug_in_manager; + GSList *list; + + gimp_action_group_add_actions (group, "plug-in-action", + plug_in_actions, + G_N_ELEMENTS (plug_in_actions)); + + for (list = gimp_plug_in_manager_get_menu_branches (manager); + list; + list = g_slist_next (list)) + { + GimpPlugInMenuBranch *branch = list->data; + + plug_in_actions_menu_branch_added (manager, + branch->file, + branch->menu_path, + branch->menu_label, + group); + } + + g_signal_connect_object (manager, + "menu-branch-added", + G_CALLBACK (plug_in_actions_menu_branch_added), + group, 0); + + for (list = manager->plug_in_procedures; + list; + list = g_slist_next (list)) + { + GimpPlugInProcedure *plug_in_proc = list->data; + + if (plug_in_proc->file) + plug_in_actions_register_procedure (group->gimp->pdb, + GIMP_PROCEDURE (plug_in_proc), + group); + } + + g_signal_connect_object (group->gimp->pdb, "register-procedure", + G_CALLBACK (plug_in_actions_register_procedure), + group, 0); + g_signal_connect_object (group->gimp->pdb, "unregister-procedure", + G_CALLBACK (plug_in_actions_unregister_procedure), + group, 0); +} + +void +plug_in_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpPlugInManager *manager = group->gimp->plug_in_manager; + GimpDrawable *drawable = NULL; + GSList *list; + + if (image) + drawable = gimp_image_get_active_drawable (image); + + for (list = manager->plug_in_procedures; list; list = g_slist_next (list)) + { + GimpPlugInProcedure *proc = list->data; + + if ((proc->menu_label || proc->menu_paths) && + ! proc->file_proc && + proc->image_types_val) + { + GimpProcedure *procedure = GIMP_PROCEDURE (proc); + gboolean sensitive; + const gchar *tooltip; + + sensitive = gimp_procedure_get_sensitive (procedure, + GIMP_OBJECT (drawable), + &tooltip); + + gimp_action_group_set_action_sensitive (group, + gimp_object_get_name (proc), + sensitive); + + if (sensitive || ! drawable || ! tooltip) + tooltip = gimp_procedure_get_blurb (procedure); + + gimp_action_group_set_action_tooltip (group, + gimp_object_get_name (proc), + tooltip); + } + } +} + + +/* private functions */ + +static void +plug_in_actions_menu_branch_added (GimpPlugInManager *manager, + GFile *file, + const gchar *menu_path, + const gchar *menu_label, + GimpActionGroup *group) +{ + const gchar *locale_domain; + const gchar *path_translated; + const gchar *label_translated; + gchar *full; + gchar *full_translated; + + locale_domain = gimp_plug_in_manager_get_locale_domain (manager, file, NULL); + + path_translated = dgettext (locale_domain, menu_path); + label_translated = dgettext (locale_domain, menu_label); + + full = g_strconcat (menu_path, "/", menu_label, NULL); + full_translated = g_strconcat (path_translated, "/", label_translated, NULL); + + if (plug_in_actions_check_translation (full, full_translated)) + plug_in_actions_build_path (group, full, full_translated); + else + plug_in_actions_build_path (group, full, full); + + g_free (full_translated); + g_free (full); +} + +static void +plug_in_actions_register_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpActionGroup *group) +{ + if (GIMP_IS_PLUG_IN_PROCEDURE (procedure)) + { + GimpPlugInProcedure *plug_in_proc = GIMP_PLUG_IN_PROCEDURE (procedure); + + g_signal_connect_object (plug_in_proc, "menu-path-added", + G_CALLBACK (plug_in_actions_menu_path_added), + group, 0); + + if ((plug_in_proc->menu_label || plug_in_proc->menu_paths) && + ! plug_in_proc->file_proc) + { +#if 0 + g_print ("%s: %s\n", G_STRFUNC, + gimp_object_get_name (procedure)); +#endif + + plug_in_actions_add_proc (group, plug_in_proc); + } + } +} + +static void +plug_in_actions_unregister_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpActionGroup *group) +{ + if (GIMP_IS_PLUG_IN_PROCEDURE (procedure)) + { + GimpPlugInProcedure *plug_in_proc = GIMP_PLUG_IN_PROCEDURE (procedure); + + g_signal_handlers_disconnect_by_func (plug_in_proc, + plug_in_actions_menu_path_added, + group); + + if ((plug_in_proc->menu_label || plug_in_proc->menu_paths) && + ! plug_in_proc->file_proc) + { + GimpAction *action; + +#if 0 + g_print ("%s: %s\n", G_STRFUNC, + gimp_object_get_name (procedure)); +#endif + + action = gimp_action_group_get_action (group, + gimp_object_get_name (procedure)); + + if (action) + gimp_action_group_remove_action (group, action); + } + } +} + +static void +plug_in_actions_menu_path_added (GimpPlugInProcedure *plug_in_proc, + const gchar *menu_path, + GimpActionGroup *group) +{ + const gchar *locale_domain; + const gchar *path_translated; + +#if 0 + g_print ("%s: %s (%s)\n", G_STRFUNC, + gimp_object_get_name (plug_in_proc), menu_path); +#endif + + locale_domain = gimp_plug_in_procedure_get_locale_domain (plug_in_proc); + + path_translated = dgettext (locale_domain, menu_path); + + if (plug_in_actions_check_translation (menu_path, path_translated)) + plug_in_actions_build_path (group, menu_path, path_translated); + else + plug_in_actions_build_path (group, menu_path, menu_path); +} + +static void +plug_in_actions_add_proc (GimpActionGroup *group, + GimpPlugInProcedure *proc) +{ + GimpProcedureActionEntry entry; + const gchar *locale_domain; + gchar *path_original = NULL; + gchar *path_translated = NULL; + + locale_domain = gimp_plug_in_procedure_get_locale_domain (proc); + + if (! proc->menu_label) + { + gchar *p1, *p2; + + path_original = proc->menu_paths->data; + path_translated = dgettext (locale_domain, path_original); + + path_original = g_strdup (path_original); + + if (plug_in_actions_check_translation (path_original, path_translated)) + path_translated = g_strdup (path_translated); + else + path_translated = g_strdup (path_original); + + p1 = strrchr (path_original, '/'); + p2 = strrchr (path_translated, '/'); + + if (p1 && p2) + { + *p1 = '\0'; + *p2 = '\0'; + } + else + { + g_warning ("bad menu path for procedure \"%s\": \"%s\"", + gimp_object_get_name (proc), path_original); + + g_free (path_original); + g_free (path_translated); + + return; + } + } + + entry.name = gimp_object_get_name (proc); + entry.icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (proc)); + entry.label = gimp_procedure_get_menu_label (GIMP_PROCEDURE (proc)); + entry.accelerator = NULL; + entry.tooltip = gimp_procedure_get_blurb (GIMP_PROCEDURE (proc)); + entry.procedure = GIMP_PROCEDURE (proc); + entry.help_id = gimp_procedure_get_help_id (GIMP_PROCEDURE (proc)); + + gimp_action_group_add_procedure_actions (group, &entry, 1, + plug_in_run_cmd_callback); + + if (proc->menu_label) + { + GList *list; + + for (list = proc->menu_paths; list; list = g_list_next (list)) + { + const gchar *original = list->data; + const gchar *translated = dgettext (locale_domain, original); + + if (plug_in_actions_check_translation (original, translated)) + plug_in_actions_build_path (group, original, translated); + else + plug_in_actions_build_path (group, original, original); + } + } + else + { + plug_in_actions_build_path (group, path_original, path_translated); + + g_free (path_original); + g_free (path_translated); + } + + if ((proc->menu_label || proc->menu_paths) && + ! proc->file_proc && + proc->image_types_val) + { + GimpContext *context = gimp_get_user_context (group->gimp); + GimpImage *image = gimp_context_get_image (context); + GimpDrawable *drawable = NULL; + gboolean sensitive; + const gchar *tooltip; + + if (image) + drawable = gimp_image_get_active_drawable (image); + + sensitive = gimp_procedure_get_sensitive (GIMP_PROCEDURE (proc), + GIMP_OBJECT (drawable), + &tooltip); + + gimp_action_group_set_action_sensitive (group, + gimp_object_get_name (proc), + sensitive); + + if (! sensitive && drawable && tooltip) + gimp_action_group_set_action_tooltip (group, + gimp_object_get_name (proc), + tooltip); + } +} + +static gboolean +plug_in_actions_check_translation (const gchar *original, + const gchar *translated) +{ + const gchar *p1; + const gchar *p2; + + /* first check if is present and identical in both strings */ + p1 = strchr (original, '>'); + p2 = strchr (translated, '>'); + + if (!p1 || !p2 || + (p1 - original) != (p2 - translated) || + strncmp (original, translated, p1 - original)) + { + g_printerr ("bad translation \"%s\"\n" + "for menu path \"%s\"\n" + "( must not be translated)\n\n", + translated, original); + return FALSE; + } + + p1++; + p2++; + + /* then check if either a '/' or nothing follows in *both* strings */ + if (! ((*p1 == '/' && *p2 == '/') || + (*p1 == '\0' && *p2 == '\0'))) + { + g_printerr ("bad translation \"%s\"\n" + "for menu path \"%s\"\n" + "( must be followed by either nothing or '/')\n\n", + translated, original); + return FALSE; + } + + /* then check the number of slashes in the remaining string */ + while (p1 && p2) + { + p1 = strchr (p1, '/'); + p2 = strchr (p2, '/'); + + if (p1) p1++; + if (p2) p2++; + } + + if (p1 || p2) + { + g_printerr ("bad translation \"%s\"\n" + "for menu path \"%s\"\n" + "(number of '/' must be the same)\n\n", + translated, original); + return FALSE; + } + + return TRUE; +} + +static void +plug_in_actions_build_path (GimpActionGroup *group, + const gchar *path_original, + const gchar *path_translated) +{ + GHashTable *path_table; + gchar *copy_original; + gchar *copy_translated; + gchar *p1, *p2; + + path_table = g_object_get_data (G_OBJECT (group), "plug-in-path-table"); + + if (! path_table) + { + path_table = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + g_object_set_data_full (G_OBJECT (group), "plug-in-path-table", + path_table, + (GDestroyNotify) g_hash_table_destroy); + } + + copy_original = gimp_strip_uline (path_original); + copy_translated = g_strdup (path_translated); + + p1 = strrchr (copy_original, '/'); + p2 = strrchr (copy_translated, '/'); + + if (p1 && p2 && ! g_hash_table_lookup (path_table, copy_original)) + { + GimpAction *action; + gchar *label; + + label = p2 + 1; + +#if 0 + g_print ("adding plug-in submenu '%s' (%s)\n", + copy_original, label); +#endif + + action = gimp_action_impl_new (copy_original, label, NULL, NULL, NULL); + gimp_action_group_add_action (group, action); + g_object_unref (action); + + g_hash_table_insert (path_table, g_strdup (copy_original), action); + + *p1 = '\0'; + *p2 = '\0'; + + /* recursively call ourselves with the last part of the path removed */ + plug_in_actions_build_path (group, copy_original, copy_translated); + } + + g_free (copy_original); + g_free (copy_translated); +} diff --git a/app/actions/plug-in-actions.h b/app/actions/plug-in-actions.h new file mode 100644 index 0000000..a586acd --- /dev/null +++ b/app/actions/plug-in-actions.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 . + */ + +#ifndef __PLUG_IN_ACTIONS_H__ +#define __PLUG_IN_ACTIONS_H__ + + +void plug_in_actions_setup (GimpActionGroup *group); +void plug_in_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __PLUG_IN_ACTIONS_H__ */ diff --git a/app/actions/plug-in-commands.c b/app/actions/plug-in-commands.c new file mode 100644 index 0000000..ed7428d --- /dev/null +++ b/app/actions/plug-in-commands.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimp-filter-history.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpitem.h" +#include "core/gimpparamspecs.h" +#include "core/gimpprogress.h" + +#include "pdb/gimpprocedure.h" + +#include "plug-in/gimppluginmanager.h" +#include "plug-in/gimppluginmanager-data.h" + +#include "widgets/gimpbufferview.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdatafactoryview.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpimageeditor.h" +#include "widgets/gimpitemtreeview.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "plug-in-commands.h" +#include "procedure-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void plug_in_reset_all_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp); + + +/* public functions */ + +void +plug_in_run_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpValueArray *args = NULL; + GimpDisplay *display = NULL; + GimpProcedure *procedure; + gsize hack; + return_if_no_gimp (gimp, data); + + hack = g_variant_get_uint64 (value); + + procedure = GSIZE_TO_POINTER (hack); + + switch (procedure->proc_type) + { + case GIMP_EXTENSION: + args = procedure_commands_get_run_mode_arg (procedure); + break; + + case GIMP_PLUGIN: + case GIMP_TEMPORARY: + if (GIMP_IS_DATA_FACTORY_VIEW (data) || + GIMP_IS_BUFFER_VIEW (data)) + { + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpObject *object; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + object = gimp_context_get_by_type (context, + gimp_container_get_children_type (container)); + + args = procedure_commands_get_data_args (procedure, object); + } + else if (GIMP_IS_IMAGE_EDITOR (data)) + { + GimpImageEditor *editor = GIMP_IMAGE_EDITOR (data); + GimpImage *image; + + image = gimp_image_editor_get_image (editor); + + args = procedure_commands_get_image_args (procedure, image); + } + else if (GIMP_IS_ITEM_TREE_VIEW (data)) + { + GimpItemTreeView *view = GIMP_ITEM_TREE_VIEW (data); + GimpImage *image; + GimpItem *item; + + image = gimp_item_tree_view_get_image (view); + + if (image) + item = GIMP_ITEM_TREE_VIEW_GET_CLASS (view)->get_active_item (image); + else + item = NULL; + + args = procedure_commands_get_item_args (procedure, image, item); + } + else + { + display = action_data_get_display (data); + + args = procedure_commands_get_display_args (procedure, display, NULL); + } + break; + + case GIMP_INTERNAL: + g_warning ("Unhandled procedure type."); + break; + } + + if (args) + { + if (procedure_commands_run_procedure_async (procedure, gimp, + GIMP_PROGRESS (display), + GIMP_RUN_INTERACTIVE, args, + display)) + { + /* remember only image plug-ins */ + if (procedure->num_args >= 2 && + GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1])) + { + gimp_filter_history_add (gimp, procedure); + } + } + + gimp_value_array_unref (args); + } +} + +void +plug_in_reset_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GtkWidget *dialog; + return_if_no_gimp (gimp, data); + +#define RESET_FILTERS_DIALOG_KEY "gimp-reset-all-filters-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (gimp), RESET_FILTERS_DIALOG_KEY); + + if (! dialog) + { + dialog = gimp_message_dialog_new (_("Reset all Filters"), + GIMP_ICON_DIALOG_QUESTION, + NULL, 0, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Reset"), 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 (plug_in_reset_all_response), + gimp); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Do you really want to reset all " + "filters to default values?")); + + dialogs_attach_dialog (G_OBJECT (gimp), RESET_FILTERS_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + + +/* private functions */ + +static void +plug_in_reset_all_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp) +{ + gtk_widget_destroy (dialog); + + if (response_id == GTK_RESPONSE_OK) + gimp_plug_in_manager_data_free (gimp->plug_in_manager); +} diff --git a/app/actions/plug-in-commands.h b/app/actions/plug-in-commands.h new file mode 100644 index 0000000..b648482 --- /dev/null +++ b/app/actions/plug-in-commands.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __PLUG_IN_COMMANDS_H__ +#define __PLUG_IN_COMMANDS_H__ + + +void plug_in_run_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void plug_in_reset_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __PLUG_IN_COMMANDS_H__ */ diff --git a/app/actions/procedure-commands.c b/app/actions/procedure-commands.c new file mode 100644 index 0000000..93ec29d --- /dev/null +++ b/app/actions/procedure-commands.c @@ -0,0 +1,326 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimpparamspecs.h" +#include "core/gimpprogress.h" + +#include "pdb/gimpprocedure.h" + +#include "display/gimpdisplay.h" + +#include "procedure-commands.h" + + +GimpValueArray * +procedure_commands_get_run_mode_arg (GimpProcedure *procedure) +{ + GimpValueArray *args; + gint n_args = 0; + + args = gimp_procedure_get_arguments (procedure); + + /* initialize the first argument */ + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_INT32 (procedure->args[n_args])) + { + g_value_set_int (gimp_value_array_index (args, n_args), + GIMP_RUN_INTERACTIVE); + n_args++; + } + + gimp_value_array_truncate (args, n_args); + + return args; +} + +GimpValueArray * +procedure_commands_get_data_args (GimpProcedure *procedure, + GimpObject *object) +{ + GimpValueArray *args; + gint n_args = 0; + + args = gimp_procedure_get_arguments (procedure); + + /* initialize the first argument */ + g_value_set_int (gimp_value_array_index (args, n_args), + GIMP_RUN_INTERACTIVE); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_STRING (procedure->args[n_args])) + { + if (object) + { + g_value_set_string (gimp_value_array_index (args, n_args), + gimp_object_get_name (object)); + n_args++; + } + else + { + g_warning ("Uh-oh, no active data object for the plug-in!"); + gimp_value_array_unref (args); + return NULL; + } + } + + gimp_value_array_truncate (args, n_args); + + return args; +} + +GimpValueArray * +procedure_commands_get_image_args (GimpProcedure *procedure, + GimpImage *image) +{ + GimpValueArray *args; + gint n_args = 0; + + args = gimp_procedure_get_arguments (procedure); + + /* initialize the first argument */ + g_value_set_int (gimp_value_array_index (args, n_args), + GIMP_RUN_INTERACTIVE); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[n_args])) + { + if (image) + { + gimp_value_set_image (gimp_value_array_index (args, n_args), image); + n_args++; + } + else + { + g_warning ("Uh-oh, no active image for the plug-in!"); + gimp_value_array_unref (args); + return NULL; + } + } + + gimp_value_array_truncate (args, n_args); + + return args; +} + +GimpValueArray * +procedure_commands_get_item_args (GimpProcedure *procedure, + GimpImage *image, + GimpItem *item) +{ + GimpValueArray *args; + gint n_args = 0; + + args = gimp_procedure_get_arguments (procedure); + + /* initialize the first argument */ + g_value_set_int (gimp_value_array_index (args, n_args), + GIMP_RUN_INTERACTIVE); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[n_args])) + { + if (image) + { + gimp_value_set_image (gimp_value_array_index (args, n_args), image); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_ITEM_ID (procedure->args[n_args])) + { + if (item && + g_type_is_a (G_TYPE_FROM_INSTANCE (item), + GIMP_PARAM_SPEC_ITEM_ID (procedure->args[n_args])->item_type)) + { + gimp_value_set_item (gimp_value_array_index (args, n_args), + item); + n_args++; + } + else + { + g_warning ("Uh-oh, no active item for the plug-in!"); + gimp_value_array_unref (args); + return NULL; + } + } + } + } + + gimp_value_array_truncate (args, n_args); + + return args; +} + +GimpValueArray * +procedure_commands_get_display_args (GimpProcedure *procedure, + GimpDisplay *display, + GimpObject *settings) +{ + GimpValueArray *args; + gint n_args = 0; + + args = gimp_procedure_get_arguments (procedure); + + /* initialize the first argument */ + g_value_set_int (gimp_value_array_index (args, n_args), + GIMP_RUN_INTERACTIVE); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_DISPLAY_ID (procedure->args[n_args])) + { + if (display) + { + gimp_value_set_display (gimp_value_array_index (args, n_args), + GIMP_OBJECT (display)); + n_args++; + } + else + { + g_warning ("Uh-oh, no active display for the plug-in!"); + gimp_value_array_unref (args); + return NULL; + } + } + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[n_args])) + { + GimpImage *image = display ? gimp_display_get_image (display) : NULL; + + if (image) + { + gimp_value_set_image (gimp_value_array_index (args, n_args), image); + n_args++; + + if (gimp_value_array_length (args) > n_args && + GIMP_IS_PARAM_SPEC_DRAWABLE_ID (procedure->args[n_args])) + { + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + gimp_value_set_drawable (gimp_value_array_index (args, n_args), + drawable); + n_args++; + } + else + { + g_warning ("Uh-oh, no active drawable for the plug-in!"); + gimp_value_array_unref (args); + return NULL; + } + } + } + } + + if (gimp_value_array_length (args) > n_args && + g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (procedure->args[n_args]), + GIMP_TYPE_OBJECT)) + { + g_value_set_object (gimp_value_array_index (args, n_args), settings); + n_args++; + } + + gimp_value_array_truncate (args, n_args); + + return args; +} + +gboolean +procedure_commands_run_procedure (GimpProcedure *procedure, + Gimp *gimp, + GimpProgress *progress, + GimpValueArray *args) +{ + GimpValueArray *return_vals; + GError *error = NULL; + + g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), FALSE); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (args != NULL, FALSE); + + g_value_set_int (gimp_value_array_index (args, 0), GIMP_RUN_NONINTERACTIVE); + + return_vals = gimp_procedure_execute (procedure, gimp, + gimp_get_user_context (gimp), + progress, args, + &error); + gimp_value_array_unref (return_vals); + + if (error) + { + gimp_message_literal (gimp, + G_OBJECT (progress), GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + + return FALSE; + } + + return TRUE; +} + +gboolean +procedure_commands_run_procedure_async (GimpProcedure *procedure, + Gimp *gimp, + GimpProgress *progress, + GimpRunMode run_mode, + GimpValueArray *args, + GimpDisplay *display) +{ + GError *error = NULL; + + g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), FALSE); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (display == NULL || GIMP_IS_DISPLAY (display), FALSE); + g_return_val_if_fail (args != NULL, FALSE); + + g_value_set_int (gimp_value_array_index (args, 0), run_mode); + + gimp_procedure_execute_async (procedure, gimp, + gimp_get_user_context (gimp), + progress, args, + GIMP_OBJECT (display), &error); + + if (error) + { + gimp_message_literal (gimp, + G_OBJECT (progress), GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + + return FALSE; + } + + return TRUE; +} diff --git a/app/actions/procedure-commands.h b/app/actions/procedure-commands.h new file mode 100644 index 0000000..13c1109 --- /dev/null +++ b/app/actions/procedure-commands.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#ifndef __PROCEDURE_COMMANDS_H__ +#define __PROCEDURE_COMMANDS_H__ + + +GimpValueArray * procedure_commands_get_run_mode_arg (GimpProcedure *procedure); +GimpValueArray * procedure_commands_get_data_args (GimpProcedure *procedure, + GimpObject *object); +GimpValueArray * procedure_commands_get_image_args (GimpProcedure *procedure, + GimpImage *image); +GimpValueArray * procedure_commands_get_item_args (GimpProcedure *procedure, + GimpImage *image, + GimpItem *item); +GimpValueArray * procedure_commands_get_display_args (GimpProcedure *procedure, + GimpDisplay *display, + GimpObject *settings); + +gboolean procedure_commands_run_procedure (GimpProcedure *procedure, + Gimp *gimp, + GimpProgress *progress, + GimpValueArray *args); +gboolean procedure_commands_run_procedure_async (GimpProcedure *procedure, + Gimp *gimp, + GimpProgress *progress, + GimpRunMode run_mode, + GimpValueArray *args, + GimpDisplay *display); + + +#endif /* __PROCEDURE_COMMANDS_H__ */ diff --git a/app/actions/quick-mask-actions.c b/app/actions/quick-mask-actions.c new file mode 100644 index 0000000..542d0f4 --- /dev/null +++ b/app/actions/quick-mask-actions.c @@ -0,0 +1,138 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpimage.h" +#include "core/gimpimage-quick-mask.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "quick-mask-actions.h" +#include "quick-mask-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry quick_mask_actions[] = +{ + { "quick-mask-popup", NULL, + NC_("quick-mask-action", "Quick Mask Menu"), NULL, NULL, NULL, + GIMP_HELP_QUICK_MASK }, + + { "quick-mask-configure", NULL, + NC_("quick-mask-action", "_Configure Color and Opacity..."), NULL, NULL, + quick_mask_configure_cmd_callback, + GIMP_HELP_QUICK_MASK_EDIT } +}; + +static const GimpToggleActionEntry quick_mask_toggle_actions[] = +{ + { "quick-mask-toggle", GIMP_ICON_QUICK_MASK_ON, + NC_("quick-mask-action", "Toggle _Quick Mask"), "Q", + NC_("quick-mask-action", "Toggle Quick Mask on/off"), + quick_mask_toggle_cmd_callback, + FALSE, + GIMP_HELP_QUICK_MASK_TOGGLE } +}; + +static const GimpRadioActionEntry quick_mask_invert_actions[] = +{ + { "quick-mask-invert-on", NULL, + NC_("quick-mask-action", "Mask _Selected Areas"), NULL, NULL, + TRUE, + GIMP_HELP_QUICK_MASK_INVERT }, + + { "quick-mask-invert-off", NULL, + NC_("quick-mask-action", "Mask _Unselected Areas"), NULL, NULL, + FALSE, + GIMP_HELP_QUICK_MASK_INVERT } +}; + + +void +quick_mask_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "quick-mask-action", + quick_mask_actions, + G_N_ELEMENTS (quick_mask_actions)); + + gimp_action_group_add_toggle_actions (group, "quick-mask-action", + quick_mask_toggle_actions, + G_N_ELEMENTS (quick_mask_toggle_actions)); + + gimp_action_group_add_radio_actions (group, "quick-mask-action", + quick_mask_invert_actions, + G_N_ELEMENTS (quick_mask_invert_actions), + NULL, + FALSE, + quick_mask_invert_cmd_callback); +} + +void +quick_mask_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + gboolean quick_mask_state = FALSE; + gboolean quick_mask_inverted = FALSE; + GimpRGB quick_mask_color; + + if (image) + { + quick_mask_state = gimp_image_get_quick_mask_state (image); + quick_mask_inverted = gimp_image_get_quick_mask_inverted (image); + + gimp_image_get_quick_mask_color (image, &quick_mask_color); + } + +#define SET_SENSITIVE(action,sensitive) \ + gimp_action_group_set_action_sensitive (group, action, (sensitive) != 0) +#define SET_ACTIVE(action,active) \ + gimp_action_group_set_action_active (group, action, (active) != 0) +#define SET_COLOR(action,color) \ + gimp_action_group_set_action_color (group, action, (color), FALSE) + + SET_SENSITIVE ("quick-mask-toggle", image); + SET_ACTIVE ("quick-mask-toggle", quick_mask_state); + + SET_SENSITIVE ("quick-mask-invert-on", image); + SET_SENSITIVE ("quick-mask-invert-off", image); + + if (quick_mask_inverted) + SET_ACTIVE ("quick-mask-invert-on", TRUE); + else + SET_ACTIVE ("quick-mask-invert-off", TRUE); + + SET_SENSITIVE ("quick-mask-configure", image); + + if (image) + SET_COLOR ("quick-mask-configure", &quick_mask_color); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +#undef SET_COLOR +} diff --git a/app/actions/quick-mask-actions.h b/app/actions/quick-mask-actions.h new file mode 100644 index 0000000..78ea13e --- /dev/null +++ b/app/actions/quick-mask-actions.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 . + */ + +#ifndef __QUICK_MASK_ACTIONS_H__ +#define __QUICK_MASK_ACTIONS_H__ + + +void quick_mask_actions_setup (GimpActionGroup *group); +void quick_mask_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __QUICK_MASK_ACTIONS_H__ */ diff --git a/app/actions/quick-mask-commands.c b/app/actions/quick-mask-commands.c new file mode 100644 index 0000000..3ea9d01 --- /dev/null +++ b/app/actions/quick-mask-commands.c @@ -0,0 +1,182 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimpimage-quick-mask.h" + +#include "widgets/gimphelp-ids.h" + +#include "dialogs/dialogs.h" +#include "dialogs/channel-options-dialog.h" +#include "dialogs/item-options-dialog.h" + +#include "actions.h" +#include "quick-mask-commands.h" + +#include "gimp-intl.h" + + +#define RGBA_EPSILON 1e-6 + + +/* local function prototypes */ + +static void quick_mask_configure_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data); + + +/* public functions */ + +void +quick_mask_toggle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + gboolean active; + return_if_no_image (image, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_image_get_quick_mask_state (image)) + { + gimp_image_set_quick_mask_state (image, active); + gimp_image_flush (image); + } +} + +void +quick_mask_invert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + gboolean inverted; + return_if_no_image (image, data); + + inverted = (gboolean) g_variant_get_int32 (value); + + if (inverted != gimp_image_get_quick_mask_inverted (image)) + { + gimp_image_quick_mask_invert (image); + gimp_image_flush (image); + } +} + +void +quick_mask_configure_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + +#define CONFIGURE_DIALOG_KEY "gimp-image-quick-mask-configure-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), CONFIGURE_DIALOG_KEY); + + if (! dialog) + { + GimpRGB color; + + gimp_image_get_quick_mask_color (image, &color); + + dialog = channel_options_dialog_new (image, NULL, + action_data_get_context (data), + widget, + _("Quick Mask Attributes"), + "gimp-quick-mask-edit", + GIMP_ICON_QUICK_MASK_ON, + _("Edit Quick Mask Attributes"), + GIMP_HELP_QUICK_MASK_EDIT, + _("Edit Quick Mask Color"), + _("_Mask opacity:"), + FALSE, + NULL, + &color, + FALSE, + FALSE, + GIMP_COLOR_TAG_NONE, + FALSE, + FALSE, + quick_mask_configure_callback, + NULL); + + item_options_dialog_set_switches_visible (dialog, FALSE); + + dialogs_attach_dialog (G_OBJECT (image), CONFIGURE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + + +/* private functions */ + +static void +quick_mask_configure_callback (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data) +{ + GimpRGB old_color; + + gimp_image_get_quick_mask_color (image, &old_color); + + if (gimp_rgba_distance (&old_color, channel_color) > RGBA_EPSILON) + { + gimp_image_set_quick_mask_color (image, channel_color); + gimp_image_flush (image); + } + + gtk_widget_destroy (dialog); +} diff --git a/app/actions/quick-mask-commands.h b/app/actions/quick-mask-commands.h new file mode 100644 index 0000000..1e8628c --- /dev/null +++ b/app/actions/quick-mask-commands.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 . + */ + +#ifndef __QUICK_MASK_COMMANDS_H__ +#define __QUICK_MASK_COMMANDS_H__ + + +void quick_mask_toggle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void quick_mask_invert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void quick_mask_configure_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __QUICK_MASK_COMMANDS_H__ */ diff --git a/app/actions/sample-points-actions.c b/app/actions/sample-points-actions.c new file mode 100644 index 0000000..60111c4 --- /dev/null +++ b/app/actions/sample-points-actions.c @@ -0,0 +1,81 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpsamplepointeditor.h" + +#include "sample-points-actions.h" +#include "sample-points-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry sample_points_actions[] = +{ + { "sample-points-popup", GIMP_ICON_SAMPLE_POINT, + NC_("sample-points-action", "Sample Point Menu"), NULL, NULL, NULL, + GIMP_HELP_SAMPLE_POINT_DIALOG } +}; + +static const GimpToggleActionEntry sample_points_toggle_actions[] = +{ + { "sample-points-sample-merged", NULL, + NC_("sample-points-action", "_Sample Merged"), "", + NC_("sample-points-action", + "Use the composite color of all visible layers"), + sample_points_sample_merged_cmd_callback, + TRUE, + GIMP_HELP_SAMPLE_POINT_SAMPLE_MERGED } +}; + + +void +sample_points_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "sample-points-action", + sample_points_actions, + G_N_ELEMENTS (sample_points_actions)); + + gimp_action_group_add_toggle_actions (group, "sample-points-action", + sample_points_toggle_actions, + G_N_ELEMENTS (sample_points_toggle_actions)); +} + +void +sample_points_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (data); + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_ACTIVE ("sample-points-sample-merged", + gimp_sample_point_editor_get_sample_merged (editor)); + +#undef SET_ACTIVE +} diff --git a/app/actions/sample-points-actions.h b/app/actions/sample-points-actions.h new file mode 100644 index 0000000..47b40fb --- /dev/null +++ b/app/actions/sample-points-actions.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 . + */ + +#ifndef __SAMPLE_POINTS_ACIONS_H__ +#define __SAMPLE_POINTS_ACIONS_H__ + + +void sample_points_actions_setup (GimpActionGroup *group); +void sample_points_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __SAMPLE_POINTS_ACTIONS_H__ */ diff --git a/app/actions/sample-points-commands.c b/app/actions/sample-points-commands.c new file mode 100644 index 0000000..c943c6d --- /dev/null +++ b/app/actions/sample-points-commands.c @@ -0,0 +1,41 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "actions-types.h" + +#include "widgets/gimpsamplepointeditor.h" + +#include "sample-points-commands.h" + + +/* public functions */ + +void +sample_points_sample_merged_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (data); + gboolean active = g_variant_get_boolean (value); + + gimp_sample_point_editor_set_sample_merged (editor, active); +} diff --git a/app/actions/sample-points-commands.h b/app/actions/sample-points-commands.h new file mode 100644 index 0000000..bfdd4ef --- /dev/null +++ b/app/actions/sample-points-commands.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 . + */ + +#ifndef __SAMPLE_POINTS_COMMANDS_H__ +#define __SAMPLE_POINTS_COMMANDS_H__ + + +void sample_points_sample_merged_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __SAMPLE_POINTS_COMMANDS_H__ */ diff --git a/app/actions/select-actions.c b/app/actions/select-actions.c new file mode 100644 index 0000000..294b077 --- /dev/null +++ b/app/actions/select-actions.c @@ -0,0 +1,199 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "select-actions.h" +#include "select-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry select_actions[] = +{ + { "selection-popup", GIMP_ICON_SELECTION, + NC_("select-action", "Selection Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_SELECTION_DIALOG }, + + { "select-menu", NULL, NC_("select-action", "_Select") }, + + { "select-all", GIMP_ICON_SELECTION_ALL, + NC_("select-action", "_All"), "A", + NC_("select-action", "Select everything"), + select_all_cmd_callback, + GIMP_HELP_SELECTION_ALL }, + + { "select-none", GIMP_ICON_SELECTION_NONE, + NC_("select-action", "_None"), "A", + NC_("select-action", "Dismiss the selection"), + select_none_cmd_callback, + GIMP_HELP_SELECTION_NONE }, + + { "select-invert", GIMP_ICON_INVERT, + NC_("select-action", "_Invert"), "I", + NC_("select-action", "Invert the selection"), + select_invert_cmd_callback, + GIMP_HELP_SELECTION_INVERT }, + + { "select-float", GIMP_ICON_LAYER_FLOATING_SELECTION, + NC_("select-action", "_Float"), "L", + NC_("select-action", "Create a floating selection"), + select_float_cmd_callback, + GIMP_HELP_SELECTION_FLOAT }, + + { "select-feather", NULL, + NC_("select-action", "Fea_ther..."), NULL, + NC_("select-action", + "Blur the selection border so that it fades out smoothly"), + select_feather_cmd_callback, + GIMP_HELP_SELECTION_FEATHER }, + + { "select-sharpen", NULL, + NC_("select-action", "_Sharpen"), NULL, + NC_("select-action", "Remove fuzziness from the selection"), + select_sharpen_cmd_callback, + GIMP_HELP_SELECTION_SHARPEN }, + + { "select-shrink", GIMP_ICON_SELECTION_SHRINK, + NC_("select-action", "S_hrink..."), NULL, + NC_("select-action", "Contract the selection"), + select_shrink_cmd_callback, + GIMP_HELP_SELECTION_SHRINK }, + + { "select-grow", GIMP_ICON_SELECTION_GROW, + NC_("select-action", "_Grow..."), NULL, + NC_("select-action", "Enlarge the selection"), + select_grow_cmd_callback, + GIMP_HELP_SELECTION_GROW }, + + { "select-border", GIMP_ICON_SELECTION_BORDER, + NC_("select-action", "Bo_rder..."), NULL, + NC_("select-action", "Replace the selection by its border"), + select_border_cmd_callback, + GIMP_HELP_SELECTION_BORDER }, + + { "select-flood", NULL, + NC_("select-action", "Re_move Holes"), NULL, + NC_("select-action", "Remove holes from the selection"), + select_flood_cmd_callback, + GIMP_HELP_SELECTION_FLOOD }, + + { "select-save", GIMP_ICON_SELECTION_TO_CHANNEL, + NC_("select-action", "Save to _Channel"), NULL, + NC_("select-action", "Save the selection to a channel"), + select_save_cmd_callback, + GIMP_HELP_SELECTION_TO_CHANNEL }, + + { "select-fill", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("select-action", "_Fill Selection Outline..."), NULL, + NC_("select-action", "Fill the selection outline"), + select_fill_cmd_callback, + GIMP_HELP_SELECTION_FILL }, + + { "select-fill-last-values", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("select-action", "_Fill Selection Outline"), NULL, + NC_("select-action", "Fill the selection outline with last used values"), + select_fill_last_vals_cmd_callback, + GIMP_HELP_SELECTION_FILL }, + + { "select-stroke", GIMP_ICON_SELECTION_STROKE, + NC_("select-action", "_Stroke Selection..."), NULL, + NC_("select-action", "Paint along the selection outline"), + select_stroke_cmd_callback, + GIMP_HELP_SELECTION_STROKE }, + + { "select-stroke-last-values", GIMP_ICON_SELECTION_STROKE, + NC_("select-action", "_Stroke Selection"), NULL, + NC_("select-action", "Stroke the selection with last used values"), + select_stroke_last_vals_cmd_callback, + GIMP_HELP_SELECTION_STROKE } +}; + + +void +select_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "select-action", + select_actions, + G_N_ELEMENTS (select_actions)); +} + +void +select_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpDrawable *drawable = NULL; + gboolean fs = FALSE; + gboolean sel = FALSE; + gboolean writable = FALSE; + gboolean children = FALSE; + + if (image) + { + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + writable = ! gimp_item_is_content_locked (GIMP_ITEM (drawable)); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + children = TRUE; + } + + fs = (gimp_image_get_floating_selection (image) != NULL); + sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("select-all", drawable); + SET_SENSITIVE ("select-none", drawable && sel); + SET_SENSITIVE ("select-invert", drawable); + SET_SENSITIVE ("select-float", writable && !children && sel); + + SET_SENSITIVE ("select-feather", drawable && sel); + SET_SENSITIVE ("select-sharpen", drawable && sel); + SET_SENSITIVE ("select-shrink", drawable && sel); + SET_SENSITIVE ("select-grow", drawable && sel); + SET_SENSITIVE ("select-border", drawable && sel); + SET_SENSITIVE ("select-flood", drawable && sel); + + SET_SENSITIVE ("select-save", drawable && !fs); + SET_SENSITIVE ("select-fill", writable && !children && sel); + SET_SENSITIVE ("select-fill-last-values", writable && !children && sel); + SET_SENSITIVE ("select-stroke", writable && !children && sel); + SET_SENSITIVE ("select-stroke-last-values", writable && !children && sel); + +#undef SET_SENSITIVE +} diff --git a/app/actions/select-actions.h b/app/actions/select-actions.h new file mode 100644 index 0000000..250d2e1 --- /dev/null +++ b/app/actions/select-actions.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 . + */ + +#ifndef __SELECT_ACTIONS_H__ +#define __SELECT_ACTIONS_H__ + + +void select_actions_setup (GimpActionGroup *group); +void select_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __SELECT_ACTIONS_H__ */ diff --git a/app/actions/select-commands.c b/app/actions/select-commands.c new file mode 100644 index 0000000..687872b --- /dev/null +++ b/app/actions/select-commands.c @@ -0,0 +1,685 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimpselection.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "items-commands.h" +#include "select-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void select_feather_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data); +static void select_border_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data); +static void select_grow_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data); +static void select_shrink_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data); + + +/* public functions */ + +void +select_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_channel_all (gimp_image_get_mask (image), TRUE); + gimp_image_flush (image); +} + +void +select_none_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); + gimp_image_flush (image); +} + +void +select_invert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_channel_invert (gimp_image_get_mask (image), TRUE); + gimp_image_flush (image); +} + +void +select_float_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GError *error = NULL; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + if (gimp_selection_float (GIMP_SELECTION (gimp_image_get_mask (image)), + gimp_image_get_active_drawable (image), + action_data_get_context (data), + TRUE, 0, 0, &error)) + { + gimp_image_flush (image); + } + else + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + } +} + +void +select_feather_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define FEATHER_DIALOG_KEY "gimp-selection-feather-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + dialog = gimp_query_size_box (_("Feather Selection"), + GTK_WIDGET (gimp_display_get_shell (display)), + gimp_standard_help_func, + GIMP_HELP_SELECTION_FEATHER, + _("Feather selection by"), + config->selection_feather_radius, 0, 32767, 3, + gimp_display_get_shell (display)->unit, + MIN (xres, yres), + FALSE, + G_OBJECT (image), "disconnect", + select_feather_callback, image); + + /* Edge lock button */ + button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image")); + g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button); + gimp_help_set_help_data (button, + _("When feathering, act as if selected areas " + "continued outside the image."), + NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + config->selection_feather_edge_lock); + gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + + dialogs_attach_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +select_sharpen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_channel_sharpen (gimp_image_get_mask (image), TRUE); + gimp_image_flush (image); +} + +void +select_shrink_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define SHRINK_DIALOG_KEY "gimp-selection-shrink-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; + gint width; + gint height; + gint max_value; + gdouble xres; + gdouble yres; + + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + NULL, NULL, &width, &height); + max_value = MIN (width, height) / 2; + + gimp_image_get_resolution (image, &xres, &yres); + + dialog = gimp_query_size_box (_("Shrink Selection"), + GTK_WIDGET (gimp_display_get_shell (display)), + gimp_standard_help_func, + GIMP_HELP_SELECTION_SHRINK, + _("Shrink selection by"), + config->selection_shrink_radius, + 1, max_value, 0, + gimp_display_get_shell (display)->unit, + MIN (xres, yres), + FALSE, + G_OBJECT (image), "disconnect", + select_shrink_callback, image); + + /* Edge lock button */ + button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image")); + g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button); + gimp_help_set_help_data (button, + _("When shrinking, act as if selected areas " + "continued outside the image."), + NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + config->selection_shrink_edge_lock); + gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + + dialogs_attach_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +select_grow_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define GROW_DIALOG_KEY "gimp-selection-grow-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), GROW_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + gint width; + gint height; + gint max_value; + gdouble xres; + gdouble yres; + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + max_value = MAX (width, height); + + gimp_image_get_resolution (image, &xres, &yres); + + dialog = gimp_query_size_box (_("Grow Selection"), + GTK_WIDGET (gimp_display_get_shell (display)), + gimp_standard_help_func, + GIMP_HELP_SELECTION_GROW, + _("Grow selection by"), + config->selection_grow_radius, + 1, max_value, 0, + gimp_display_get_shell (display)->unit, + MIN (xres, yres), + FALSE, + G_OBJECT (image), "disconnect", + select_grow_callback, image); + + dialogs_attach_dialog (G_OBJECT (image), GROW_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +select_border_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + GtkWidget *dialog; + return_if_no_display (display, data); + + image = gimp_display_get_image (display); + +#define BORDER_DIALOG_KEY "gimp-selection-border-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), BORDER_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *combo; + GtkWidget *button; + gint width; + gint height; + gint max_value; + gdouble xres; + gdouble yres; + + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + NULL, NULL, &width, &height); + max_value = MIN (width, height) / 2; + + gimp_image_get_resolution (image, &xres, &yres); + + dialog = gimp_query_size_box (_("Border Selection"), + GTK_WIDGET (gimp_display_get_shell (display)), + gimp_standard_help_func, + GIMP_HELP_SELECTION_BORDER, + _("Border selection by"), + config->selection_border_radius, + 1, max_value, 0, + gimp_display_get_shell (display)->unit, + MIN (xres, yres), + FALSE, + G_OBJECT (image), "disconnect", + select_border_callback, image); + + /* Border style combo */ + combo = gimp_enum_combo_box_new (GIMP_TYPE_CHANNEL_BORDER_STYLE); + gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), + _("Border style")); + + gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), combo, + FALSE, FALSE, 0); + + g_object_set_data (G_OBJECT (dialog), "border-style-combo", combo); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + config->selection_border_style); + gtk_widget_show (combo); + + /* Edge lock button */ + button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image")); + g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button); + gimp_help_set_help_data (button, + _("When bordering, act as if selected areas " + "continued outside the image."), + NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + config->selection_border_edge_lock); + gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + + dialogs_attach_dialog (G_OBJECT (image), BORDER_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +select_flood_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + gimp_channel_flood (gimp_image_get_mask (image), TRUE); + gimp_image_flush (image); +} + +void +select_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpChannel *channel; + GtkWidget *widget; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + channel = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (gimp_image_get_mask (image)), + GIMP_TYPE_CHANNEL)); + + /* saved selections are not visible by default */ + gimp_item_set_visible (GIMP_ITEM (channel), FALSE, FALSE); + + gimp_image_add_channel (image, channel, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (image->gimp)), + image->gimp, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + "gimp-channel-list"); +} + +void +select_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + items_fill_cmd_callback (action, + image, GIMP_ITEM (gimp_image_get_mask (image)), + "gimp-selection-fill-dialog", + _("Fill Selection Outline"), + GIMP_ICON_TOOL_BUCKET_FILL, + GIMP_HELP_SELECTION_FILL, + data); +} + +void +select_fill_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + items_fill_last_vals_cmd_callback (action, + image, + GIMP_ITEM (gimp_image_get_mask (image)), + data); +} + +void +select_stroke_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + items_stroke_cmd_callback (action, + image, GIMP_ITEM (gimp_image_get_mask (image)), + "gimp-selection-stroke-dialog", + _("Stroke Selection"), + GIMP_ICON_SELECTION_STROKE, + GIMP_HELP_SELECTION_STROKE, + data); +} + +void +select_stroke_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + return_if_no_image (image, data); + + items_stroke_last_vals_cmd_callback (action, + image, + GIMP_ITEM (gimp_image_get_mask (image)), + data); +} + + +/* private functions */ + +static void +select_feather_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; + gdouble radius_x; + gdouble radius_y; + + button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle"); + + g_object_set (config, + "selection-feather-radius", size, + "selection-feather-edge-lock", + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)), + NULL); + + radius_x = config->selection_feather_radius; + radius_y = config->selection_feather_radius; + + if (unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gdouble factor; + + gimp_image_get_resolution (image, &xres, &yres); + + factor = (MAX (xres, yres) / + MIN (xres, yres)); + + if (xres == MIN (xres, yres)) + radius_y *= factor; + else + radius_x *= factor; + } + + gimp_channel_feather (gimp_image_get_mask (image), radius_x, radius_y, + config->selection_feather_edge_lock, + TRUE); + gimp_image_flush (image); +} + +static void +select_border_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *combo; + GtkWidget *button; + gdouble radius_x; + gdouble radius_y; + gint border_style; + + combo = g_object_get_data (G_OBJECT (widget), "border-style-combo"); + button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle"); + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &border_style); + + g_object_set (config, + "selection-border-radius", size, + "selection-border-style", border_style, + "selection-border-edge-lock", + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)), + NULL); + + radius_x = ROUND (config->selection_border_radius); + radius_y = ROUND (config->selection_border_radius); + + if (unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gdouble factor; + + gimp_image_get_resolution (image, &xres, &yres); + + factor = (MAX (xres, yres) / + MIN (xres, yres)); + + if (xres == MIN (xres, yres)) + radius_y *= factor; + else + radius_x *= factor; + } + + gimp_channel_border (gimp_image_get_mask (image), radius_x, radius_y, + config->selection_border_style, + config->selection_border_edge_lock, + TRUE); + gimp_image_flush (image); +} + +static void +select_grow_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + gdouble radius_x; + gdouble radius_y; + + g_object_set (config, + "selection-grow-radius", size, + NULL); + + radius_x = ROUND (config->selection_grow_radius); + radius_y = ROUND (config->selection_grow_radius); + + if (unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gdouble factor; + + gimp_image_get_resolution (image, &xres, &yres); + + factor = (MAX (xres, yres) / + MIN (xres, yres)); + + if (xres == MIN (xres, yres)) + radius_y *= factor; + else + radius_x *= factor; + } + + gimp_channel_grow (gimp_image_get_mask (image), radius_x, radius_y, TRUE); + gimp_image_flush (image); +} + +static void +select_shrink_callback (GtkWidget *widget, + gdouble size, + GimpUnit unit, + gpointer data) +{ + GimpImage *image = GIMP_IMAGE (data); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; + gint radius_x; + gint radius_y; + + button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle"); + + g_object_set (config, + "selection-shrink-radius", size, + "selection-shrink-edge-lock", + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)), + NULL); + + radius_x = ROUND (config->selection_shrink_radius); + radius_y = ROUND (config->selection_shrink_radius); + + if (unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gdouble factor; + + gimp_image_get_resolution (image, &xres, &yres); + + factor = (MAX (xres, yres) / + MIN (xres, yres)); + + if (xres == MIN (xres, yres)) + radius_y *= factor; + else + radius_x *= factor; + } + + gimp_channel_shrink (gimp_image_get_mask (image), radius_x, radius_y, + config->selection_shrink_edge_lock, + TRUE); + gimp_image_flush (image); +} diff --git a/app/actions/select-commands.h b/app/actions/select-commands.h new file mode 100644 index 0000000..e6d2a30 --- /dev/null +++ b/app/actions/select-commands.h @@ -0,0 +1,70 @@ +/* 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 . + */ + +#ifndef __SELECT_COMMANDS_H__ +#define __SELECT_COMMANDS_H__ + + +void select_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_none_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_invert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_float_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_feather_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_sharpen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_shrink_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_grow_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_border_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_flood_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void select_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_fill_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_stroke_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void select_stroke_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __SELECT_COMMANDS_H__ */ diff --git a/app/actions/templates-actions.c b/app/actions/templates-actions.c new file mode 100644 index 0000000..e3a59ad --- /dev/null +++ b/app/actions/templates-actions.c @@ -0,0 +1,105 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "templates-actions.h" +#include "templates-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry templates_actions[] = +{ + { "templates-popup", GIMP_ICON_TEMPLATE, + NC_("templates-action", "Templates Menu"), NULL, NULL, NULL, + GIMP_HELP_TEMPLATE_DIALOG }, + + { "templates-create-image", GIMP_ICON_IMAGE, + NC_("templates-action", "_Create Image from Template"), "", + NC_("templates-action", "Create a new image from the selected template"), + templates_create_image_cmd_callback, + GIMP_HELP_TEMPLATE_IMAGE_NEW }, + + { "templates-new", GIMP_ICON_DOCUMENT_NEW, + NC_("templates-action", "_New Template..."), NULL, + NC_("templates-action", "Create a new template"), + templates_new_cmd_callback, + GIMP_HELP_TEMPLATE_NEW }, + + { "templates-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("templates-action", "D_uplicate Template..."), "", + NC_("templates-action", "Duplicate this template"), + templates_duplicate_cmd_callback, + GIMP_HELP_TEMPLATE_DUPLICATE }, + + { "templates-edit", GIMP_ICON_EDIT, + NC_("templates-action", "_Edit Template..."), NULL, + NC_("templates-action", "Edit this template"), + templates_edit_cmd_callback, + GIMP_HELP_TEMPLATE_EDIT }, + + { "templates-delete", GIMP_ICON_EDIT_DELETE, + NC_("templates-action", "_Delete Template"), NULL, + NC_("templates-action", "Delete this template"), + templates_delete_cmd_callback, + GIMP_HELP_TEMPLATE_DELETE } +}; + + +void +templates_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "templates-action", + templates_actions, + G_N_ELEMENTS (templates_actions)); +} + +void +templates_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpContext *context = action_data_get_context (data); + GimpTemplate *template = NULL; + + if (context) + template = gimp_context_get_template (context); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("templates-create-image", template); + SET_SENSITIVE ("templates-new", context); + SET_SENSITIVE ("templates-duplicate", template); + SET_SENSITIVE ("templates-edit", template); + SET_SENSITIVE ("templates-delete", template); + +#undef SET_SENSITIVE +} diff --git a/app/actions/templates-actions.h b/app/actions/templates-actions.h new file mode 100644 index 0000000..904a40d --- /dev/null +++ b/app/actions/templates-actions.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 . + */ + +#ifndef __TEMPLATES_ACTIONS_H__ +#define __TEMPLATES_ACTIONS_H__ + + +void templates_actions_setup (GimpActionGroup *group); +void templates_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TEMPLATES_COMMANDS_H__ */ diff --git a/app/actions/templates-commands.c b/app/actions/templates-commands.c new file mode 100644 index 0000000..6039de0 --- /dev/null +++ b/app/actions/templates-commands.c @@ -0,0 +1,345 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage-new.h" +#include "core/gimptemplate.h" + +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimptemplateeditor.h" +#include "widgets/gimptemplateview.h" +#include "widgets/gimpwidgets-utils.h" + +#include "dialogs/dialogs.h" +#include "dialogs/template-options-dialog.h" + +#include "actions.h" +#include "templates-commands.h" + +#include "gimp-intl.h" + + +typedef struct +{ + GimpContext *context; + GimpContainer *container; + GimpTemplate *template; +} TemplateDeleteData; + + +/* local function prototypes */ + +static void templates_new_callback (GtkWidget *dialog, + GimpTemplate *template, + GimpTemplate *edit_template, + GimpContext *context, + gpointer user_data); +static void templates_edit_callback (GtkWidget *dialog, + GimpTemplate *template, + GimpTemplate *edit_template, + GimpContext *context, + gpointer user_data); +static void templates_delete_response (GtkWidget *dialog, + gint response_id, + TemplateDeleteData *delete_data); +static void templates_delete_data_free (TemplateDeleteData *delete_data); + + +/* public functions */ + +void +templates_create_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpTemplate *template; + return_if_no_gimp (gimp, data); + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + template = gimp_context_get_template (context); + + if (template && gimp_container_have (container, GIMP_OBJECT (template))) + { + GtkWidget *widget = GTK_WIDGET (editor); + GimpImage *image; + + image = gimp_image_new_from_template (gimp, template, context); + gimp_create_display (gimp, image, gimp_template_get_unit (template), 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (image); + + gimp_image_new_set_last_template (gimp, template); + } +} + +void +templates_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GtkWidget *dialog; + + context = gimp_container_view_get_context (editor->view); + +#define NEW_DIALOG_KEY "gimp-template-new-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (context->gimp), NEW_DIALOG_KEY); + + if (! dialog) + { + dialog = template_options_dialog_new (NULL, context, + GTK_WIDGET (editor), + _("New Template"), + "gimp-template-new", + GIMP_ICON_TEMPLATE, + _("Create a New Template"), + GIMP_HELP_TEMPLATE_NEW, + templates_new_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (context->gimp), NEW_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +templates_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpTemplate *template; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + template = gimp_context_get_template (context); + + if (template && gimp_container_have (container, GIMP_OBJECT (template))) + { + GimpTemplate *new_template; + + new_template = gimp_config_duplicate (GIMP_CONFIG (template)); + + gimp_container_add (container, GIMP_OBJECT (new_template)); + gimp_context_set_by_type (context, + gimp_container_get_children_type (container), + GIMP_OBJECT (new_template)); + g_object_unref (new_template); + + templates_edit_cmd_callback (action, value, data); + } +} + +void +templates_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpTemplate *template; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + template = gimp_context_get_template (context); + + if (template && gimp_container_have (container, GIMP_OBJECT (template))) + { + GtkWidget *dialog; + +#define EDIT_DIALOG_KEY "gimp-template-edit-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (template), EDIT_DIALOG_KEY); + + if (! dialog) + { + dialog = template_options_dialog_new (template, context, + GTK_WIDGET (editor), + _("Edit Template"), + "gimp-template-edit", + GIMP_ICON_EDIT, + _("Edit Template"), + GIMP_HELP_TEMPLATE_EDIT, + templates_edit_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (template), EDIT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +void +templates_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContainer *container; + GimpContext *context; + GimpTemplate *template; + + container = gimp_container_view_get_container (editor->view); + context = gimp_container_view_get_context (editor->view); + + template = gimp_context_get_template (context); + + if (template && gimp_container_have (container, GIMP_OBJECT (template))) + { + TemplateDeleteData *delete_data = g_slice_new (TemplateDeleteData); + GtkWidget *dialog; + + delete_data->context = context; + delete_data->container = container; + delete_data->template = template; + + dialog = + gimp_message_dialog_new (_("Delete Template"), "edit-delete", + GTK_WIDGET (editor), 0, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Delete"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) templates_delete_data_free, delete_data); + + g_signal_connect_object (template, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect (dialog, "response", + G_CALLBACK (templates_delete_response), + delete_data); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Are you sure you want to delete " + "template '%s' from the list and " + "from disk?"), + gimp_object_get_name (template)); + gtk_widget_show (dialog); + } +} + + +/* private functions */ + +static void +templates_new_callback (GtkWidget *dialog, + GimpTemplate *template, + GimpTemplate *edit_template, + GimpContext *context, + gpointer user_data) +{ + gimp_container_add (context->gimp->templates, GIMP_OBJECT (edit_template)); + gimp_context_set_template (gimp_get_user_context (context->gimp), + edit_template); + + gtk_widget_destroy (dialog); +} + +static void +templates_edit_callback (GtkWidget *dialog, + GimpTemplate *template, + GimpTemplate *edit_template, + GimpContext *context, + gpointer user_data) +{ + gimp_config_sync (G_OBJECT (edit_template), + G_OBJECT (template), 0); + + gtk_widget_destroy (dialog); +} + +static void +templates_delete_response (GtkWidget *dialog, + gint response_id, + TemplateDeleteData *delete_data) +{ + if (response_id == GTK_RESPONSE_OK) + { + GimpObject *new_active = NULL; + + if (delete_data->template == + gimp_context_get_template (delete_data->context)) + { + new_active = gimp_container_get_neighbor_of (delete_data->container, + GIMP_OBJECT (delete_data->template)); + } + + if (gimp_container_have (delete_data->container, + GIMP_OBJECT (delete_data->template))) + { + if (new_active) + gimp_context_set_by_type (delete_data->context, + gimp_container_get_children_type (delete_data->container), + new_active); + + gimp_container_remove (delete_data->container, + GIMP_OBJECT (delete_data->template)); + } + } + + gtk_widget_destroy (dialog); +} + +static void +templates_delete_data_free (TemplateDeleteData *delete_data) +{ + g_slice_free (TemplateDeleteData, delete_data); +} diff --git a/app/actions/templates-commands.h b/app/actions/templates-commands.h new file mode 100644 index 0000000..91356f2 --- /dev/null +++ b/app/actions/templates-commands.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 . + */ + +#ifndef __TEMPLATES_COMMANDS_H__ +#define __TEMPLATES_COMMANDS_H__ + + +void templates_create_image_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void templates_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void templates_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void templates_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void templates_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TEMPLATES_COMMANDS_H__ */ diff --git a/app/actions/text-editor-actions.c b/app/actions/text-editor-actions.c new file mode 100644 index 0000000..5a31220 --- /dev/null +++ b/app/actions/text-editor-actions.c @@ -0,0 +1,148 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimptexteditor.h" +#include "widgets/gimphelp-ids.h" + +#include "text-editor-actions.h" +#include "text-editor-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry text_editor_actions[] = +{ + { "text-editor-toolbar", GIMP_ICON_EDIT, + "Text Editor Toolbar", NULL, NULL, NULL, + GIMP_HELP_TEXT_EDITOR_DIALOG }, + + { "text-editor-load", GIMP_ICON_DOCUMENT_OPEN, + NC_("text-editor-action", "Open"), NULL, + NC_("text-editor-action", "Load text from file"), + text_editor_load_cmd_callback, + NULL }, + + { "text-editor-clear", GIMP_ICON_EDIT_CLEAR, + NC_("text-editor-action", "Clear"), NULL, + NC_("text-editor-action", "Clear all text"), + text_editor_clear_cmd_callback, + NULL } +}; + +static const GimpRadioActionEntry text_editor_direction_actions[] = +{ + { "text-editor-direction-ltr", GIMP_ICON_FORMAT_TEXT_DIRECTION_LTR, + NC_("text-editor-action", "LTR"), NULL, + NC_("text-editor-action", "From left to right"), + GIMP_TEXT_DIRECTION_LTR, + NULL }, + + { "text-editor-direction-rtl", GIMP_ICON_FORMAT_TEXT_DIRECTION_RTL, + NC_("text-editor-action", "RTL"), NULL, + NC_("text-editor-action", "From right to left"), + GIMP_TEXT_DIRECTION_RTL, + NULL }, + + { "text-editor-direction-ttb-rtl", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL, + NC_("text-editor-action", "TTB-RTL"), NULL, + NC_("text-editor-action", "Vertical, right to left (mixed orientation)"), + GIMP_TEXT_DIRECTION_TTB_RTL, + NULL }, + + { "text-editor-direction-ttb-rtl-upright", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL_UPRIGHT, + NC_("text-editor-action", "TTB-RTL-UPRIGHT"), NULL, + NC_("text-editor-action", "Vertical, right to left (upright orientation)"), + GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT, + NULL }, + + { "text-editor-direction-ttb-ltr", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR, + NC_("text-editor-action", "TTB-LTR"), NULL, + NC_("text-editor-action", "Vertical, left to right (mixed orientation)"), + GIMP_TEXT_DIRECTION_TTB_LTR, + NULL }, + + { "text-editor-direction-ttb-ltr-upright", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR_UPRIGHT, + NC_("text-editor-action", "TTB-LTR-UPRIGHT"), NULL, + NC_("text-editor-action", "Vertical, left to right (upright orientation)"), + GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT, + NULL }, +}; + + +void +text_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "text-editor-action", + text_editor_actions, + G_N_ELEMENTS (text_editor_actions)); + + gimp_action_group_add_radio_actions (group, "text-editor-action", + text_editor_direction_actions, + G_N_ELEMENTS (text_editor_direction_actions), + NULL, + GIMP_TEXT_DIRECTION_LTR, + text_editor_direction_cmd_callback); +} + +void +text_editor_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpTextEditor *editor = GIMP_TEXT_EDITOR (data); + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + switch (editor->base_dir) + { + case GIMP_TEXT_DIRECTION_LTR: + SET_ACTIVE ("text-editor-direction-ltr", TRUE); + break; + + case GIMP_TEXT_DIRECTION_RTL: + SET_ACTIVE ("text-editor-direction-rtl", TRUE); + break; + + case GIMP_TEXT_DIRECTION_TTB_RTL: + SET_ACTIVE ("text-editor-direction-ttb-rtl", TRUE); + break; + + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + SET_ACTIVE ("text-editor-direction-ttb-rtl-upright", TRUE); + break; + + case GIMP_TEXT_DIRECTION_TTB_LTR: + SET_ACTIVE ("text-editor-direction-ttb-ltr", TRUE); + break; + + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + SET_ACTIVE ("text-editor-direction-ttb-ltr-upright", TRUE); + break; + } + +#undef SET_ACTIVE +} diff --git a/app/actions/text-editor-actions.h b/app/actions/text-editor-actions.h new file mode 100644 index 0000000..a2940b7 --- /dev/null +++ b/app/actions/text-editor-actions.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 . + */ + +#ifndef __TEXT_EDITOR_ACIONS_H__ +#define __TEXT_EDITOR_ACIONS_H__ + + +void text_editor_actions_setup (GimpActionGroup *group); +void text_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TEXT_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/text-editor-commands.c b/app/actions/text-editor-commands.c new file mode 100644 index 0000000..3a7764d --- /dev/null +++ b/app/actions/text-editor-commands.c @@ -0,0 +1,153 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimptextbuffer.h" +#include "widgets/gimptexteditor.h" +#include "widgets/gimpuimanager.h" + +#include "text-editor-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void text_editor_load_response (GtkWidget *dialog, + gint response_id, + GimpTextEditor *editor); + + +/* public functions */ + +void +text_editor_load_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextEditor *editor = GIMP_TEXT_EDITOR (data); + + if (! editor->file_dialog) + { + GtkWidget *dialog; + + dialog = editor->file_dialog = + gtk_file_chooser_dialog_new (_("Open Text File (UTF-8)"), + GTK_WINDOW (editor), + 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); + + gtk_window_set_role (GTK_WINDOW (dialog), "gimp-text-load-file"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + g_object_add_weak_pointer (G_OBJECT (dialog), + (gpointer) &editor->file_dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (text_editor_load_response), + editor); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + } + + gtk_window_present (GTK_WINDOW (editor->file_dialog)); +} + +void +text_editor_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextEditor *editor = GIMP_TEXT_EDITOR (data); + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (editor->view)); + + gtk_text_buffer_set_text (buffer, "", 0); +} + +void +text_editor_direction_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextEditor *editor = GIMP_TEXT_EDITOR (data); + GimpTextDirection direction; + + direction = (GimpTextDirection) g_variant_get_int32 (value); + + gimp_text_editor_set_direction (editor, direction); +} + + +/* private functions */ + +static void +text_editor_load_response (GtkWidget *dialog, + gint response_id, + GimpTextEditor *editor) +{ + if (response_id == GTK_RESPONSE_OK) + { + GtkTextBuffer *buffer; + GFile *file; + GError *error = NULL; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (editor->view)); + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + + if (! gimp_text_buffer_load (GIMP_TEXT_BUFFER (buffer), file, &error)) + { + gimp_message (editor->ui_manager->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), + error->message); + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_object_unref (file); + } + + gtk_widget_hide (dialog); +} diff --git a/app/actions/text-editor-commands.h b/app/actions/text-editor-commands.h new file mode 100644 index 0000000..1ea89d0 --- /dev/null +++ b/app/actions/text-editor-commands.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 . + */ + +#ifndef __TEXT_EDITOR_COMMANDS_H__ +#define __TEXT_EDITOR_COMMANDS_H__ + + +void text_editor_load_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_editor_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_editor_direction_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TEXT_EDITOR_COMMANDS_H__ */ diff --git a/app/actions/text-tool-actions.c b/app/actions/text-tool-actions.c new file mode 100644 index 0000000..1692253 --- /dev/null +++ b/app/actions/text-tool-actions.c @@ -0,0 +1,229 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpimage.h" + +#include "text/gimptextlayer.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimptexteditor.h" +#include "widgets/gimphelp-ids.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "tools/gimptool.h" +#include "tools/gimptexttool.h" + +#include "text-tool-actions.h" +#include "text-tool-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry text_tool_actions[] = +{ + { "text-tool-popup", NULL, + NC_("text-tool-action", "Text Tool Menu"), NULL, NULL, NULL, + NULL }, + + { "text-tool-input-methods-menu", NULL, + NC_("text-tool-action", "Input _Methods"), NULL, NULL, NULL, + NULL }, + + { "text-tool-cut", GIMP_ICON_EDIT_CUT, + NC_("text-tool-action", "Cu_t"), NULL, "X", + text_tool_cut_cmd_callback, + NULL }, + + { "text-tool-copy", GIMP_ICON_EDIT_COPY, + NC_("text-tool-action", "_Copy"), NULL, "C", + text_tool_copy_cmd_callback, + NULL }, + + { "text-tool-paste", GIMP_ICON_EDIT_PASTE, + NC_("text-tool-action", "_Paste"), NULL, "V", + text_tool_paste_cmd_callback, + NULL }, + + { "text-tool-delete", GIMP_ICON_EDIT_DELETE, + NC_("text-tool-action", "_Delete"), NULL, NULL, + text_tool_delete_cmd_callback, + NULL }, + + { "text-tool-load", GIMP_ICON_DOCUMENT_OPEN, + NC_("text-tool-action", "_Open text file..."), NULL, NULL, + text_tool_load_cmd_callback, + NULL }, + + { "text-tool-clear", GIMP_ICON_EDIT_CLEAR, + NC_("text-tool-action", "Cl_ear"), NULL, + NC_("text-tool-action", "Clear all text"), + text_tool_clear_cmd_callback, + NULL }, + + { "text-tool-text-to-path", GIMP_ICON_PATH, + NC_("text-tool-action", "_Path from Text"), "", + NC_("text-tool-action", + "Create a path from the outlines of the current text"), + text_tool_text_to_path_cmd_callback, + NULL }, + + { "text-tool-text-along-path", GIMP_ICON_PATH, + NC_("text-tool-action", "Text _along Path"), "", + NC_("text-tool-action", + "Bend the text along the currently active path"), + text_tool_text_along_path_cmd_callback, + NULL } +}; + +static const GimpRadioActionEntry text_tool_direction_actions[] = +{ + { "text-tool-direction-ltr", GIMP_ICON_FORMAT_TEXT_DIRECTION_LTR, + NC_("text-tool-action", "From left to right"), NULL, NULL, + GIMP_TEXT_DIRECTION_LTR, + NULL }, + + { "text-tool-direction-rtl", GIMP_ICON_FORMAT_TEXT_DIRECTION_RTL, + NC_("text-tool-action", "From right to left"), NULL, NULL, + GIMP_TEXT_DIRECTION_RTL, + NULL }, + + { "text-tool-direction-ttb-rtl", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL, + NC_("text-tool-action", "Vertical, right to left (mixed orientation)"), NULL, NULL, + GIMP_TEXT_DIRECTION_TTB_RTL, + NULL }, + + { "text-tool-direction-ttb-rtl-upright", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL_UPRIGHT, + NC_("text-tool-action", "Vertical, right to left (upright orientation)"), NULL, NULL, + GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT, + NULL }, + + { "text-tool-direction-ttb-ltr", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR, + NC_("text-tool-action", "Vertical, left to right (mixed orientation)"), NULL, NULL, + GIMP_TEXT_DIRECTION_TTB_LTR, + NULL }, + + { "text-tool-direction-ttb-ltr-upright", GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR_UPRIGHT, + NC_("text-tool-action", "Vertical, left to right (upright orientation)"), NULL, NULL, + GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT, + NULL } +}; + + +#define SET_HIDE_EMPTY(action,condition) \ + gimp_action_group_set_action_hide_empty (group, action, (condition) != 0) + +void +text_tool_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "text-tool-action", + text_tool_actions, + G_N_ELEMENTS (text_tool_actions)); + + gimp_action_group_add_radio_actions (group, "text-tool-action", + text_tool_direction_actions, + G_N_ELEMENTS (text_tool_direction_actions), + NULL, + GIMP_TEXT_DIRECTION_LTR, + text_tool_direction_cmd_callback); + + SET_HIDE_EMPTY ("text-tool-input-methods-menu", FALSE); +} + +/* The following code is written on the assumption that this is for a + * context menu, activated by right-clicking in a text layer. + * Therefore, the tool must have a display. If for any reason the + * code is adapted to a different situation, some existence testing + * will need to be added. + */ +void +text_tool_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + GimpDisplay *display = GIMP_TOOL (text_tool)->display; + GimpImage *image = gimp_display_get_image (display); + GimpLayer *layer; + GimpVectors *vectors; + GimpDisplayShell *shell; + GtkClipboard *clipboard; + gboolean text_layer = FALSE; + gboolean text_sel = FALSE; /* some text is selected */ + gboolean clip = FALSE; /* clipboard has text available */ + gboolean input_method_menu; + gboolean unicode_menu; + GimpTextDirection direction; + gint i; + + layer = gimp_image_get_active_layer (image); + + if (layer) + text_layer = gimp_item_is_text_layer (GIMP_ITEM (layer)); + + vectors = gimp_image_get_active_vectors (image); + + text_sel = gimp_text_tool_get_has_text_selection (text_tool); + + /* see whether there is text available for pasting */ + shell = gimp_display_get_shell (display); + clipboard = gtk_widget_get_clipboard (shell->canvas, + GDK_SELECTION_CLIPBOARD); + clip = gtk_clipboard_wait_is_text_available (clipboard); + + g_object_get (gtk_widget_get_settings (shell->canvas), + "gtk-show-input-method-menu", &input_method_menu, + "gtk-show-unicode-menu", &unicode_menu, + NULL); + +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_SENSITIVE ("text-tool-cut", text_sel); + SET_SENSITIVE ("text-tool-copy", text_sel); + SET_SENSITIVE ("text-tool-paste", clip); + SET_SENSITIVE ("text-tool-delete", text_sel); + SET_SENSITIVE ("text-tool-clear", text_layer); + SET_SENSITIVE ("text-tool-load", image); + SET_SENSITIVE ("text-tool-text-to-path", text_layer); + SET_SENSITIVE ("text-tool-text-along-path", text_layer && vectors); + + direction = gimp_text_tool_get_direction (text_tool); + for (i = 0; i < G_N_ELEMENTS (text_tool_direction_actions); i++) + { + if (direction == text_tool_direction_actions[i].value) + { + SET_ACTIVE (text_tool_direction_actions[i].name, TRUE); + break; + } + } + + SET_VISIBLE ("text-tool-input-methods-menu", input_method_menu); +} diff --git a/app/actions/text-tool-actions.h b/app/actions/text-tool-actions.h new file mode 100644 index 0000000..5efca91 --- /dev/null +++ b/app/actions/text-tool-actions.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 . + */ + +#ifndef __TEXT_TOOL_ACTIONS_H__ +#define __TEXT_TOOL_ACTIONS_H__ + + +void text_tool_actions_setup (GimpActionGroup *group); +void text_tool_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TEXT_TOOL_ACTIONS_H__ */ diff --git a/app/actions/text-tool-commands.c b/app/actions/text-tool-commands.c new file mode 100644 index 0000000..296bbf3 --- /dev/null +++ b/app/actions/text-tool-commands.c @@ -0,0 +1,229 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimptextbuffer.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" + +#include "tools/gimptexttool.h" + +#include "dialogs/dialogs.h" + +#include "text-tool-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void text_tool_load_dialog_response (GtkWidget *dialog, + gint response_id, + GimpTextTool *tool); + + +/* public functions */ + +void +text_tool_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_cut_clipboard (text_tool); +} + +void +text_tool_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_copy_clipboard (text_tool); +} + +void +text_tool_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_paste_clipboard (text_tool); +} + +void +text_tool_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_delete_selection (text_tool); +} + +void +text_tool_load_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + GtkWidget *dialog; + + dialog = dialogs_get_dialog (G_OBJECT (text_tool), "gimp-text-file-dialog"); + + if (! dialog) + { + GtkWidget *parent = NULL; + + if (GIMP_TOOL (text_tool)->display) + { + GimpDisplayShell *shell; + + shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + } + + dialog = gtk_file_chooser_dialog_new (_("Open Text File (UTF-8)"), + parent ? GTK_WINDOW (parent) : 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); + + gtk_window_set_role (GTK_WINDOW (dialog), "gimp-text-load-file"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + + g_signal_connect (dialog, "response", + G_CALLBACK (text_tool_load_dialog_response), + text_tool); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + dialogs_attach_dialog (G_OBJECT (text_tool), + "gimp-text-file-dialog", dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +text_tool_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer); + GtkTextIter start, end; + + gtk_text_buffer_get_bounds (buffer, &start, &end); + gtk_text_buffer_select_range (buffer, &start, &end); + gimp_text_tool_delete_selection (text_tool); +} + +void +text_tool_text_to_path_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_create_vectors (text_tool); +} + +void +text_tool_text_along_path_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + + gimp_text_tool_create_vectors_warped (text_tool); +} + +void +text_tool_direction_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpTextTool *text_tool = GIMP_TEXT_TOOL (data); + GimpTextDirection direction; + + direction = (GimpTextDirection) g_variant_get_int32 (value); + + g_object_set (text_tool->proxy, + "base-direction", direction, + NULL); +} + + +/* private functions */ + +static void +text_tool_load_dialog_response (GtkWidget *dialog, + gint response_id, + GimpTextTool *tool) +{ + if (response_id == GTK_RESPONSE_OK) + { + GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + GError *error = NULL; + + if (! gimp_text_buffer_load (tool->buffer, file, &error)) + { + gimp_message (GIMP_TOOL (tool)->tool_info->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), + error->message); + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_object_unref (file); + } + + gtk_widget_hide (dialog); +} diff --git a/app/actions/text-tool-commands.h b/app/actions/text-tool-commands.h new file mode 100644 index 0000000..d7bef1c --- /dev/null +++ b/app/actions/text-tool-commands.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __TEXT_TOOL_COMMANDS_H__ +#define __TEXT_TOOL_COMMANDS_H__ + + +void text_tool_cut_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_load_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_clear_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_text_to_path_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_text_along_path_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void text_tool_direction_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TEXT_TOOL_COMMANDS_H__ */ diff --git a/app/actions/tool-options-actions.c b/app/actions/tool-options-actions.c new file mode 100644 index 0000000..10c59eb --- /dev/null +++ b/app/actions/tool-options-actions.c @@ -0,0 +1,238 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimplist.h" +#include "core/gimptoolinfo.h" +#include "core/gimptoolpreset.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "tool-options-actions.h" +#include "tool-options-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void tool_options_actions_update_presets (GimpActionGroup *group, + const gchar *action_prefix, + GimpActionCallback callback, + const gchar *help_id, + GimpContainer *presets, + gboolean need_writable, + gboolean need_deletable); + + +/* global variables */ + +static const GimpActionEntry tool_options_actions[] = +{ + { "tool-options-popup", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tool-options-action", "Tool Options Menu"), NULL, NULL, NULL, + GIMP_HELP_TOOL_OPTIONS_DIALOG }, + + { "tool-options-save-preset-menu", GIMP_ICON_DOCUMENT_SAVE, + NC_("tool-options-action", "_Save Tool Preset"), "", NULL, NULL, + GIMP_HELP_TOOL_OPTIONS_SAVE }, + + { "tool-options-restore-preset-menu", GIMP_ICON_DOCUMENT_REVERT, + NC_("tool-options-action", "_Restore Tool Preset"), "", NULL, NULL, + GIMP_HELP_TOOL_OPTIONS_RESTORE }, + + { "tool-options-edit-preset-menu", GIMP_ICON_EDIT, + NC_("tool-options-action", "E_dit Tool Preset"), NULL, NULL, NULL, + GIMP_HELP_TOOL_OPTIONS_EDIT }, + + { "tool-options-delete-preset-menu", GIMP_ICON_EDIT_DELETE, + NC_("tool-options-action", "_Delete Tool Preset"), "", NULL, NULL, + GIMP_HELP_TOOL_OPTIONS_DELETE }, + + { "tool-options-save-new-preset", GIMP_ICON_DOCUMENT_NEW, + NC_("tool-options-action", "_New Tool Preset..."), "", NULL, + tool_options_save_new_preset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_SAVE }, + + { "tool-options-reset", GIMP_ICON_RESET, + NC_("tool-options-action", "R_eset Tool Options"), NULL, + NC_("tool-options-action", "Reset to default values"), + tool_options_reset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_RESET }, + + { "tool-options-reset-all", GIMP_ICON_RESET, + NC_("tool-options-action", "Reset _all Tool Options"), NULL, + NC_("tool-options-action", "Reset all tool options"), + tool_options_reset_all_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_RESET } +}; + + +/* public functions */ + +#define SET_VISIBLE(action,condition) \ + gimp_action_group_set_action_visible (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_HIDE_EMPTY(action,condition) \ + gimp_action_group_set_action_hide_empty (group, action, (condition) != 0) + +void +tool_options_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "tool-options-action", + tool_options_actions, + G_N_ELEMENTS (tool_options_actions)); + + SET_HIDE_EMPTY ("tool-options-restore-preset-menu", FALSE); + SET_HIDE_EMPTY ("tool-options-edit-preset-menu", FALSE); + SET_HIDE_EMPTY ("tool-options-delete-preset-menu", FALSE); +} + +void +tool_options_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpContext *context = gimp_get_user_context (group->gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + + SET_VISIBLE ("tool-options-save-preset-menu", tool_info->presets); + SET_VISIBLE ("tool-options-restore-preset-menu", tool_info->presets); + SET_VISIBLE ("tool-options-edit-preset-menu", tool_info->presets); + SET_VISIBLE ("tool-options-delete-preset-menu", tool_info->presets); + + tool_options_actions_update_presets (group, "tool-options-save-preset", + tool_options_save_preset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_SAVE, + tool_info->presets, + TRUE /* writable */, + FALSE /* deletable */); + + tool_options_actions_update_presets (group, "tool-options-restore-preset", + tool_options_restore_preset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_RESTORE, + tool_info->presets, + FALSE /* writable */, + FALSE /* deletable */); + + tool_options_actions_update_presets (group, "tool-options-edit-preset", + tool_options_edit_preset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_EDIT, + tool_info->presets, + FALSE /* writable */, + FALSE /* deletable */); + + tool_options_actions_update_presets (group, "tool-options-delete-preset", + tool_options_delete_preset_cmd_callback, + GIMP_HELP_TOOL_OPTIONS_DELETE, + tool_info->presets, + FALSE /* writable */, + TRUE /* deletable */); +} + + +/* private function */ + +static void +tool_options_actions_update_presets (GimpActionGroup *group, + const gchar *action_prefix, + GimpActionCallback callback, + const gchar *help_id, + GimpContainer *presets, + gboolean need_writable, + gboolean need_deletable) +{ + GList *list; + gint n_children = 0; + gint i; + + for (i = 0; ; i++) + { + gchar *action_name; + GimpAction *action; + + action_name = g_strdup_printf ("%s-%03d", action_prefix, i); + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (! action) + break; + + gimp_action_group_remove_action (group, action); + } + + if (presets) + n_children = gimp_container_get_n_children (presets); + + if (n_children > 0) + { + GimpEnumActionEntry entry; + + entry.name = NULL; + entry.label = NULL; + entry.accelerator = ""; + entry.tooltip = NULL; + entry.value = 0; + entry.value_variable = FALSE; + entry.help_id = help_id; + + for (list = GIMP_LIST (presets)->queue->head, i = 0; + list; + list = g_list_next (list), i++) + { + GimpObject *preset = list->data; + GdkPixbuf *pixbuf = NULL; + + entry.name = g_strdup_printf ("%s-%03d", action_prefix, i); + entry.label = gimp_object_get_name (preset); + entry.icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (preset)); + entry.value = i; + + g_object_get (preset, "icon-pixbuf", &pixbuf, NULL); + + gimp_action_group_add_enum_actions (group, NULL, &entry, 1, callback); + + if (need_writable) + SET_SENSITIVE (entry.name, + gimp_data_is_writable (GIMP_DATA (preset))); + + if (need_deletable) + SET_SENSITIVE (entry.name, + gimp_data_is_deletable (GIMP_DATA (preset))); + + if (pixbuf) + gimp_action_group_set_action_pixbuf (group, entry.name, pixbuf); + + g_free ((gchar *) entry.name); + } + } +} + +#undef SET_VISIBLE +#undef SET_SENSITIVE +#undef SET_HIDE_EMPTY diff --git a/app/actions/tool-options-actions.h b/app/actions/tool-options-actions.h new file mode 100644 index 0000000..2115fe1 --- /dev/null +++ b/app/actions/tool-options-actions.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 . + */ + +#ifndef __TOOL_OPTIONS_ACTIONS_H__ +#define __TOOL_OPTIONS_ACTIONS_H__ + + +void tool_options_actions_setup (GimpActionGroup *group); +void tool_options_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TOOL_OPTIONS_ACTIONS_H__ */ diff --git a/app/actions/tool-options-commands.c b/app/actions/tool-options-commands.c new file mode 100644 index 0000000..94fd3c6 --- /dev/null +++ b/app/actions/tool-options-commands.c @@ -0,0 +1,268 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpdatafactory.h" +#include "core/gimptoolinfo.h" +#include "core/gimptooloptions.h" +#include "core/gimptoolpreset.h" + +#include "widgets/gimpdataeditor.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpeditor.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimptooloptionseditor.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "dialogs/data-delete-dialog.h" + +#include "tool-options-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void tool_options_show_preset_editor (Gimp *gimp, + GimpEditor *editor, + GimpToolPreset *preset); + + +/* public functions */ + +void +tool_options_save_new_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer user_data) +{ + GimpEditor *editor = GIMP_EDITOR (user_data); + Gimp *gimp = gimp_editor_get_ui_manager (editor)->gimp; + GimpContext *context = gimp_get_user_context (gimp); + GimpData *data; + + data = gimp_data_factory_data_new (context->gimp->tool_preset_factory, + context, _("Untitled")); + + tool_options_show_preset_editor (gimp, editor, GIMP_TOOL_PRESET (data)); +} + +void +tool_options_save_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + Gimp *gimp = gimp_editor_get_ui_manager (editor)->gimp; + GimpContext *context = gimp_get_user_context (gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + GimpToolPreset *preset; + gint index; + + index = g_variant_get_int32 (value); + + preset = (GimpToolPreset *) + gimp_container_get_child_by_index (tool_info->presets, index); + + if (preset) + { + gimp_config_sync (G_OBJECT (tool_info->tool_options), + G_OBJECT (preset->tool_options), 0); + + tool_options_show_preset_editor (gimp, editor, preset); + } +} + +void +tool_options_restore_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + Gimp *gimp = gimp_editor_get_ui_manager (editor)->gimp; + GimpContext *context = gimp_get_user_context (gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + GimpToolPreset *preset; + gint index; + + index = g_variant_get_int32 (value); + + preset = (GimpToolPreset *) + gimp_container_get_child_by_index (tool_info->presets, index); + + if (preset) + { + if (gimp_context_get_tool_preset (context) != preset) + gimp_context_set_tool_preset (context, preset); + else + gimp_context_tool_preset_changed (context); + } +} + +void +tool_options_edit_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + Gimp *gimp = gimp_editor_get_ui_manager (editor)->gimp; + GimpContext *context = gimp_get_user_context (gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + GimpToolPreset *preset; + gint index; + + index = g_variant_get_int32 (value); + + preset = (GimpToolPreset *) + gimp_container_get_child_by_index (tool_info->presets, index); + + if (preset) + { + tool_options_show_preset_editor (gimp, editor, preset); + } +} + +void +tool_options_delete_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + GimpContext *context = gimp_get_user_context (gimp_editor_get_ui_manager (editor)->gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + GimpToolPreset *preset; + gint index; + + index = g_variant_get_int32 (value); + + preset = (GimpToolPreset *) + gimp_container_get_child_by_index (tool_info->presets, index); + + if (preset && + gimp_data_is_deletable (GIMP_DATA (preset))) + { + GimpDataFactory *factory = context->gimp->tool_preset_factory; + GtkWidget *dialog; + + dialog = data_delete_dialog_new (factory, GIMP_DATA (preset), NULL, + GTK_WIDGET (editor)); + gtk_widget_show (dialog); + } +} + +void +tool_options_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + GimpContext *context = gimp_get_user_context (gimp_editor_get_ui_manager (editor)->gimp); + GimpToolInfo *tool_info = gimp_context_get_tool (context); + + gimp_config_reset (GIMP_CONFIG (tool_info->tool_options)); +} + +void +tool_options_reset_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpEditor *editor = GIMP_EDITOR (data); + GtkWidget *dialog; + + dialog = gimp_message_dialog_new (_("Reset All Tool Options"), + GIMP_ICON_DIALOG_QUESTION, + GTK_WIDGET (editor), + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Reset"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (gtk_widget_get_toplevel (GTK_WIDGET (editor)), + "unmap", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Do you really want to reset all " + "tool options to default values?")); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + Gimp *gimp = gimp_editor_get_ui_manager (editor)->gimp; + GList *list; + + for (list = gimp_get_tool_info_iter (gimp); + list; + list = g_list_next (list)) + { + GimpToolInfo *tool_info = list->data; + + gimp_config_reset (GIMP_CONFIG (tool_info->tool_options)); + } + } + + gtk_widget_destroy (dialog); +} + + +/* private functions */ + +static void +tool_options_show_preset_editor (Gimp *gimp, + GimpEditor *editor, + GimpToolPreset *preset) +{ + GtkWidget *dockable; + + dockable = + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)), + gimp, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (GTK_WIDGET (editor)), + gimp_widget_get_monitor (GTK_WIDGET (editor)), + "gimp-tool-preset-editor"); + + gimp_data_editor_set_data (GIMP_DATA_EDITOR (gtk_bin_get_child (GTK_BIN (dockable))), + GIMP_DATA (preset)); +} diff --git a/app/actions/tool-options-commands.h b/app/actions/tool-options-commands.h new file mode 100644 index 0000000..8594e44 --- /dev/null +++ b/app/actions/tool-options-commands.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 . + */ + +#ifndef __TOOL_OPTIONS_COMMANDS_H__ +#define __TOOL_OPTIONS_COMMANDS_H__ + + +void tool_options_save_new_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tool_options_save_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_options_restore_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_options_edit_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_options_delete_preset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tool_options_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_options_reset_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TOOL_OPTIONS_COMMANDS_H__ */ diff --git a/app/actions/tool-preset-editor-actions.c b/app/actions/tool-preset-editor-actions.c new file mode 100644 index 0000000..8b22e6f --- /dev/null +++ b/app/actions/tool-preset-editor-actions.c @@ -0,0 +1,105 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdataeditor.h" +#include "widgets/gimphelp-ids.h" + +#include "data-editor-commands.h" +#include "tool-preset-editor-actions.h" +#include "tool-preset-editor-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry tool_preset_editor_actions[] = +{ + { "tool-preset-editor-popup", GIMP_ICON_TOOL_PRESET, + NC_("tool-preset-editor-action", "Tool Preset Editor Menu"), NULL, NULL, NULL, + GIMP_HELP_TOOL_PRESET_EDITOR_DIALOG }, + + { "tool-preset-editor-save", GIMP_ICON_DOCUMENT_SAVE, + NC_("tool-preset-editor-action", "_Save Tool Options to Preset"), NULL, + NC_("tool-preset-editor-action", "Save the active tool options to this " + "tool preset"), + tool_preset_editor_save_cmd_callback, + GIMP_HELP_TOOL_PRESET_SAVE }, + + { "tool-preset-editor-restore", GIMP_ICON_DOCUMENT_REVERT, + NC_("tool-preset-editor-action", "_Restore Tool Preset"), NULL, + NC_("tool-preset-editor-action", "Restore this tool preset"), + tool_preset_editor_restore_cmd_callback, + GIMP_HELP_TOOL_PRESET_RESTORE } +}; + + +static const GimpToggleActionEntry tool_preset_editor_toggle_actions[] = +{ + { "tool-preset-editor-edit-active", GIMP_ICON_LINKED, + NC_("tool-preset-editor-action", "Edit Active Tool Preset"), NULL, NULL, + data_editor_edit_active_cmd_callback, + FALSE, + GIMP_HELP_TOOL_PRESET_EDITOR_EDIT_ACTIVE } +}; + + +void +tool_preset_editor_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "tool-preset-editor-action", + tool_preset_editor_actions, + G_N_ELEMENTS (tool_preset_editor_actions)); + + gimp_action_group_add_toggle_actions (group, "tool-preset-editor-action", + tool_preset_editor_toggle_actions, + G_N_ELEMENTS (tool_preset_editor_toggle_actions)); + +} + +void +tool_preset_editor_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpDataEditor *data_editor = GIMP_DATA_EDITOR (user_data); + gboolean edit_active = FALSE; + + edit_active = gimp_data_editor_get_edit_active (data_editor); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_SENSITIVE ("tool-preset-editor-save", data_editor->data); + SET_SENSITIVE ("tool-preset-editor-restore", data_editor->data); + SET_ACTIVE ("tool-preset-editor-edit-active", edit_active); + +#undef SET_SENSITIVE +#undef SET_ACTIVE +} diff --git a/app/actions/tool-preset-editor-actions.h b/app/actions/tool-preset-editor-actions.h new file mode 100644 index 0000000..35275c5 --- /dev/null +++ b/app/actions/tool-preset-editor-actions.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 . + */ + +#ifndef __TOOL_PRESET_EDITOR_ACTIONS_H__ +#define __TOOL_PRESET_EDITOR_ACTIONS_H__ + + +void tool_preset_editor_actions_setup (GimpActionGroup *group); +void tool_preset_editor_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TOOL_PRESET_EDITOR_ACTIONS_H__ */ diff --git a/app/actions/tool-preset-editor-commands.c b/app/actions/tool-preset-editor-commands.c new file mode 100644 index 0000000..654ae16 --- /dev/null +++ b/app/actions/tool-preset-editor-commands.c @@ -0,0 +1,90 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimptoolinfo.h" +#include "core/gimptoolpreset.h" + +#include "widgets/gimpdataeditor.h" + +#include "tool-preset-editor-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +tool_preset_editor_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDataEditor *editor = GIMP_DATA_EDITOR (data); + GimpContext *context = editor->context; + GimpToolPreset *preset; + GimpToolInfo *tool_info; + + preset = GIMP_TOOL_PRESET (gimp_data_editor_get_data (editor)); + tool_info = gimp_context_get_tool (gimp_get_user_context (context->gimp)); + + if (tool_info && preset) + { + GimpToolInfo *preset_tool; + + preset_tool = gimp_context_get_tool (GIMP_CONTEXT (preset->tool_options)); + + if (tool_info != preset_tool) + { + gimp_message (context->gimp, + G_OBJECT (editor), GIMP_MESSAGE_WARNING, + _("Can't save '%s' tool options to an " + "existing '%s' tool preset."), + tool_info->label, + preset_tool->label); + return; + } + + gimp_config_sync (G_OBJECT (tool_info->tool_options), + G_OBJECT (preset->tool_options), 0); + } +} + +void +tool_preset_editor_restore_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDataEditor *editor = GIMP_DATA_EDITOR (data); + GimpContext *context = editor->context; + GimpToolPreset *preset; + + preset = GIMP_TOOL_PRESET (gimp_data_editor_get_data (editor)); + + if (preset) + gimp_context_tool_preset_changed (gimp_get_user_context (context->gimp)); +} diff --git a/app/actions/tool-preset-editor-commands.h b/app/actions/tool-preset-editor-commands.h new file mode 100644 index 0000000..f90f636 --- /dev/null +++ b/app/actions/tool-preset-editor-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __TOOL_PRESET_EDITOR_COMMANDS_H__ +#define __TOOL_PRESET_EDITOR_COMMANDS_H__ + + +void tool_preset_editor_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_preset_editor_restore_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TOOL_PRESET_EDITOR_COMMANDS_H__ */ diff --git a/app/actions/tool-presets-actions.c b/app/actions/tool-presets-actions.c new file mode 100644 index 0000000..36baabd --- /dev/null +++ b/app/actions/tool-presets-actions.c @@ -0,0 +1,155 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpdata.h" +#include "core/gimptoolpreset.h" +#include "core/gimptooloptions.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "data-commands.h" +#include "tool-presets-actions.h" +#include "tool-presets-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry tool_presets_actions[] = +{ + { "tool-presets-popup", GIMP_ICON_TOOL_PRESET, + NC_("tool-presets-action", "Tool Presets Menu"), NULL, NULL, NULL, + GIMP_HELP_TOOL_PRESET_DIALOG }, + + { "tool-presets-new", GIMP_ICON_DOCUMENT_NEW, + NC_("tool-presets-action", "_New Tool Preset"), NULL, + NC_("tool-presets-action", "Create a new tool preset"), + data_new_cmd_callback, + GIMP_HELP_TOOL_PRESET_NEW }, + + { "tool-presets-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("tool-presets-action", "D_uplicate Tool Preset"), NULL, + NC_("tool-presets-action", "Duplicate this tool preset"), + data_duplicate_cmd_callback, + GIMP_HELP_TOOL_PRESET_DUPLICATE }, + + { "tool-presets-copy-location", GIMP_ICON_EDIT_COPY, + NC_("tool-presets-action", "Copy Tool Preset _Location"), NULL, + NC_("tool-presets-action", "Copy tool preset file location to clipboard"), + data_copy_location_cmd_callback, + GIMP_HELP_TOOL_PRESET_COPY_LOCATION }, + + { "tool-presets-show-in-file-manager", GIMP_ICON_FILE_MANAGER, + NC_("tool-presets-action", "Show in _File Manager"), NULL, + NC_("tool-presets-action", "Show tool preset file location in the file manager"), + data_show_in_file_manager_cmd_callback, + GIMP_HELP_TOOL_PRESET_SHOW_IN_FILE_MANAGER }, + + { "tool-presets-save", GIMP_ICON_DOCUMENT_SAVE, + NC_("tool-presets-action", "_Save Tool Options to Preset"), NULL, + NC_("tool-presets-action", "Save the active tool options to this " + "tool preset"), + tool_presets_save_cmd_callback, + GIMP_HELP_TOOL_PRESET_SAVE }, + + { "tool-presets-restore", GIMP_ICON_DOCUMENT_REVERT, + NC_("tool-presets-action", "_Restore Tool Preset"), NULL, + NC_("tool-presets-action", "Restore this tool preset"), + tool_presets_restore_cmd_callback, + GIMP_HELP_TOOL_PRESET_RESTORE }, + + { "tool-presets-delete", GIMP_ICON_EDIT_DELETE, + NC_("tool-presets-action", "_Delete Tool Preset"), NULL, + NC_("tool-presets-action", "Delete this tool preset"), + data_delete_cmd_callback, + GIMP_HELP_TOOL_PRESET_DELETE }, + + { "tool-presets-refresh", GIMP_ICON_VIEW_REFRESH, + NC_("tool-presets-action", "_Refresh Tool Presets"), NULL, + NC_("tool-presets-action", "Refresh tool presets"), + data_refresh_cmd_callback, + GIMP_HELP_TOOL_PRESET_REFRESH } +}; + +static const GimpStringActionEntry tool_presets_edit_actions[] = +{ + { "tool-presets-edit", GIMP_ICON_EDIT, + NC_("tool-presets-action", "_Edit Tool Preset..."), NULL, + NC_("tool-presets-action", "Edit this tool preset"), + "gimp-tool-preset-editor", + GIMP_HELP_TOOL_PRESET_EDIT } +}; + + +void +tool_presets_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "tool-presets-action", + tool_presets_actions, + G_N_ELEMENTS (tool_presets_actions)); + + gimp_action_group_add_string_actions (group, "tool-presets-action", + tool_presets_edit_actions, + G_N_ELEMENTS (tool_presets_edit_actions), + data_edit_cmd_callback); +} + +void +tool_presets_actions_update (GimpActionGroup *group, + gpointer user_data) +{ + GimpContext *context = action_data_get_context (user_data); + GimpToolPreset *tool_preset = NULL; + GimpData *data = NULL; + GFile *file = NULL; + + if (context) + { + tool_preset = gimp_context_get_tool_preset (context); + + if (tool_preset) + { + data = GIMP_DATA (tool_preset); + + file = gimp_data_get_file (data); + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + SET_SENSITIVE ("tool-presets-edit", tool_preset); + SET_SENSITIVE ("tool-presets-duplicate", tool_preset && gimp_data_is_duplicatable (data)); + SET_SENSITIVE ("tool-presets-copy-location", file); + SET_SENSITIVE ("tool-presets-show-in-file-manager", file); + SET_SENSITIVE ("tool-presets-save", tool_preset); + SET_SENSITIVE ("tool-presets-restore", tool_preset); + SET_SENSITIVE ("tool-presets-delete", tool_preset && gimp_data_is_deletable (data)); + +#undef SET_SENSITIVE +} diff --git a/app/actions/tool-presets-actions.h b/app/actions/tool-presets-actions.h new file mode 100644 index 0000000..7cdf969 --- /dev/null +++ b/app/actions/tool-presets-actions.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 . + */ + +#ifndef __TOOL_PRESETS_ACTIONS_H__ +#define __TOOL_PRESETS_ACTIONS_H__ + + +void tool_presets_actions_setup (GimpActionGroup *group); +void tool_presets_actions_update (GimpActionGroup *group, + gpointer user_data); + + +#endif /* __TOOL_PRESET_ACTIONS_H__ */ diff --git a/app/actions/tool-presets-commands.c b/app/actions/tool-presets-commands.c new file mode 100644 index 0000000..bc60557 --- /dev/null +++ b/app/actions/tool-presets-commands.c @@ -0,0 +1,95 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimptoolinfo.h" +#include "core/gimptoolpreset.h" + +#include "widgets/gimpcontainereditor.h" +#include "widgets/gimpcontainerview.h" + +#include "tool-presets-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +tool_presets_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpToolPreset *preset; + GimpToolInfo *tool_info; + + context = gimp_container_view_get_context (editor->view); + + preset = gimp_context_get_tool_preset (context); + tool_info = gimp_context_get_tool (gimp_get_user_context (context->gimp)); + + if (tool_info && preset) + { + GimpToolInfo *preset_tool; + + preset_tool = gimp_context_get_tool (GIMP_CONTEXT (preset->tool_options)); + + if (tool_info != preset_tool) + { + gimp_message (context->gimp, + G_OBJECT (editor), GIMP_MESSAGE_WARNING, + _("Can't save '%s' tool options to an " + "existing '%s' tool preset."), + tool_info->label, + preset_tool->label); + return; + } + + gimp_config_sync (G_OBJECT (tool_info->tool_options), + G_OBJECT (preset->tool_options), 0); + } +} + +void +tool_presets_restore_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContainerEditor *editor = GIMP_CONTAINER_EDITOR (data); + GimpContext *context; + GimpToolPreset *preset; + + context = gimp_container_view_get_context (editor->view); + + preset = gimp_context_get_tool_preset (context); + + if (preset) + gimp_context_tool_preset_changed (gimp_get_user_context (context->gimp)); +} diff --git a/app/actions/tool-presets-commands.h b/app/actions/tool-presets-commands.h new file mode 100644 index 0000000..ecf9c21 --- /dev/null +++ b/app/actions/tool-presets-commands.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __TOOL_PRESETS_COMMANDS_H__ +#define __TOOL_PRESETS_COMMANDS_H__ + + +void tool_presets_save_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tool_presets_restore_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TOOL_PRESETS_COMMANDS_H__ */ diff --git a/app/actions/tools-actions.c b/app/actions/tools-actions.c new file mode 100644 index 0000000..9ee1c6c --- /dev/null +++ b/app/actions/tools-actions.c @@ -0,0 +1,812 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "tools-actions.h" +#include "tools-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry tools_actions[] = +{ + { "tools-menu", NULL, NC_("tools-action", "_Tools") }, + { "tools-select-menu", NULL, NC_("tools-action", "_Selection Tools") }, + { "tools-paint-menu", NULL, NC_("tools-action", "_Paint Tools") }, + { "tools-transform-menu", NULL, NC_("tools-action", "_Transform Tools") }, + { "tools-color-menu", NULL, NC_("tools-action", "_Color Tools") }, +}; + +static const GimpStringActionEntry tools_alternative_actions[] = +{ + { "tools-by-color-select-short", GIMP_ICON_TOOL_BY_COLOR_SELECT, + NC_("tools-action", "_By Color"), NULL, + NC_("tools-action", "Select regions with similar colors"), + "gimp-by-color-select-tool", + GIMP_HELP_TOOL_BY_COLOR_SELECT }, + + { "tools-rotate-arbitrary", GIMP_ICON_TOOL_ROTATE, + NC_("tools-action", "_Arbitrary Rotation..."), "", + NC_("tools-action", "Rotate drawable by an arbitrary angle"), + "gimp-rotate-layer", + GIMP_HELP_TOOL_ROTATE }, + + { "tools-rotate-image-arbitrary", GIMP_ICON_TOOL_ROTATE, + NC_("tools-action", "_Arbitrary Rotation..."), "", + NC_("tools-action", "Rotate image by an arbitrary angle"), + "gimp-rotate-image", + GIMP_HELP_TOOL_ROTATE } +}; + +static const GimpEnumActionEntry tools_color_average_radius_actions[] = +{ + { "tools-color-average-radius-set", GIMP_ICON_TOOL_COLOR_PICKER, + "Set Color Picker Radius", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_size_actions[] = +{ + { "tools-paintbrush-size-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Size", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_aspect_ratio_actions[] = +{ + { "tools-paintbrush-aspect-ratio-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Aspect Ratio", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_angle_actions[] = +{ + { "tools-paintbrush-angle-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Angle", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_spacing_actions[] = +{ + { "tools-paintbrush-spacing-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Spacing", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_hardness_actions[] = +{ + { "tools-paintbrush-hardness-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Hardness", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_paintbrush_force_actions[] = +{ + { "tools-paintbrush-force-set", GIMP_ICON_TOOL_PAINTBRUSH, + "Set Brush Force", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_ink_blob_size_actions[] = +{ + { "tools-ink-blob-size-set", GIMP_ICON_TOOL_INK, + "Set Ink Blob Size", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_ink_blob_aspect_actions[] = +{ + { "tools-ink-blob-aspect-set", GIMP_ICON_TOOL_INK, + "Set Ink Blob Aspect", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_ink_blob_angle_actions[] = +{ + { "tools-ink-blob-angle-set", GIMP_ICON_TOOL_INK, + "Set Ink Blob Angle", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_airbrush_rate_actions[] = +{ + { "tools-airbrush-rate-set", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-airbrush-rate-minimum", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-airbrush-rate-maximum", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-airbrush-rate-decrease", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-airbrush-rate-increase", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-airbrush-rate-decrease-skip", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-airbrush-rate-increase-skip", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Rate: Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry tools_airbrush_flow_actions[] = +{ + { "tools-airbrush-flow-set", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-airbrush-flow-minimum", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Set to Minimum"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-airbrush-flow-maximum", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Set to Maximum"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-airbrush-flow-decrease", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-airbrush-flow-increase", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-airbrush-flow-decrease-skip", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-airbrush-flow-increase-skip", GIMP_ICON_TOOL_AIRBRUSH, + NC_("tools-action", "Airbrush Flow: Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry tools_mybrush_radius_actions[] = +{ + { "tools-mypaint-brush-radius-set", GIMP_ICON_TOOL_MYPAINT_BRUSH, + "Set MyPaint Brush Radius", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_mybrush_hardness_actions[] = +{ + { "tools-mypaint-brush-hardness-set", GIMP_ICON_TOOL_MYPAINT_BRUSH, + "Set MyPaint Brush Hardness", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_foreground_select_brush_size_actions[] = +{ + { "tools-foreground-select-brush-size-set", + GIMP_ICON_TOOL_FOREGROUND_SELECT, + "Set Foreground Select Brush Size", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_transform_preview_opacity_actions[] = +{ + { "tools-transform-preview-opacity-set", GIMP_ICON_TOOL_PERSPECTIVE, + "Set Transform Tool Preview Opacity", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_warp_effect_size_actions[] = +{ + { "tools-warp-effect-size-set", GIMP_ICON_TOOL_WARP, + "Set Warp Effect Size", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_warp_effect_hardness_actions[] = +{ + { "tools-warp-effect-hardness-set", GIMP_ICON_TOOL_WARP, + "Set Warp Effect Hardness", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL } +}; + +static const GimpEnumActionEntry tools_opacity_actions[] = +{ + { "tools-opacity-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-opacity-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Set to Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-opacity-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-opacity-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-opacity-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Decrease by 1"), "less", NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-opacity-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Increase by 1"), "greater", NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-opacity-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Decrease by 10"), "less", NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-opacity-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Increase by 10"), "greater", NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-opacity-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-opacity-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Opacity: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_size_actions[] = +{ + { "tools-size-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-size-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Set to Default Value"), "backslash", NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-size-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-size-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-size-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Decrease by 1"), "bracketleft", NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-size-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Increase by 1"), "bracketright", NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-size-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Decrease by 10"), "braceleft", NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-size-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Increase by 10"), "braceright", NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-size-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-size-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Size: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_aspect_actions[] = +{ + { "tools-aspect-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-aspect-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Set To Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-aspect-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-aspect-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-aspect-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Decrease by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-aspect-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Increase by 0.1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-aspect-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-aspect-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-aspect-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-aspect-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Aspect Ratio: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_angle_actions[] = +{ + { "tools-angle-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-angle-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Set Angle To Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-angle-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-angle-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-angle-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Decrease by 1°"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-angle-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Increase by 1°"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-angle-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Decrease by 15°"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-angle-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Increase by 15°"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-angle-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-angle-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Angle: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_spacing_actions[] = +{ + { "tools-spacing-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-spacing-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Set To Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-spacing-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-spacing-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-spacing-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-spacing-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-spacing-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-spacing-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-spacing-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-spacing-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Spacing: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_hardness_actions[] = +{ + { "tools-hardness-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-hardness-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Set to Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-hardness-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-hardness-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-hardness-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-hardness-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-hardness-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-hardness-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-hardness-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-hardness-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Hardness: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_force_actions[] = +{ + { "tools-force-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Set"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-force-set-to-default", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Set to Default Value"), NULL, NULL, + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + NULL }, + { "tools-force-minimum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Minimize"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-force-maximum", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Maximize"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-force-decrease", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Decrease by 1"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-force-increase", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Increase by 1"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + { "tools-force-decrease-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Decrease by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + { "tools-force-increase-skip", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Increase by 10"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL }, + { "tools-force-decrease-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Decrease Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_PREVIOUS, FALSE, + NULL }, + { "tools-force-increase-percent", GIMP_ICON_DIALOG_TOOL_OPTIONS, + NC_("tools-action", "Tool's Force: Increase Relative"), NULL, NULL, + GIMP_ACTION_SELECT_PERCENT_NEXT, FALSE, + NULL }, +}; + +static const GimpEnumActionEntry tools_object_1_actions[] = +{ + { "tools-object-1-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Select Object 1 by Index", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-object-1-first", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "First Object 1", NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-object-1-last", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Last Object 1", NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-object-1-previous", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Previous Object 1", NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-object-1-next", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Next Object 1", NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry tools_object_2_actions[] = +{ + { "tools-object-2-set", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Select Object 2 by Index", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + { "tools-object-2-first", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "First Object 2", NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + { "tools-object-2-last", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Last Object 2", NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + { "tools-object-2-previous", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Previous Object 2", NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + { "tools-object-2-next", GIMP_ICON_DIALOG_TOOL_OPTIONS, + "Next Object 2", NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL } +}; + + +void +tools_actions_setup (GimpActionGroup *group) +{ + GimpAction *action; + GList *list; + + gimp_action_group_add_actions (group, "tools-action", + tools_actions, + G_N_ELEMENTS (tools_actions)); + + gimp_action_group_add_string_actions (group, "tools-action", + tools_alternative_actions, + G_N_ELEMENTS (tools_alternative_actions), + tools_select_cmd_callback); + + action = gimp_action_group_get_action (group, + "tools-by-color-select-short"); + gimp_action_set_accel_path (action, "/tools/tools-by-color-select"); + + gimp_action_group_add_enum_actions (group, NULL, + tools_color_average_radius_actions, + G_N_ELEMENTS (tools_color_average_radius_actions), + tools_color_average_radius_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_size_actions, + G_N_ELEMENTS (tools_paintbrush_size_actions), + tools_paintbrush_size_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_aspect_ratio_actions, + G_N_ELEMENTS (tools_paintbrush_aspect_ratio_actions), + tools_paintbrush_aspect_ratio_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_angle_actions, + G_N_ELEMENTS (tools_paintbrush_angle_actions), + tools_paintbrush_angle_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_spacing_actions, + G_N_ELEMENTS (tools_paintbrush_spacing_actions), + tools_paintbrush_spacing_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_hardness_actions, + G_N_ELEMENTS (tools_paintbrush_hardness_actions), + tools_paintbrush_hardness_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_paintbrush_force_actions, + G_N_ELEMENTS (tools_paintbrush_force_actions), + tools_paintbrush_force_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_ink_blob_size_actions, + G_N_ELEMENTS (tools_ink_blob_size_actions), + tools_ink_blob_size_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_ink_blob_aspect_actions, + G_N_ELEMENTS (tools_ink_blob_aspect_actions), + tools_ink_blob_aspect_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_ink_blob_angle_actions, + G_N_ELEMENTS (tools_ink_blob_angle_actions), + tools_ink_blob_angle_cmd_callback); + + gimp_action_group_add_enum_actions (group, "tools-action", + tools_airbrush_rate_actions, + G_N_ELEMENTS (tools_airbrush_rate_actions), + tools_airbrush_rate_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_airbrush_flow_actions, + G_N_ELEMENTS (tools_airbrush_flow_actions), + tools_airbrush_flow_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_mybrush_radius_actions, + G_N_ELEMENTS (tools_mybrush_radius_actions), + tools_mybrush_radius_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_mybrush_hardness_actions, + G_N_ELEMENTS (tools_mybrush_hardness_actions), + tools_mybrush_hardness_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_foreground_select_brush_size_actions, + G_N_ELEMENTS (tools_foreground_select_brush_size_actions), + tools_fg_select_brush_size_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_transform_preview_opacity_actions, + G_N_ELEMENTS (tools_transform_preview_opacity_actions), + tools_transform_preview_opacity_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_warp_effect_size_actions, + G_N_ELEMENTS (tools_warp_effect_size_actions), + tools_warp_effect_size_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_warp_effect_hardness_actions, + G_N_ELEMENTS (tools_warp_effect_hardness_actions), + tools_warp_effect_hardness_cmd_callback); + + gimp_action_group_add_enum_actions (group, "tools-action", + tools_opacity_actions, + G_N_ELEMENTS (tools_opacity_actions), + tools_opacity_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_size_actions, + G_N_ELEMENTS (tools_size_actions), + tools_size_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_aspect_actions, + G_N_ELEMENTS (tools_aspect_actions), + tools_aspect_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_angle_actions, + G_N_ELEMENTS (tools_angle_actions), + tools_angle_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_spacing_actions, + G_N_ELEMENTS (tools_spacing_actions), + tools_spacing_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_hardness_actions, + G_N_ELEMENTS (tools_hardness_actions), + tools_hardness_cmd_callback); + gimp_action_group_add_enum_actions (group, "tools-action", + tools_force_actions, + G_N_ELEMENTS (tools_force_actions), + tools_force_cmd_callback); + + gimp_action_group_add_enum_actions (group, NULL, + tools_object_1_actions, + G_N_ELEMENTS (tools_object_1_actions), + tools_object_1_cmd_callback); + gimp_action_group_add_enum_actions (group, NULL, + tools_object_2_actions, + G_N_ELEMENTS (tools_object_2_actions), + tools_object_2_cmd_callback); + + for (list = gimp_get_tool_info_iter (group->gimp); + list; + list = g_list_next (list)) + { + GimpToolInfo *tool_info = list->data; + + if (tool_info->menu_label) + { + GimpStringActionEntry entry; + gchar *name; + const gchar *icon_name; + const gchar *identifier; + + name = gimp_tool_info_get_action_name (tool_info); + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + identifier = gimp_object_get_name (tool_info); + + entry.name = name; + entry.icon_name = icon_name; + entry.label = tool_info->menu_label; + entry.accelerator = tool_info->menu_accel; + entry.tooltip = tool_info->tooltip; + entry.help_id = tool_info->help_id; + entry.value = identifier; + + gimp_action_group_add_string_actions (group, NULL, + &entry, 1, + tools_select_cmd_callback); + + g_free (name); + } + } +} + +void +tools_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) + + /* Since these are listed under the Image and Layer Menus, + * they are disabled without an image to keep consistency + */ + SET_SENSITIVE ("tools-rotate-arbitrary", image); + SET_SENSITIVE ("tools-rotate-image-arbitrary", image); + +#undef SET_SENSITIVE +} diff --git a/app/actions/tools-actions.h b/app/actions/tools-actions.h new file mode 100644 index 0000000..9bec68b --- /dev/null +++ b/app/actions/tools-actions.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 . + */ + +#ifndef __TOOLS_ACTIONS_H__ +#define __TOOLS_ACTIONS_H__ + + +void tools_actions_setup (GimpActionGroup *group); +void tools_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __TOOLS_ACTIONS_H__ */ diff --git a/app/actions/tools-commands.c b/app/actions/tools-commands.c new file mode 100644 index 0000000..7e84c53 --- /dev/null +++ b/app/actions/tools-commands.c @@ -0,0 +1,820 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "actions-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimptoolinfo.h" + +#include "paint/gimpinkoptions.h" +#include "paint/gimpairbrushoptions.h" +#include "paint/gimpmybrushoptions.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpenumaction.h" +#include "widgets/gimpuimanager.h" + +#include "display/gimpdisplay.h" + +#include "tools/gimp-tools.h" +#include "tools/gimpcoloroptions.h" +#include "tools/gimpforegroundselectoptions.h" +#include "tools/gimprectangleoptions.h" +#include "tools/gimptool.h" +#include "tools/gimptoolcontrol.h" +#include "tools/gimptransformoptions.h" +#include "tools/gimptransformtool.h" +#include "tools/gimpwarpoptions.h" +#include "tools/tool_manager.h" + +#include "actions.h" +#include "tools-commands.h" + + +/* local function prototypes */ + +static void tools_activate_enum_action (const gchar *action_desc, + GVariant *value); + + +/* local variables */ + +/* this is a hack to allow GimpToolButton to activate a tool-selection action + * without initializing the tool + */ +static gint tools_select_cmd_initialize_blocked = 0; + + +/* public functions */ + +void +tools_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpToolInfo *tool_info; + GimpContext *context; + GimpDisplay *display; + const gchar *tool_name; + gboolean set_transform_type = FALSE; + GimpTransformType transform_type; + return_if_no_gimp (gimp, data); + + tool_name = g_variant_get_string (value, NULL); + + /* special case gimp-rotate-tool being called from the Image or Layer + * menus + */ + if (strcmp (tool_name, "gimp-rotate-layer") == 0) + { + tool_name = "gimp-rotate-tool"; + set_transform_type = TRUE; + transform_type = GIMP_TRANSFORM_TYPE_LAYER; + } + else if (strcmp (tool_name, "gimp-rotate-image") == 0) + { + tool_name = "gimp-rotate-tool"; + set_transform_type = TRUE; + transform_type = GIMP_TRANSFORM_TYPE_IMAGE; + } + + tool_info = gimp_get_tool_info (gimp, tool_name); + + context = gimp_get_user_context (gimp); + + /* always allocate a new tool when selected from the image menu + */ + if (gimp_context_get_tool (context) != tool_info || + tools_select_cmd_initialize_blocked) + { + gimp_context_set_tool (context, tool_info); + } + else + { + gimp_context_tool_changed (context); + } + + if (set_transform_type) + { + GimpTool *tool = tool_manager_get_active (gimp); + + gimp_transform_tool_set_type (GIMP_TRANSFORM_TOOL (tool), + transform_type); + } + + if (! tools_select_cmd_initialize_blocked) + { + display = gimp_context_get_display (context); + + if (display && gimp_display_get_image (display)) + tool_manager_initialize_active (gimp, display); + } +} + +void +tools_select_cmd_block_initialize (void) +{ + tools_select_cmd_initialize_blocked++; +} + +void +tools_select_cmd_unblock_initialize (void) +{ + g_return_if_fail (tools_select_cmd_initialize_blocked > 0); + + tools_select_cmd_initialize_blocked--; +} + +void +tools_color_average_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_COLOR_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "average-radius", + 1.0, 1.0, 10.0, 0.1, FALSE); + } +} + +void +tools_paintbrush_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-size", + 0.1, 1.0, 10.0, 1.0, FALSE); + } +} + +void +tools_paintbrush_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-angle", + 0.1, 1.0, 15.0, 0.1, TRUE); + } +} + +void +tools_paintbrush_aspect_ratio_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-aspect-ratio", + 0.01, 0.1, 1.0, 0.1, TRUE); + } +} + +void +tools_paintbrush_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-spacing", + 0.001, 0.01, 0.1, 0.1, FALSE); + } +} + +void +tools_paintbrush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-hardness", + 0.001, 0.01, 0.1, 0.1, FALSE); + } +} + +void +tools_paintbrush_force_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_PAINT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "brush-force", + 0.001, 0.01, 0.1, 0.1, FALSE); + } +} + +void +tools_ink_blob_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_INK_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "size", + 0.1, 1.0, 10.0, 0.1, FALSE); + } +} + +void +tools_ink_blob_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_INK_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "blob-aspect", + 1.0, 0.1, 1.0, 0.1, FALSE); + } +} + +void +tools_ink_blob_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_INK_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "blob-angle", + gimp_deg_to_rad (0.1), + gimp_deg_to_rad (1.0), + gimp_deg_to_rad (15.0), + 0.1, TRUE); + } +} + +void +tools_airbrush_rate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_AIRBRUSH_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "rate", + 0.1, 1.0, 10.0, 0.1, FALSE); + } +} + +void +tools_airbrush_flow_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_AIRBRUSH_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "flow", + 0.1, 1.0, 10.0, 0.1, FALSE); + } +} + +void +tools_mybrush_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_MYBRUSH_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "radius", + 0.1, 0.1, 0.5, 1.0, FALSE); + } +} + +void +tools_mybrush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_MYBRUSH_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "hardness", + 0.001, 0.01, 0.1, 1.0, FALSE); + } +} + +void +tools_fg_select_brush_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_FOREGROUND_SELECT_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "stroke-width", + 1.0, 4.0, 16.0, 0.1, FALSE); + } +} + +void +tools_transform_preview_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_TRANSFORM_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "preview-opacity", + 0.01, 0.1, 0.5, 0.1, FALSE); + } +} + +void +tools_warp_effect_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_WARP_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "effect-size", + 1.0, 4.0, 16.0, 0.1, FALSE); + } +} + +void +tools_warp_effect_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpToolInfo *tool_info; + GimpActionSelectType select_type; + return_if_no_context (context, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + tool_info = gimp_context_get_tool (context); + + if (tool_info && GIMP_IS_WARP_OPTIONS (tool_info->tool_options)) + { + action_select_property (select_type, + action_data_get_display (data), + G_OBJECT (tool_info->tool_options), + "effect-hardness", + 0.001, 0.01, 0.1, 0.1, FALSE); + } +} + +void +tools_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_opacity (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_size (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_aspect (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_angle (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_spacing (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_hardness (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_force_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_force (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_object_1_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_object_1 (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + +void +tools_object_2_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpContext *context; + GimpTool *tool; + return_if_no_context (context, data); + + tool = tool_manager_get_active (context->gimp); + + if (tool) + { + const gchar *action_desc; + + action_desc = gimp_tool_control_get_action_object_2 (tool->control); + + if (action_desc) + tools_activate_enum_action (action_desc, value); + } +} + + +/* private functions */ + +static void +tools_activate_enum_action (const gchar *action_desc, + GVariant *value) +{ + gchar *group_name; + gchar *action_name; + + group_name = g_strdup (action_desc); + action_name = strchr (group_name, '/'); + + if (action_name) + { + GList *managers; + GimpAction *action; + + *action_name++ = '\0'; + + managers = gimp_ui_managers_from_name (""); + + action = gimp_ui_manager_find_action (managers->data, + group_name, action_name); + + if (GIMP_IS_ENUM_ACTION (action) && + GIMP_ENUM_ACTION (action)->value_variable) + { + gimp_action_emit_activate (GIMP_ACTION (action), value); + } + } + + g_free (group_name); +} diff --git a/app/actions/tools-commands.h b/app/actions/tools-commands.h new file mode 100644 index 0000000..38ab455 --- /dev/null +++ b/app/actions/tools-commands.h @@ -0,0 +1,120 @@ +/* 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 . + */ + +#ifndef __TOOLS_COMMANDS_H__ +#define __TOOLS_COMMANDS_H__ + + +void tools_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_select_cmd_block_initialize (void); +void tools_select_cmd_unblock_initialize (void); + +void tools_color_average_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_paintbrush_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_paintbrush_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_paintbrush_aspect_ratio_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_paintbrush_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_paintbrush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_paintbrush_force_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_ink_blob_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_ink_blob_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_ink_blob_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_airbrush_rate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_airbrush_flow_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_mybrush_radius_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_mybrush_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_fg_select_brush_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_transform_preview_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_warp_effect_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_warp_effect_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_opacity_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_size_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_aspect_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_angle_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_spacing_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_hardness_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_force_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void tools_object_1_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void tools_object_2_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __TOOLS_COMMANDS_H__ */ diff --git a/app/actions/vectors-actions.c b/app/actions/vectors-actions.c new file mode 100644 index 0000000..300203b --- /dev/null +++ b/app/actions/vectors-actions.c @@ -0,0 +1,465 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "core/gimpchannel.h" +#include "core/gimpcontainer.h" +#include "core/gimpimage.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "actions.h" +#include "items-actions.h" +#include "vectors-actions.h" +#include "vectors-commands.h" + +#include "gimp-intl.h" + + +static const GimpActionEntry vectors_actions[] = +{ + { "vectors-popup", GIMP_ICON_DIALOG_PATHS, + NC_("vectors-action", "Paths Menu"), NULL, NULL, NULL, + GIMP_HELP_PATH_DIALOG }, + + { "vectors-color-tag-menu", NULL, + NC_("vectors-action", "Color Tag"), NULL, NULL, NULL, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-edit", GIMP_ICON_TOOL_PATH, + NC_("vectors-action", "Edit Pa_th"), NULL, + NC_("vectors-action", "Edit the active path"), + vectors_edit_cmd_callback, + GIMP_HELP_TOOL_VECTORS }, + + { "vectors-edit-attributes", GIMP_ICON_EDIT, + NC_("vectors-action", "_Edit Path Attributes..."), NULL, + NC_("vectors-action", "Edit path attributes"), + vectors_edit_attributes_cmd_callback, + GIMP_HELP_PATH_EDIT }, + + { "vectors-new", GIMP_ICON_DOCUMENT_NEW, + NC_("vectors-action", "_New Path..."), NULL, + NC_("vectors-action", "Create a new path..."), + vectors_new_cmd_callback, + GIMP_HELP_PATH_NEW }, + + { "vectors-new-last-values", GIMP_ICON_DOCUMENT_NEW, + NC_("vectors-action", "_New Path with last values"), NULL, + NC_("vectors-action", "Create a new path with last used values"), + vectors_new_last_vals_cmd_callback, + GIMP_HELP_PATH_NEW }, + + { "vectors-duplicate", GIMP_ICON_OBJECT_DUPLICATE, + NC_("vectors-action", "D_uplicate Path"), NULL, + NC_("vectors-action", "Duplicate this path"), + vectors_duplicate_cmd_callback, + GIMP_HELP_PATH_DUPLICATE }, + + { "vectors-delete", GIMP_ICON_EDIT_DELETE, + NC_("vectors-action", "_Delete Path"), NULL, + NC_("vectors-action", "Delete this path"), + vectors_delete_cmd_callback, + GIMP_HELP_PATH_DELETE }, + + { "vectors-merge-visible", NULL, + NC_("vectors-action", "Merge _Visible Paths"), NULL, NULL, + vectors_merge_visible_cmd_callback, + GIMP_HELP_PATH_MERGE_VISIBLE }, + + { "vectors-raise", GIMP_ICON_GO_UP, + NC_("vectors-action", "_Raise Path"), NULL, + NC_("vectors-action", "Raise this path"), + vectors_raise_cmd_callback, + GIMP_HELP_PATH_RAISE }, + + { "vectors-raise-to-top", GIMP_ICON_GO_TOP, + NC_("vectors-action", "Raise Path to _Top"), NULL, + NC_("vectors-action", "Raise this path to the top"), + vectors_raise_to_top_cmd_callback, + GIMP_HELP_PATH_RAISE_TO_TOP }, + + { "vectors-lower", GIMP_ICON_GO_DOWN, + NC_("vectors-action", "_Lower Path"), NULL, + NC_("vectors-action", "Lower this path"), + vectors_lower_cmd_callback, + GIMP_HELP_PATH_LOWER }, + + { "vectors-lower-to-bottom", GIMP_ICON_GO_BOTTOM, + NC_("vectors-action", "Lower Path to _Bottom"), NULL, + NC_("vectors-action", "Lower this path to the bottom"), + vectors_lower_to_bottom_cmd_callback, + GIMP_HELP_PATH_LOWER_TO_BOTTOM }, + + { "vectors-fill", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("vectors-action", "Fill Pat_h..."), NULL, + NC_("vectors-action", "Fill the path"), + vectors_fill_cmd_callback, + GIMP_HELP_PATH_FILL }, + + { "vectors-fill-last-values", GIMP_ICON_TOOL_BUCKET_FILL, + NC_("vectors-action", "Fill Path"), NULL, + NC_("vectors-action", "Fill the path with last values"), + vectors_fill_last_vals_cmd_callback, + GIMP_HELP_PATH_FILL }, + + { "vectors-stroke", GIMP_ICON_PATH_STROKE, + NC_("vectors-action", "Stro_ke Path..."), NULL, + NC_("vectors-action", "Paint along the path"), + vectors_stroke_cmd_callback, + GIMP_HELP_PATH_STROKE }, + + { "vectors-stroke-last-values", GIMP_ICON_PATH_STROKE, + NC_("vectors-action", "Stro_ke Path"), NULL, + NC_("vectors-action", "Paint along the path with last values"), + vectors_stroke_last_vals_cmd_callback, + GIMP_HELP_PATH_STROKE }, + + { "vectors-copy", GIMP_ICON_EDIT_COPY, + NC_("vectors-action", "Co_py Path"), "", NULL, + vectors_copy_cmd_callback, + GIMP_HELP_PATH_COPY }, + + { "vectors-paste", GIMP_ICON_EDIT_PASTE, + NC_("vectors-action", "Paste Pat_h"), "", NULL, + vectors_paste_cmd_callback, + GIMP_HELP_PATH_PASTE }, + + { "vectors-export", GIMP_ICON_DOCUMENT_SAVE, + NC_("vectors-action", "E_xport Path..."), "", NULL, + vectors_export_cmd_callback, + GIMP_HELP_PATH_EXPORT }, + + { "vectors-import", GIMP_ICON_DOCUMENT_OPEN, + NC_("vectors-action", "I_mport Path..."), "", NULL, + vectors_import_cmd_callback, + GIMP_HELP_PATH_IMPORT } +}; + +static const GimpToggleActionEntry vectors_toggle_actions[] = +{ + { "vectors-visible", GIMP_ICON_VISIBLE, + NC_("vectors-action", "Toggle Path _Visibility"), NULL, NULL, + vectors_visible_cmd_callback, + FALSE, + GIMP_HELP_PATH_VISIBLE }, + + { "vectors-linked", GIMP_ICON_LINKED, + NC_("vectors-action", "Toggle Path _Linked State"), NULL, NULL, + vectors_linked_cmd_callback, + FALSE, + GIMP_HELP_PATH_LINKED }, + + { "vectors-lock-content", NULL /* GIMP_ICON_LOCK */, + NC_("vectors-action", "L_ock Strokes of Path"), NULL, NULL, + vectors_lock_content_cmd_callback, + FALSE, + GIMP_HELP_PATH_LOCK_STROKES }, + + { "vectors-lock-position", GIMP_ICON_TOOL_MOVE, + NC_("vectors-action", "L_ock Position of Path"), NULL, NULL, + vectors_lock_position_cmd_callback, + FALSE, + GIMP_HELP_PATH_LOCK_POSITION } +}; + +static const GimpEnumActionEntry vectors_color_tag_actions[] = +{ + { "vectors-color-tag-none", GIMP_ICON_EDIT_CLEAR, + NC_("vectors-action", "None"), NULL, + NC_("vectors-action", "Path Color Tag: Clear"), + GIMP_COLOR_TAG_NONE, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-blue", NULL, + NC_("vectors-action", "Blue"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Blue"), + GIMP_COLOR_TAG_BLUE, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-green", NULL, + NC_("vectors-action", "Green"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Green"), + GIMP_COLOR_TAG_GREEN, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-yellow", NULL, + NC_("vectors-action", "Yellow"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Yellow"), + GIMP_COLOR_TAG_YELLOW, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-orange", NULL, + NC_("vectors-action", "Orange"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Orange"), + GIMP_COLOR_TAG_ORANGE, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-brown", NULL, + NC_("vectors-action", "Brown"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Brown"), + GIMP_COLOR_TAG_BROWN, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-red", NULL, + NC_("vectors-action", "Red"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Red"), + GIMP_COLOR_TAG_RED, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-violet", NULL, + NC_("vectors-action", "Violet"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Violet"), + GIMP_COLOR_TAG_VIOLET, FALSE, + GIMP_HELP_PATH_COLOR_TAG }, + + { "vectors-color-tag-gray", NULL, + NC_("vectors-action", "Gray"), NULL, + NC_("vectors-action", "Path Color Tag: Set to Gray"), + GIMP_COLOR_TAG_GRAY, FALSE, + GIMP_HELP_PATH_COLOR_TAG } +}; + +static const GimpEnumActionEntry vectors_to_selection_actions[] = +{ + { "vectors-selection-replace", GIMP_ICON_SELECTION_REPLACE, + NC_("vectors-action", "Path to Sele_ction"), NULL, + NC_("vectors-action", "Path to selection"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_PATH_SELECTION_REPLACE }, + + { "vectors-selection-from-vectors", GIMP_ICON_SELECTION_REPLACE, + NC_("vectors-action", "Fr_om Path"), "V", + NC_("vectors-action", "Replace selection with path"), + GIMP_CHANNEL_OP_REPLACE, FALSE, + GIMP_HELP_PATH_SELECTION_REPLACE }, + + { "vectors-selection-add", GIMP_ICON_SELECTION_ADD, + NC_("vectors-action", "_Add to Selection"), NULL, + NC_("vectors-action", "Add path to selection"), + GIMP_CHANNEL_OP_ADD, FALSE, + GIMP_HELP_PATH_SELECTION_ADD }, + + { "vectors-selection-subtract", GIMP_ICON_SELECTION_SUBTRACT, + NC_("vectors-action", "_Subtract from Selection"), NULL, + NC_("vectors-action", "Subtract path from selection"), + GIMP_CHANNEL_OP_SUBTRACT, FALSE, + GIMP_HELP_PATH_SELECTION_SUBTRACT }, + + { "vectors-selection-intersect", GIMP_ICON_SELECTION_INTERSECT, + NC_("vectors-action", "_Intersect with Selection"), NULL, + NC_("vectors-action", "Intersect path with selection"), + GIMP_CHANNEL_OP_INTERSECT, FALSE, + GIMP_HELP_PATH_SELECTION_INTERSECT } +}; + +static const GimpEnumActionEntry vectors_selection_to_vectors_actions[] = +{ + { "vectors-selection-to-vectors", GIMP_ICON_SELECTION_TO_PATH, + NC_("vectors-action", "Selecti_on to Path"), NULL, + NC_("vectors-action", "Selection to path"), + FALSE, FALSE, + GIMP_HELP_SELECTION_TO_PATH }, + + { "vectors-selection-to-vectors-short", GIMP_ICON_SELECTION_TO_PATH, + NC_("vectors-action", "To _Path"), NULL, + NC_("vectors-action", "Selection to path"), + FALSE, FALSE, + GIMP_HELP_SELECTION_TO_PATH }, + + { "vectors-selection-to-vectors-advanced", GIMP_ICON_SELECTION_TO_PATH, + NC_("vectors-action", "Selection to Path (_Advanced)"), NULL, + NC_("vectors-action", "Advanced options"), + TRUE, FALSE, + GIMP_HELP_SELECTION_TO_PATH } +}; + +static const GimpEnumActionEntry vectors_select_actions[] = +{ + { "vectors-select-top", NULL, + NC_("vectors-action", "Select _Top Path"), NULL, + NC_("vectors-action", "Select the topmost path"), + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_PATH_TOP }, + + { "vectors-select-bottom", NULL, + NC_("vectors-action", "Select _Bottom Path"), NULL, + NC_("vectors-action", "Select the bottommost path"), + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_PATH_BOTTOM }, + + { "vectors-select-previous", NULL, + NC_("vectors-action", "Select _Previous Path"), NULL, + NC_("vectors-action", "Select the path above the current path"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_PATH_PREVIOUS }, + + { "vectors-select-next", NULL, + NC_("vectors-action", "Select _Next Path"), NULL, + NC_("vectors-action", "Select the vector below the current path"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_PATH_NEXT } +}; + +void +vectors_actions_setup (GimpActionGroup *group) +{ + gimp_action_group_add_actions (group, "vectors-action", + vectors_actions, + G_N_ELEMENTS (vectors_actions)); + + gimp_action_group_add_toggle_actions (group, "vectors-action", + vectors_toggle_actions, + G_N_ELEMENTS (vectors_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "vectors-action", + vectors_color_tag_actions, + G_N_ELEMENTS (vectors_color_tag_actions), + vectors_color_tag_cmd_callback); + + gimp_action_group_add_enum_actions (group, "vectors-action", + vectors_to_selection_actions, + G_N_ELEMENTS (vectors_to_selection_actions), + vectors_to_selection_cmd_callback); + + gimp_action_group_add_enum_actions (group, "vectors-action", + vectors_selection_to_vectors_actions, + G_N_ELEMENTS (vectors_selection_to_vectors_actions), + vectors_selection_to_vectors_cmd_callback); + + gimp_action_group_add_enum_actions (group, "vectors-action", + vectors_select_actions, + G_N_ELEMENTS (vectors_select_actions), + vectors_select_cmd_callback); + + items_actions_setup (group, "vectors"); +} + +void +vectors_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpImage *image = action_data_get_image (data); + GimpVectors *vectors = NULL; + GimpDrawable *drawable = NULL; + gint n_vectors = 0; + gboolean mask_empty = TRUE; + gboolean dr_writable = FALSE; + gboolean dr_children = FALSE; + GList *next = NULL; + GList *prev = NULL; + + if (image) + { + n_vectors = gimp_image_get_n_vectors (image); + mask_empty = gimp_channel_is_empty (gimp_image_get_mask (image)); + + vectors = gimp_image_get_active_vectors (image); + + if (vectors) + { + GList *vectors_list; + GList *list; + + vectors_list = gimp_item_get_container_iter (GIMP_ITEM (vectors)); + + list = g_list_find (vectors_list, vectors); + + if (list) + { + prev = g_list_previous (list); + next = g_list_next (list); + } + } + + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + dr_writable = ! gimp_item_is_content_locked (GIMP_ITEM (drawable)); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + dr_children = TRUE; + } + } + +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_SENSITIVE ("vectors-edit", vectors); + SET_SENSITIVE ("vectors-edit-attributes", vectors); + + SET_SENSITIVE ("vectors-new", image); + SET_SENSITIVE ("vectors-new-last-values", image); + SET_SENSITIVE ("vectors-duplicate", vectors); + SET_SENSITIVE ("vectors-delete", vectors); + SET_SENSITIVE ("vectors-merge-visible", n_vectors > 1); + + SET_SENSITIVE ("vectors-raise", vectors && prev); + SET_SENSITIVE ("vectors-raise-to-top", vectors && prev); + SET_SENSITIVE ("vectors-lower", vectors && next); + SET_SENSITIVE ("vectors-lower-to-bottom", vectors && next); + + SET_SENSITIVE ("vectors-copy", vectors); + SET_SENSITIVE ("vectors-paste", image); + SET_SENSITIVE ("vectors-export", vectors); + SET_SENSITIVE ("vectors-import", image); + + SET_SENSITIVE ("vectors-selection-to-vectors", image && !mask_empty); + SET_SENSITIVE ("vectors-selection-to-vectors-short", image && !mask_empty); + SET_SENSITIVE ("vectors-selection-to-vectors-advanced", image && !mask_empty); + SET_SENSITIVE ("vectors-fill", vectors && + dr_writable && + !dr_children); + SET_SENSITIVE ("vectors-fill-last-values", vectors && + dr_writable && + !dr_children); + SET_SENSITIVE ("vectors-stroke", vectors && + dr_writable && + !dr_children); + SET_SENSITIVE ("vectors-stroke-last-values", vectors && + dr_writable && + !dr_children); + + SET_SENSITIVE ("vectors-selection-replace", vectors); + SET_SENSITIVE ("vectors-selection-from-vectors", vectors); + SET_SENSITIVE ("vectors-selection-add", vectors); + SET_SENSITIVE ("vectors-selection-subtract", vectors); + SET_SENSITIVE ("vectors-selection-intersect", vectors); + + SET_SENSITIVE ("vectors-select-top", vectors && prev); + SET_SENSITIVE ("vectors-select-bottom", vectors && next); + SET_SENSITIVE ("vectors-select-previous", vectors && prev); + SET_SENSITIVE ("vectors-select-next", vectors && next); + +#undef SET_SENSITIVE +#undef SET_ACTIVE + + items_actions_update (group, "vectors", GIMP_ITEM (vectors)); +} diff --git a/app/actions/vectors-actions.h b/app/actions/vectors-actions.h new file mode 100644 index 0000000..b5422b8 --- /dev/null +++ b/app/actions/vectors-actions.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 . + */ + +#ifndef __VECTORS_ACTIONS_H__ +#define __VECTORS_ACTIONS_H__ + + +void vectors_actions_setup (GimpActionGroup *group); +void vectors_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __VECTORS_ACTIONS_H__ */ diff --git a/app/actions/vectors-commands.c b/app/actions/vectors-commands.c new file mode 100644 index 0000000..43b3044 --- /dev/null +++ b/app/actions/vectors-commands.c @@ -0,0 +1,892 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdialogconfig.h" + +#include "core/gimp.h" +#include "core/gimp-utils.h" +#include "core/gimpchannel.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-merge.h" +#include "core/gimpimage-undo.h" +#include "core/gimpparamspecs.h" +#include "core/gimpprogress.h" +#include "core/gimptoolinfo.h" + +#include "pdb/gimppdb.h" +#include "pdb/gimpprocedure.h" + +#include "vectors/gimpvectors.h" +#include "vectors/gimpvectors-export.h" +#include "vectors/gimpvectors-import.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpclipboard.h" +#include "widgets/gimphelp-ids.h" + +#include "display/gimpdisplay.h" + +#include "tools/gimpvectortool.h" +#include "tools/tool_manager.h" + +#include "dialogs/dialogs.h" +#include "dialogs/vectors-export-dialog.h" +#include "dialogs/vectors-import-dialog.h" +#include "dialogs/vectors-options-dialog.h" + +#include "actions.h" +#include "items-commands.h" +#include "vectors-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void vectors_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + gpointer user_data); +static void vectors_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + gpointer user_data); +static void vectors_import_callback (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *import_folder, + gboolean merge_vectors, + gboolean scale_vectors, + gpointer user_data); +static void vectors_export_callback (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *export_folder, + gboolean active_only, + gpointer user_data); + + +/* public functions */ + +void +vectors_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpTool *active_tool; + return_if_no_vectors (image, vectors, data); + + active_tool = tool_manager_get_active (image->gimp); + + if (! GIMP_IS_VECTOR_TOOL (active_tool)) + { + GimpToolInfo *tool_info = gimp_get_tool_info (image->gimp, + "gimp-vector-tool"); + + if (GIMP_IS_TOOL_INFO (tool_info)) + { + gimp_context_set_tool (action_data_get_context (data), tool_info); + active_tool = tool_manager_get_active (image->gimp); + } + } + + if (GIMP_IS_VECTOR_TOOL (active_tool)) + gimp_vector_tool_set_vectors (GIMP_VECTOR_TOOL (active_tool), vectors); +} + +void +vectors_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_vectors (image, vectors, data); + return_if_no_widget (widget, data); + +#define EDIT_DIALOG_KEY "gimp-vectors-edit-attributes-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (vectors), EDIT_DIALOG_KEY); + + if (! dialog) + { + GimpItem *item = GIMP_ITEM (vectors); + + dialog = vectors_options_dialog_new (image, vectors, + action_data_get_context (data), + widget, + _("Path Attributes"), + "gimp-vectors-edit", + GIMP_ICON_EDIT, + _("Edit Path Attributes"), + GIMP_HELP_PATH_EDIT, + gimp_object_get_name (vectors), + gimp_item_get_visible (item), + gimp_item_get_linked (item), + gimp_item_get_color_tag (item), + gimp_item_get_lock_content (item), + gimp_item_get_lock_position (item), + vectors_edit_attributes_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (vectors), EDIT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +vectors_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + +#define NEW_DIALOG_KEY "gimp-vectors-new-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + dialog = vectors_options_dialog_new (image, NULL, + action_data_get_context (data), + widget, + _("New Path"), + "gimp-vectors-new", + GIMP_ICON_PATH, + _("Create a New Path"), + GIMP_HELP_PATH_NEW, + config->vectors_new_name, + FALSE, + FALSE, + GIMP_COLOR_TAG_NONE, + FALSE, + FALSE, + vectors_new_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +vectors_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpDialogConfig *config; + return_if_no_image (image, data); + + config = GIMP_DIALOG_CONFIG (image->gimp->config); + + vectors = gimp_vectors_new (image, config->vectors_new_name); + gimp_image_add_vectors (image, vectors, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); +} + +void +vectors_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + gimp_image_raise_item (image, GIMP_ITEM (vectors), NULL); + gimp_image_flush (image); +} + +void +vectors_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + gimp_image_raise_item_to_top (image, GIMP_ITEM (vectors)); + gimp_image_flush (image); +} + +void +vectors_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + gimp_image_lower_item (image, GIMP_ITEM (vectors), NULL); + gimp_image_flush (image); +} + +void +vectors_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + gimp_image_lower_item_to_bottom (image, GIMP_ITEM (vectors)); + gimp_image_flush (image); +} + +void +vectors_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpVectors *new_vectors; + return_if_no_vectors (image, vectors, data); + + new_vectors = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), + G_TYPE_FROM_INSTANCE (vectors))); + /* use the actual parent here, not GIMP_IMAGE_ACTIVE_PARENT because + * the latter would add a duplicated group inside itself instead of + * above it + */ + gimp_image_add_vectors (image, new_vectors, + gimp_vectors_get_parent (vectors), -1, + TRUE); + gimp_image_flush (image); +} + +void +vectors_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + gimp_image_remove_vectors (image, vectors, TRUE, NULL); + gimp_image_flush (image); +} + +void +vectors_merge_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GtkWidget *widget; + GError *error = NULL; + return_if_no_vectors (image, vectors, data); + return_if_no_widget (widget, data); + + if (! gimp_image_merge_visible_vectors (image, &error)) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_WARNING, + error->message); + g_clear_error (&error); + return; + } + + gimp_image_flush (image); +} + +void +vectors_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpChannelOps operation; + return_if_no_vectors (image, vectors, data); + + operation = (GimpChannelOps) g_variant_get_int32 (value); + + gimp_item_to_selection (GIMP_ITEM (vectors), operation, + TRUE, FALSE, 0, 0); + gimp_image_flush (image); +} + +void +vectors_selection_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GimpProcedure *procedure; + GimpValueArray *args; + GimpDisplay *display; + gboolean advanced; + GError *error = NULL; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + advanced = (gboolean) g_variant_get_int32 (value); + + if (advanced) + procedure = gimp_pdb_lookup_procedure (image->gimp->pdb, + "plug-in-sel2path-advanced"); + else + procedure = gimp_pdb_lookup_procedure (image->gimp->pdb, + "plug-in-sel2path"); + + if (! procedure) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_ERROR, + "Selection to path procedure lookup failed."); + return; + } + + display = gimp_context_get_display (action_data_get_context (data)); + + args = gimp_procedure_get_arguments (procedure); + gimp_value_array_truncate (args, 2); + + g_value_set_int (gimp_value_array_index (args, 0), + GIMP_RUN_INTERACTIVE); + gimp_value_set_image (gimp_value_array_index (args, 1), + image); + + gimp_procedure_execute_async (procedure, image->gimp, + action_data_get_context (data), + GIMP_PROGRESS (display), args, + GIMP_OBJECT (display), &error); + + gimp_value_array_unref (args); + + if (error) + { + gimp_message_literal (image->gimp, + G_OBJECT (widget), GIMP_MESSAGE_ERROR, + error->message); + g_error_free (error); + } +} + +void +vectors_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_fill_cmd_callback (action, + image, GIMP_ITEM (vectors), + "gimp-vectors-fill-dialog", + _("Fill Path"), + GIMP_ICON_TOOL_BUCKET_FILL, + GIMP_HELP_PATH_FILL, + data); +} + +void +vectors_fill_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_fill_last_vals_cmd_callback (action, + image, GIMP_ITEM (vectors), + data); +} + +void +vectors_stroke_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_stroke_cmd_callback (action, + image, GIMP_ITEM (vectors), + "gimp-vectors-stroke-dialog", + _("Stroke Path"), + GIMP_ICON_PATH_STROKE, + GIMP_HELP_PATH_STROKE, + data); +} + +void +vectors_stroke_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_stroke_last_vals_cmd_callback (action, + image, GIMP_ITEM (vectors), + data); +} + +void +vectors_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + gchar *svg; + return_if_no_vectors (image, vectors, data); + + svg = gimp_vectors_export_string (image, vectors); + + if (svg) + { + gimp_clipboard_set_svg (image->gimp, svg); + g_free (svg); + } +} + +void +vectors_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + gchar *svg; + gsize svg_size; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + + svg = gimp_clipboard_get_svg (image->gimp, &svg_size); + + if (svg) + { + GError *error = NULL; + + if (! gimp_vectors_import_buffer (image, svg, svg_size, + TRUE, FALSE, + GIMP_IMAGE_ACTIVE_PARENT, -1, + NULL, &error)) + { + gimp_message (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + } + else + { + gimp_image_flush (image); + } + + g_free (svg); + } +} + +void +vectors_export_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_vectors (image, vectors, data); + return_if_no_widget (widget, data); + +#define EXPORT_DIALOG_KEY "gimp-vectors-export-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GFile *folder = NULL; + + if (config->vectors_export_path) + folder = gimp_file_new_for_config_path (config->vectors_export_path, + NULL); + + dialog = vectors_export_dialog_new (image, widget, + folder, + config->vectors_export_active_only, + vectors_export_callback, + NULL); + + if (folder) + g_object_unref (folder); + + dialogs_attach_dialog (G_OBJECT (image), EXPORT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +vectors_import_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GtkWidget *widget; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_widget (widget, data); + +#define IMPORT_DIALOG_KEY "gimp-vectors-import-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (image), IMPORT_DIALOG_KEY); + + if (! dialog) + { + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GFile *folder = NULL; + + if (config->vectors_import_path) + folder = gimp_file_new_for_config_path (config->vectors_import_path, + NULL); + + dialog = vectors_import_dialog_new (image, widget, + folder, + config->vectors_import_merge, + config->vectors_import_scale, + vectors_import_callback, + NULL); + + dialogs_attach_dialog (G_OBJECT (image), IMPORT_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +vectors_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_visible_cmd_callback (action, value, image, GIMP_ITEM (vectors)); +} + +void +vectors_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_linked_cmd_callback (action, value, image, GIMP_ITEM (vectors)); +} + +void +vectors_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_lock_content_cmd_callback (action, value, image, GIMP_ITEM (vectors)); +} + +void +vectors_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + return_if_no_vectors (image, vectors, data); + + items_lock_position_cmd_callback (action, value, image, GIMP_ITEM (vectors)); +} + +void +vectors_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpColorTag color_tag; + return_if_no_vectors (image, vectors, data); + + color_tag = (GimpColorTag) g_variant_get_int32 (value); + + items_color_tag_cmd_callback (action, image, GIMP_ITEM (vectors), + color_tag); +} + + +/* private functions */ + +static void +vectors_new_callback (GtkWidget *dialog, + GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + + g_object_set (config, + "path-new-name", vectors_name, + NULL); + + vectors = gimp_vectors_new (image, config->vectors_new_name); + gimp_item_set_visible (GIMP_ITEM (vectors), vectors_visible, FALSE); + gimp_item_set_linked (GIMP_ITEM (vectors), vectors_linked, FALSE); + gimp_item_set_color_tag (GIMP_ITEM (vectors), vectors_color_tag, FALSE); + gimp_item_set_lock_content (GIMP_ITEM (vectors), vectors_lock_content, FALSE); + gimp_item_set_lock_position (GIMP_ITEM (vectors), vectors_lock_position, FALSE); + + gimp_image_add_vectors (image, vectors, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + gimp_image_flush (image); + + gtk_widget_destroy (dialog); +} + +static void +vectors_edit_attributes_callback (GtkWidget *dialog, + GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + gpointer user_data) +{ + GimpItem *item = GIMP_ITEM (vectors); + + if (strcmp (vectors_name, gimp_object_get_name (vectors)) || + vectors_visible != gimp_item_get_visible (item) || + vectors_linked != gimp_item_get_linked (item) || + vectors_color_tag != gimp_item_get_color_tag (item) || + vectors_lock_content != gimp_item_get_lock_content (item) || + vectors_lock_position != gimp_item_get_lock_position (item)) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_PROPERTIES, + _("Path Attributes")); + + if (strcmp (vectors_name, gimp_object_get_name (vectors))) + gimp_item_rename (GIMP_ITEM (vectors), vectors_name, NULL); + + if (vectors_visible != gimp_item_get_visible (item)) + gimp_item_set_visible (item, vectors_visible, TRUE); + + if (vectors_linked != gimp_item_get_linked (item)) + gimp_item_set_linked (item, vectors_linked, TRUE); + + if (vectors_color_tag != gimp_item_get_color_tag (item)) + gimp_item_set_color_tag (item, vectors_color_tag, TRUE); + + if (vectors_lock_content != gimp_item_get_lock_content (item)) + gimp_item_set_lock_content (item, vectors_lock_content, TRUE); + + if (vectors_lock_position != gimp_item_get_lock_position (item)) + gimp_item_set_lock_position (item, vectors_lock_position, TRUE); + + gimp_image_undo_group_end (image); + + gimp_image_flush (image); + } + + gtk_widget_destroy (dialog); +} + +static void +vectors_import_callback (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *import_folder, + gboolean merge_vectors, + gboolean scale_vectors, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + gchar *path = NULL; + GError *error = NULL; + + if (import_folder) + path = gimp_file_get_config_path (import_folder, NULL); + + g_object_set (config, + "path-import-path", path, + "path-import-merge", merge_vectors, + "path-import-scale", scale_vectors, + NULL); + + if (path) + g_free (path); + + if (gimp_vectors_import_file (image, file, + config->vectors_import_merge, + config->vectors_import_scale, + GIMP_IMAGE_ACTIVE_PARENT, -1, + NULL, &error)) + { + gimp_image_flush (image); + } + else + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_error_free (error); + return; + } + + gtk_widget_destroy (dialog); +} + +static void +vectors_export_callback (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *export_folder, + gboolean active_only, + gpointer user_data) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpVectors *vectors = NULL; + gchar *path = NULL; + GError *error = NULL; + + if (export_folder) + path = gimp_file_get_config_path (export_folder, NULL); + + g_object_set (config, + "path-export-path", path, + "path-export-active-only", active_only, + NULL); + + if (path) + g_free (path); + + if (config->vectors_export_active_only) + vectors = gimp_image_get_active_vectors (image); + + if (! gimp_vectors_export_file (image, vectors, file, &error)) + { + gimp_message (image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + return; + } + + gtk_widget_destroy (dialog); +} + +void +vectors_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpVectors *vectors; + GimpContainer *container; + GimpVectors *new_vectors; + GimpActionSelectType select_type; + return_if_no_image (image, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + vectors = gimp_image_get_active_vectors (image); + + if (vectors) + container = gimp_item_get_container (GIMP_ITEM (vectors)); + else + container = gimp_image_get_vectors (image); + + new_vectors = (GimpVectors *) action_select_object (select_type, + container, + (GimpObject *) vectors); + + if (new_vectors && new_vectors != vectors) + { + gimp_image_set_active_vectors (image, new_vectors); + gimp_image_flush (image); + } +} diff --git a/app/actions/vectors-commands.h b/app/actions/vectors-commands.h new file mode 100644 index 0000000..5ce88b9 --- /dev/null +++ b/app/actions/vectors-commands.h @@ -0,0 +1,112 @@ +/* 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 . + */ + +#ifndef __VECTORS_COMMANDS_H__ +#define __VECTORS_COMMANDS_H__ + + +void vectors_edit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_edit_attributes_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_new_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_raise_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_raise_to_top_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_lower_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_lower_to_bottom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_duplicate_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_delete_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_merge_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_to_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_selection_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_fill_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_stroke_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_stroke_last_vals_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_copy_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_paste_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_export_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_import_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_visible_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_linked_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_lock_content_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void vectors_lock_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_color_tag_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void vectors_select_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __VECTORS_COMMANDS_H__ */ diff --git a/app/actions/view-actions.c b/app/actions/view-actions.c new file mode 100644 index 0000000..bbf272c --- /dev/null +++ b/app/actions/view-actions.c @@ -0,0 +1,1211 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdisplayoptions.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimprender.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-appearance.h" +#include "display/gimpdisplayshell-scale.h" +#include "display/gimpimagewindow.h" + +#include "actions.h" +#include "view-actions.h" +#include "view-commands.h" +#include "window-actions.h" +#include "window-commands.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void view_actions_set_zoom (GimpActionGroup *group, + GimpDisplayShell *shell); +static void view_actions_set_rotate (GimpActionGroup *group, + GimpDisplayShell *shell); +static void view_actions_check_type_notify (GimpDisplayConfig *config, + GParamSpec *pspec, + GimpActionGroup *group); + + +static const GimpActionEntry view_actions[] = +{ + { "view-menu", NULL, NC_("view-action", "_View") }, + { "view-zoom-menu", NULL, NC_("view-action", "_Zoom") }, + { "view-rotate-menu", NULL, NC_("view-action", "_Flip & Rotate") }, + { "view-padding-color-menu", NULL, NC_("view-action", "_Padding Color") }, + + { "view-color-management-menu", NULL, + NC_("view-action", "_Color Management") }, + + { "view-display-intent-menu", NULL, + NC_("view-action", "Display _Rendering Intent") }, + + { "view-softproof-intent-menu", NULL, + NC_("view-action", "Soft-Proofing Re_ndering Intent") }, + + { "view-move-to-screen-menu", GIMP_ICON_WINDOW_MOVE_TO_SCREEN, + NC_("view-action", "Move to Screen"), NULL, NULL, NULL, + GIMP_HELP_VIEW_CHANGE_SCREEN }, + + { "view-new", GIMP_ICON_WINDOW_NEW, + NC_("view-action", "_New View"), NULL, + NC_("view-action", "Create another view on this image"), + view_new_cmd_callback, + GIMP_HELP_VIEW_NEW }, + + { "view-close", GIMP_ICON_WINDOW_CLOSE, + NC_("view-action", "_Close View"), "W", + NC_("view-action", "Close the active image view"), + view_close_cmd_callback, + GIMP_HELP_FILE_CLOSE }, + + { "view-scroll-center", GIMP_ICON_CENTER, + NC_("view-action", "C_enter Image in Window"), "J", + NC_("view-action", "Scroll the image so that it is centered in the window"), + view_scroll_center_cmd_callback, + GIMP_HELP_VIEW_SCROLL_CENTER }, + + { "view-zoom-fit-in", GIMP_ICON_ZOOM_FIT_BEST, + NC_("view-action", "_Fit Image in Window"), "J", + NC_("view-action", "Adjust the zoom ratio so that the image becomes fully visible"), + view_zoom_fit_in_cmd_callback, + GIMP_HELP_VIEW_ZOOM_FIT_IN }, + + { "view-zoom-fill", GIMP_ICON_ZOOM_FIT_BEST, + NC_("view-action", "Fi_ll Window"), NULL, + NC_("view-action", "Adjust the zoom ratio so that the entire window is used"), + view_zoom_fill_cmd_callback, + GIMP_HELP_VIEW_ZOOM_FILL }, + + { "view-zoom-selection", GIMP_ICON_SELECTION, + NC_("view-action", "Zoom to _Selection"), NULL, + NC_("view-action", "Adjust the zoom ratio so that the selection fills the window"), + view_zoom_selection_cmd_callback, + GIMP_HELP_VIEW_ZOOM_SELECTION }, + + { "view-zoom-revert", NULL, + NC_("view-action", "Re_vert Zoom"), "grave", + NC_("view-action", "Restore the previous zoom level"), + view_zoom_revert_cmd_callback, + GIMP_HELP_VIEW_ZOOM_REVERT }, + + { "view-rotate-other", NULL, + NC_("view-action", "Othe_r rotation angle..."), NULL, + NC_("view-action", "Set a custom rotation angle"), + view_rotate_other_cmd_callback, + GIMP_HELP_VIEW_ROTATE_OTHER }, + + { "view-navigation-window", GIMP_ICON_DIALOG_NAVIGATION, + NC_("view-action", "Na_vigation Window"), NULL, + NC_("view-action", "Show an overview window for this image"), + view_navigation_window_cmd_callback, + GIMP_HELP_NAVIGATION_DIALOG }, + + { "view-display-filters", GIMP_ICON_DISPLAY_FILTER, + NC_("view-action", "Display _Filters..."), NULL, + NC_("view-action", "Configure filters applied to this view"), + view_display_filters_cmd_callback, + GIMP_HELP_DISPLAY_FILTER_DIALOG }, + + { "view-color-management-reset", GIMP_ICON_RESET, + NC_("view-action", "As in _Preferences"), NULL, + NC_("view-action", + "Reset color management to what's configured in preferences"), + view_color_management_reset_cmd_callback, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-profile", NULL, + NC_("view-action", "Soft-_Proofing Profile..."), NULL, + NC_("view-action", "Set the soft-proofing profile"), + view_softproof_profile_cmd_callback, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-shrink-wrap", GIMP_ICON_ZOOM_FIT_BEST, + NC_("view-action", "Shrink _Wrap"), "J", + NC_("view-action", "Reduce the image window to the size of the image display"), + view_shrink_wrap_cmd_callback, + GIMP_HELP_VIEW_SHRINK_WRAP }, + + { "view-open-display", NULL, + NC_("view-action", "_Open Display..."), NULL, + NC_("view-action", "Connect to another display"), + window_open_display_cmd_callback, + NULL } +}; + +static const GimpToggleActionEntry view_toggle_actions[] = +{ + + { "view-show-all", NULL, + NC_("view-action", "Show _All"), NULL, + NC_("view-action", "Show full image content"), + view_show_all_cmd_callback, + FALSE, + GIMP_HELP_VIEW_SHOW_ALL }, + + { "view-dot-for-dot", NULL, + NC_("view-action", "_Dot for Dot"), NULL, + NC_("view-action", "A pixel on the screen represents an image pixel"), + view_dot_for_dot_cmd_callback, + TRUE, + GIMP_HELP_VIEW_DOT_FOR_DOT }, + + { "view-color-management-enable", NULL, + NC_("view-action", "_Color-Manage this View"), NULL, + NC_("view-action", "Use color management for this view"), + view_color_management_enable_cmd_callback, + TRUE, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-color-management-softproof", NULL, + NC_("view-action", "_Proof Colors"), NULL, + NC_("view-action", "Use this view for soft-proofing"), + view_color_management_softproof_cmd_callback, + FALSE, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-display-black-point-compensation", NULL, + NC_("view-action", "_Black Point Compensation"), NULL, + NC_("view-action", "Use black point compensation for image display"), + view_display_bpc_cmd_callback, + TRUE, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-black-point-compensation", NULL, + NC_("view-action", "_Black Point Compensation"), NULL, + NC_("view-action", "Use black point compensation for soft-proofing"), + view_softproof_bpc_cmd_callback, + TRUE, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-gamut-check", NULL, + NC_("view-action", "_Mark Out Of Gamut Colors"), NULL, + NC_("view-action", "When soft-proofing, mark colors which cannot " + "be represented in the target color space"), + view_softproof_gamut_check_cmd_callback, + FALSE, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-show-selection", NULL, + NC_("view-action", "Show _Selection"), "T", + NC_("view-action", "Display the selection outline"), + view_toggle_selection_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_SELECTION }, + + { "view-show-layer-boundary", NULL, + NC_("view-action", "Show _Layer Boundary"), NULL, + NC_("view-action", "Draw a border around the active layer"), + view_toggle_layer_boundary_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_LAYER_BOUNDARY }, + + { "view-show-canvas-boundary", NULL, + NC_("view-action", "Show Canvas Bounda_ry"), NULL, + NC_("view-action", "Draw a border around the canvas"), + view_toggle_canvas_boundary_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_CANVAS_BOUNDARY }, + + { "view-show-guides", NULL, + NC_("view-action", "Show _Guides"), "T", + NC_("view-action", "Display the image's guides"), + view_toggle_guides_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_GUIDES }, + + { "view-show-grid", NULL, + NC_("view-action", "S_how Grid"), NULL, + NC_("view-action", "Display the image's grid"), + view_toggle_grid_cmd_callback, + FALSE, + GIMP_HELP_VIEW_SHOW_GRID }, + + { "view-show-sample-points", NULL, + NC_("view-action", "Sh_ow Sample Points"), NULL, + NC_("view-action", "Display the image's color sample points"), + view_toggle_sample_points_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_SAMPLE_POINTS }, + + { "view-snap-to-guides", NULL, + NC_("view-action", "Snap to Gu_ides"), NULL, + NC_("view-action", "Tool operations snap to guides"), + view_snap_to_guides_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SNAP_TO_GUIDES }, + + { "view-snap-to-grid", NULL, + NC_("view-action", "Sna_p to Grid"), NULL, + NC_("view-action", "Tool operations snap to the grid"), + view_snap_to_grid_cmd_callback, + FALSE, + GIMP_HELP_VIEW_SNAP_TO_GRID }, + + { "view-snap-to-canvas", NULL, + NC_("view-action", "Snap to _Canvas Edges"), NULL, + NC_("view-action", "Tool operations snap to the canvas edges"), + view_snap_to_canvas_cmd_callback, + FALSE, + GIMP_HELP_VIEW_SNAP_TO_CANVAS }, + + { "view-snap-to-vectors", NULL, + NC_("view-action", "Snap t_o Active Path"), NULL, + NC_("view-action", "Tool operations snap to the active path"), + view_snap_to_vectors_cmd_callback, + FALSE, + GIMP_HELP_VIEW_SNAP_TO_VECTORS }, + + { "view-show-menubar", NULL, + NC_("view-action", "Show _Menubar"), NULL, + NC_("view-action", "Show this window's menubar"), + view_toggle_menubar_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_MENUBAR }, + + { "view-show-rulers", NULL, + NC_("view-action", "Show R_ulers"), "R", + NC_("view-action", "Show this window's rulers"), + view_toggle_rulers_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_RULERS }, + + { "view-show-scrollbars", NULL, + NC_("view-action", "Show Scroll_bars"), NULL, + NC_("view-action", "Show this window's scrollbars"), + view_toggle_scrollbars_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_SCROLLBARS }, + + { "view-show-statusbar", NULL, + NC_("view-action", "Show S_tatusbar"), NULL, + NC_("view-action", "Show this window's statusbar"), + view_toggle_statusbar_cmd_callback, + TRUE, + GIMP_HELP_VIEW_SHOW_STATUSBAR }, + + { "view-fullscreen", GIMP_ICON_VIEW_FULLSCREEN, + NC_("view-action", "Fullscr_een"), "F11", + NC_("view-action", "Toggle fullscreen view"), + view_fullscreen_cmd_callback, + FALSE, + GIMP_HELP_VIEW_FULLSCREEN } +}; + +static const GimpEnumActionEntry view_zoom_actions[] = +{ + { "view-zoom", NULL, + NC_("view-zoom-action", "Set zoom factor"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + + { "view-zoom-minimum", GIMP_ICON_ZOOM_OUT, + NC_("view-zoom-action", "Zoom out as far as possible"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-maximum", GIMP_ICON_ZOOM_IN, + NC_("view-zoom-action", "Zoom in as far as possible"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-out", GIMP_ICON_ZOOM_OUT, + NC_("view-zoom-action", "Zoom _Out"), "minus", + NC_("view-zoom-action", "Zoom out"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-in", GIMP_ICON_ZOOM_IN, + NC_("view-zoom-action", "Zoom _In"), "plus", + NC_("view-zoom-action", "Zoom in"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-out-accel", GIMP_ICON_CHAR_PICKER, + NC_("view-zoom-action", "Zoom Out"), "KP_Subtract", + NC_("view-zoom-action", "Zoom out"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-in-accel", GIMP_ICON_CHAR_PICKER, + NC_("view-zoom-action", "Zoom In"), "KP_Add", + NC_("view-zoom-action", "Zoom in"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-out-skip", GIMP_ICON_ZOOM_OUT, + NC_("view-zoom-action", "Zoom out a lot"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-in-skip", GIMP_ICON_ZOOM_IN, + NC_("view-zoom-action", "Zoom in a lot"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + GIMP_HELP_VIEW_ZOOM_IN } +}; + +static const GimpRadioActionEntry view_zoom_explicit_actions[] = +{ + { "view-zoom-16-1", NULL, + NC_("view-zoom-action", "1_6:1 (1600%)"), "5", + NC_("view-zoom-action", "Zoom 16:1"), + 160000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-16-1-accel", NULL, + NC_("view-zoom-action", "1_6:1 (1600%)"), "KP_5", + NC_("view-zoom-action", "Zoom 16:1"), + 160000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-8-1", NULL, + NC_("view-zoom-action", "_8:1 (800%)"), "4", + NC_("view-zoom-action", "Zoom 8:1"), + 80000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-8-1-accel", NULL, + NC_("view-zoom-action", "_8:1 (800%)"), "KP_4", + NC_("view-zoom-action", "Zoom 8:1"), + 80000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-4-1", NULL, + NC_("view-zoom-action", "_4:1 (400%)"), "3", + NC_("view-zoom-action", "Zoom 4:1"), + 40000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-4-1-accel", NULL, + NC_("view-zoom-action", "_4:1 (400%)"), "KP_3", + NC_("view-zoom-action", "Zoom 4:1"), + 40000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-2-1", NULL, + NC_("view-zoom-action", "_2:1 (200%)"), "2", + NC_("view-zoom-action", "Zoom 2:1"), + 20000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-2-1-accel", NULL, + NC_("view-zoom-action", "_2:1 (200%)"), "KP_2", + NC_("view-zoom-action", "Zoom 2:1"), + 20000, + GIMP_HELP_VIEW_ZOOM_IN }, + + { "view-zoom-1-1", GIMP_ICON_ZOOM_ORIGINAL, + NC_("view-zoom-action", "_1:1 (100%)"), "1", + NC_("view-zoom-action", "Zoom 1:1"), + 10000, + GIMP_HELP_VIEW_ZOOM_100 }, + + { "view-zoom-1-1-accel", GIMP_ICON_ZOOM_ORIGINAL, + NC_("view-zoom-action", "_1:1 (100%)"), "KP_1", + NC_("view-zoom-action", "Zoom 1:1"), + 10000, + GIMP_HELP_VIEW_ZOOM_100 }, + + { "view-zoom-1-2", NULL, + NC_("view-zoom-action", "1:_2 (50%)"), "2", + NC_("view-zoom-action", "Zoom 1:2"), + 5000, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-1-4", NULL, + NC_("view-zoom-action", "1:_4 (25%)"), "3", + NC_("view-zoom-action", "Zoom 1:4"), + 2500, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-1-8", NULL, + NC_("view-zoom-action", "1:_8 (12.5%)"), "4", + NC_("view-zoom-action", "Zoom 1:8"), + 1250, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-1-16", NULL, + NC_("view-zoom-action", "1:1_6 (6.25%)"), "5", + NC_("view-zoom-action", "Zoom 1:16"), + 625, + GIMP_HELP_VIEW_ZOOM_OUT }, + + { "view-zoom-other", NULL, + NC_("view-zoom-action", "Othe_r zoom factor..."), NULL, + NC_("view-zoom-action", "Set a custom zoom factor"), + 0, + GIMP_HELP_VIEW_ZOOM_OTHER } +}; + +static const GimpToggleActionEntry view_flip_actions[] = +{ + { "view-flip-horizontally", GIMP_ICON_OBJECT_FLIP_HORIZONTAL, + NC_("view-action", "Flip _Horizontally"), NULL, + NC_("view-action", "Flip the view horizontally"), + view_flip_horizontally_cmd_callback, + FALSE, + GIMP_HELP_VIEW_FLIP }, + + { "view-flip-vertically", GIMP_ICON_OBJECT_FLIP_VERTICAL, + NC_("view-action", "Flip _Vertically"), NULL, + NC_("view-action", "Flip the view vertically"), + view_flip_vertically_cmd_callback, + FALSE, + GIMP_HELP_VIEW_FLIP } +}; + +static const GimpEnumActionEntry view_rotate_absolute_actions[] = +{ + { "view-rotate-set-absolute", NULL, + "Display Rotation Absolute Angle Set", NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + + { "view-rotate-reset", GIMP_ICON_RESET, + NC_("view-action", "_Reset Flip & Rotate"), "exclam", + NC_("view-action", + "Reset flipping to unflipped and the angle of rotation to 0°"), + GIMP_ACTION_SELECT_SET_TO_DEFAULT, FALSE, + GIMP_HELP_VIEW_ROTATE_RESET }, +}; + +static const GimpEnumActionEntry view_rotate_relative_actions[] = +{ + { "view-rotate-15", GIMP_ICON_OBJECT_ROTATE_90, + NC_("view-action", "Rotate 15° _clockwise"), NULL, + NC_("view-action", "Rotate the view 15 degrees to the right"), + GIMP_ACTION_SELECT_NEXT, FALSE, + GIMP_HELP_VIEW_ROTATE_15 }, + + { "view-rotate-90", GIMP_ICON_OBJECT_ROTATE_90, + NC_("view-action", "Rotate 90° _clockwise"), NULL, + NC_("view-action", "Rotate the view 90 degrees to the right"), + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + GIMP_HELP_VIEW_ROTATE_90 }, + + { "view-rotate-180", GIMP_ICON_OBJECT_ROTATE_180, + NC_("view-action", "Rotate _180°"), NULL, + NC_("view-action", "Turn the view upside-down"), + GIMP_ACTION_SELECT_LAST, FALSE, + GIMP_HELP_VIEW_ROTATE_180 }, + + { "view-rotate-270", GIMP_ICON_OBJECT_ROTATE_270, + NC_("view-action", "Rotate 90° counter-clock_wise"), NULL, + NC_("view-action", "Rotate the view 90 degrees to the left"), + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + GIMP_HELP_VIEW_ROTATE_270 }, + + { "view-rotate-345", GIMP_ICON_OBJECT_ROTATE_270, + NC_("view-action", "Rotate 15° counter-clock_wise"), NULL, + NC_("view-action", "Rotate the view 15 degrees to the left"), + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + GIMP_HELP_VIEW_ROTATE_345 } +}; + +static const GimpRadioActionEntry view_display_intent_actions[] = +{ + { "view-display-intent-perceptual", NULL, + NC_("view-action", "_Perceptual"), NULL, + NC_("view-action", "Display rendering intent is perceptual"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-display-intent-relative-colorimetric", NULL, + NC_("view-action", "_Relative Colorimetric"), NULL, + NC_("view-action", "Display rendering intent is relative colorimetric"), + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-display-intent-saturation", NULL, + NC_("view-action", "_Saturation"), NULL, + NC_("view-action", "Display rendering intent is saturation"), + GIMP_COLOR_RENDERING_INTENT_SATURATION, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-display-intent-absolute-colorimetric", NULL, + NC_("view-action", "_Absolute Colorimetric"), NULL, + NC_("view-action", "Display rendering intent is absolute colorimetric"), + GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC, + GIMP_HELP_VIEW_COLOR_MANAGEMENT } +}; + +static const GimpRadioActionEntry view_softproof_intent_actions[] = +{ + { "view-softproof-intent-perceptual", NULL, + NC_("view-action", "_Perceptual"), NULL, + NC_("view-action", "Soft-proofing rendering intent is perceptual"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-intent-relative-colorimetric", NULL, + NC_("view-action", "_Relative Colorimetric"), NULL, + NC_("view-action", "Soft-proofing rendering intent is relative colorimetric"), + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-intent-saturation", NULL, + NC_("view-action", "_Saturation"), NULL, + NC_("view-action", "Soft-proofing rendering intent is saturation"), + GIMP_COLOR_RENDERING_INTENT_SATURATION, + GIMP_HELP_VIEW_COLOR_MANAGEMENT }, + + { "view-softproof-intent-absolute-colorimetric", NULL, + NC_("view-action", "_Absolute Colorimetric"), NULL, + NC_("view-action", "Soft-proofing rendering intent is absolute colorimetric"), + GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC, + GIMP_HELP_VIEW_COLOR_MANAGEMENT } +}; + +static const GimpEnumActionEntry view_padding_color_actions[] = +{ + { "view-padding-color-theme", NULL, + NC_("view-padding-color", "From _Theme"), NULL, + NC_("view-padding-color", "Use the current theme's background color"), + GIMP_CANVAS_PADDING_MODE_DEFAULT, FALSE, + GIMP_HELP_VIEW_PADDING_COLOR }, + + { "view-padding-color-light-check", NULL, + NC_("view-padding-color", "_Light Check Color"), NULL, + NC_("view-padding-color", "Use the light check color"), + GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK, FALSE, + GIMP_HELP_VIEW_PADDING_COLOR }, + + { "view-padding-color-dark-check", NULL, + NC_("view-padding-color", "_Dark Check Color"), NULL, + NC_("view-padding-color", "Use the dark check color"), + GIMP_CANVAS_PADDING_MODE_DARK_CHECK, FALSE, + GIMP_HELP_VIEW_PADDING_COLOR }, + + { "view-padding-color-custom", GIMP_ICON_PALETTE, + NC_("view-padding-color", "_Custom Color..."), NULL, + NC_("view-padding-color", "Use an arbitrary color"), + GIMP_CANVAS_PADDING_MODE_CUSTOM, FALSE, + GIMP_HELP_VIEW_PADDING_COLOR }, + + { "view-padding-color-prefs", GIMP_ICON_RESET, + NC_("view-padding-color", "As in _Preferences"), NULL, + NC_("view-padding-color", + "Reset padding color to what's configured in preferences"), + GIMP_CANVAS_PADDING_MODE_RESET, FALSE, + GIMP_HELP_VIEW_PADDING_COLOR } +}; + +static const GimpToggleActionEntry view_padding_color_toggle_actions[] = +{ + { "view-padding-color-in-show-all", NULL, + NC_("view-padding-color", "Keep Padding in \"Show _All\" Mode"), NULL, + NC_("view-padding-color", + "Keep canvas padding when \"View -> Show All\" is enabled"), + view_padding_color_in_show_all_cmd_callback, + FALSE, + GIMP_HELP_VIEW_PADDING_COLOR } +}; + +static const GimpEnumActionEntry view_scroll_horizontal_actions[] = +{ + { "view-scroll-horizontal", NULL, + NC_("view-action", "Set horizontal scroll offset"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + + { "view-scroll-left-border", NULL, + NC_("view-action", "Scroll to left border"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + + { "view-scroll-right-border", NULL, + NC_("view-action", "Scroll to right border"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + + { "view-scroll-left", NULL, + NC_("view-action", "Scroll left"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + + { "view-scroll-right", NULL, + NC_("view-action", "Scroll right"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + + { "view-scroll-page-left", NULL, + NC_("view-action", "Scroll page left"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + + { "view-scroll-page-right", NULL, + NC_("view-action", "Scroll page right"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + +static const GimpEnumActionEntry view_scroll_vertical_actions[] = +{ + { "view-scroll-vertical", NULL, + NC_("view-action", "Set vertical scroll offset"), NULL, NULL, + GIMP_ACTION_SELECT_SET, TRUE, + NULL }, + + { "view-scroll-top-border", NULL, + NC_("view-action", "Scroll to top border"), NULL, NULL, + GIMP_ACTION_SELECT_FIRST, FALSE, + NULL }, + + { "view-scroll-bottom-border", NULL, + NC_("view-action", "Scroll to bottom border"), NULL, NULL, + GIMP_ACTION_SELECT_LAST, FALSE, + NULL }, + + { "view-scroll-up", NULL, + NC_("view-action", "Scroll up"), NULL, NULL, + GIMP_ACTION_SELECT_PREVIOUS, FALSE, + NULL }, + + { "view-scroll-down", NULL, + NC_("view-action", "Scroll down"), NULL, NULL, + GIMP_ACTION_SELECT_NEXT, FALSE, + NULL }, + + { "view-scroll-page-up", NULL, + NC_("view-action", "Scroll page up"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_PREVIOUS, FALSE, + NULL }, + + { "view-scroll-page-down", NULL, + NC_("view-action", "Scroll page down"), NULL, NULL, + GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, + NULL } +}; + + +void +view_actions_setup (GimpActionGroup *group) +{ + GimpAction *action; + + gimp_action_group_add_actions (group, "view-action", + view_actions, + G_N_ELEMENTS (view_actions)); + + gimp_action_group_add_toggle_actions (group, "view-action", + view_toggle_actions, + G_N_ELEMENTS (view_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "view-zoom-action", + view_zoom_actions, + G_N_ELEMENTS (view_zoom_actions), + view_zoom_cmd_callback); + + gimp_action_group_add_radio_actions (group, "view-zoom-action", + view_zoom_explicit_actions, + G_N_ELEMENTS (view_zoom_explicit_actions), + NULL, + 10000, + view_zoom_explicit_cmd_callback); + + gimp_action_group_add_toggle_actions (group, "view-action", + view_flip_actions, + G_N_ELEMENTS (view_flip_actions)); + + gimp_action_group_add_enum_actions (group, "view-action", + view_rotate_absolute_actions, + G_N_ELEMENTS (view_rotate_absolute_actions), + view_rotate_absolute_cmd_callback); + + gimp_action_group_add_enum_actions (group, "view-action", + view_rotate_relative_actions, + G_N_ELEMENTS (view_rotate_relative_actions), + view_rotate_relative_cmd_callback); + + gimp_action_group_add_radio_actions (group, "view-action", + view_display_intent_actions, + G_N_ELEMENTS (view_display_intent_actions), + NULL, + GIMP_COLOR_MANAGEMENT_DISPLAY, + view_display_intent_cmd_callback); + + gimp_action_group_add_radio_actions (group, "view-action", + view_softproof_intent_actions, + G_N_ELEMENTS (view_softproof_intent_actions), + NULL, + GIMP_COLOR_MANAGEMENT_DISPLAY, + view_softproof_intent_cmd_callback); + + gimp_action_group_add_enum_actions (group, "view-padding-color", + view_padding_color_actions, + G_N_ELEMENTS (view_padding_color_actions), + view_padding_color_cmd_callback); + + gimp_action_group_add_toggle_actions (group, "view-padding-color", + view_padding_color_toggle_actions, + G_N_ELEMENTS (view_padding_color_toggle_actions)); + + gimp_action_group_add_enum_actions (group, "view-action", + view_scroll_horizontal_actions, + G_N_ELEMENTS (view_scroll_horizontal_actions), + view_scroll_horizontal_cmd_callback); + + gimp_action_group_add_enum_actions (group, "view-action", + view_scroll_vertical_actions, + G_N_ELEMENTS (view_scroll_vertical_actions), + view_scroll_vertical_cmd_callback); + + /* connect "activate" of view-zoom-other manually so it can be + * selected even if it's the active item of the radio group + */ + action = gimp_action_group_get_action (group, "view-zoom-other"); + + g_signal_connect (action, "activate", + G_CALLBACK (view_zoom_other_cmd_callback), + group->user_data); + + g_signal_connect_object (group->gimp->config, "notify::check-type", + G_CALLBACK (view_actions_check_type_notify), + group, 0); + view_actions_check_type_notify (GIMP_DISPLAY_CONFIG (group->gimp->config), + NULL, group); + + if (GIMP_IS_IMAGE_WINDOW (group->user_data) || + GIMP_IS_GIMP (group->user_data)) + { + /* add window actions only if the context of the group is + * the display itself or the global popup (not if the context + * is a dock) + * (see dock-actions.c) + */ + window_actions_setup (group, GIMP_HELP_VIEW_CHANGE_SCREEN); + } +} + +void +view_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpDisplay *display = action_data_get_display (data); + GimpImage *image = NULL; + GimpDisplayShell *shell = NULL; + GimpDisplayOptions *options = NULL; + GimpColorConfig *color_config = NULL; + gchar *label = NULL; + gboolean fullscreen = FALSE; + gboolean revert_enabled = FALSE; /* able to revert zoom? */ + gboolean flip_horizontally = FALSE; + gboolean flip_vertically = FALSE; + gboolean cm = FALSE; + gboolean sp = FALSE; + gboolean d_bpc = FALSE; + gboolean s_bpc = FALSE; + gboolean gammut = FALSE; + + if (display) + { + GimpImageWindow *window; + const gchar *action = NULL; + + image = gimp_display_get_image (display); + shell = gimp_display_get_shell (display); + window = gimp_display_shell_get_window (shell); + + if (window) + fullscreen = gimp_image_window_get_fullscreen (window); + + options = (image ? + (fullscreen ? shell->fullscreen_options : shell->options) : + shell->no_image_options); + + revert_enabled = gimp_display_shell_scale_can_revert (shell); + + flip_horizontally = shell->flip_horizontally; + flip_vertically = shell->flip_vertically; + + color_config = gimp_display_shell_get_color_config (shell); + + switch (gimp_color_config_get_mode (color_config)) + { + case GIMP_COLOR_MANAGEMENT_OFF: + break; + + case GIMP_COLOR_MANAGEMENT_DISPLAY: + cm = (image != NULL); + break; + + case GIMP_COLOR_MANAGEMENT_SOFTPROOF: + cm = (image != NULL); + sp = (image != NULL); + break; + } + + switch (gimp_color_config_get_display_intent (color_config)) + { + case GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL: + action = "view-display-intent-perceptual"; + break; + + case GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC: + action = "view-display-intent-relative-colorimetric"; + break; + + case GIMP_COLOR_RENDERING_INTENT_SATURATION: + action = "view-display-intent-saturation"; + break; + + case GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: + action = "view-display-intent-absolute-colorimetric"; + break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + + switch (gimp_color_config_get_simulation_intent (color_config)) + { + case GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL: + action = "view-softproof-intent-perceptual"; + break; + + case GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC: + action = "view-softproof-intent-relative-colorimetric"; + break; + + case GIMP_COLOR_RENDERING_INTENT_SATURATION: + action = "view-softproof-intent-saturation"; + break; + + case GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: + action = "view-softproof-intent-absolute-colorimetric"; + break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + + d_bpc = gimp_color_config_get_display_bpc (color_config); + s_bpc = gimp_color_config_get_simulation_bpc (color_config); + gammut = gimp_color_config_get_simulation_gamut_check (color_config); + } + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) +#define SET_SENSITIVE(action,condition) \ + gimp_action_group_set_action_sensitive (group, action, (condition) != 0) +#define SET_COLOR(action,color) \ + gimp_action_group_set_action_color (group, action, color, FALSE) + + SET_SENSITIVE ("view-new", image); + SET_SENSITIVE ("view-close", image); + + SET_SENSITIVE ("view-show-all", image); + SET_ACTIVE ("view-show-all", display && shell->show_all); + + SET_SENSITIVE ("view-dot-for-dot", image); + SET_ACTIVE ("view-dot-for-dot", display && shell->dot_for_dot); + + SET_SENSITIVE ("view-scroll-center", image); + + SET_SENSITIVE ("view-zoom-revert", revert_enabled); + if (revert_enabled) + { + label = g_strdup_printf (_("Re_vert Zoom (%d%%)"), + ROUND (shell->last_scale * 100)); + gimp_action_group_set_action_label (group, "view-zoom-revert", label); + g_free (label); + } + else + { + gimp_action_group_set_action_label (group, "view-zoom-revert", + _("Re_vert Zoom")); + } + + SET_SENSITIVE ("view-zoom", image); + SET_SENSITIVE ("view-zoom-minimum", image); + SET_SENSITIVE ("view-zoom-maximum", image); + SET_SENSITIVE ("view-zoom-in", image); + SET_SENSITIVE ("view-zoom-in-accel", image); + SET_SENSITIVE ("view-zoom-in-skip", image); + SET_SENSITIVE ("view-zoom-out", image); + SET_SENSITIVE ("view-zoom-out-accel", image); + SET_SENSITIVE ("view-zoom-out-skip", image); + + SET_SENSITIVE ("view-zoom-fit-in", image); + SET_SENSITIVE ("view-zoom-fill", image); + SET_SENSITIVE ("view-zoom-selection", image); + SET_SENSITIVE ("view-zoom-revert", image); + + SET_SENSITIVE ("view-zoom-16-1", image); + SET_SENSITIVE ("view-zoom-16-1-accel", image); + SET_SENSITIVE ("view-zoom-8-1", image); + SET_SENSITIVE ("view-zoom-8-1-accel", image); + SET_SENSITIVE ("view-zoom-4-1", image); + SET_SENSITIVE ("view-zoom-4-1-accel", image); + SET_SENSITIVE ("view-zoom-2-1", image); + SET_SENSITIVE ("view-zoom-2-1-accel", image); + SET_SENSITIVE ("view-zoom-1-1", image); + SET_SENSITIVE ("view-zoom-1-1-accel", image); + SET_SENSITIVE ("view-zoom-1-2", image); + SET_SENSITIVE ("view-zoom-1-4", image); + SET_SENSITIVE ("view-zoom-1-8", image); + SET_SENSITIVE ("view-zoom-1-16", image); + SET_SENSITIVE ("view-zoom-other", image); + + SET_SENSITIVE ("view-flip-horizontally", image); + SET_ACTIVE ("view-flip-horizontally", flip_horizontally); + + SET_SENSITIVE ("view-flip-vertically", image); + SET_ACTIVE ("view-flip-vertically", flip_vertically); + + SET_SENSITIVE ("view-rotate-reset", image); + SET_SENSITIVE ("view-rotate-15", image); + SET_SENSITIVE ("view-rotate-345", image); + SET_SENSITIVE ("view-rotate-90", image); + SET_SENSITIVE ("view-rotate-180", image); + SET_SENSITIVE ("view-rotate-270", image); + SET_SENSITIVE ("view-rotate-other", image); + + if (image) + { + view_actions_set_zoom (group, shell); + view_actions_set_rotate (group, shell); + } + + SET_SENSITIVE ("view-navigation-window", image); + SET_SENSITIVE ("view-display-filters", image); + + SET_SENSITIVE ("view-color-management-enable", image); + SET_ACTIVE ("view-color-management-enable", cm); + SET_SENSITIVE ("view-color-management-softproof", image); + SET_ACTIVE ("view-color-management-softproof", sp); + SET_SENSITIVE ("view-display-intent-perceptual", cm); + SET_SENSITIVE ("view-display-intent-relative-colorimetric", cm); + SET_SENSITIVE ("view-display-intent-saturation", cm); + SET_SENSITIVE ("view-display-intent-absolute-colorimetric", cm); + SET_SENSITIVE ("view-display-black-point-compensation", cm); + SET_ACTIVE ("view-display-black-point-compensation", d_bpc); + SET_SENSITIVE ("view-softproof-profile", sp); + SET_SENSITIVE ("view-softproof-intent-perceptual", sp); + SET_SENSITIVE ("view-softproof-intent-relative-colorimetric", sp); + SET_SENSITIVE ("view-softproof-intent-saturation", sp); + SET_SENSITIVE ("view-softproof-intent-absolute-colorimetric", sp); + SET_SENSITIVE ("view-softproof-black-point-compensation", sp); + SET_ACTIVE ("view-softproof-black-point-compensation", s_bpc); + SET_SENSITIVE ("view-softproof-gamut-check", sp); + SET_ACTIVE ("view-softproof-gamut-check", gammut); + SET_SENSITIVE ("view-color-management-reset", image); + + SET_SENSITIVE ("view-show-selection", image); + SET_ACTIVE ("view-show-selection", display && options->show_selection); + SET_SENSITIVE ("view-show-layer-boundary", image); + SET_ACTIVE ("view-show-layer-boundary", display && options->show_layer_boundary); + SET_SENSITIVE ("view-show-canvas-boundary", image && shell->show_all); + SET_ACTIVE ("view-show-canvas-boundary", display && options->show_canvas_boundary); + SET_SENSITIVE ("view-show-guides", image); + SET_ACTIVE ("view-show-guides", display && options->show_guides); + SET_SENSITIVE ("view-show-grid", image); + SET_ACTIVE ("view-show-grid", display && options->show_grid); + SET_SENSITIVE ("view-show-sample-points", image); + SET_ACTIVE ("view-show-sample-points", display && options->show_sample_points); + + SET_SENSITIVE ("view-snap-to-guides", image); + SET_ACTIVE ("view-snap-to-guides", display && options->snap_to_guides); + SET_SENSITIVE ("view-snap-to-grid", image); + SET_ACTIVE ("view-snap-to-grid", display && options->snap_to_grid); + SET_SENSITIVE ("view-snap-to-canvas", image); + SET_ACTIVE ("view-snap-to-canvas", display && options->snap_to_canvas); + SET_SENSITIVE ("view-snap-to-vectors", image); + SET_ACTIVE ("view-snap-to-vectors", display && options->snap_to_path); + + SET_SENSITIVE ("view-padding-color-theme", image); + SET_SENSITIVE ("view-padding-color-light-check", image); + SET_SENSITIVE ("view-padding-color-dark-check", image); + SET_SENSITIVE ("view-padding-color-custom", image); + SET_SENSITIVE ("view-padding-color-prefs", image); + + if (display) + { + SET_COLOR ("view-padding-color-menu", &options->padding_color); + + if (shell->canvas) + { + GtkStyle *style = gtk_widget_get_style (shell->canvas); + GimpRGB color; + + gtk_widget_ensure_style (shell->canvas); + gimp_rgb_set_gdk_color (&color, style->bg + GTK_STATE_NORMAL); + gimp_rgb_set_alpha (&color, GIMP_OPACITY_OPAQUE); + + SET_COLOR ("view-padding-color-theme", &color); + } + } + + SET_SENSITIVE ("view-padding-color-in-show-all", image); + SET_ACTIVE ("view-padding-color-in-show-all", display && options->padding_in_show_all); + + SET_SENSITIVE ("view-show-menubar", image); + SET_ACTIVE ("view-show-menubar", display && options->show_menubar); + SET_SENSITIVE ("view-show-rulers", image); + SET_ACTIVE ("view-show-rulers", display && options->show_rulers); + SET_SENSITIVE ("view-show-scrollbars", image); + SET_ACTIVE ("view-show-scrollbars", display && options->show_scrollbars); + SET_SENSITIVE ("view-show-statusbar", image); + SET_ACTIVE ("view-show-statusbar", display && options->show_statusbar); + + SET_SENSITIVE ("view-shrink-wrap", image); + SET_ACTIVE ("view-fullscreen", display && fullscreen); + + if (GIMP_IS_IMAGE_WINDOW (group->user_data) || + GIMP_IS_GIMP (group->user_data)) + { + GtkWidget *window = NULL; + + if (shell) + window = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + /* see view_actions_setup() */ + if (GTK_IS_WINDOW (window)) + window_actions_update (group, window); + } + +#undef SET_ACTIVE +#undef SET_SENSITIVE +#undef SET_COLOR +} + + +/* private functions */ + +static void +view_actions_set_zoom (GimpActionGroup *group, + GimpDisplayShell *shell) +{ + const gchar *action = NULL; + gchar *str; + gchar *label; + guint scale; + + g_object_get (shell->zoom, + "percentage", &str, + NULL); + + scale = ROUND (gimp_zoom_model_get_factor (shell->zoom) * 1000); + + switch (scale) + { + case 16000: action = "view-zoom-16-1"; break; + case 8000: action = "view-zoom-8-1"; break; + case 4000: action = "view-zoom-4-1"; break; + case 2000: action = "view-zoom-2-1"; break; + case 1000: action = "view-zoom-1-1"; break; + case 500: action = "view-zoom-1-2"; break; + case 250: action = "view-zoom-1-4"; break; + case 125: action = "view-zoom-1-8"; break; + case 63: + case 62: action = "view-zoom-1-16"; break; + } + + if (! action) + { + action = "view-zoom-other"; + + label = g_strdup_printf (_("Othe_r (%s)..."), str); + gimp_action_group_set_action_label (group, action, label); + g_free (label); + + shell->other_scale = gimp_zoom_model_get_factor (shell->zoom); + } + + gimp_action_group_set_action_active (group, action, TRUE); + + label = g_strdup_printf (_("_Zoom (%s)"), str); + gimp_action_group_set_action_label (group, "view-zoom-menu", label); + g_free (label); + + /* flag as dirty */ + shell->other_scale = - fabs (shell->other_scale); + + g_free (str); +} + +static void +view_actions_set_rotate (GimpActionGroup *group, + GimpDisplayShell *shell) +{ + const gchar *flip; + gchar *label; + + if (shell->flip_horizontally && + shell->flip_vertically) + { + /* please preserve the trailing space */ + /* H: Horizontal, V: Vertical */ + flip = _("(H+V) "); + } + else if (shell->flip_horizontally) + { + /* please preserve the trailing space */ + /* H: Horizontal */ + flip = _("(H) "); + } + else if (shell->flip_vertically) + { + /* please preserve the trailing space */ + /* V: Vertical */ + flip = _("(V) "); + } + else + { + flip = ""; + } + + label = g_strdup_printf (_("_Flip %s& Rotate (%d°)"), + flip, (gint) shell->rotate_angle); + gimp_action_group_set_action_label (group, "view-rotate-menu", label); + g_free (label); +} + +static void +view_actions_check_type_notify (GimpDisplayConfig *config, + GParamSpec *pspec, + GimpActionGroup *group) +{ + gimp_action_group_set_action_color (group, "view-padding-color-light-check", + gimp_render_light_check_color (), + FALSE); + gimp_action_group_set_action_color (group, "view-padding-color-dark-check", + gimp_render_dark_check_color (), + FALSE); +} diff --git a/app/actions/view-actions.h b/app/actions/view-actions.h new file mode 100644 index 0000000..97aad0a --- /dev/null +++ b/app/actions/view-actions.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 . + */ + +#ifndef __VIEW_ACTIONS_H__ +#define __VIEW_ACTIONS_H__ + + +void view_actions_setup (GimpActionGroup *group); +void view_actions_update (GimpActionGroup *group, + gpointer data); + + +#endif /* __VIEW_ACTIONS_H__ */ diff --git a/app/actions/view-commands.c b/app/actions/view-commands.c new file mode 100644 index 0000000..2c2629c --- /dev/null +++ b/app/actions/view-commands.c @@ -0,0 +1,1281 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdisplayoptions.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpgrouplayer.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpcolordialog.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-appearance.h" +#include "display/gimpdisplayshell-filter-dialog.h" +#include "display/gimpdisplayshell-rotate.h" +#include "display/gimpdisplayshell-rotate-dialog.h" +#include "display/gimpdisplayshell-scale.h" +#include "display/gimpdisplayshell-scale-dialog.h" +#include "display/gimpdisplayshell-scroll.h" +#include "display/gimpdisplayshell-close.h" +#include "display/gimpimagewindow.h" + +#include "dialogs/color-profile-dialog.h" +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "view-commands.h" + +#include "gimp-intl.h" + + +#define SET_ACTIVE(manager,action_name,active) \ + { GimpActionGroup *group = \ + gimp_ui_manager_get_action_group (manager, "view"); \ + gimp_action_group_set_action_active (group, action_name, active); } + +#define IS_ACTIVE_DISPLAY(display) \ + ((display) == \ + gimp_context_get_display (gimp_get_user_context ((display)->gimp))) + + +/* local function prototypes */ + +static void view_softproof_profile_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); +static void view_padding_color_dialog_update (GimpColorDialog *dialog, + const GimpRGB *color, + GimpColorDialogState state, + GimpDisplayShell *shell); + + +/* public functions */ + +void +view_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + gimp_create_display (display->gimp, + gimp_display_get_image (display), + shell->unit, gimp_zoom_model_get_factor (shell->zoom), + G_OBJECT (gtk_widget_get_screen (GTK_WIDGET (shell))), + gimp_widget_get_monitor (GTK_WIDGET (shell))); +} + +void +view_close_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + GimpImage *image; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + image = gimp_display_get_image (display); + + /* Check for the image so we don't close the last display. */ + if (image) + gimp_display_shell_close (shell, FALSE); +} + +void +view_scroll_center_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + return_if_no_display (display, data); + + gimp_display_shell_scroll_center_image (gimp_display_get_shell (display), + TRUE, TRUE); +} + +void +view_zoom_fit_in_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + return_if_no_display (display, data); + + gimp_display_shell_scale_fit_in (gimp_display_get_shell (display)); +} + +void +view_zoom_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + return_if_no_display (display, data); + + gimp_display_shell_scale_fill (gimp_display_get_shell (display)); +} + +void +view_zoom_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImage *image; + gint x, y, width, height; + return_if_no_display (display, data); + return_if_no_image (image, data); + + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height); + + gimp_display_shell_scale_to_rectangle (gimp_display_get_shell (display), + GIMP_ZOOM_IN, + x, y, width, height, + FALSE); +} + +void +view_zoom_revert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + return_if_no_display (display, data); + + gimp_display_shell_scale_revert (gimp_display_get_shell (display)); +} + +void +view_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpActionSelectType select_type; + return_if_no_shell (shell, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + switch (select_type) + { + case GIMP_ACTION_SELECT_FIRST: + gimp_display_shell_scale (shell, + GIMP_ZOOM_OUT_MAX, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + case GIMP_ACTION_SELECT_LAST: + gimp_display_shell_scale (shell, + GIMP_ZOOM_IN_MAX, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + case GIMP_ACTION_SELECT_PREVIOUS: + gimp_display_shell_scale (shell, + GIMP_ZOOM_OUT, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + case GIMP_ACTION_SELECT_NEXT: + gimp_display_shell_scale (shell, + GIMP_ZOOM_IN, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + case GIMP_ACTION_SELECT_SKIP_PREVIOUS: + gimp_display_shell_scale (shell, + GIMP_ZOOM_OUT_MORE, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + case GIMP_ACTION_SELECT_SKIP_NEXT: + gimp_display_shell_scale (shell, + GIMP_ZOOM_IN_MORE, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + + default: + { + gdouble scale = gimp_zoom_model_get_factor (shell->zoom); + + scale = action_select_value (select_type, + scale, + 0.0, 512.0, 1.0, + 1.0 / 8.0, 1.0, 16.0, 0.0, + FALSE); + + /* min = 1.0 / 256, max = 256.0 */ + /* scale = min * (max / min)**(i/n), i = 0..n */ + scale = pow (65536.0, scale / 512.0) / 256.0; + + gimp_display_shell_scale (shell, + GIMP_ZOOM_TO, + scale, + GIMP_ZOOM_FOCUS_BEST_GUESS); + break; + } + } +} + +void +view_zoom_explicit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gint factor; + return_if_no_shell (shell, data); + + factor = g_variant_get_int32 (value); + + if (factor != 0 /* not Other... */) + { + if (fabs (factor - gimp_zoom_model_get_factor (shell->zoom)) > 0.0001) + gimp_display_shell_scale (shell, + GIMP_ZOOM_TO, + (gdouble) factor / 10000, + GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS); + } +} + +/* not a GimpActionCallback */ +void +view_zoom_other_cmd_callback (GimpAction *action, + gpointer data) +{ + GimpDisplayShell *shell; + return_if_no_shell (shell, data); + + /* check if we are activated by the user or from + * view_actions_set_zoom(), also this is really a GtkToggleAction + * NOT a GimpToggleAction + */ + if (gtk_toggle_action_get_active ((GtkToggleAction *) action) && + shell->other_scale != gimp_zoom_model_get_factor (shell->zoom)) + { + gimp_display_shell_scale_dialog (shell); + } +} + +void +view_show_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + active = g_variant_get_boolean (value); + + if (active != shell->show_all) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + gimp_display_shell_set_show_all (shell, active); + + if (window) + SET_ACTIVE (gimp_image_window_get_ui_manager (window), + "view-show-all", shell->show_all); + + if (IS_ACTIVE_DISPLAY (display)) + SET_ACTIVE (shell->popup_manager, "view-show-all", + shell->show_all); + } +} + +void +view_dot_for_dot_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + active = g_variant_get_boolean (value); + + if (active != shell->dot_for_dot) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + gimp_display_shell_scale_set_dot_for_dot (shell, active); + + if (window) + SET_ACTIVE (gimp_image_window_get_ui_manager (window), + "view-dot-for-dot", shell->dot_for_dot); + + if (IS_ACTIVE_DISPLAY (display)) + SET_ACTIVE (shell->popup_manager, "view-dot-for-dot", + shell->dot_for_dot); + } +} + +void +view_flip_horizontally_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + active = g_variant_get_boolean (value); + + if (active != shell->flip_horizontally) + { + gimp_display_shell_flip (shell, active, shell->flip_vertically); + } +} + +void +view_flip_vertically_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + active = g_variant_get_boolean (value); + + if (active != shell->flip_vertically) + { + gimp_display_shell_flip (shell, shell->flip_horizontally, active); + } +} + +void +view_rotate_absolute_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + GimpActionSelectType select_type; + gdouble angle = 0.0; + return_if_no_display (display, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + shell = gimp_display_get_shell (display); + + angle = action_select_value (select_type, + 0.0, + -180.0, 180.0, 0.0, + 1.0, 15.0, 90.0, 0.0, + TRUE); + + gimp_display_shell_rotate_to (shell, angle); + + if (select_type == GIMP_ACTION_SELECT_SET_TO_DEFAULT) + gimp_display_shell_flip (shell, FALSE, FALSE); +} + +void +view_rotate_relative_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + GimpActionSelectType select_type; + gdouble delta = 0.0; + return_if_no_display (display, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + shell = gimp_display_get_shell (display); + + delta = action_select_value (select_type, + 0.0, + -180.0, 180.0, 0.0, + 1.0, 15.0, 90.0, 0.0, + TRUE); + + gimp_display_shell_rotate (shell, delta); +} + +void +view_rotate_other_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + + gimp_display_shell_rotate_dialog (shell); +} + +void +view_scroll_horizontal_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GtkAdjustment *adj; + GimpActionSelectType select_type; + gdouble offset; + return_if_no_shell (shell, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + adj = shell->hsbdata; + + offset = action_select_value (select_type, + gtk_adjustment_get_value (adj), + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj), + gtk_adjustment_get_lower (adj), + 1, + gtk_adjustment_get_step_increment (adj), + gtk_adjustment_get_page_increment (adj), + 0, + FALSE); + + gtk_adjustment_set_value (shell->hsbdata, offset); +} + +void +view_scroll_vertical_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GtkAdjustment *adj; + GimpActionSelectType select_type; + gdouble offset; + return_if_no_shell (shell, data); + + select_type = (GimpActionSelectType) g_variant_get_int32 (value); + + adj = shell->vsbdata; + + offset = action_select_value (select_type, + gtk_adjustment_get_value (adj), + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj), + gtk_adjustment_get_lower (adj), + 1, + gtk_adjustment_get_step_increment (adj), + gtk_adjustment_get_page_increment (adj), + 0, + FALSE); + + gtk_adjustment_set_value (shell->vsbdata, offset); +} + +void +view_navigation_window_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpDisplayShell *shell; + return_if_no_gimp (gimp, data); + return_if_no_shell (shell, data); + + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)), + gimp, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (GTK_WIDGET (shell)), + gimp_widget_get_monitor (GTK_WIDGET (shell)), + "gimp-navigation-view"); +} + +void +view_display_filters_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GtkWidget *dialog; + return_if_no_shell (shell, data); + +#define FILTERS_DIALOG_KEY "gimp-display-filters-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (shell), FILTERS_DIALOG_KEY); + + if (! dialog) + { + dialog = gimp_display_shell_filter_dialog_new (shell); + + dialogs_attach_dialog (G_OBJECT (shell), FILTERS_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +view_color_management_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *global_config; + GimpColorConfig *shell_config; + return_if_no_shell (shell, data); + + global_config = GIMP_CORE_CONFIG (shell->display->config)->color_management; + shell_config = gimp_display_shell_get_color_config (shell); + + gimp_config_copy (GIMP_CONFIG (global_config), + GIMP_CONFIG (shell_config), + 0); + shell->color_config_set = FALSE; +} + +void +view_color_management_enable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + GimpColorManagementMode mode; + gboolean active; + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + + active = g_variant_get_boolean (value); + + mode = gimp_color_config_get_mode (color_config); + + if (active) + { + if (mode != GIMP_COLOR_MANAGEMENT_SOFTPROOF) + mode = GIMP_COLOR_MANAGEMENT_DISPLAY; + } + else + { + mode = GIMP_COLOR_MANAGEMENT_OFF; + } + + if (mode != gimp_color_config_get_mode (color_config)) + { + g_object_set (color_config, + "mode", mode, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_color_management_softproof_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + GimpColorManagementMode mode; + gboolean active; + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + + active = g_variant_get_boolean (value); + + mode = gimp_color_config_get_mode (color_config); + + if (active) + { + mode = GIMP_COLOR_MANAGEMENT_SOFTPROOF; + } + else + { + if (mode != GIMP_COLOR_MANAGEMENT_OFF) + mode = GIMP_COLOR_MANAGEMENT_DISPLAY; + } + + if (mode != gimp_color_config_get_mode (color_config)) + { + g_object_set (color_config, + "mode", mode, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_display_intent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + GimpColorRenderingIntent intent; + return_if_no_shell (shell, data); + + intent = (GimpColorRenderingIntent) g_variant_get_int32 (value); + + color_config = gimp_display_shell_get_color_config (shell); + + if (intent != gimp_color_config_get_display_intent (color_config)) + { + g_object_set (color_config, + "display-rendering-intent", intent, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_display_bpc_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + gboolean active; + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + + active = g_variant_get_boolean (value); + + if (active != gimp_color_config_get_display_bpc (color_config)) + { + g_object_set (color_config, + "display-use-black-point-compensation", active, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_softproof_profile_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpImage *image; + GimpDisplayShell *shell; + GimpColorConfig *color_config; + GtkWidget *dialog; + return_if_no_image (image, data); + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + +#define SOFTPROOF_PROFILE_DIALOG_KEY "gimp-softproof-profile-dialog" + + dialog = dialogs_get_dialog (G_OBJECT (shell), SOFTPROOF_PROFILE_DIALOG_KEY); + + if (! dialog) + { + GimpColorProfile *current_profile; + + current_profile = gimp_color_config_get_simulation_color_profile (color_config, + NULL); + + dialog = color_profile_dialog_new (COLOR_PROFILE_DIALOG_SELECT_SOFTPROOF_PROFILE, + image, + action_data_get_context (data), + GTK_WIDGET (shell), + current_profile, + NULL, + 0, 0, + view_softproof_profile_callback, + shell); + + dialogs_attach_dialog (G_OBJECT (shell), + SOFTPROOF_PROFILE_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +void +view_softproof_intent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + GimpColorRenderingIntent intent; + return_if_no_shell (shell, data); + + intent = (GimpColorRenderingIntent) g_variant_get_int32 (value); + + color_config = gimp_display_shell_get_color_config (shell); + + if (intent != gimp_color_config_get_simulation_intent (color_config)) + { + g_object_set (color_config, + "simulation-rendering-intent", intent, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_softproof_bpc_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + gboolean active; + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + + active = g_variant_get_boolean (value); + + if (active != gimp_color_config_get_simulation_bpc (color_config)) + { + g_object_set (color_config, + "simulation-use-black-point-compensation", active, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_softproof_gamut_check_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + GimpColorConfig *color_config; + gboolean active; + return_if_no_shell (shell, data); + + color_config = gimp_display_shell_get_color_config (shell); + + active = g_variant_get_boolean (value); + + if (active != gimp_color_config_get_simulation_gamut_check (color_config)) + { + g_object_set (color_config, + "simulation-gamut-check", active, + NULL); + shell->color_config_set = TRUE; + } +} + +void +view_toggle_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_selection (shell)) + { + gimp_display_shell_set_show_selection (shell, active); + } +} + +void +view_toggle_layer_boundary_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_layer (shell)) + { + gimp_display_shell_set_show_layer (shell, active); + } +} + +void +view_toggle_canvas_boundary_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_canvas (shell)) + { + gimp_display_shell_set_show_canvas (shell, active); + } +} + +void +view_toggle_menubar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_menubar (shell)) + { + gimp_display_shell_set_show_menubar (shell, active); + } +} + +void +view_toggle_rulers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_rulers (shell)) + { + gimp_display_shell_set_show_rulers (shell, active); + } +} + +void +view_toggle_scrollbars_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_scrollbars (shell)) + { + gimp_display_shell_set_show_scrollbars (shell, active); + } +} + +void +view_toggle_statusbar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_statusbar (shell)) + { + gimp_display_shell_set_show_statusbar (shell, active); + } +} + +void +view_toggle_guides_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_guides (shell)) + { + gimp_display_shell_set_show_guides (shell, active); + } +} + +void +view_toggle_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_grid (shell)) + { + gimp_display_shell_set_show_grid (shell, active); + } +} + +void +view_toggle_sample_points_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_show_sample_points (shell)) + { + gimp_display_shell_set_show_sample_points (shell, active); + } +} + +void +view_snap_to_guides_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_snap_to_guides (shell)) + { + gimp_display_shell_set_snap_to_guides (shell, active); + } +} + +void +view_snap_to_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_snap_to_grid (shell)) + { + gimp_display_shell_set_snap_to_grid (shell, active); + } +} + +void +view_snap_to_canvas_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_snap_to_canvas (shell)) + { + gimp_display_shell_set_snap_to_canvas (shell, active); + } +} + +void +view_snap_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_snap_to_vectors (shell)) + { + gimp_display_shell_set_snap_to_vectors (shell, active); + } +} + +void +view_padding_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpImageWindow *window; + GimpDisplayShell *shell; + GimpDisplayOptions *options; + GimpCanvasPaddingMode padding_mode; + gboolean fullscreen; + return_if_no_display (display, data); + + padding_mode = (GimpCanvasPaddingMode) g_variant_get_int32 (value); + + shell = gimp_display_get_shell (display); + window = gimp_display_shell_get_window (shell); + + if (window) + fullscreen = gimp_image_window_get_fullscreen (window); + else + fullscreen = FALSE; + + if (fullscreen) + options = shell->fullscreen_options; + else + options = shell->options; + +#define PADDING_COLOR_DIALOG_KEY "gimp-padding-color-dialog" + + switch (padding_mode) + { + case GIMP_CANVAS_PADDING_MODE_DEFAULT: + case GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK: + case GIMP_CANVAS_PADDING_MODE_DARK_CHECK: + dialogs_destroy_dialog (G_OBJECT (shell), PADDING_COLOR_DIALOG_KEY); + + options->padding_mode_set = TRUE; + + gimp_display_shell_set_padding (shell, padding_mode, + &options->padding_color); + break; + + case GIMP_CANVAS_PADDING_MODE_CUSTOM: + { + GtkWidget *dialog; + + dialog = dialogs_get_dialog (G_OBJECT (shell), PADDING_COLOR_DIALOG_KEY); + + if (! dialog) + { + GimpImage *image = gimp_display_get_image (display); + GimpDisplayShell *shell = gimp_display_get_shell (display); + + dialog = + gimp_color_dialog_new (GIMP_VIEWABLE (image), + action_data_get_context (data), + _("Set Canvas Padding Color"), + GIMP_ICON_FONT, + _("Set Custom Canvas Padding Color"), + GTK_WIDGET (shell), + NULL, NULL, + &options->padding_color, + FALSE, FALSE); + + g_signal_connect (dialog, "update", + G_CALLBACK (view_padding_color_dialog_update), + shell); + + dialogs_attach_dialog (G_OBJECT (shell), + PADDING_COLOR_DIALOG_KEY, dialog); + } + + gtk_window_present (GTK_WINDOW (dialog)); + } + break; + + case GIMP_CANVAS_PADDING_MODE_RESET: + dialogs_destroy_dialog (G_OBJECT (shell), PADDING_COLOR_DIALOG_KEY); + + { + GimpDisplayOptions *default_options; + + options->padding_mode_set = FALSE; + + if (fullscreen) + default_options = display->config->default_fullscreen_view; + else + default_options = display->config->default_view; + + gimp_display_shell_set_padding (shell, + default_options->padding_mode, + &default_options->padding_color); + gimp_display_shell_set_padding_in_show_all (shell, + default_options->padding_in_show_all); + } + break; + } +} + +void +view_padding_color_in_show_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + gboolean active; + return_if_no_shell (shell, data); + + active = g_variant_get_boolean (value); + + if (active != gimp_display_shell_get_padding_in_show_all (shell)) + { + gimp_display_shell_set_padding_in_show_all (shell, active); + } +} + +void +view_shrink_wrap_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplayShell *shell; + return_if_no_shell (shell, data); + + gimp_display_shell_scale_shrink_wrap (shell, FALSE); +} + +void +view_fullscreen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + GimpDisplayShell *shell; + GimpImageWindow *window; + return_if_no_display (display, data); + + shell = gimp_display_get_shell (display); + window = gimp_display_shell_get_window (shell); + + if (window) + { + gboolean active = g_variant_get_boolean (value); + + gimp_image_window_set_fullscreen (window, active); + } +} + + +/* private functions */ + +static void +view_softproof_profile_callback (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data) +{ + GimpDisplayShell *shell = user_data; + GimpColorConfig *color_config; + gchar *path = NULL; + + color_config = gimp_display_shell_get_color_config (shell); + + if (new_file) + path = g_file_get_path (new_file); + + g_object_set (color_config, + "printer-profile", path, + NULL); + shell->color_config_set = TRUE; + + gtk_widget_destroy (dialog); +} + +static void +view_padding_color_dialog_update (GimpColorDialog *dialog, + const GimpRGB *color, + GimpColorDialogState state, + GimpDisplayShell *shell) +{ + GimpImageWindow *window; + GimpDisplayOptions *options; + gboolean fullscreen; + + window = gimp_display_shell_get_window (shell); + + if (window) + fullscreen = gimp_image_window_get_fullscreen (window); + else + fullscreen = FALSE; + + if (fullscreen) + options = shell->fullscreen_options; + else + options = shell->options; + + switch (state) + { + case GIMP_COLOR_DIALOG_OK: + options->padding_mode_set = TRUE; + + gimp_display_shell_set_padding (shell, GIMP_CANVAS_PADDING_MODE_CUSTOM, + color); + /* fallthru */ + + case GIMP_COLOR_DIALOG_CANCEL: + gtk_widget_destroy (GTK_WIDGET (dialog)); + break; + + default: + break; + } +} diff --git a/app/actions/view-commands.h b/app/actions/view-commands.h new file mode 100644 index 0000000..17b15b2 --- /dev/null +++ b/app/actions/view-commands.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 . + */ + +#ifndef __VIEW_COMMANDS_H__ +#define __VIEW_COMMANDS_H__ + + +void view_new_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_close_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_scroll_center_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_zoom_fit_in_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_zoom_fill_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_zoom_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_zoom_revert_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_zoom_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_zoom_explicit_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +/* not a GimpActionCallback */ +void view_zoom_other_cmd_callback (GimpAction *action, + gpointer data); + +void view_show_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_dot_for_dot_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_scroll_horizontal_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_scroll_vertical_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_flip_horizontally_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_flip_vertically_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_rotate_absolute_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_rotate_relative_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_rotate_other_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_navigation_window_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_display_filters_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_color_management_reset_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_color_management_enable_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_color_management_softproof_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_display_intent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_display_bpc_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_softproof_profile_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_softproof_intent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_softproof_bpc_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_softproof_gamut_check_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_toggle_selection_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_layer_boundary_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_canvas_boundary_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_menubar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_rulers_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_scrollbars_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_statusbar_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_guides_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_toggle_sample_points_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_snap_to_guides_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_snap_to_grid_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_snap_to_canvas_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_snap_to_vectors_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_padding_color_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_padding_color_in_show_all_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void view_shrink_wrap_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void view_fullscreen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __VIEW_COMMANDS_H__ */ diff --git a/app/actions/window-actions.c b/app/actions/window-actions.c new file mode 100644 index 0000000..ec2e9be --- /dev/null +++ b/app/actions/window-actions.c @@ -0,0 +1,297 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimphelp-ids.h" + +#include "actions.h" +#include "window-actions.h" +#include "window-commands.h" + +#include "gimp-intl.h" + + +/* private functions */ + +static void window_actions_display_opened (GdkDisplayManager *manager, + GdkDisplay *display, + GimpActionGroup *group); +static void window_actions_display_closed (GdkDisplay *display, + gboolean is_error, + GimpActionGroup *group); + + +/* public functions */ + +void +window_actions_setup (GimpActionGroup *group, + const gchar *move_to_screen_help_id) +{ + GdkDisplayManager *manager = gdk_display_manager_get (); + GSList *displays; + GSList *list; + + g_object_set_data_full (G_OBJECT (group), "move-to-screen-help-id", + g_strdup (move_to_screen_help_id), + (GDestroyNotify) g_free); + + g_object_set_data_full (G_OBJECT (group), "display-table", + g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL), + (GDestroyNotify) g_hash_table_unref); + + displays = gdk_display_manager_list_displays (manager); + + /* present displays in the order in which they were opened */ + displays = g_slist_reverse (displays); + + for (list = displays; list; list = g_slist_next (list)) + { + window_actions_display_opened (manager, list->data, group); + } + + g_slist_free (displays); + + g_signal_connect_object (manager, "display-opened", + G_CALLBACK (window_actions_display_opened), + G_OBJECT (group), 0); +} + +void +window_actions_update (GimpActionGroup *group, + GtkWidget *window) +{ + const gchar *group_name; + gint show_menu = FALSE; + gchar *name; + + group_name = gimp_action_group_get_name (group); + +#define SET_ACTIVE(action,active) \ + gimp_action_group_set_action_active (group, action, (active) != 0) +#define SET_VISIBLE(action,active) \ + gimp_action_group_set_action_visible (group, action, (active) != 0) + + if (GTK_IS_WINDOW (window)) + { + GdkScreen *screen; + gchar *screen_name; + +#ifndef GIMP_UNSTABLE + { + GdkDisplay *display; + + display = gtk_widget_get_display (window); + show_menu = (gdk_display_get_n_screens (display) > 1); + } +#else + show_menu = TRUE; +#endif /* !GIMP_UNSTABLE */ + + if (! show_menu) + { + GdkDisplayManager *manager = gdk_display_manager_get (); + GSList *displays; + + displays = gdk_display_manager_list_displays (manager); + show_menu = (displays->next != NULL); + g_slist_free (displays); + } + + screen = gtk_widget_get_screen (window); + + screen_name = gdk_screen_make_display_name (screen); + name = g_strdup_printf ("%s-move-to-screen-%s", group_name, screen_name); + g_free (screen_name); + + SET_ACTIVE (name, TRUE); + g_free (name); + } + + name = g_strdup_printf ("%s-move-to-screen-menu", group_name); + SET_VISIBLE (name, show_menu); + g_free (name); + +#undef SET_ACTIVE +#undef SET_VISIBLE +} + + +/* private functions */ + +static void +window_actions_display_opened (GdkDisplayManager *manager, + GdkDisplay *display, + GimpActionGroup *group) +{ + GimpRadioActionEntry *entries; + GHashTable *displays; + const gchar *display_name; + const gchar *help_id; + const gchar *group_name; + GSList *radio_group; + gint count; + gint n_screens; + gint i; + + displays = g_object_get_data (G_OBJECT (group), "display-table"); + + display_name = gdk_display_get_name (display); + + count = GPOINTER_TO_INT (g_hash_table_lookup (displays, + display_name)); + + g_hash_table_insert (displays, g_strdup (display_name), + GINT_TO_POINTER (count + 1)); + + /* don't add the same display twice */ + if (count > 0) + return; + + help_id = g_object_get_data (G_OBJECT (group), "change-to-screen-help-id"); + + group_name = gimp_action_group_get_name (group); + + n_screens = gdk_display_get_n_screens (display); + + entries = g_new0 (GimpRadioActionEntry, n_screens); + + for (i = 0; i < n_screens; i++) + { + GdkScreen *screen = gdk_display_get_screen (display, i); + gchar *screen_name; + + screen_name = gdk_screen_make_display_name (screen); + + entries[i].name = g_strdup_printf ("%s-move-to-screen-%s", + group_name, screen_name); + entries[i].icon_name = GIMP_ICON_WINDOW_MOVE_TO_SCREEN; + entries[i].label = g_strdup_printf (_("Screen %s"), screen_name); + entries[i].accelerator = NULL; + entries[i].tooltip = g_strdup_printf (_("Move this window to " + "screen %s"), screen_name); + entries[i].value = g_quark_from_string (screen_name); + entries[i].help_id = help_id; + + g_free (screen_name); + } + + radio_group = g_object_get_data (G_OBJECT (group), + "change-to-screen-radio-group"); + radio_group = gimp_action_group_add_radio_actions (group, NULL, + entries, n_screens, + radio_group, 0, + window_move_to_screen_cmd_callback); + g_object_set_data (G_OBJECT (group), "change-to-screen-radio-group", + radio_group); + + for (i = 0; i < n_screens; i++) + { + GdkScreen *screen = gdk_display_get_screen (display, i); + GimpAction *action; + + action = gimp_action_group_get_action (group, entries[i].name); + + if (action) + g_object_set_data (G_OBJECT (action), "screen", screen); + + g_free ((gchar *) entries[i].name); + g_free ((gchar *) entries[i].tooltip); + g_free ((gchar *) entries[i].label); + } + + g_free (entries); + + g_signal_connect_object (display, "closed", + G_CALLBACK (window_actions_display_closed), + G_OBJECT (group), 0); +} + +static void +window_actions_display_closed (GdkDisplay *display, + gboolean is_error, + GimpActionGroup *group) +{ + GHashTable *displays; + const gchar *display_name; + const gchar *group_name; + gint count; + gint n_screens; + gint i; + + displays = g_object_get_data (G_OBJECT (group), "display-table"); + + display_name = gdk_display_get_name (display); + + count = GPOINTER_TO_INT (g_hash_table_lookup (displays, + display_name)); + + /* don't remove the same display twice */ + if (count > 1) + { + g_hash_table_insert (displays, g_strdup (display_name), + GINT_TO_POINTER (count - 1)); + return; + } + + g_hash_table_remove (displays, display_name); + + group_name = gimp_action_group_get_name (group); + + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) + { + GdkScreen *screen = gdk_display_get_screen (display, i); + GimpAction *action; + gchar *screen_name; + gchar *action_name; + + screen_name = gdk_screen_make_display_name (screen); + action_name = g_strdup_printf ("%s-move-to-screen-%s", + group_name, screen_name); + g_free (screen_name); + + action = gimp_action_group_get_action (group, action_name); + + if (action) + { + GSList *radio_group; + + radio_group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action)); + if (radio_group->data == (gpointer) action) + radio_group = radio_group->next; + + gimp_action_group_remove_action (group, action); + + g_object_set_data (G_OBJECT (group), "change-to-screen-radio-group", + radio_group); + } + + g_free (action_name); + } +} diff --git a/app/actions/window-actions.h b/app/actions/window-actions.h new file mode 100644 index 0000000..2aae4e2 --- /dev/null +++ b/app/actions/window-actions.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 . + */ + +#ifndef __WINDOW_ACTIONS_H__ +#define __WINDOW_ACTIONS_H__ + + +void window_actions_setup (GimpActionGroup *group, + const gchar *move_to_screen_help_id); +void window_actions_update (GimpActionGroup *group, + GtkWidget *window); + + +#endif /* __WINDOW_ACTIONS_H__ */ diff --git a/app/actions/window-commands.c b/app/actions/window-commands.c new file mode 100644 index 0000000..046f986 --- /dev/null +++ b/app/actions/window-commands.c @@ -0,0 +1,158 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" + +#include "actions.h" +#include "window-commands.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +window_close_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + return_if_no_widget (widget, data); + + if (! gtk_widget_is_toplevel (widget)) + widget = gtk_widget_get_toplevel (widget); + + if (widget && 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); + } +} + +void +window_open_display_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; + GtkWidget *dialog; + GtkWidget *entry; + return_if_no_widget (widget, data); + + dialog = gimp_message_dialog_new ("Open Display", GIMP_ICON_WILBER_EEK, + widget, GTK_DIALOG_MODAL, + NULL, NULL, + + _("_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_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + "Experimental multi-display stuff!\n" + "Click OK and have fun crashing GIMP..."); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + "Please enter the name of the new display:"); + + entry = gtk_entry_new (); + gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); + gtk_box_pack_start (GTK_BOX (GIMP_MESSAGE_DIALOG (dialog)->box), entry, + TRUE, TRUE, 0); + + gtk_widget_grab_focus (entry); + gtk_widget_show_all (dialog); + + while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + gchar *screen_name; + + screen_name = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); + + if (strcmp (screen_name, "")) + { + GdkDisplay *display; + + gtk_widget_set_sensitive (dialog, FALSE); + + display = gdk_display_open (screen_name); + + if (! display) + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + "Can't open display '%s'. " + "Please try another one:", + screen_name); + + g_free (screen_name); + + gtk_widget_set_sensitive (dialog, TRUE); + + if (display) + break; + } + + gtk_widget_grab_focus (entry); + } + + gtk_widget_destroy (dialog); +} + +void +window_move_to_screen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWidget *widget; +#if 0 + GdkScreen *screen; +#endif + return_if_no_widget (widget, data); + +#if 0 + if (! gtk_widget_is_toplevel (widget)) + widget = gtk_widget_get_toplevel (widget); + + screen = g_object_get_data (G_OBJECT (current), "screen"); + + if (GDK_IS_SCREEN (screen) && screen != gtk_widget_get_screen (widget)) + { + gtk_window_set_screen (GTK_WINDOW (widget), screen); + } +#endif +} diff --git a/app/actions/window-commands.h b/app/actions/window-commands.h new file mode 100644 index 0000000..41478d5 --- /dev/null +++ b/app/actions/window-commands.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 . + */ + +#ifndef __WINDOW_COMMANDS_H__ +#define __WINDOW_COMMANDS_H__ + + +void window_close_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void window_open_display_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void window_move_to_screen_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __WINDOW_COMMANDS_H__ */ diff --git a/app/actions/windows-actions.c b/app/actions/windows-actions.c new file mode 100644 index 0000000..52b2a9c --- /dev/null +++ b/app/actions/windows-actions.c @@ -0,0 +1,618 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdisplayconfig.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimplist.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimphelp-ids.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "dialogs/dialogs.h" + +#include "windows-actions.h" +#include "windows-commands.h" + +#include "gimp-intl.h" + + +static void windows_actions_display_add (GimpContainer *container, + GimpDisplay *display, + GimpActionGroup *group); +static void windows_actions_display_remove (GimpContainer *container, + GimpDisplay *display, + GimpActionGroup *group); +static void windows_actions_display_reorder (GimpContainer *container, + GimpDisplay *display, + gint position, + GimpActionGroup *group); +static void windows_actions_image_notify (GimpDisplay *display, + const GParamSpec *unused, + GimpActionGroup *group); +static void windows_actions_title_notify (GimpDisplayShell *shell, + const GParamSpec *unused, + GimpActionGroup *group); +static void windows_actions_update_display_accels (GimpActionGroup *group); + +static void windows_actions_dock_window_added (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpActionGroup *group); +static void windows_actions_dock_window_removed (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpActionGroup *group); +static void windows_actions_dock_window_notify (GimpDockWindow *dock, + const GParamSpec *pspec, + GimpActionGroup *group); +static void windows_actions_recent_add (GimpContainer *container, + GimpSessionInfo *info, + GimpActionGroup *group); +static void windows_actions_recent_remove (GimpContainer *container, + GimpSessionInfo *info, + GimpActionGroup *group); +static void windows_actions_single_window_mode_notify (GimpDisplayConfig *config, + GParamSpec *pspec, + GimpActionGroup *group); + + +/* The only reason we have "Tab" in the action entries below is to + * give away the hardcoded keyboard shortcut. If the user changes the + * shortcut to something else, both that shortcut and Tab will + * work. The reason we have the shortcut hardcoded is because + * gtk_accelerator_valid() returns FALSE for GDK_tab. + */ + +static const GimpActionEntry windows_actions[] = +{ + { "windows-menu", NULL, NC_("windows-action", + "_Windows") }, + { "windows-docks-menu", NULL, NC_("windows-action", + "_Recently Closed Docks") }, + { "windows-dialogs-menu", NULL, NC_("windows-action", + "_Dockable Dialogs") }, + + { "windows-show-display-next", NULL, + NC_("windows-action", "Next Image"), "Tab", + NC_("windows-action", "Switch to the next image"), + windows_show_display_next_cmd_callback, + NULL }, + + { "windows-show-display-previous", NULL, + NC_("windows-action", "Previous Image"), "Tab", + NC_("windows-action", "Switch to the previous image"), + windows_show_display_previous_cmd_callback, + NULL }, + + { "windows-tab-position", NULL, NC_("windows-action", + "_Tabs Position") }, +}; + +static const GimpToggleActionEntry windows_toggle_actions[] = +{ + { "windows-hide-docks", NULL, + NC_("windows-action", "_Hide Docks"), "Tab", + NC_("windows-action", "When enabled, docks and other dialogs are hidden, leaving only image windows."), + windows_hide_docks_cmd_callback, + FALSE, + GIMP_HELP_WINDOWS_HIDE_DOCKS }, + + { "windows-show-tabs", NULL, + NC_("windows-action", "_Show Tabs"), NULL, + NC_("windows-action", "When enabled, the image tabs bar is shown."), + windows_show_tabs_cmd_callback, + FALSE, + GIMP_HELP_WINDOWS_SHOW_TABS }, + + { "windows-use-single-window-mode", NULL, + NC_("windows-action", "Single-Window _Mode"), NULL, + NC_("windows-action", "When enabled, GIMP is in a single-window mode."), + windows_use_single_window_mode_cmd_callback, + FALSE, + GIMP_HELP_WINDOWS_USE_SINGLE_WINDOW_MODE } +}; + +static const GimpRadioActionEntry windows_tabs_position_actions[] = +{ + { "windows-tabs-position-top", GIMP_ICON_GO_TOP, + NC_("windows-tabs-position-action", "_Top"), NULL, + NC_("windows-tabs-position-action", "Position the tabs on the top"), + GIMP_POSITION_TOP, GIMP_HELP_WINDOWS_TABS_POSITION }, + + { "windows-tabs-position-bottom", GIMP_ICON_GO_BOTTOM, + NC_("windows-tabs-position-action", "_Bottom"), NULL, + NC_("windows-tabs-position-action", "Position the tabs on the bottom"), + GIMP_POSITION_BOTTOM, GIMP_HELP_WINDOWS_TABS_POSITION }, + + { "windows-tabs-position-left", GIMP_ICON_GO_FIRST, + NC_("windows-tabs-position-action", "_Left"), NULL, + NC_("windows-tabs-position-action", "Position the tabs on the left"), + GIMP_POSITION_LEFT, GIMP_HELP_WINDOWS_TABS_POSITION }, + + { "windows-tabs-position-right", GIMP_ICON_GO_LAST, + NC_("windows-tabs-position-action", "_Right"), NULL, + NC_("windows-tabs-position-action", "Position the tabs on the right"), + GIMP_POSITION_RIGHT, GIMP_HELP_WINDOWS_TABS_POSITION }, +}; + +void +windows_actions_setup (GimpActionGroup *group) +{ + GList *list; + + gimp_action_group_add_actions (group, "windows-action", + windows_actions, + G_N_ELEMENTS (windows_actions)); + + gimp_action_group_add_toggle_actions (group, "windows-action", + windows_toggle_actions, + G_N_ELEMENTS (windows_toggle_actions)); + + gimp_action_group_add_radio_actions (group, "windows-tabs-position-action", + windows_tabs_position_actions, + G_N_ELEMENTS (windows_tabs_position_actions), + NULL, 0, + windows_set_tabs_position_cmd_callback); + + gimp_action_group_set_action_hide_empty (group, "windows-docks-menu", FALSE); + + g_signal_connect_object (group->gimp->displays, "add", + G_CALLBACK (windows_actions_display_add), + group, 0); + g_signal_connect_object (group->gimp->displays, "remove", + G_CALLBACK (windows_actions_display_remove), + group, 0); + g_signal_connect_object (group->gimp->displays, "reorder", + G_CALLBACK (windows_actions_display_reorder), + group, 0); + + for (list = gimp_get_display_iter (group->gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + windows_actions_display_add (group->gimp->displays, display, group); + } + + g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-added", + G_CALLBACK (windows_actions_dock_window_added), + group, 0); + g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-removed", + G_CALLBACK (windows_actions_dock_window_removed), + group, 0); + + for (list = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ()); + list; + list = g_list_next (list)) + { + GimpDockWindow *dock_window = list->data; + + if (GIMP_IS_DOCK_WINDOW (dock_window)) + windows_actions_dock_window_added (gimp_dialog_factory_get_singleton (), + dock_window, + group); + } + + g_signal_connect_object (global_recent_docks, "add", + G_CALLBACK (windows_actions_recent_add), + group, 0); + g_signal_connect_object (global_recent_docks, "remove", + G_CALLBACK (windows_actions_recent_remove), + group, 0); + + for (list = GIMP_LIST (global_recent_docks)->queue->head; + list; + list = g_list_next (list)) + { + GimpSessionInfo *info = list->data; + + windows_actions_recent_add (global_recent_docks, info, group); + } + + g_signal_connect_object (group->gimp->config, "notify::single-window-mode", + G_CALLBACK (windows_actions_single_window_mode_notify), + group, 0); +} + +void +windows_actions_update (GimpActionGroup *group, + gpointer data) +{ + GimpGuiConfig *config = GIMP_GUI_CONFIG (group->gimp->config); + const gchar *action = NULL; + +#define SET_ACTIVE(action,condition) \ + gimp_action_group_set_action_active (group, action, (condition) != 0) + + SET_ACTIVE ("windows-use-single-window-mode", config->single_window_mode); + SET_ACTIVE ("windows-hide-docks", config->hide_docks); + SET_ACTIVE ("windows-show-tabs", config->show_tabs); + + switch (config->tabs_position) + { + case GIMP_POSITION_TOP: + action = "windows-tabs-position-top"; + break; + case GIMP_POSITION_BOTTOM: + action = "windows-tabs-position-bottom"; + break; + case GIMP_POSITION_LEFT: + action = "windows-tabs-position-left"; + break; + case GIMP_POSITION_RIGHT: + action = "windows-tabs-position-right"; + break; + default: + action = "windows-tabs-position-top"; + break; + } + + gimp_action_group_set_action_active (group, action, TRUE); + gimp_action_group_set_action_sensitive (group, "windows-tab-position", config->single_window_mode); + gimp_action_group_set_action_sensitive (group, "windows-show-tabs", config->single_window_mode); + +#undef SET_ACTIVE +} + +gchar * +windows_actions_dock_window_to_action_name (GimpDockWindow *dock_window) +{ + return g_strdup_printf ("windows-dock-%04d", + gimp_dock_window_get_id (dock_window)); +} + + +/* private functions */ + +static void +windows_actions_display_add (GimpContainer *container, + GimpDisplay *display, + GimpActionGroup *group) +{ + GimpDisplayShell *shell = gimp_display_get_shell (display); + + g_signal_connect_object (display, "notify::image", + G_CALLBACK (windows_actions_image_notify), + group, 0); + + g_signal_connect_object (shell, "notify::title", + G_CALLBACK (windows_actions_title_notify), + group, 0); + + windows_actions_image_notify (display, NULL, group); +} + +static void +windows_actions_display_remove (GimpContainer *container, + GimpDisplay *display, + GimpActionGroup *group) +{ + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpAction *action; + gchar *action_name; + + if (shell) + g_signal_handlers_disconnect_by_func (shell, + windows_actions_title_notify, + group); + + action_name = gimp_display_get_action_name (display); + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (action) + gimp_action_group_remove_action_and_accel (group, action); + + windows_actions_update_display_accels (group); +} + +static void +windows_actions_display_reorder (GimpContainer *container, + GimpDisplay *display, + gint new_index, + GimpActionGroup *group) +{ + windows_actions_update_display_accels (group); +} + +static void +windows_actions_image_notify (GimpDisplay *display, + const GParamSpec *unused, + GimpActionGroup *group) +{ + GimpImage *image = gimp_display_get_image (display); + GimpAction *action; + gchar *action_name; + + action_name = gimp_display_get_action_name (display); + + action = gimp_action_group_get_action (group, action_name); + + if (! action) + { + GimpActionEntry entry; + + entry.name = action_name; + entry.icon_name = GIMP_ICON_IMAGE; + entry.label = ""; + entry.accelerator = NULL; + entry.tooltip = NULL; + entry.callback = windows_show_display_cmd_callback; + entry.help_id = NULL; + + gimp_action_group_add_actions (group, NULL, &entry, 1); + + gimp_action_group_set_action_always_show_image (group, action_name, + TRUE); + action = gimp_action_group_get_action (group, action_name); + + g_object_set_data (G_OBJECT (action), "display", display); + } + + g_free (action_name); + + if (image) + { + const gchar *display_name; + gchar *escaped; + gchar *title; + + display_name = gimp_image_get_display_name (image); + escaped = gimp_escape_uline (display_name); + + title = g_strdup_printf ("%s-%d.%d", escaped, + gimp_image_get_ID (image), + gimp_display_get_instance (display)); + g_free (escaped); + + g_object_set (action, + "visible", TRUE, + "label", title, + "tooltip", gimp_image_get_display_path (image), + "viewable", image, + "context", gimp_get_user_context (group->gimp), + NULL); + + g_free (title); + + windows_actions_update_display_accels (group); + } + else + { + g_object_set (action, + "visible", FALSE, + "viewable", NULL, + NULL); + } +} + +static void +windows_actions_title_notify (GimpDisplayShell *shell, + const GParamSpec *unused, + GimpActionGroup *group) +{ + windows_actions_image_notify (shell->display, NULL, group); +} + +static void +windows_actions_update_display_accels (GimpActionGroup *group) +{ + GList *list; + gint i; + + for (list = gimp_get_display_iter (group->gimp), i = 0; + list && i < 10; + list = g_list_next (list), i++) + { + GimpDisplay *display = list->data; + GimpAction *action; + gchar *action_name; + + if (! gimp_display_get_image (display)) + break; + + action_name = gimp_display_get_action_name (display); + + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (action) + { + const gchar *accel_path; + guint accel_key; + + accel_path = gimp_action_get_accel_path (action); + + if (i < 9) + accel_key = GDK_KEY_1 + i; + else + accel_key = GDK_KEY_0; + + gtk_accel_map_change_entry (accel_path, + accel_key, GDK_MOD1_MASK, + TRUE); + } + } +} + +static void +windows_actions_dock_window_added (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpActionGroup *group) +{ + GimpAction *action; + GimpActionEntry entry; + gchar *action_name = windows_actions_dock_window_to_action_name (dock_window); + + entry.name = action_name; + entry.icon_name = NULL; + entry.label = ""; + entry.accelerator = NULL; + entry.tooltip = NULL; + entry.callback = windows_show_dock_cmd_callback; + entry.help_id = GIMP_HELP_WINDOWS_SHOW_DOCK; + + gimp_action_group_add_actions (group, NULL, &entry, 1); + + action = gimp_action_group_get_action (group, action_name); + + g_object_set (action, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + g_object_set_data (G_OBJECT (action), "dock-window", dock_window); + + g_free (action_name); + + g_signal_connect_object (dock_window, "notify::title", + G_CALLBACK (windows_actions_dock_window_notify), + group, 0); + + if (gtk_window_get_title (GTK_WINDOW (dock_window))) + windows_actions_dock_window_notify (dock_window, NULL, group); +} + +static void +windows_actions_dock_window_removed (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpActionGroup *group) +{ + GimpAction *action; + gchar *action_name; + + action_name = windows_actions_dock_window_to_action_name (dock_window); + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (action) + gimp_action_group_remove_action_and_accel (group, action); +} + +static void +windows_actions_dock_window_notify (GimpDockWindow *dock_window, + const GParamSpec *pspec, + GimpActionGroup *group) +{ + GimpAction *action; + gchar *action_name; + + action_name = windows_actions_dock_window_to_action_name (dock_window); + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (action) + g_object_set (action, + "label", gtk_window_get_title (GTK_WINDOW (dock_window)), + "tooltip", gtk_window_get_title (GTK_WINDOW (dock_window)), + NULL); +} + +static void +windows_actions_recent_add (GimpContainer *container, + GimpSessionInfo *info, + GimpActionGroup *group) +{ + GimpAction *action; + GimpActionEntry entry; + gint info_id; + static gint info_id_counter = 1; + gchar *action_name; + + info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), + "recent-action-id")); + + if (! info_id) + { + info_id = info_id_counter++; + + g_object_set_data (G_OBJECT (info), "recent-action-id", + GINT_TO_POINTER (info_id)); + } + + action_name = g_strdup_printf ("windows-recent-%04d", info_id); + + entry.name = action_name; + entry.icon_name = NULL; + entry.label = gimp_object_get_name (info); + entry.accelerator = NULL; + entry.tooltip = gimp_object_get_name (info); + entry.callback = windows_open_recent_cmd_callback; + entry.help_id = GIMP_HELP_WINDOWS_OPEN_RECENT_DOCK; + + gimp_action_group_add_actions (group, NULL, &entry, 1); + + action = gimp_action_group_get_action (group, action_name); + + g_object_set (action, + "ellipsize", PANGO_ELLIPSIZE_END, + "max-width-chars", 30, + NULL); + + g_object_set_data (G_OBJECT (action), "info", info); + + g_free (action_name); +} + +static void +windows_actions_recent_remove (GimpContainer *container, + GimpSessionInfo *info, + GimpActionGroup *group) +{ + GimpAction *action; + gint info_id; + gchar *action_name; + + info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), + "recent-action-id")); + + action_name = g_strdup_printf ("windows-recent-%04d", info_id); + action = gimp_action_group_get_action (group, action_name); + g_free (action_name); + + if (action) + gimp_action_group_remove_action_and_accel (group, action); +} + +static void +windows_actions_single_window_mode_notify (GimpDisplayConfig *config, + GParamSpec *pspec, + GimpActionGroup *group) +{ + gimp_action_group_set_action_active (group, + "windows-use-single-window-mode", + GIMP_GUI_CONFIG (config)->single_window_mode); +} diff --git a/app/actions/windows-actions.h b/app/actions/windows-actions.h new file mode 100644 index 0000000..6d73fed --- /dev/null +++ b/app/actions/windows-actions.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 . + */ + +#ifndef __WINDOWS_ACTIONS_H__ +#define __WINDOWS_ACTIONS_H__ + + +void windows_actions_setup (GimpActionGroup *group); +void windows_actions_update (GimpActionGroup *group, + gpointer data); +gchar * windows_actions_dock_window_to_action_name (GimpDockWindow *dock_window); + + +#endif /* __WINDOWS_ACTIONS_H__ */ diff --git a/app/actions/windows-commands.c b/app/actions/windows-commands.c new file mode 100644 index 0000000..873b595 --- /dev/null +++ b/app/actions/windows-commands.c @@ -0,0 +1,225 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "actions-types.h" + +#include "config/gimpdisplayconfig.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "dialogs/dialogs.h" + +#include "actions.h" +#include "dialogs-actions.h" +#include "windows-commands.h" + +#include "gimp-intl.h" + + +void +windows_hide_docks_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + gboolean active; + return_if_no_gimp (gimp, data); + + active = g_variant_get_boolean (value); + + if (active != GIMP_GUI_CONFIG (gimp->config)->hide_docks) + g_object_set (gimp->config, + "hide-docks", active, + NULL); +} + +void +windows_use_single_window_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + gboolean active; + return_if_no_gimp (gimp, data); + + active = g_variant_get_boolean (value); + + if (active != GIMP_GUI_CONFIG (gimp->config)->single_window_mode) + g_object_set (gimp->config, + "single-window-mode", active, + NULL); +} + +void +windows_show_tabs_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + gboolean active; + return_if_no_gimp (gimp, data); + + active = g_variant_get_boolean (value); + + if (active != GIMP_GUI_CONFIG (gimp->config)->show_tabs) + g_object_set (gimp->config, + "show-tabs", active, + NULL); +} + + +void +windows_set_tabs_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + Gimp *gimp; + GimpPosition position; + return_if_no_gimp (gimp, data); + + position = (GimpPosition) g_variant_get_int32 (value); + + if (position != GIMP_GUI_CONFIG (gimp->config)->tabs_position) + g_object_set (gimp->config, + "tabs-position", position, + NULL); +} + +void +windows_show_display_next_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + Gimp *gimp; + gint index; + return_if_no_display (display, data); + return_if_no_gimp (gimp, data); + + index = gimp_container_get_child_index (gimp->displays, + GIMP_OBJECT (display)); + index++; + + if (index >= gimp_container_get_n_children (gimp->displays)) + index = 0; + + display = GIMP_DISPLAY (gimp_container_get_child_by_index (gimp->displays, + index)); + gimp_display_shell_present (gimp_display_get_shell (display)); +} + +void +windows_show_display_previous_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display; + Gimp *gimp; + gint index; + return_if_no_display (display, data); + return_if_no_gimp (gimp, data); + + index = gimp_container_get_child_index (gimp->displays, + GIMP_OBJECT (display)); + index--; + + if (index < 0) + index = gimp_container_get_n_children (gimp->displays) - 1; + + display = GIMP_DISPLAY (gimp_container_get_child_by_index (gimp->displays, + index)); + gimp_display_shell_present (gimp_display_get_shell (display)); +} + +void +windows_show_display_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpDisplay *display = g_object_get_data (G_OBJECT (action), "display"); + + gimp_display_shell_present (gimp_display_get_shell (display)); +} + +void +windows_show_dock_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GtkWindow *dock_window = g_object_get_data (G_OBJECT (action), "dock-window"); + + gtk_window_present (dock_window); +} + +void +windows_open_recent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data) +{ + GimpSessionInfo *info; + GimpDialogFactoryEntry *entry; + Gimp *gimp; + GtkWidget *widget; + return_if_no_gimp (gimp, data); + return_if_no_widget (widget, data); + + info = g_object_get_data (G_OBJECT (action), "info"); + entry = gimp_session_info_get_factory_entry (info); + + if (entry && strcmp ("gimp-toolbox-window", entry->identifier) == 0 && + dialogs_actions_toolbox_exists (gimp)) + { + gimp_message (gimp, + G_OBJECT (action_data_get_widget (data)), + GIMP_MESSAGE_WARNING, + _("The chosen recent dock contains a toolbox. Please " + "close the currently open toolbox and try again.")); + return; + } + + g_object_ref (info); + + gimp_container_remove (global_recent_docks, GIMP_OBJECT (info)); + + gimp_dialog_factory_add_session_info (gimp_dialog_factory_get_singleton (), + info); + + gimp_session_info_restore (info, gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget)); + + g_object_unref (info); +} diff --git a/app/actions/windows-commands.h b/app/actions/windows-commands.h new file mode 100644 index 0000000..712d3c1 --- /dev/null +++ b/app/actions/windows-commands.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __WINDOWS_COMMANDS_H__ +#define __WINDOWS_COMMANDS_H__ + + +void windows_hide_docks_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_use_single_window_mode_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void windows_show_tabs_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_set_tabs_position_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + +void windows_show_display_next_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_show_display_previous_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_show_display_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_show_dock_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); +void windows_open_recent_cmd_callback (GimpAction *action, + GVariant *value, + gpointer data); + + +#endif /* __WINDOWS_COMMANDS_H__ */ diff --git a/app/app.c b/app/app.c new file mode 100644 index 0000000..3bb210b --- /dev/null +++ b/app/app.c @@ -0,0 +1,523 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include + +#include + +#ifdef G_OS_WIN32 +#include +#include +#endif + +#undef GIMP_DISABLE_DEPRECATED /* for compat enums */ +#include "libgimpbase/gimpbase.h" +#define GIMP_DISABLE_DEPRECATED +#include "libgimpconfig/gimpconfig.h" + +#include "core/core-types.h" + +#include "config/gimplangrc.h" +#include "config/gimprc.h" + +#include "gegl/gimp-gegl.h" + +#include "core/gimp.h" +#include "core/gimp-batch.h" +#include "core/gimp-user-install.h" +#include "core/gimpimage.h" + +#include "file/file-open.h" + +#ifndef GIMP_CONSOLE_COMPILATION +#include "dialogs/user-install-dialog.h" + +#include "gui/gui.h" +#endif + +#include "app.h" +#include "errors.h" +#include "language.h" +#include "sanity.h" +#include "gimp-debug.h" + +#include "gimp-intl.h" +#include "gimp-update.h" + + +/* local prototypes */ + +static void app_init_update_noop (const gchar *text1, + const gchar *text2, + gdouble percentage); +static void app_restore_after_callback (Gimp *gimp, + GimpInitStatusFunc status_callback); +static gboolean app_exit_after_callback (Gimp *gimp, + gboolean kill_it, + GMainLoop **loop); + +GType gimp_convert_dither_type_compat_get_type (void); /* compat cruft */ +GType gimp_layer_mode_effects_get_type (void); /* compat cruft */ + + +/* local variables */ + +static GObject *initial_screen = NULL; +static gint initial_monitor = 0; + + +/* public functions */ + +void +app_libs_init (GOptionContext *context, + gboolean no_interface) +{ + GQuark quark; + + /* disable OpenCL before GEGL is even initialized; this way we only + * enable if wanted in gimprc, instead of always enabling, and then + * disabling again if wanted in gimprc + */ + g_object_set (gegl_config (), + "use-opencl", FALSE, + "application-license", "GPL3", + NULL); + + g_option_context_add_group (context, gegl_get_option_group ()); + +#ifndef GIMP_CONSOLE_COMPILATION + if (! no_interface) + { + gui_libs_init (context); + } +#endif + + /* keep compat enum code in sync with pdb/enumcode.pl */ + quark = g_quark_from_static_string ("gimp-compat-enum"); + + g_type_set_qdata (GIMP_TYPE_CONVERT_DITHER_TYPE, quark, + (gpointer) gimp_convert_dither_type_compat_get_type ()); + g_type_set_qdata (GIMP_TYPE_LAYER_MODE, quark, + (gpointer) gimp_layer_mode_effects_get_type ()); +} + +void +app_abort (gboolean no_interface, + const gchar *abort_message) +{ +#ifndef GIMP_CONSOLE_COMPILATION + if (no_interface) +#endif + { + g_print ("%s\n\n", abort_message); + } +#ifndef GIMP_CONSOLE_COMPILATION + else + { + gui_abort (abort_message); + } +#endif + + app_exit (EXIT_FAILURE); +} + +void +app_exit (gint status) +{ + exit (status); +} + +void +app_run (const gchar *full_prog_name, + const gchar **filenames, + GFile *alternate_system_gimprc, + GFile *alternate_gimprc, + const gchar *session_name, + const gchar *batch_interpreter, + const gchar **batch_commands, + gboolean as_new, + gboolean no_interface, + gboolean no_data, + gboolean no_fonts, + gboolean no_splash, + gboolean be_verbose, + gboolean use_shm, + gboolean use_cpu_accel, + gboolean console_messages, + gboolean use_debug_handler, + gboolean show_playground, + gboolean show_debug_menu, + GimpStackTraceMode stack_trace_mode, + GimpPDBCompatMode pdb_compat_mode, + const gchar *backtrace_file) +{ + GimpInitStatusFunc update_status_func = NULL; + Gimp *gimp; + GMainLoop *loop; + GMainLoop *run_loop; + GFile *default_folder = NULL; + GFile *gimpdir; + const gchar *abort_message; + GimpLangRc *temprc; + gchar *language = NULL; + GError *font_error = NULL; + + if (filenames && filenames[0] && ! filenames[1] && + g_file_test (filenames[0], G_FILE_TEST_IS_DIR)) + { + if (g_path_is_absolute (filenames[0])) + { + default_folder = g_file_new_for_path (filenames[0]); + } + else + { + gchar *absolute = g_build_path (G_DIR_SEPARATOR_S, + g_get_current_dir (), + filenames[0], + NULL); + default_folder = g_file_new_for_path (absolute); + g_free (absolute); + } + + filenames = NULL; + } + + /* Language needs to be determined first, before any GimpContext is + * instantiated (which happens when the Gimp object is created) + * because its properties need to be properly localized in the + * settings language (if different from system language). Otherwise we + * end up with pieces of GUI always using the system language (cf. bug + * 787457). Therefore we do a first pass on "gimprc" file for the sole + * purpose of getting the settings language, so that we can initialize + * it before anything else. + */ + temprc = gimp_lang_rc_new (alternate_system_gimprc, + alternate_gimprc, + be_verbose); + language = gimp_lang_rc_get_language (temprc); + g_object_unref (temprc); + + /* change the locale if a language if specified */ + language_init (language); + if (language) + g_free (language); + + /* Create an instance of the "Gimp" object which is the root of the + * core object system + */ + gimp = gimp_new (full_prog_name, + session_name, + default_folder, + be_verbose, + no_data, + no_fonts, + no_interface, + use_shm, + use_cpu_accel, + console_messages, + show_playground, + show_debug_menu, + stack_trace_mode, + pdb_compat_mode); + + if (default_folder) + g_object_unref (default_folder); + + gimp_cpu_accel_set_use (use_cpu_accel); + + /* Check if the user's gimp_directory exists + */ + gimpdir = gimp_directory_file (NULL); + + if (g_file_query_file_type (gimpdir, G_FILE_QUERY_INFO_NONE, NULL) != + G_FILE_TYPE_DIRECTORY) + { + GimpUserInstall *install = gimp_user_install_new (G_OBJECT (gimp), + be_verbose); + +#ifdef GIMP_CONSOLE_COMPILATION + gimp_user_install_run (install); +#else + if (! (no_interface ? + gimp_user_install_run (install) : + user_install_dialog_run (install))) + exit (EXIT_FAILURE); +#endif + + gimp_user_install_free (install); + } + + g_object_unref (gimpdir); + + gimp_load_config (gimp, alternate_system_gimprc, alternate_gimprc); + + /* Initialize the error handling after creating/migrating the config + * directory because it will create some folders for backup and crash + * logs in advance. Therefore running this before + * gimp_user_install_new() would break migration as well as initial + * folder creations. + * + * It also needs to be run after gimp_load_config() because error + * handling is based on Preferences. It means that early loading code + * is not handled by our debug code, but that's not a big deal. + */ + errors_init (gimp, full_prog_name, use_debug_handler, + stack_trace_mode, backtrace_file); + + /* run the late-stage sanity check. it's important that this check is run + * after the call to language_init() (see comment in sanity_check_late().) + */ + abort_message = sanity_check_late (); + if (abort_message) + app_abort (no_interface, abort_message); + + /* initialize lowlevel stuff */ + gimp_gegl_init (gimp); + + /* Connect our restore_after callback before gui_init() connects + * theirs, so ours runs first and can grab the initial monitor + * before the GUI's restore_after callback resets it. + */ + g_signal_connect_after (gimp, "restore", + G_CALLBACK (app_restore_after_callback), + NULL); + +#ifndef GIMP_CONSOLE_COMPILATION + if (! no_interface) + update_status_func = gui_init (gimp, no_splash); +#endif + + if (! update_status_func) + update_status_func = app_init_update_noop; + + /* Create all members of the global Gimp instance which need an already + * parsed gimprc, e.g. the data factories + */ + gimp_initialize (gimp, update_status_func); + + /* Load all data files + */ + gimp_restore (gimp, update_status_func, &font_error); + + /* enable autosave late so we don't autosave when the + * monitor resolution is set in gui_init() + */ + gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), TRUE); + + /* check for updates *after* enabling config autosave, so that the timestamp + * is saved + */ + gimp_update_auto_check (gimp->edit_config); + + loop = run_loop = g_main_loop_new (NULL, FALSE); + + g_signal_connect_after (gimp, "exit", + G_CALLBACK (app_exit_after_callback), + &run_loop); + +#ifndef GIMP_CONSOLE_COMPILATION + if (run_loop && ! no_interface) + { + /* Before opening images from command line, check for salvaged images + * and query interactively to know if we should recover or discard + * them. + */ + GList *recovered_files; + GList *iter; + + recovered_files = errors_recovered (); + if (recovered_files && + gui_recover (g_list_length (recovered_files))) + { + for (iter = recovered_files; iter; iter = iter->next) + { + GFile *file; + GimpImage *image; + GError *error = NULL; + GimpPDBStatusType status; + + file = g_file_new_for_path (iter->data); + image = file_open_with_display (gimp, + gimp_get_user_context (gimp), + NULL, + file, as_new, + initial_screen, + initial_monitor, + &status, &error); + if (image) + { + /* Break ties with the backup directory. */ + gimp_image_set_file (image, NULL); + /* One of the rare exceptions where we should call + * gimp_image_dirty() directly instead of creating + * an undo. We want the image to be dirty from + * scratch, without anything to undo. + */ + gimp_image_dirty (image, GIMP_DIRTY_IMAGE); + } + else + { + g_error_free (error); + } + + g_object_unref (file); + } + } + /* Delete backup XCF images. */ + for (iter = recovered_files; iter; iter = iter->next) + { + g_unlink (iter->data); + } + g_list_free_full (recovered_files, g_free); + } +#endif + + /* Load the images given on the command-line. */ + if (filenames) + { + gint i; + + for (i = 0; filenames[i] != NULL; i++) + { + if (run_loop) + { + GFile *file = g_file_new_for_commandline_arg (filenames[i]); + + file_open_from_command_line (gimp, file, as_new, + initial_screen, + initial_monitor); + + g_object_unref (file); + } + } + } + + /* The software is now fully loaded and ready to be used and get + * external input. + */ + gimp->initialized = TRUE; + + if (font_error) + { + gimp_message_literal (gimp, NULL, + GIMP_MESSAGE_INFO, + font_error->message); + g_error_free (font_error); + } + + if (run_loop) + gimp_batch_run (gimp, batch_interpreter, batch_commands); + + if (run_loop) + { + gimp_threads_leave (gimp); + g_main_loop_run (loop); + gimp_threads_enter (gimp); + } + + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + g_main_loop_unref (loop); + + gimp_gegl_exit (gimp); + + errors_exit (); + + g_object_unref (gimp); + + gimp_debug_instances (); + + gegl_exit (); +} + + +/* private functions */ + +static void +app_init_update_noop (const gchar *text1, + const gchar *text2, + gdouble percentage) +{ + /* deliberately do nothing */ +} + +static void +app_restore_after_callback (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + /* Getting the display name for a -1 display returns the initial + * monitor during startup. Need to call this from a restore_after + * callback, because before restore(), the GUI can't return anything, + * after after restore() the initial monitor gets reset. + */ + g_free (gimp_get_display_name (gimp, -1, &initial_screen, &initial_monitor)); +} + +static gboolean +app_exit_after_callback (Gimp *gimp, + gboolean kill_it, + GMainLoop **loop) +{ + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + /* + * In stable releases, we simply call exit() here. This speeds up + * the process of quitting GIMP and also works around the problem + * that plug-ins might still be running. + * + * In unstable releases, we shut down GIMP properly in an attempt + * to catch possible problems in our finalizers. + */ + +#ifndef GIMP_RELEASE + + if (g_main_loop_is_running (*loop)) + g_main_loop_quit (*loop); + + *loop = NULL; + +#else + + gimp_gegl_exit (gimp); + + gegl_exit (); + + exit (EXIT_SUCCESS); + +#endif + + return FALSE; +} diff --git a/app/app.h b/app/app.h new file mode 100644 index 0000000..1ec9447 --- /dev/null +++ b/app/app.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 . + */ + +#ifndef __APP_H__ +#define __APP_H__ + + +#ifndef GIMP_APP_GLUE_COMPILATION +#error You must not #include "app.h" from a subdir +#endif + + +void app_libs_init (GOptionContext *context, + gboolean no_interface); +void app_abort (gboolean no_interface, + const gchar *abort_message) G_GNUC_NORETURN; +void app_exit (gint status) G_GNUC_NORETURN; + +void app_run (const gchar *full_prog_name, + const gchar **filenames, + GFile *alternate_system_gimprc, + GFile *alternate_gimprc, + const gchar *session_name, + const gchar *batch_interpreter, + const gchar **batch_commands, + gboolean as_new, + gboolean no_interface, + gboolean no_data, + gboolean no_fonts, + gboolean no_splash, + gboolean be_verbose, + gboolean use_shm, + gboolean use_cpu_accel, + gboolean console_messages, + gboolean use_debug_handler, + gboolean show_playground, + gboolean show_debug_menu, + GimpStackTraceMode stack_trace_mode, + GimpPDBCompatMode pdb_compat_mode, + const gchar *backtrace_file); + + +#endif /* __APP_H__ */ diff --git a/app/config/Makefile.am b/app/config/Makefile.am new file mode 100644 index 0000000..04ac126 --- /dev/null +++ b/app/config/Makefile.am @@ -0,0 +1,165 @@ +## Process this file with automake to produce Makefile.in + +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la + +if OS_WIN32 +else +libm = -lm +endif + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Config\" \ + -DGIMP_APP_VERSION_STRING=\"$(GIMP_APP_VERSION)\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GIO_UNIX_CFLAGS) \ + $(GIO_WINDOWS_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(CAIRO_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(MYPAINT_BRUSHES_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappconfig.a + +libappconfig_a_sources = \ + config-enums.h \ + config-types.h \ + gimpconfig-dump.c \ + gimpconfig-dump.h \ + gimpconfig-file.c \ + gimpconfig-file.h \ + gimpconfig-utils.c \ + gimpconfig-utils.h \ + gimpcoreconfig.c \ + gimpcoreconfig.h \ + gimpdialogconfig.c \ + gimpdialogconfig.h \ + gimpdisplayconfig.c \ + gimpdisplayconfig.h \ + gimpdisplayoptions.c \ + gimpdisplayoptions.h \ + gimpgeglconfig.c \ + gimpgeglconfig.h \ + gimpguiconfig.c \ + gimpguiconfig.h \ + gimplangrc.c \ + gimplangrc.h \ + gimppluginconfig.c \ + gimppluginconfig.h \ + gimprc.c \ + gimprc.h \ + gimprc-blurbs.h \ + gimprc-deserialize.c \ + gimprc-deserialize.h \ + gimprc-serialize.c \ + gimprc-serialize.h \ + gimprc-unknown.c \ + gimprc-unknown.h \ + gimpxmlparser.c \ + gimpxmlparser.h + +libappconfig_a_built_sources = \ + config-enums.c + +libappconfig_a_SOURCES = \ + $(libappconfig_a_built_sources) \ + $(libappconfig_a_sources) + +EXTRA_PROGRAMS = test-config + +# +# unit tests for the GimpConfig system +# + +TESTS = test-config + +test_config_DEPENDENCIES = $(gimpconfig_libs) + +# We need this due to circular dependencies +test_config_LDFLAGS = \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_param_spec_duplicate \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy \ + -Wl,-u,$(SYMPREFIX)gimp_async_set_new \ + -Wl,-u,$(SYMPREFIX)gimp_uncancelable_waitable_new + +test_config_LDADD = \ + ../xcf/libappxcf.a \ + ../pdb/libappinternal-procs.a \ + ../pdb/libapppdb.a \ + ../plug-in/libappplug-in.a \ + ../vectors/libappvectors.a \ + ../core/libappcore.a \ + ../file/libappfile.a \ + ../file-data/libappfile-data.a \ + ../text/libapptext.a \ + ../paint/libapppaint.a \ + ../gegl/libappgegl.a \ + ../operations/libappoperations.a \ + ../operations/layer-modes/libapplayermodes.a \ + ../operations/layer-modes-legacy/libapplayermodeslegacy.a \ + libappconfig.a \ + ../gimp-debug.o \ + ../gimp-log.o \ + $(libgimpmodule) \ + $(libgimpcolor) \ + $(libgimpthumb) \ + $(libgimpmath) \ + $(libgimpconfig) \ + $(libgimpbase) \ + $(PANGOCAIRO_LIBS) \ + $(HARFBUZZ_LIBS) \ + $(GDK_PIXBUF_LIBS) \ + $(GEGL_LIBS) \ + $(GIO_LIBS) \ + $(GEXIV2_LIBS) \ + $(Z_LIBS) \ + $(JSON_C_LIBS) \ + $(LIBMYPAINT_LIBS) \ + $(libm) + +CLEANFILES = $(EXTRA_PROGRAMS) foorc + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-cec +CLEANFILES += $(gen_sources) + +xgen-cec: $(srcdir)/config-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"config-enums.h\"\n#include\"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/config-enums.c: xgen-cec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/config/Makefile.in b/app/config/Makefile.in new file mode 100644 index 0000000..ab37152 --- /dev/null +++ b/app/config/Makefile.in @@ -0,0 +1,1505 @@ +# 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@ +EXTRA_PROGRAMS = test-config$(EXEEXT) +TESTS = test-config$(EXEEXT) +subdir = app/config +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 = +libappconfig_a_AR = $(AR) $(ARFLAGS) +libappconfig_a_LIBADD = +am__objects_1 = config-enums.$(OBJEXT) +am__objects_2 = gimpconfig-dump.$(OBJEXT) gimpconfig-file.$(OBJEXT) \ + gimpconfig-utils.$(OBJEXT) gimpcoreconfig.$(OBJEXT) \ + gimpdialogconfig.$(OBJEXT) gimpdisplayconfig.$(OBJEXT) \ + gimpdisplayoptions.$(OBJEXT) gimpgeglconfig.$(OBJEXT) \ + gimpguiconfig.$(OBJEXT) gimplangrc.$(OBJEXT) \ + gimppluginconfig.$(OBJEXT) gimprc.$(OBJEXT) \ + gimprc-deserialize.$(OBJEXT) gimprc-serialize.$(OBJEXT) \ + gimprc-unknown.$(OBJEXT) gimpxmlparser.$(OBJEXT) +am_libappconfig_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappconfig_a_OBJECTS = $(am_libappconfig_a_OBJECTS) +test_config_SOURCES = test-config.c +test_config_OBJECTS = test-config.$(OBJEXT) +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 = +test_config_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(test_config_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)/config-enums.Po \ + ./$(DEPDIR)/gimpconfig-dump.Po ./$(DEPDIR)/gimpconfig-file.Po \ + ./$(DEPDIR)/gimpconfig-utils.Po ./$(DEPDIR)/gimpcoreconfig.Po \ + ./$(DEPDIR)/gimpdialogconfig.Po \ + ./$(DEPDIR)/gimpdisplayconfig.Po \ + ./$(DEPDIR)/gimpdisplayoptions.Po \ + ./$(DEPDIR)/gimpgeglconfig.Po ./$(DEPDIR)/gimpguiconfig.Po \ + ./$(DEPDIR)/gimplangrc.Po ./$(DEPDIR)/gimppluginconfig.Po \ + ./$(DEPDIR)/gimprc-deserialize.Po \ + ./$(DEPDIR)/gimprc-serialize.Po ./$(DEPDIR)/gimprc-unknown.Po \ + ./$(DEPDIR)/gimprc.Po ./$(DEPDIR)/gimpxmlparser.Po \ + ./$(DEPDIR)/test-config.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 = $(libappconfig_a_SOURCES) test-config.c +DIST_SOURCES = $(libappconfig_a_SOURCES) test-config.c +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__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +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__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver +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@ +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la +@OS_WIN32_FALSE@libm = -lm +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Config\" \ + -DGIMP_APP_VERSION_STRING=\"$(GIMP_APP_VERSION)\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GIO_UNIX_CFLAGS) \ + $(GIO_WINDOWS_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(CAIRO_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(MYPAINT_BRUSHES_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappconfig.a +libappconfig_a_sources = \ + config-enums.h \ + config-types.h \ + gimpconfig-dump.c \ + gimpconfig-dump.h \ + gimpconfig-file.c \ + gimpconfig-file.h \ + gimpconfig-utils.c \ + gimpconfig-utils.h \ + gimpcoreconfig.c \ + gimpcoreconfig.h \ + gimpdialogconfig.c \ + gimpdialogconfig.h \ + gimpdisplayconfig.c \ + gimpdisplayconfig.h \ + gimpdisplayoptions.c \ + gimpdisplayoptions.h \ + gimpgeglconfig.c \ + gimpgeglconfig.h \ + gimpguiconfig.c \ + gimpguiconfig.h \ + gimplangrc.c \ + gimplangrc.h \ + gimppluginconfig.c \ + gimppluginconfig.h \ + gimprc.c \ + gimprc.h \ + gimprc-blurbs.h \ + gimprc-deserialize.c \ + gimprc-deserialize.h \ + gimprc-serialize.c \ + gimprc-serialize.h \ + gimprc-unknown.c \ + gimprc-unknown.h \ + gimpxmlparser.c \ + gimpxmlparser.h + +libappconfig_a_built_sources = \ + config-enums.c + +libappconfig_a_SOURCES = \ + $(libappconfig_a_built_sources) \ + $(libappconfig_a_sources) + +test_config_DEPENDENCIES = $(gimpconfig_libs) + +# We need this due to circular dependencies +test_config_LDFLAGS = \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_param_spec_duplicate \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy \ + -Wl,-u,$(SYMPREFIX)gimp_async_set_new \ + -Wl,-u,$(SYMPREFIX)gimp_uncancelable_waitable_new + +test_config_LDADD = \ + ../xcf/libappxcf.a \ + ../pdb/libappinternal-procs.a \ + ../pdb/libapppdb.a \ + ../plug-in/libappplug-in.a \ + ../vectors/libappvectors.a \ + ../core/libappcore.a \ + ../file/libappfile.a \ + ../file-data/libappfile-data.a \ + ../text/libapptext.a \ + ../paint/libapppaint.a \ + ../gegl/libappgegl.a \ + ../operations/libappoperations.a \ + ../operations/layer-modes/libapplayermodes.a \ + ../operations/layer-modes-legacy/libapplayermodeslegacy.a \ + libappconfig.a \ + ../gimp-debug.o \ + ../gimp-log.o \ + $(libgimpmodule) \ + $(libgimpcolor) \ + $(libgimpthumb) \ + $(libgimpmath) \ + $(libgimpconfig) \ + $(libgimpbase) \ + $(PANGOCAIRO_LIBS) \ + $(HARFBUZZ_LIBS) \ + $(GDK_PIXBUF_LIBS) \ + $(GEGL_LIBS) \ + $(GIO_LIBS) \ + $(GEXIV2_LIBS) \ + $(Z_LIBS) \ + $(JSON_C_LIBS) \ + $(LIBMYPAINT_LIBS) \ + $(libm) + +CLEANFILES = $(EXTRA_PROGRAMS) foorc $(gen_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-cec +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(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 app/config/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/config/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) + +libappconfig.a: $(libappconfig_a_OBJECTS) $(libappconfig_a_DEPENDENCIES) $(EXTRA_libappconfig_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappconfig.a + $(AM_V_AR)$(libappconfig_a_AR) libappconfig.a $(libappconfig_a_OBJECTS) $(libappconfig_a_LIBADD) + $(AM_V_at)$(RANLIB) libappconfig.a + +test-config$(EXEEXT): $(test_config_OBJECTS) $(test_config_DEPENDENCIES) $(EXTRA_test_config_DEPENDENCIES) + @rm -f test-config$(EXEEXT) + $(AM_V_CCLD)$(test_config_LINK) $(test_config_OBJECTS) $(test_config_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-enums.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcoreconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdialogconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgeglconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpguiconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplangrc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprc-deserialize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprc-serialize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprc-unknown.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpxmlparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-config.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 + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-config.log: test-config$(EXEEXT) + @p='test-config$(EXEEXT)'; \ + b='test-config'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +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 + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +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: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +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 clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/config-enums.Po + -rm -f ./$(DEPDIR)/gimpconfig-dump.Po + -rm -f ./$(DEPDIR)/gimpconfig-file.Po + -rm -f ./$(DEPDIR)/gimpconfig-utils.Po + -rm -f ./$(DEPDIR)/gimpcoreconfig.Po + -rm -f ./$(DEPDIR)/gimpdialogconfig.Po + -rm -f ./$(DEPDIR)/gimpdisplayconfig.Po + -rm -f ./$(DEPDIR)/gimpdisplayoptions.Po + -rm -f ./$(DEPDIR)/gimpgeglconfig.Po + -rm -f ./$(DEPDIR)/gimpguiconfig.Po + -rm -f ./$(DEPDIR)/gimplangrc.Po + -rm -f ./$(DEPDIR)/gimppluginconfig.Po + -rm -f ./$(DEPDIR)/gimprc-deserialize.Po + -rm -f ./$(DEPDIR)/gimprc-serialize.Po + -rm -f ./$(DEPDIR)/gimprc-unknown.Po + -rm -f ./$(DEPDIR)/gimprc.Po + -rm -f ./$(DEPDIR)/gimpxmlparser.Po + -rm -f ./$(DEPDIR)/test-config.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)/config-enums.Po + -rm -f ./$(DEPDIR)/gimpconfig-dump.Po + -rm -f ./$(DEPDIR)/gimpconfig-file.Po + -rm -f ./$(DEPDIR)/gimpconfig-utils.Po + -rm -f ./$(DEPDIR)/gimpcoreconfig.Po + -rm -f ./$(DEPDIR)/gimpdialogconfig.Po + -rm -f ./$(DEPDIR)/gimpdisplayconfig.Po + -rm -f ./$(DEPDIR)/gimpdisplayoptions.Po + -rm -f ./$(DEPDIR)/gimpgeglconfig.Po + -rm -f ./$(DEPDIR)/gimpguiconfig.Po + -rm -f ./$(DEPDIR)/gimplangrc.Po + -rm -f ./$(DEPDIR)/gimppluginconfig.Po + -rm -f ./$(DEPDIR)/gimprc-deserialize.Po + -rm -f ./$(DEPDIR)/gimprc-serialize.Po + -rm -f ./$(DEPDIR)/gimprc-unknown.Po + -rm -f ./$(DEPDIR)/gimprc.Po + -rm -f ./$(DEPDIR)/gimpxmlparser.Po + -rm -f ./$(DEPDIR)/test-config.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: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + 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 recheck tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +xgen-cec: $(srcdir)/config-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"config-enums.h\"\n#include\"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/config-enums.c: xgen-cec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/config/config-enums.c b/app/config/config-enums.c new file mode 100644 index 0000000..aefdfb2 --- /dev/null +++ b/app/config/config-enums.c @@ -0,0 +1,397 @@ + +/* Generated data (by gimp-mkenums) */ + +#include "config.h" +#include +#include "libgimpbase/gimpbase.h" +#include "config-enums.h" +#include"gimp-intl.h" + +/* enumerations from "config-enums.h" */ +GType +gimp_canvas_padding_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CANVAS_PADDING_MODE_DEFAULT, "GIMP_CANVAS_PADDING_MODE_DEFAULT", "default" }, + { GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK, "GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK", "light-check" }, + { GIMP_CANVAS_PADDING_MODE_DARK_CHECK, "GIMP_CANVAS_PADDING_MODE_DARK_CHECK", "dark-check" }, + { GIMP_CANVAS_PADDING_MODE_CUSTOM, "GIMP_CANVAS_PADDING_MODE_CUSTOM", "custom" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CANVAS_PADDING_MODE_DEFAULT, NC_("canvas-padding-mode", "From theme"), NULL }, + { GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK, NC_("canvas-padding-mode", "Light check color"), NULL }, + { GIMP_CANVAS_PADDING_MODE_DARK_CHECK, NC_("canvas-padding-mode", "Dark check color"), NULL }, + { GIMP_CANVAS_PADDING_MODE_CUSTOM, NC_("canvas-padding-mode", "Custom color"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCanvasPaddingMode", values); + gimp_type_set_translation_context (type, "canvas-padding-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_cursor_format_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CURSOR_FORMAT_BITMAP, "GIMP_CURSOR_FORMAT_BITMAP", "bitmap" }, + { GIMP_CURSOR_FORMAT_PIXBUF, "GIMP_CURSOR_FORMAT_PIXBUF", "pixbuf" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CURSOR_FORMAT_BITMAP, NC_("cursor-format", "Black & white"), NULL }, + { GIMP_CURSOR_FORMAT_PIXBUF, NC_("cursor-format", "Fancy"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCursorFormat", values); + gimp_type_set_translation_context (type, "cursor-format"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_cursor_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CURSOR_MODE_TOOL_ICON, "GIMP_CURSOR_MODE_TOOL_ICON", "tool-icon" }, + { GIMP_CURSOR_MODE_TOOL_CROSSHAIR, "GIMP_CURSOR_MODE_TOOL_CROSSHAIR", "tool-crosshair" }, + { GIMP_CURSOR_MODE_CROSSHAIR, "GIMP_CURSOR_MODE_CROSSHAIR", "crosshair" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CURSOR_MODE_TOOL_ICON, NC_("cursor-mode", "Tool icon"), NULL }, + { GIMP_CURSOR_MODE_TOOL_CROSSHAIR, NC_("cursor-mode", "Tool icon with crosshair"), NULL }, + { GIMP_CURSOR_MODE_CROSSHAIR, NC_("cursor-mode", "Crosshair only"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCursorMode", values); + gimp_type_set_translation_context (type, "cursor-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_export_file_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_EXPORT_FILE_PNG, "GIMP_EXPORT_FILE_PNG", "png" }, + { GIMP_EXPORT_FILE_JPG, "GIMP_EXPORT_FILE_JPG", "jpg" }, + { GIMP_EXPORT_FILE_ORA, "GIMP_EXPORT_FILE_ORA", "ora" }, + { GIMP_EXPORT_FILE_PSD, "GIMP_EXPORT_FILE_PSD", "psd" }, + { GIMP_EXPORT_FILE_PDF, "GIMP_EXPORT_FILE_PDF", "pdf" }, + { GIMP_EXPORT_FILE_TIF, "GIMP_EXPORT_FILE_TIF", "tif" }, + { GIMP_EXPORT_FILE_BMP, "GIMP_EXPORT_FILE_BMP", "bmp" }, + { GIMP_EXPORT_FILE_WEBP, "GIMP_EXPORT_FILE_WEBP", "webp" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_EXPORT_FILE_PNG, NC_("export-file-type", "PNG Image"), NULL }, + { GIMP_EXPORT_FILE_JPG, NC_("export-file-type", "JPEG Image"), NULL }, + { GIMP_EXPORT_FILE_ORA, NC_("export-file-type", "OpenRaster Image"), NULL }, + { GIMP_EXPORT_FILE_PSD, NC_("export-file-type", "Photoshop Image"), NULL }, + { GIMP_EXPORT_FILE_PDF, NC_("export-file-type", "Portable Document Format"), NULL }, + { GIMP_EXPORT_FILE_TIF, NC_("export-file-type", "TIFF Image"), NULL }, + { GIMP_EXPORT_FILE_BMP, NC_("export-file-type", "Windows BMP Image"), NULL }, + { GIMP_EXPORT_FILE_WEBP, NC_("export-file-type", "WebP Image"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpExportFileType", values); + gimp_type_set_translation_context (type, "export-file-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_handedness_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDEDNESS_LEFT, "GIMP_HANDEDNESS_LEFT", "left" }, + { GIMP_HANDEDNESS_RIGHT, "GIMP_HANDEDNESS_RIGHT", "right" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDEDNESS_LEFT, NC_("handedness", "Left-handed"), NULL }, + { GIMP_HANDEDNESS_RIGHT, NC_("handedness", "Right-handed"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpHandedness", values); + gimp_type_set_translation_context (type, "handedness"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_help_browser_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HELP_BROWSER_GIMP, "GIMP_HELP_BROWSER_GIMP", "gimp" }, + { GIMP_HELP_BROWSER_WEB_BROWSER, "GIMP_HELP_BROWSER_WEB_BROWSER", "web-browser" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HELP_BROWSER_GIMP, NC_("help-browser-type", "GIMP help browser"), NULL }, + { GIMP_HELP_BROWSER_WEB_BROWSER, NC_("help-browser-type", "Web browser"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpHelpBrowserType", values); + gimp_type_set_translation_context (type, "help-browser-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_icon_size_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ICON_SIZE_AUTO, "GIMP_ICON_SIZE_AUTO", "auto" }, + { GIMP_ICON_SIZE_THEME, "GIMP_ICON_SIZE_THEME", "theme" }, + { GIMP_ICON_SIZE_SMALL, "GIMP_ICON_SIZE_SMALL", "small" }, + { GIMP_ICON_SIZE_MEDIUM, "GIMP_ICON_SIZE_MEDIUM", "medium" }, + { GIMP_ICON_SIZE_LARGE, "GIMP_ICON_SIZE_LARGE", "large" }, + { GIMP_ICON_SIZE_HUGE, "GIMP_ICON_SIZE_HUGE", "huge" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ICON_SIZE_AUTO, NC_("icon-size", "Guess ideal size"), NULL }, + { GIMP_ICON_SIZE_THEME, NC_("icon-size", "Theme-set size"), NULL }, + { GIMP_ICON_SIZE_SMALL, NC_("icon-size", "Small size"), NULL }, + { GIMP_ICON_SIZE_MEDIUM, NC_("icon-size", "Medium size"), NULL }, + { GIMP_ICON_SIZE_LARGE, NC_("icon-size", "Large size"), NULL }, + { GIMP_ICON_SIZE_HUGE, NC_("icon-size", "Huge size"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpIconSize", values); + gimp_type_set_translation_context (type, "icon-size"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_position_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_POSITION_TOP, "GIMP_POSITION_TOP", "top" }, + { GIMP_POSITION_BOTTOM, "GIMP_POSITION_BOTTOM", "bottom" }, + { GIMP_POSITION_LEFT, "GIMP_POSITION_LEFT", "left" }, + { GIMP_POSITION_RIGHT, "GIMP_POSITION_RIGHT", "right" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_POSITION_TOP, NC_("position", "Top"), NULL }, + { GIMP_POSITION_BOTTOM, NC_("position", "Bottom"), NULL }, + { GIMP_POSITION_LEFT, NC_("position", "Left"), NULL }, + { GIMP_POSITION_RIGHT, NC_("position", "Right"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpPosition", values); + gimp_type_set_translation_context (type, "position"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_space_bar_action_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_SPACE_BAR_ACTION_NONE, "GIMP_SPACE_BAR_ACTION_NONE", "none" }, + { GIMP_SPACE_BAR_ACTION_PAN, "GIMP_SPACE_BAR_ACTION_PAN", "pan" }, + { GIMP_SPACE_BAR_ACTION_MOVE, "GIMP_SPACE_BAR_ACTION_MOVE", "move" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_SPACE_BAR_ACTION_NONE, NC_("space-bar-action", "No action"), NULL }, + { GIMP_SPACE_BAR_ACTION_PAN, NC_("space-bar-action", "Pan view"), NULL }, + { GIMP_SPACE_BAR_ACTION_MOVE, NC_("space-bar-action", "Switch to Move tool"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpSpaceBarAction", values); + gimp_type_set_translation_context (type, "space-bar-action"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_tool_group_menu_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK, "GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK", "click" }, + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER, "GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER", "hover" }, + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN, "GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN", "hover-single-column" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK, NC_("tool-group-menu-mode", "Show on click"), NULL }, + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER, NC_("tool-group-menu-mode", "Show on hover"), NULL }, + { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN, NC_("tool-group-menu-mode", "Show on hover in single column"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpToolGroupMenuMode", values); + gimp_type_set_translation_context (type, "tool-group-menu-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_window_hint_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_WINDOW_HINT_NORMAL, "GIMP_WINDOW_HINT_NORMAL", "normal" }, + { GIMP_WINDOW_HINT_UTILITY, "GIMP_WINDOW_HINT_UTILITY", "utility" }, + { GIMP_WINDOW_HINT_KEEP_ABOVE, "GIMP_WINDOW_HINT_KEEP_ABOVE", "keep-above" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_WINDOW_HINT_NORMAL, NC_("window-hint", "Normal window"), NULL }, + { GIMP_WINDOW_HINT_UTILITY, NC_("window-hint", "Utility window"), NULL }, + { GIMP_WINDOW_HINT_KEEP_ABOVE, NC_("window-hint", "Keep above"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpWindowHint", values); + gimp_type_set_translation_context (type, "window-hint"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_zoom_quality_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ZOOM_QUALITY_LOW, "GIMP_ZOOM_QUALITY_LOW", "low" }, + { GIMP_ZOOM_QUALITY_HIGH, "GIMP_ZOOM_QUALITY_HIGH", "high" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ZOOM_QUALITY_LOW, NC_("zoom-quality", "Low"), NULL }, + { GIMP_ZOOM_QUALITY_HIGH, NC_("zoom-quality", "High"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpZoomQuality", values); + gimp_type_set_translation_context (type, "zoom-quality"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + + +/* Generated data ends here */ + diff --git a/app/config/config-enums.h b/app/config/config-enums.h new file mode 100644 index 0000000..c2d30bb --- /dev/null +++ b/app/config/config-enums.h @@ -0,0 +1,173 @@ +/* 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 . + */ + +#ifndef __CONFIG_ENUMS_H__ +#define __CONFIG_ENUMS_H__ + + +#define GIMP_TYPE_CANVAS_PADDING_MODE (gimp_canvas_padding_mode_get_type ()) + +GType gimp_canvas_padding_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CANVAS_PADDING_MODE_DEFAULT, /*< desc="From theme" >*/ + GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK, /*< desc="Light check color" >*/ + GIMP_CANVAS_PADDING_MODE_DARK_CHECK, /*< desc="Dark check color" >*/ + GIMP_CANVAS_PADDING_MODE_CUSTOM, /*< desc="Custom color" >*/ + GIMP_CANVAS_PADDING_MODE_RESET = -1 /*< skip >*/ +} GimpCanvasPaddingMode; + + +#define GIMP_TYPE_CURSOR_FORMAT (gimp_cursor_format_get_type ()) + +GType gimp_cursor_format_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CURSOR_FORMAT_BITMAP, /*< desc="Black & white" >*/ + GIMP_CURSOR_FORMAT_PIXBUF /*< desc="Fancy" >*/ +} GimpCursorFormat; + + +#define GIMP_TYPE_CURSOR_MODE (gimp_cursor_mode_get_type ()) + +GType gimp_cursor_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CURSOR_MODE_TOOL_ICON, /*< desc="Tool icon" >*/ + GIMP_CURSOR_MODE_TOOL_CROSSHAIR, /*< desc="Tool icon with crosshair" >*/ + GIMP_CURSOR_MODE_CROSSHAIR /*< desc="Crosshair only" >*/ +} GimpCursorMode; + + +#define GIMP_TYPE_EXPORT_FILE_TYPE (gimp_export_file_type_get_type ()) + +GType gimp_export_file_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_EXPORT_FILE_PNG, /*< desc="PNG Image" >*/ + GIMP_EXPORT_FILE_JPG, /*< desc="JPEG Image" >*/ + GIMP_EXPORT_FILE_ORA, /*< desc="OpenRaster Image" >*/ + GIMP_EXPORT_FILE_PSD, /*< desc="Photoshop Image" >*/ + GIMP_EXPORT_FILE_PDF, /*< desc="Portable Document Format" >*/ + GIMP_EXPORT_FILE_TIF, /*< desc="TIFF Image" >*/ + GIMP_EXPORT_FILE_BMP, /*< desc="Windows BMP Image" >*/ + GIMP_EXPORT_FILE_WEBP, /*< desc="WebP Image" >*/ +} GimpExportFileType; + + +#define GIMP_TYPE_HANDEDNESS (gimp_handedness_get_type ()) + +GType gimp_handedness_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDEDNESS_LEFT, /*< desc="Left-handed" >*/ + GIMP_HANDEDNESS_RIGHT /*< desc="Right-handed" >*/ +} GimpHandedness; + + +#define GIMP_TYPE_HELP_BROWSER_TYPE (gimp_help_browser_type_get_type ()) + +GType gimp_help_browser_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HELP_BROWSER_GIMP, /*< desc="GIMP help browser" >*/ + GIMP_HELP_BROWSER_WEB_BROWSER /*< desc="Web browser" >*/ +} GimpHelpBrowserType; + + +#define GIMP_TYPE_ICON_SIZE (gimp_icon_size_get_type ()) + +GType gimp_icon_size_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_ICON_SIZE_AUTO, /*< desc="Guess ideal size" > */ + GIMP_ICON_SIZE_THEME, /*< desc="Theme-set size" > */ + GIMP_ICON_SIZE_SMALL, /*< desc="Small size" > */ + GIMP_ICON_SIZE_MEDIUM, /*< desc="Medium size" > */ + GIMP_ICON_SIZE_LARGE, /*< desc="Large size" > */ + GIMP_ICON_SIZE_HUGE /*< desc="Huge size" > */ +} GimpIconSize; + + +#define GIMP_TYPE_POSITION (gimp_position_get_type ()) + +GType gimp_position_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_POSITION_TOP, /*< desc="Top" >*/ + GIMP_POSITION_BOTTOM, /*< desc="Bottom" >*/ + GIMP_POSITION_LEFT, /*< desc="Left" >*/ + GIMP_POSITION_RIGHT /*< desc="Right" >*/ +} GimpPosition; + + +#define GIMP_TYPE_SPACE_BAR_ACTION (gimp_space_bar_action_get_type ()) + +GType gimp_space_bar_action_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_SPACE_BAR_ACTION_NONE, /*< desc="No action" >*/ + GIMP_SPACE_BAR_ACTION_PAN, /*< desc="Pan view" >*/ + GIMP_SPACE_BAR_ACTION_MOVE /*< desc="Switch to Move tool" >*/ +} GimpSpaceBarAction; + + +#define GIMP_TYPE_TOOL_GROUP_MENU_MODE (gimp_tool_group_menu_mode_get_type ()) + +GType gimp_tool_group_menu_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK, /*< desc="Show on click" >*/ + GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER, /*< desc="Show on hover" >*/ + GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN /*< desc="Show on hover in single column" >*/ +} GimpToolGroupMenuMode; + + +#define GIMP_TYPE_WINDOW_HINT (gimp_window_hint_get_type ()) + +GType gimp_window_hint_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_WINDOW_HINT_NORMAL, /*< desc="Normal window" >*/ + GIMP_WINDOW_HINT_UTILITY, /*< desc="Utility window" >*/ + GIMP_WINDOW_HINT_KEEP_ABOVE /*< desc="Keep above" >*/ +} GimpWindowHint; + + +#define GIMP_TYPE_ZOOM_QUALITY (gimp_zoom_quality_get_type ()) + +GType gimp_zoom_quality_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_ZOOM_QUALITY_LOW, /*< desc="Low" >*/ + GIMP_ZOOM_QUALITY_HIGH /*< desc="High" >*/ +} GimpZoomQuality; + + +#endif /* __CONFIG_ENUMS_H__ */ diff --git a/app/config/config-types.h b/app/config/config-types.h new file mode 100644 index 0000000..40b0802 --- /dev/null +++ b/app/config/config-types.h @@ -0,0 +1,59 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpConfig typedefs + * Copyright (C) 2001-2002 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 . + */ + +#ifndef __CONFIG_TYPES_H__ +#define __CONFIG_TYPES_H__ + + +#include "libgimpconfig/gimpconfigtypes.h" + +#include "config/config-enums.h" + + +#define GIMP_OPACITY_TRANSPARENT 0.0 +#define GIMP_OPACITY_OPAQUE 1.0 + + +typedef struct _GimpGeglConfig GimpGeglConfig; +typedef struct _GimpCoreConfig GimpCoreConfig; +typedef struct _GimpDisplayConfig GimpDisplayConfig; +typedef struct _GimpGuiConfig GimpGuiConfig; +typedef struct _GimpDialogConfig GimpDialogConfig; +typedef struct _GimpLangRc GimpLangRc; +typedef struct _GimpPluginConfig GimpPluginConfig; +typedef struct _GimpRc GimpRc; + +typedef struct _GimpXmlParser GimpXmlParser; + +typedef struct _GimpDisplayOptions GimpDisplayOptions; + +/* should be in core/core-types.h */ +typedef struct _GimpGrid GimpGrid; +typedef struct _GimpTemplate GimpTemplate; + + +/* for now these are defines, but can be turned into something + * fancier for nicer debugging + */ +#define gimp_assert g_assert +#define gimp_assert_not_reached g_assert_not_reached + + +#endif /* __CONFIG_TYPES_H__ */ diff --git a/app/config/gimpconfig-dump.c b/app/config/gimpconfig-dump.c new file mode 100644 index 0000000..1a2b376 --- /dev/null +++ b/app/config/gimpconfig-dump.c @@ -0,0 +1,644 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpConfig object property dumper. + * Copyright (C) 2001-2006 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#ifdef G_OS_WIN32 +#include +#else +#include +#endif + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimpconfig-dump.h" +#include "gimprc.h" + + + +static void dump_gimprc_system (GimpConfig *rc, + GimpConfigWriter *writer, + GOutputStream *output); +static void dump_gimprc_manpage (GimpConfig *rc, + GimpConfigWriter *writer, + GOutputStream *output); +static gchar * dump_describe_param (GParamSpec *param_spec); +static void dump_with_linebreaks (GOutputStream *output, + const gchar *text); + + +gboolean +gimp_config_dump (GObject *gimp, + GimpConfigDumpFormat format) +{ + GOutputStream *output; + GimpConfigWriter *writer; + GimpConfig *rc; + + g_return_val_if_fail (G_IS_OBJECT (gimp), FALSE); + + rc = g_object_new (GIMP_TYPE_RC, + "gimp", gimp, + NULL); + +#ifdef G_OS_WIN32 + output = g_win32_output_stream_new ((gpointer) 1, FALSE); +#else + output = g_unix_output_stream_new (1, FALSE); +#endif + + writer = gimp_config_writer_new_stream (output, NULL, NULL); + + switch (format) + { + case GIMP_CONFIG_DUMP_NONE: + break; + + case GIMP_CONFIG_DUMP_GIMPRC: + gimp_config_writer_comment (writer, + "Dump of the GIMP default configuration"); + gimp_config_writer_linefeed (writer); + gimp_config_serialize_properties (rc, writer); + gimp_config_writer_linefeed (writer); + break; + + case GIMP_CONFIG_DUMP_GIMPRC_SYSTEM: + dump_gimprc_system (rc, writer, output); + break; + + case GIMP_CONFIG_DUMP_GIMPRC_MANPAGE: + dump_gimprc_manpage (rc, writer, output); + break; + } + + gimp_config_writer_finish (writer, NULL, NULL); + g_object_unref (output); + g_object_unref (rc); + + return TRUE; +} + + +static const gchar system_gimprc_header[] = +"This is the system-wide gimprc file. Any change made in this file " +"will affect all users of this system, provided that they are not " +"overriding the default values in their personal gimprc file.\n" +"\n" +"Lines that start with a '#' are comments. Blank lines are ignored.\n" +"\n" +"By default everything in this file is commented out. The file then " +"documents the default values and shows what changes are possible.\n" +"\n" +"The variable ${gimp_dir} is set to the value of the environment " +"variable GIMP2_DIRECTORY or, if that is not set, the compiled-in " +"default value is used. If GIMP2_DIRECTORY is not an absolute path, " +"it is interpreted relative to your home directory."; + +static void +dump_gimprc_system (GimpConfig *rc, + GimpConfigWriter *writer, + GOutputStream *output) +{ + GObjectClass *klass; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + gimp_config_writer_comment (writer, system_gimprc_header); + gimp_config_writer_linefeed (writer); + + klass = G_OBJECT_GET_CLASS (rc); + property_specs = g_object_class_list_properties (klass, &n_property_specs); + + for (i = 0; i < n_property_specs; i++) + { + GParamSpec *prop_spec = property_specs[i]; + gchar *comment; + + if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE)) + continue; + + if (prop_spec->flags & GIMP_CONFIG_PARAM_IGNORE) + continue; + + comment = dump_describe_param (prop_spec); + if (comment) + { + gimp_config_writer_comment (writer, comment); + g_free (comment); + } + + gimp_config_writer_comment_mode (writer, TRUE); + gimp_config_writer_linefeed (writer); + + if (! strcmp (prop_spec->name, "num-processors")) + { + gimp_config_writer_open (writer, "num-processors"); + gimp_config_writer_printf (writer, "1"); + gimp_config_writer_close (writer); + } + else if (! strcmp (prop_spec->name, "tile-cache-size")) + { + gimp_config_writer_open (writer, "tile-cache-size"); + gimp_config_writer_printf (writer, "2g"); + gimp_config_writer_close (writer); + } + else if (! strcmp (prop_spec->name, "undo-size")) + { + gimp_config_writer_open (writer, "undo-size"); + gimp_config_writer_printf (writer, "1g"); + gimp_config_writer_close (writer); + } + else if (! strcmp (prop_spec->name, "mypaint-brush-path")) + { + gchar *path = g_strdup_printf ("@mypaint_brushes_dir@%s" + "~/.mypaint/brushes", + G_SEARCHPATH_SEPARATOR_S); + + gimp_config_writer_open (writer, "mypaint-brush-path"); + gimp_config_writer_string (writer, path); + gimp_config_writer_close (writer); + + g_free (path); + } + else + { + gimp_config_serialize_property (rc, prop_spec, writer); + } + + gimp_config_writer_comment_mode (writer, FALSE); + gimp_config_writer_linefeed (writer); + } + + g_free (property_specs); +} + + +static const gchar man_page_header[] = +".\\\" This man-page is auto-generated by gimp --dump-gimprc-manpage.\n" +"\n" +".TH GIMPRC 5 \"Version @GIMP_VERSION@\" \"GIMP Manual Pages\"\n" +".SH NAME\n" +"gimprc \\- gimp configuration file\n" +".SH DESCRIPTION\n" +"The\n" +".B gimprc\n" +"file is a configuration file read by GIMP when it starts up. There\n" +"are two of these: one system-wide one stored in\n" +"@gimpsysconfdir@/gimprc and a per-user @manpage_gimpdir@/gimprc\n" +"which may override system settings.\n" +"\n" +"Comments are introduced by a hash sign (#), and continue until the end\n" +"of the line. Blank lines are ignored.\n" +"\n" +"The\n" +".B gimprc\n" +"file associates values with properties. These properties may be set\n" +"by lisp-like assignments of the form:\n" +".IP\n" +"\\f3(\\f2property\\-name\\ value\\f3)\\f1\n" +".TP\n" +"where:\n" +".TP 10\n" +".I property\\-name\n" +"is one of the property names described below.\n" +".TP\n" +".I value\n" +"is the value the property is to be set to.\n" +".PP\n" +"\n" +"Either spaces or tabs may be used to separate the name from the value.\n" +".PP\n" +".SH PROPERTIES\n" +"Valid properties and their default values are:\n" +"\n"; + +static const gchar *man_page_path = +".PP\n" +".SH PATH EXPANSION\n" +"Strings of type PATH are expanded in a manner similar to\n" +".BR bash (1).\n" +"Specifically: tilde (~) is expanded to the user's home directory. Note that\n" +"the bash feature of being able to refer to other user's home directories\n" +"by writing ~userid/ is not valid in this file.\n" +"\n" +"${variable} is expanded to the current value of an environment variable.\n" +"There are a few variables that are pre-defined:\n" +".TP\n" +".I gimp_dir\n" +"The personal gimp directory which is set to the value of the environment\n" +"variable GIMP2_DIRECTORY or to @manpage_gimpdir@.\n" +".TP\n" +".I gimp_data_dir\n" +"Base for paths to shareable data, which is set to the value of the\n" +"environment variable GIMP2_DATADIR or to the compiled-in default value\n" +"@gimpdatadir@.\n" +".TP\n" +".I gimp_plug_in_dir\n" +"Base to paths for architecture-specific plug-ins and modules, which is set\n" +"to the value of the environment variable GIMP2_PLUGINDIR or to the\n" +"compiled-in default value @gimpplugindir@.\n" +".TP\n" +".I gimp_sysconf_dir\n" +"Path to configuration files, which is set to the value of the environment\n" +"variable GIMP2_SYSCONFDIR or to the compiled-in default value \n" +"@gimpsysconfdir@.\n" +".TP\n" +".I gimp_cache_dir\n" +"Path to cached files, which is set to the value of the environment\n" +"variable GIMP2_CACHEDIR or to the system default for per-user cached files.\n" +".TP\n" +".I gimp_temp_dir\n" +"Path to temporary files, which is set to the value of the environment\n" +"variable GIMP2_TEMPDIR or to the system default for temporary files.\n" +"\n"; + +static const gchar man_page_footer[] = +".SH FILES\n" +".TP\n" +".I @gimpsysconfdir@/gimprc\n" +"System-wide configuration file\n" +".TP\n" +".I @manpage_gimpdir@/gimprc\n" +"Per-user configuration file\n" +"\n" +".SH \"SEE ALSO\"\n" +".BR gimp (1)\n"; + + +static void +dump_gimprc_manpage (GimpConfig *rc, + GimpConfigWriter *writer, + GOutputStream *output) +{ + GObjectClass *klass; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + + g_output_stream_printf (output, NULL, NULL, NULL, + "%s", man_page_header); + + klass = G_OBJECT_GET_CLASS (rc); + property_specs = g_object_class_list_properties (klass, &n_property_specs); + + for (i = 0; i < n_property_specs; i++) + { + GParamSpec *prop_spec = property_specs[i]; + gchar *desc; + gboolean success; + + if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE)) + continue; + + if (prop_spec->flags & GIMP_CONFIG_PARAM_IGNORE) + continue; + + g_output_stream_printf (output, NULL, NULL, NULL, + ".TP\n"); + + if (! strcmp (prop_spec->name, "num-processors")) + { + gimp_config_writer_open (writer, "num-processors"); + gimp_config_writer_printf (writer, "1"); + gimp_config_writer_close (writer); + + success = TRUE; + } + else if (! strcmp (prop_spec->name, "tile-cache-size")) + { + gimp_config_writer_open (writer, "tile-cache-size"); + gimp_config_writer_printf (writer, "2g"); + gimp_config_writer_close (writer); + + success = TRUE; + } + else if (! strcmp (prop_spec->name, "undo-size")) + { + gimp_config_writer_open (writer, "undo-size"); + gimp_config_writer_printf (writer, "1g"); + gimp_config_writer_close (writer); + + success = TRUE; + } + else if (! strcmp (prop_spec->name, "mypaint-brush-path")) + { + gchar *path = g_strdup_printf ("@mypaint_brushes_dir@%s" + "~/.mypaint/brushes", + G_SEARCHPATH_SEPARATOR_S); + + gimp_config_writer_open (writer, "mypaint-brush-path"); + gimp_config_writer_string (writer, path); + gimp_config_writer_close (writer); + + g_free (path); + + success = TRUE; + } + else + { + success = gimp_config_serialize_property (rc, prop_spec, writer); + } + + if (success) + { + g_output_stream_printf (output, NULL, NULL, NULL, + "\n"); + + desc = dump_describe_param (prop_spec); + + dump_with_linebreaks (output, desc); + + g_output_stream_printf (output, NULL, NULL, NULL, + "\n"); + + g_free (desc); + } + } + + g_free (property_specs); + + g_output_stream_printf (output, NULL, NULL, NULL, + "%s", man_page_path); + g_output_stream_printf (output, NULL, NULL, NULL, + "%s", man_page_footer); +} + + +static const gchar display_format_description[] = +"This is a format string; certain % character sequences are recognised and " +"expanded as follows:\n" +"\n" +"%% literal percent sign\n" +"%f bare filename, or \"Untitled\"\n" +"%F full path to file, or \"Untitled\"\n" +"%p PDB image id\n" +"%i view instance number\n" +"%t image type (RGB, grayscale, indexed)\n" +"%z zoom factor as a percentage\n" +"%s source scale factor\n" +"%d destination scale factor\n" +"%Dx expands to x if the image is dirty, the empty string otherwise\n" +"%Cx expands to x if the image is clean, the empty string otherwise\n" +"%B expands to (modified) if the image is dirty, the empty string otherwise\n" +"%A expands to (clean) if the image is clean, the empty string otherwise\n" +"%Nx expands to x if the image is export-dirty, the empty string otherwise\n" +"%Ex expands to x if the image is export-clean, the empty string otherwise\n" +"%l the number of layers\n" +"%L the number of layers (long form)\n" +"%m memory used by the image\n" +"%n the name of the active layer/channel\n" +"%P the PDB id of the active layer/channel\n" +"%w image width in pixels\n" +"%W image width in real-world units\n" +"%h image height in pixels\n" +"%H image height in real-world units\n" +"%M the image size expressed in megapixels\n" +"%u unit symbol\n" +"%U unit abbreviation\n" +"%x the width of the active layer/channel in pixels\n" +"%X the width of the active layer/channel in real-world units\n" +"%y the height of the active layer/channel in pixels\n" +"%Y the height of the active layer/channel in real-world units\n" +"%o the name of the image's color profile\n\n"; + + +static gchar * +dump_describe_param (GParamSpec *param_spec) +{ + const gchar *blurb = g_param_spec_get_blurb (param_spec); + const gchar *values = NULL; + + if (!blurb) + { + g_warning ("FIXME: Property '%s' has no blurb.", param_spec->name); + + blurb = g_strdup_printf ("The %s property has no description.", + param_spec->name); + } + + if (GIMP_IS_PARAM_SPEC_RGB (param_spec)) + { + if (gimp_param_spec_rgb_has_alpha (param_spec)) + values = + "The color is specified in the form (color-rgba red green blue " + "alpha) with channel values as floats in the range of 0.0 to 1.0."; + else + values = + "The color is specified in the form (color-rgb red green blue) " + "with channel values as floats in the range of 0.0 to 1.0."; + } + else if (GIMP_IS_PARAM_SPEC_MEMSIZE (param_spec)) + { + values = + "The integer size can contain a suffix of 'B', 'K', 'M' or 'G' which " + "makes GIMP interpret the size as being specified in bytes, kilobytes, " + "megabytes or gigabytes. If no suffix is specified the size defaults " + "to being specified in kilobytes."; + } + else if (GIMP_IS_PARAM_SPEC_CONFIG_PATH (param_spec)) + { + switch (gimp_param_spec_config_path_type (param_spec)) + { + case GIMP_CONFIG_PATH_FILE: + values = "This is a single filename."; + break; + + case GIMP_CONFIG_PATH_FILE_LIST: + switch (G_SEARCHPATH_SEPARATOR) + { + case ':': + values = "This is a colon-separated list of files."; + break; + case ';': + values = "This is a semicolon-separated list of files."; + break; + default: + g_warning ("unhandled G_SEARCHPATH_SEPARATOR value"); + break; + } + break; + + case GIMP_CONFIG_PATH_DIR: + values = "This is a single folder."; + break; + + case GIMP_CONFIG_PATH_DIR_LIST: + switch (G_SEARCHPATH_SEPARATOR) + { + case ':': + values = "This is a colon-separated list of folders to search."; + break; + case ';': + values = "This is a semicolon-separated list of folders to search."; + break; + default: + g_warning ("unhandled G_SEARCHPATH_SEPARATOR value"); + break; + } + break; + } + } + else if (GIMP_IS_PARAM_SPEC_UNIT (param_spec)) + { + values = + "The unit can be one inches, millimeters, points or picas plus " + "those in your user units database."; + } + else if (g_type_is_a (param_spec->value_type, GIMP_TYPE_CONFIG)) + { + values = "This is a parameter list."; + } + else + { + switch (G_TYPE_FUNDAMENTAL (param_spec->value_type)) + { + case G_TYPE_BOOLEAN: + values = "Possible values are yes and no."; + break; + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_LONG: + case G_TYPE_ULONG: + values = "This is an integer value."; + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + values = "This is a float value."; + break; + case G_TYPE_STRING: + /* eek */ + if (strcmp (g_param_spec_get_name (param_spec), "image-title-format") + && + strcmp (g_param_spec_get_name (param_spec), "image-status-format")) + { + values = "This is a string value."; + } + else + { + values = display_format_description; + } + break; + case G_TYPE_ENUM: + { + GEnumClass *enum_class; + GEnumValue *enum_value; + GString *str; + gint i; + + enum_class = g_type_class_peek (param_spec->value_type); + + str = g_string_new (blurb); + + g_string_append (str, " Possible values are "); + + for (i = 0, enum_value = enum_class->values; + i < enum_class->n_values; + i++, enum_value++) + { + g_string_append (str, enum_value->value_nick); + + switch (enum_class->n_values - i) + { + case 1: + g_string_append_c (str, '.'); + break; + case 2: + g_string_append (str, " and "); + break; + default: + g_string_append (str, ", "); + break; + } + } + + return g_string_free (str, FALSE); + } + break; + default: + break; + } + } + + if (!values) + g_warning ("FIXME: Can't tell anything about a %s.", + g_type_name (param_spec->value_type)); + + if (strcmp (blurb, "") == 0) + return g_strdup_printf ("%s", values); + else + return g_strdup_printf ("%s %s", blurb, values); +} + + +#define LINE_LENGTH 78 + +static void +dump_with_linebreaks (GOutputStream *output, + const gchar *text) +{ + gint len = strlen (text); + + while (len > 0) + { + const gchar *t; + gint i, space; + + /* groff doesn't like lines to start with a single quote */ + if (*text == '\'') + g_output_stream_printf (output, NULL, NULL, NULL, + "\\&"); /* a zero width space */ + + for (t = text, i = 0, space = 0; + *t != '\n' && (i <= LINE_LENGTH || space == 0) && i < len; + t++, i++) + { + if (g_ascii_isspace (*t)) + space = i; + } + + if (i > LINE_LENGTH && space && *t != '\n') + i = space; + + g_output_stream_write_all (output, text, i, NULL, NULL, NULL); + g_output_stream_printf (output, NULL, NULL, NULL, + "\n"); + + if (*t == '\n') + g_output_stream_printf (output, NULL, NULL, NULL, + ".br\n"); + + i++; + + text += i; + len -= i; + } +} diff --git a/app/config/gimpconfig-dump.h b/app/config/gimpconfig-dump.h new file mode 100644 index 0000000..ead2da3 --- /dev/null +++ b/app/config/gimpconfig-dump.h @@ -0,0 +1,37 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2003 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 . + */ + +#ifndef __GIMP_CONFIG_DUMP_H__ +#define __GIMP_CONFIG_DUMP_H__ + + +typedef enum +{ + GIMP_CONFIG_DUMP_NONE, + GIMP_CONFIG_DUMP_GIMPRC, + GIMP_CONFIG_DUMP_GIMPRC_SYSTEM, + GIMP_CONFIG_DUMP_GIMPRC_MANPAGE +} GimpConfigDumpFormat; + + +gboolean gimp_config_dump (GObject *gimp, + GimpConfigDumpFormat format); + + +#endif /* __GIMP_CONFIG_DUMP_H__ */ diff --git a/app/config/gimpconfig-file.c b/app/config/gimpconfig-file.c new file mode 100644 index 0000000..9823b8f --- /dev/null +++ b/app/config/gimpconfig-file.c @@ -0,0 +1,241 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * File Utitility functions for GimpConfig. + * Copyright (C) 2001-2003 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#ifdef G_OS_WIN32 +#include "libgimpbase/gimpwin32-io.h" +#endif + +#include "config-types.h" + +#include "gimpconfig-file.h" + +#include "gimp-intl.h" + + +gboolean +gimp_config_file_copy (const gchar *source, + const gchar *dest, + const gchar *old_options_pattern, + GRegexEvalCallback update_callback, + GError **error) +{ + gchar buffer[8192]; + FILE *sfile; + FILE *dfile; + GStatBuf stat_buf; + gint nbytes; + gint unwritten_len = 0; + GRegex *old_options_regexp = NULL; + + if (old_options_pattern && update_callback) + { + old_options_regexp = g_regex_new (old_options_pattern, 0, 0, error); + + /* error set by g_regex_new. */ + if (! old_options_regexp) + return FALSE; + } + + sfile = g_fopen (source, "rb"); + if (sfile == 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 (source), g_strerror (errno)); + if (old_options_regexp) + g_regex_unref (old_options_regexp); + return FALSE; + } + + dfile = g_fopen (dest, "wb"); + if (dfile == 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 (dest), g_strerror (errno)); + fclose (sfile); + if (old_options_regexp) + g_regex_unref (old_options_regexp); + return FALSE; + } + + while ((nbytes = fread (buffer + unwritten_len, 1, + sizeof (buffer) - unwritten_len, sfile)) > 0 || unwritten_len) + { + size_t read_len = nbytes + unwritten_len; + size_t write_len; + gchar* eol = NULL; + gchar* write_bytes = NULL; + + if (old_options_regexp && update_callback) + { + eol = g_strrstr_len (buffer, read_len, "\n"); + if (eol) + { + *eol = '\0'; + read_len = strlen (buffer) + 1; + *eol++ = '\n'; + } + else if (! feof (sfile)) + { + gchar format[256]; + + /* We are in unlikely case where a single config line is + * longer than the buffer! + */ + + g_snprintf (format, sizeof (format), + _("Error parsing '%%s': line longer than %s characters."), + G_GINT64_FORMAT); + + g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + format, + gimp_filename_to_utf8 (source), + (gint64) sizeof (buffer)); + + fclose (sfile); + fclose (dfile); + g_regex_unref (old_options_regexp); + return FALSE; + } + + write_bytes = g_regex_replace_eval (old_options_regexp, buffer, + read_len, 0, 0, update_callback, + NULL, error); + if (write_bytes == NULL) + { + /* error already set. */ + fclose (sfile); + fclose (dfile); + g_regex_unref (old_options_regexp); + return FALSE; + } + write_len = strlen (write_bytes); + } + else + { + write_bytes = buffer; + write_len = read_len; + } + + if (fwrite (write_bytes, 1, write_len, dfile) < write_len) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Error writing '%s': %s"), + gimp_filename_to_utf8 (dest), g_strerror (errno)); + if (old_options_regexp && update_callback) + { + g_free (write_bytes); + g_regex_unref (old_options_regexp); + } + fclose (sfile); + fclose (dfile); + return FALSE; + } + + if (old_options_regexp && update_callback) + { + g_free (write_bytes); + + if (eol) + { + unwritten_len = nbytes + unwritten_len - read_len; + memmove (buffer, eol, unwritten_len); + } + else + /* EOF */ + break; + } + } + + if (ferror (sfile)) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Error reading '%s': %s"), + gimp_filename_to_utf8 (source), g_strerror (errno)); + fclose (sfile); + fclose (dfile); + if (old_options_regexp) + g_regex_unref (old_options_regexp); + return FALSE; + } + + fclose (sfile); + + if (fclose (dfile) == EOF) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Error writing '%s': %s"), + gimp_filename_to_utf8 (dest), g_strerror (errno)); + if (old_options_regexp) + g_regex_unref (old_options_regexp); + return FALSE; + } + + if (g_stat (source, &stat_buf) == 0) + { + g_chmod (dest, stat_buf.st_mode); + } + + if (old_options_regexp) + g_regex_unref (old_options_regexp); + return TRUE; +} + +gboolean +gimp_config_file_backup_on_error (GFile *file, + const gchar *name, + GError **error) +{ + gchar *path; + gchar *backup; + gboolean success; + + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + path = g_file_get_path (file); + backup = g_strconcat (path, "~", NULL); + + success = gimp_config_file_copy (path, backup, NULL, NULL, error); + + if (success) + g_message (_("There was an error parsing your '%s' file. " + "Default values will be used. A backup of your " + "configuration has been created at '%s'."), + name, gimp_filename_to_utf8 (backup)); + + g_free (backup); + g_free (path); + + return success; +} diff --git a/app/config/gimpconfig-file.h b/app/config/gimpconfig-file.h new file mode 100644 index 0000000..fc61886 --- /dev/null +++ b/app/config/gimpconfig-file.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * File utitility functions for GimpConfig. + * Copyright (C) 2001-2003 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 . + */ + +#ifndef __GIMP_CONFIG_FILE_H__ +#define __GIMP_CONFIG_FILE_H__ + + +gboolean gimp_config_file_copy (const gchar *source, + const gchar *dest, + const gchar *old_options_regexp, + GRegexEvalCallback update_callback, + GError **error); + +gboolean gimp_config_file_backup_on_error (GFile *file, + const gchar *name, + GError **error); + + +#endif /* __GIMP_CONFIG_FILE_H__ */ diff --git a/app/config/gimpconfig-utils.c b/app/config/gimpconfig-utils.c new file mode 100644 index 0000000..fbe08d3 --- /dev/null +++ b/app/config/gimpconfig-utils.c @@ -0,0 +1,223 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * Utitility functions for GimpConfig. + * Copyright (C) 2001-2003 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 . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimpconfig-utils.h" + + +static void +gimp_config_connect_notify (GObject *src, + GParamSpec *param_spec, + GObject *dest) +{ + if (param_spec->flags & G_PARAM_READABLE) + { + GParamSpec *dest_spec; + + dest_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (dest), + param_spec->name); + + if (dest_spec && + (dest_spec->value_type == param_spec->value_type) && + (dest_spec->flags & G_PARAM_WRITABLE) && + (dest_spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0) + { + GValue value = G_VALUE_INIT; + + g_value_init (&value, param_spec->value_type); + + g_object_get_property (src, param_spec->name, &value); + + g_signal_handlers_block_by_func (dest, + gimp_config_connect_notify, src); + g_object_set_property (dest, param_spec->name, &value); + g_signal_handlers_unblock_by_func (dest, + gimp_config_connect_notify, src); + + g_value_unset (&value); + } + } +} + +/** + * gimp_config_connect: + * @a: a #GObject + * @b: another #GObject + * @property_name: the name of a property to connect or %NULL for all + * + * Connects the two object @a and @b in a way that property changes of + * one are propagated to the other. This is a two-way connection. + * + * If @property_name is %NULL the connection is setup for all + * properties. It is not required that @a and @b are of the same type. + * Only changes on properties that exist in both object classes and + * are of the same value_type are propagated. + **/ +void +gimp_config_connect (GObject *a, + GObject *b, + const gchar *property_name) +{ + gchar *signal_name; + + g_return_if_fail (a != b); + g_return_if_fail (G_IS_OBJECT (a) && G_IS_OBJECT (b)); + + if (property_name) + signal_name = g_strconcat ("notify::", property_name, NULL); + else + signal_name = "notify"; + + g_signal_connect_object (a, signal_name, + G_CALLBACK (gimp_config_connect_notify), + b, 0); + g_signal_connect_object (b, signal_name, + G_CALLBACK (gimp_config_connect_notify), + a, 0); + + if (property_name) + g_free (signal_name); +} + +static void +gimp_config_connect_full_notify (GObject *src, + GParamSpec *param_spec, + GObject *dest) +{ + if (param_spec->flags & G_PARAM_READABLE) + { + gchar *attach_key; + gchar *dest_prop_name; + GParamSpec *dest_spec = NULL; + + attach_key = g_strdup_printf ("%p-%s", dest, param_spec->name); + dest_prop_name = g_object_get_data (src, attach_key); + g_free (attach_key); + + if (dest_prop_name) + dest_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (dest), + dest_prop_name); + + if (dest_spec && + (dest_spec->value_type == param_spec->value_type) && + (dest_spec->flags & G_PARAM_WRITABLE) && + (dest_spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0) + { + GValue value = G_VALUE_INIT; + + g_value_init (&value, param_spec->value_type); + + g_object_get_property (src, param_spec->name, &value); + + g_signal_handlers_block_by_func (dest, + gimp_config_connect_full_notify, src); + g_object_set_property (dest, dest_prop_name, &value); + g_signal_handlers_unblock_by_func (dest, + gimp_config_connect_full_notify, src); + + g_value_unset (&value); + } + } +} + +/** + * gimp_config_connect_full: + * @a: a #GObject + * @b: another #GObject + * @property_name_a: the name of a property of @a to connect + * @property_name_b: the name of a property of @b to connect + * + * Connects the two object @a and @b in a way that property changes of + * one are propagated to the other. This is a two-way connection. + * + * If @property_name is %NULL the connection is setup for all + * properties. It is not required that @a and @b are of the same type. + * Only changes on properties that exist in both object classes and + * are of the same value_type are propagated. + **/ +void +gimp_config_connect_full (GObject *a, + GObject *b, + const gchar *property_name_a, + const gchar *property_name_b) +{ + gchar *signal_name; + gchar *attach_key; + + g_return_if_fail (a != b); + g_return_if_fail (G_IS_OBJECT (a) && G_IS_OBJECT (b)); + g_return_if_fail (property_name_a != NULL); + g_return_if_fail (property_name_b != NULL); + + signal_name = g_strconcat ("notify::", property_name_a, NULL); + attach_key = g_strdup_printf ("%p-%s", b, property_name_a); + + g_signal_connect_object (a, signal_name, + G_CALLBACK (gimp_config_connect_full_notify), + b, 0); + g_object_set_data_full (a, attach_key, g_strdup (property_name_b), + (GDestroyNotify) g_free); + + g_free (signal_name); + g_free (attach_key); + + signal_name = g_strconcat ("notify::", property_name_b, NULL); + attach_key = g_strdup_printf ("%p-%s", a, property_name_b); + + g_signal_connect_object (b, signal_name, + G_CALLBACK (gimp_config_connect_full_notify), + a, 0); + g_object_set_data_full (b, attach_key, g_strdup (property_name_a), + (GDestroyNotify) g_free); + + g_free (signal_name); + g_free (attach_key); +} + +/** + * gimp_config_disconnect: + * @a: a #GObject + * @b: another #GObject + * + * Removes a connection between @dest and @src that was previously set + * up using gimp_config_connect(). + **/ +void +gimp_config_disconnect (GObject *a, + GObject *b) +{ + g_return_if_fail (G_IS_OBJECT (a) && G_IS_OBJECT (b)); + + g_signal_handlers_disconnect_by_func (b, + G_CALLBACK (gimp_config_connect_notify), + a); + g_signal_handlers_disconnect_by_func (a, + G_CALLBACK (gimp_config_connect_notify), + b); +} + diff --git a/app/config/gimpconfig-utils.h b/app/config/gimpconfig-utils.h new file mode 100644 index 0000000..7928ca9 --- /dev/null +++ b/app/config/gimpconfig-utils.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * Utitility functions for GimpConfig. + * Copyright (C) 2001-2003 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 . + */ + +#ifndef __APP_GIMP_CONFIG_UTILS_H__ +#define __APP_GIMP_CONFIG_UTILS_H__ + + +void gimp_config_connect (GObject *a, + GObject *b, + const gchar *property_name); +void gimp_config_connect_full (GObject *a, + GObject *b, + const gchar *property_name_a, + const gchar *property_name_b); +void gimp_config_disconnect (GObject *a, + GObject *b); + + +#endif /* __APP_GIMP_CONFIG_UTILS_H__ */ diff --git a/app/config/gimpcoreconfig.c b/app/config/gimpcoreconfig.c new file mode 100644 index 0000000..823e808 --- /dev/null +++ b/app/config/gimpcoreconfig.c @@ -0,0 +1,1415 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpCoreConfig class + * Copyright (C) 2001 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 . + */ + +#include "config.h" + +#include +#include +#include + +#ifdef G_OS_WIN32 +#include + +/* Constant available since Shell32.dll 5.0 */ +#ifndef CSIDL_LOCAL_APPDATA +#define CSIDL_LOCAL_APPDATA 0x001c +#endif + +#endif + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "core/core-types.h" +#include "core/gimp-utils.h" +#include "core/gimpgrid.h" +#include "core/gimptemplate.h" + +#include "gimprc-blurbs.h" +#include "gimpcoreconfig.h" + +#include "gimp-intl.h" + + +#define GIMP_DEFAULT_BRUSH "2. Hardness 050" +#define GIMP_DEFAULT_DYNAMICS "Dynamics Off" +#define GIMP_DEFAULT_PATTERN "Pine" +#define GIMP_DEFAULT_PALETTE "Default" +#define GIMP_DEFAULT_GRADIENT "FG to BG (RGB)" +#define GIMP_DEFAULT_TOOL_PRESET "Current Options" +#define GIMP_DEFAULT_FONT "Sans-serif" +#define GIMP_DEFAULT_MYPAINT_BRUSH "Fixme" +#define GIMP_DEFAULT_COMMENT "Created with GIMP" + + +enum +{ + PROP_0, + PROP_LANGUAGE, + PROP_INTERPOLATION_TYPE, + PROP_DEFAULT_THRESHOLD, + PROP_PLUG_IN_PATH, + PROP_MODULE_PATH, + PROP_INTERPRETER_PATH, + PROP_ENVIRON_PATH, + PROP_BRUSH_PATH, + PROP_BRUSH_PATH_WRITABLE, + PROP_DYNAMICS_PATH, + PROP_DYNAMICS_PATH_WRITABLE, + PROP_MYPAINT_BRUSH_PATH, + PROP_MYPAINT_BRUSH_PATH_WRITABLE, + PROP_PATTERN_PATH, + PROP_PATTERN_PATH_WRITABLE, + PROP_PALETTE_PATH, + PROP_PALETTE_PATH_WRITABLE, + PROP_GRADIENT_PATH, + PROP_GRADIENT_PATH_WRITABLE, + PROP_TOOL_PRESET_PATH, + PROP_TOOL_PRESET_PATH_WRITABLE, + PROP_FONT_PATH, + PROP_FONT_PATH_WRITABLE, + PROP_DEFAULT_BRUSH, + PROP_DEFAULT_DYNAMICS, + PROP_DEFAULT_MYPAINT_BRUSH, + PROP_DEFAULT_PATTERN, + PROP_DEFAULT_PALETTE, + PROP_DEFAULT_GRADIENT, + PROP_DEFAULT_TOOL_PRESET, + PROP_DEFAULT_FONT, + PROP_GLOBAL_BRUSH, + PROP_GLOBAL_DYNAMICS, + PROP_GLOBAL_PATTERN, + PROP_GLOBAL_PALETTE, + PROP_GLOBAL_GRADIENT, + PROP_GLOBAL_FONT, + PROP_DEFAULT_IMAGE, + PROP_DEFAULT_GRID, + PROP_UNDO_LEVELS, + PROP_UNDO_SIZE, + PROP_UNDO_PREVIEW_SIZE, + PROP_FILTER_HISTORY_SIZE, + PROP_PLUGINRC_PATH, + PROP_LAYER_PREVIEWS, + PROP_GROUP_LAYER_PREVIEWS, + PROP_LAYER_PREVIEW_SIZE, + PROP_THUMBNAIL_SIZE, + PROP_THUMBNAIL_FILESIZE_LIMIT, + PROP_COLOR_MANAGEMENT, + PROP_SAVE_DOCUMENT_HISTORY, + PROP_QUICK_MASK_COLOR, + PROP_IMPORT_PROMOTE_FLOAT, + PROP_IMPORT_PROMOTE_DITHER, + PROP_IMPORT_ADD_ALPHA, + PROP_IMPORT_RAW_PLUG_IN, + PROP_EXPORT_FILE_TYPE, + PROP_EXPORT_COLOR_PROFILE, + PROP_EXPORT_METADATA_EXIF, + PROP_EXPORT_METADATA_XMP, + PROP_EXPORT_METADATA_IPTC, + PROP_DEBUG_POLICY, + PROP_CHECK_UPDATES, + PROP_CHECK_UPDATE_TIMESTAMP, + PROP_LAST_RELEASE_TIMESTAMP, + PROP_LAST_RELEASE_COMMENT, + PROP_LAST_REVISION, + PROP_LAST_KNOWN_RELEASE, + + /* ignored, only for backward compatibility: */ + PROP_INSTALL_COLORMAP, + PROP_MIN_COLORS +}; + + +static void gimp_core_config_finalize (GObject *object); +static void gimp_core_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_core_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_core_config_default_image_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gimp_core_config_default_grid_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gimp_core_config_color_management_notify (GObject *object, + GParamSpec *pspec, + gpointer data); + + +G_DEFINE_TYPE (GimpCoreConfig, gimp_core_config, GIMP_TYPE_GEGL_CONFIG) + +#define parent_class gimp_core_config_parent_class + +#ifdef G_OS_WIN32 +/* + * Taken from glib 2.35 code / gimpenv.c. + * Only temporary until the user-font folder detection can go upstream + * in fontconfig! + * XXX + */ +static gchar * +get_special_folder (int csidl) +{ + wchar_t path[MAX_PATH+1]; + HRESULT hr; + LPITEMIDLIST pidl = NULL; + BOOL b; + gchar *retval = NULL; + + hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl); + if (hr == S_OK) + { + b = SHGetPathFromIDListW (pidl, path); + if (b) + retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL); + CoTaskMemFree (pidl); + } + + return retval; +} +#endif + +static void +gimp_core_config_class_init (GimpCoreConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + gchar *path; + gchar *mypaint_brushes; + GimpRGB red = { 1.0, 0, 0, 0.5 }; + guint64 undo_size; + + object_class->finalize = gimp_core_config_finalize; + object_class->set_property = gimp_core_config_set_property; + object_class->get_property = gimp_core_config_get_property; + + GIMP_CONFIG_PROP_STRING (object_class, PROP_LANGUAGE, + "language", + "Language", + LANGUAGE_BLURB, + NULL, /* take from environment */ + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_INTERPOLATION_TYPE, + "interpolation-type", + "Interpolation", + INTERPOLATION_TYPE_BLURB, + GIMP_TYPE_INTERPOLATION_TYPE, + GIMP_INTERPOLATION_CUBIC, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_DEFAULT_THRESHOLD, + "default-threshold", + "Default threshold", + DEFAULT_THRESHOLD_BLURB, + 0, 255, 15, + GIMP_PARAM_STATIC_STRINGS); + + path = gimp_config_build_plug_in_path ("plug-ins"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_PLUG_IN_PATH, + "plug-in-path", + "Plug-in path", + PLUG_IN_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + path = gimp_config_build_plug_in_path ("modules"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_MODULE_PATH, + "module-path", + "Module path", + MODULE_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + path = gimp_config_build_plug_in_path ("interpreters"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_INTERPRETER_PATH, + "interpreter-path", + "Interpreter path", + INTERPRETER_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + path = gimp_config_build_plug_in_path ("environ"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_ENVIRON_PATH, + "environ-path", + "Environment path", + ENVIRON_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + path = gimp_config_build_data_path ("brushes"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_BRUSH_PATH, + "brush-path", + "Brush path", + BRUSH_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("brushes"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_BRUSH_PATH_WRITABLE, + "brush-path-writable", + "Writable brush path", + BRUSH_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("dynamics"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_DYNAMICS_PATH, + "dynamics-path", + "Dynamics path", + DYNAMICS_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("dynamics"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_DYNAMICS_PATH_WRITABLE, + "dynamics-path-writable", + "Writable dynamics path", + DYNAMICS_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + +#ifdef ENABLE_RELOCATABLE_RESOURCES + mypaint_brushes = g_build_filename ("${gimp_installation_dir}", + "share", "mypaint-data", + "1.0", "brushes", NULL); +#else + mypaint_brushes = g_strdup (MYPAINT_BRUSHES_DIR); +#endif + + path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + "~/.mypaint/brushes", + mypaint_brushes, + NULL); + g_free (mypaint_brushes); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_MYPAINT_BRUSH_PATH, + "mypaint-brush-path", + "MyPaint brush path", + MYPAINT_BRUSH_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + "~/.mypaint/brushes", + NULL); + GIMP_CONFIG_PROP_PATH (object_class, PROP_MYPAINT_BRUSH_PATH_WRITABLE, + "mypaint-brush-path-writable", + "Writable MyPaint brush path", + MYPAINT_BRUSH_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("patterns"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_PATTERN_PATH, + "pattern-path", + "Pattern path", + PATTERN_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("patterns"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_PATTERN_PATH_WRITABLE, + "pattern-path-writable", + "Writable pattern path", + PATTERN_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("palettes"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_PALETTE_PATH, + "palette-path", + "Palette path", + PALETTE_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("palettes"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_PALETTE_PATH_WRITABLE, + "palette-path-writable", + "Writable palette path", + PALETTE_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("gradients"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_GRADIENT_PATH, + "gradient-path", + "Gradient path", + GRADIENT_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("gradients"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_GRADIENT_PATH_WRITABLE, + "gradient-path-writable", + "Writable gradient path", + GRADIENT_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("tool-presets"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_TOOL_PRESET_PATH, + "tool-preset-path", + "Tool preset path", + TOOL_PRESET_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_writable_path ("tool-presets"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_TOOL_PRESET_PATH_WRITABLE, + "tool-preset-path-writable", + "Writable tool preset path", + TOOL_PRESET_PATH_WRITABLE_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + path = gimp_config_build_data_path ("fonts"); +#if defined G_OS_WIN32 + /* XXX: since a Windows 10 update, build 17704, Microsoft added the + * concept of user-installed fonts (until now it was only possible to + * have system-wide fonts! How weird is that?). + * A feature request at fontconfig is also done, but until this gets + * implemented upstream, let's add the folder ourselves in GIMP's + * default list of folders. + * See: https://gitlab.gnome.org/GNOME/gimp/issues/2949 + * Also: https://gitlab.freedesktop.org/fontconfig/fontconfig/issues/144 + */ + { + gchar *user_fonts_dir = get_special_folder (CSIDL_LOCAL_APPDATA); + + if (user_fonts_dir) + { + gchar *path2; + gchar *tmp; + + path2 = g_build_filename (user_fonts_dir, + "Microsoft", "Windows", "Fonts", NULL); + g_free (user_fonts_dir); + + /* G_SEARCHPATH_SEPARATOR-separated list of folders. */ + tmp = g_strconcat (path2, G_SEARCHPATH_SEPARATOR_S, path, NULL); + g_free (path2); + g_free (path); + path = tmp; + } + } +#endif + GIMP_CONFIG_PROP_PATH (object_class, PROP_FONT_PATH, + "font-path", + "Font path", + FONT_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + g_free (path); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_FONT_PATH_WRITABLE, + "font-path-writable", + "Writable font path", + NULL, + GIMP_CONFIG_PATH_DIR_LIST, NULL, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_BRUSH, + "default-brush", + "Default brush", + DEFAULT_BRUSH_BLURB, + GIMP_DEFAULT_BRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_DYNAMICS, + "default-dynamics", + "Default dynamics", + DEFAULT_DYNAMICS_BLURB, + GIMP_DEFAULT_DYNAMICS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_MYPAINT_BRUSH, + "default-mypaint-brush", + "Default MyPaint brush", + DEFAULT_MYPAINT_BRUSH_BLURB, + GIMP_DEFAULT_MYPAINT_BRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_PATTERN, + "default-pattern", + "Default pattern", + DEFAULT_PATTERN_BLURB, + GIMP_DEFAULT_PATTERN, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_PALETTE, + "default-palette", + "Default palette", + DEFAULT_PALETTE_BLURB, + GIMP_DEFAULT_PALETTE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_GRADIENT, + "default-gradient", + "Default gradient", + DEFAULT_GRADIENT_BLURB, + GIMP_DEFAULT_GRADIENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_TOOL_PRESET, + "default-tool-preset", + "Default tool preset", + DEFAULT_TOOL_PRESET_BLURB, + GIMP_DEFAULT_TOOL_PRESET, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_FONT, + "default-font", + "Default font", + DEFAULT_FONT_BLURB, + GIMP_DEFAULT_FONT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_BRUSH, + "global-brush", + "Global brush", + GLOBAL_BRUSH_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_DYNAMICS, + "global-dynamics", + "Global dynamics", + GLOBAL_DYNAMICS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_PATTERN, + "global-pattern", + "Global pattern", + GLOBAL_PATTERN_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_PALETTE, + "global-palette", + "Global palette", + GLOBAL_PALETTE_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_GRADIENT, + "global-gradient", + "Global gradient", + GLOBAL_GRADIENT_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_FONT, + "global-font", + "Global font", + GLOBAL_FONT_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_IMAGE, + "default-image", + "Default image", + DEFAULT_IMAGE_BLURB, + GIMP_TYPE_TEMPLATE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_GRID, + "default-grid", + "Default grid", + DEFAULT_GRID_BLURB, + GIMP_TYPE_GRID, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_INT (object_class, PROP_UNDO_LEVELS, + "undo-levels", + "Undo levels", + UNDO_LEVELS_BLURB, + 0, 1 << 20, 5, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + + undo_size = gimp_get_physical_memory_size (); + + if (undo_size > 0) + undo_size = undo_size / 8; /* 1/8th of the memory */ + else + undo_size = 1 << 26; /* 64GB */ + + GIMP_CONFIG_PROP_MEMSIZE (object_class, PROP_UNDO_SIZE, + "undo-size", + "Undo size", + UNDO_SIZE_BLURB, + 0, GIMP_MAX_MEMSIZE, undo_size, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_UNDO_PREVIEW_SIZE, + "undo-preview-size", + "Undo preview size", + UNDO_PREVIEW_SIZE_BLURB, + GIMP_TYPE_VIEW_SIZE, + GIMP_VIEW_SIZE_LARGE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_INT (object_class, PROP_FILTER_HISTORY_SIZE, + "plug-in-history-size", /* compat name */ + "Filter history size", + FILTER_HISTORY_SIZE_BLURB, + 0, 256, 10, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_PATH (object_class, + PROP_PLUGINRC_PATH, + "pluginrc-path", + "plugninrc path", + PLUGINRC_PATH_BLURB, + GIMP_CONFIG_PATH_FILE, + "${gimp_dir}" G_DIR_SEPARATOR_S "pluginrc", + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LAYER_PREVIEWS, + "layer-previews", + "Layer previews", + LAYER_PREVIEWS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GROUP_LAYER_PREVIEWS, + "group-layer-previews", + "Layer group previews", + GROUP_LAYER_PREVIEWS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_PREVIEW_SIZE, + "layer-preview-size", + "Layer preview size", + LAYER_PREVIEW_SIZE_BLURB, + GIMP_TYPE_VIEW_SIZE, + GIMP_VIEW_SIZE_MEDIUM, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_THUMBNAIL_SIZE, + "thumbnail-size", + "Thumbnail size", + THUMBNAIL_SIZE_BLURB, + GIMP_TYPE_THUMBNAIL_SIZE, + GIMP_THUMBNAIL_SIZE_NORMAL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_MEMSIZE (object_class, PROP_THUMBNAIL_FILESIZE_LIMIT, + "thumbnail-filesize-limit", + "Thumbnail file size limit", + THUMBNAIL_FILESIZE_LIMIT_BLURB, + 0, GIMP_MAX_MEMSIZE, 1 << 22, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_COLOR_MANAGEMENT, + "color-management", + "Color management", + COLOR_MANAGEMENT_BLURB, + GIMP_TYPE_COLOR_CONFIG, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CHECK_UPDATES, + "check-updates", + "Check for updates", + CHECK_UPDATES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT64 (object_class, PROP_CHECK_UPDATE_TIMESTAMP, + "check-update-timestamp", + "timestamp of the last update check", + CHECK_UPDATE_TIMESTAMP_BLURB, + 0, G_MAXINT64, 0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT64 (object_class, PROP_LAST_RELEASE_TIMESTAMP, + "last-release-timestamp", + "timestamp of the last release", + LAST_RELEASE_TIMESTAMP_BLURB, + 0, G_MAXINT64, 0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_LAST_RELEASE_COMMENT, + "last-release-comment", + "Comment for last release", + LAST_KNOWN_RELEASE_BLURB, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_LAST_KNOWN_RELEASE, + "last-known-release", + "last known release of GIMP", + LAST_KNOWN_RELEASE_BLURB, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_LAST_REVISION, + "last-revision", + "Last revision of current release", + LAST_RELEASE_TIMESTAMP_BLURB, + 0, G_MAXINT, 0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_DOCUMENT_HISTORY, + "save-document-history", + "Save document history", + SAVE_DOCUMENT_HISTORY_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_QUICK_MASK_COLOR, + "quick-mask-color", + "Quick mask color", + QUICK_MASK_COLOR_BLURB, + TRUE, &red, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_IMPORT_PROMOTE_FLOAT, + "import-promote-float", + "Import promote float", + IMPORT_PROMOTE_FLOAT_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_IMPORT_PROMOTE_DITHER, + "import-promote-dither", + "Import promote dither", + IMPORT_PROMOTE_DITHER_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_IMPORT_ADD_ALPHA, + "import-add-alpha", + "Import add alpha", + IMPORT_ADD_ALPHA_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_IMPORT_RAW_PLUG_IN, + "import-raw-plug-in", + "Import raw plug-in", + IMPORT_RAW_PLUG_IN_BLURB, + GIMP_CONFIG_PATH_FILE, + "", + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_EXPORT_FILE_TYPE, + "export-file-type", + "Default export file type", + EXPORT_FILE_TYPE_BLURB, + GIMP_TYPE_EXPORT_FILE_TYPE, + GIMP_EXPORT_FILE_PNG, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPORT_COLOR_PROFILE, + "export-color-profile", + "Export Color Profile", + EXPORT_COLOR_PROFILE_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPORT_METADATA_EXIF, + "export-metadata-exif", + "Export Exif metadata", + EXPORT_METADATA_EXIF_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPORT_METADATA_XMP, + "export-metadata-xmp", + "Export XMP metadata", + EXPORT_METADATA_XMP_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPORT_METADATA_IPTC, + "export-metadata-iptc", + "Export IPTC metadata", + EXPORT_METADATA_IPTC_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_DEBUG_POLICY, + "debug-policy", + "Try generating backtrace upon errors", + GENERATE_BACKTRACE_BLURB, + GIMP_TYPE_DEBUG_POLICY, +#ifdef GIMP_UNSTABLE + GIMP_DEBUG_POLICY_WARNING, +#else + GIMP_DEBUG_POLICY_FATAL, +#endif + GIMP_PARAM_STATIC_STRINGS); + + /* only for backward compatibility: */ + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_INSTALL_COLORMAP, + "install-colormap", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_INT (object_class, PROP_MIN_COLORS, + "min-colors", + NULL, NULL, + 27, 256, 144, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); +} + +static void +gimp_core_config_init (GimpCoreConfig *config) +{ + config->default_image = g_object_new (GIMP_TYPE_TEMPLATE, + "name", "Default Image", + "comment", GIMP_DEFAULT_COMMENT, + NULL); + g_signal_connect (config->default_image, "notify", + G_CALLBACK (gimp_core_config_default_image_notify), + config); + + config->default_grid = g_object_new (GIMP_TYPE_GRID, + "name", "Default Grid", + NULL); + g_signal_connect (config->default_grid, "notify", + G_CALLBACK (gimp_core_config_default_grid_notify), + config); + + config->color_management = g_object_new (GIMP_TYPE_COLOR_CONFIG, NULL); + g_signal_connect (config->color_management, "notify", + G_CALLBACK (gimp_core_config_color_management_notify), + config); +} + +static void +gimp_core_config_finalize (GObject *object) +{ + GimpCoreConfig *core_config = GIMP_CORE_CONFIG (object); + + g_free (core_config->language); + g_free (core_config->plug_in_path); + g_free (core_config->module_path); + g_free (core_config->interpreter_path); + g_free (core_config->environ_path); + g_free (core_config->brush_path); + g_free (core_config->brush_path_writable); + g_free (core_config->dynamics_path); + g_free (core_config->dynamics_path_writable); + g_free (core_config->pattern_path); + g_free (core_config->pattern_path_writable); + g_free (core_config->palette_path); + g_free (core_config->palette_path_writable); + g_free (core_config->gradient_path); + g_free (core_config->gradient_path_writable); + g_free (core_config->tool_preset_path); + g_free (core_config->tool_preset_path_writable); + g_free (core_config->font_path); + g_free (core_config->font_path_writable); + g_free (core_config->default_brush); + g_free (core_config->default_dynamics); + g_free (core_config->default_pattern); + g_free (core_config->default_palette); + g_free (core_config->default_gradient); + g_free (core_config->default_tool_preset); + g_free (core_config->default_font); + g_free (core_config->plug_in_rc_path); + g_free (core_config->import_raw_plug_in); + + g_clear_pointer (&core_config->last_known_release, g_free); + g_clear_pointer (&core_config->last_release_comment, g_free); + + g_clear_object (&core_config->default_image); + g_clear_object (&core_config->default_grid); + g_clear_object (&core_config->color_management); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_core_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCoreConfig *core_config = GIMP_CORE_CONFIG (object); + + switch (property_id) + { + case PROP_LANGUAGE: + g_free (core_config->language); + core_config->language = g_value_dup_string (value); + break; + case PROP_INTERPOLATION_TYPE: + core_config->interpolation_type = g_value_get_enum (value); + break; + case PROP_DEFAULT_THRESHOLD: + core_config->default_threshold = g_value_get_int (value); + break; + case PROP_PLUG_IN_PATH: + g_free (core_config->plug_in_path); + core_config->plug_in_path = g_value_dup_string (value); + break; + case PROP_MODULE_PATH: + g_free (core_config->module_path); + core_config->module_path = g_value_dup_string (value); + break; + case PROP_INTERPRETER_PATH: + g_free (core_config->interpreter_path); + core_config->interpreter_path = g_value_dup_string (value); + break; + case PROP_ENVIRON_PATH: + g_free (core_config->environ_path); + core_config->environ_path = g_value_dup_string (value); + break; + case PROP_BRUSH_PATH: + g_free (core_config->brush_path); + core_config->brush_path = g_value_dup_string (value); + break; + case PROP_BRUSH_PATH_WRITABLE: + g_free (core_config->brush_path_writable); + core_config->brush_path_writable = g_value_dup_string (value); + break; + case PROP_DYNAMICS_PATH: + g_free (core_config->dynamics_path); + core_config->dynamics_path = g_value_dup_string (value); + break; + case PROP_DYNAMICS_PATH_WRITABLE: + g_free (core_config->dynamics_path_writable); + core_config->dynamics_path_writable = g_value_dup_string (value); + break; + case PROP_MYPAINT_BRUSH_PATH: + g_free (core_config->mypaint_brush_path); + core_config->mypaint_brush_path = g_value_dup_string (value); + break; + case PROP_MYPAINT_BRUSH_PATH_WRITABLE: + g_free (core_config->mypaint_brush_path_writable); + core_config->mypaint_brush_path_writable = g_value_dup_string (value); + break; + case PROP_PATTERN_PATH: + g_free (core_config->pattern_path); + core_config->pattern_path = g_value_dup_string (value); + break; + case PROP_PATTERN_PATH_WRITABLE: + g_free (core_config->pattern_path_writable); + core_config->pattern_path_writable = g_value_dup_string (value); + break; + case PROP_PALETTE_PATH: + g_free (core_config->palette_path); + core_config->palette_path = g_value_dup_string (value); + break; + case PROP_PALETTE_PATH_WRITABLE: + g_free (core_config->palette_path_writable); + core_config->palette_path_writable = g_value_dup_string (value); + break; + case PROP_GRADIENT_PATH: + g_free (core_config->gradient_path); + core_config->gradient_path = g_value_dup_string (value); + break; + case PROP_GRADIENT_PATH_WRITABLE: + g_free (core_config->gradient_path_writable); + core_config->gradient_path_writable = g_value_dup_string (value); + break; + case PROP_TOOL_PRESET_PATH: + g_free (core_config->tool_preset_path); + core_config->tool_preset_path = g_value_dup_string (value); + break; + case PROP_TOOL_PRESET_PATH_WRITABLE: + g_free (core_config->tool_preset_path_writable); + core_config->tool_preset_path_writable = g_value_dup_string (value); + break; + case PROP_FONT_PATH: + g_free (core_config->font_path); + core_config->font_path = g_value_dup_string (value); + break; + case PROP_FONT_PATH_WRITABLE: + g_free (core_config->font_path_writable); + core_config->font_path_writable = g_value_dup_string (value); + break; + case PROP_DEFAULT_BRUSH: + g_free (core_config->default_brush); + core_config->default_brush = g_value_dup_string (value); + break; + case PROP_DEFAULT_DYNAMICS: + g_free (core_config->default_dynamics); + core_config->default_dynamics = g_value_dup_string (value); + break; + case PROP_DEFAULT_MYPAINT_BRUSH: + g_free (core_config->default_mypaint_brush); + core_config->default_mypaint_brush = g_value_dup_string (value); + break; + case PROP_DEFAULT_PATTERN: + g_free (core_config->default_pattern); + core_config->default_pattern = g_value_dup_string (value); + break; + case PROP_DEFAULT_PALETTE: + g_free (core_config->default_palette); + core_config->default_palette = g_value_dup_string (value); + break; + case PROP_DEFAULT_GRADIENT: + g_free (core_config->default_gradient); + core_config->default_gradient = g_value_dup_string (value); + break; + case PROP_DEFAULT_TOOL_PRESET: + g_free (core_config->default_tool_preset); + core_config->default_tool_preset = g_value_dup_string (value); + break; + case PROP_DEFAULT_FONT: + g_free (core_config->default_font); + core_config->default_font = g_value_dup_string (value); + break; + case PROP_GLOBAL_BRUSH: + core_config->global_brush = g_value_get_boolean (value); + break; + case PROP_GLOBAL_DYNAMICS: + core_config->global_dynamics = g_value_get_boolean (value); + break; + case PROP_GLOBAL_PATTERN: + core_config->global_pattern = g_value_get_boolean (value); + break; + case PROP_GLOBAL_PALETTE: + core_config->global_palette = g_value_get_boolean (value); + break; + case PROP_GLOBAL_GRADIENT: + core_config->global_gradient = g_value_get_boolean (value); + break; + case PROP_GLOBAL_FONT: + core_config->global_font = g_value_get_boolean (value); + break; + case PROP_DEFAULT_IMAGE: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value) , + G_OBJECT (core_config->default_image), 0); + break; + case PROP_DEFAULT_GRID: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value), + G_OBJECT (core_config->default_grid), 0); + break; + case PROP_FILTER_HISTORY_SIZE: + core_config->filter_history_size = g_value_get_int (value); + break; + case PROP_UNDO_LEVELS: + core_config->levels_of_undo = g_value_get_int (value); + break; + case PROP_UNDO_SIZE: + core_config->undo_size = g_value_get_uint64 (value); + break; + case PROP_UNDO_PREVIEW_SIZE: + core_config->undo_preview_size = g_value_get_enum (value); + break; + case PROP_PLUGINRC_PATH: + g_free (core_config->plug_in_rc_path); + core_config->plug_in_rc_path = g_value_dup_string (value); + break; + case PROP_LAYER_PREVIEWS: + core_config->layer_previews = g_value_get_boolean (value); + break; + case PROP_GROUP_LAYER_PREVIEWS: + core_config->group_layer_previews = g_value_get_boolean (value); + break; + case PROP_LAYER_PREVIEW_SIZE: + core_config->layer_preview_size = g_value_get_enum (value); + break; + case PROP_THUMBNAIL_SIZE: + core_config->thumbnail_size = g_value_get_enum (value); + break; + case PROP_THUMBNAIL_FILESIZE_LIMIT: + core_config->thumbnail_filesize_limit = g_value_get_uint64 (value); + break; + case PROP_COLOR_MANAGEMENT: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value), + G_OBJECT (core_config->color_management), 0); + break; + case PROP_CHECK_UPDATES: + core_config->check_updates = g_value_get_boolean (value); + break; + case PROP_CHECK_UPDATE_TIMESTAMP: + core_config->check_update_timestamp = g_value_get_int64 (value); + break; + case PROP_LAST_RELEASE_TIMESTAMP: + core_config->last_release_timestamp = g_value_get_int64 (value); + break; + case PROP_LAST_RELEASE_COMMENT: + g_clear_pointer (&core_config->last_release_comment, g_free); + core_config->last_release_comment = g_value_dup_string (value); + break; + case PROP_LAST_REVISION: + core_config->last_revision = g_value_get_int (value); + break; + case PROP_LAST_KNOWN_RELEASE: + g_clear_pointer (&core_config->last_known_release, g_free); + core_config->last_known_release = g_value_dup_string (value); + break; + case PROP_SAVE_DOCUMENT_HISTORY: + core_config->save_document_history = g_value_get_boolean (value); + break; + case PROP_QUICK_MASK_COLOR: + gimp_value_get_rgb (value, &core_config->quick_mask_color); + break; + case PROP_IMPORT_PROMOTE_FLOAT: + core_config->import_promote_float = g_value_get_boolean (value); + break; + case PROP_IMPORT_PROMOTE_DITHER: + core_config->import_promote_dither = g_value_get_boolean (value); + break; + case PROP_IMPORT_ADD_ALPHA: + core_config->import_add_alpha = g_value_get_boolean (value); + break; + case PROP_IMPORT_RAW_PLUG_IN: + g_free (core_config->import_raw_plug_in); + core_config->import_raw_plug_in = g_value_dup_string (value); + break; + case PROP_EXPORT_FILE_TYPE: + core_config->export_file_type = g_value_get_enum (value); + break; + case PROP_EXPORT_COLOR_PROFILE: + core_config->export_color_profile = g_value_get_boolean (value); + break; + case PROP_EXPORT_METADATA_EXIF: + core_config->export_metadata_exif = g_value_get_boolean (value); + break; + case PROP_EXPORT_METADATA_XMP: + core_config->export_metadata_xmp = g_value_get_boolean (value); + break; + case PROP_EXPORT_METADATA_IPTC: + core_config->export_metadata_iptc = g_value_get_boolean (value); + break; + case PROP_DEBUG_POLICY: + core_config->debug_policy = g_value_get_enum (value); + break; + + case PROP_INSTALL_COLORMAP: + case PROP_MIN_COLORS: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_core_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCoreConfig *core_config = GIMP_CORE_CONFIG (object); + + switch (property_id) + { + case PROP_LANGUAGE: + g_value_set_string (value, core_config->language); + break; + case PROP_INTERPOLATION_TYPE: + g_value_set_enum (value, core_config->interpolation_type); + break; + case PROP_DEFAULT_THRESHOLD: + g_value_set_int (value, core_config->default_threshold); + break; + case PROP_PLUG_IN_PATH: + g_value_set_string (value, core_config->plug_in_path); + break; + case PROP_MODULE_PATH: + g_value_set_string (value, core_config->module_path); + break; + case PROP_INTERPRETER_PATH: + g_value_set_string (value, core_config->interpreter_path); + break; + case PROP_ENVIRON_PATH: + g_value_set_string (value, core_config->environ_path); + break; + case PROP_BRUSH_PATH: + g_value_set_string (value, core_config->brush_path); + break; + case PROP_BRUSH_PATH_WRITABLE: + g_value_set_string (value, core_config->brush_path_writable); + break; + case PROP_DYNAMICS_PATH: + g_value_set_string (value, core_config->dynamics_path); + break; + case PROP_DYNAMICS_PATH_WRITABLE: + g_value_set_string (value, core_config->dynamics_path_writable); + break; + case PROP_MYPAINT_BRUSH_PATH: + g_value_set_string (value, core_config->mypaint_brush_path); + break; + case PROP_MYPAINT_BRUSH_PATH_WRITABLE: + g_value_set_string (value, core_config->mypaint_brush_path_writable); + break; + case PROP_PATTERN_PATH: + g_value_set_string (value, core_config->pattern_path); + break; + case PROP_PATTERN_PATH_WRITABLE: + g_value_set_string (value, core_config->pattern_path_writable); + break; + case PROP_PALETTE_PATH: + g_value_set_string (value, core_config->palette_path); + break; + case PROP_PALETTE_PATH_WRITABLE: + g_value_set_string (value, core_config->palette_path_writable); + break; + case PROP_GRADIENT_PATH: + g_value_set_string (value, core_config->gradient_path); + break; + case PROP_GRADIENT_PATH_WRITABLE: + g_value_set_string (value, core_config->gradient_path_writable); + break; + case PROP_TOOL_PRESET_PATH: + g_value_set_string (value, core_config->tool_preset_path); + break; + case PROP_TOOL_PRESET_PATH_WRITABLE: + g_value_set_string (value, core_config->tool_preset_path_writable); + break; + case PROP_FONT_PATH: + g_value_set_string (value, core_config->font_path); + break; + case PROP_FONT_PATH_WRITABLE: + g_value_set_string (value, core_config->font_path_writable); + break; + case PROP_DEFAULT_BRUSH: + g_value_set_string (value, core_config->default_brush); + break; + case PROP_DEFAULT_DYNAMICS: + g_value_set_string (value, core_config->default_dynamics); + break; + case PROP_DEFAULT_MYPAINT_BRUSH: + g_value_set_string (value, core_config->default_mypaint_brush); + break; + case PROP_DEFAULT_PATTERN: + g_value_set_string (value, core_config->default_pattern); + break; + case PROP_DEFAULT_PALETTE: + g_value_set_string (value, core_config->default_palette); + break; + case PROP_DEFAULT_GRADIENT: + g_value_set_string (value, core_config->default_gradient); + break; + case PROP_DEFAULT_TOOL_PRESET: + g_value_set_string (value, core_config->default_tool_preset); + break; + case PROP_DEFAULT_FONT: + g_value_set_string (value, core_config->default_font); + break; + case PROP_GLOBAL_BRUSH: + g_value_set_boolean (value, core_config->global_brush); + break; + case PROP_GLOBAL_DYNAMICS: + g_value_set_boolean (value, core_config->global_dynamics); + break; + case PROP_GLOBAL_PATTERN: + g_value_set_boolean (value, core_config->global_pattern); + break; + case PROP_GLOBAL_PALETTE: + g_value_set_boolean (value, core_config->global_palette); + break; + case PROP_GLOBAL_GRADIENT: + g_value_set_boolean (value, core_config->global_gradient); + break; + case PROP_GLOBAL_FONT: + g_value_set_boolean (value, core_config->global_font); + break; + case PROP_DEFAULT_IMAGE: + g_value_set_object (value, core_config->default_image); + break; + case PROP_DEFAULT_GRID: + g_value_set_object (value, core_config->default_grid); + break; + case PROP_FILTER_HISTORY_SIZE: + g_value_set_int (value, core_config->filter_history_size); + break; + case PROP_UNDO_LEVELS: + g_value_set_int (value, core_config->levels_of_undo); + break; + case PROP_UNDO_SIZE: + g_value_set_uint64 (value, core_config->undo_size); + break; + case PROP_UNDO_PREVIEW_SIZE: + g_value_set_enum (value, core_config->undo_preview_size); + break; + case PROP_PLUGINRC_PATH: + g_value_set_string (value, core_config->plug_in_rc_path); + break; + case PROP_LAYER_PREVIEWS: + g_value_set_boolean (value, core_config->layer_previews); + break; + case PROP_GROUP_LAYER_PREVIEWS: + g_value_set_boolean (value, core_config->group_layer_previews); + break; + case PROP_LAYER_PREVIEW_SIZE: + g_value_set_enum (value, core_config->layer_preview_size); + break; + case PROP_THUMBNAIL_SIZE: + g_value_set_enum (value, core_config->thumbnail_size); + break; + case PROP_THUMBNAIL_FILESIZE_LIMIT: + g_value_set_uint64 (value, core_config->thumbnail_filesize_limit); + break; + case PROP_COLOR_MANAGEMENT: + g_value_set_object (value, core_config->color_management); + break; + case PROP_CHECK_UPDATES: + g_value_set_boolean (value, core_config->check_updates); + break; + case PROP_CHECK_UPDATE_TIMESTAMP: + g_value_set_int64 (value, core_config->check_update_timestamp); + break; + case PROP_LAST_RELEASE_TIMESTAMP: + g_value_set_int64 (value, core_config->last_release_timestamp); + break; + case PROP_LAST_RELEASE_COMMENT: + g_value_set_string (value, core_config->last_release_comment); + break; + case PROP_LAST_REVISION: + g_value_set_int (value, core_config->last_revision); + break; + case PROP_LAST_KNOWN_RELEASE: + g_value_set_string (value, core_config->last_known_release); + break; + case PROP_SAVE_DOCUMENT_HISTORY: + g_value_set_boolean (value, core_config->save_document_history); + break; + case PROP_QUICK_MASK_COLOR: + gimp_value_set_rgb (value, &core_config->quick_mask_color); + break; + case PROP_IMPORT_PROMOTE_FLOAT: + g_value_set_boolean (value, core_config->import_promote_float); + break; + case PROP_IMPORT_PROMOTE_DITHER: + g_value_set_boolean (value, core_config->import_promote_dither); + break; + case PROP_IMPORT_ADD_ALPHA: + g_value_set_boolean (value, core_config->import_add_alpha); + break; + case PROP_IMPORT_RAW_PLUG_IN: + g_value_set_string (value, core_config->import_raw_plug_in); + break; + case PROP_EXPORT_FILE_TYPE: + g_value_set_enum (value, core_config->export_file_type); + break; + case PROP_EXPORT_COLOR_PROFILE: + g_value_set_boolean (value, core_config->export_color_profile); + break; + case PROP_EXPORT_METADATA_EXIF: + g_value_set_boolean (value, core_config->export_metadata_exif); + break; + case PROP_EXPORT_METADATA_XMP: + g_value_set_boolean (value, core_config->export_metadata_xmp); + break; + case PROP_EXPORT_METADATA_IPTC: + g_value_set_boolean (value, core_config->export_metadata_iptc); + break; + case PROP_DEBUG_POLICY: + g_value_set_enum (value, core_config->debug_policy); + break; + + case PROP_INSTALL_COLORMAP: + case PROP_MIN_COLORS: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_core_config_default_image_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + g_object_notify (G_OBJECT (data), "default-image"); +} + +static void +gimp_core_config_default_grid_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + g_object_notify (G_OBJECT (data), "default-grid"); +} + +static void +gimp_core_config_color_management_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + g_object_notify (G_OBJECT (data), "color-management"); +} diff --git a/app/config/gimpcoreconfig.h b/app/config/gimpcoreconfig.h new file mode 100644 index 0000000..f413ae4 --- /dev/null +++ b/app/config/gimpcoreconfig.h @@ -0,0 +1,123 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpCoreConfig class + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_CORE_CONFIG_H__ +#define __GIMP_CORE_CONFIG_H__ + +#include "operations/operations-enums.h" +#include "core/core-enums.h" + +#include "config/gimpgeglconfig.h" + + +#define GIMP_TYPE_CORE_CONFIG (gimp_core_config_get_type ()) +#define GIMP_CORE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CORE_CONFIG, GimpCoreConfig)) +#define GIMP_CORE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CORE_CONFIG, GimpCoreConfigClass)) +#define GIMP_IS_CORE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CORE_CONFIG)) +#define GIMP_IS_CORE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CORE_CONFIG)) + + +typedef struct _GimpCoreConfigClass GimpCoreConfigClass; + +struct _GimpCoreConfig +{ + GimpGeglConfig parent_instance; + + gchar *language; + GimpInterpolationType interpolation_type; + gint default_threshold; + gchar *plug_in_path; + gchar *module_path; + gchar *interpreter_path; + gchar *environ_path; + gchar *brush_path; + gchar *brush_path_writable; + gchar *dynamics_path; + gchar *dynamics_path_writable; + gchar *mypaint_brush_path; + gchar *mypaint_brush_path_writable; + gchar *pattern_path; + gchar *pattern_path_writable; + gchar *palette_path; + gchar *palette_path_writable; + gchar *gradient_path; + gchar *gradient_path_writable; + gchar *tool_preset_path; + gchar *tool_preset_path_writable; + gchar *font_path; + gchar *font_path_writable; /* unused */ + gchar *default_brush; + gchar *default_dynamics; + gchar *default_mypaint_brush; + gchar *default_pattern; + gchar *default_palette; + gchar *default_tool_preset; + gchar *default_gradient; + gchar *default_font; + gboolean global_brush; + gboolean global_dynamics; + gboolean global_pattern; + gboolean global_palette; + gboolean global_gradient; + gboolean global_font; + GimpTemplate *default_image; + GimpGrid *default_grid; + gint levels_of_undo; + guint64 undo_size; + GimpViewSize undo_preview_size; + gint filter_history_size; + gchar *plug_in_rc_path; + gboolean layer_previews; + gboolean group_layer_previews; + GimpViewSize layer_preview_size; + GimpThumbnailSize thumbnail_size; + guint64 thumbnail_filesize_limit; + GimpColorConfig *color_management; + gboolean save_document_history; + GimpRGB quick_mask_color; + gboolean import_promote_float; + gboolean import_promote_dither; + gboolean import_add_alpha; + gchar *import_raw_plug_in; + GimpExportFileType export_file_type; + gboolean export_color_profile; + gboolean export_metadata_exif; + gboolean export_metadata_xmp; + gboolean export_metadata_iptc; + GimpDebugPolicy debug_policy; + + gboolean check_updates; + gint64 check_update_timestamp; + gchar *last_known_release; + gint64 last_release_timestamp; + gchar *last_release_comment; + gint last_revision; +}; + +struct _GimpCoreConfigClass +{ + GimpGeglConfigClass parent_class; +}; + + +GType gimp_core_config_get_type (void) G_GNUC_CONST; + + +#endif /* GIMP_CORE_CONFIG_H__ */ diff --git a/app/config/gimpdialogconfig.c b/app/config/gimpdialogconfig.c new file mode 100644 index 0000000..f81d778 --- /dev/null +++ b/app/config/gimpdialogconfig.c @@ -0,0 +1,991 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDialogConfig class + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core/core-types.h" /* fill and stroke options */ +#include "core/gimp.h" +#include "core/gimpstrokeoptions.h" + +#include "config-types.h" + +#include "gimprc-blurbs.h" +#include "gimpdialogconfig.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + + PROP_GIMP, + + PROP_COLOR_PROFILE_POLICY, + + PROP_COLOR_PROFILE_PATH, + + PROP_IMAGE_CONVERT_PROFILE_INTENT, + PROP_IMAGE_CONVERT_PROFILE_BPC, + + PROP_IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD, + PROP_IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD, + PROP_IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD, + + PROP_IMAGE_CONVERT_INDEXED_PALETTE_TYPE, + PROP_IMAGE_CONVERT_INDEXED_MAX_COLORS, + PROP_IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES, + PROP_IMAGE_CONVERT_INDEXED_DITHER_TYPE, + PROP_IMAGE_CONVERT_INDEXED_DITHER_ALPHA, + PROP_IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS, + + PROP_IMAGE_RESIZE_FILL_TYPE, + PROP_IMAGE_RESIZE_LAYER_SET, + PROP_IMAGE_RESIZE_RESIZE_TEXT_LAYERS, + + PROP_LAYER_NEW_NAME, + PROP_LAYER_NEW_MODE, + PROP_LAYER_NEW_BLEND_SPACE, + PROP_LAYER_NEW_COMPOSITE_SPACE, + PROP_LAYER_NEW_COMPOSITE_MODE, + PROP_LAYER_NEW_OPACITY, + PROP_LAYER_NEW_FILL_TYPE, + + PROP_LAYER_RESIZE_FILL_TYPE, + + PROP_LAYER_ADD_MASK_TYPE, + PROP_LAYER_ADD_MASK_INVERT, + + PROP_LAYER_MERGE_TYPE, + PROP_LAYER_MERGE_ACTIVE_GROUP_ONLY, + PROP_LAYER_MERGE_DISCARD_INVISIBLE, + + PROP_CHANNEL_NEW_NAME, + PROP_CHANNEL_NEW_COLOR, + + PROP_VECTORS_NEW_NAME, + + PROP_VECTORS_EXPORT_PATH, + PROP_VECTORS_EXPORT_ACTIVE_ONLY, + + PROP_VECTORS_IMPORT_PATH, + PROP_VECTORS_IMPORT_MERGE, + PROP_VECTORS_IMPORT_SCALE, + + PROP_SELECTION_FEATHER_RADIUS, + PROP_SELECTION_FEATHER_EDGE_LOCK, + + PROP_SELECTION_GROW_RADIUS, + + PROP_SELECTION_SHRINK_RADIUS, + PROP_SELECTION_SHRINK_EDGE_LOCK, + + PROP_SELECTION_BORDER_RADIUS, + PROP_SELECTION_BORDER_STYLE, + PROP_SELECTION_BORDER_EDGE_LOCK, + + PROP_FILL_OPTIONS, + PROP_STROKE_OPTIONS +}; + + +typedef struct _GimpDialogConfigPrivate GimpDialogConfigPrivate; + +struct _GimpDialogConfigPrivate +{ + Gimp *gimp; +}; + +#define GET_PRIVATE(config) \ + ((GimpDialogConfigPrivate *) gimp_dialog_config_get_instance_private ((GimpDialogConfig *) (config))) + + +static void gimp_dialog_config_constructed (GObject *object); +static void gimp_dialog_config_finalize (GObject *object); +static void gimp_dialog_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_dialog_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_dialog_config_fill_options_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gimp_dialog_config_stroke_options_notify (GObject *object, + GParamSpec *pspec, + gpointer data); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpDialogConfig, gimp_dialog_config, + GIMP_TYPE_GUI_CONFIG) + +#define parent_class gimp_dialog_config_parent_class + + +static void +gimp_dialog_config_class_init (GimpDialogConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpRGB half_transparent = { 0.0, 0.0, 0.0, 0.5 }; + + object_class->constructed = gimp_dialog_config_constructed; + object_class->finalize = gimp_dialog_config_finalize; + object_class->set_property = gimp_dialog_config_set_property; + object_class->get_property = gimp_dialog_config_get_property; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", + NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_COLOR_PROFILE_POLICY, + "color-profile-policy", + "Color profile policy", + COLOR_PROFILE_POLICY_BLURB, + GIMP_TYPE_COLOR_PROFILE_POLICY, + GIMP_COLOR_PROFILE_POLICY_ASK, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_COLOR_PROFILE_PATH, + "color-profile-path", + "Default color profile folder path", + COLOR_PROFILE_PATH_BLURB, + GIMP_CONFIG_PATH_FILE, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_IMAGE_CONVERT_PROFILE_INTENT, + "image-convert-profile-intent", + "Default rendering intent for color profile conversion", + IMAGE_CONVERT_PROFILE_INTENT_BLURB, + GIMP_TYPE_COLOR_RENDERING_INTENT, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_IMAGE_CONVERT_PROFILE_BPC, + "image-convert-profile-black-point-compensation", + "Default 'Black point compensation' for " + "color profile conversion", + IMAGE_CONVERT_PROFILE_BPC_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, + PROP_IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD, + "image-convert-precision-layer-dither-method", + "Default layer dither type for precision conversion", + IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD_BLURB, + GEGL_TYPE_DITHER_METHOD, + GEGL_DITHER_NONE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, + PROP_IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD, + "image-convert-precision-text-layer-dither-method", + "Default text layer dither type for precision conversion", + IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD_BLURB, + GEGL_TYPE_DITHER_METHOD, + GEGL_DITHER_NONE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, + PROP_IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD, + "image-convert-precision-channel-dither-method", + "Default channel dither type for precision conversion", + IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD_BLURB, + GEGL_TYPE_DITHER_METHOD, + GEGL_DITHER_NONE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, + PROP_IMAGE_CONVERT_INDEXED_PALETTE_TYPE, + "image-convert-indexed-palette-type", + "Default palette type for indexed conversion", + IMAGE_CONVERT_INDEXED_PALETTE_TYPE_BLURB, + GIMP_TYPE_CONVERT_PALETTE_TYPE, + GIMP_CONVERT_PALETTE_GENERATE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, + PROP_IMAGE_CONVERT_INDEXED_MAX_COLORS, + "image-convert-indexed-max-colors", + "Default maximum number of colors for indexed conversion", + IMAGE_CONVERT_INDEXED_MAX_COLORS_BLURB, + 2, 256, 256, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, + PROP_IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES, + "image-convert-indexed-remove-duplicates", + "Default remove duplicates for indexed conversion", + IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, + PROP_IMAGE_CONVERT_INDEXED_DITHER_TYPE, + "image-convert-indexed-dither-type", + "Default dither type for indexed conversion", + IMAGE_CONVERT_INDEXED_DITHER_TYPE_BLURB, + GIMP_TYPE_CONVERT_DITHER_TYPE, + GIMP_CONVERT_DITHER_NONE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, + PROP_IMAGE_CONVERT_INDEXED_DITHER_ALPHA, + "image-convert-indexed-dither-alpha", + "Default dither alpha for indexed conversion", + IMAGE_CONVERT_INDEXED_DITHER_ALPHA_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, + PROP_IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS, + "image-convert-indexed-dither-text-layers", + "Default dither text layers for indexed conversion", + IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_IMAGE_RESIZE_FILL_TYPE, + "image-resize-fill-type", + "Default image resize fill type", + IMAGE_RESIZE_FILL_TYPE_BLURB, + GIMP_TYPE_FILL_TYPE, + GIMP_FILL_TRANSPARENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_IMAGE_RESIZE_LAYER_SET, + "image-resize-layer-set", + "Default image resize layer set", + IMAGE_RESIZE_LAYER_SET_BLURB, + GIMP_TYPE_ITEM_SET, + GIMP_ITEM_SET_NONE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_IMAGE_RESIZE_RESIZE_TEXT_LAYERS, + "image-resize-resize-text-layers", + "Default image resize text layers", + IMAGE_RESIZE_RESIZE_TEXT_LAYERS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_LAYER_NEW_NAME, + "layer-new-name", + "Default new layer name", + LAYER_NEW_NAME_BLURB, + _("Layer"), + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_NEW_MODE, + "layer-new-mode", + "Default new layer mode", + LAYER_NEW_MODE_BLURB, + GIMP_TYPE_LAYER_MODE, + GIMP_LAYER_MODE_NORMAL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_NEW_BLEND_SPACE, + "layer-new-blend-space", + "Default new layer blend space", + LAYER_NEW_BLEND_SPACE_BLURB, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_NEW_COMPOSITE_SPACE, + "layer-new-composite-space", + "Default new layer composite space", + LAYER_NEW_COMPOSITE_SPACE_BLURB, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_NEW_COMPOSITE_MODE, + "layer-new-composite-mode", + "Default new layer composite mode", + LAYER_NEW_COMPOSITE_MODE_BLURB, + GIMP_TYPE_LAYER_COMPOSITE_MODE, + GIMP_LAYER_COMPOSITE_AUTO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LAYER_NEW_OPACITY, + "layer-new-opacity", + "Default new layer opacity", + LAYER_NEW_OPACITY_BLURB, + GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE, + GIMP_OPACITY_OPAQUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_NEW_FILL_TYPE, + "layer-new-fill-type", + "Default new layer fill type", + LAYER_NEW_FILL_TYPE_BLURB, + GIMP_TYPE_FILL_TYPE, + GIMP_FILL_TRANSPARENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_RESIZE_FILL_TYPE, + "layer-resize-fill-type", + "Default layer resize fill type", + LAYER_RESIZE_FILL_TYPE_BLURB, + GIMP_TYPE_FILL_TYPE, + GIMP_FILL_TRANSPARENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_ADD_MASK_TYPE, + "layer-add-mask-type", + "Default layer mask type", + LAYER_ADD_MASK_TYPE_BLURB, + GIMP_TYPE_ADD_MASK_TYPE, + GIMP_ADD_MASK_WHITE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LAYER_ADD_MASK_INVERT, + "layer-add-mask-invert", + "Default layer mask invert", + LAYER_ADD_MASK_INVERT_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_LAYER_MERGE_TYPE, + "layer-merge-type", + "Default layer merge type", + LAYER_MERGE_TYPE_BLURB, + GIMP_TYPE_MERGE_TYPE, + GIMP_EXPAND_AS_NECESSARY, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LAYER_MERGE_ACTIVE_GROUP_ONLY, + "layer-merge-active-group-only", + "Default layer merge active group only", + LAYER_MERGE_ACTIVE_GROUP_ONLY_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LAYER_MERGE_DISCARD_INVISIBLE, + "layer-merge-discard-invisible", + "Default layer merge discard invisible", + LAYER_MERGE_DISCARD_INVISIBLE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_CHANNEL_NEW_NAME, + "channel-new-name", + "Default new channel name", + CHANNEL_NEW_NAME_BLURB, + _("Channel"), + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_CHANNEL_NEW_COLOR, + "channel-new-color", + "Default new channel color and opacity", + CHANNEL_NEW_COLOR_BLURB, + TRUE, + &half_transparent, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_VECTORS_NEW_NAME, + "path-new-name", + "Default new path name", + VECTORS_NEW_NAME_BLURB, + _("Path"), + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_VECTORS_EXPORT_PATH, + "path-export-path", + "Default path export folder path", + VECTORS_EXPORT_PATH_BLURB, + GIMP_CONFIG_PATH_FILE, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_VECTORS_EXPORT_ACTIVE_ONLY, + "path-export-active-only", + "Default export only the active path", + VECTORS_EXPORT_ACTIVE_ONLY_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_VECTORS_IMPORT_PATH, + "path-import-path", + "Default path import folder path", + VECTORS_IMPORT_PATH_BLURB, + GIMP_CONFIG_PATH_FILE, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_VECTORS_IMPORT_MERGE, + "path-import-merge", + "Default merge imported vectors", + VECTORS_IMPORT_MERGE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_VECTORS_IMPORT_SCALE, + "path-import-scale", + "Default scale imported vectors", + VECTORS_IMPORT_SCALE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SELECTION_FEATHER_RADIUS, + "selection-feather-radius", + "Selection feather radius", + SELECTION_FEATHER_RADIUS_BLURB, + 0.0, 32767.0, 5.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SELECTION_FEATHER_EDGE_LOCK, + "selection-feather-edge-lock", + "Selection feather edge lock", + SELECTION_FEATHER_EDGE_LOCK_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SELECTION_GROW_RADIUS, + "selection-grow-radius", + "Selection grow radius", + SELECTION_GROW_RADIUS_BLURB, + 1.0, 32767.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SELECTION_SHRINK_RADIUS, + "selection-shrink-radius", + "Selection shrink radius", + SELECTION_SHRINK_RADIUS_BLURB, + 1.0, 32767.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SELECTION_SHRINK_EDGE_LOCK, + "selection-shrink-edge-lock", + "Selection shrink edge lock", + SELECTION_SHRINK_EDGE_LOCK_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SELECTION_BORDER_RADIUS, + "selection-border-radius", + "Selection border radius", + SELECTION_BORDER_RADIUS_BLURB, + 1.0, 32767.0, 5.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SELECTION_BORDER_EDGE_LOCK, + "selection-border-edge-lock", + "Selection border edge lock", + SELECTION_BORDER_EDGE_LOCK_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_SELECTION_BORDER_STYLE, + "selection-border-style", + "Selection border style", + SELECTION_BORDER_STYLE_BLURB, + GIMP_TYPE_CHANNEL_BORDER_STYLE, + GIMP_CHANNEL_BORDER_STYLE_SMOOTH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_FILL_OPTIONS, + "fill-options", + "Fill Options", + FILL_OPTIONS_BLURB, + GIMP_TYPE_FILL_OPTIONS, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_STROKE_OPTIONS, + "stroke-options", + "Stroke Options", + STROKE_OPTIONS_BLURB, + GIMP_TYPE_STROKE_OPTIONS, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); +} + +static void +gimp_dialog_config_init (GimpDialogConfig *config) +{ +} + +static void +gimp_dialog_config_constructed (GObject *object) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (object); + GimpDialogConfigPrivate *priv = GET_PRIVATE (object); + GimpContext *context; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (priv->gimp)); + + context = gimp_get_user_context (priv->gimp); + + config->fill_options = gimp_fill_options_new (priv->gimp, context, TRUE); + gimp_context_set_serialize_properties (GIMP_CONTEXT (config->fill_options), + 0); + + g_signal_connect (config->fill_options, "notify", + G_CALLBACK (gimp_dialog_config_fill_options_notify), + config); + + config->stroke_options = gimp_stroke_options_new (priv->gimp, context, TRUE); + gimp_context_set_serialize_properties (GIMP_CONTEXT (config->stroke_options), + 0); + + g_signal_connect (config->stroke_options, "notify", + G_CALLBACK (gimp_dialog_config_stroke_options_notify), + config); +} + +static void +gimp_dialog_config_finalize (GObject *object) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (object); + + g_clear_pointer (&config->color_profile_path, g_free); + g_clear_pointer (&config->layer_new_name, g_free); + g_clear_pointer (&config->channel_new_name, g_free); + g_clear_pointer (&config->vectors_new_name, g_free); + g_clear_pointer (&config->vectors_export_path, g_free); + g_clear_pointer (&config->vectors_import_path, g_free); + + g_clear_object (&config->fill_options); + g_clear_object (&config->stroke_options); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_dialog_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (object); + GimpDialogConfigPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GIMP: + priv->gimp = g_value_get_object (value); /* don't ref */ + break; + + case PROP_COLOR_PROFILE_POLICY: + config->color_profile_policy = g_value_get_enum (value); + break; + + case PROP_COLOR_PROFILE_PATH: + if (config->color_profile_path) + g_free (config->color_profile_path); + config->color_profile_path = g_value_dup_string (value); + break; + + case PROP_IMAGE_CONVERT_PROFILE_INTENT: + config->image_convert_profile_intent = g_value_get_enum (value); + break; + case PROP_IMAGE_CONVERT_PROFILE_BPC: + config->image_convert_profile_bpc = g_value_get_boolean (value); + break; + + case PROP_IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD: + config->image_convert_precision_layer_dither_method = + g_value_get_enum (value); + break; + case PROP_IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD: + config->image_convert_precision_text_layer_dither_method = + g_value_get_enum (value); + break; + case PROP_IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD: + config->image_convert_precision_channel_dither_method = + g_value_get_enum (value); + break; + + case PROP_IMAGE_CONVERT_INDEXED_PALETTE_TYPE: + config->image_convert_indexed_palette_type = g_value_get_enum (value); + break; + case PROP_IMAGE_CONVERT_INDEXED_MAX_COLORS: + config->image_convert_indexed_max_colors = g_value_get_int (value); + break; + case PROP_IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES: + config->image_convert_indexed_remove_duplicates = g_value_get_boolean (value); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_TYPE: + config->image_convert_indexed_dither_type = g_value_get_enum (value); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_ALPHA: + config->image_convert_indexed_dither_alpha = g_value_get_boolean (value); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS: + config->image_convert_indexed_dither_text_layers = g_value_get_boolean (value); + break; + + case PROP_IMAGE_RESIZE_FILL_TYPE: + config->image_resize_fill_type = g_value_get_enum (value); + break; + case PROP_IMAGE_RESIZE_LAYER_SET: + config->image_resize_layer_set = g_value_get_enum (value); + break; + case PROP_IMAGE_RESIZE_RESIZE_TEXT_LAYERS: + config->image_resize_resize_text_layers = g_value_get_boolean (value); + break; + + case PROP_LAYER_NEW_NAME: + if (config->layer_new_name) + g_free (config->layer_new_name); + config->layer_new_name = g_value_dup_string (value); + break; + case PROP_LAYER_NEW_MODE: + config->layer_new_mode = g_value_get_enum (value); + break; + case PROP_LAYER_NEW_BLEND_SPACE: + config->layer_new_blend_space = g_value_get_enum (value); + break; + case PROP_LAYER_NEW_COMPOSITE_SPACE: + config->layer_new_composite_space = g_value_get_enum (value); + break; + case PROP_LAYER_NEW_COMPOSITE_MODE: + config->layer_new_composite_mode = g_value_get_enum (value); + break; + case PROP_LAYER_NEW_OPACITY: + config->layer_new_opacity = g_value_get_double (value); + break; + case PROP_LAYER_NEW_FILL_TYPE: + config->layer_new_fill_type = g_value_get_enum (value); + break; + + case PROP_LAYER_RESIZE_FILL_TYPE: + config->layer_resize_fill_type = g_value_get_enum (value); + break; + + case PROP_LAYER_ADD_MASK_TYPE: + config->layer_add_mask_type = g_value_get_enum (value); + break; + case PROP_LAYER_ADD_MASK_INVERT: + config->layer_add_mask_invert = g_value_get_boolean (value); + break; + + case PROP_LAYER_MERGE_TYPE: + config->layer_merge_type = g_value_get_enum (value); + break; + case PROP_LAYER_MERGE_ACTIVE_GROUP_ONLY: + config->layer_merge_active_group_only = g_value_get_boolean (value); + break; + case PROP_LAYER_MERGE_DISCARD_INVISIBLE: + config->layer_merge_discard_invisible = g_value_get_boolean (value); + break; + + case PROP_CHANNEL_NEW_NAME: + if (config->channel_new_name) + g_free (config->channel_new_name); + config->channel_new_name = g_value_dup_string (value); + break; + case PROP_CHANNEL_NEW_COLOR: + gimp_value_get_rgb (value, &config->channel_new_color); + break; + + case PROP_VECTORS_NEW_NAME: + if (config->vectors_new_name) + g_free (config->vectors_new_name); + config->vectors_new_name = g_value_dup_string (value); + break; + + case PROP_VECTORS_EXPORT_PATH: + if (config->vectors_export_path) + g_free (config->vectors_export_path); + config->vectors_export_path = g_value_dup_string (value); + break; + case PROP_VECTORS_EXPORT_ACTIVE_ONLY: + config->vectors_export_active_only = g_value_get_boolean (value); + break; + + case PROP_VECTORS_IMPORT_PATH: + if (config->vectors_import_path) + g_free (config->vectors_import_path); + config->vectors_import_path = g_value_dup_string (value); + break; + case PROP_VECTORS_IMPORT_MERGE: + config->vectors_import_merge = g_value_get_boolean (value); + break; + case PROP_VECTORS_IMPORT_SCALE: + config->vectors_import_scale = g_value_get_boolean (value); + break; + + case PROP_SELECTION_FEATHER_RADIUS: + config->selection_feather_radius = g_value_get_double (value); + break; + case PROP_SELECTION_FEATHER_EDGE_LOCK: + config->selection_feather_edge_lock = g_value_get_boolean (value); + break; + + case PROP_SELECTION_GROW_RADIUS: + config->selection_grow_radius = g_value_get_double (value); + break; + + case PROP_SELECTION_SHRINK_RADIUS: + config->selection_shrink_radius = g_value_get_double (value); + break; + case PROP_SELECTION_SHRINK_EDGE_LOCK: + config->selection_shrink_edge_lock = g_value_get_boolean (value); + break; + + case PROP_SELECTION_BORDER_RADIUS: + config->selection_border_radius = g_value_get_double (value); + break; + case PROP_SELECTION_BORDER_EDGE_LOCK: + config->selection_border_edge_lock = g_value_get_boolean (value); + break; + case PROP_SELECTION_BORDER_STYLE: + config->selection_border_style = g_value_get_enum (value); + break; + + case PROP_FILL_OPTIONS: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value) , + G_OBJECT (config->fill_options), 0); + break; + case PROP_STROKE_OPTIONS: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value) , + G_OBJECT (config->stroke_options), 0); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dialog_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (object); + GimpDialogConfigPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, priv->gimp); + break; + + case PROP_COLOR_PROFILE_POLICY: + g_value_set_enum (value, config->color_profile_policy); + break; + + case PROP_COLOR_PROFILE_PATH: + g_value_set_string (value, config->color_profile_path); + break; + + case PROP_IMAGE_CONVERT_PROFILE_INTENT: + g_value_set_enum (value, config->image_convert_profile_intent); + break; + case PROP_IMAGE_CONVERT_PROFILE_BPC: + g_value_set_boolean (value, config->image_convert_profile_bpc); + break; + + case PROP_IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD: + g_value_set_enum (value, + config->image_convert_precision_layer_dither_method); + break; + case PROP_IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD: + g_value_set_enum (value, + config->image_convert_precision_text_layer_dither_method); + break; + case PROP_IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD: + g_value_set_enum (value, + config->image_convert_precision_channel_dither_method); + break; + + case PROP_IMAGE_CONVERT_INDEXED_PALETTE_TYPE: + g_value_set_enum (value, config->image_convert_indexed_palette_type); + break; + case PROP_IMAGE_CONVERT_INDEXED_MAX_COLORS: + g_value_set_int (value, config->image_convert_indexed_max_colors); + break; + case PROP_IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES: + g_value_set_boolean (value, config->image_convert_indexed_remove_duplicates); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_TYPE: + g_value_set_enum (value, config->image_convert_indexed_dither_type); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_ALPHA: + g_value_set_boolean (value, config->image_convert_indexed_dither_alpha); + break; + case PROP_IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS: + g_value_set_boolean (value, config->image_convert_indexed_dither_text_layers); + break; + + case PROP_IMAGE_RESIZE_FILL_TYPE: + g_value_set_enum (value, config->image_resize_fill_type); + break; + case PROP_IMAGE_RESIZE_LAYER_SET: + g_value_set_enum (value, config->image_resize_layer_set); + break; + case PROP_IMAGE_RESIZE_RESIZE_TEXT_LAYERS: + g_value_set_boolean (value, config->image_resize_resize_text_layers); + break; + + case PROP_LAYER_NEW_NAME: + g_value_set_string (value, config->layer_new_name); + break; + case PROP_LAYER_NEW_MODE: + g_value_set_enum (value, config->layer_new_mode); + break; + case PROP_LAYER_NEW_BLEND_SPACE: + g_value_set_enum (value, config->layer_new_blend_space); + break; + case PROP_LAYER_NEW_COMPOSITE_SPACE: + g_value_set_enum (value, config->layer_new_composite_space); + break; + case PROP_LAYER_NEW_COMPOSITE_MODE: + g_value_set_enum (value, config->layer_new_composite_mode); + break; + case PROP_LAYER_NEW_OPACITY: + g_value_set_double (value, config->layer_new_opacity); + break; + case PROP_LAYER_NEW_FILL_TYPE: + g_value_set_enum (value, config->layer_new_fill_type); + break; + + case PROP_LAYER_RESIZE_FILL_TYPE: + g_value_set_enum (value, config->layer_resize_fill_type); + break; + + case PROP_LAYER_ADD_MASK_TYPE: + g_value_set_enum (value, config->layer_add_mask_type); + break; + case PROP_LAYER_ADD_MASK_INVERT: + g_value_set_boolean (value, config->layer_add_mask_invert); + break; + + case PROP_LAYER_MERGE_TYPE: + g_value_set_enum (value, config->layer_merge_type); + break; + case PROP_LAYER_MERGE_ACTIVE_GROUP_ONLY: + g_value_set_boolean (value, config->layer_merge_active_group_only); + break; + case PROP_LAYER_MERGE_DISCARD_INVISIBLE: + g_value_set_boolean (value, config->layer_merge_discard_invisible); + break; + + case PROP_CHANNEL_NEW_NAME: + g_value_set_string (value, config->channel_new_name); + break; + case PROP_CHANNEL_NEW_COLOR: + gimp_value_set_rgb (value, &config->channel_new_color); + break; + + case PROP_VECTORS_NEW_NAME: + g_value_set_string (value, config->vectors_new_name); + break; + + case PROP_VECTORS_EXPORT_PATH: + g_value_set_string (value, config->vectors_export_path); + break; + case PROP_VECTORS_EXPORT_ACTIVE_ONLY: + g_value_set_boolean (value, config->vectors_export_active_only); + break; + + case PROP_VECTORS_IMPORT_PATH: + g_value_set_string (value, config->vectors_import_path); + break; + case PROP_VECTORS_IMPORT_MERGE: + g_value_set_boolean (value, config->vectors_import_merge); + break; + case PROP_VECTORS_IMPORT_SCALE: + g_value_set_boolean (value, config->vectors_import_scale); + break; + + case PROP_SELECTION_FEATHER_RADIUS: + g_value_set_double (value, config->selection_feather_radius); + break; + case PROP_SELECTION_FEATHER_EDGE_LOCK: + g_value_set_boolean (value, config->selection_feather_edge_lock); + break; + + case PROP_SELECTION_GROW_RADIUS: + g_value_set_double (value, config->selection_grow_radius); + break; + + case PROP_SELECTION_SHRINK_RADIUS: + g_value_set_double (value, config->selection_shrink_radius); + break; + case PROP_SELECTION_SHRINK_EDGE_LOCK: + g_value_set_boolean (value, config->selection_shrink_edge_lock); + break; + + case PROP_SELECTION_BORDER_RADIUS: + g_value_set_double (value, config->selection_border_radius); + break; + case PROP_SELECTION_BORDER_EDGE_LOCK: + g_value_set_boolean (value, config->selection_border_edge_lock); + break; + case PROP_SELECTION_BORDER_STYLE: + g_value_set_enum (value, config->selection_border_style); + break; + + case PROP_FILL_OPTIONS: + g_value_set_object (value, config->fill_options); + break; + case PROP_STROKE_OPTIONS: + g_value_set_object (value, config->stroke_options); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dialog_config_fill_options_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + /* ignore notifications on parent class properties such as fg/bg */ + if (pspec->owner_type == G_TYPE_FROM_INSTANCE (object)) + g_object_notify (G_OBJECT (data), "fill-options"); +} + +static void +gimp_dialog_config_stroke_options_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + /* see above */ + if (pspec->owner_type == G_TYPE_FROM_INSTANCE (object)) + g_object_notify (G_OBJECT (data), "stroke-options"); +} diff --git a/app/config/gimpdialogconfig.h b/app/config/gimpdialogconfig.h new file mode 100644 index 0000000..1053f6d --- /dev/null +++ b/app/config/gimpdialogconfig.h @@ -0,0 +1,123 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDialogConfig class + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DIALOG_CONFIG_H__ +#define __GIMP_DIALOG_CONFIG_H__ + +#include "config/gimpguiconfig.h" + + +/* We don't want to include stuff from core/ here, instead do the next + * less ugly hack... + */ +typedef struct _GimpFillOptions GimpFillOptions; +typedef struct _GimpStrokeOptions GimpStrokeOptions; + + +#define GIMP_TYPE_DIALOG_CONFIG (gimp_dialog_config_get_type ()) +#define GIMP_DIALOG_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DIALOG_CONFIG, GimpDialogConfig)) +#define GIMP_DIALOG_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DIALOG_CONFIG, GimpDialogConfigClass)) +#define GIMP_IS_DIALOG_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DIALOG_CONFIG)) +#define GIMP_IS_DIALOG_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DIALOG_CONFIG)) + + +typedef struct _GimpDialogConfigClass GimpDialogConfigClass; + +struct _GimpDialogConfig +{ + GimpGuiConfig parent_instance; + + GimpColorProfilePolicy color_profile_policy; + + gchar *color_profile_path; + + GimpColorRenderingIntent image_convert_profile_intent; + gboolean image_convert_profile_bpc; + + GeglDitherMethod image_convert_precision_layer_dither_method; + GeglDitherMethod image_convert_precision_text_layer_dither_method; + GeglDitherMethod image_convert_precision_channel_dither_method; + + GimpConvertPaletteType image_convert_indexed_palette_type; + gint image_convert_indexed_max_colors; + gboolean image_convert_indexed_remove_duplicates; + GimpConvertDitherType image_convert_indexed_dither_type; + gboolean image_convert_indexed_dither_alpha; + gboolean image_convert_indexed_dither_text_layers; + + GimpFillType image_resize_fill_type; + GimpItemSet image_resize_layer_set; + gboolean image_resize_resize_text_layers; + + gchar *layer_new_name; + GimpLayerMode layer_new_mode; + GimpLayerColorSpace layer_new_blend_space; + GimpLayerColorSpace layer_new_composite_space; + GimpLayerCompositeMode layer_new_composite_mode; + gdouble layer_new_opacity; + GimpFillType layer_new_fill_type; + + GimpFillType layer_resize_fill_type; + + GimpAddMaskType layer_add_mask_type; + gboolean layer_add_mask_invert; + + GimpMergeType layer_merge_type; + gboolean layer_merge_active_group_only; + gboolean layer_merge_discard_invisible; + + gchar *channel_new_name; + GimpRGB channel_new_color; + + gchar *vectors_new_name; + + gchar *vectors_export_path; + gboolean vectors_export_active_only; + + gchar *vectors_import_path; + gboolean vectors_import_merge; + gboolean vectors_import_scale; + + gdouble selection_feather_radius; + gboolean selection_feather_edge_lock; + + gdouble selection_grow_radius; + + gdouble selection_shrink_radius; + gboolean selection_shrink_edge_lock; + + gdouble selection_border_radius; + gboolean selection_border_edge_lock; + GimpChannelBorderStyle selection_border_style; + + GimpFillOptions *fill_options; + GimpStrokeOptions *stroke_options; +}; + +struct _GimpDialogConfigClass +{ + GimpGuiConfigClass parent_class; +}; + + +GType gimp_dialog_config_get_type (void) G_GNUC_CONST; + + +#endif /* GIMP_DIALOG_CONFIG_H__ */ diff --git a/app/config/gimpdisplayconfig.c b/app/config/gimpdisplayconfig.c new file mode 100644 index 0000000..d38a6a1 --- /dev/null +++ b/app/config/gimpdisplayconfig.c @@ -0,0 +1,623 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDisplayConfig class + * Copyright (C) 2001 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-blurbs.h" +#include "gimpdisplayconfig.h" +#include "gimpdisplayoptions.h" + +#include "gimp-intl.h" + + +#define DEFAULT_ACTIVATE_ON_FOCUS TRUE +#define DEFAULT_MONITOR_RESOLUTION 96.0 +#define DEFAULT_MARCHING_ANTS_SPEED 200 +#define DEFAULT_USE_EVENT_HISTORY FALSE + +enum +{ + PROP_0, + PROP_TRANSPARENCY_SIZE, + PROP_TRANSPARENCY_TYPE, + PROP_SNAP_DISTANCE, + PROP_MARCHING_ANTS_SPEED, + PROP_RESIZE_WINDOWS_ON_ZOOM, + PROP_RESIZE_WINDOWS_ON_RESIZE, + PROP_DEFAULT_SHOW_ALL, + PROP_DEFAULT_DOT_FOR_DOT, + PROP_INITIAL_ZOOM_TO_FIT, + PROP_CURSOR_MODE, + PROP_CURSOR_UPDATING, + PROP_SHOW_BRUSH_OUTLINE, + PROP_SNAP_BRUSH_OUTLINE, + PROP_SHOW_PAINT_TOOL_CURSOR, + PROP_IMAGE_TITLE_FORMAT, + PROP_IMAGE_STATUS_FORMAT, + PROP_MONITOR_XRESOLUTION, + PROP_MONITOR_YRESOLUTION, + PROP_MONITOR_RES_FROM_GDK, + PROP_NAV_PREVIEW_SIZE, + PROP_DEFAULT_VIEW, + PROP_DEFAULT_FULLSCREEN_VIEW, + PROP_ACTIVATE_ON_FOCUS, + PROP_SPACE_BAR_ACTION, + PROP_ZOOM_QUALITY, + PROP_USE_EVENT_HISTORY, + + /* ignored, only for backward compatibility: */ + PROP_DEFAULT_SNAP_TO_GUIDES, + PROP_DEFAULT_SNAP_TO_GRID, + PROP_DEFAULT_SNAP_TO_CANVAS, + PROP_DEFAULT_SNAP_TO_PATH, + PROP_CONFIRM_ON_CLOSE, + PROP_XOR_COLOR, + PROP_PERFECT_MOUSE +}; + + +static void gimp_display_config_finalize (GObject *object); +static void gimp_display_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_display_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_display_config_view_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gimp_display_config_fullscreen_notify (GObject *object, + GParamSpec *pspec, + gpointer data); + + +G_DEFINE_TYPE (GimpDisplayConfig, gimp_display_config, GIMP_TYPE_CORE_CONFIG) + +#define parent_class gimp_display_config_parent_class + + +static void +gimp_display_config_class_init (GimpDisplayConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpRGB color = { 0, 0, 0, 0 }; + + object_class->finalize = gimp_display_config_finalize; + object_class->set_property = gimp_display_config_set_property; + object_class->get_property = gimp_display_config_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TRANSPARENCY_SIZE, + "transparency-size", + "Transparency size", + TRANSPARENCY_SIZE_BLURB, + GIMP_TYPE_CHECK_SIZE, + GIMP_CHECK_SIZE_MEDIUM_CHECKS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TRANSPARENCY_TYPE, + "transparency-type", + "Transparency type", + TRANSPARENCY_TYPE_BLURB, + GIMP_TYPE_CHECK_TYPE, + GIMP_CHECK_TYPE_GRAY_CHECKS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_SNAP_DISTANCE, + "snap-distance", + "Snap distance", + DEFAULT_SNAP_DISTANCE_BLURB, + 1, 255, 8, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_MARCHING_ANTS_SPEED, + "marching-ants-speed", + "Marching ants speed", + MARCHING_ANTS_SPEED_BLURB, + 10, 10000, DEFAULT_MARCHING_ANTS_SPEED, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_RESIZE_WINDOWS_ON_ZOOM, + "resize-windows-on-zoom", + "Resize windows on zoom", + RESIZE_WINDOWS_ON_ZOOM_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_RESIZE_WINDOWS_ON_RESIZE, + "resize-windows-on-resize", + "Resize windows on resize", + RESIZE_WINDOWS_ON_RESIZE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SHOW_ALL, + "default-show-all", + "Default show-all", + DEFAULT_SHOW_ALL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_DOT_FOR_DOT, + "default-dot-for-dot", + "Default dot-for-dot", + DEFAULT_DOT_FOR_DOT_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_INITIAL_ZOOM_TO_FIT, + "initial-zoom-to-fit", + "Initial zoom-to-fit", + INITIAL_ZOOM_TO_FIT_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CURSOR_MODE, + "cursor-mode", + "Cursor mode", + CURSOR_MODE_BLURB, + GIMP_TYPE_CURSOR_MODE, + GIMP_CURSOR_MODE_TOOL_CROSSHAIR, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CURSOR_UPDATING, + "cursor-updating", + "Cursor updating", + CURSOR_UPDATING_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_BRUSH_OUTLINE, + "show-brush-outline", + "Show brush outline", + SHOW_BRUSH_OUTLINE_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_BRUSH_OUTLINE, + "snap-brush-outline", + "Snap brush outline", + SNAP_BRUSH_OUTLINE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_PAINT_TOOL_CURSOR, + "show-paint-tool-cursor", + "Show paint tool cursor", + SHOW_PAINT_TOOL_CURSOR_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_IMAGE_TITLE_FORMAT, + "image-title-format", + "Image title format", + IMAGE_TITLE_FORMAT_BLURB, + GIMP_CONFIG_DEFAULT_IMAGE_TITLE_FORMAT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_IMAGE_STATUS_FORMAT, + "image-status-format", + "Image statusbar format", + IMAGE_STATUS_FORMAT_BLURB, + GIMP_CONFIG_DEFAULT_IMAGE_STATUS_FORMAT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RESOLUTION (object_class, PROP_MONITOR_XRESOLUTION, + "monitor-xresolution", + "Monitor resolution X", + MONITOR_XRESOLUTION_BLURB, + DEFAULT_MONITOR_RESOLUTION, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RESOLUTION (object_class, PROP_MONITOR_YRESOLUTION, + "monitor-yresolution", + "Monitor resolution Y", + MONITOR_YRESOLUTION_BLURB, + DEFAULT_MONITOR_RESOLUTION, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MONITOR_RES_FROM_GDK, + "monitor-resolution-from-windowing-system", + "Monitor resolution from windowing system", + MONITOR_RES_FROM_GDK_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_NAV_PREVIEW_SIZE, + "navigation-preview-size", + "Navigation preview size", + NAVIGATION_PREVIEW_SIZE_BLURB, + GIMP_TYPE_VIEW_SIZE, + GIMP_VIEW_SIZE_MEDIUM, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_VIEW, + "default-view", + "Default view options", + DEFAULT_VIEW_BLURB, + GIMP_TYPE_DISPLAY_OPTIONS, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_DEFAULT_FULLSCREEN_VIEW, + "default-fullscreen-view", + "Default fullscreen view options", + DEFAULT_FULLSCREEN_VIEW_BLURB, + GIMP_TYPE_DISPLAY_OPTIONS, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ACTIVATE_ON_FOCUS, + "activate-on-focus", + "Activate on focus", + ACTIVATE_ON_FOCUS_BLURB, + DEFAULT_ACTIVATE_ON_FOCUS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_SPACE_BAR_ACTION, + "space-bar-action", + "Space bar action", + SPACE_BAR_ACTION_BLURB, + GIMP_TYPE_SPACE_BAR_ACTION, + GIMP_SPACE_BAR_ACTION_PAN, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_ZOOM_QUALITY, + "zoom-quality", + "Zoom quality", + ZOOM_QUALITY_BLURB, + GIMP_TYPE_ZOOM_QUALITY, + GIMP_ZOOM_QUALITY_HIGH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_EVENT_HISTORY, + "use-event-history", + "Use event history", + DEFAULT_USE_EVENT_HISTORY_BLURB, + DEFAULT_USE_EVENT_HISTORY, + GIMP_PARAM_STATIC_STRINGS); + + /* only for backward compatibility: */ + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SNAP_TO_GUIDES, + "default-snap-to-guides", + NULL, NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SNAP_TO_GRID, + "default-snap-to-grid", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SNAP_TO_CANVAS, + "default-snap-to-canvas", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEFAULT_SNAP_TO_PATH, + "default-snap-to-path", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONFIRM_ON_CLOSE, + "confirm-on-close", + NULL, NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_XOR_COLOR, + "xor-color", + NULL, NULL, + FALSE, &color, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PERFECT_MOUSE, + "perfect-mouse", + NULL, NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); +} + +static void +gimp_display_config_init (GimpDisplayConfig *config) +{ + config->default_view = + g_object_new (GIMP_TYPE_DISPLAY_OPTIONS, NULL); + + g_signal_connect (config->default_view, "notify", + G_CALLBACK (gimp_display_config_view_notify), + config); + + config->default_fullscreen_view = + g_object_new (GIMP_TYPE_DISPLAY_OPTIONS, NULL); + + g_signal_connect (config->default_fullscreen_view, "notify", + G_CALLBACK (gimp_display_config_fullscreen_notify), + config); +} + +static void +gimp_display_config_finalize (GObject *object) +{ + GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (object); + + g_free (display_config->image_title_format); + g_free (display_config->image_status_format); + + g_clear_object (&display_config->default_view); + g_clear_object (&display_config->default_fullscreen_view); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_display_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (object); + + switch (property_id) + { + case PROP_TRANSPARENCY_SIZE: + display_config->transparency_size = g_value_get_enum (value); + break; + case PROP_TRANSPARENCY_TYPE: + display_config->transparency_type = g_value_get_enum (value); + break; + case PROP_SNAP_DISTANCE: + display_config->snap_distance = g_value_get_int (value); + break; + case PROP_MARCHING_ANTS_SPEED: + display_config->marching_ants_speed = g_value_get_int (value); + break; + case PROP_RESIZE_WINDOWS_ON_ZOOM: + display_config->resize_windows_on_zoom = g_value_get_boolean (value); + break; + case PROP_RESIZE_WINDOWS_ON_RESIZE: + display_config->resize_windows_on_resize = g_value_get_boolean (value); + break; + case PROP_DEFAULT_SHOW_ALL: + display_config->default_show_all = g_value_get_boolean (value); + break; + case PROP_DEFAULT_DOT_FOR_DOT: + display_config->default_dot_for_dot = g_value_get_boolean (value); + break; + case PROP_INITIAL_ZOOM_TO_FIT: + display_config->initial_zoom_to_fit = g_value_get_boolean (value); + break; + case PROP_CURSOR_MODE: + display_config->cursor_mode = g_value_get_enum (value); + break; + case PROP_CURSOR_UPDATING: + display_config->cursor_updating = g_value_get_boolean (value); + break; + case PROP_SHOW_BRUSH_OUTLINE: + display_config->show_brush_outline = g_value_get_boolean (value); + break; + case PROP_SNAP_BRUSH_OUTLINE: + display_config->snap_brush_outline = g_value_get_boolean (value); + break; + case PROP_SHOW_PAINT_TOOL_CURSOR: + display_config->show_paint_tool_cursor = g_value_get_boolean (value); + break; + case PROP_IMAGE_TITLE_FORMAT: + g_free (display_config->image_title_format); + display_config->image_title_format = g_value_dup_string (value); + break; + case PROP_IMAGE_STATUS_FORMAT: + g_free (display_config->image_status_format); + display_config->image_status_format = g_value_dup_string (value); + break; + case PROP_MONITOR_XRESOLUTION: + display_config->monitor_xres = g_value_get_double (value); + break; + case PROP_MONITOR_YRESOLUTION: + display_config->monitor_yres = g_value_get_double (value); + break; + case PROP_MONITOR_RES_FROM_GDK: + display_config->monitor_res_from_gdk = g_value_get_boolean (value); + break; + case PROP_NAV_PREVIEW_SIZE: + display_config->nav_preview_size = g_value_get_enum (value); + break; + case PROP_DEFAULT_VIEW: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value), + G_OBJECT (display_config->default_view), 0); + break; + case PROP_DEFAULT_FULLSCREEN_VIEW: + if (g_value_get_object (value)) + gimp_config_sync (g_value_get_object (value), + G_OBJECT (display_config->default_fullscreen_view), + 0); + break; + case PROP_ACTIVATE_ON_FOCUS: + display_config->activate_on_focus = g_value_get_boolean (value); + break; + case PROP_SPACE_BAR_ACTION: + display_config->space_bar_action = g_value_get_enum (value); + break; + case PROP_ZOOM_QUALITY: + display_config->zoom_quality = g_value_get_enum (value); + break; + case PROP_USE_EVENT_HISTORY: + display_config->use_event_history = g_value_get_boolean (value); + break; + + case PROP_DEFAULT_SNAP_TO_GUIDES: + case PROP_DEFAULT_SNAP_TO_GRID: + case PROP_DEFAULT_SNAP_TO_CANVAS: + case PROP_DEFAULT_SNAP_TO_PATH: + case PROP_CONFIRM_ON_CLOSE: + case PROP_XOR_COLOR: + case PROP_PERFECT_MOUSE: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (object); + + switch (property_id) + { + case PROP_TRANSPARENCY_SIZE: + g_value_set_enum (value, display_config->transparency_size); + break; + case PROP_TRANSPARENCY_TYPE: + g_value_set_enum (value, display_config->transparency_type); + break; + case PROP_SNAP_DISTANCE: + g_value_set_int (value, display_config->snap_distance); + break; + case PROP_MARCHING_ANTS_SPEED: + g_value_set_int (value, display_config->marching_ants_speed); + break; + case PROP_RESIZE_WINDOWS_ON_ZOOM: + g_value_set_boolean (value, display_config->resize_windows_on_zoom); + break; + case PROP_RESIZE_WINDOWS_ON_RESIZE: + g_value_set_boolean (value, display_config->resize_windows_on_resize); + break; + case PROP_DEFAULT_SHOW_ALL: + g_value_set_boolean (value, display_config->default_show_all); + break; + case PROP_DEFAULT_DOT_FOR_DOT: + g_value_set_boolean (value, display_config->default_dot_for_dot); + break; + case PROP_INITIAL_ZOOM_TO_FIT: + g_value_set_boolean (value, display_config->initial_zoom_to_fit); + break; + case PROP_CURSOR_MODE: + g_value_set_enum (value, display_config->cursor_mode); + break; + case PROP_CURSOR_UPDATING: + g_value_set_boolean (value, display_config->cursor_updating); + break; + case PROP_SHOW_BRUSH_OUTLINE: + g_value_set_boolean (value, display_config->show_brush_outline); + break; + case PROP_SNAP_BRUSH_OUTLINE: + g_value_set_boolean (value, display_config->snap_brush_outline); + break; + case PROP_SHOW_PAINT_TOOL_CURSOR: + g_value_set_boolean (value, display_config->show_paint_tool_cursor); + break; + case PROP_IMAGE_TITLE_FORMAT: + g_value_set_string (value, display_config->image_title_format); + break; + case PROP_IMAGE_STATUS_FORMAT: + g_value_set_string (value, display_config->image_status_format); + break; + case PROP_MONITOR_XRESOLUTION: + g_value_set_double (value, display_config->monitor_xres); + break; + case PROP_MONITOR_YRESOLUTION: + g_value_set_double (value, display_config->monitor_yres); + break; + case PROP_MONITOR_RES_FROM_GDK: + g_value_set_boolean (value, display_config->monitor_res_from_gdk); + break; + case PROP_NAV_PREVIEW_SIZE: + g_value_set_enum (value, display_config->nav_preview_size); + break; + case PROP_DEFAULT_VIEW: + g_value_set_object (value, display_config->default_view); + break; + case PROP_DEFAULT_FULLSCREEN_VIEW: + g_value_set_object (value, display_config->default_fullscreen_view); + break; + case PROP_ACTIVATE_ON_FOCUS: + g_value_set_boolean (value, display_config->activate_on_focus); + break; + case PROP_SPACE_BAR_ACTION: + g_value_set_enum (value, display_config->space_bar_action); + break; + case PROP_ZOOM_QUALITY: + g_value_set_enum (value, display_config->zoom_quality); + break; + case PROP_USE_EVENT_HISTORY: + g_value_set_boolean (value, display_config->use_event_history); + break; + + case PROP_DEFAULT_SNAP_TO_GUIDES: + case PROP_DEFAULT_SNAP_TO_GRID: + case PROP_DEFAULT_SNAP_TO_CANVAS: + case PROP_DEFAULT_SNAP_TO_PATH: + case PROP_CONFIRM_ON_CLOSE: + case PROP_XOR_COLOR: + case PROP_PERFECT_MOUSE: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_config_view_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + g_object_notify (G_OBJECT (data), "default-view"); +} + +static void +gimp_display_config_fullscreen_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + g_object_notify (G_OBJECT (data), "default-fullscreen-view"); +} diff --git a/app/config/gimpdisplayconfig.h b/app/config/gimpdisplayconfig.h new file mode 100644 index 0000000..e4c2a72 --- /dev/null +++ b/app/config/gimpdisplayconfig.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDisplayConfig class + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_DISPLAY_CONFIG_H__ +#define __GIMP_DISPLAY_CONFIG_H__ + +#include "config/gimpcoreconfig.h" + + +#define GIMP_CONFIG_DEFAULT_IMAGE_TITLE_FORMAT "%D*%f-%p.%i (%t, %o, %L) %wx%h" +#define GIMP_CONFIG_DEFAULT_IMAGE_STATUS_FORMAT "%n (%m)" + + +#define GIMP_TYPE_DISPLAY_CONFIG (gimp_display_config_get_type ()) +#define GIMP_DISPLAY_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DISPLAY_CONFIG, GimpDisplayConfig)) +#define GIMP_DISPLAY_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DISPLAY_CONFIG, GimpDisplayConfigClass)) +#define GIMP_IS_DISPLAY_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DISPLAY_CONFIG)) +#define GIMP_IS_DISPLAY_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DISPLAY_CONFIG)) + + +typedef struct _GimpDisplayConfigClass GimpDisplayConfigClass; + +struct _GimpDisplayConfig +{ + GimpCoreConfig parent_instance; + + GimpCheckSize transparency_size; + GimpCheckType transparency_type; + gint snap_distance; + gint marching_ants_speed; + gboolean resize_windows_on_zoom; + gboolean resize_windows_on_resize; + gboolean default_show_all; + gboolean default_dot_for_dot; + gboolean initial_zoom_to_fit; + GimpCursorMode cursor_mode; + gboolean cursor_updating; + gboolean show_brush_outline; + gboolean snap_brush_outline; + gboolean show_paint_tool_cursor; + gchar *image_title_format; + gchar *image_status_format; + gdouble monitor_xres; + gdouble monitor_yres; + gboolean monitor_res_from_gdk; + GimpViewSize nav_preview_size; + GimpDisplayOptions *default_view; + GimpDisplayOptions *default_fullscreen_view; + gboolean activate_on_focus; + GimpSpaceBarAction space_bar_action; + GimpZoomQuality zoom_quality; + gboolean use_event_history; +}; + +struct _GimpDisplayConfigClass +{ + GimpCoreConfigClass parent_class; +}; + + +GType gimp_display_config_get_type (void) G_GNUC_CONST; + + +#endif /* GIMP_DISPLAY_CONFIG_H__ */ diff --git a/app/config/gimpdisplayoptions.c b/app/config/gimpdisplayoptions.c new file mode 100644 index 0000000..b51a653 --- /dev/null +++ b/app/config/gimpdisplayoptions.c @@ -0,0 +1,597 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDisplayOptions + * Copyright (C) 2003 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-blurbs.h" + +#include "gimpdisplayoptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_SHOW_MENUBAR, + PROP_SHOW_STATUSBAR, + PROP_SHOW_RULERS, + PROP_SHOW_SCROLLBARS, + PROP_SHOW_SELECTION, + PROP_SHOW_LAYER_BOUNDARY, + PROP_SHOW_CANVAS_BOUNDARY, + PROP_SHOW_GUIDES, + PROP_SHOW_GRID, + PROP_SHOW_SAMPLE_POINTS, + PROP_SNAP_TO_GUIDES, + PROP_SNAP_TO_GRID, + PROP_SNAP_TO_CANVAS, + PROP_SNAP_TO_PATH, + PROP_PADDING_MODE, + PROP_PADDING_COLOR, + PROP_PADDING_IN_SHOW_ALL +}; + + +static void gimp_display_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_display_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_CODE (GimpDisplayOptions, + gimp_display_options, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +typedef struct _GimpDisplayOptions GimpDisplayOptionsFullscreen; +typedef struct _GimpDisplayOptionsClass GimpDisplayOptionsFullscreenClass; + +#define gimp_display_options_fullscreen_init gimp_display_options_init + +G_DEFINE_TYPE_WITH_CODE (GimpDisplayOptionsFullscreen, + gimp_display_options_fullscreen, + GIMP_TYPE_DISPLAY_OPTIONS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +typedef struct _GimpDisplayOptions GimpDisplayOptionsNoImage; +typedef struct _GimpDisplayOptionsClass GimpDisplayOptionsNoImageClass; + +#define gimp_display_options_no_image_init gimp_display_options_init + +G_DEFINE_TYPE_WITH_CODE (GimpDisplayOptionsNoImage, + gimp_display_options_no_image, + GIMP_TYPE_DISPLAY_OPTIONS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + + +static void +gimp_display_options_class_init (GimpDisplayOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpRGB white; + + gimp_rgba_set (&white, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + + object_class->set_property = gimp_display_options_set_property; + object_class->get_property = gimp_display_options_get_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_MENUBAR, + "show-menubar", + "Show menubar", + SHOW_MENUBAR_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_STATUSBAR, + "show-statusbar", + "Show statusbar", + SHOW_STATUSBAR_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_RULERS, + "show-rulers", + "Show rulers", + SHOW_RULERS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SCROLLBARS, + "show-scrollbars", + "Show scrollbars", + SHOW_SCROLLBARS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SELECTION, + "show-selection", + "Show selection", + SHOW_SELECTION_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_LAYER_BOUNDARY, + "show-layer-boundary", + "Show layer boundary", + SHOW_LAYER_BOUNDARY_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_CANVAS_BOUNDARY, + "show-canvas-boundary", + "Show canvas boundary", + SHOW_CANVAS_BOUNDARY_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GUIDES, + "show-guides", + "Show guides", + SHOW_GUIDES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GRID, + "show-grid", + "Show grid", + SHOW_GRID_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SAMPLE_POINTS, + "show-sample-points", + "Show sample points", + SHOW_SAMPLE_POINTS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GUIDES, + "snap-to-guides", + "Snap to guides", + SNAP_TO_GUIDES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GRID, + "snap-to-grid", + "Snap to grid", + SNAP_TO_GRID_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_CANVAS, + "snap-to-canvas", + "Snap to canvas", + SNAP_TO_CANVAS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_PATH, + "snap-to-path", + "Snap to path", + SNAP_TO_PATH_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_PADDING_MODE, + "padding-mode", + "Padding mode", + CANVAS_PADDING_MODE_BLURB, + GIMP_TYPE_CANVAS_PADDING_MODE, + GIMP_CANVAS_PADDING_MODE_DEFAULT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_PADDING_COLOR, + "padding-color", + "Padding color", + CANVAS_PADDING_COLOR_BLURB, + FALSE, &white, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PADDING_IN_SHOW_ALL, + "padding-in-show-all", + "Keep padding in \"Show All\" mode", + CANVAS_PADDING_IN_SHOW_ALL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_display_options_fullscreen_class_init (GimpDisplayOptionsFullscreenClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpRGB black; + + gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); + + object_class->set_property = gimp_display_options_set_property; + object_class->get_property = gimp_display_options_get_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_MENUBAR, + "show-menubar", + "Show menubar", + SHOW_MENUBAR_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_STATUSBAR, + "show-statusbar", + "Show statusbar", + SHOW_STATUSBAR_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_RULERS, + "show-rulers", + "Show rulers", + SHOW_RULERS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SCROLLBARS, + "show-scrollbars", + "Show scrollbars", + SHOW_SCROLLBARS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SELECTION, + "show-selection", + "Show selection", + SHOW_SELECTION_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_LAYER_BOUNDARY, + "show-layer-boundary", + "Show layer boundary", + SHOW_LAYER_BOUNDARY_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_CANVAS_BOUNDARY, + "show-canvas-boundary", + "Show canvas boundary", + SHOW_CANVAS_BOUNDARY_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GUIDES, + "show-guides", + "Show guides", + SHOW_GUIDES_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GRID, + "show-grid", + "Show grid", + SHOW_GRID_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SAMPLE_POINTS, + "show-sample-points", + "Show sample points", + SHOW_SAMPLE_POINTS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GUIDES, + "snap-to-guides", + "Snap to guides", + SNAP_TO_GUIDES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GRID, + "snap-to-grid", + "Snap to grid", + SHOW_SCROLLBARS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_CANVAS, + "snap-to-canvas", + "Snap to canvas", + SNAP_TO_CANVAS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_PATH, + "snap-to-path", + "Snap to path", + SNAP_TO_PATH_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_PADDING_MODE, + "padding-mode", + "Padding mode", + CANVAS_PADDING_MODE_BLURB, + GIMP_TYPE_CANVAS_PADDING_MODE, + GIMP_CANVAS_PADDING_MODE_CUSTOM, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_PADDING_COLOR, + "padding-color", + "Padding color", + CANVAS_PADDING_COLOR_BLURB, + FALSE, &black, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PADDING_IN_SHOW_ALL, + "padding-in-show-all", + "Keep padding in \"Show All\" mode", + CANVAS_PADDING_IN_SHOW_ALL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_display_options_no_image_class_init (GimpDisplayOptionsNoImageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_display_options_set_property; + object_class->get_property = gimp_display_options_get_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_RULERS, + "show-rulers", + "Show rulers", + SHOW_RULERS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SCROLLBARS, + "show-scrollbars", + "Show scrollbars", + SHOW_SCROLLBARS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SELECTION, + "show-selection", + "Show selection", + SHOW_SELECTION_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_LAYER_BOUNDARY, + "show-layer-boundary", + "Show layer boundary", + SHOW_LAYER_BOUNDARY_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_CANVAS_BOUNDARY, + "show-canvas-boundary", + "Show canvas boundary", + SHOW_CANVAS_BOUNDARY_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GUIDES, + "show-guides", + "Show guides", + SHOW_GUIDES_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_GRID, + "show-grid", + "Show grid", + SHOW_GRID_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SAMPLE_POINTS, + "show-sample-points", + "Show sample points", + SHOW_SAMPLE_POINTS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GUIDES, + "snap-to-guides", + "Snap to guides", + SNAP_TO_GUIDES_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_GRID, + "snap-to-grid", + "Snap to grid", + SHOW_SCROLLBARS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_CANVAS, + "snap-to-canvas", + "Snap to canvas", + SNAP_TO_CANVAS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SNAP_TO_PATH, + "snap-to-path", + "Snap tp path", + SNAP_TO_PATH_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_display_options_init (GimpDisplayOptions *options) +{ + options->padding_mode_set = FALSE; +} + +static void +gimp_display_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDisplayOptions *options = GIMP_DISPLAY_OPTIONS (object); + + switch (property_id) + { + case PROP_SHOW_MENUBAR: + options->show_menubar = g_value_get_boolean (value); + break; + case PROP_SHOW_STATUSBAR: + options->show_statusbar = g_value_get_boolean (value); + break; + case PROP_SHOW_RULERS: + options->show_rulers = g_value_get_boolean (value); + break; + case PROP_SHOW_SCROLLBARS: + options->show_scrollbars = g_value_get_boolean (value); + break; + case PROP_SHOW_SELECTION: + options->show_selection = g_value_get_boolean (value); + break; + case PROP_SHOW_LAYER_BOUNDARY: + options->show_layer_boundary = g_value_get_boolean (value); + break; + case PROP_SHOW_CANVAS_BOUNDARY: + options->show_canvas_boundary = g_value_get_boolean (value); + break; + case PROP_SHOW_GUIDES: + options->show_guides = g_value_get_boolean (value); + break; + case PROP_SHOW_GRID: + options->show_grid = g_value_get_boolean (value); + break; + case PROP_SHOW_SAMPLE_POINTS: + options->show_sample_points = g_value_get_boolean (value); + break; + case PROP_SNAP_TO_GUIDES: + options->snap_to_guides = g_value_get_boolean (value); + break; + case PROP_SNAP_TO_GRID: + options->snap_to_grid = g_value_get_boolean (value); + break; + case PROP_SNAP_TO_CANVAS: + options->snap_to_canvas = g_value_get_boolean (value); + break; + case PROP_SNAP_TO_PATH: + options->snap_to_path = g_value_get_boolean (value); + break; + case PROP_PADDING_MODE: + options->padding_mode = g_value_get_enum (value); + break; + case PROP_PADDING_COLOR: + options->padding_color = *(GimpRGB *) g_value_get_boxed (value); + break; + case PROP_PADDING_IN_SHOW_ALL: + options->padding_in_show_all = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDisplayOptions *options = GIMP_DISPLAY_OPTIONS (object); + + switch (property_id) + { + case PROP_SHOW_MENUBAR: + g_value_set_boolean (value, options->show_menubar); + break; + case PROP_SHOW_STATUSBAR: + g_value_set_boolean (value, options->show_statusbar); + break; + case PROP_SHOW_RULERS: + g_value_set_boolean (value, options->show_rulers); + break; + case PROP_SHOW_SCROLLBARS: + g_value_set_boolean (value, options->show_scrollbars); + break; + case PROP_SHOW_SELECTION: + g_value_set_boolean (value, options->show_selection); + break; + case PROP_SHOW_LAYER_BOUNDARY: + g_value_set_boolean (value, options->show_layer_boundary); + break; + case PROP_SHOW_CANVAS_BOUNDARY: + g_value_set_boolean (value, options->show_canvas_boundary); + break; + case PROP_SHOW_GUIDES: + g_value_set_boolean (value, options->show_guides); + break; + case PROP_SHOW_GRID: + g_value_set_boolean (value, options->show_grid); + break; + case PROP_SHOW_SAMPLE_POINTS: + g_value_set_boolean (value, options->show_sample_points); + break; + case PROP_SNAP_TO_GUIDES: + g_value_set_boolean (value, options->snap_to_guides); + break; + case PROP_SNAP_TO_GRID: + g_value_set_boolean (value, options->snap_to_grid); + break; + case PROP_SNAP_TO_CANVAS: + g_value_set_boolean (value, options->snap_to_canvas); + break; + case PROP_SNAP_TO_PATH: + g_value_set_boolean (value, options->snap_to_path); + break; + case PROP_PADDING_MODE: + g_value_set_enum (value, options->padding_mode); + break; + case PROP_PADDING_COLOR: + g_value_set_boxed (value, &options->padding_color); + break; + case PROP_PADDING_IN_SHOW_ALL: + g_value_set_boolean (value, options->padding_in_show_all); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/config/gimpdisplayoptions.h b/app/config/gimpdisplayoptions.h new file mode 100644 index 0000000..8da5e7a --- /dev/null +++ b/app/config/gimpdisplayoptions.h @@ -0,0 +1,78 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDisplayOptions + * Copyright (C) 2003 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 . + */ + +#ifndef __GIMP_DISPLAY_OPTIONS_H__ +#define __GIMP_DISPLAY_OPTIONS_H__ + + +#define GIMP_TYPE_DISPLAY_OPTIONS (gimp_display_options_get_type ()) +#define GIMP_TYPE_DISPLAY_OPTIONS_FULLSCREEN (gimp_display_options_fullscreen_get_type ()) +#define GIMP_TYPE_DISPLAY_OPTIONS_NO_IMAGE (gimp_display_options_no_image_get_type ()) + +#define GIMP_DISPLAY_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DISPLAY_OPTIONS, GimpDisplayOptions)) +#define GIMP_DISPLAY_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DISPLAY_OPTIONS, GimpDisplayOptionsClass)) +#define GIMP_IS_DISPLAY_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DISPLAY_OPTIONS)) +#define GIMP_IS_DISPLAY_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DISPLAY_OPTIONS)) +#define GIMP_DISPLAY_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DISPLAY_OPTIONS, GimpDisplayOptionsClass)) + + +typedef struct _GimpDisplayOptionsClass GimpDisplayOptionsClass; + +struct _GimpDisplayOptions +{ + GObject parent_instance; + + /* GimpImageWindow options */ + gboolean show_menubar; + gboolean show_statusbar; + + /* GimpDisplayShell options */ + gboolean show_rulers; + gboolean show_scrollbars; + gboolean show_selection; + gboolean show_layer_boundary; + gboolean show_canvas_boundary; + gboolean show_guides; + gboolean show_grid; + gboolean show_sample_points; + + gboolean snap_to_guides; + gboolean snap_to_grid; + gboolean snap_to_canvas; + gboolean snap_to_path; + + GimpCanvasPaddingMode padding_mode; + GimpRGB padding_color; + gboolean padding_mode_set; + gboolean padding_in_show_all; +}; + +struct _GimpDisplayOptionsClass +{ + GObjectClass parent_class; +}; + + +GType gimp_display_options_get_type (void) G_GNUC_CONST; +GType gimp_display_options_fullscreen_get_type (void) G_GNUC_CONST; +GType gimp_display_options_no_image_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_DISPLAY_OPTIONS_H__ */ diff --git a/app/config/gimpgeglconfig.c b/app/config/gimpgeglconfig.c new file mode 100644 index 0000000..1994c44 --- /dev/null +++ b/app/config/gimpgeglconfig.c @@ -0,0 +1,273 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGeglConfig class + * Copyright (C) 2001 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core/core-types.h" /* eek */ + +#include "gimprc-blurbs.h" +#include "gimpgeglconfig.h" + +#include "core/gimp-utils.h" + +#include "gimp-debug.h" + +#include "gimp-intl.h" + + +#define GIMP_DEFAULT_SWAP_COMPRESSION "fast" + +#define GIMP_MAX_MEM_PROCESS (MIN (G_MAXSIZE, GIMP_MAX_MEMSIZE)) + + +enum +{ + PROP_0, + PROP_TEMP_PATH, + PROP_SWAP_PATH, + PROP_SWAP_COMPRESSION, + PROP_NUM_PROCESSORS, + PROP_TILE_CACHE_SIZE, + PROP_USE_OPENCL, + + /* ignored, only for backward compatibility: */ + PROP_STINGY_MEMORY_USE +}; + + +static void gimp_gegl_config_constructed (GObject *object); +static void gimp_gegl_config_finalize (GObject *object); +static void gimp_gegl_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_gegl_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpGeglConfig, gimp_gegl_config, G_TYPE_OBJECT) + +#define parent_class gimp_gegl_config_parent_class + + +static void +gimp_gegl_config_class_init (GimpGeglConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + gint n_threads; + gint max_n_threads; + guint64 memory_size; + + parent_class = g_type_class_peek_parent (klass); + + object_class->constructed = gimp_gegl_config_constructed; + object_class->finalize = gimp_gegl_config_finalize; + object_class->set_property = gimp_gegl_config_set_property; + object_class->get_property = gimp_gegl_config_get_property; + + GIMP_CONFIG_PROP_PATH (object_class, PROP_TEMP_PATH, + "temp-path", + "Temp path", + TEMP_PATH_BLURB, + GIMP_CONFIG_PATH_DIR, + "${gimp_temp_dir}", + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_SWAP_PATH, + "swap-path", + "Swap path", + SWAP_PATH_BLURB, + GIMP_CONFIG_PATH_DIR, + "${gimp_cache_dir}", + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_SWAP_COMPRESSION, + "swap-compression", + "Swap compression", + SWAP_COMPRESSION_BLURB, + GIMP_DEFAULT_SWAP_COMPRESSION, + GIMP_PARAM_STATIC_STRINGS); + + n_threads = g_get_num_processors (); + + max_n_threads = + G_PARAM_SPEC_INT (g_object_class_find_property (G_OBJECT_GET_CLASS (gegl_config ()), + "threads"))->maximum; + + n_threads = MIN (n_threads, max_n_threads); + + GIMP_CONFIG_PROP_INT (object_class, PROP_NUM_PROCESSORS, + "num-processors", + "Number of threads to use", + NUM_PROCESSORS_BLURB, + 1, max_n_threads, n_threads, + GIMP_PARAM_STATIC_STRINGS); + + memory_size = gimp_get_physical_memory_size (); + + /* limit to the amount one process can handle */ + memory_size = MIN (GIMP_MAX_MEM_PROCESS, memory_size); + + if (memory_size > 0) + memory_size = memory_size / 2; /* half the memory */ + else + memory_size = 1 << 30; /* 1GB */ + + GIMP_CONFIG_PROP_MEMSIZE (object_class, PROP_TILE_CACHE_SIZE, + "tile-cache-size", + "Tile cache size", + TILE_CACHE_SIZE_BLURB, + 0, GIMP_MAX_MEM_PROCESS, + memory_size, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_CONFIRM); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_OPENCL, + "use-opencl", + "Use OpenCL", + USE_OPENCL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + /* only for backward compatibility: */ + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_STINGY_MEMORY_USE, + "stingy-memory-use", + NULL, NULL, + FALSE, + GIMP_CONFIG_PARAM_IGNORE); +} + +static void +gimp_gegl_config_init (GimpGeglConfig *config) +{ +} + +static void +gimp_gegl_config_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_debug_add_instance (object, G_OBJECT_GET_CLASS (object)); +} + +static void +gimp_gegl_config_finalize (GObject *object) +{ + GimpGeglConfig *gegl_config = GIMP_GEGL_CONFIG (object); + + g_free (gegl_config->temp_path); + g_free (gegl_config->swap_path); + g_free (gegl_config->swap_compression); + + gimp_debug_remove_instance (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_gegl_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpGeglConfig *gegl_config = GIMP_GEGL_CONFIG (object); + + switch (property_id) + { + case PROP_TEMP_PATH: + g_free (gegl_config->temp_path); + gegl_config->temp_path = g_value_dup_string (value); + break; + case PROP_SWAP_PATH: + g_free (gegl_config->swap_path); + gegl_config->swap_path = g_value_dup_string (value); + break; + case PROP_SWAP_COMPRESSION: + g_free (gegl_config->swap_compression); + gegl_config->swap_compression = g_value_dup_string (value); + break; + case PROP_NUM_PROCESSORS: + gegl_config->num_processors = g_value_get_int (value); + break; + case PROP_TILE_CACHE_SIZE: + gegl_config->tile_cache_size = g_value_get_uint64 (value); + break; + case PROP_USE_OPENCL: + gegl_config->use_opencl = g_value_get_boolean (value); + break; + + case PROP_STINGY_MEMORY_USE: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_gegl_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpGeglConfig *gegl_config = GIMP_GEGL_CONFIG (object); + + switch (property_id) + { + case PROP_TEMP_PATH: + g_value_set_string (value, gegl_config->temp_path); + break; + case PROP_SWAP_PATH: + g_value_set_string (value, gegl_config->swap_path); + break; + case PROP_SWAP_COMPRESSION: + g_value_set_string (value, gegl_config->swap_compression); + break; + case PROP_NUM_PROCESSORS: + g_value_set_int (value, gegl_config->num_processors); + break; + case PROP_TILE_CACHE_SIZE: + g_value_set_uint64 (value, gegl_config->tile_cache_size); + break; + case PROP_USE_OPENCL: + g_value_set_boolean (value, gegl_config->use_opencl); + break; + + case PROP_STINGY_MEMORY_USE: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/config/gimpgeglconfig.h b/app/config/gimpgeglconfig.h new file mode 100644 index 0000000..4ca948f --- /dev/null +++ b/app/config/gimpgeglconfig.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGeglConfig class + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_GEGL_CONFIG_H__ +#define __GIMP_GEGL_CONFIG_H__ + + +#define GIMP_TYPE_GEGL_CONFIG (gimp_gegl_config_get_type ()) +#define GIMP_GEGL_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GEGL_CONFIG, GimpGeglConfig)) +#define GIMP_GEGL_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GEGL_CONFIG, GimpGeglConfigClass)) +#define GIMP_IS_GEGL_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GEGL_CONFIG)) +#define GIMP_IS_GEGL_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GEGL_CONFIG)) + + +typedef struct _GimpGeglConfigClass GimpGeglConfigClass; + +struct _GimpGeglConfig +{ + GObject parent_instance; + + gchar *temp_path; + gchar *swap_path; + gchar *swap_compression; + gint num_processors; + guint64 tile_cache_size; + gboolean use_opencl; +}; + +struct _GimpGeglConfigClass +{ + GObjectClass parent_class; +}; + + +GType gimp_gegl_config_get_type (void) G_GNUC_CONST; + + +#endif /* GIMP_GEGL_CONFIG_H__ */ diff --git a/app/config/gimpguiconfig.c b/app/config/gimpguiconfig.c new file mode 100644 index 0000000..79ff839 --- /dev/null +++ b/app/config/gimpguiconfig.c @@ -0,0 +1,1017 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGuiConfig class + * Copyright (C) 2001 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 . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-blurbs.h" +#include "gimpguiconfig.h" + +#include "gimp-intl.h" + + +#ifdef HAVE_WEBKIT +#define DEFAULT_HELP_BROWSER GIMP_HELP_BROWSER_GIMP +#else +#define DEFAULT_HELP_BROWSER GIMP_HELP_BROWSER_WEB_BROWSER +#endif + +#define DEFAULT_USER_MANUAL_ONLINE_URI \ + "https://docs.gimp.org/" GIMP_APP_VERSION_STRING + + +enum +{ + SIZE_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_EDIT_NON_VISIBLE, + PROP_MOVE_TOOL_CHANGES_ACTIVE, + PROP_FILTER_TOOL_MAX_RECENT, + PROP_FILTER_TOOL_USE_LAST_SETTINGS, + PROP_FILTER_TOOL_SHOW_COLOR_OPTIONS, + PROP_TRUST_DIRTY_FLAG, + PROP_SAVE_DEVICE_STATUS, + PROP_DEVICES_SHARE_TOOL, + PROP_SAVE_SESSION_INFO, + PROP_RESTORE_SESSION, + PROP_RESTORE_MONITOR, + PROP_SAVE_TOOL_OPTIONS, + PROP_COMPACT_SLIDERS, + PROP_SHOW_TOOLTIPS, + PROP_TEAROFF_MENUS, + PROP_CAN_CHANGE_ACCELS, + PROP_SAVE_ACCELS, + PROP_RESTORE_ACCELS, + PROP_LAST_OPENED_SIZE, + PROP_MAX_NEW_IMAGE_SIZE, + PROP_TOOLBOX_COLOR_AREA, + PROP_TOOLBOX_FOO_AREA, + PROP_TOOLBOX_IMAGE_AREA, + PROP_TOOLBOX_WILBER, + PROP_TOOLBOX_GROUPS, + PROP_TOOLBOX_GROUP_MENU_MODE, + PROP_THEME_PATH, + PROP_THEME, + PROP_ICON_THEME_PATH, + PROP_ICON_THEME, + PROP_ICON_SIZE, + PROP_USE_HELP, + PROP_SHOW_HELP_BUTTON, + PROP_HELP_LOCALES, + PROP_HELP_BROWSER, + PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS, + PROP_ACTION_HISTORY_SIZE, + PROP_USER_MANUAL_ONLINE, + PROP_USER_MANUAL_ONLINE_URI, + PROP_DOCK_WINDOW_HINT, + PROP_CURSOR_HANDEDNESS, + + PROP_PLAYGROUND_NPD_TOOL, + PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL, + + PROP_HIDE_DOCKS, + PROP_SINGLE_WINDOW_MODE, + PROP_SHOW_TABS, + PROP_TABS_POSITION, + PROP_LAST_TIP_SHOWN, + + /* ignored, only for backward compatibility: */ + PROP_CURSOR_FORMAT, + PROP_IMAGE_MAP_TOOL_MAX_RECENT, + PROP_INFO_WINDOW_PER_DISPLAY, + PROP_MENU_MNEMONICS, + PROP_SHOW_TOOL_TIPS, + PROP_SHOW_TIPS, + PROP_TOOLBOX_WINDOW_HINT, + PROP_TRANSIENT_DOCKS, + PROP_WEB_BROWSER +}; + + +static void gimp_gui_config_finalize (GObject *object); +static void gimp_gui_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_gui_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void monitor_resolution_changed (GimpDisplayConfig *display_config, + GParamSpec *pspec, + GimpGuiConfig *gui_config); + +G_DEFINE_TYPE (GimpGuiConfig, gimp_gui_config, GIMP_TYPE_DISPLAY_CONFIG) + +#define parent_class gimp_gui_config_parent_class + +static guint signals[LAST_SIGNAL] = { 0, }; + +static void +gimp_gui_config_class_init (GimpGuiConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + gchar *path; + + signals[SIZE_CHANGED] = + g_signal_new ("size-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpGuiConfigClass, size_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_gui_config_finalize; + object_class->set_property = gimp_gui_config_set_property; + object_class->get_property = gimp_gui_config_get_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EDIT_NON_VISIBLE, + "edit-non-visible", + "Non-visible layers can be edited", + EDIT_NON_VISIBLE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MOVE_TOOL_CHANGES_ACTIVE, + "move-tool-changes-active", + "Move tool changes active layer", + MOVE_TOOL_CHANGES_ACTIVE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_FILTER_TOOL_MAX_RECENT, + "filter-tool-max-recent", + "Max recent settings to keep in filters", + FILTER_TOOL_MAX_RECENT_BLURB, + 0, 255, 10, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FILTER_TOOL_USE_LAST_SETTINGS, + "filter-tool-use-last-settings", + "Use last used settings in filters", + FILTER_TOOL_USE_LAST_SETTINGS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FILTER_TOOL_SHOW_COLOR_OPTIONS, + "filter-tool-show-color-options", + "Show avanced color options in filters", + FILTER_TOOL_SHOW_COLOR_OPTIONS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TRUST_DIRTY_FLAG, + "trust-dirty-flag", + "Trust dirty flag", + TRUST_DIRTY_FLAG_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_DEVICE_STATUS, + "save-device-status", + "Save device status", + SAVE_DEVICE_STATUS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DEVICES_SHARE_TOOL, + "devices-share-tool", + "Devices share tool", + DEVICES_SHARE_TOOL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_SESSION_INFO, + "save-session-info", + "Save session", + SAVE_SESSION_INFO_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_RESTORE_SESSION, + "restore-session", + "Restore session", + RESTORE_SESSION_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_RESTORE_MONITOR, + "restore-monitor", + "Restore monitor", + RESTORE_MONITOR_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_TOOL_OPTIONS, + "save-tool-options", + "Save tool options", + SAVE_TOOL_OPTIONS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COMPACT_SLIDERS, + "compact-sliders", + "Compact sliders", + COMPACT_SLIDERS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_TOOLTIPS, + "show-tooltips", + "Show tooltips", + SHOW_TOOLTIPS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TEAROFF_MENUS, + "tearoff-menus", + "Tearoff menus", + TEAROFF_MENUS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CAN_CHANGE_ACCELS, + "can-change-accels", + "Can change accelerators", + CAN_CHANGE_ACCELS_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_ACCELS, + "save-accels", + "Save accelerators", + SAVE_ACCELS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_RESTORE_ACCELS, + "restore-accels", + "Restore acclerator", + RESTORE_ACCELS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_LAST_OPENED_SIZE, + "last-opened-size", + "Size of recently used menu", + LAST_OPENED_SIZE_BLURB, + 0, 1024, 10, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_MEMSIZE (object_class, PROP_MAX_NEW_IMAGE_SIZE, + "max-new-image-size", + "Maximum new image size", + MAX_NEW_IMAGE_SIZE_BLURB, + 0, GIMP_MAX_MEMSIZE, 1 << 27, /* 128MB */ + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TOOLBOX_COLOR_AREA, + "toolbox-color-area", + "Show toolbox color area", + TOOLBOX_COLOR_AREA_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TOOLBOX_FOO_AREA, + "toolbox-foo-area", + "Show toolbox foo area", + TOOLBOX_FOO_AREA_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TOOLBOX_IMAGE_AREA, + "toolbox-image-area", + "Show toolbox image area", + TOOLBOX_IMAGE_AREA_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TOOLBOX_WILBER, + "toolbox-wilber", + "Show toolbox wilber", + TOOLBOX_WILBER_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TOOLBOX_GROUPS, + "toolbox-groups", + "Use toolbox groups", + TOOLBOX_GROUPS_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TOOLBOX_GROUP_MENU_MODE, + "toolbox-group-menu-mode", + "Toolbox group menu mode", + TOOLBOX_GROUP_MENU_MODE_BLURB, + GIMP_TYPE_TOOL_GROUP_MENU_MODE, + GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN, + GIMP_PARAM_STATIC_STRINGS); + + path = gimp_config_build_data_path ("themes"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_THEME_PATH, + "theme-path", + "Theme path", + THEME_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_THEME, + "theme", + "Theme", + THEME_BLURB, + GIMP_CONFIG_DEFAULT_THEME, + GIMP_PARAM_STATIC_STRINGS); + + path = gimp_config_build_data_path ("icons"); + GIMP_CONFIG_PROP_PATH (object_class, PROP_ICON_THEME_PATH, + "icon-theme-path", + "Icon theme path", + ICON_THEME_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + g_free (path); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_ICON_THEME, + "icon-theme", + "Icon theme", + ICON_THEME_BLURB, + GIMP_CONFIG_DEFAULT_ICON_THEME, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_ENUM (object_class, PROP_ICON_SIZE, + "icon-size", + "icon-size", + ICON_SIZE_BLURB, + GIMP_TYPE_ICON_SIZE, + GIMP_ICON_SIZE_AUTO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_HELP, + "use-help", + "Use help", + USE_HELP_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_HELP_BUTTON, + "show-help-button", + "Show help button", + SHOW_HELP_BUTTON_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_HELP_LOCALES, + "help-locales", + "Help locales", + HELP_LOCALES_BLURB, + "", + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_HELP_BROWSER, + "help-browser", + "Help browser", + HELP_BROWSER_BLURB, + GIMP_TYPE_HELP_BROWSER_TYPE, + DEFAULT_HELP_BROWSER, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USER_MANUAL_ONLINE, + "user-manual-online", + "User manual online", + USER_MANUAL_ONLINE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_USER_MANUAL_ONLINE_URI, + "user-manual-online-uri", + "User manual online URI", + USER_MANUAL_ONLINE_URI_BLURB, + DEFAULT_USER_MANUAL_ONLINE_URI, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS, + "search-show-unavailable-actions", + "Show unavailable actions", + SEARCH_SHOW_UNAVAILABLE_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_ACTION_HISTORY_SIZE, + "action-history-size", + "Action history size", + ACTION_HISTORY_SIZE_BLURB, + 0, 1000, 100, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_DOCK_WINDOW_HINT, + "dock-window-hint", + "Dock window hint", + DOCK_WINDOW_HINT_BLURB, + GIMP_TYPE_WINDOW_HINT, + GIMP_WINDOW_HINT_UTILITY, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CURSOR_HANDEDNESS, + "cursor-handedness", + "Cursor handedness", + CURSOR_HANDEDNESS_BLURB, + GIMP_TYPE_HANDEDNESS, + GIMP_HANDEDNESS_RIGHT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PLAYGROUND_NPD_TOOL, + "playground-npd-tool", + "Playground N-Point Deformation tool", + PLAYGROUND_NPD_TOOL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, + PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL, + "playground-seamless-clone-tool", + "Playground Seamless Clone tool", + PLAYGROUND_SEAMLESS_CLONE_TOOL_BLURB, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_RESTART); + + g_object_class_install_property (object_class, PROP_HIDE_DOCKS, + g_param_spec_boolean ("hide-docks", + NULL, + HIDE_DOCKS_BLURB, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_SINGLE_WINDOW_MODE, + g_param_spec_boolean ("single-window-mode", + NULL, + SINGLE_WINDOW_MODE_BLURB, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_SHOW_TABS, + g_param_spec_boolean ("show-tabs", + NULL, + SHOW_TABS_BLURB, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_TABS_POSITION, + g_param_spec_enum ("tabs-position", NULL, NULL, + GIMP_TYPE_POSITION, + GIMP_POSITION_TOP, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_LAST_TIP_SHOWN, + g_param_spec_int ("last-tip-shown", + NULL, NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS)); + + /* only for backward compatibility: */ + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CURSOR_FORMAT, + "cursor-format", + NULL, NULL, + GIMP_TYPE_CURSOR_FORMAT, + GIMP_CURSOR_FORMAT_PIXBUF, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_INT (object_class, PROP_IMAGE_MAP_TOOL_MAX_RECENT, + "image-map-tool-max-recent", + NULL, NULL, + 0, 255, 10, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_INFO_WINDOW_PER_DISPLAY, + "info-window-per-display", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MENU_MNEMONICS, + "menu-mnemonics", + NULL, NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_TOOL_TIPS, + "show-tool-tips", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_TIPS, + "show-tips", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TOOLBOX_WINDOW_HINT, + "toolbox-window-hint", + NULL, NULL, + GIMP_TYPE_WINDOW_HINT, + GIMP_WINDOW_HINT_UTILITY, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_TRANSIENT_DOCKS, + "transient-docks", + NULL, NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); + + GIMP_CONFIG_PROP_PATH (object_class, PROP_WEB_BROWSER, + "web-browser", + NULL, NULL, + GIMP_CONFIG_PATH_FILE, + "not used any longer", + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_IGNORE); +} + +static void +gimp_gui_config_init (GimpGuiConfig *config) +{ +} + +static void +gimp_gui_config_finalize (GObject *object) +{ + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (object); + + g_free (gui_config->theme_path); + g_free (gui_config->theme); + g_free (gui_config->icon_theme_path); + g_free (gui_config->icon_theme); + g_free (gui_config->help_locales); + g_free (gui_config->user_manual_online_uri); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_gui_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (object); + + switch (property_id) + { + case PROP_EDIT_NON_VISIBLE: + gui_config->edit_non_visible = g_value_get_boolean (value); + break; + case PROP_MOVE_TOOL_CHANGES_ACTIVE: + gui_config->move_tool_changes_active = g_value_get_boolean (value); + break; + case PROP_FILTER_TOOL_MAX_RECENT: + case PROP_IMAGE_MAP_TOOL_MAX_RECENT: + gui_config->filter_tool_max_recent = g_value_get_int (value); + break; + case PROP_FILTER_TOOL_USE_LAST_SETTINGS: + gui_config->filter_tool_use_last_settings = g_value_get_boolean (value); + break; + case PROP_FILTER_TOOL_SHOW_COLOR_OPTIONS: + gui_config->filter_tool_show_color_options = g_value_get_boolean (value); + break; + case PROP_TRUST_DIRTY_FLAG: + gui_config->trust_dirty_flag = g_value_get_boolean (value); + break; + case PROP_SAVE_DEVICE_STATUS: + gui_config->save_device_status = g_value_get_boolean (value); + break; + case PROP_DEVICES_SHARE_TOOL: + gui_config->devices_share_tool = g_value_get_boolean (value); + break; + case PROP_SAVE_SESSION_INFO: + gui_config->save_session_info = g_value_get_boolean (value); + break; + case PROP_RESTORE_SESSION: + gui_config->restore_session = g_value_get_boolean (value); + break; + case PROP_RESTORE_MONITOR: + gui_config->restore_monitor = g_value_get_boolean (value); + break; + case PROP_SAVE_TOOL_OPTIONS: + gui_config->save_tool_options = g_value_get_boolean (value); + break; + case PROP_COMPACT_SLIDERS: + gui_config->compact_sliders = g_value_get_boolean (value); + break; + case PROP_SHOW_TOOLTIPS: + gui_config->show_tooltips = g_value_get_boolean (value); + break; + case PROP_TEAROFF_MENUS: + gui_config->tearoff_menus = g_value_get_boolean (value); + break; + case PROP_CAN_CHANGE_ACCELS: + gui_config->can_change_accels = g_value_get_boolean (value); + break; + case PROP_SAVE_ACCELS: + gui_config->save_accels = g_value_get_boolean (value); + break; + case PROP_RESTORE_ACCELS: + gui_config->restore_accels = g_value_get_boolean (value); + break; + case PROP_LAST_OPENED_SIZE: + gui_config->last_opened_size = g_value_get_int (value); + break; + case PROP_MAX_NEW_IMAGE_SIZE: + gui_config->max_new_image_size = g_value_get_uint64 (value); + break; + case PROP_TOOLBOX_COLOR_AREA: + gui_config->toolbox_color_area = g_value_get_boolean (value); + break; + case PROP_TOOLBOX_FOO_AREA: + gui_config->toolbox_foo_area = g_value_get_boolean (value); + break; + case PROP_TOOLBOX_IMAGE_AREA: + gui_config->toolbox_image_area = g_value_get_boolean (value); + break; + case PROP_TOOLBOX_WILBER: + gui_config->toolbox_wilber = g_value_get_boolean (value); + break; + case PROP_TOOLBOX_GROUPS: + gui_config->toolbox_groups = g_value_get_boolean (value); + break; + case PROP_TOOLBOX_GROUP_MENU_MODE: + gui_config->toolbox_group_menu_mode = g_value_get_enum (value); + break; + case PROP_THEME_PATH: + g_free (gui_config->theme_path); + gui_config->theme_path = g_value_dup_string (value); + break; + case PROP_THEME: + g_free (gui_config->theme); + gui_config->theme = g_value_dup_string (value); + break; + case PROP_ICON_THEME_PATH: + g_free (gui_config->icon_theme_path); + gui_config->icon_theme_path = g_value_dup_string (value); + break; + case PROP_ICON_THEME: + g_free (gui_config->icon_theme); + gui_config->icon_theme = g_value_dup_string (value); + break; + case PROP_ICON_SIZE: + { + GimpIconSize size = g_value_get_enum (value); + + g_signal_handlers_disconnect_by_func (GIMP_DISPLAY_CONFIG (gui_config), + G_CALLBACK (monitor_resolution_changed), + gui_config); + if (size == GIMP_ICON_SIZE_AUTO) + { + g_signal_connect (GIMP_DISPLAY_CONFIG (gui_config), + "notify::monitor-xresolution", + G_CALLBACK (monitor_resolution_changed), + gui_config); + g_signal_connect (GIMP_DISPLAY_CONFIG (gui_config), + "notify::monitor-yresolution", + G_CALLBACK (monitor_resolution_changed), + gui_config); + } + gui_config->icon_size = size; + g_signal_emit (gui_config, signals[SIZE_CHANGED], 0); + } + break; + case PROP_USE_HELP: + gui_config->use_help = g_value_get_boolean (value); + break; + case PROP_SHOW_HELP_BUTTON: + gui_config->show_help_button = g_value_get_boolean (value); + break; + case PROP_HELP_LOCALES: + g_free (gui_config->help_locales); + gui_config->help_locales = g_value_dup_string (value); + break; + case PROP_HELP_BROWSER: + gui_config->help_browser = g_value_get_enum (value); + break; + case PROP_USER_MANUAL_ONLINE: + gui_config->user_manual_online = g_value_get_boolean (value); + break; + case PROP_USER_MANUAL_ONLINE_URI: + g_free (gui_config->user_manual_online_uri); + gui_config->user_manual_online_uri = g_value_dup_string (value); + break; + case PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS: + gui_config->search_show_unavailable = g_value_get_boolean (value); + break; + case PROP_ACTION_HISTORY_SIZE: + gui_config->action_history_size = g_value_get_int (value); + break; + case PROP_DOCK_WINDOW_HINT: + gui_config->dock_window_hint = g_value_get_enum (value); + break; + case PROP_CURSOR_HANDEDNESS: + gui_config->cursor_handedness = g_value_get_enum (value); + break; + + case PROP_PLAYGROUND_NPD_TOOL: + gui_config->playground_npd_tool = g_value_get_boolean (value); + break; + case PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL: + gui_config->playground_seamless_clone_tool = g_value_get_boolean (value); + break; + + case PROP_HIDE_DOCKS: + gui_config->hide_docks = g_value_get_boolean (value); + break; + case PROP_SINGLE_WINDOW_MODE: + gui_config->single_window_mode = g_value_get_boolean (value); + break; + case PROP_SHOW_TABS: + gui_config->show_tabs = g_value_get_boolean (value); + break; + case PROP_TABS_POSITION: + gui_config->tabs_position = g_value_get_enum (value); + break; + case PROP_LAST_TIP_SHOWN: + gui_config->last_tip_shown = g_value_get_int (value); + break; + + case PROP_CURSOR_FORMAT: + case PROP_INFO_WINDOW_PER_DISPLAY: + case PROP_MENU_MNEMONICS: + case PROP_SHOW_TOOL_TIPS: + case PROP_SHOW_TIPS: + case PROP_TOOLBOX_WINDOW_HINT: + case PROP_TRANSIENT_DOCKS: + case PROP_WEB_BROWSER: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_gui_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (object); + + switch (property_id) + { + case PROP_EDIT_NON_VISIBLE: + g_value_set_boolean (value, gui_config->edit_non_visible); + break; + case PROP_MOVE_TOOL_CHANGES_ACTIVE: + g_value_set_boolean (value, gui_config->move_tool_changes_active); + break; + case PROP_FILTER_TOOL_MAX_RECENT: + case PROP_IMAGE_MAP_TOOL_MAX_RECENT: + g_value_set_int (value, gui_config->filter_tool_max_recent); + break; + case PROP_FILTER_TOOL_USE_LAST_SETTINGS: + g_value_set_boolean (value, gui_config->filter_tool_use_last_settings); + break; + case PROP_FILTER_TOOL_SHOW_COLOR_OPTIONS: + g_value_set_boolean (value, gui_config->filter_tool_show_color_options); + break; + case PROP_TRUST_DIRTY_FLAG: + g_value_set_boolean (value, gui_config->trust_dirty_flag); + break; + case PROP_SAVE_DEVICE_STATUS: + g_value_set_boolean (value, gui_config->save_device_status); + break; + case PROP_DEVICES_SHARE_TOOL: + g_value_set_boolean (value, gui_config->devices_share_tool); + break; + case PROP_SAVE_SESSION_INFO: + g_value_set_boolean (value, gui_config->save_session_info); + break; + case PROP_RESTORE_SESSION: + g_value_set_boolean (value, gui_config->restore_session); + break; + case PROP_RESTORE_MONITOR: + g_value_set_boolean (value, gui_config->restore_monitor); + break; + case PROP_SAVE_TOOL_OPTIONS: + g_value_set_boolean (value, gui_config->save_tool_options); + break; + case PROP_COMPACT_SLIDERS: + g_value_set_boolean (value, gui_config->compact_sliders); + break; + case PROP_SHOW_TOOLTIPS: + g_value_set_boolean (value, gui_config->show_tooltips); + break; + case PROP_TEAROFF_MENUS: + g_value_set_boolean (value, gui_config->tearoff_menus); + break; + case PROP_CAN_CHANGE_ACCELS: + g_value_set_boolean (value, gui_config->can_change_accels); + break; + case PROP_SAVE_ACCELS: + g_value_set_boolean (value, gui_config->save_accels); + break; + case PROP_RESTORE_ACCELS: + g_value_set_boolean (value, gui_config->restore_accels); + break; + case PROP_LAST_OPENED_SIZE: + g_value_set_int (value, gui_config->last_opened_size); + break; + case PROP_MAX_NEW_IMAGE_SIZE: + g_value_set_uint64 (value, gui_config->max_new_image_size); + break; + case PROP_TOOLBOX_COLOR_AREA: + g_value_set_boolean (value, gui_config->toolbox_color_area); + break; + case PROP_TOOLBOX_FOO_AREA: + g_value_set_boolean (value, gui_config->toolbox_foo_area); + break; + case PROP_TOOLBOX_IMAGE_AREA: + g_value_set_boolean (value, gui_config->toolbox_image_area); + break; + case PROP_TOOLBOX_WILBER: + g_value_set_boolean (value, gui_config->toolbox_wilber); + break; + case PROP_TOOLBOX_GROUPS: + g_value_set_boolean (value, gui_config->toolbox_groups); + break; + case PROP_TOOLBOX_GROUP_MENU_MODE: + g_value_set_enum (value, gui_config->toolbox_group_menu_mode); + break; + case PROP_THEME_PATH: + g_value_set_string (value, gui_config->theme_path); + break; + case PROP_THEME: + g_value_set_string (value, gui_config->theme); + break; + case PROP_ICON_THEME_PATH: + g_value_set_string (value, gui_config->icon_theme_path); + break; + case PROP_ICON_THEME: + g_value_set_string (value, gui_config->icon_theme); + break; + case PROP_ICON_SIZE: + g_value_set_enum (value, gui_config->icon_size); + break; + case PROP_USE_HELP: + g_value_set_boolean (value, gui_config->use_help); + break; + case PROP_SHOW_HELP_BUTTON: + g_value_set_boolean (value, gui_config->show_help_button); + break; + case PROP_HELP_LOCALES: + g_value_set_string (value, gui_config->help_locales); + break; + case PROP_HELP_BROWSER: + g_value_set_enum (value, gui_config->help_browser); + break; + case PROP_USER_MANUAL_ONLINE: + g_value_set_boolean (value, gui_config->user_manual_online); + break; + case PROP_USER_MANUAL_ONLINE_URI: + g_value_set_string (value, gui_config->user_manual_online_uri); + break; + case PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS: + g_value_set_boolean (value, gui_config->search_show_unavailable); + break; + case PROP_ACTION_HISTORY_SIZE: + g_value_set_int (value, gui_config->action_history_size); + break; + case PROP_DOCK_WINDOW_HINT: + g_value_set_enum (value, gui_config->dock_window_hint); + break; + case PROP_CURSOR_HANDEDNESS: + g_value_set_enum (value, gui_config->cursor_handedness); + break; + + case PROP_PLAYGROUND_NPD_TOOL: + g_value_set_boolean (value, gui_config->playground_npd_tool); + break; + case PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL: + g_value_set_boolean (value, gui_config->playground_seamless_clone_tool); + break; + + case PROP_HIDE_DOCKS: + g_value_set_boolean (value, gui_config->hide_docks); + break; + case PROP_SINGLE_WINDOW_MODE: + g_value_set_boolean (value, gui_config->single_window_mode); + break; + case PROP_SHOW_TABS: + g_value_set_boolean (value, gui_config->show_tabs); + break; + case PROP_TABS_POSITION: + g_value_set_enum (value, gui_config->tabs_position); + break; + case PROP_LAST_TIP_SHOWN: + g_value_set_int (value, gui_config->last_tip_shown); + break; + + case PROP_CURSOR_FORMAT: + case PROP_INFO_WINDOW_PER_DISPLAY: + case PROP_MENU_MNEMONICS: + case PROP_SHOW_TOOL_TIPS: + case PROP_SHOW_TIPS: + case PROP_TOOLBOX_WINDOW_HINT: + case PROP_TRANSIENT_DOCKS: + case PROP_WEB_BROWSER: + /* ignored */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +monitor_resolution_changed (GimpDisplayConfig *display_config, + GParamSpec *pspec, + GimpGuiConfig *gui_config) +{ + if (gui_config->icon_size == GIMP_ICON_SIZE_AUTO) + { + g_signal_emit (gui_config, signals[SIZE_CHANGED], 0); + } +} + +GimpIconSize +gimp_gui_config_detect_icon_size (GimpGuiConfig *gui_config) +{ + GimpIconSize size = gui_config->icon_size; + + if (size == GIMP_ICON_SIZE_AUTO) + { + GimpDisplayConfig *display_config; + + display_config = GIMP_DISPLAY_CONFIG (gui_config); + + if (display_config->monitor_xres < 100.0 || + display_config->monitor_yres < 100.0) + size = GIMP_ICON_SIZE_SMALL; + else if (display_config->monitor_xres < 192.0 || + display_config->monitor_yres < 192.0) + size = GIMP_ICON_SIZE_MEDIUM; + else if (display_config->monitor_xres < 250.0 || + display_config->monitor_yres < 250.0) + size = GIMP_ICON_SIZE_LARGE; + else + size = GIMP_ICON_SIZE_HUGE; + } + + return size; +} diff --git a/app/config/gimpguiconfig.h b/app/config/gimpguiconfig.h new file mode 100644 index 0000000..b2a25ee --- /dev/null +++ b/app/config/gimpguiconfig.h @@ -0,0 +1,110 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGuiConfig class + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_GUI_CONFIG_H__ +#define __GIMP_GUI_CONFIG_H__ + +#include "config/gimpdisplayconfig.h" + + +#define GIMP_CONFIG_DEFAULT_THEME "Dark" +#define GIMP_CONFIG_DEFAULT_ICON_THEME "Symbolic" + + +#define GIMP_TYPE_GUI_CONFIG (gimp_gui_config_get_type ()) +#define GIMP_GUI_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GUI_CONFIG, GimpGuiConfig)) +#define GIMP_GUI_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GUI_CONFIG, GimpGuiConfigClass)) +#define GIMP_IS_GUI_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GUI_CONFIG)) +#define GIMP_IS_GUI_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GUI_CONFIG)) + + +typedef struct _GimpGuiConfigClass GimpGuiConfigClass; + +struct _GimpGuiConfig +{ + GimpDisplayConfig parent_instance; + + gboolean edit_non_visible; + gboolean move_tool_changes_active; + gint filter_tool_max_recent; + gboolean filter_tool_use_last_settings; + gboolean filter_tool_show_color_options; + gboolean trust_dirty_flag; + gboolean save_device_status; + gboolean devices_share_tool; + gboolean save_session_info; + gboolean restore_session; + gboolean restore_monitor; + gboolean save_tool_options; + gboolean compact_sliders; + gboolean show_tooltips; + gboolean tearoff_menus; + gboolean can_change_accels; + gboolean save_accels; + gboolean restore_accels; + gint last_opened_size; + guint64 max_new_image_size; + gboolean toolbox_color_area; + gboolean toolbox_foo_area; + gboolean toolbox_image_area; + gboolean toolbox_wilber; + gboolean toolbox_groups; + GimpToolGroupMenuMode toolbox_group_menu_mode; + gchar *theme_path; + gchar *theme; + gchar *icon_theme_path; + gchar *icon_theme; + GimpIconSize icon_size; + gboolean use_help; + gboolean show_help_button; + gchar *help_locales; + GimpHelpBrowserType help_browser; + gboolean user_manual_online; + gchar *user_manual_online_uri; + gboolean search_show_unavailable; + gint action_history_size; + GimpWindowHint dock_window_hint; + GimpHandedness cursor_handedness; + + /* experimental playground */ + gboolean playground_npd_tool; + gboolean playground_seamless_clone_tool; + + /* saved in sessionrc */ + gboolean hide_docks; + gboolean single_window_mode; + gboolean show_tabs; + GimpPosition tabs_position; + gint last_tip_shown; +}; + +struct _GimpGuiConfigClass +{ + GimpDisplayConfigClass parent_class; + + void (* size_changed) (GimpGuiConfig *config); +}; + + +GType gimp_gui_config_get_type (void) G_GNUC_CONST; + +GimpIconSize gimp_gui_config_detect_icon_size (GimpGuiConfig *config); + +#endif /* GIMP_GUI_CONFIG_H__ */ diff --git a/app/config/gimplangrc.c b/app/config/gimplangrc.c new file mode 100644 index 0000000..34e26f4 --- /dev/null +++ b/app/config/gimplangrc.c @@ -0,0 +1,280 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpLangRc: pre-parsing of gimprc returning the language. + * Copyright (C) 2017 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimplangrc.h" + +enum +{ + PROP_0, + PROP_VERBOSE, + PROP_SYSTEM_GIMPRC, + PROP_USER_GIMPRC, + PROP_LANGUAGE +}; + + +static void gimp_lang_rc_constructed (GObject *object); +static void gimp_lang_rc_finalize (GObject *object); +static void gimp_lang_rc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_lang_rc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +/* Just use GimpConfig interface's default implementation which will + * fill the PROP_LANGUAGE property. */ +G_DEFINE_TYPE_WITH_CODE (GimpLangRc, gimp_lang_rc, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +#define parent_class gimp_lang_rc_parent_class + + +static void +gimp_lang_rc_class_init (GimpLangRcClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = gimp_lang_rc_constructed; + object_class->finalize = gimp_lang_rc_finalize; + object_class->set_property = gimp_lang_rc_set_property; + object_class->get_property = gimp_lang_rc_get_property; + + g_object_class_install_property (object_class, PROP_VERBOSE, + g_param_spec_boolean ("verbose", + NULL, NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SYSTEM_GIMPRC, + g_param_spec_object ("system-gimprc", + NULL, NULL, + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_USER_GIMPRC, + g_param_spec_object ("user-gimprc", + NULL, NULL, + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_LANGUAGE, + "language", NULL, NULL, NULL, + GIMP_PARAM_STATIC_STRINGS); + +} + +static void +gimp_lang_rc_init (GimpLangRc *rc) +{ +} + +static void +gimp_lang_rc_constructed (GObject *object) +{ + GimpLangRc *rc = GIMP_LANG_RC (object); + GError *error = NULL; + + if (rc->verbose) + g_print ("Parsing '%s' for configured language.\n", + gimp_file_get_utf8_name (rc->system_gimprc)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (rc), + rc->system_gimprc, NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + g_message ("%s", error->message); + + g_clear_error (&error); + } + + if (rc->verbose) + g_print ("Parsing '%s' for configured language.\n", + gimp_file_get_utf8_name (rc->user_gimprc)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (rc), + rc->user_gimprc, NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + g_message ("%s", error->message); + + g_clear_error (&error); + } + + if (rc->verbose) + { + if (rc->language) + g_print ("Language property found: %s.\n", rc->language); + else + g_print ("No language property found.\n"); + } +} + +static void +gimp_lang_rc_finalize (GObject *object) +{ + GimpLangRc *rc = GIMP_LANG_RC (object); + + g_clear_object (&rc->system_gimprc); + g_clear_object (&rc->user_gimprc); + + g_clear_pointer (&rc->language, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_lang_rc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpLangRc *rc = GIMP_LANG_RC (object); + + switch (property_id) + { + case PROP_VERBOSE: + rc->verbose = g_value_get_boolean (value); + break; + + case PROP_SYSTEM_GIMPRC: + if (rc->system_gimprc) + g_object_unref (rc->system_gimprc); + + if (g_value_get_object (value)) + rc->system_gimprc = g_value_dup_object (value); + else + rc->system_gimprc = gimp_sysconf_directory_file ("gimprc", NULL); + break; + + case PROP_USER_GIMPRC: + if (rc->user_gimprc) + g_object_unref (rc->user_gimprc); + + if (g_value_get_object (value)) + rc->user_gimprc = g_value_dup_object (value); + else + rc->user_gimprc = gimp_directory_file ("gimprc", NULL); + break; + + case PROP_LANGUAGE: + if (rc->language) + g_free (rc->language); + rc->language = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_lang_rc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLangRc *rc = GIMP_LANG_RC (object); + + switch (property_id) + { + case PROP_VERBOSE: + g_value_set_boolean (value, rc->verbose); + break; + case PROP_SYSTEM_GIMPRC: + g_value_set_object (value, rc->system_gimprc); + break; + case PROP_USER_GIMPRC: + g_value_set_object (value, rc->user_gimprc); + break; + case PROP_LANGUAGE: + g_value_set_string (value, rc->language); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gimp_lang_rc_new: + * @system_gimprc: the name of the system-wide gimprc file or %NULL to + * use the standard location + * @user_gimprc: the name of the user gimprc file or %NULL to use the + * standard location + * @verbose: enable console messages about loading the language + * + * Creates a new GimpLangRc object which only looks for the configure + * language. + * + * Returns: the new #GimpLangRc. + */ +GimpLangRc * +gimp_lang_rc_new (GFile *system_gimprc, + GFile *user_gimprc, + gboolean verbose) +{ + GimpLangRc *rc; + + g_return_val_if_fail (system_gimprc == NULL || G_IS_FILE (system_gimprc), + NULL); + g_return_val_if_fail (user_gimprc == NULL || G_IS_FILE (user_gimprc), + NULL); + + rc = g_object_new (GIMP_TYPE_LANG_RC, + "verbose", verbose, + "system-gimprc", system_gimprc, + "user-gimprc", user_gimprc, + NULL); + + return rc; +} + +/** + * gimp_lang_rc_get_language: + * @lang_rc: a #GimpLangRc object. + * + * This function looks up the language set in `gimprc`. + * + * Return value: a newly allocated string representing the language or + * %NULL if the key couldn't be found. + **/ +gchar * +gimp_lang_rc_get_language (GimpLangRc *rc) +{ + return rc->language ? g_strdup (rc->language) : NULL; +} diff --git a/app/config/gimplangrc.h b/app/config/gimplangrc.h new file mode 100644 index 0000000..62412fd --- /dev/null +++ b/app/config/gimplangrc.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpLangRc: pre-parsing of gimprc returning the language. + * Copyright (C) 2017 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_LANG_RC_H__ +#define __GIMP_LANG_RC_H__ + + +#define GIMP_TYPE_LANG_RC (gimp_lang_rc_get_type ()) +#define GIMP_LANG_RC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LANG_RC, GimpLangRc)) +#define GIMP_LANG_RC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LANG_RC, GimpLangRcClass)) +#define GIMP_IS_LANG_RC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LANG_RC)) +#define GIMP_IS_LANG_RC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LANG_RC)) + + +typedef struct _GimpLangRcClass GimpLangRcClass; + +struct _GimpLangRc +{ + GObject parent_instance; + + GFile *user_gimprc; + GFile *system_gimprc; + gboolean verbose; + + gchar *language; +}; + +struct _GimpLangRcClass +{ + GObjectClass parent_class; +}; + + +GType gimp_lang_rc_get_type (void) G_GNUC_CONST; + +GimpLangRc * gimp_lang_rc_new (GFile *system_gimprc, + GFile *user_gimprc, + gboolean verbose); +gchar * gimp_lang_rc_get_language (GimpLangRc *rc); + + +#endif /* GIMP_LANG_RC_H__ */ + diff --git a/app/config/gimppluginconfig.c b/app/config/gimppluginconfig.c new file mode 100644 index 0000000..5fb4fe2 --- /dev/null +++ b/app/config/gimppluginconfig.c @@ -0,0 +1,217 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpPluginConfig class + * Copyright (C) 2001 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-blurbs.h" +#include "gimppluginconfig.h" + + +enum +{ + PROP_0, + PROP_FRACTALEXPLORER_PATH, + PROP_GFIG_PATH, + PROP_GFLARE_PATH, + PROP_GIMPRESSIONIST_PATH, + PROP_SCRIPT_FU_PATH +}; + + +static void gimp_plugin_config_finalize (GObject *object); +static void gimp_plugin_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_plugin_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpPluginConfig, gimp_plugin_config, GIMP_TYPE_DIALOG_CONFIG) + +#define parent_class gimp_plugin_config_parent_class + + +static void +gimp_plugin_config_class_init (GimpPluginConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + gchar *path; + + object_class->finalize = gimp_plugin_config_finalize; + object_class->set_property = gimp_plugin_config_set_property; + object_class->get_property = gimp_plugin_config_get_property; + + path = gimp_config_build_data_path ("fractalexplorer"); + GIMP_CONFIG_PROP_PATH (object_class, + PROP_FRACTALEXPLORER_PATH, + "fractalexplorer-path", + "Fractal Explorer path", + FRACTALEXPLORER_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS); + g_free (path); + + path = gimp_config_build_data_path ("gfig"); + GIMP_CONFIG_PROP_PATH (object_class, + PROP_GFIG_PATH, + "gfig-path", + "GFig path", + GFIG_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS); + g_free (path); + + path = gimp_config_build_data_path ("gflare"); + GIMP_CONFIG_PROP_PATH (object_class, + PROP_GFLARE_PATH, + "gflare-path", + "GFlare path", + GFLARE_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS); + g_free (path); + + path = gimp_config_build_data_path ("gimpressionist"); + GIMP_CONFIG_PROP_PATH (object_class, + PROP_GIMPRESSIONIST_PATH, + "gimpressionist-path", + "GIMPressionist path", + GIMPRESSIONIST_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS); + g_free (path); + + path = gimp_config_build_data_path ("scripts"); + GIMP_CONFIG_PROP_PATH (object_class, + PROP_SCRIPT_FU_PATH, + "script-fu-path", + "Script-Fu path", + SCRIPT_FU_PATH_BLURB, + GIMP_CONFIG_PATH_DIR_LIST, path, + GIMP_PARAM_STATIC_STRINGS); + g_free (path); +} + +static void +gimp_plugin_config_init (GimpPluginConfig *config) +{ +} + +static void +gimp_plugin_config_finalize (GObject *object) +{ + GimpPluginConfig *plugin_config = GIMP_PLUGIN_CONFIG (object); + + g_free (plugin_config->fractalexplorer_path); + g_free (plugin_config->gfig_path); + g_free (plugin_config->gflare_path); + g_free (plugin_config->gimpressionist_path); + g_free (plugin_config->script_fu_path); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_plugin_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpPluginConfig *plugin_config = GIMP_PLUGIN_CONFIG (object); + + switch (property_id) + { + case PROP_FRACTALEXPLORER_PATH: + g_free (plugin_config->fractalexplorer_path); + plugin_config->fractalexplorer_path = g_value_dup_string (value); + break; + + case PROP_GFIG_PATH: + g_free (plugin_config->gfig_path); + plugin_config->gfig_path = g_value_dup_string (value); + break; + + case PROP_GFLARE_PATH: + g_free (plugin_config->gflare_path); + plugin_config->gflare_path = g_value_dup_string (value); + break; + + case PROP_GIMPRESSIONIST_PATH: + g_free (plugin_config->gimpressionist_path); + plugin_config->gimpressionist_path = g_value_dup_string (value); + break; + + case PROP_SCRIPT_FU_PATH: + g_free (plugin_config->script_fu_path); + plugin_config->script_fu_path = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_plugin_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpPluginConfig *plugin_config = GIMP_PLUGIN_CONFIG (object); + + switch (property_id) + { + case PROP_FRACTALEXPLORER_PATH: + g_value_set_string (value, plugin_config->fractalexplorer_path); + break; + + case PROP_GFIG_PATH: + g_value_set_string (value, plugin_config->gfig_path); + break; + + case PROP_GFLARE_PATH: + g_value_set_string (value, plugin_config->gflare_path); + break; + + case PROP_GIMPRESSIONIST_PATH: + g_value_set_string (value, plugin_config->gimpressionist_path); + break; + + case PROP_SCRIPT_FU_PATH: + g_value_set_string (value, plugin_config->script_fu_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/config/gimppluginconfig.h b/app/config/gimppluginconfig.h new file mode 100644 index 0000000..e9b0f53 --- /dev/null +++ b/app/config/gimppluginconfig.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpPluginConfig class + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_PLUGIN_CONFIG_H__ +#define __GIMP_PLUGIN_CONFIG_H__ + +#include "config/gimpdialogconfig.h" + + +#define GIMP_TYPE_PLUGIN_CONFIG (gimp_plugin_config_get_type ()) +#define GIMP_PLUGIN_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PLUGIN_CONFIG, GimpPluginConfig)) +#define GIMP_PLUGIN_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PLUGIN_CONFIG, GimpPluginConfigClass)) +#define GIMP_IS_PLUGIN_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PLUGIN_CONFIG)) +#define GIMP_IS_PLUGIN_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PLUGIN_CONFIG)) + + +typedef struct _GimpPluginConfigClass GimpPluginConfigClass; + +struct _GimpPluginConfig +{ + GimpDialogConfig parent_instance; + + gchar *fractalexplorer_path; + gchar *gfig_path; + gchar *gflare_path; + gchar *gimpressionist_path; + gchar *script_fu_path; +}; + +struct _GimpPluginConfigClass +{ + GimpGuiConfigClass parent_class; +}; + + +GType gimp_plugin_config_get_type (void) G_GNUC_CONST; + + +#endif /* GIMP_PLUGIN_CONFIG_H__ */ diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h new file mode 100644 index 0000000..51b771a --- /dev/null +++ b/app/config/gimprc-blurbs.h @@ -0,0 +1,747 @@ +/* gimprc-blurbs.h -- descriptions for gimprc properties */ + +#ifndef __GIMP_RC_BLURBS_H__ +#define __GIMP_RC_BLURBS_H__ + + +/* Not all strings defined here are used in the user interface + * (the preferences dialog mainly) and only those that are should + * be marked for translation. + */ + +#define ACTIVATE_ON_FOCUS_BLURB \ +_("When enabled, an image will become the active image when its image " \ + "window receives the focus. This is useful for window managers using " \ + "\"click to focus\".") + +#define BRUSH_PATH_BLURB \ +"Sets the brush search path." + +#define BRUSH_PATH_WRITABLE_BLURB "" + +#define DYNAMICS_PATH_BLURB \ +_("Sets the dynamics search path.") + +#define DYNAMICS_PATH_WRITABLE_BLURB "" + +#define TOOL_PRESET_PATH_BLURB \ +_("Sets the dynamics search path.") + +#define TOOL_PRESET_PATH_WRITABLE_BLURB "" + +#define CANVAS_PADDING_COLOR_BLURB \ +_("Sets the canvas padding color used if the padding mode is set to " \ + "custom color.") + +#define CANVAS_PADDING_IN_SHOW_ALL_BLURB \ +_("Specifies whether to keep the canvas padding when \"View -> Show All\" " \ + "is enabled.") + +#define CANVAS_PADDING_MODE_BLURB \ +_("Specifies how the area around the image should be drawn.") + +#define CHECK_UPDATES_BLURB \ +_("Check for availability of GIMP updates through background internet queries.") + +#define CHECK_UPDATE_TIMESTAMP_BLURB \ +_("Timestamp of the last update check.") + +#define COLOR_MANAGEMENT_BLURB \ +"Defines the color management behavior." + +#define COLOR_PROFILE_POLICY_BLURB \ +_("How to handle embedded color profiles when opening a file.") + +#define COLOR_PROFILE_PATH_BLURB \ +_("Sets the default folder path for all color profile file dialogs.") + +#define COMPACT_SLIDERS_BLURB \ +_("Use compact style for sliders.") + +#define CURSOR_MODE_BLURB \ +_("Sets the type of mouse pointers to use.") + +#define CURSOR_HANDEDNESS_BLURB \ +_("Sets the handedness for cursor positioning.") + +#define CURSOR_UPDATING_BLURB \ +_("Context-dependent mouse pointers are helpful. They are enabled by " \ + "default. However, they require overhead that you may want to do without.") + +#define DEFAULT_BRUSH_BLURB \ +"Specify a default brush. The brush is searched for in the " \ +"specified brush path." + +#define DEFAULT_DYNAMICS_BLURB \ +"Specify a default dynamics. The dynamics is searched for in the " \ +"specified dynamics path." + +#define DEFAULT_TOOL_PRESET_BLURB \ +"Specify a default tool preset. The tool preset is searched for in the " \ +"specified tool prests path." + +#define DEFAULT_SHOW_ALL_BLURB \ +_("Show full image content by default.") + +#define DEFAULT_DOT_FOR_DOT_BLURB \ +_("When enabled, this will ensure that each pixel of an image gets " \ + "mapped to a pixel on the screen.") + +#define DEFAULT_FONT_BLURB \ +"Specify a default font." + +#define DEFAULT_GRADIENT_BLURB \ +"Specify a default gradient." + +#define DEFAULT_GRID_BLURB \ +"Specify a default image grid." + +#define DEFAULT_IMAGE_BLURB \ +"Sets the default image in the \"File/New\" dialog." + +#define DEFAULT_MYPAINT_BRUSH_BLURB \ +"Specify a default MyPaint brush." + +#define DEFAULT_PATTERN_BLURB \ +"Specify a default pattern." + +#define DEFAULT_PALETTE_BLURB \ +"Specify a default palette." + +#define DEFAULT_SNAP_DISTANCE_BLURB \ +_("This is the distance in pixels where Guide and Grid snapping " \ + "activates.") + +#define SNAP_TO_GUIDES_BLURB \ +_("Snap to guides by default in new image windows.") + +#define SNAP_TO_GRID_BLURB \ +_("Snap to the grid by default in new image windows.") + +#define SNAP_TO_CANVAS_BLURB \ +_("Snap to the canvas edges by default in new image windows.") + +#define SNAP_TO_PATH_BLURB \ +_("Snap to the active path by default in new image windows.") + +#define DEFAULT_THRESHOLD_BLURB \ +_("Tools such as fuzzy-select and bucket fill find regions based on a " \ + "seed-fill algorithm. The seed fill starts at the initially selected " \ + "pixel and progresses in all directions until the difference of pixel " \ + "intensity from the original is greater than a specified threshold. " \ + "This value represents the default threshold.") + +#define DEFAULT_VIEW_BLURB \ +"Sets the default settings for the image view." + +#define DEFAULT_FULLSCREEN_VIEW_BLURB \ +"Sets the default settings used when an image is viewed in fullscreen mode." + +#define DOCK_WINDOW_HINT_BLURB \ +_("The window type hint that is set on dock windows and the toolbox " \ + "window. This may affect the way your window manager decorates and " \ + "handles these windows.") + +#define ENVIRON_PATH_BLURB \ +"Sets the environ search path." + +#define FRACTALEXPLORER_PATH_BLURB \ +"Where to search for fractals used by the Fractal Explorer plug-in." + +#define GFIG_PATH_BLURB \ +"Where to search for Gfig figures used by the Gfig plug-in." + +#define GFLARE_PATH_BLURB \ +"Where to search for gflares used by the GFlare plug-in." + +#define GIMPRESSIONIST_PATH_BLURB \ +"Where to search for data used by the Gimpressionist plug-in." + +#define GLOBAL_BRUSH_BLURB \ +_("When enabled, the selected brush will be used for all tools.") + +#define GLOBAL_DYNAMICS_BLURB \ +_("When enabled, the selected dynamics will be used for all tools.") + +#define GLOBAL_FONT_BLURB \ +"When enabled, the selected font will be used for all tools." + +#define GLOBAL_GRADIENT_BLURB \ +_("When enabled, the selected gradient will be used for all tools.") + +#define GLOBAL_PATTERN_BLURB \ +_("When enabled, the selected pattern will be used for all tools.") + +#define GLOBAL_PALETTE_BLURB \ +"When enabled, the selected palette will be used for all tools." + +#define GRADIENT_PATH_BLURB \ +"Sets the gradient search path." + +#define GRADIENT_PATH_WRITABLE_BLURB "" + +#define FONT_PATH_BLURB \ +"Where to look for fonts in addition to the system-wide installed fonts." + +#define HELP_BROWSER_BLURB \ +_("Sets the browser used by the help system.") + +#define HELP_LOCALES_BLURB \ +"Specifies the language preferences used by the help system. This is a " \ +"colon-separated list of language identifiers with decreasing priority. " \ +"If empty, the language is taken from the user's locale setting." + +#define FILTER_TOOL_MAX_RECENT_BLURB \ +_("How many recent settings to keep around in filter tools.") + +#define FILTER_TOOL_USE_LAST_SETTINGS_BLURB \ +_("Default to the last used settings in filter tools.") + +#define FILTER_TOOL_SHOW_COLOR_OPTIONS_BLURB \ +_("Show advanced color options in filter tools.") + +#define IMAGE_STATUS_FORMAT_BLURB \ +_("Sets the text to appear in image window status bars.") + +#define IMAGE_TITLE_FORMAT_BLURB \ +_("Sets the text to appear in image window titles.") + +#define IMPORT_PROMOTE_FLOAT_BLURB \ +_("Promote imported images to floating point precision. Does not apply " \ + "to indexed images.") + +#define IMPORT_PROMOTE_DITHER_BLURB \ +_("When promoting imported images to floating point precision, also add " \ + "minimal noise in order to distribute color values a bit.") + +#define IMPORT_ADD_ALPHA_BLURB \ +_("Add an alpha channel to all layers of imported images.") + +#define IMPORT_RAW_PLUG_IN_BLURB \ +_("Which plug-in to use for importing raw digital camera files.") + +#define EXPORT_FILE_TYPE_BLURB \ +_("Export file type used by default.") + +#define EXPORT_COLOR_PROFILE_BLURB \ +_("Export the image's color profile by default.") + +/* Translators: tooltip for configuration option (checkbox). + * It determines how file export plug-ins handle Exif by default. + */ +#define EXPORT_METADATA_EXIF_BLURB \ +_("Export Exif metadata by default.") + +/* Translators: tooltip for configuration option (checkbox). + * It determines how file export plug-ins handle XMP by default. + */ +#define EXPORT_METADATA_XMP_BLURB \ +_("Export XMP metadata by default.") + +/* Translators: tooltip for configuration option (checkbox). + * It determines how file export plug-ins handle IPTC by default. + */ +#define EXPORT_METADATA_IPTC_BLURB \ +_("Export IPTC metadata by default.") + +#define GENERATE_BACKTRACE_BLURB \ +_("Try generating debug data for bug reporting when appropriate.") + +#define INITIAL_ZOOM_TO_FIT_BLURB \ +_("When enabled, this will ensure that the full image is visible after a " \ + "file is opened, otherwise it will be displayed with a scale of 1:1.") + +#define INTERPOLATION_TYPE_BLURB \ +_("Sets the level of interpolation used for scaling and other " \ + "transformations.") + +#define INTERPRETER_PATH_BLURB \ +"Sets the interpreter search path." + +#define LANGUAGE_BLURB \ +_("Specifies the language to use for the user interface.") + +#define LAST_KNOWN_RELEASE_BLURB \ +_("The last known release version of GIMP as queried from official website.") + +#define LAST_OPENED_SIZE_BLURB \ +_("How many recently opened image filenames to keep on the File menu.") + +#define LAST_RELEASE_TIMESTAMP_BLURB \ +_("The timestamp for the last known release date.") + +#define LAST_REVISION_BLURB \ +_("The last revision number for the release.") + +#define MARCHING_ANTS_SPEED_BLURB \ +_("Speed of marching ants in the selection outline. This value is in " \ + "milliseconds (less time indicates faster marching).") + +#define MAX_NEW_IMAGE_SIZE_BLURB \ +_("GIMP will warn the user if an attempt is made to create an image that " \ + "would take more memory than the size specified here.") + +#define MODULE_PATH_BLURB \ +"Sets the module search path." + +#define MONITOR_RES_FROM_GDK_BLURB \ +"When enabled, GIMP will use the monitor resolution from the windowing system." + +#define MONITOR_XRESOLUTION_BLURB \ +_("Sets the monitor's horizontal resolution, in dots per inch. If set to " \ + "0, forces the X server to be queried for both horizontal and vertical " \ + "resolution information.") + +#define MONITOR_YRESOLUTION_BLURB \ +_("Sets the monitor's vertical resolution, in dots per inch. If set to " \ + "0, forces the X server to be queried for both horizontal and vertical " \ + "resolution information.") + +#define EDIT_NON_VISIBLE_BLURB \ +_("When enabled, non-visible layers can be edited as normal.") + +#define MOVE_TOOL_CHANGES_ACTIVE_BLURB \ +_("If enabled, the move tool sets the edited layer or path as active. " \ + "This used to be the default behaviour in older versions.") + +#define MYPAINT_BRUSH_PATH_BLURB \ +"Sets the brush search path." + +#define MYPAINT_BRUSH_PATH_WRITABLE_BLURB "" + +#define NAVIGATION_PREVIEW_SIZE_BLURB \ +_("Sets the size of the navigation preview available in the lower right " \ + "corner of the image window.") + +#define NUM_PROCESSORS_BLURB \ +_("Sets how many threads GIMP should use for operations that support it.") + +#define PALETTE_PATH_BLURB \ +"Sets the palette search path." + +#define PALETTE_PATH_WRITABLE_BLURB "" + +#define PATTERN_PATH_BLURB \ +"Sets the pattern search path." + +#define PATTERN_PATH_WRITABLE_BLURB "" + +#define FILTER_HISTORY_SIZE_BLURB \ +"How many recently used filters and plug-ins to keep on the Filters menu." + +#define PLUG_IN_PATH_BLURB \ +"Sets the plug-in search path." + +#define PLUGINRC_PATH_BLURB \ +"Sets the pluginrc search path." + +#define LAYER_PREVIEWS_BLURB \ +_("Sets whether GIMP should create previews of layers and channels. " \ + "Previews in the layers and channels dialog are nice to have but they " \ + "can slow things down when working with large images.") + +#define GROUP_LAYER_PREVIEWS_BLURB \ +_("Sets whether GIMP should create previews of layer groups. " \ + "Layer group previews are more expensive than ordinary layer previews.") + +#define LAYER_PREVIEW_SIZE_BLURB \ +_("Sets the preview size used for layers and channel previews in newly " \ + "created dialogs.") + +#define QUICK_MASK_COLOR_BLURB \ +_("Sets the default quick mask color.") + +#define RESIZE_WINDOWS_ON_RESIZE_BLURB \ +_("When enabled, the image window will automatically resize itself " \ + "whenever the physical image size changes. This setting only takes " \ + "effect in multi-window mode.") + +#define RESIZE_WINDOWS_ON_ZOOM_BLURB \ +_("When enabled, the image window will automatically resize itself " \ + "when zooming into and out of images. This setting only takes " \ + "effect in multi-window mode.") + +#define RESTORE_SESSION_BLURB \ +_("Let GIMP try to restore your last saved session on each startup.") + +#define RESTORE_MONITOR_BLURB \ +_("When enabled, GIMP will try to restore windows on the monitor they " \ + "were open before. When disabled, windows will appear on the currently " \ + "used monitor.") + +#define SAVE_DEVICE_STATUS_BLURB \ +_("Remember the current tool, pattern, color, and brush across GIMP " \ + "sessions.") + +#define DEVICES_SHARE_TOOL_BLURB \ +_("When enabled, the same tool and tool options will be used for all " \ + "input devices. No tool switching will occur when the input device " \ + "changes.") + +#define SAVE_DOCUMENT_HISTORY_BLURB \ +_("Keep a permanent record of all opened and saved files in the Recent " \ + "Documents list.") + +#define SAVE_SESSION_INFO_BLURB \ +_("Save the positions and sizes of the main dialogs when GIMP exits.") + +#define SAVE_TOOL_OPTIONS_BLURB \ +_("Save the tool options when GIMP exits.") + +#define SCRIPT_FU_PATH_BLURB \ +"This path will be searched for scripts when the Script-Fu plug-in is run." + +#define SHOW_BRUSH_OUTLINE_BLURB \ +_("When enabled, all paint tools will show a preview of the current " \ + "brush's outline.") + +#define SNAP_BRUSH_OUTLINE_BLURB \ +_("When enabled, the brush outline will snap to individual dabs while " \ + "painting.") + +#define SHOW_HELP_BUTTON_BLURB \ +_("When enabled, dialogs will show a help button that gives access to " \ + "the related help page. Without this button, the help page can still " \ + "be reached by pressing F1.") + +#define SHOW_PAINT_TOOL_CURSOR_BLURB \ +_("When enabled, the mouse pointer will be shown over the image while " \ + "using a paint tool.") + +#define SHOW_MENUBAR_BLURB \ +_("When enabled, the menubar is visible by default. This can also be " \ + "toggled with the \"View->Show Menubar\" command.") + +#define SHOW_RULERS_BLURB \ +_("When enabled, the rulers are visible by default. This can also be " \ + "toggled with the \"View->Show Rulers\" command.") + +#define SHOW_SCROLLBARS_BLURB \ +_("When enabled, the scrollbars are visible by default. This can also be " \ + "toggled with the \"View->Show Scrollbars\" command.") + +#define SHOW_STATUSBAR_BLURB \ +_("When enabled, the statusbar is visible by default. This can also be " \ + "toggled with the \"View->Show Statusbar\" command.") + +#define SHOW_SELECTION_BLURB \ +_("When enabled, the selection is visible by default. This can also be " \ + "toggled with the \"View->Show Selection\" command.") + +#define SHOW_LAYER_BOUNDARY_BLURB \ +_("When enabled, the layer boundary is visible by default. This can also " \ + "be toggled with the \"View->Show Layer Boundary\" command.") + +#define SHOW_CANVAS_BOUNDARY_BLURB \ +_("When enabled, the canvas boundary is visible by default. This can also " \ + "be toggled with the \"View->Show Canvas Boundary\" command.") + +#define SHOW_GUIDES_BLURB \ +_("When enabled, the guides are visible by default. This can also be " \ + "toggled with the \"View->Show Guides\" command.") + +#define SHOW_GRID_BLURB \ +_("When enabled, the grid is visible by default. This can also be toggled " \ + "with the \"View->Show Grid\" command.") + +#define SHOW_SAMPLE_POINTS_BLURB \ +_("When enabled, the sample points are visible by default. This can also be " \ + "toggled with the \"View->Show Sample Points\" command.") + +#define SHOW_TOOLTIPS_BLURB \ +_("Show a tooltip when the pointer hovers over an item.") + +#define SINGLE_WINDOW_MODE_BLURB \ +_("Use GIMP in a single-window mode.") + +#define HIDE_DOCKS_BLURB \ +_("Hide docks and other windows, leaving only image windows.") + +#define SHOW_TABS_BLURB \ +_("Show the image tabs bar in single window mode.") + +#define PLAYGROUND_NPD_TOOL_BLURB \ +_("Enable the N-Point Deformation tool.") + +#define PLAYGROUND_HANDLE_TRANSFORM_TOOL_BLURB \ +_("Enable the Handle Transform tool.") + +#define PLAYGROUND_SYMMETRY_BLURB \ +_("Enable symmetry on painting.") + +#define PLAYGROUND_MYBRUSH_TOOL_BLURB \ +_("Enable the MyPaint Brush tool.") + +#define PLAYGROUND_SEAMLESS_CLONE_TOOL_BLURB \ +_("Enable the Seamless Clone tool.") + +#define SPACE_BAR_ACTION_BLURB \ +_("What to do when the space bar is pressed in the image window.") + +#define SWAP_COMPRESSION_BLURB \ +_("The compression method used for tile data stored in the swap file.") + +#define SWAP_PATH_BLURB \ +_("Sets the swap file location. GIMP uses a tile based memory allocation " \ + "scheme. The swap file is used to quickly and easily swap tiles out to " \ + "disk and back in. Be aware that the swap file can easily get very large " \ + "if GIMP is used with large images. " \ + "Also, things can get horribly slow if the swap file is created on " \ + "a folder that is mounted over NFS. For these reasons, it may be " \ + "desirable to put your swap file in \"/tmp\".") + +#define TEAROFF_MENUS_BLURB \ +_("When enabled, menus can be torn off.") + +#define CAN_CHANGE_ACCELS_BLURB \ +_("When enabled, you can change keyboard shortcuts for menu items " \ + "by hitting a key combination while the menu item is highlighted.") + +#define SAVE_ACCELS_BLURB \ +_("Save changed keyboard shortcuts when GIMP exits.") + +#define RESTORE_ACCELS_BLURB \ +_("Restore saved keyboard shortcuts on each GIMP startup.") + +#define TEMP_PATH_BLURB \ +_("Sets the folder for temporary storage. Files will appear here " \ + "during the course of running GIMP. Most files will disappear " \ + "when GIMP exits, but some files are likely to remain, so it " \ + "is best if this folder not be one that is shared by other users.") + +#define THEME_BLURB \ +_("The name of the theme to use.") + +#define THEME_PATH_BLURB \ +"Sets the theme search path." + +#define ICON_THEME_BLURB \ +"The name of the icon theme to use." + +#define ICON_SIZE_BLURB \ +"The size of the icons to use." + +#define ICON_THEME_PATH_BLURB \ +"Sets the icon theme search path." + +#define IMAGE_CONVERT_PROFILE_INTENT_BLURB \ +_("Sets the default rendering intent for the 'Convert to Color Profile' dialog.") + +#define IMAGE_CONVERT_PROFILE_BPC_BLURB \ +_("Sets the default 'Black Point Compensation' state for the " \ + "'Convert to Color Profile' dialog.") + +#define IMAGE_CONVERT_PRECISION_LAYER_DITHER_METHOD_BLURB \ +_("Sets the default layer dithering method for the 'Convert Precision' dialog.") + +#define IMAGE_CONVERT_PRECISION_TEXT_LAYER_DITHER_METHOD_BLURB \ +_("Sets the default text layer dithering method for the 'Convert Precision' dialog.") + +#define IMAGE_CONVERT_PRECISION_CHANNEL_DITHER_METHOD_BLURB \ +_("Sets the default channel dithering method for the 'Convert Precision' dialog.") + +#define IMAGE_CONVERT_INDEXED_PALETTE_TYPE_BLURB \ +_("Sets the default palette type for the 'Convert to Indexed' dialog.") + +#define IMAGE_CONVERT_INDEXED_MAX_COLORS_BLURB \ +_("Sets the default maximum number of colors for the 'Convert to Indexed' dialog.") + +#define IMAGE_CONVERT_INDEXED_REMOVE_DUPLICATES_BLURB \ +_("Sets the default 'Remove duplicate colors' state for the 'Convert to Indexed' dialog.") + +#define IMAGE_CONVERT_INDEXED_DITHER_TYPE_BLURB \ +_("Sets the default dithering type for the 'Convert to Indexed' dialog.") + +#define IMAGE_CONVERT_INDEXED_DITHER_ALPHA_BLURB \ +_("Sets the default 'Dither alpha' state for the 'Convert to Indexed' dialog.") + +#define IMAGE_CONVERT_INDEXED_DITHER_TEXT_LAYERS_BLURB \ +_("Sets the default 'Dither text layers' state for the 'Convert to Indexed' dialog.") + +#define IMAGE_RESIZE_FILL_TYPE_BLURB \ +_("Sets the default fill type for the 'Canvas Size' dialog.") + +#define IMAGE_RESIZE_LAYER_SET_BLURB \ +_("Sets the default set of layers to resize for the 'Canvas Size' dialog.") + +#define IMAGE_RESIZE_RESIZE_TEXT_LAYERS_BLURB \ +_("Sets the default 'Resize text layers' state for the 'Canvas Size' dialog.") + +#define LAYER_NEW_NAME_BLURB \ +_("Sets the default layer name for the 'New Layer' dialog.") + +#define LAYER_NEW_MODE_BLURB \ +_("Sets the default mode for the 'New Layer' dialog.") + +#define LAYER_NEW_BLEND_SPACE_BLURB \ +_("Sets the default blend space for the 'New Layer' dialog.") + +#define LAYER_NEW_COMPOSITE_SPACE_BLURB \ +_("Sets the default composite space for the 'New Layer' dialog.") + +#define LAYER_NEW_COMPOSITE_MODE_BLURB \ +_("Sets the default composite mode for the 'New Layer' dialog.") + +#define LAYER_NEW_OPACITY_BLURB \ +_("Sets the default opacity for the 'New Layer' dialog.") + +#define LAYER_NEW_FILL_TYPE_BLURB \ +_("Sets the default fill type for the 'New Layer' dialog.") + +#define LAYER_RESIZE_FILL_TYPE_BLURB \ +_("Sets the default fill type for the 'Layer Boundary Size' dialog.") + +#define LAYER_ADD_MASK_TYPE_BLURB \ +_("Sets the default mask for the 'Add Layer Mask' dialog.") + +#define LAYER_ADD_MASK_INVERT_BLURB \ +_("Sets the default 'invert mask' state for the 'Add Layer Mask' dialog.") + +#define LAYER_MERGE_TYPE_BLURB \ +_("Sets the default merge type for the 'Merge Visible Layers' dialog.") + +#define LAYER_MERGE_ACTIVE_GROUP_ONLY_BLURB \ +_("Sets the default 'Active group only' for the 'Merge Visible Layers' dialog.") + +#define LAYER_MERGE_DISCARD_INVISIBLE_BLURB \ +_("Sets the default 'Discard invisible' for the 'Merge Visible Layers' dialog.") + +#define CHANNEL_NEW_NAME_BLURB \ +_("Sets the default channel name for the 'New Channel' dialog.") + +#define CHANNEL_NEW_COLOR_BLURB \ +_("Sets the default color and opacity for the 'New Channel' dialog.") + +#define VECTORS_NEW_NAME_BLURB \ +_("Sets the default path name for the 'New Path' dialog.") + +#define VECTORS_EXPORT_PATH_BLURB \ +_("Sets the default folder path for the 'Export Path' dialog.") + +#define VECTORS_EXPORT_ACTIVE_ONLY_BLURB \ +_("Sets the default 'Export the active path' state for the 'Export Path' dialog.") + +#define VECTORS_IMPORT_PATH_BLURB \ +_("Sets the default folder path for the 'Import Path' dialog.") + +#define VECTORS_IMPORT_MERGE_BLURB \ +_("Sets the default 'Merge imported paths' state for the 'Import Path' dialog.") + +#define VECTORS_IMPORT_SCALE_BLURB \ +_("Sets the default 'Scale imported paths to fit size' state for the 'Import Path' dialog.") + +#define SELECTION_FEATHER_RADIUS_BLURB \ +_("Sets the default feather radius for the 'Feather Selection' dialog.") + +#define SELECTION_FEATHER_EDGE_LOCK_BLURB \ +_("Sets the default 'Selected areas continue outside the image' setting " \ + "for the 'Feather Selection' dialog.") + +#define SELECTION_GROW_RADIUS_BLURB \ +_("Sets the default grow radius for the 'Grow Selection' dialog.") + +#define SELECTION_SHRINK_RADIUS_BLURB \ +_("Sets the default shrink radius for the 'Shrink Selection' dialog.") + +#define SELECTION_SHRINK_EDGE_LOCK_BLURB \ +_("Sets the default 'Selected areas continue outside the image' setting " \ + "for the 'Shrink Selection' dialog.") + +#define SELECTION_BORDER_RADIUS_BLURB \ +_("Sets the default border radius for the 'Border Selection' dialog.") + +#define SELECTION_BORDER_EDGE_LOCK_BLURB \ +_("Sets the default 'Selected areas continue outside the image' setting " \ + "for the 'Border Selection' dialog.") + +#define SELECTION_BORDER_STYLE_BLURB \ +_("Sets the default border style for the 'Border Selection' dialog.") + +#define FILL_OPTIONS_BLURB \ +"The default fill options for the fill dialogs." + +#define STROKE_OPTIONS_BLURB \ +"The default stroke options for the stroke dialogs." + +#define THUMBNAIL_SIZE_BLURB \ +_("Sets the size of the thumbnail shown in the Open dialog.") + +#define THUMBNAIL_FILESIZE_LIMIT_BLURB \ +_("The thumbnail in the Open dialog will be automatically updated " \ + "if the file being previewed is smaller than the size set here.") + +#define TILE_CACHE_SIZE_BLURB \ +_("When the amount of pixel data exceeds this limit, GIMP will start to " \ + "swap tiles to disk. This is a lot slower but it makes it possible to " \ + "work on images that wouldn't fit into memory otherwise. If you have a " \ + "lot of RAM, you may want to set this to a higher value.") + +#define TOOLBOX_COLOR_AREA_BLURB \ +_("Show the current foreground and background colors in the toolbox.") + +#define TOOLBOX_FOO_AREA_BLURB \ +_("Show the currently selected brush, pattern and gradient in the toolbox.") + +#define TOOLBOX_GROUP_MENU_MODE_BLURB \ +_("Menu mode of grouped tools.") + +#define TOOLBOX_GROUPS_BLURB \ +_("Use a single toolbox button for grouped tools.") + +#define TOOLBOX_IMAGE_AREA_BLURB \ +_("Show the currently active image in the toolbox.") + +#define TOOLBOX_WILBER_BLURB \ +_("Show the GIMP mascot at the top of the toolbox.") + +#define TRANSPARENCY_TYPE_BLURB \ +_("Sets the manner in which transparency is displayed in images.") + +#define TRANSPARENCY_SIZE_BLURB \ +_("Sets the size of the checkerboard used to display transparency.") + +#define TRUST_DIRTY_FLAG_BLURB \ +_("When enabled, GIMP will not save an image if it has not been changed " \ + "since it was opened.") + +#define UNDO_LEVELS_BLURB \ +_("Sets the minimal number of operations that can be undone. More undo " \ + "levels are kept available until the undo-size limit is reached.") + +#define UNDO_SIZE_BLURB \ +_("Sets an upper limit to the memory that is used per image to keep " \ + "operations on the undo stack. Regardless of this setting, at least " \ + "as many undo-levels as configured can be undone.") + +#define UNDO_PREVIEW_SIZE_BLURB \ +_("Sets the size of the previews in the Undo History.") + +#define USE_HELP_BLURB \ +_("When enabled, pressing F1 will open the help browser.") + +#define USE_OPENCL_BLURB \ +_("When enabled, uses OpenCL for some operations.") + +#define USER_MANUAL_ONLINE_BLURB \ +"When enabled, the online user manual will be used by the help system. " \ +"Otherwise the locally installed copy is used." + +#define USER_MANUAL_ONLINE_URI_BLURB \ +"The location of the online user manual. This is used if " \ +"'user-manual-online' is enabled." + +#define ZOOM_QUALITY_BLURB \ +"There's a tradeoff between speed and quality of the zoomed-out display." + +#define DEFAULT_USE_EVENT_HISTORY_BLURB \ +"Bugs in event history buffer are frequent so in case of cursor " \ +"offset problems turning it off helps." + +#define SEARCH_SHOW_UNAVAILABLE_BLURB \ +_("When enabled, a search of actions will also return inactive actions.") + +#define ACTION_HISTORY_SIZE_BLURB \ +_("The maximum number of actions saved in history.") + + +#endif /* __GIMP_RC_BLURBS_H__ */ diff --git a/app/config/gimprc-deserialize.c b/app/config/gimprc-deserialize.c new file mode 100644 index 0000000..0278485 --- /dev/null +++ b/app/config/gimprc-deserialize.c @@ -0,0 +1,174 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc deserialization routines + * Copyright (C) 2001-2002 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-deserialize.h" +#include "gimprc-unknown.h" + +#include "gimp-intl.h" + + +static GTokenType gimp_rc_deserialize_unknown (GimpConfig *config, + GScanner *scanner); + + +gboolean +gimp_rc_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GObjectClass *klass; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + guint scope_id; + guint old_scope_id; + GTokenType token; + GTokenType next; + + g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE); + + klass = G_OBJECT_GET_CLASS (config); + + property_specs = g_object_class_list_properties (klass, &n_property_specs); + if (! property_specs) + return TRUE; + + scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config)); + old_scope_id = g_scanner_set_scope (scanner, scope_id); + + for (i = 0; i < n_property_specs; i++) + { + GParamSpec *prop_spec = property_specs[i]; + + if (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) + { + g_scanner_scope_add_symbol (scanner, scope_id, + prop_spec->name, prop_spec); + } + } + + g_free (property_specs); + + g_object_freeze_notify (G_OBJECT (config)); + + token = G_TOKEN_LEFT_PAREN; + + while (TRUE) + { + next = g_scanner_peek_next_token (scanner); + + if (G_UNLIKELY (next != token && ! (token == G_TOKEN_SYMBOL && + next == G_TOKEN_IDENTIFIER))) + { + break; + } + + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_IDENTIFIER: + token = gimp_rc_deserialize_unknown (config, scanner); + break; + + case G_TOKEN_SYMBOL: + token = gimp_config_deserialize_property (config, + scanner, nest_level); + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + g_scanner_set_scope (scanner, old_scope_id); + + g_object_thaw_notify (G_OBJECT (config)); + + if (token == G_TOKEN_NONE) + return FALSE; + + /* If the unknown token value couldn't be parsed the default error + message is rather confusing. We try to produce something more + meaningful here ... + */ + if (token == G_TOKEN_STRING && next == G_TOKEN_IDENTIFIER) + { + g_scanner_unexp_token (scanner, G_TOKEN_SYMBOL, NULL, NULL, NULL, + _("fatal parse error"), TRUE); + return FALSE; + } + + return gimp_config_deserialize_return (scanner, token, nest_level); +} + +static GTokenType +gimp_rc_deserialize_unknown (GimpConfig *config, + GScanner *scanner) +{ + gchar *key; + guint old_scope_id; + + old_scope_id = g_scanner_set_scope (scanner, 0); + + if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING) + return G_TOKEN_STRING; + + key = g_strdup (scanner->value.v_identifier); + + g_scanner_get_next_token (scanner); + + g_scanner_set_scope (scanner, old_scope_id); + + if (! g_utf8_validate (scanner->value.v_string, -1, NULL)) + { + g_scanner_error (scanner, + _("value for token %s is not a valid UTF-8 string"), + key); + g_free (key); + return G_TOKEN_NONE; + } + + gimp_rc_add_unknown_token (config, key, scanner->value.v_string); + g_free (key); + + return G_TOKEN_RIGHT_PAREN; +} diff --git a/app/config/gimprc-deserialize.h b/app/config/gimprc-deserialize.h new file mode 100644 index 0000000..06b39fc --- /dev/null +++ b/app/config/gimprc-deserialize.h @@ -0,0 +1,31 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc deserialization routines + * Copyright (C) 2001-2005 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 . + */ + +#ifndef __GIMP_RC_DESERIALIZE_H__ +#define __GIMP_RC_DESERIALIZE_H__ + + +gboolean gimp_rc_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); + + +#endif /* __GIMP_RC_DESERIALIZE_H__ */ diff --git a/app/config/gimprc-serialize.c b/app/config/gimprc-serialize.c new file mode 100644 index 0000000..add6ac0 --- /dev/null +++ b/app/config/gimprc-serialize.c @@ -0,0 +1,120 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc serialization routines + * Copyright (C) 2001-2005 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc.h" +#include "gimprc-serialize.h" +#include "gimprc-unknown.h" + + +static gboolean gimp_rc_serialize_properties_diff (GimpConfig *config, + GimpConfig *compare, + GimpConfigWriter *writer); +static gboolean gimp_rc_serialize_unknown_tokens (GimpConfig *config, + GimpConfigWriter *writer); + + +gboolean +gimp_rc_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + if (data && GIMP_IS_RC (data)) + { + if (! gimp_rc_serialize_properties_diff (config, data, writer)) + return FALSE; + } + else + { + if (! gimp_config_serialize_properties (config, writer)) + return FALSE; + } + + return gimp_rc_serialize_unknown_tokens (config, writer); +} + +static gboolean +gimp_rc_serialize_properties_diff (GimpConfig *config, + GimpConfig *compare, + GimpConfigWriter *writer) +{ + GList *diff; + GList *list; + gboolean retval = TRUE; + + g_return_val_if_fail (G_IS_OBJECT (config), FALSE); + g_return_val_if_fail (G_IS_OBJECT (compare), FALSE); + g_return_val_if_fail (G_TYPE_FROM_INSTANCE (config) == + G_TYPE_FROM_INSTANCE (compare), FALSE); + + diff = gimp_config_diff (G_OBJECT (config), + G_OBJECT (compare), GIMP_CONFIG_PARAM_SERIALIZE); + + for (list = diff; list; list = g_list_next (list)) + { + GParamSpec *prop_spec = list->data; + + if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE)) + continue; + + if (! gimp_config_serialize_property (config, prop_spec, writer)) + { + retval = FALSE; + break; + } + } + + g_list_free (diff); + + return retval; +} + +static void +serialize_unknown_token (const gchar *key, + const gchar *value, + gpointer data) +{ + GimpConfigWriter *writer = data; + + gimp_config_writer_open (writer, key); + gimp_config_writer_string (writer, value); + gimp_config_writer_close (writer); +} + +static gboolean +gimp_rc_serialize_unknown_tokens (GimpConfig *config, + GimpConfigWriter *writer) +{ + g_return_val_if_fail (G_IS_OBJECT (config), FALSE); + + gimp_config_writer_linefeed (writer); + gimp_rc_foreach_unknown_token (config, serialize_unknown_token, writer); + + return TRUE; +} diff --git a/app/config/gimprc-serialize.h b/app/config/gimprc-serialize.h new file mode 100644 index 0000000..d314835 --- /dev/null +++ b/app/config/gimprc-serialize.h @@ -0,0 +1,30 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc serialization routines + * Copyright (C) 2001-2005 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 . + */ + +#ifndef __GIMP_RC_SERIALIZE_H__ +#define __GIMP_RC_SERIALIZE_H__ + + +gboolean gimp_rc_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); + + +#endif /* __GIMP_RC_SERIALIZE_H__ */ diff --git a/app/config/gimprc-unknown.c b/app/config/gimprc-unknown.c new file mode 100644 index 0000000..055b240 --- /dev/null +++ b/app/config/gimprc-unknown.c @@ -0,0 +1,214 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc serialization and deserialization helpers + * Copyright (C) 2001-2005 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 . + */ + +#include "config.h" + +#include + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimprc-unknown.h" + + +/* + * Code to store and lookup unknown tokens (string key/value pairs). + */ + +#define GIMP_RC_UNKNOWN_TOKENS "gimp-rc-unknown-tokens" + +typedef struct +{ + gchar *key; + gchar *value; +} GimpConfigToken; + +static void gimp_rc_destroy_unknown_tokens (GSList *unknown_tokens); + + +/** + * gimp_rc_add_unknown_token: + * @config: a #GObject. + * @key: a nul-terminated string to identify the value. + * @value: a nul-terminated string representing the value. + * + * This function adds arbitrary key/value pairs to a GObject. It's + * purpose is to attach additional data to a #GimpConfig object that + * can be stored along with the object properties when serializing the + * object to a configuration file. Please note however that the + * default gimp_config_serialize() implementation does not serialize + * unknown tokens. + * + * If you want to remove a key/value pair from the object, call this + * function with a %NULL @value. + **/ +void +gimp_rc_add_unknown_token (GimpConfig *config, + const gchar *key, + const gchar *value) +{ + GimpConfigToken *token; + GSList *unknown_tokens; + GSList *last; + GSList *list; + + g_return_if_fail (GIMP_IS_CONFIG (config)); + g_return_if_fail (key != NULL); + + unknown_tokens = (GSList *) g_object_get_data (G_OBJECT (config), + GIMP_RC_UNKNOWN_TOKENS); + + for (last = NULL, list = unknown_tokens; + list; + last = list, list = g_slist_next (list)) + { + token = (GimpConfigToken *) list->data; + + if (strcmp (token->key, key) == 0) + { + g_free (token->value); + + if (value) + { + token->value = g_strdup (value); + } + else + { + g_free (token->key); + + unknown_tokens = g_slist_remove (unknown_tokens, token); + g_object_set_data_full (G_OBJECT (config), + GIMP_RC_UNKNOWN_TOKENS, + unknown_tokens, + (GDestroyNotify) gimp_rc_destroy_unknown_tokens); + } + + return; + } + } + + if (!value) + return; + + token = g_slice_new (GimpConfigToken); + token->key = g_strdup (key); + token->value = g_strdup (value); + + if (last) + { + last = g_slist_last (g_slist_append (last, token)); + } + else + { + unknown_tokens = g_slist_append (NULL, token); + + g_object_set_data_full (G_OBJECT (config), + GIMP_RC_UNKNOWN_TOKENS, + unknown_tokens, + (GDestroyNotify) gimp_rc_destroy_unknown_tokens); + } +} + +/** + * gimp_rc_lookup_unknown_token: + * @config: a #GObject. + * @key: a nul-terminated string to identify the value. + * + * This function retrieves data that was previously attached using + * gimp_rc_add_unknown_token(). You should not free or modify + * the returned string. + * + * Returns: a pointer to a constant string. + **/ +const gchar * +gimp_rc_lookup_unknown_token (GimpConfig *config, + const gchar *key) +{ + GSList *unknown_tokens; + GSList *list; + + g_return_val_if_fail (GIMP_IS_CONFIG (config), NULL); + g_return_val_if_fail (key != NULL, NULL); + + unknown_tokens = g_object_get_data (G_OBJECT (config), + GIMP_RC_UNKNOWN_TOKENS); + + for (list = unknown_tokens; list; list = g_slist_next (list)) + { + GimpConfigToken *token = list->data; + + if (strcmp (token->key, key) == 0) + return token->value; + } + + return NULL; +} + +/** + * gimp_rc_foreach_unknown_token: + * @config: a #GObject. + * @func: a function to call for each key/value pair. + * @user_data: data to pass to @func. + * + * Calls @func for each key/value stored with the @config using + * gimp_rc_add_unknown_token(). + **/ +void +gimp_rc_foreach_unknown_token (GimpConfig *config, + GimpConfigForeachFunc func, + gpointer user_data) +{ + GSList *unknown_tokens; + GSList *list; + + g_return_if_fail (GIMP_IS_CONFIG (config)); + g_return_if_fail (func != NULL); + + unknown_tokens = g_object_get_data (G_OBJECT (config), + GIMP_RC_UNKNOWN_TOKENS); + + for (list = unknown_tokens; list; list = g_slist_next (list)) + { + GimpConfigToken *token = list->data; + + func (token->key, token->value, user_data); + } +} + +static void +gimp_rc_destroy_unknown_tokens (GSList *unknown_tokens) +{ + GSList *list; + + for (list = unknown_tokens; list; list = g_slist_next (list)) + { + GimpConfigToken *token = list->data; + + g_free (token->key); + g_free (token->value); + g_slice_free (GimpConfigToken, token); + } + + g_slist_free (unknown_tokens); +} diff --git a/app/config/gimprc-unknown.h b/app/config/gimprc-unknown.h new file mode 100644 index 0000000..33a6243 --- /dev/null +++ b/app/config/gimprc-unknown.h @@ -0,0 +1,40 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GimpRc serialization and deserialization helpers + * Copyright (C) 2001-2005 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 . + */ + +#ifndef __GIMP_RC_UNKNOWN_H__ +#define __GIMP_RC_UNKNOWN_H__ + + +typedef void (* GimpConfigForeachFunc) (const gchar *key, + const gchar *value, + gpointer user_data); + + +void gimp_rc_add_unknown_token (GimpConfig *config, + const gchar *key, + const gchar *value); +const gchar * gimp_rc_lookup_unknown_token (GimpConfig *config, + const gchar *key); +void gimp_rc_foreach_unknown_token (GimpConfig *config, + GimpConfigForeachFunc func, + gpointer user_data); + + +#endif /* __GIMP_RC_UNKNOWN_H__ */ diff --git a/app/config/gimprc.c b/app/config/gimprc.c new file mode 100644 index 0000000..53d54e5 --- /dev/null +++ b/app/config/gimprc.c @@ -0,0 +1,593 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc, the object for GIMPs user configuration file gimprc. + * Copyright (C) 2001-2002 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "config-types.h" + +#include "gimpconfig-file.h" +#include "gimprc.h" +#include "gimprc-deserialize.h" +#include "gimprc-serialize.h" +#include "gimprc-unknown.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_VERBOSE, + PROP_SYSTEM_GIMPRC, + PROP_USER_GIMPRC +}; + + +static void gimp_rc_config_iface_init (GimpConfigInterface *iface); + +static void gimp_rc_dispose (GObject *object); +static void gimp_rc_finalize (GObject *object); +static void gimp_rc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_rc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static GimpConfig * gimp_rc_duplicate (GimpConfig *object); +static gboolean gimp_rc_idle_save (GimpRc *rc); +static void gimp_rc_notify (GimpRc *rc, + GParamSpec *param, + gpointer data); + + +G_DEFINE_TYPE_WITH_CODE (GimpRc, gimp_rc, GIMP_TYPE_PLUGIN_CONFIG, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_rc_config_iface_init)) + +#define parent_class gimp_rc_parent_class + + +static void +gimp_rc_class_init (GimpRcClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_rc_dispose; + object_class->finalize = gimp_rc_finalize; + object_class->set_property = gimp_rc_set_property; + object_class->get_property = gimp_rc_get_property; + + g_object_class_install_property (object_class, PROP_VERBOSE, + g_param_spec_boolean ("verbose", + NULL, NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SYSTEM_GIMPRC, + g_param_spec_object ("system-gimprc", + NULL, NULL, + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USER_GIMPRC, + g_param_spec_object ("user-gimprc", + NULL, NULL, + G_TYPE_FILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_rc_init (GimpRc *rc) +{ + rc->autosave = FALSE; + rc->save_idle_id = 0; +} + +static void +gimp_rc_dispose (GObject *object) +{ + GimpRc *rc = GIMP_RC (object); + + if (rc->save_idle_id) + gimp_rc_idle_save (rc); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_rc_finalize (GObject *object) +{ + GimpRc *rc = GIMP_RC (object); + + g_clear_object (&rc->system_gimprc); + g_clear_object (&rc->user_gimprc); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_rc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpRc *rc = GIMP_RC (object); + + switch (property_id) + { + case PROP_VERBOSE: + rc->verbose = g_value_get_boolean (value); + break; + + case PROP_SYSTEM_GIMPRC: + if (rc->system_gimprc) + g_object_unref (rc->system_gimprc); + + if (g_value_get_object (value)) + rc->system_gimprc = g_value_dup_object (value); + else + rc->system_gimprc = gimp_sysconf_directory_file ("gimprc", NULL); + break; + + case PROP_USER_GIMPRC: + if (rc->user_gimprc) + g_object_unref (rc->user_gimprc); + + if (g_value_get_object (value)) + rc->user_gimprc = g_value_dup_object (value); + else + rc->user_gimprc = gimp_directory_file ("gimprc", NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_rc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpRc *rc = GIMP_RC (object); + + switch (property_id) + { + case PROP_VERBOSE: + g_value_set_boolean (value, rc->verbose); + break; + case PROP_SYSTEM_GIMPRC: + g_value_set_object (value, rc->system_gimprc); + break; + case PROP_USER_GIMPRC: + g_value_set_object (value, rc->user_gimprc); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_rc_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_rc_serialize; + iface->deserialize = gimp_rc_deserialize; + iface->duplicate = gimp_rc_duplicate; +} + +static void +gimp_rc_duplicate_unknown_token (const gchar *key, + const gchar *value, + gpointer user_data) +{ + gimp_rc_add_unknown_token (GIMP_CONFIG (user_data), key, value); +} + +static GimpConfig * +gimp_rc_duplicate (GimpConfig *config) +{ + GObject *gimp; + GimpConfig *dup; + + g_object_get (config, "gimp", &gimp, NULL); + + dup = g_object_new (GIMP_TYPE_RC, + "gimp", gimp, + NULL); + + if (gimp) + g_object_unref (gimp); + + gimp_config_sync (G_OBJECT (config), G_OBJECT (dup), 0); + + gimp_rc_foreach_unknown_token (config, + gimp_rc_duplicate_unknown_token, dup); + + return dup; +} + +static gboolean +gimp_rc_idle_save (GimpRc *rc) +{ + gimp_rc_save (rc); + + rc->save_idle_id = 0; + + return FALSE; +} + +static void +gimp_rc_notify (GimpRc *rc, + GParamSpec *param, + gpointer data) +{ + if (!rc->autosave) + return; + + if (!rc->save_idle_id) + rc->save_idle_id = g_idle_add ((GSourceFunc) gimp_rc_idle_save, rc); +} + +/** + * gimp_rc_new: + * @gimp: a #Gimp + * @system_gimprc: the name of the system-wide gimprc file or %NULL to + * use the standard location + * @user_gimprc: the name of the user gimprc file or %NULL to use the + * standard location + * @verbose: enable console messages about loading and saving + * + * Creates a new GimpRc object and loads the system-wide and the user + * configuration files. + * + * Returns: the new #GimpRc. + */ +GimpRc * +gimp_rc_new (GObject *gimp, + GFile *system_gimprc, + GFile *user_gimprc, + gboolean verbose) +{ + GimpRc *rc; + + g_return_val_if_fail (G_IS_OBJECT (gimp), NULL); + g_return_val_if_fail (system_gimprc == NULL || G_IS_FILE (system_gimprc), + NULL); + g_return_val_if_fail (user_gimprc == NULL || G_IS_FILE (user_gimprc), + NULL); + + rc = g_object_new (GIMP_TYPE_RC, + "gimp", gimp, + "verbose", verbose, + "system-gimprc", system_gimprc, + "user-gimprc", user_gimprc, + NULL); + + gimp_rc_load_system (rc); + gimp_rc_load_user (rc); + + return rc; +} + +void +gimp_rc_load_system (GimpRc *rc) +{ + GError *error = NULL; + + g_return_if_fail (GIMP_IS_RC (rc)); + + if (rc->verbose) + g_print ("Parsing '%s'\n", + gimp_file_get_utf8_name (rc->system_gimprc)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (rc), + rc->system_gimprc, NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + g_message ("%s", error->message); + + g_clear_error (&error); + } +} + +void +gimp_rc_load_user (GimpRc *rc) +{ + GError *error = NULL; + + g_return_if_fail (GIMP_IS_RC (rc)); + + if (rc->verbose) + g_print ("Parsing '%s'\n", + gimp_file_get_utf8_name (rc->user_gimprc)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (rc), + rc->user_gimprc, NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + { + g_message ("%s", error->message); + + gimp_config_file_backup_on_error (rc->user_gimprc, "gimprc", NULL); + } + + g_clear_error (&error); + } +} + +void +gimp_rc_set_autosave (GimpRc *rc, + gboolean autosave) +{ + g_return_if_fail (GIMP_IS_RC (rc)); + + autosave = autosave ? TRUE : FALSE; + + if (rc->autosave == autosave) + return; + + if (autosave) + g_signal_connect (rc, "notify", + G_CALLBACK (gimp_rc_notify), + NULL); + else + g_signal_handlers_disconnect_by_func (rc, gimp_rc_notify, NULL); + + rc->autosave = autosave; +} + + +/** + * gimp_rc_query: + * @rc: a #GimpRc object. + * @key: a string used as a key for the lookup. + * + * This function looks up @key in the object properties of @rc. If + * there's a matching property, a string representation of its value + * is returned. If no property is found, the list of unknown tokens + * attached to the @rc object is searched. + * + * Return value: a newly allocated string representing the value or %NULL + * if the key couldn't be found. + **/ +gchar * +gimp_rc_query (GimpRc *rc, + const gchar *key) +{ + GObjectClass *klass; + GObject *rc_object; + GParamSpec **property_specs; + GParamSpec *prop_spec; + guint i, n_property_specs; + gchar *retval = NULL; + + g_return_val_if_fail (GIMP_IS_RC (rc), NULL); + g_return_val_if_fail (key != NULL, NULL); + + rc_object = G_OBJECT (rc); + klass = G_OBJECT_GET_CLASS (rc); + + property_specs = g_object_class_list_properties (klass, &n_property_specs); + + if (!property_specs) + return NULL; + + for (i = 0, prop_spec = NULL; i < n_property_specs && !prop_spec; i++) + { + prop_spec = property_specs[i]; + + if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) || + strcmp (prop_spec->name, key)) + { + prop_spec = NULL; + } + } + + if (prop_spec) + { + GString *str = g_string_new (NULL); + GValue value = G_VALUE_INIT; + + g_value_init (&value, prop_spec->value_type); + g_object_get_property (rc_object, prop_spec->name, &value); + + if (gimp_config_serialize_value (&value, str, FALSE)) + retval = g_string_free (str, FALSE); + else + g_string_free (str, TRUE); + + g_value_unset (&value); + } + else + { + retval = g_strdup (gimp_rc_lookup_unknown_token (GIMP_CONFIG (rc), key)); + } + + g_free (property_specs); + + if (!retval) + { + const gchar * const path_tokens[] = + { + "gimp_dir", + "gimp_data_dir", + "gimp_plug_in_dir", + "gimp_plugin_dir", + "gimp_sysconf_dir" + }; + + for (i = 0; !retval && i < G_N_ELEMENTS (path_tokens); i++) + if (strcmp (key, path_tokens[i]) == 0) + retval = g_strdup_printf ("${%s}", path_tokens[i]); + } + + if (retval) + { + gchar *tmp = gimp_config_path_expand (retval, FALSE, NULL); + + if (tmp) + { + g_free (retval); + retval = tmp; + } + } + + return retval; +} + +/** + * gimp_rc_set_unknown_token: + * @gimprc: a #GimpRc object. + * @token: + * @value: + * + * Calls gimp_rc_add_unknown_token() and triggers an idle-save if + * autosave is enabled on @gimprc. + **/ +void +gimp_rc_set_unknown_token (GimpRc *rc, + const gchar *token, + const gchar *value) +{ + g_return_if_fail (GIMP_IS_RC (rc)); + + gimp_rc_add_unknown_token (GIMP_CONFIG (rc), token, value); + + if (rc->autosave) + gimp_rc_notify (rc, NULL, NULL); +} + +/** + * gimp_rc_save: + * @gimprc: a #GimpRc object. + * + * Saves any settings that differ from the system-wide defined + * defaults to the users personal gimprc file. + **/ +void +gimp_rc_save (GimpRc *rc) +{ + GObject *gimp; + GimpRc *global; + gchar *header; + GError *error = NULL; + + const gchar *top = + "GIMP gimprc\n" + "\n" + "This is your personal gimprc file. Any variable defined in this file " + "takes precedence over the value defined in the system-wide gimprc: "; + const gchar *bottom = + "\n" + "Most values can be set within GIMP by changing some options in " + "the Preferences dialog."; + const gchar *footer = + "end of gimprc"; + + g_return_if_fail (GIMP_IS_RC (rc)); + + g_object_get (rc, "gimp", &gimp, NULL); + + global = g_object_new (GIMP_TYPE_RC, + "gimp", gimp, + NULL); + + if (gimp) + g_object_unref (gimp); + + gimp_config_deserialize_gfile (GIMP_CONFIG (global), + rc->system_gimprc, NULL, NULL); + + header = g_strconcat (top, gimp_file_get_utf8_name (rc->system_gimprc), + bottom, NULL); + + if (rc->verbose) + g_print ("Writing '%s'\n", + gimp_file_get_utf8_name (rc->user_gimprc)); + + if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (rc), + rc->user_gimprc, + header, footer, global, + &error)) + { + g_message ("%s", error->message); + g_error_free (error); + } + + g_free (header); + g_object_unref (global); +} + +/** + * gimp_rc_migrate: + * @rc: a #GimpRc object. + * + * Resets all GimpParamConfigPath properties of the passed rc object + * to their default values, in order to prevent paths in a migrated + * gimprc to refer to folders in the old GIMP's user directory. + **/ +void +gimp_rc_migrate (GimpRc *rc) +{ + GParamSpec **pspecs; + guint n_pspecs; + gint i; + + g_return_if_fail (GIMP_IS_RC (rc)); + + pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (rc), &n_pspecs); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + + if (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec)) + { + GValue value = G_VALUE_INIT; + + g_value_init (&value, pspec->value_type); + + g_param_value_set_default (pspec, &value); + g_object_set_property (G_OBJECT (rc), pspec->name, &value); + + g_value_unset (&value); + } + } + + g_free (pspecs); +} diff --git a/app/config/gimprc.h b/app/config/gimprc.h new file mode 100644 index 0000000..fbe47a1 --- /dev/null +++ b/app/config/gimprc.h @@ -0,0 +1,77 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpRc + * Copyright (C) 2001 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 . + */ + +#ifndef __GIMP_RC_H__ +#define __GIMP_RC_H__ + +#include "config/gimppluginconfig.h" + + +#define GIMP_TYPE_RC (gimp_rc_get_type ()) +#define GIMP_RC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_RC, GimpRc)) +#define GIMP_RC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_RC, GimpRcClass)) +#define GIMP_IS_RC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_RC)) +#define GIMP_IS_RC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_RC)) + + +typedef struct _GimpRcClass GimpRcClass; + +struct _GimpRc +{ + GimpPluginConfig parent_instance; + + GFile *user_gimprc; + GFile *system_gimprc; + gboolean verbose; + gboolean autosave; + guint save_idle_id; +}; + +struct _GimpRcClass +{ + GimpPluginConfigClass parent_class; +}; + + +GType gimp_rc_get_type (void) G_GNUC_CONST; + +GimpRc * gimp_rc_new (GObject *gimp, + GFile *system_gimprc, + GFile *user_gimprc, + gboolean verbose); + +void gimp_rc_load_system (GimpRc *rc); +void gimp_rc_load_user (GimpRc *rc); + +void gimp_rc_set_autosave (GimpRc *rc, + gboolean autosave); +void gimp_rc_save (GimpRc *rc); + +gchar * gimp_rc_query (GimpRc *rc, + const gchar *key); + +void gimp_rc_set_unknown_token (GimpRc *rc, + const gchar *token, + const gchar *value); + +void gimp_rc_migrate (GimpRc *rc); + + +#endif /* GIMP_RC_H__ */ diff --git a/app/config/gimpxmlparser.c b/app/config/gimpxmlparser.c new file mode 100644 index 0000000..1bec8f8 --- /dev/null +++ b/app/config/gimpxmlparser.c @@ -0,0 +1,404 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GimpXmlParser + * Copyright (C) 2003 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 . + */ + +#include "config.h" + +#include + +#include + +#include "config-types.h" + +#include "gimpxmlparser.h" + + +struct _GimpXmlParser +{ + GMarkupParseContext *context; +}; + + +static gboolean parse_encoding (const gchar *text, + gint text_len, + gchar **encodind); + + +/** + * gimp_xml_parser_new: + * @markup_parser: a #GMarkupParser + * @user_data: user data to pass to #GMarkupParser functions + * + * GimpXmlParser is a thin wrapper around GMarkupParser. This function + * creates one for you and sets up a GMarkupParseContext. + * + * Return value: a new #GimpXmlParser + **/ +GimpXmlParser * +gimp_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; +} + +/** + * gimp_xml_parser_parse_file: + * @parser: a #GimpXmlParser + * @filename: name of a file to parse + * @error: return location for possible errors + * + * This function creates a GIOChannel for @filename and calls + * gimp_xml_parser_parse_io_channel() for you. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean +gimp_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 = gimp_xml_parser_parse_io_channel (parser, io, error); + + g_io_channel_unref (io); + + return success; +} + +/** + * gimp_xml_parser_parse_gfile: + * @parser: a #GimpXmlParser + * @file: the #GFile to parse + * @error: return location for possible errors + * + * This function creates a GIOChannel for @file and calls + * gimp_xml_parser_parse_io_channel() for you. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean +gimp_xml_parser_parse_gfile (GimpXmlParser *parser, + GFile *file, + GError **error) +{ + gchar *path; + gboolean success; + + g_return_val_if_fail (parser != NULL, FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + + path = g_file_get_path (file); + + success = gimp_xml_parser_parse_file (parser, path, error); + + g_free (path); + + return success; +} + +/** + * gimp_xml_parser_parse_fd: + * @parser: a #GimpXmlParser + * @fd: a file descriptor + * @error: return location for possible errors + * + * This function creates a GIOChannel for @fd and calls + * gimp_xml_parser_parse_io_channel() for you. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean +gimp_xml_parser_parse_fd (GimpXmlParser *parser, + gint fd, + GError **error) +{ + GIOChannel *io; + gboolean success; + + g_return_val_if_fail (parser != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + +#ifdef G_OS_WIN32 + io = g_io_channel_win32_new_fd (fd); +#else + io = g_io_channel_unix_new (fd); +#endif + + success = gimp_xml_parser_parse_io_channel (parser, io, error); + + g_io_channel_unref (io); + + return success; +} + +/** + * gimp_xml_parser_parse_io_channel: + * @parser: a #GimpXmlParser + * @io: a #GIOChannel + * @error: return location for possible errors + * + * Makes @parser read from the specified @io channel. This function + * returns when the GIOChannel becomes empty (end of file) or an + * error occurs, either reading from @io or parsing the read data. + * + * This function tries to determine the character encoding from the + * XML header and converts the content to UTF-8 for you. For this + * feature to work, the XML header with the encoding attribute must be + * contained in the first 4096 bytes read. Otherwise UTF-8 encoding + * will be assumed and parsing may break later if this assumption + * was wrong. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean +gimp_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 ("gimp_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; + + 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; + } + } +} + +/** + * gimp_xml_parser_parse_buffer: + * @parser: a #GimpXmlParser + * @buffer: a string buffer + * @len: the number of byes in @buffer or -1 if @buffer is nul-terminated + * @error: return location for possible errors + * + * This function uses the given @parser to parse the XML in @buffer. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean +gimp_xml_parser_parse_buffer (GimpXmlParser *parser, + const gchar *buffer, + gssize len, + GError **error) +{ + gchar *encoding = NULL; + gchar *conv = NULL; + gboolean success; + + g_return_val_if_fail (parser != NULL, FALSE); + g_return_val_if_fail (buffer != NULL || len == 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (len < 0) + len = strlen (buffer); + + if (parse_encoding (buffer, len, &encoding) && encoding) + { + if (g_ascii_strcasecmp (encoding, "UTF-8") && + g_ascii_strcasecmp (encoding, "UTF8")) + { + gsize written; + + conv = g_convert (buffer, len, + "UTF-8", encoding, NULL, &written, error); + if (! conv) + { + g_free (encoding); + return FALSE; + } + + len = written; + } + + g_free (encoding); + } + + success = g_markup_parse_context_parse (parser->context, + conv ? conv : buffer, len, error); + + if (conv) + g_free (conv); + + return success; +} + +/** + * gimp_xml_parser_free: + * @parser: a #GimpXmlParser + * + * Frees the resources allocated for @parser. You must not access + * @parser after calling this function. + **/ +void +gimp_xml_parser_free (GimpXmlParser *parser) +{ + g_return_if_fail (parser != NULL); + + g_markup_parse_context_free (parser->context); + g_slice_free (GimpXmlParser, parser); +} + + +/* Try to determine encoding from XML header. This function returns + FALSE when it doesn't have enough text to parse. It returns TRUE + and sets encoding when the XML header has been parsed. + */ +static 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, ""); + 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; +} diff --git a/app/config/gimpxmlparser.h b/app/config/gimpxmlparser.h new file mode 100644 index 0000000..f744472 --- /dev/null +++ b/app/config/gimpxmlparser.h @@ -0,0 +1,46 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GimpXmlParser + * Copyright (C) 2003 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 . + */ + +#ifndef __GIMP_XML_PARSER_H__ +#define __GIMP_XML_PARSER_H__ + + +GimpXmlParser * gimp_xml_parser_new (const GMarkupParser *markup_parser, + gpointer user_data); +gboolean gimp_xml_parser_parse_file (GimpXmlParser *parser, + const gchar *filename, + GError **error); +gboolean gimp_xml_parser_parse_gfile (GimpXmlParser *parser, + GFile *file, + GError **error); +gboolean gimp_xml_parser_parse_fd (GimpXmlParser *parser, + gint fd, + GError **error); +gboolean gimp_xml_parser_parse_io_channel (GimpXmlParser *parser, + GIOChannel *io, + GError **error); +gboolean gimp_xml_parser_parse_buffer (GimpXmlParser *parser, + const gchar *buffer, + gssize len, + GError **error); +void gimp_xml_parser_free (GimpXmlParser *parser); + + +#endif /* __GIMP_XML_PARSER_H__ */ diff --git a/app/config/test-config.c b/app/config/test-config.c new file mode 100644 index 0000000..1aff0a4 --- /dev/null +++ b/app/config/test-config.c @@ -0,0 +1,278 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Test suite for GimpConfig. + * Copyright (C) 2001-2002 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 . + */ + +#include "config.h" + +#include "stdlib.h" +#include "string.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpbase/gimpbase-private.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core/core-types.h" +#include "core/gimpgrid.h" + +#include "gimprc-unknown.h" + + +static void notify_callback (GObject *object, + GParamSpec *pspec); +static void output_unknown_token (const gchar *key, + const gchar *value, + gpointer data); + +static void units_init (void); + + +int +main (int argc, + char *argv[]) +{ + GimpConfig *grid; + GimpConfig *grid2; + const gchar *filename = "foorc"; + gchar *header; + gchar *result; + GList *list; + gint i; + GError *error = NULL; + + for (i = 1; i < argc; i++) + { + if (strcmp (argv[i], "--g-fatal-warnings") == 0) + { + GLogLevelFlags fatal_mask; + + fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); + fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; + g_log_set_always_fatal (fatal_mask); + } + } + + units_init (); + + g_print ("\nTesting GimpConfig ...\n"); + + g_print (" Creating a new Grid object ..."); + grid = g_object_new (GIMP_TYPE_GRID, NULL); + g_print (" done.\n"); + + g_print (" Adding the unknown token (foobar \"hadjaha\") ..."); + gimp_rc_add_unknown_token (grid, "foobar", "hadjaha"); + g_print (" done.\n"); + + g_print (" Serializing %s to '%s' ...", + g_type_name (G_TYPE_FROM_INSTANCE (grid)), filename); + + if (! gimp_config_serialize_to_file (grid, + filename, + "foorc", "end of foorc", + NULL, &error)) + { + g_print ("%s\n", error->message); + return EXIT_FAILURE; + } + g_print (" done.\n"); + + g_signal_connect (grid, "notify", + G_CALLBACK (notify_callback), + NULL); + + g_print (" Deserializing from '%s' ...\n", filename); + if (! gimp_config_deserialize_file (grid, filename, NULL, &error)) + { + g_print ("%s\n", error->message); + return EXIT_FAILURE; + } + header = " Unknown string tokens:\n"; + gimp_rc_foreach_unknown_token (grid, output_unknown_token, &header); + g_print (" done.\n\n"); + + g_print (" Changing a property ..."); + g_object_set (grid, "style", GIMP_GRID_DOTS, NULL); + + g_print (" Testing gimp_config_duplicate() ..."); + grid2 = gimp_config_duplicate (grid); + g_print (" done.\n"); + + g_signal_connect (grid2, "notify", + G_CALLBACK (notify_callback), + NULL); + + g_print (" Changing a property in the duplicate ..."); + g_object_set (grid2, "xspacing", 20.0, NULL); + + g_print (" Creating a diff between the two ..."); + for (list = gimp_config_diff (G_OBJECT (grid), G_OBJECT (grid2), 0); + list; + list = list->next) + { + GParamSpec *pspec = list->data; + + g_print ("%c%s", (list->prev ? ',' : ' '), pspec->name); + } + g_print ("\n\n"); + + g_object_unref (grid2); + + g_print (" Deserializing from gimpconfig.c (should fail) ..."); + if (! gimp_config_deserialize_file (grid, "gimpconfig.c", NULL, &error)) + { + g_print (" OK, failed. The error was:\n %s\n", error->message); + g_error_free (error); + error = NULL; + } + else + { + g_print ("This test should have failed :-(\n"); + return EXIT_FAILURE; + } + + g_print (" Serializing to a string and back ... "); + + result = gimp_config_serialize_to_string (grid, NULL); + + grid2 = g_object_new (GIMP_TYPE_GRID, NULL); + + if (! gimp_config_deserialize_string (grid2, result, -1, NULL, &error)) + { + g_print ("failed!\nThe error was:\n %s\n", error->message); + g_error_free (error); + return EXIT_FAILURE; + } + else + { + GList *diff = gimp_config_diff (G_OBJECT (grid), G_OBJECT (grid2), 0); + + if (diff) + { + GList *list; + + g_print ("succeeded but properties differ:\n"); + for (list = diff; list; list = list->next) + { + GParamSpec *pspec = list->data; + g_print (" %s\n", pspec->name); + } + return EXIT_FAILURE; + } + + g_print ("OK (%u bytes)\n", result ? (guint) strlen (result) : 0); + } + + g_free (result); + g_object_unref (grid2); + g_object_unref (grid); + + g_print ("\nFinished test of GimpConfig.\n\n"); + + return EXIT_SUCCESS; +} + +static void +notify_callback (GObject *object, + GParamSpec *pspec) +{ + GString *str; + GValue value = G_VALUE_INIT; + + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + + g_value_init (&value, pspec->value_type); + g_object_get_property (object, pspec->name, &value); + + str = g_string_new (NULL); + + if (gimp_config_serialize_value (&value, str, TRUE)) + { + g_print (" %s -> %s\n", pspec->name, str->str); + } + else + { + g_print (" %s changed but we failed to serialize its value!\n", + pspec->name); + } + + g_string_free (str, TRUE); + g_value_unset (&value); +} + +static void +output_unknown_token (const gchar *key, + const gchar *value, + gpointer data) +{ + gchar **header = (gchar **) data; + gchar *escaped = g_strescape (value, NULL); + + if (*header) + { + g_print ("%s", *header); + *header = NULL; + } + + g_print (" %s \"%s\"\n", key, escaped); + + g_free (escaped); +} + + +/* minimal dummy units implementation */ + +static const gchar * +unit_get_identifier (GimpUnit unit) +{ + switch (unit) + { + case GIMP_UNIT_PIXEL: + return "pixels"; + case GIMP_UNIT_INCH: + return "inches"; + case GIMP_UNIT_MM: + return "millimeters"; + case GIMP_UNIT_POINT: + return "points"; + case GIMP_UNIT_PICA: + return "picas"; + default: + return NULL; + } +} + +static gint +unit_get_number_of_units (void) +{ + return GIMP_UNIT_END; +} + +static void +units_init (void) +{ + GimpUnitVtable vtable; + + vtable.unit_get_number_of_units = unit_get_number_of_units; + vtable.unit_get_identifier = unit_get_identifier; + + gimp_base_init (&vtable); +} diff --git a/app/core/Makefile.am b/app/core/Makefile.am new file mode 100644 index 0000000..c25f302 --- /dev/null +++ b/app/core/Makefile.am @@ -0,0 +1,550 @@ +## Process this file with automake to produce Makefile.in + +if PLATFORM_OSX +xobjective_c = "-xobjective-c" +xobjective_cxx = "-xobjective-c++" +xnone = "-xnone" +endif + +AM_CPPFLAGS = \ + -DGIMPDIR=\""$(gimpdir)"\" \ + -DGIMP_APP_VERSION=\"$(GIMP_APP_VERSION)\" \ + -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \ + -DG_LOG_DOMAIN=\"Gimp-Core\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(LIBMYPAINT_CFLAGS) \ + $(MYPAINT_BRUSHES_CFLAGS) \ + $(GEXIV2_CFLAGS) \ + $(LIBUNWIND_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappcore.a + +libappcore_a_sources = \ + core-enums.h \ + core-types.h \ + gimp.c \ + gimp.h \ + gimp-atomic.c \ + gimp-atomic.h \ + gimp-batch.c \ + gimp-batch.h \ + gimp-cairo.c \ + gimp-cairo.h \ + gimp-contexts.c \ + gimp-contexts.h \ + gimp-data-factories.c \ + gimp-data-factories.h \ + gimp-edit.c \ + gimp-edit.h \ + gimp-filter-history.c \ + gimp-filter-history.h \ + gimp-gradients.c \ + gimp-gradients.h \ + gimp-gui.c \ + gimp-gui.h \ + gimp-internal-data.c \ + gimp-internal-data.h \ + gimp-memsize.c \ + gimp-memsize.h \ + gimp-modules.c \ + gimp-modules.h \ + gimp-palettes.c \ + gimp-palettes.h \ + gimp-parallel.cc \ + gimp-parallel.h \ + gimp-parasites.c \ + gimp-parasites.h \ + gimp-spawn.c \ + gimp-spawn.h \ + gimp-tags.c \ + gimp-tags.h \ + gimp-templates.c \ + gimp-templates.h \ + gimp-transform-resize.c \ + gimp-transform-resize.h \ + gimp-transform-3d-utils.c \ + gimp-transform-3d-utils.h \ + gimp-transform-utils.c \ + gimp-transform-utils.h \ + gimp-units.c \ + gimp-units.h \ + gimp-user-install.c \ + gimp-user-install.h \ + gimp-utils.c \ + gimp-utils.h \ + gimpasync.c \ + gimpasync.h \ + gimpasyncset.c \ + gimpasyncset.h \ + gimpauxitem.c \ + gimpauxitem.h \ + gimpauxitemundo.c \ + gimpauxitemundo.h \ + gimpbacktrace.h \ + gimpbacktrace-backend.h \ + gimpbacktrace-linux.c \ + gimpbacktrace-none.c \ + gimpbacktrace-windows.c \ + gimpbezierdesc.h \ + gimpbezierdesc.c \ + gimpboundary.c \ + gimpboundary.h \ + gimpbrush.c \ + gimpbrush.h \ + gimpbrush-boundary.c \ + gimpbrush-boundary.h \ + gimpbrush-header.h \ + gimpbrush-load.c \ + gimpbrush-load.h \ + gimpbrush-mipmap.cc \ + gimpbrush-mipmap.h \ + gimpbrush-private.h \ + gimpbrush-save.c \ + gimpbrush-save.h \ + gimpbrush-transform.cc \ + gimpbrush-transform.h \ + gimpbrushcache.c \ + gimpbrushcache.h \ + gimpbrushclipboard.c \ + gimpbrushclipboard.h \ + gimpbrushgenerated.c \ + gimpbrushgenerated.h \ + gimpbrushgenerated-load.c \ + gimpbrushgenerated-load.h \ + gimpbrushgenerated-save.c \ + gimpbrushgenerated-save.h \ + gimpbrushpipe.c \ + gimpbrushpipe.h \ + gimpbrushpipe-load.c \ + gimpbrushpipe-load.h \ + gimpbrushpipe-save.c \ + gimpbrushpipe-save.h \ + gimpbuffer.c \ + gimpbuffer.h \ + gimpcancelable.c \ + gimpcancelable.h \ + gimpchannel.c \ + gimpchannel.h \ + gimpchannel-combine.c \ + gimpchannel-combine.h \ + gimpchannel-select.c \ + gimpchannel-select.h \ + gimpchannelpropundo.c \ + gimpchannelpropundo.h \ + gimpchannelundo.c \ + gimpchannelundo.h \ + gimpchunkiterator.c \ + gimpchunkiterator.h \ + gimpcontainer.c \ + gimpcontainer.h \ + gimpcontainer-filter.c \ + gimpcontainer-filter.h \ + gimpcontext.c \ + gimpcontext.h \ + gimpcoords.c \ + gimpcoords.h \ + gimpcoords-interpolate.c \ + gimpcoords-interpolate.h \ + gimpcurve.c \ + gimpcurve.h \ + gimpcurve-load.c \ + gimpcurve-load.h \ + gimpcurve-map.c \ + gimpcurve-map.h \ + gimpcurve-save.c \ + gimpcurve-save.h \ + gimpdashpattern.c \ + gimpdashpattern.h \ + gimpdata.c \ + gimpdata.h \ + gimpdatafactory.c \ + gimpdatafactory.h \ + gimpdataloaderfactory.c \ + gimpdataloaderfactory.h \ + gimpdocumentlist.c \ + gimpdocumentlist.h \ + gimpdrawable.c \ + gimpdrawable.h \ + gimpdrawable-bucket-fill.c \ + gimpdrawable-bucket-fill.h \ + gimpdrawable-combine.c \ + gimpdrawable-combine.h \ + gimpdrawable-edit.c \ + gimpdrawable-edit.h \ + gimpdrawable-equalize.c \ + gimpdrawable-equalize.h \ + gimpdrawable-fill.c \ + gimpdrawable-fill.h \ + gimpdrawable-filters.c \ + gimpdrawable-filters.h \ + gimpdrawable-floating-selection.c \ + gimpdrawable-floating-selection.h \ + gimpdrawable-foreground-extract.c \ + gimpdrawable-foreground-extract.h \ + gimpdrawable-gradient.c \ + gimpdrawable-gradient.h \ + gimpdrawable-histogram.c \ + gimpdrawable-histogram.h \ + gimpdrawable-levels.c \ + gimpdrawable-levels.h \ + gimpdrawable-offset.c \ + gimpdrawable-offset.h \ + gimpdrawable-operation.c \ + gimpdrawable-operation.h \ + gimpdrawable-preview.c \ + gimpdrawable-preview.h \ + gimpdrawable-private.h \ + gimpdrawable-shadow.c \ + gimpdrawable-shadow.h \ + gimpdrawable-stroke.c \ + gimpdrawable-stroke.h \ + gimpdrawable-transform.c \ + gimpdrawable-transform.h \ + gimpdrawablefilter.c \ + gimpdrawablefilter.h \ + gimpdrawablemodundo.c \ + gimpdrawablemodundo.h \ + gimpdrawablestack.c \ + gimpdrawablestack.h \ + gimpdrawableundo.c \ + gimpdrawableundo.h \ + gimpdynamics.c \ + gimpdynamics.h \ + gimpdynamics-load.c \ + gimpdynamics-load.h \ + gimpdynamics-save.c \ + gimpdynamics-save.h \ + gimpdynamicsoutput.c \ + gimpdynamicsoutput.h \ + gimperror.c \ + gimperror.h \ + gimpfilloptions.c \ + gimpfilloptions.h \ + gimpfilter.c \ + gimpfilter.h \ + gimpfilteredcontainer.c \ + gimpfilteredcontainer.h \ + gimpfilterstack.c \ + gimpfilterstack.h \ + gimpfloatingselectionundo.c \ + gimpfloatingselectionundo.h \ + gimpgradient.c \ + gimpgradient.h \ + gimpgradient-load.c \ + gimpgradient-load.h \ + gimpgradient-save.c \ + gimpgradient-save.h \ + gimpgrid.c \ + gimpgrid.h \ + gimpgrouplayer.c \ + gimpgrouplayer.h \ + gimpgrouplayerundo.c \ + gimpgrouplayerundo.h \ + gimpguide.c \ + gimpguide.h \ + gimpguideundo.c \ + gimpguideundo.h \ + gimphistogram.c \ + gimphistogram.h \ + gimpidtable.c \ + gimpidtable.h \ + gimpimage.c \ + gimpimage.h \ + gimpimage-arrange.c \ + gimpimage-arrange.h \ + gimpimage-color-profile.c \ + gimpimage-color-profile.h \ + gimpimage-colormap.c \ + gimpimage-colormap.h \ + gimpimage-convert-indexed.c \ + gimpimage-convert-indexed.h \ + gimpimage-convert-fsdither.h \ + gimpimage-convert-data.h \ + gimpimage-convert-precision.c \ + gimpimage-convert-precision.h \ + gimpimage-convert-type.c \ + gimpimage-convert-type.h \ + gimpimage-crop.c \ + gimpimage-crop.h \ + gimpimage-duplicate.c \ + gimpimage-duplicate.h \ + gimpimage-flip.c \ + gimpimage-flip.h \ + gimpimage-grid.h \ + gimpimage-grid.c \ + gimpimage-guides.c \ + gimpimage-guides.h \ + gimpimage-item-list.c \ + gimpimage-item-list.h \ + gimpimage-merge.c \ + gimpimage-merge.h \ + gimpimage-metadata.c \ + gimpimage-metadata.h \ + gimpimage-new.c \ + gimpimage-new.h \ + gimpimage-pick-color.c \ + gimpimage-pick-color.h \ + gimpimage-pick-item.c \ + gimpimage-pick-item.h \ + gimpimage-preview.c \ + gimpimage-preview.h \ + gimpimage-private.h \ + gimpimage-quick-mask.c \ + gimpimage-quick-mask.h \ + gimpimage-resize.c \ + gimpimage-resize.h \ + gimpimage-rotate.c \ + gimpimage-rotate.h \ + gimpimage-sample-points.c \ + gimpimage-sample-points.h \ + gimpimage-scale.c \ + gimpimage-scale.h \ + gimpimage-snap.c \ + gimpimage-snap.h \ + gimpimage-symmetry.c \ + gimpimage-symmetry.h \ + gimpimage-transform.c \ + gimpimage-transform.h \ + gimpimage-undo.c \ + gimpimage-undo.h \ + gimpimage-undo-push.c \ + gimpimage-undo-push.h \ + gimpimageproxy.c \ + gimpimageproxy.h \ + gimpimageundo.c \ + gimpimageundo.h \ + gimpimagefile.c \ + gimpimagefile.h \ + gimpitem.c \ + gimpitem.h \ + gimpitem-exclusive.c \ + gimpitem-exclusive.h \ + gimpitem-linked.c \ + gimpitem-linked.h \ + gimpitem-preview.c \ + gimpitem-preview.h \ + gimpitempropundo.c \ + gimpitempropundo.h \ + gimpitemstack.c \ + gimpitemstack.h \ + gimpitemtree.c \ + gimpitemtree.h \ + gimpitemundo.c \ + gimpitemundo.h \ + gimplayer.c \ + gimplayer.h \ + gimplayer-floating-selection.c \ + gimplayer-floating-selection.h \ + gimplayer-new.c \ + gimplayer-new.h \ + gimplayermask.c \ + gimplayermask.h \ + gimplayermaskpropundo.c \ + gimplayermaskpropundo.h \ + gimplayermaskundo.c \ + gimplayermaskundo.h \ + gimplayerpropundo.c \ + gimplayerpropundo.h \ + gimplayerstack.c \ + gimplayerstack.h \ + gimplayerundo.c \ + gimplayerundo.h \ + gimplineart.c \ + gimplineart.h \ + gimplist.c \ + gimplist.h \ + gimpmaskundo.c \ + gimpmaskundo.h \ + gimpmybrush.c \ + gimpmybrush.h \ + gimpmybrush-load.c \ + gimpmybrush-load.h \ + gimpmybrush-private.h \ + gimpobject.c \ + gimpobject.h \ + gimpobjectqueue.c \ + gimpobjectqueue.h \ + gimppaintinfo.c \ + gimppaintinfo.h \ + gimppattern.c \ + gimppattern.h \ + gimppattern-header.h \ + gimppattern-load.c \ + gimppattern-load.h \ + gimppattern-save.c \ + gimppattern-save.h \ + gimppatternclipboard.c \ + gimppatternclipboard.h \ + gimppalette.c \ + gimppalette.h \ + gimppalette-import.c \ + gimppalette-import.h \ + gimppalette-load.c \ + gimppalette-load.h \ + gimppalette-save.c \ + gimppalette-save.h \ + gimppalettemru.c \ + gimppalettemru.h \ + gimpparamspecs.c \ + gimpparamspecs.h \ + gimpparamspecs-desc.c \ + gimpparamspecs-desc.h \ + gimpparamspecs-duplicate.c \ + gimpparamspecs-duplicate.h \ + gimpparasitelist.c \ + gimpparasitelist.h \ + gimppdbprogress.c \ + gimppdbprogress.h \ + gimppickable.c \ + gimppickable.h \ + gimppickable-auto-shrink.c \ + gimppickable-auto-shrink.h \ + gimppickable-contiguous-region.cc \ + gimppickable-contiguous-region.h \ + gimpprogress.c \ + gimpprogress.h \ + gimpprojectable.c \ + gimpprojectable.h \ + gimpprojection.c \ + gimpprojection.h \ + gimpsamplepoint.c \ + gimpsamplepoint.h \ + gimpsamplepointundo.c \ + gimpsamplepointundo.h \ + gimpscanconvert.c \ + gimpscanconvert.h \ + gimpselection.c \ + gimpselection.h \ + gimpsettings.c \ + gimpsettings.h \ + gimpstrokeoptions.c \ + gimpstrokeoptions.h \ + gimpsubprogress.c \ + gimpsubprogress.h \ + gimpsymmetry.c \ + gimpsymmetry.h \ + gimpsymmetry-mandala.c \ + gimpsymmetry-mandala.h \ + gimpsymmetry-mirror.c \ + gimpsymmetry-mirror.h \ + gimpsymmetry-tiling.c \ + gimpsymmetry-tiling.h \ + gimptag.c \ + gimptag.h \ + gimptagcache.c \ + gimptagcache.h \ + gimptagged.c \ + gimptagged.h \ + gimptaggedcontainer.c \ + gimptaggedcontainer.h \ + gimptempbuf.c \ + gimptempbuf.h \ + gimptemplate.c \ + gimptemplate.h \ + gimptilehandlerprojectable.c \ + gimptilehandlerprojectable.h \ + gimptoolgroup.c \ + gimptoolgroup.h \ + gimptoolinfo.c \ + gimptoolinfo.h \ + gimptoolitem.c \ + gimptoolitem.h \ + gimptooloptions.c \ + gimptooloptions.h \ + gimptoolpreset.c \ + gimptoolpreset.h \ + gimptoolpreset-load.c \ + gimptoolpreset-load.h \ + gimptoolpreset-save.c \ + gimptoolpreset-save.h \ + gimptreehandler.c \ + gimptreehandler.h \ + gimptreeproxy.c \ + gimptreeproxy.h \ + gimptriviallycancelablewaitable.c \ + gimptriviallycancelablewaitable.h \ + gimpuncancelablewaitable.c \ + gimpuncancelablewaitable.h \ + gimpunit.c \ + gimpunit.h \ + gimpundo.c \ + gimpundo.h \ + gimpundostack.c \ + gimpundostack.h \ + gimpviewable.c \ + gimpviewable.h \ + gimpwaitable.c \ + gimpwaitable.h + +libappcore_a_built_sources = \ + core-enums.c \ + gimpmarshal.c \ + gimpmarshal.h + +libappcore_a_extra_sources = \ + gimpmarshal.list + +libappcore_a_SOURCES = $(libappcore_a_built_sources) $(libappcore_a_sources) + +BUILT_SOURCES = \ + $(libappcore_a_built_sources) + +EXTRA_DIST = \ + $(libappcore_a_extra_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-gmh xgen-gmc xgen-cec +CLEANFILES = $(gen_sources) + +gimpmarshal.h: $(srcdir)/gimpmarshal.list + $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gimp_marshal $(srcdir)/gimpmarshal.list --header >> xgen-gmh \ + && (cmp -s xgen-gmh $(@F) || cp xgen-gmh $(@F)) \ + && rm -f xgen-gmh xgen-gmh~ + +gimpmarshal.c: gimpmarshal.h + $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gimp_marshal $(srcdir)/gimpmarshal.list --header --body >> xgen-gmc \ + && cp xgen-gmc $(@F) \ + && rm -f xgen-gmc xgen-gmc~ + +xgen-cec: $(srcdir)/core-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"core-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/core-enums.c: xgen-cec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/core/Makefile.in b/app/core/Makefile.in new file mode 100644 index 0000000..0d84372 --- /dev/null +++ b/app/core/Makefile.in @@ -0,0 +1,2438 @@ +# 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 = app/core +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 = +libappcore_a_AR = $(AR) $(ARFLAGS) +libappcore_a_LIBADD = +am__objects_1 = core-enums.$(OBJEXT) gimpmarshal.$(OBJEXT) +am__objects_2 = gimp.$(OBJEXT) gimp-atomic.$(OBJEXT) \ + gimp-batch.$(OBJEXT) gimp-cairo.$(OBJEXT) \ + gimp-contexts.$(OBJEXT) gimp-data-factories.$(OBJEXT) \ + gimp-edit.$(OBJEXT) gimp-filter-history.$(OBJEXT) \ + gimp-gradients.$(OBJEXT) gimp-gui.$(OBJEXT) \ + gimp-internal-data.$(OBJEXT) gimp-memsize.$(OBJEXT) \ + gimp-modules.$(OBJEXT) gimp-palettes.$(OBJEXT) \ + gimp-parallel.$(OBJEXT) gimp-parasites.$(OBJEXT) \ + gimp-spawn.$(OBJEXT) gimp-tags.$(OBJEXT) \ + gimp-templates.$(OBJEXT) gimp-transform-resize.$(OBJEXT) \ + gimp-transform-3d-utils.$(OBJEXT) \ + gimp-transform-utils.$(OBJEXT) gimp-units.$(OBJEXT) \ + gimp-user-install.$(OBJEXT) gimp-utils.$(OBJEXT) \ + gimpasync.$(OBJEXT) gimpasyncset.$(OBJEXT) \ + gimpauxitem.$(OBJEXT) gimpauxitemundo.$(OBJEXT) \ + gimpbacktrace-linux.$(OBJEXT) gimpbacktrace-none.$(OBJEXT) \ + gimpbacktrace-windows.$(OBJEXT) gimpbezierdesc.$(OBJEXT) \ + gimpboundary.$(OBJEXT) gimpbrush.$(OBJEXT) \ + gimpbrush-boundary.$(OBJEXT) gimpbrush-load.$(OBJEXT) \ + gimpbrush-mipmap.$(OBJEXT) gimpbrush-save.$(OBJEXT) \ + gimpbrush-transform.$(OBJEXT) gimpbrushcache.$(OBJEXT) \ + gimpbrushclipboard.$(OBJEXT) gimpbrushgenerated.$(OBJEXT) \ + gimpbrushgenerated-load.$(OBJEXT) \ + gimpbrushgenerated-save.$(OBJEXT) gimpbrushpipe.$(OBJEXT) \ + gimpbrushpipe-load.$(OBJEXT) gimpbrushpipe-save.$(OBJEXT) \ + gimpbuffer.$(OBJEXT) gimpcancelable.$(OBJEXT) \ + gimpchannel.$(OBJEXT) gimpchannel-combine.$(OBJEXT) \ + gimpchannel-select.$(OBJEXT) gimpchannelpropundo.$(OBJEXT) \ + gimpchannelundo.$(OBJEXT) gimpchunkiterator.$(OBJEXT) \ + gimpcontainer.$(OBJEXT) gimpcontainer-filter.$(OBJEXT) \ + gimpcontext.$(OBJEXT) gimpcoords.$(OBJEXT) \ + gimpcoords-interpolate.$(OBJEXT) gimpcurve.$(OBJEXT) \ + gimpcurve-load.$(OBJEXT) gimpcurve-map.$(OBJEXT) \ + gimpcurve-save.$(OBJEXT) gimpdashpattern.$(OBJEXT) \ + gimpdata.$(OBJEXT) gimpdatafactory.$(OBJEXT) \ + gimpdataloaderfactory.$(OBJEXT) gimpdocumentlist.$(OBJEXT) \ + gimpdrawable.$(OBJEXT) gimpdrawable-bucket-fill.$(OBJEXT) \ + gimpdrawable-combine.$(OBJEXT) gimpdrawable-edit.$(OBJEXT) \ + gimpdrawable-equalize.$(OBJEXT) gimpdrawable-fill.$(OBJEXT) \ + gimpdrawable-filters.$(OBJEXT) \ + gimpdrawable-floating-selection.$(OBJEXT) \ + gimpdrawable-foreground-extract.$(OBJEXT) \ + gimpdrawable-gradient.$(OBJEXT) \ + gimpdrawable-histogram.$(OBJEXT) gimpdrawable-levels.$(OBJEXT) \ + gimpdrawable-offset.$(OBJEXT) gimpdrawable-operation.$(OBJEXT) \ + gimpdrawable-preview.$(OBJEXT) gimpdrawable-shadow.$(OBJEXT) \ + gimpdrawable-stroke.$(OBJEXT) gimpdrawable-transform.$(OBJEXT) \ + gimpdrawablefilter.$(OBJEXT) gimpdrawablemodundo.$(OBJEXT) \ + gimpdrawablestack.$(OBJEXT) gimpdrawableundo.$(OBJEXT) \ + gimpdynamics.$(OBJEXT) gimpdynamics-load.$(OBJEXT) \ + gimpdynamics-save.$(OBJEXT) gimpdynamicsoutput.$(OBJEXT) \ + gimperror.$(OBJEXT) gimpfilloptions.$(OBJEXT) \ + gimpfilter.$(OBJEXT) gimpfilteredcontainer.$(OBJEXT) \ + gimpfilterstack.$(OBJEXT) gimpfloatingselectionundo.$(OBJEXT) \ + gimpgradient.$(OBJEXT) gimpgradient-load.$(OBJEXT) \ + gimpgradient-save.$(OBJEXT) gimpgrid.$(OBJEXT) \ + gimpgrouplayer.$(OBJEXT) gimpgrouplayerundo.$(OBJEXT) \ + gimpguide.$(OBJEXT) gimpguideundo.$(OBJEXT) \ + gimphistogram.$(OBJEXT) gimpidtable.$(OBJEXT) \ + gimpimage.$(OBJEXT) gimpimage-arrange.$(OBJEXT) \ + gimpimage-color-profile.$(OBJEXT) gimpimage-colormap.$(OBJEXT) \ + gimpimage-convert-indexed.$(OBJEXT) \ + gimpimage-convert-precision.$(OBJEXT) \ + gimpimage-convert-type.$(OBJEXT) gimpimage-crop.$(OBJEXT) \ + gimpimage-duplicate.$(OBJEXT) gimpimage-flip.$(OBJEXT) \ + gimpimage-grid.$(OBJEXT) gimpimage-guides.$(OBJEXT) \ + gimpimage-item-list.$(OBJEXT) gimpimage-merge.$(OBJEXT) \ + gimpimage-metadata.$(OBJEXT) gimpimage-new.$(OBJEXT) \ + gimpimage-pick-color.$(OBJEXT) gimpimage-pick-item.$(OBJEXT) \ + gimpimage-preview.$(OBJEXT) gimpimage-quick-mask.$(OBJEXT) \ + gimpimage-resize.$(OBJEXT) gimpimage-rotate.$(OBJEXT) \ + gimpimage-sample-points.$(OBJEXT) gimpimage-scale.$(OBJEXT) \ + gimpimage-snap.$(OBJEXT) gimpimage-symmetry.$(OBJEXT) \ + gimpimage-transform.$(OBJEXT) gimpimage-undo.$(OBJEXT) \ + gimpimage-undo-push.$(OBJEXT) gimpimageproxy.$(OBJEXT) \ + gimpimageundo.$(OBJEXT) gimpimagefile.$(OBJEXT) \ + gimpitem.$(OBJEXT) gimpitem-exclusive.$(OBJEXT) \ + gimpitem-linked.$(OBJEXT) gimpitem-preview.$(OBJEXT) \ + gimpitempropundo.$(OBJEXT) gimpitemstack.$(OBJEXT) \ + gimpitemtree.$(OBJEXT) gimpitemundo.$(OBJEXT) \ + gimplayer.$(OBJEXT) gimplayer-floating-selection.$(OBJEXT) \ + gimplayer-new.$(OBJEXT) gimplayermask.$(OBJEXT) \ + gimplayermaskpropundo.$(OBJEXT) gimplayermaskundo.$(OBJEXT) \ + gimplayerpropundo.$(OBJEXT) gimplayerstack.$(OBJEXT) \ + gimplayerundo.$(OBJEXT) gimplineart.$(OBJEXT) \ + gimplist.$(OBJEXT) gimpmaskundo.$(OBJEXT) \ + gimpmybrush.$(OBJEXT) gimpmybrush-load.$(OBJEXT) \ + gimpobject.$(OBJEXT) gimpobjectqueue.$(OBJEXT) \ + gimppaintinfo.$(OBJEXT) gimppattern.$(OBJEXT) \ + gimppattern-load.$(OBJEXT) gimppattern-save.$(OBJEXT) \ + gimppatternclipboard.$(OBJEXT) gimppalette.$(OBJEXT) \ + gimppalette-import.$(OBJEXT) gimppalette-load.$(OBJEXT) \ + gimppalette-save.$(OBJEXT) gimppalettemru.$(OBJEXT) \ + gimpparamspecs.$(OBJEXT) gimpparamspecs-desc.$(OBJEXT) \ + gimpparamspecs-duplicate.$(OBJEXT) gimpparasitelist.$(OBJEXT) \ + gimppdbprogress.$(OBJEXT) gimppickable.$(OBJEXT) \ + gimppickable-auto-shrink.$(OBJEXT) \ + gimppickable-contiguous-region.$(OBJEXT) \ + gimpprogress.$(OBJEXT) gimpprojectable.$(OBJEXT) \ + gimpprojection.$(OBJEXT) gimpsamplepoint.$(OBJEXT) \ + gimpsamplepointundo.$(OBJEXT) gimpscanconvert.$(OBJEXT) \ + gimpselection.$(OBJEXT) gimpsettings.$(OBJEXT) \ + gimpstrokeoptions.$(OBJEXT) gimpsubprogress.$(OBJEXT) \ + gimpsymmetry.$(OBJEXT) gimpsymmetry-mandala.$(OBJEXT) \ + gimpsymmetry-mirror.$(OBJEXT) gimpsymmetry-tiling.$(OBJEXT) \ + gimptag.$(OBJEXT) gimptagcache.$(OBJEXT) gimptagged.$(OBJEXT) \ + gimptaggedcontainer.$(OBJEXT) gimptempbuf.$(OBJEXT) \ + gimptemplate.$(OBJEXT) gimptilehandlerprojectable.$(OBJEXT) \ + gimptoolgroup.$(OBJEXT) gimptoolinfo.$(OBJEXT) \ + gimptoolitem.$(OBJEXT) gimptooloptions.$(OBJEXT) \ + gimptoolpreset.$(OBJEXT) gimptoolpreset-load.$(OBJEXT) \ + gimptoolpreset-save.$(OBJEXT) gimptreehandler.$(OBJEXT) \ + gimptreeproxy.$(OBJEXT) \ + gimptriviallycancelablewaitable.$(OBJEXT) \ + gimpuncancelablewaitable.$(OBJEXT) gimpunit.$(OBJEXT) \ + gimpundo.$(OBJEXT) gimpundostack.$(OBJEXT) \ + gimpviewable.$(OBJEXT) gimpwaitable.$(OBJEXT) +am_libappcore_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappcore_a_OBJECTS = $(am_libappcore_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)/core-enums.Po \ + ./$(DEPDIR)/gimp-atomic.Po ./$(DEPDIR)/gimp-batch.Po \ + ./$(DEPDIR)/gimp-cairo.Po ./$(DEPDIR)/gimp-contexts.Po \ + ./$(DEPDIR)/gimp-data-factories.Po ./$(DEPDIR)/gimp-edit.Po \ + ./$(DEPDIR)/gimp-filter-history.Po \ + ./$(DEPDIR)/gimp-gradients.Po ./$(DEPDIR)/gimp-gui.Po \ + ./$(DEPDIR)/gimp-internal-data.Po ./$(DEPDIR)/gimp-memsize.Po \ + ./$(DEPDIR)/gimp-modules.Po ./$(DEPDIR)/gimp-palettes.Po \ + ./$(DEPDIR)/gimp-parallel.Po ./$(DEPDIR)/gimp-parasites.Po \ + ./$(DEPDIR)/gimp-spawn.Po ./$(DEPDIR)/gimp-tags.Po \ + ./$(DEPDIR)/gimp-templates.Po \ + ./$(DEPDIR)/gimp-transform-3d-utils.Po \ + ./$(DEPDIR)/gimp-transform-resize.Po \ + ./$(DEPDIR)/gimp-transform-utils.Po ./$(DEPDIR)/gimp-units.Po \ + ./$(DEPDIR)/gimp-user-install.Po ./$(DEPDIR)/gimp-utils.Po \ + ./$(DEPDIR)/gimp.Po ./$(DEPDIR)/gimpasync.Po \ + ./$(DEPDIR)/gimpasyncset.Po ./$(DEPDIR)/gimpauxitem.Po \ + ./$(DEPDIR)/gimpauxitemundo.Po \ + ./$(DEPDIR)/gimpbacktrace-linux.Po \ + ./$(DEPDIR)/gimpbacktrace-none.Po \ + ./$(DEPDIR)/gimpbacktrace-windows.Po \ + ./$(DEPDIR)/gimpbezierdesc.Po ./$(DEPDIR)/gimpboundary.Po \ + ./$(DEPDIR)/gimpbrush-boundary.Po \ + ./$(DEPDIR)/gimpbrush-load.Po ./$(DEPDIR)/gimpbrush-mipmap.Po \ + ./$(DEPDIR)/gimpbrush-save.Po \ + ./$(DEPDIR)/gimpbrush-transform.Po ./$(DEPDIR)/gimpbrush.Po \ + ./$(DEPDIR)/gimpbrushcache.Po \ + ./$(DEPDIR)/gimpbrushclipboard.Po \ + ./$(DEPDIR)/gimpbrushgenerated-load.Po \ + ./$(DEPDIR)/gimpbrushgenerated-save.Po \ + ./$(DEPDIR)/gimpbrushgenerated.Po \ + ./$(DEPDIR)/gimpbrushpipe-load.Po \ + ./$(DEPDIR)/gimpbrushpipe-save.Po ./$(DEPDIR)/gimpbrushpipe.Po \ + ./$(DEPDIR)/gimpbuffer.Po ./$(DEPDIR)/gimpcancelable.Po \ + ./$(DEPDIR)/gimpchannel-combine.Po \ + ./$(DEPDIR)/gimpchannel-select.Po ./$(DEPDIR)/gimpchannel.Po \ + ./$(DEPDIR)/gimpchannelpropundo.Po \ + ./$(DEPDIR)/gimpchannelundo.Po \ + ./$(DEPDIR)/gimpchunkiterator.Po \ + ./$(DEPDIR)/gimpcontainer-filter.Po \ + ./$(DEPDIR)/gimpcontainer.Po ./$(DEPDIR)/gimpcontext.Po \ + ./$(DEPDIR)/gimpcoords-interpolate.Po \ + ./$(DEPDIR)/gimpcoords.Po ./$(DEPDIR)/gimpcurve-load.Po \ + ./$(DEPDIR)/gimpcurve-map.Po ./$(DEPDIR)/gimpcurve-save.Po \ + ./$(DEPDIR)/gimpcurve.Po ./$(DEPDIR)/gimpdashpattern.Po \ + ./$(DEPDIR)/gimpdata.Po ./$(DEPDIR)/gimpdatafactory.Po \ + ./$(DEPDIR)/gimpdataloaderfactory.Po \ + ./$(DEPDIR)/gimpdocumentlist.Po \ + ./$(DEPDIR)/gimpdrawable-bucket-fill.Po \ + ./$(DEPDIR)/gimpdrawable-combine.Po \ + ./$(DEPDIR)/gimpdrawable-edit.Po \ + ./$(DEPDIR)/gimpdrawable-equalize.Po \ + ./$(DEPDIR)/gimpdrawable-fill.Po \ + ./$(DEPDIR)/gimpdrawable-filters.Po \ + ./$(DEPDIR)/gimpdrawable-floating-selection.Po \ + ./$(DEPDIR)/gimpdrawable-foreground-extract.Po \ + ./$(DEPDIR)/gimpdrawable-gradient.Po \ + ./$(DEPDIR)/gimpdrawable-histogram.Po \ + ./$(DEPDIR)/gimpdrawable-levels.Po \ + ./$(DEPDIR)/gimpdrawable-offset.Po \ + ./$(DEPDIR)/gimpdrawable-operation.Po \ + ./$(DEPDIR)/gimpdrawable-preview.Po \ + ./$(DEPDIR)/gimpdrawable-shadow.Po \ + ./$(DEPDIR)/gimpdrawable-stroke.Po \ + ./$(DEPDIR)/gimpdrawable-transform.Po \ + ./$(DEPDIR)/gimpdrawable.Po ./$(DEPDIR)/gimpdrawablefilter.Po \ + ./$(DEPDIR)/gimpdrawablemodundo.Po \ + ./$(DEPDIR)/gimpdrawablestack.Po \ + ./$(DEPDIR)/gimpdrawableundo.Po \ + ./$(DEPDIR)/gimpdynamics-load.Po \ + ./$(DEPDIR)/gimpdynamics-save.Po ./$(DEPDIR)/gimpdynamics.Po \ + ./$(DEPDIR)/gimpdynamicsoutput.Po ./$(DEPDIR)/gimperror.Po \ + ./$(DEPDIR)/gimpfilloptions.Po ./$(DEPDIR)/gimpfilter.Po \ + ./$(DEPDIR)/gimpfilteredcontainer.Po \ + ./$(DEPDIR)/gimpfilterstack.Po \ + ./$(DEPDIR)/gimpfloatingselectionundo.Po \ + ./$(DEPDIR)/gimpgradient-load.Po \ + ./$(DEPDIR)/gimpgradient-save.Po ./$(DEPDIR)/gimpgradient.Po \ + ./$(DEPDIR)/gimpgrid.Po ./$(DEPDIR)/gimpgrouplayer.Po \ + ./$(DEPDIR)/gimpgrouplayerundo.Po ./$(DEPDIR)/gimpguide.Po \ + ./$(DEPDIR)/gimpguideundo.Po ./$(DEPDIR)/gimphistogram.Po \ + ./$(DEPDIR)/gimpidtable.Po ./$(DEPDIR)/gimpimage-arrange.Po \ + ./$(DEPDIR)/gimpimage-color-profile.Po \ + ./$(DEPDIR)/gimpimage-colormap.Po \ + ./$(DEPDIR)/gimpimage-convert-indexed.Po \ + ./$(DEPDIR)/gimpimage-convert-precision.Po \ + ./$(DEPDIR)/gimpimage-convert-type.Po \ + ./$(DEPDIR)/gimpimage-crop.Po \ + ./$(DEPDIR)/gimpimage-duplicate.Po \ + ./$(DEPDIR)/gimpimage-flip.Po ./$(DEPDIR)/gimpimage-grid.Po \ + ./$(DEPDIR)/gimpimage-guides.Po \ + ./$(DEPDIR)/gimpimage-item-list.Po \ + ./$(DEPDIR)/gimpimage-merge.Po \ + ./$(DEPDIR)/gimpimage-metadata.Po ./$(DEPDIR)/gimpimage-new.Po \ + ./$(DEPDIR)/gimpimage-pick-color.Po \ + ./$(DEPDIR)/gimpimage-pick-item.Po \ + ./$(DEPDIR)/gimpimage-preview.Po \ + ./$(DEPDIR)/gimpimage-quick-mask.Po \ + ./$(DEPDIR)/gimpimage-resize.Po \ + ./$(DEPDIR)/gimpimage-rotate.Po \ + ./$(DEPDIR)/gimpimage-sample-points.Po \ + ./$(DEPDIR)/gimpimage-scale.Po ./$(DEPDIR)/gimpimage-snap.Po \ + ./$(DEPDIR)/gimpimage-symmetry.Po \ + ./$(DEPDIR)/gimpimage-transform.Po \ + ./$(DEPDIR)/gimpimage-undo-push.Po \ + ./$(DEPDIR)/gimpimage-undo.Po ./$(DEPDIR)/gimpimage.Po \ + ./$(DEPDIR)/gimpimagefile.Po ./$(DEPDIR)/gimpimageproxy.Po \ + ./$(DEPDIR)/gimpimageundo.Po ./$(DEPDIR)/gimpitem-exclusive.Po \ + ./$(DEPDIR)/gimpitem-linked.Po ./$(DEPDIR)/gimpitem-preview.Po \ + ./$(DEPDIR)/gimpitem.Po ./$(DEPDIR)/gimpitempropundo.Po \ + ./$(DEPDIR)/gimpitemstack.Po ./$(DEPDIR)/gimpitemtree.Po \ + ./$(DEPDIR)/gimpitemundo.Po \ + ./$(DEPDIR)/gimplayer-floating-selection.Po \ + ./$(DEPDIR)/gimplayer-new.Po ./$(DEPDIR)/gimplayer.Po \ + ./$(DEPDIR)/gimplayermask.Po \ + ./$(DEPDIR)/gimplayermaskpropundo.Po \ + ./$(DEPDIR)/gimplayermaskundo.Po \ + ./$(DEPDIR)/gimplayerpropundo.Po ./$(DEPDIR)/gimplayerstack.Po \ + ./$(DEPDIR)/gimplayerundo.Po ./$(DEPDIR)/gimplineart.Po \ + ./$(DEPDIR)/gimplist.Po ./$(DEPDIR)/gimpmarshal.Po \ + ./$(DEPDIR)/gimpmaskundo.Po ./$(DEPDIR)/gimpmybrush-load.Po \ + ./$(DEPDIR)/gimpmybrush.Po ./$(DEPDIR)/gimpobject.Po \ + ./$(DEPDIR)/gimpobjectqueue.Po ./$(DEPDIR)/gimppaintinfo.Po \ + ./$(DEPDIR)/gimppalette-import.Po \ + ./$(DEPDIR)/gimppalette-load.Po \ + ./$(DEPDIR)/gimppalette-save.Po ./$(DEPDIR)/gimppalette.Po \ + ./$(DEPDIR)/gimppalettemru.Po \ + ./$(DEPDIR)/gimpparamspecs-desc.Po \ + ./$(DEPDIR)/gimpparamspecs-duplicate.Po \ + ./$(DEPDIR)/gimpparamspecs.Po ./$(DEPDIR)/gimpparasitelist.Po \ + ./$(DEPDIR)/gimppattern-load.Po \ + ./$(DEPDIR)/gimppattern-save.Po ./$(DEPDIR)/gimppattern.Po \ + ./$(DEPDIR)/gimppatternclipboard.Po \ + ./$(DEPDIR)/gimppdbprogress.Po \ + ./$(DEPDIR)/gimppickable-auto-shrink.Po \ + ./$(DEPDIR)/gimppickable-contiguous-region.Po \ + ./$(DEPDIR)/gimppickable.Po ./$(DEPDIR)/gimpprogress.Po \ + ./$(DEPDIR)/gimpprojectable.Po ./$(DEPDIR)/gimpprojection.Po \ + ./$(DEPDIR)/gimpsamplepoint.Po \ + ./$(DEPDIR)/gimpsamplepointundo.Po \ + ./$(DEPDIR)/gimpscanconvert.Po ./$(DEPDIR)/gimpselection.Po \ + ./$(DEPDIR)/gimpsettings.Po ./$(DEPDIR)/gimpstrokeoptions.Po \ + ./$(DEPDIR)/gimpsubprogress.Po \ + ./$(DEPDIR)/gimpsymmetry-mandala.Po \ + ./$(DEPDIR)/gimpsymmetry-mirror.Po \ + ./$(DEPDIR)/gimpsymmetry-tiling.Po ./$(DEPDIR)/gimpsymmetry.Po \ + ./$(DEPDIR)/gimptag.Po ./$(DEPDIR)/gimptagcache.Po \ + ./$(DEPDIR)/gimptagged.Po ./$(DEPDIR)/gimptaggedcontainer.Po \ + ./$(DEPDIR)/gimptempbuf.Po ./$(DEPDIR)/gimptemplate.Po \ + ./$(DEPDIR)/gimptilehandlerprojectable.Po \ + ./$(DEPDIR)/gimptoolgroup.Po ./$(DEPDIR)/gimptoolinfo.Po \ + ./$(DEPDIR)/gimptoolitem.Po ./$(DEPDIR)/gimptooloptions.Po \ + ./$(DEPDIR)/gimptoolpreset-load.Po \ + ./$(DEPDIR)/gimptoolpreset-save.Po \ + ./$(DEPDIR)/gimptoolpreset.Po ./$(DEPDIR)/gimptreehandler.Po \ + ./$(DEPDIR)/gimptreeproxy.Po \ + ./$(DEPDIR)/gimptriviallycancelablewaitable.Po \ + ./$(DEPDIR)/gimpuncancelablewaitable.Po \ + ./$(DEPDIR)/gimpundo.Po ./$(DEPDIR)/gimpundostack.Po \ + ./$(DEPDIR)/gimpunit.Po ./$(DEPDIR)/gimpviewable.Po \ + ./$(DEPDIR)/gimpwaitable.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 = +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 = $(libappcore_a_SOURCES) +DIST_SOURCES = $(libappcore_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 +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@ +@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c" +@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++" +@PLATFORM_OSX_TRUE@xnone = "-xnone" +AM_CPPFLAGS = \ + -DGIMPDIR=\""$(gimpdir)"\" \ + -DGIMP_APP_VERSION=\"$(GIMP_APP_VERSION)\" \ + -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \ + -DG_LOG_DOMAIN=\"Gimp-Core\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(LIBMYPAINT_CFLAGS) \ + $(MYPAINT_BRUSHES_CFLAGS) \ + $(GEXIV2_CFLAGS) \ + $(LIBUNWIND_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappcore.a +libappcore_a_sources = \ + core-enums.h \ + core-types.h \ + gimp.c \ + gimp.h \ + gimp-atomic.c \ + gimp-atomic.h \ + gimp-batch.c \ + gimp-batch.h \ + gimp-cairo.c \ + gimp-cairo.h \ + gimp-contexts.c \ + gimp-contexts.h \ + gimp-data-factories.c \ + gimp-data-factories.h \ + gimp-edit.c \ + gimp-edit.h \ + gimp-filter-history.c \ + gimp-filter-history.h \ + gimp-gradients.c \ + gimp-gradients.h \ + gimp-gui.c \ + gimp-gui.h \ + gimp-internal-data.c \ + gimp-internal-data.h \ + gimp-memsize.c \ + gimp-memsize.h \ + gimp-modules.c \ + gimp-modules.h \ + gimp-palettes.c \ + gimp-palettes.h \ + gimp-parallel.cc \ + gimp-parallel.h \ + gimp-parasites.c \ + gimp-parasites.h \ + gimp-spawn.c \ + gimp-spawn.h \ + gimp-tags.c \ + gimp-tags.h \ + gimp-templates.c \ + gimp-templates.h \ + gimp-transform-resize.c \ + gimp-transform-resize.h \ + gimp-transform-3d-utils.c \ + gimp-transform-3d-utils.h \ + gimp-transform-utils.c \ + gimp-transform-utils.h \ + gimp-units.c \ + gimp-units.h \ + gimp-user-install.c \ + gimp-user-install.h \ + gimp-utils.c \ + gimp-utils.h \ + gimpasync.c \ + gimpasync.h \ + gimpasyncset.c \ + gimpasyncset.h \ + gimpauxitem.c \ + gimpauxitem.h \ + gimpauxitemundo.c \ + gimpauxitemundo.h \ + gimpbacktrace.h \ + gimpbacktrace-backend.h \ + gimpbacktrace-linux.c \ + gimpbacktrace-none.c \ + gimpbacktrace-windows.c \ + gimpbezierdesc.h \ + gimpbezierdesc.c \ + gimpboundary.c \ + gimpboundary.h \ + gimpbrush.c \ + gimpbrush.h \ + gimpbrush-boundary.c \ + gimpbrush-boundary.h \ + gimpbrush-header.h \ + gimpbrush-load.c \ + gimpbrush-load.h \ + gimpbrush-mipmap.cc \ + gimpbrush-mipmap.h \ + gimpbrush-private.h \ + gimpbrush-save.c \ + gimpbrush-save.h \ + gimpbrush-transform.cc \ + gimpbrush-transform.h \ + gimpbrushcache.c \ + gimpbrushcache.h \ + gimpbrushclipboard.c \ + gimpbrushclipboard.h \ + gimpbrushgenerated.c \ + gimpbrushgenerated.h \ + gimpbrushgenerated-load.c \ + gimpbrushgenerated-load.h \ + gimpbrushgenerated-save.c \ + gimpbrushgenerated-save.h \ + gimpbrushpipe.c \ + gimpbrushpipe.h \ + gimpbrushpipe-load.c \ + gimpbrushpipe-load.h \ + gimpbrushpipe-save.c \ + gimpbrushpipe-save.h \ + gimpbuffer.c \ + gimpbuffer.h \ + gimpcancelable.c \ + gimpcancelable.h \ + gimpchannel.c \ + gimpchannel.h \ + gimpchannel-combine.c \ + gimpchannel-combine.h \ + gimpchannel-select.c \ + gimpchannel-select.h \ + gimpchannelpropundo.c \ + gimpchannelpropundo.h \ + gimpchannelundo.c \ + gimpchannelundo.h \ + gimpchunkiterator.c \ + gimpchunkiterator.h \ + gimpcontainer.c \ + gimpcontainer.h \ + gimpcontainer-filter.c \ + gimpcontainer-filter.h \ + gimpcontext.c \ + gimpcontext.h \ + gimpcoords.c \ + gimpcoords.h \ + gimpcoords-interpolate.c \ + gimpcoords-interpolate.h \ + gimpcurve.c \ + gimpcurve.h \ + gimpcurve-load.c \ + gimpcurve-load.h \ + gimpcurve-map.c \ + gimpcurve-map.h \ + gimpcurve-save.c \ + gimpcurve-save.h \ + gimpdashpattern.c \ + gimpdashpattern.h \ + gimpdata.c \ + gimpdata.h \ + gimpdatafactory.c \ + gimpdatafactory.h \ + gimpdataloaderfactory.c \ + gimpdataloaderfactory.h \ + gimpdocumentlist.c \ + gimpdocumentlist.h \ + gimpdrawable.c \ + gimpdrawable.h \ + gimpdrawable-bucket-fill.c \ + gimpdrawable-bucket-fill.h \ + gimpdrawable-combine.c \ + gimpdrawable-combine.h \ + gimpdrawable-edit.c \ + gimpdrawable-edit.h \ + gimpdrawable-equalize.c \ + gimpdrawable-equalize.h \ + gimpdrawable-fill.c \ + gimpdrawable-fill.h \ + gimpdrawable-filters.c \ + gimpdrawable-filters.h \ + gimpdrawable-floating-selection.c \ + gimpdrawable-floating-selection.h \ + gimpdrawable-foreground-extract.c \ + gimpdrawable-foreground-extract.h \ + gimpdrawable-gradient.c \ + gimpdrawable-gradient.h \ + gimpdrawable-histogram.c \ + gimpdrawable-histogram.h \ + gimpdrawable-levels.c \ + gimpdrawable-levels.h \ + gimpdrawable-offset.c \ + gimpdrawable-offset.h \ + gimpdrawable-operation.c \ + gimpdrawable-operation.h \ + gimpdrawable-preview.c \ + gimpdrawable-preview.h \ + gimpdrawable-private.h \ + gimpdrawable-shadow.c \ + gimpdrawable-shadow.h \ + gimpdrawable-stroke.c \ + gimpdrawable-stroke.h \ + gimpdrawable-transform.c \ + gimpdrawable-transform.h \ + gimpdrawablefilter.c \ + gimpdrawablefilter.h \ + gimpdrawablemodundo.c \ + gimpdrawablemodundo.h \ + gimpdrawablestack.c \ + gimpdrawablestack.h \ + gimpdrawableundo.c \ + gimpdrawableundo.h \ + gimpdynamics.c \ + gimpdynamics.h \ + gimpdynamics-load.c \ + gimpdynamics-load.h \ + gimpdynamics-save.c \ + gimpdynamics-save.h \ + gimpdynamicsoutput.c \ + gimpdynamicsoutput.h \ + gimperror.c \ + gimperror.h \ + gimpfilloptions.c \ + gimpfilloptions.h \ + gimpfilter.c \ + gimpfilter.h \ + gimpfilteredcontainer.c \ + gimpfilteredcontainer.h \ + gimpfilterstack.c \ + gimpfilterstack.h \ + gimpfloatingselectionundo.c \ + gimpfloatingselectionundo.h \ + gimpgradient.c \ + gimpgradient.h \ + gimpgradient-load.c \ + gimpgradient-load.h \ + gimpgradient-save.c \ + gimpgradient-save.h \ + gimpgrid.c \ + gimpgrid.h \ + gimpgrouplayer.c \ + gimpgrouplayer.h \ + gimpgrouplayerundo.c \ + gimpgrouplayerundo.h \ + gimpguide.c \ + gimpguide.h \ + gimpguideundo.c \ + gimpguideundo.h \ + gimphistogram.c \ + gimphistogram.h \ + gimpidtable.c \ + gimpidtable.h \ + gimpimage.c \ + gimpimage.h \ + gimpimage-arrange.c \ + gimpimage-arrange.h \ + gimpimage-color-profile.c \ + gimpimage-color-profile.h \ + gimpimage-colormap.c \ + gimpimage-colormap.h \ + gimpimage-convert-indexed.c \ + gimpimage-convert-indexed.h \ + gimpimage-convert-fsdither.h \ + gimpimage-convert-data.h \ + gimpimage-convert-precision.c \ + gimpimage-convert-precision.h \ + gimpimage-convert-type.c \ + gimpimage-convert-type.h \ + gimpimage-crop.c \ + gimpimage-crop.h \ + gimpimage-duplicate.c \ + gimpimage-duplicate.h \ + gimpimage-flip.c \ + gimpimage-flip.h \ + gimpimage-grid.h \ + gimpimage-grid.c \ + gimpimage-guides.c \ + gimpimage-guides.h \ + gimpimage-item-list.c \ + gimpimage-item-list.h \ + gimpimage-merge.c \ + gimpimage-merge.h \ + gimpimage-metadata.c \ + gimpimage-metadata.h \ + gimpimage-new.c \ + gimpimage-new.h \ + gimpimage-pick-color.c \ + gimpimage-pick-color.h \ + gimpimage-pick-item.c \ + gimpimage-pick-item.h \ + gimpimage-preview.c \ + gimpimage-preview.h \ + gimpimage-private.h \ + gimpimage-quick-mask.c \ + gimpimage-quick-mask.h \ + gimpimage-resize.c \ + gimpimage-resize.h \ + gimpimage-rotate.c \ + gimpimage-rotate.h \ + gimpimage-sample-points.c \ + gimpimage-sample-points.h \ + gimpimage-scale.c \ + gimpimage-scale.h \ + gimpimage-snap.c \ + gimpimage-snap.h \ + gimpimage-symmetry.c \ + gimpimage-symmetry.h \ + gimpimage-transform.c \ + gimpimage-transform.h \ + gimpimage-undo.c \ + gimpimage-undo.h \ + gimpimage-undo-push.c \ + gimpimage-undo-push.h \ + gimpimageproxy.c \ + gimpimageproxy.h \ + gimpimageundo.c \ + gimpimageundo.h \ + gimpimagefile.c \ + gimpimagefile.h \ + gimpitem.c \ + gimpitem.h \ + gimpitem-exclusive.c \ + gimpitem-exclusive.h \ + gimpitem-linked.c \ + gimpitem-linked.h \ + gimpitem-preview.c \ + gimpitem-preview.h \ + gimpitempropundo.c \ + gimpitempropundo.h \ + gimpitemstack.c \ + gimpitemstack.h \ + gimpitemtree.c \ + gimpitemtree.h \ + gimpitemundo.c \ + gimpitemundo.h \ + gimplayer.c \ + gimplayer.h \ + gimplayer-floating-selection.c \ + gimplayer-floating-selection.h \ + gimplayer-new.c \ + gimplayer-new.h \ + gimplayermask.c \ + gimplayermask.h \ + gimplayermaskpropundo.c \ + gimplayermaskpropundo.h \ + gimplayermaskundo.c \ + gimplayermaskundo.h \ + gimplayerpropundo.c \ + gimplayerpropundo.h \ + gimplayerstack.c \ + gimplayerstack.h \ + gimplayerundo.c \ + gimplayerundo.h \ + gimplineart.c \ + gimplineart.h \ + gimplist.c \ + gimplist.h \ + gimpmaskundo.c \ + gimpmaskundo.h \ + gimpmybrush.c \ + gimpmybrush.h \ + gimpmybrush-load.c \ + gimpmybrush-load.h \ + gimpmybrush-private.h \ + gimpobject.c \ + gimpobject.h \ + gimpobjectqueue.c \ + gimpobjectqueue.h \ + gimppaintinfo.c \ + gimppaintinfo.h \ + gimppattern.c \ + gimppattern.h \ + gimppattern-header.h \ + gimppattern-load.c \ + gimppattern-load.h \ + gimppattern-save.c \ + gimppattern-save.h \ + gimppatternclipboard.c \ + gimppatternclipboard.h \ + gimppalette.c \ + gimppalette.h \ + gimppalette-import.c \ + gimppalette-import.h \ + gimppalette-load.c \ + gimppalette-load.h \ + gimppalette-save.c \ + gimppalette-save.h \ + gimppalettemru.c \ + gimppalettemru.h \ + gimpparamspecs.c \ + gimpparamspecs.h \ + gimpparamspecs-desc.c \ + gimpparamspecs-desc.h \ + gimpparamspecs-duplicate.c \ + gimpparamspecs-duplicate.h \ + gimpparasitelist.c \ + gimpparasitelist.h \ + gimppdbprogress.c \ + gimppdbprogress.h \ + gimppickable.c \ + gimppickable.h \ + gimppickable-auto-shrink.c \ + gimppickable-auto-shrink.h \ + gimppickable-contiguous-region.cc \ + gimppickable-contiguous-region.h \ + gimpprogress.c \ + gimpprogress.h \ + gimpprojectable.c \ + gimpprojectable.h \ + gimpprojection.c \ + gimpprojection.h \ + gimpsamplepoint.c \ + gimpsamplepoint.h \ + gimpsamplepointundo.c \ + gimpsamplepointundo.h \ + gimpscanconvert.c \ + gimpscanconvert.h \ + gimpselection.c \ + gimpselection.h \ + gimpsettings.c \ + gimpsettings.h \ + gimpstrokeoptions.c \ + gimpstrokeoptions.h \ + gimpsubprogress.c \ + gimpsubprogress.h \ + gimpsymmetry.c \ + gimpsymmetry.h \ + gimpsymmetry-mandala.c \ + gimpsymmetry-mandala.h \ + gimpsymmetry-mirror.c \ + gimpsymmetry-mirror.h \ + gimpsymmetry-tiling.c \ + gimpsymmetry-tiling.h \ + gimptag.c \ + gimptag.h \ + gimptagcache.c \ + gimptagcache.h \ + gimptagged.c \ + gimptagged.h \ + gimptaggedcontainer.c \ + gimptaggedcontainer.h \ + gimptempbuf.c \ + gimptempbuf.h \ + gimptemplate.c \ + gimptemplate.h \ + gimptilehandlerprojectable.c \ + gimptilehandlerprojectable.h \ + gimptoolgroup.c \ + gimptoolgroup.h \ + gimptoolinfo.c \ + gimptoolinfo.h \ + gimptoolitem.c \ + gimptoolitem.h \ + gimptooloptions.c \ + gimptooloptions.h \ + gimptoolpreset.c \ + gimptoolpreset.h \ + gimptoolpreset-load.c \ + gimptoolpreset-load.h \ + gimptoolpreset-save.c \ + gimptoolpreset-save.h \ + gimptreehandler.c \ + gimptreehandler.h \ + gimptreeproxy.c \ + gimptreeproxy.h \ + gimptriviallycancelablewaitable.c \ + gimptriviallycancelablewaitable.h \ + gimpuncancelablewaitable.c \ + gimpuncancelablewaitable.h \ + gimpunit.c \ + gimpunit.h \ + gimpundo.c \ + gimpundo.h \ + gimpundostack.c \ + gimpundostack.h \ + gimpviewable.c \ + gimpviewable.h \ + gimpwaitable.c \ + gimpwaitable.h + +libappcore_a_built_sources = \ + core-enums.c \ + gimpmarshal.c \ + gimpmarshal.h + +libappcore_a_extra_sources = \ + gimpmarshal.list + +libappcore_a_SOURCES = $(libappcore_a_built_sources) $(libappcore_a_sources) +BUILT_SOURCES = \ + $(libappcore_a_built_sources) + +EXTRA_DIST = \ + $(libappcore_a_extra_sources) + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-gmh xgen-gmc xgen-cec +CLEANFILES = $(gen_sources) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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 app/core/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/core/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) + +libappcore.a: $(libappcore_a_OBJECTS) $(libappcore_a_DEPENDENCIES) $(EXTRA_libappcore_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappcore.a + $(AM_V_AR)$(libappcore_a_AR) libappcore.a $(libappcore_a_OBJECTS) $(libappcore_a_LIBADD) + $(AM_V_at)$(RANLIB) libappcore.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core-enums.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-atomic.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-batch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-cairo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-contexts.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-data-factories.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-edit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-filter-history.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gradients.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gui.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-internal-data.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-memsize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-modules.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-palettes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-parallel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-parasites.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-spawn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-tags.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-templates.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-transform-3d-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-transform-resize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-transform-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-units.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-user-install.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-utils.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)/gimpasync.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpasyncset.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpauxitem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpauxitemundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbacktrace-linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbacktrace-none.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbacktrace-windows.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbezierdesc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpboundary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush-boundary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush-mipmap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush-transform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushcache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushclipboard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushgenerated-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushgenerated-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushgenerated.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushpipe-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushpipe-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushpipe.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbuffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcancelable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannel-combine.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannel-select.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannelpropundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannelundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchunkiterator.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcontainer-filter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcontainer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcontext.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcoords-interpolate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcoords.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurve-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurve-map.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurve-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurve.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdashpattern.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdata.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdatafactory.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdataloaderfactory.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdocumentlist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-bucket-fill.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-combine.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-edit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-equalize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-fill.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-filters.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-floating-selection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-foreground-extract.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-gradient.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-histogram.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-levels.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-offset.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-operation.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-preview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-shadow.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-stroke.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable-transform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawablefilter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawablemodundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawablestack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawableundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdynamics-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdynamics-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdynamics.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdynamicsoutput.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimperror.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfilloptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfilter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfilteredcontainer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfilterstack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfloatingselectionundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradient-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradient-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradient.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgrouplayer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgrouplayerundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpguide.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpguideundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphistogram.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpidtable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-arrange.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-color-profile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-colormap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-convert-indexed.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-convert-precision.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-convert-type.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-crop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-duplicate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-flip.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-grid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-guides.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-item-list.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-merge.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-metadata.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-new.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-pick-color.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-pick-item.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-preview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-quick-mask.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-resize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-rotate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-sample-points.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-scale.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-snap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-symmetry.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-transform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-undo-push.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage-undo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagefile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageproxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitem-exclusive.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitem-linked.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitem-preview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitempropundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitemstack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitemtree.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitemundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayer-floating-selection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayer-new.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayermask.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayermaskpropundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayermaskundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayerpropundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayerstack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayerundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplineart.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmarshal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmaskundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmybrush-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmybrush.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpobject.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpobjectqueue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintinfo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette-import.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalettemru.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparamspecs-desc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparamspecs-duplicate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparamspecs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparasitelist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppattern-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppattern-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppattern.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatternclipboard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppdbprogress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickable-auto-shrink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickable-contiguous-region.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprogress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprojectable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprojection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsamplepoint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsamplepointundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpscanconvert.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpselection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsettings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpstrokeoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsubprogress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsymmetry-mandala.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsymmetry-mirror.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsymmetry-tiling.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsymmetry.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptag.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptagcache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptagged.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptaggedcontainer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptempbuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptemplate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptilehandlerprojectable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolgroup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolinfo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolitem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptooloptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolpreset-load.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolpreset-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolpreset.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptreehandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptreeproxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptriviallycancelablewaitable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpuncancelablewaitable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpundostack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpviewable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwaitable.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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LIBRARIES) +installdirs: +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-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/core-enums.Po + -rm -f ./$(DEPDIR)/gimp-atomic.Po + -rm -f ./$(DEPDIR)/gimp-batch.Po + -rm -f ./$(DEPDIR)/gimp-cairo.Po + -rm -f ./$(DEPDIR)/gimp-contexts.Po + -rm -f ./$(DEPDIR)/gimp-data-factories.Po + -rm -f ./$(DEPDIR)/gimp-edit.Po + -rm -f ./$(DEPDIR)/gimp-filter-history.Po + -rm -f ./$(DEPDIR)/gimp-gradients.Po + -rm -f ./$(DEPDIR)/gimp-gui.Po + -rm -f ./$(DEPDIR)/gimp-internal-data.Po + -rm -f ./$(DEPDIR)/gimp-memsize.Po + -rm -f ./$(DEPDIR)/gimp-modules.Po + -rm -f ./$(DEPDIR)/gimp-palettes.Po + -rm -f ./$(DEPDIR)/gimp-parallel.Po + -rm -f ./$(DEPDIR)/gimp-parasites.Po + -rm -f ./$(DEPDIR)/gimp-spawn.Po + -rm -f ./$(DEPDIR)/gimp-tags.Po + -rm -f ./$(DEPDIR)/gimp-templates.Po + -rm -f ./$(DEPDIR)/gimp-transform-3d-utils.Po + -rm -f ./$(DEPDIR)/gimp-transform-resize.Po + -rm -f ./$(DEPDIR)/gimp-transform-utils.Po + -rm -f ./$(DEPDIR)/gimp-units.Po + -rm -f ./$(DEPDIR)/gimp-user-install.Po + -rm -f ./$(DEPDIR)/gimp-utils.Po + -rm -f ./$(DEPDIR)/gimp.Po + -rm -f ./$(DEPDIR)/gimpasync.Po + -rm -f ./$(DEPDIR)/gimpasyncset.Po + -rm -f ./$(DEPDIR)/gimpauxitem.Po + -rm -f ./$(DEPDIR)/gimpauxitemundo.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-linux.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-none.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-windows.Po + -rm -f ./$(DEPDIR)/gimpbezierdesc.Po + -rm -f ./$(DEPDIR)/gimpboundary.Po + -rm -f ./$(DEPDIR)/gimpbrush-boundary.Po + -rm -f ./$(DEPDIR)/gimpbrush-load.Po + -rm -f ./$(DEPDIR)/gimpbrush-mipmap.Po + -rm -f ./$(DEPDIR)/gimpbrush-save.Po + -rm -f ./$(DEPDIR)/gimpbrush-transform.Po + -rm -f ./$(DEPDIR)/gimpbrush.Po + -rm -f ./$(DEPDIR)/gimpbrushcache.Po + -rm -f ./$(DEPDIR)/gimpbrushclipboard.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated-load.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated-save.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe-load.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe-save.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe.Po + -rm -f ./$(DEPDIR)/gimpbuffer.Po + -rm -f ./$(DEPDIR)/gimpcancelable.Po + -rm -f ./$(DEPDIR)/gimpchannel-combine.Po + -rm -f ./$(DEPDIR)/gimpchannel-select.Po + -rm -f ./$(DEPDIR)/gimpchannel.Po + -rm -f ./$(DEPDIR)/gimpchannelpropundo.Po + -rm -f ./$(DEPDIR)/gimpchannelundo.Po + -rm -f ./$(DEPDIR)/gimpchunkiterator.Po + -rm -f ./$(DEPDIR)/gimpcontainer-filter.Po + -rm -f ./$(DEPDIR)/gimpcontainer.Po + -rm -f ./$(DEPDIR)/gimpcontext.Po + -rm -f ./$(DEPDIR)/gimpcoords-interpolate.Po + -rm -f ./$(DEPDIR)/gimpcoords.Po + -rm -f ./$(DEPDIR)/gimpcurve-load.Po + -rm -f ./$(DEPDIR)/gimpcurve-map.Po + -rm -f ./$(DEPDIR)/gimpcurve-save.Po + -rm -f ./$(DEPDIR)/gimpcurve.Po + -rm -f ./$(DEPDIR)/gimpdashpattern.Po + -rm -f ./$(DEPDIR)/gimpdata.Po + -rm -f ./$(DEPDIR)/gimpdatafactory.Po + -rm -f ./$(DEPDIR)/gimpdataloaderfactory.Po + -rm -f ./$(DEPDIR)/gimpdocumentlist.Po + -rm -f ./$(DEPDIR)/gimpdrawable-bucket-fill.Po + -rm -f ./$(DEPDIR)/gimpdrawable-combine.Po + -rm -f ./$(DEPDIR)/gimpdrawable-edit.Po + -rm -f ./$(DEPDIR)/gimpdrawable-equalize.Po + -rm -f ./$(DEPDIR)/gimpdrawable-fill.Po + -rm -f ./$(DEPDIR)/gimpdrawable-filters.Po + -rm -f ./$(DEPDIR)/gimpdrawable-floating-selection.Po + -rm -f ./$(DEPDIR)/gimpdrawable-foreground-extract.Po + -rm -f ./$(DEPDIR)/gimpdrawable-gradient.Po + -rm -f ./$(DEPDIR)/gimpdrawable-histogram.Po + -rm -f ./$(DEPDIR)/gimpdrawable-levels.Po + -rm -f ./$(DEPDIR)/gimpdrawable-offset.Po + -rm -f ./$(DEPDIR)/gimpdrawable-operation.Po + -rm -f ./$(DEPDIR)/gimpdrawable-preview.Po + -rm -f ./$(DEPDIR)/gimpdrawable-shadow.Po + -rm -f ./$(DEPDIR)/gimpdrawable-stroke.Po + -rm -f ./$(DEPDIR)/gimpdrawable-transform.Po + -rm -f ./$(DEPDIR)/gimpdrawable.Po + -rm -f ./$(DEPDIR)/gimpdrawablefilter.Po + -rm -f ./$(DEPDIR)/gimpdrawablemodundo.Po + -rm -f ./$(DEPDIR)/gimpdrawablestack.Po + -rm -f ./$(DEPDIR)/gimpdrawableundo.Po + -rm -f ./$(DEPDIR)/gimpdynamics-load.Po + -rm -f ./$(DEPDIR)/gimpdynamics-save.Po + -rm -f ./$(DEPDIR)/gimpdynamics.Po + -rm -f ./$(DEPDIR)/gimpdynamicsoutput.Po + -rm -f ./$(DEPDIR)/gimperror.Po + -rm -f ./$(DEPDIR)/gimpfilloptions.Po + -rm -f ./$(DEPDIR)/gimpfilter.Po + -rm -f ./$(DEPDIR)/gimpfilteredcontainer.Po + -rm -f ./$(DEPDIR)/gimpfilterstack.Po + -rm -f ./$(DEPDIR)/gimpfloatingselectionundo.Po + -rm -f ./$(DEPDIR)/gimpgradient-load.Po + -rm -f ./$(DEPDIR)/gimpgradient-save.Po + -rm -f ./$(DEPDIR)/gimpgradient.Po + -rm -f ./$(DEPDIR)/gimpgrid.Po + -rm -f ./$(DEPDIR)/gimpgrouplayer.Po + -rm -f ./$(DEPDIR)/gimpgrouplayerundo.Po + -rm -f ./$(DEPDIR)/gimpguide.Po + -rm -f ./$(DEPDIR)/gimpguideundo.Po + -rm -f ./$(DEPDIR)/gimphistogram.Po + -rm -f ./$(DEPDIR)/gimpidtable.Po + -rm -f ./$(DEPDIR)/gimpimage-arrange.Po + -rm -f ./$(DEPDIR)/gimpimage-color-profile.Po + -rm -f ./$(DEPDIR)/gimpimage-colormap.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-indexed.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-precision.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-type.Po + -rm -f ./$(DEPDIR)/gimpimage-crop.Po + -rm -f ./$(DEPDIR)/gimpimage-duplicate.Po + -rm -f ./$(DEPDIR)/gimpimage-flip.Po + -rm -f ./$(DEPDIR)/gimpimage-grid.Po + -rm -f ./$(DEPDIR)/gimpimage-guides.Po + -rm -f ./$(DEPDIR)/gimpimage-item-list.Po + -rm -f ./$(DEPDIR)/gimpimage-merge.Po + -rm -f ./$(DEPDIR)/gimpimage-metadata.Po + -rm -f ./$(DEPDIR)/gimpimage-new.Po + -rm -f ./$(DEPDIR)/gimpimage-pick-color.Po + -rm -f ./$(DEPDIR)/gimpimage-pick-item.Po + -rm -f ./$(DEPDIR)/gimpimage-preview.Po + -rm -f ./$(DEPDIR)/gimpimage-quick-mask.Po + -rm -f ./$(DEPDIR)/gimpimage-resize.Po + -rm -f ./$(DEPDIR)/gimpimage-rotate.Po + -rm -f ./$(DEPDIR)/gimpimage-sample-points.Po + -rm -f ./$(DEPDIR)/gimpimage-scale.Po + -rm -f ./$(DEPDIR)/gimpimage-snap.Po + -rm -f ./$(DEPDIR)/gimpimage-symmetry.Po + -rm -f ./$(DEPDIR)/gimpimage-transform.Po + -rm -f ./$(DEPDIR)/gimpimage-undo-push.Po + -rm -f ./$(DEPDIR)/gimpimage-undo.Po + -rm -f ./$(DEPDIR)/gimpimage.Po + -rm -f ./$(DEPDIR)/gimpimagefile.Po + -rm -f ./$(DEPDIR)/gimpimageproxy.Po + -rm -f ./$(DEPDIR)/gimpimageundo.Po + -rm -f ./$(DEPDIR)/gimpitem-exclusive.Po + -rm -f ./$(DEPDIR)/gimpitem-linked.Po + -rm -f ./$(DEPDIR)/gimpitem-preview.Po + -rm -f ./$(DEPDIR)/gimpitem.Po + -rm -f ./$(DEPDIR)/gimpitempropundo.Po + -rm -f ./$(DEPDIR)/gimpitemstack.Po + -rm -f ./$(DEPDIR)/gimpitemtree.Po + -rm -f ./$(DEPDIR)/gimpitemundo.Po + -rm -f ./$(DEPDIR)/gimplayer-floating-selection.Po + -rm -f ./$(DEPDIR)/gimplayer-new.Po + -rm -f ./$(DEPDIR)/gimplayer.Po + -rm -f ./$(DEPDIR)/gimplayermask.Po + -rm -f ./$(DEPDIR)/gimplayermaskpropundo.Po + -rm -f ./$(DEPDIR)/gimplayermaskundo.Po + -rm -f ./$(DEPDIR)/gimplayerpropundo.Po + -rm -f ./$(DEPDIR)/gimplayerstack.Po + -rm -f ./$(DEPDIR)/gimplayerundo.Po + -rm -f ./$(DEPDIR)/gimplineart.Po + -rm -f ./$(DEPDIR)/gimplist.Po + -rm -f ./$(DEPDIR)/gimpmarshal.Po + -rm -f ./$(DEPDIR)/gimpmaskundo.Po + -rm -f ./$(DEPDIR)/gimpmybrush-load.Po + -rm -f ./$(DEPDIR)/gimpmybrush.Po + -rm -f ./$(DEPDIR)/gimpobject.Po + -rm -f ./$(DEPDIR)/gimpobjectqueue.Po + -rm -f ./$(DEPDIR)/gimppaintinfo.Po + -rm -f ./$(DEPDIR)/gimppalette-import.Po + -rm -f ./$(DEPDIR)/gimppalette-load.Po + -rm -f ./$(DEPDIR)/gimppalette-save.Po + -rm -f ./$(DEPDIR)/gimppalette.Po + -rm -f ./$(DEPDIR)/gimppalettemru.Po + -rm -f ./$(DEPDIR)/gimpparamspecs-desc.Po + -rm -f ./$(DEPDIR)/gimpparamspecs-duplicate.Po + -rm -f ./$(DEPDIR)/gimpparamspecs.Po + -rm -f ./$(DEPDIR)/gimpparasitelist.Po + -rm -f ./$(DEPDIR)/gimppattern-load.Po + -rm -f ./$(DEPDIR)/gimppattern-save.Po + -rm -f ./$(DEPDIR)/gimppattern.Po + -rm -f ./$(DEPDIR)/gimppatternclipboard.Po + -rm -f ./$(DEPDIR)/gimppdbprogress.Po + -rm -f ./$(DEPDIR)/gimppickable-auto-shrink.Po + -rm -f ./$(DEPDIR)/gimppickable-contiguous-region.Po + -rm -f ./$(DEPDIR)/gimppickable.Po + -rm -f ./$(DEPDIR)/gimpprogress.Po + -rm -f ./$(DEPDIR)/gimpprojectable.Po + -rm -f ./$(DEPDIR)/gimpprojection.Po + -rm -f ./$(DEPDIR)/gimpsamplepoint.Po + -rm -f ./$(DEPDIR)/gimpsamplepointundo.Po + -rm -f ./$(DEPDIR)/gimpscanconvert.Po + -rm -f ./$(DEPDIR)/gimpselection.Po + -rm -f ./$(DEPDIR)/gimpsettings.Po + -rm -f ./$(DEPDIR)/gimpstrokeoptions.Po + -rm -f ./$(DEPDIR)/gimpsubprogress.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-mandala.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-mirror.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-tiling.Po + -rm -f ./$(DEPDIR)/gimpsymmetry.Po + -rm -f ./$(DEPDIR)/gimptag.Po + -rm -f ./$(DEPDIR)/gimptagcache.Po + -rm -f ./$(DEPDIR)/gimptagged.Po + -rm -f ./$(DEPDIR)/gimptaggedcontainer.Po + -rm -f ./$(DEPDIR)/gimptempbuf.Po + -rm -f ./$(DEPDIR)/gimptemplate.Po + -rm -f ./$(DEPDIR)/gimptilehandlerprojectable.Po + -rm -f ./$(DEPDIR)/gimptoolgroup.Po + -rm -f ./$(DEPDIR)/gimptoolinfo.Po + -rm -f ./$(DEPDIR)/gimptoolitem.Po + -rm -f ./$(DEPDIR)/gimptooloptions.Po + -rm -f ./$(DEPDIR)/gimptoolpreset-load.Po + -rm -f ./$(DEPDIR)/gimptoolpreset-save.Po + -rm -f ./$(DEPDIR)/gimptoolpreset.Po + -rm -f ./$(DEPDIR)/gimptreehandler.Po + -rm -f ./$(DEPDIR)/gimptreeproxy.Po + -rm -f ./$(DEPDIR)/gimptriviallycancelablewaitable.Po + -rm -f ./$(DEPDIR)/gimpuncancelablewaitable.Po + -rm -f ./$(DEPDIR)/gimpundo.Po + -rm -f ./$(DEPDIR)/gimpundostack.Po + -rm -f ./$(DEPDIR)/gimpunit.Po + -rm -f ./$(DEPDIR)/gimpviewable.Po + -rm -f ./$(DEPDIR)/gimpwaitable.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)/core-enums.Po + -rm -f ./$(DEPDIR)/gimp-atomic.Po + -rm -f ./$(DEPDIR)/gimp-batch.Po + -rm -f ./$(DEPDIR)/gimp-cairo.Po + -rm -f ./$(DEPDIR)/gimp-contexts.Po + -rm -f ./$(DEPDIR)/gimp-data-factories.Po + -rm -f ./$(DEPDIR)/gimp-edit.Po + -rm -f ./$(DEPDIR)/gimp-filter-history.Po + -rm -f ./$(DEPDIR)/gimp-gradients.Po + -rm -f ./$(DEPDIR)/gimp-gui.Po + -rm -f ./$(DEPDIR)/gimp-internal-data.Po + -rm -f ./$(DEPDIR)/gimp-memsize.Po + -rm -f ./$(DEPDIR)/gimp-modules.Po + -rm -f ./$(DEPDIR)/gimp-palettes.Po + -rm -f ./$(DEPDIR)/gimp-parallel.Po + -rm -f ./$(DEPDIR)/gimp-parasites.Po + -rm -f ./$(DEPDIR)/gimp-spawn.Po + -rm -f ./$(DEPDIR)/gimp-tags.Po + -rm -f ./$(DEPDIR)/gimp-templates.Po + -rm -f ./$(DEPDIR)/gimp-transform-3d-utils.Po + -rm -f ./$(DEPDIR)/gimp-transform-resize.Po + -rm -f ./$(DEPDIR)/gimp-transform-utils.Po + -rm -f ./$(DEPDIR)/gimp-units.Po + -rm -f ./$(DEPDIR)/gimp-user-install.Po + -rm -f ./$(DEPDIR)/gimp-utils.Po + -rm -f ./$(DEPDIR)/gimp.Po + -rm -f ./$(DEPDIR)/gimpasync.Po + -rm -f ./$(DEPDIR)/gimpasyncset.Po + -rm -f ./$(DEPDIR)/gimpauxitem.Po + -rm -f ./$(DEPDIR)/gimpauxitemundo.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-linux.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-none.Po + -rm -f ./$(DEPDIR)/gimpbacktrace-windows.Po + -rm -f ./$(DEPDIR)/gimpbezierdesc.Po + -rm -f ./$(DEPDIR)/gimpboundary.Po + -rm -f ./$(DEPDIR)/gimpbrush-boundary.Po + -rm -f ./$(DEPDIR)/gimpbrush-load.Po + -rm -f ./$(DEPDIR)/gimpbrush-mipmap.Po + -rm -f ./$(DEPDIR)/gimpbrush-save.Po + -rm -f ./$(DEPDIR)/gimpbrush-transform.Po + -rm -f ./$(DEPDIR)/gimpbrush.Po + -rm -f ./$(DEPDIR)/gimpbrushcache.Po + -rm -f ./$(DEPDIR)/gimpbrushclipboard.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated-load.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated-save.Po + -rm -f ./$(DEPDIR)/gimpbrushgenerated.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe-load.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe-save.Po + -rm -f ./$(DEPDIR)/gimpbrushpipe.Po + -rm -f ./$(DEPDIR)/gimpbuffer.Po + -rm -f ./$(DEPDIR)/gimpcancelable.Po + -rm -f ./$(DEPDIR)/gimpchannel-combine.Po + -rm -f ./$(DEPDIR)/gimpchannel-select.Po + -rm -f ./$(DEPDIR)/gimpchannel.Po + -rm -f ./$(DEPDIR)/gimpchannelpropundo.Po + -rm -f ./$(DEPDIR)/gimpchannelundo.Po + -rm -f ./$(DEPDIR)/gimpchunkiterator.Po + -rm -f ./$(DEPDIR)/gimpcontainer-filter.Po + -rm -f ./$(DEPDIR)/gimpcontainer.Po + -rm -f ./$(DEPDIR)/gimpcontext.Po + -rm -f ./$(DEPDIR)/gimpcoords-interpolate.Po + -rm -f ./$(DEPDIR)/gimpcoords.Po + -rm -f ./$(DEPDIR)/gimpcurve-load.Po + -rm -f ./$(DEPDIR)/gimpcurve-map.Po + -rm -f ./$(DEPDIR)/gimpcurve-save.Po + -rm -f ./$(DEPDIR)/gimpcurve.Po + -rm -f ./$(DEPDIR)/gimpdashpattern.Po + -rm -f ./$(DEPDIR)/gimpdata.Po + -rm -f ./$(DEPDIR)/gimpdatafactory.Po + -rm -f ./$(DEPDIR)/gimpdataloaderfactory.Po + -rm -f ./$(DEPDIR)/gimpdocumentlist.Po + -rm -f ./$(DEPDIR)/gimpdrawable-bucket-fill.Po + -rm -f ./$(DEPDIR)/gimpdrawable-combine.Po + -rm -f ./$(DEPDIR)/gimpdrawable-edit.Po + -rm -f ./$(DEPDIR)/gimpdrawable-equalize.Po + -rm -f ./$(DEPDIR)/gimpdrawable-fill.Po + -rm -f ./$(DEPDIR)/gimpdrawable-filters.Po + -rm -f ./$(DEPDIR)/gimpdrawable-floating-selection.Po + -rm -f ./$(DEPDIR)/gimpdrawable-foreground-extract.Po + -rm -f ./$(DEPDIR)/gimpdrawable-gradient.Po + -rm -f ./$(DEPDIR)/gimpdrawable-histogram.Po + -rm -f ./$(DEPDIR)/gimpdrawable-levels.Po + -rm -f ./$(DEPDIR)/gimpdrawable-offset.Po + -rm -f ./$(DEPDIR)/gimpdrawable-operation.Po + -rm -f ./$(DEPDIR)/gimpdrawable-preview.Po + -rm -f ./$(DEPDIR)/gimpdrawable-shadow.Po + -rm -f ./$(DEPDIR)/gimpdrawable-stroke.Po + -rm -f ./$(DEPDIR)/gimpdrawable-transform.Po + -rm -f ./$(DEPDIR)/gimpdrawable.Po + -rm -f ./$(DEPDIR)/gimpdrawablefilter.Po + -rm -f ./$(DEPDIR)/gimpdrawablemodundo.Po + -rm -f ./$(DEPDIR)/gimpdrawablestack.Po + -rm -f ./$(DEPDIR)/gimpdrawableundo.Po + -rm -f ./$(DEPDIR)/gimpdynamics-load.Po + -rm -f ./$(DEPDIR)/gimpdynamics-save.Po + -rm -f ./$(DEPDIR)/gimpdynamics.Po + -rm -f ./$(DEPDIR)/gimpdynamicsoutput.Po + -rm -f ./$(DEPDIR)/gimperror.Po + -rm -f ./$(DEPDIR)/gimpfilloptions.Po + -rm -f ./$(DEPDIR)/gimpfilter.Po + -rm -f ./$(DEPDIR)/gimpfilteredcontainer.Po + -rm -f ./$(DEPDIR)/gimpfilterstack.Po + -rm -f ./$(DEPDIR)/gimpfloatingselectionundo.Po + -rm -f ./$(DEPDIR)/gimpgradient-load.Po + -rm -f ./$(DEPDIR)/gimpgradient-save.Po + -rm -f ./$(DEPDIR)/gimpgradient.Po + -rm -f ./$(DEPDIR)/gimpgrid.Po + -rm -f ./$(DEPDIR)/gimpgrouplayer.Po + -rm -f ./$(DEPDIR)/gimpgrouplayerundo.Po + -rm -f ./$(DEPDIR)/gimpguide.Po + -rm -f ./$(DEPDIR)/gimpguideundo.Po + -rm -f ./$(DEPDIR)/gimphistogram.Po + -rm -f ./$(DEPDIR)/gimpidtable.Po + -rm -f ./$(DEPDIR)/gimpimage-arrange.Po + -rm -f ./$(DEPDIR)/gimpimage-color-profile.Po + -rm -f ./$(DEPDIR)/gimpimage-colormap.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-indexed.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-precision.Po + -rm -f ./$(DEPDIR)/gimpimage-convert-type.Po + -rm -f ./$(DEPDIR)/gimpimage-crop.Po + -rm -f ./$(DEPDIR)/gimpimage-duplicate.Po + -rm -f ./$(DEPDIR)/gimpimage-flip.Po + -rm -f ./$(DEPDIR)/gimpimage-grid.Po + -rm -f ./$(DEPDIR)/gimpimage-guides.Po + -rm -f ./$(DEPDIR)/gimpimage-item-list.Po + -rm -f ./$(DEPDIR)/gimpimage-merge.Po + -rm -f ./$(DEPDIR)/gimpimage-metadata.Po + -rm -f ./$(DEPDIR)/gimpimage-new.Po + -rm -f ./$(DEPDIR)/gimpimage-pick-color.Po + -rm -f ./$(DEPDIR)/gimpimage-pick-item.Po + -rm -f ./$(DEPDIR)/gimpimage-preview.Po + -rm -f ./$(DEPDIR)/gimpimage-quick-mask.Po + -rm -f ./$(DEPDIR)/gimpimage-resize.Po + -rm -f ./$(DEPDIR)/gimpimage-rotate.Po + -rm -f ./$(DEPDIR)/gimpimage-sample-points.Po + -rm -f ./$(DEPDIR)/gimpimage-scale.Po + -rm -f ./$(DEPDIR)/gimpimage-snap.Po + -rm -f ./$(DEPDIR)/gimpimage-symmetry.Po + -rm -f ./$(DEPDIR)/gimpimage-transform.Po + -rm -f ./$(DEPDIR)/gimpimage-undo-push.Po + -rm -f ./$(DEPDIR)/gimpimage-undo.Po + -rm -f ./$(DEPDIR)/gimpimage.Po + -rm -f ./$(DEPDIR)/gimpimagefile.Po + -rm -f ./$(DEPDIR)/gimpimageproxy.Po + -rm -f ./$(DEPDIR)/gimpimageundo.Po + -rm -f ./$(DEPDIR)/gimpitem-exclusive.Po + -rm -f ./$(DEPDIR)/gimpitem-linked.Po + -rm -f ./$(DEPDIR)/gimpitem-preview.Po + -rm -f ./$(DEPDIR)/gimpitem.Po + -rm -f ./$(DEPDIR)/gimpitempropundo.Po + -rm -f ./$(DEPDIR)/gimpitemstack.Po + -rm -f ./$(DEPDIR)/gimpitemtree.Po + -rm -f ./$(DEPDIR)/gimpitemundo.Po + -rm -f ./$(DEPDIR)/gimplayer-floating-selection.Po + -rm -f ./$(DEPDIR)/gimplayer-new.Po + -rm -f ./$(DEPDIR)/gimplayer.Po + -rm -f ./$(DEPDIR)/gimplayermask.Po + -rm -f ./$(DEPDIR)/gimplayermaskpropundo.Po + -rm -f ./$(DEPDIR)/gimplayermaskundo.Po + -rm -f ./$(DEPDIR)/gimplayerpropundo.Po + -rm -f ./$(DEPDIR)/gimplayerstack.Po + -rm -f ./$(DEPDIR)/gimplayerundo.Po + -rm -f ./$(DEPDIR)/gimplineart.Po + -rm -f ./$(DEPDIR)/gimplist.Po + -rm -f ./$(DEPDIR)/gimpmarshal.Po + -rm -f ./$(DEPDIR)/gimpmaskundo.Po + -rm -f ./$(DEPDIR)/gimpmybrush-load.Po + -rm -f ./$(DEPDIR)/gimpmybrush.Po + -rm -f ./$(DEPDIR)/gimpobject.Po + -rm -f ./$(DEPDIR)/gimpobjectqueue.Po + -rm -f ./$(DEPDIR)/gimppaintinfo.Po + -rm -f ./$(DEPDIR)/gimppalette-import.Po + -rm -f ./$(DEPDIR)/gimppalette-load.Po + -rm -f ./$(DEPDIR)/gimppalette-save.Po + -rm -f ./$(DEPDIR)/gimppalette.Po + -rm -f ./$(DEPDIR)/gimppalettemru.Po + -rm -f ./$(DEPDIR)/gimpparamspecs-desc.Po + -rm -f ./$(DEPDIR)/gimpparamspecs-duplicate.Po + -rm -f ./$(DEPDIR)/gimpparamspecs.Po + -rm -f ./$(DEPDIR)/gimpparasitelist.Po + -rm -f ./$(DEPDIR)/gimppattern-load.Po + -rm -f ./$(DEPDIR)/gimppattern-save.Po + -rm -f ./$(DEPDIR)/gimppattern.Po + -rm -f ./$(DEPDIR)/gimppatternclipboard.Po + -rm -f ./$(DEPDIR)/gimppdbprogress.Po + -rm -f ./$(DEPDIR)/gimppickable-auto-shrink.Po + -rm -f ./$(DEPDIR)/gimppickable-contiguous-region.Po + -rm -f ./$(DEPDIR)/gimppickable.Po + -rm -f ./$(DEPDIR)/gimpprogress.Po + -rm -f ./$(DEPDIR)/gimpprojectable.Po + -rm -f ./$(DEPDIR)/gimpprojection.Po + -rm -f ./$(DEPDIR)/gimpsamplepoint.Po + -rm -f ./$(DEPDIR)/gimpsamplepointundo.Po + -rm -f ./$(DEPDIR)/gimpscanconvert.Po + -rm -f ./$(DEPDIR)/gimpselection.Po + -rm -f ./$(DEPDIR)/gimpsettings.Po + -rm -f ./$(DEPDIR)/gimpstrokeoptions.Po + -rm -f ./$(DEPDIR)/gimpsubprogress.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-mandala.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-mirror.Po + -rm -f ./$(DEPDIR)/gimpsymmetry-tiling.Po + -rm -f ./$(DEPDIR)/gimpsymmetry.Po + -rm -f ./$(DEPDIR)/gimptag.Po + -rm -f ./$(DEPDIR)/gimptagcache.Po + -rm -f ./$(DEPDIR)/gimptagged.Po + -rm -f ./$(DEPDIR)/gimptaggedcontainer.Po + -rm -f ./$(DEPDIR)/gimptempbuf.Po + -rm -f ./$(DEPDIR)/gimptemplate.Po + -rm -f ./$(DEPDIR)/gimptilehandlerprojectable.Po + -rm -f ./$(DEPDIR)/gimptoolgroup.Po + -rm -f ./$(DEPDIR)/gimptoolinfo.Po + -rm -f ./$(DEPDIR)/gimptoolitem.Po + -rm -f ./$(DEPDIR)/gimptooloptions.Po + -rm -f ./$(DEPDIR)/gimptoolpreset-load.Po + -rm -f ./$(DEPDIR)/gimptoolpreset-save.Po + -rm -f ./$(DEPDIR)/gimptoolpreset.Po + -rm -f ./$(DEPDIR)/gimptreehandler.Po + -rm -f ./$(DEPDIR)/gimptreeproxy.Po + -rm -f ./$(DEPDIR)/gimptriviallycancelablewaitable.Po + -rm -f ./$(DEPDIR)/gimpuncancelablewaitable.Po + -rm -f ./$(DEPDIR)/gimpundo.Po + -rm -f ./$(DEPDIR)/gimpundostack.Po + -rm -f ./$(DEPDIR)/gimpunit.Po + -rm -f ./$(DEPDIR)/gimpviewable.Po + -rm -f ./$(DEPDIR)/gimpwaitable.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: 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-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 + + +gimpmarshal.h: $(srcdir)/gimpmarshal.list + $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gimp_marshal $(srcdir)/gimpmarshal.list --header >> xgen-gmh \ + && (cmp -s xgen-gmh $(@F) || cp xgen-gmh $(@F)) \ + && rm -f xgen-gmh xgen-gmh~ + +gimpmarshal.c: gimpmarshal.h + $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gimp_marshal $(srcdir)/gimpmarshal.list --header --body >> xgen-gmc \ + && cp xgen-gmc $(@F) \ + && rm -f xgen-gmc xgen-gmc~ + +xgen-cec: $(srcdir)/core-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"core-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/core-enums.c: xgen-cec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/core/core-enums.c b/app/core/core-enums.c new file mode 100644 index 0000000..f8ecb14 --- /dev/null +++ b/app/core/core-enums.c @@ -0,0 +1,1316 @@ + +/* Generated data (by gimp-mkenums) */ + +#include "config.h" +#include +#include "libgimpbase/gimpbase.h" +#include "core-enums.h" +#include "gimp-intl.h" + +/* enumerations from "core-enums.h" */ +GType +gimp_align_reference_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ALIGN_REFERENCE_FIRST, "GIMP_ALIGN_REFERENCE_FIRST", "first" }, + { GIMP_ALIGN_REFERENCE_IMAGE, "GIMP_ALIGN_REFERENCE_IMAGE", "image" }, + { GIMP_ALIGN_REFERENCE_SELECTION, "GIMP_ALIGN_REFERENCE_SELECTION", "selection" }, + { GIMP_ALIGN_REFERENCE_ACTIVE_LAYER, "GIMP_ALIGN_REFERENCE_ACTIVE_LAYER", "active-layer" }, + { GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL, "GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL", "active-channel" }, + { GIMP_ALIGN_REFERENCE_ACTIVE_PATH, "GIMP_ALIGN_REFERENCE_ACTIVE_PATH", "active-path" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ALIGN_REFERENCE_FIRST, NC_("align-reference-type", "First item"), NULL }, + { GIMP_ALIGN_REFERENCE_IMAGE, NC_("align-reference-type", "Image"), NULL }, + { GIMP_ALIGN_REFERENCE_SELECTION, NC_("align-reference-type", "Selection"), NULL }, + { GIMP_ALIGN_REFERENCE_ACTIVE_LAYER, NC_("align-reference-type", "Active layer"), NULL }, + { GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL, NC_("align-reference-type", "Active channel"), NULL }, + { GIMP_ALIGN_REFERENCE_ACTIVE_PATH, NC_("align-reference-type", "Active path"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpAlignReferenceType", values); + gimp_type_set_translation_context (type, "align-reference-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_alignment_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ALIGN_LEFT, "GIMP_ALIGN_LEFT", "align-left" }, + { GIMP_ALIGN_HCENTER, "GIMP_ALIGN_HCENTER", "align-hcenter" }, + { GIMP_ALIGN_RIGHT, "GIMP_ALIGN_RIGHT", "align-right" }, + { GIMP_ALIGN_TOP, "GIMP_ALIGN_TOP", "align-top" }, + { GIMP_ALIGN_VCENTER, "GIMP_ALIGN_VCENTER", "align-vcenter" }, + { GIMP_ALIGN_BOTTOM, "GIMP_ALIGN_BOTTOM", "align-bottom" }, + { GIMP_ARRANGE_LEFT, "GIMP_ARRANGE_LEFT", "arrange-left" }, + { GIMP_ARRANGE_HCENTER, "GIMP_ARRANGE_HCENTER", "arrange-hcenter" }, + { GIMP_ARRANGE_RIGHT, "GIMP_ARRANGE_RIGHT", "arrange-right" }, + { GIMP_ARRANGE_TOP, "GIMP_ARRANGE_TOP", "arrange-top" }, + { GIMP_ARRANGE_VCENTER, "GIMP_ARRANGE_VCENTER", "arrange-vcenter" }, + { GIMP_ARRANGE_BOTTOM, "GIMP_ARRANGE_BOTTOM", "arrange-bottom" }, + { GIMP_ARRANGE_HFILL, "GIMP_ARRANGE_HFILL", "arrange-hfill" }, + { GIMP_ARRANGE_VFILL, "GIMP_ARRANGE_VFILL", "arrange-vfill" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ALIGN_LEFT, "GIMP_ALIGN_LEFT", NULL }, + { GIMP_ALIGN_HCENTER, "GIMP_ALIGN_HCENTER", NULL }, + { GIMP_ALIGN_RIGHT, "GIMP_ALIGN_RIGHT", NULL }, + { GIMP_ALIGN_TOP, "GIMP_ALIGN_TOP", NULL }, + { GIMP_ALIGN_VCENTER, "GIMP_ALIGN_VCENTER", NULL }, + { GIMP_ALIGN_BOTTOM, "GIMP_ALIGN_BOTTOM", NULL }, + { GIMP_ARRANGE_LEFT, "GIMP_ARRANGE_LEFT", NULL }, + { GIMP_ARRANGE_HCENTER, "GIMP_ARRANGE_HCENTER", NULL }, + { GIMP_ARRANGE_RIGHT, "GIMP_ARRANGE_RIGHT", NULL }, + { GIMP_ARRANGE_TOP, "GIMP_ARRANGE_TOP", NULL }, + { GIMP_ARRANGE_VCENTER, "GIMP_ARRANGE_VCENTER", NULL }, + { GIMP_ARRANGE_BOTTOM, "GIMP_ARRANGE_BOTTOM", NULL }, + { GIMP_ARRANGE_HFILL, "GIMP_ARRANGE_HFILL", NULL }, + { GIMP_ARRANGE_VFILL, "GIMP_ARRANGE_VFILL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpAlignmentType", values); + gimp_type_set_translation_context (type, "alignment-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_channel_border_style_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CHANNEL_BORDER_STYLE_HARD, "GIMP_CHANNEL_BORDER_STYLE_HARD", "hard" }, + { GIMP_CHANNEL_BORDER_STYLE_SMOOTH, "GIMP_CHANNEL_BORDER_STYLE_SMOOTH", "smooth" }, + { GIMP_CHANNEL_BORDER_STYLE_FEATHERED, "GIMP_CHANNEL_BORDER_STYLE_FEATHERED", "feathered" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CHANNEL_BORDER_STYLE_HARD, NC_("channel-border-style", "Hard"), NULL }, + { GIMP_CHANNEL_BORDER_STYLE_SMOOTH, NC_("channel-border-style", "Smooth"), NULL }, + { GIMP_CHANNEL_BORDER_STYLE_FEATHERED, NC_("channel-border-style", "Feathered"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpChannelBorderStyle", values); + gimp_type_set_translation_context (type, "channel-border-style"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_color_pick_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_COLOR_PICK_MODE_PIXEL, "GIMP_COLOR_PICK_MODE_PIXEL", "pixel" }, + { GIMP_COLOR_PICK_MODE_RGB_PERCENT, "GIMP_COLOR_PICK_MODE_RGB_PERCENT", "rgb-percent" }, + { GIMP_COLOR_PICK_MODE_RGB_U8, "GIMP_COLOR_PICK_MODE_RGB_U8", "rgb-u8" }, + { GIMP_COLOR_PICK_MODE_HSV, "GIMP_COLOR_PICK_MODE_HSV", "hsv" }, + { GIMP_COLOR_PICK_MODE_LCH, "GIMP_COLOR_PICK_MODE_LCH", "lch" }, + { GIMP_COLOR_PICK_MODE_LAB, "GIMP_COLOR_PICK_MODE_LAB", "lab" }, + { GIMP_COLOR_PICK_MODE_CMYK, "GIMP_COLOR_PICK_MODE_CMYK", "cmyk" }, + { GIMP_COLOR_PICK_MODE_XYY, "GIMP_COLOR_PICK_MODE_XYY", "xyy" }, + { GIMP_COLOR_PICK_MODE_YUV, "GIMP_COLOR_PICK_MODE_YUV", "yuv" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_COLOR_PICK_MODE_PIXEL, NC_("color-pick-mode", "Pixel"), NULL }, + { GIMP_COLOR_PICK_MODE_RGB_PERCENT, NC_("color-pick-mode", "RGB (%)"), NULL }, + { GIMP_COLOR_PICK_MODE_RGB_U8, NC_("color-pick-mode", "RGB (0..255)"), NULL }, + { GIMP_COLOR_PICK_MODE_HSV, NC_("color-pick-mode", "HSV"), NULL }, + { GIMP_COLOR_PICK_MODE_LCH, NC_("color-pick-mode", "CIE LCh"), NULL }, + { GIMP_COLOR_PICK_MODE_LAB, NC_("color-pick-mode", "CIE LAB"), NULL }, + { GIMP_COLOR_PICK_MODE_CMYK, NC_("color-pick-mode", "CMYK"), NULL }, + { GIMP_COLOR_PICK_MODE_XYY, NC_("color-pick-mode", "CIE xyY"), NULL }, + { GIMP_COLOR_PICK_MODE_YUV, NC_("color-pick-mode", "CIE Yu'v'"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpColorPickMode", values); + gimp_type_set_translation_context (type, "color-pick-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_color_profile_policy_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_COLOR_PROFILE_POLICY_ASK, "GIMP_COLOR_PROFILE_POLICY_ASK", "ask" }, + { GIMP_COLOR_PROFILE_POLICY_KEEP, "GIMP_COLOR_PROFILE_POLICY_KEEP", "keep" }, + { GIMP_COLOR_PROFILE_POLICY_CONVERT, "GIMP_COLOR_PROFILE_POLICY_CONVERT", "convert" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_COLOR_PROFILE_POLICY_ASK, NC_("color-profile-policy", "Ask what to do"), NULL }, + { GIMP_COLOR_PROFILE_POLICY_KEEP, NC_("color-profile-policy", "Keep embedded profile"), NULL }, + { GIMP_COLOR_PROFILE_POLICY_CONVERT, NC_("color-profile-policy", "Convert to built-in sRGB or grayscale profile"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpColorProfilePolicy", values); + gimp_type_set_translation_context (type, "color-profile-policy"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_component_mask_get_type (void) +{ + static const GFlagsValue values[] = + { + { GIMP_COMPONENT_MASK_RED, "GIMP_COMPONENT_MASK_RED", "red" }, + { GIMP_COMPONENT_MASK_GREEN, "GIMP_COMPONENT_MASK_GREEN", "green" }, + { GIMP_COMPONENT_MASK_BLUE, "GIMP_COMPONENT_MASK_BLUE", "blue" }, + { GIMP_COMPONENT_MASK_ALPHA, "GIMP_COMPONENT_MASK_ALPHA", "alpha" }, + { GIMP_COMPONENT_MASK_ALL, "GIMP_COMPONENT_MASK_ALL", "all" }, + { 0, NULL, NULL } + }; + + static const GimpFlagsDesc descs[] = + { + { GIMP_COMPONENT_MASK_RED, "GIMP_COMPONENT_MASK_RED", NULL }, + { GIMP_COMPONENT_MASK_GREEN, "GIMP_COMPONENT_MASK_GREEN", NULL }, + { GIMP_COMPONENT_MASK_BLUE, "GIMP_COMPONENT_MASK_BLUE", NULL }, + { GIMP_COMPONENT_MASK_ALPHA, "GIMP_COMPONENT_MASK_ALPHA", NULL }, + { GIMP_COMPONENT_MASK_ALL, "GIMP_COMPONENT_MASK_ALL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_flags_register_static ("GimpComponentMask", values); + gimp_type_set_translation_context (type, "component-mask"); + gimp_flags_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_container_policy_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CONTAINER_POLICY_STRONG, "GIMP_CONTAINER_POLICY_STRONG", "strong" }, + { GIMP_CONTAINER_POLICY_WEAK, "GIMP_CONTAINER_POLICY_WEAK", "weak" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CONTAINER_POLICY_STRONG, "GIMP_CONTAINER_POLICY_STRONG", NULL }, + { GIMP_CONTAINER_POLICY_WEAK, "GIMP_CONTAINER_POLICY_WEAK", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpContainerPolicy", values); + gimp_type_set_translation_context (type, "container-policy"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_convert_dither_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CONVERT_DITHER_NONE, "GIMP_CONVERT_DITHER_NONE", "none" }, + { GIMP_CONVERT_DITHER_FS, "GIMP_CONVERT_DITHER_FS", "fs" }, + { GIMP_CONVERT_DITHER_FS_LOWBLEED, "GIMP_CONVERT_DITHER_FS_LOWBLEED", "fs-lowbleed" }, + { GIMP_CONVERT_DITHER_FIXED, "GIMP_CONVERT_DITHER_FIXED", "fixed" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CONVERT_DITHER_NONE, NC_("convert-dither-type", "None"), NULL }, + { GIMP_CONVERT_DITHER_FS, NC_("convert-dither-type", "Floyd-Steinberg (normal)"), NULL }, + { GIMP_CONVERT_DITHER_FS_LOWBLEED, NC_("convert-dither-type", "Floyd-Steinberg (reduced color bleeding)"), NULL }, + { GIMP_CONVERT_DITHER_FIXED, NC_("convert-dither-type", "Positioned"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpConvertDitherType", values); + gimp_type_set_translation_context (type, "convert-dither-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_convolution_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_NORMAL_CONVOL, "GIMP_NORMAL_CONVOL", "normal-convol" }, + { GIMP_ABSOLUTE_CONVOL, "GIMP_ABSOLUTE_CONVOL", "absolute-convol" }, + { GIMP_NEGATIVE_CONVOL, "GIMP_NEGATIVE_CONVOL", "negative-convol" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_NORMAL_CONVOL, "GIMP_NORMAL_CONVOL", NULL }, + { GIMP_ABSOLUTE_CONVOL, "GIMP_ABSOLUTE_CONVOL", NULL }, + { GIMP_NEGATIVE_CONVOL, "GIMP_NEGATIVE_CONVOL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpConvolutionType", values); + gimp_type_set_translation_context (type, "convolution-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_curve_point_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CURVE_POINT_SMOOTH, "GIMP_CURVE_POINT_SMOOTH", "smooth" }, + { GIMP_CURVE_POINT_CORNER, "GIMP_CURVE_POINT_CORNER", "corner" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CURVE_POINT_SMOOTH, NC_("curve-point-type", "Smooth"), NULL }, + { GIMP_CURVE_POINT_CORNER, NC_("curve-point-type", "Corner"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCurvePointType", values); + gimp_type_set_translation_context (type, "curve-point-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_curve_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CURVE_SMOOTH, "GIMP_CURVE_SMOOTH", "smooth" }, + { GIMP_CURVE_FREE, "GIMP_CURVE_FREE", "free" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CURVE_SMOOTH, NC_("curve-type", "Smooth"), NULL }, + { GIMP_CURVE_FREE, NC_("curve-type", "Freehand"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCurveType", values); + gimp_type_set_translation_context (type, "curve-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_dash_preset_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_DASH_CUSTOM, "GIMP_DASH_CUSTOM", "custom" }, + { GIMP_DASH_LINE, "GIMP_DASH_LINE", "line" }, + { GIMP_DASH_LONG_DASH, "GIMP_DASH_LONG_DASH", "long-dash" }, + { GIMP_DASH_MEDIUM_DASH, "GIMP_DASH_MEDIUM_DASH", "medium-dash" }, + { GIMP_DASH_SHORT_DASH, "GIMP_DASH_SHORT_DASH", "short-dash" }, + { GIMP_DASH_SPARSE_DOTS, "GIMP_DASH_SPARSE_DOTS", "sparse-dots" }, + { GIMP_DASH_NORMAL_DOTS, "GIMP_DASH_NORMAL_DOTS", "normal-dots" }, + { GIMP_DASH_DENSE_DOTS, "GIMP_DASH_DENSE_DOTS", "dense-dots" }, + { GIMP_DASH_STIPPLES, "GIMP_DASH_STIPPLES", "stipples" }, + { GIMP_DASH_DASH_DOT, "GIMP_DASH_DASH_DOT", "dash-dot" }, + { GIMP_DASH_DASH_DOT_DOT, "GIMP_DASH_DASH_DOT_DOT", "dash-dot-dot" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_DASH_CUSTOM, NC_("dash-preset", "Custom"), NULL }, + { GIMP_DASH_LINE, NC_("dash-preset", "Line"), NULL }, + { GIMP_DASH_LONG_DASH, NC_("dash-preset", "Long dashes"), NULL }, + { GIMP_DASH_MEDIUM_DASH, NC_("dash-preset", "Medium dashes"), NULL }, + { GIMP_DASH_SHORT_DASH, NC_("dash-preset", "Short dashes"), NULL }, + { GIMP_DASH_SPARSE_DOTS, NC_("dash-preset", "Sparse dots"), NULL }, + { GIMP_DASH_NORMAL_DOTS, NC_("dash-preset", "Normal dots"), NULL }, + { GIMP_DASH_DENSE_DOTS, NC_("dash-preset", "Dense dots"), NULL }, + { GIMP_DASH_STIPPLES, NC_("dash-preset", "Stipples"), NULL }, + { GIMP_DASH_DASH_DOT, NC_("dash-preset", "Dash, dot"), NULL }, + { GIMP_DASH_DASH_DOT_DOT, NC_("dash-preset", "Dash, dot, dot"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpDashPreset", values); + gimp_type_set_translation_context (type, "dash-preset"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_debug_policy_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_DEBUG_POLICY_WARNING, "GIMP_DEBUG_POLICY_WARNING", "warning" }, + { GIMP_DEBUG_POLICY_CRITICAL, "GIMP_DEBUG_POLICY_CRITICAL", "critical" }, + { GIMP_DEBUG_POLICY_FATAL, "GIMP_DEBUG_POLICY_FATAL", "fatal" }, + { GIMP_DEBUG_POLICY_NEVER, "GIMP_DEBUG_POLICY_NEVER", "never" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_DEBUG_POLICY_WARNING, NC_("debug-policy", "Debug warnings, critical errors and crashes"), NULL }, + { GIMP_DEBUG_POLICY_CRITICAL, NC_("debug-policy", "Debug critical errors and crashes"), NULL }, + { GIMP_DEBUG_POLICY_FATAL, NC_("debug-policy", "Debug crashes only"), NULL }, + { GIMP_DEBUG_POLICY_NEVER, NC_("debug-policy", "Never debug GIMP"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpDebugPolicy", values); + gimp_type_set_translation_context (type, "debug-policy"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_dirty_mask_get_type (void) +{ + static const GFlagsValue values[] = + { + { GIMP_DIRTY_NONE, "GIMP_DIRTY_NONE", "none" }, + { GIMP_DIRTY_IMAGE, "GIMP_DIRTY_IMAGE", "image" }, + { GIMP_DIRTY_IMAGE_SIZE, "GIMP_DIRTY_IMAGE_SIZE", "image-size" }, + { GIMP_DIRTY_IMAGE_META, "GIMP_DIRTY_IMAGE_META", "image-meta" }, + { GIMP_DIRTY_IMAGE_STRUCTURE, "GIMP_DIRTY_IMAGE_STRUCTURE", "image-structure" }, + { GIMP_DIRTY_ITEM, "GIMP_DIRTY_ITEM", "item" }, + { GIMP_DIRTY_ITEM_META, "GIMP_DIRTY_ITEM_META", "item-meta" }, + { GIMP_DIRTY_DRAWABLE, "GIMP_DIRTY_DRAWABLE", "drawable" }, + { GIMP_DIRTY_VECTORS, "GIMP_DIRTY_VECTORS", "vectors" }, + { GIMP_DIRTY_SELECTION, "GIMP_DIRTY_SELECTION", "selection" }, + { GIMP_DIRTY_ACTIVE_DRAWABLE, "GIMP_DIRTY_ACTIVE_DRAWABLE", "active-drawable" }, + { GIMP_DIRTY_ALL, "GIMP_DIRTY_ALL", "all" }, + { 0, NULL, NULL } + }; + + static const GimpFlagsDesc descs[] = + { + { GIMP_DIRTY_NONE, "GIMP_DIRTY_NONE", NULL }, + { GIMP_DIRTY_IMAGE, "GIMP_DIRTY_IMAGE", NULL }, + { GIMP_DIRTY_IMAGE_SIZE, "GIMP_DIRTY_IMAGE_SIZE", NULL }, + { GIMP_DIRTY_IMAGE_META, "GIMP_DIRTY_IMAGE_META", NULL }, + { GIMP_DIRTY_IMAGE_STRUCTURE, "GIMP_DIRTY_IMAGE_STRUCTURE", NULL }, + { GIMP_DIRTY_ITEM, "GIMP_DIRTY_ITEM", NULL }, + { GIMP_DIRTY_ITEM_META, "GIMP_DIRTY_ITEM_META", NULL }, + { GIMP_DIRTY_DRAWABLE, "GIMP_DIRTY_DRAWABLE", NULL }, + { GIMP_DIRTY_VECTORS, "GIMP_DIRTY_VECTORS", NULL }, + { GIMP_DIRTY_SELECTION, "GIMP_DIRTY_SELECTION", NULL }, + { GIMP_DIRTY_ACTIVE_DRAWABLE, "GIMP_DIRTY_ACTIVE_DRAWABLE", NULL }, + { GIMP_DIRTY_ALL, "GIMP_DIRTY_ALL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_flags_register_static ("GimpDirtyMask", values); + gimp_type_set_translation_context (type, "dirty-mask"); + gimp_flags_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_dynamics_output_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_DYNAMICS_OUTPUT_OPACITY, "GIMP_DYNAMICS_OUTPUT_OPACITY", "opacity" }, + { GIMP_DYNAMICS_OUTPUT_SIZE, "GIMP_DYNAMICS_OUTPUT_SIZE", "size" }, + { GIMP_DYNAMICS_OUTPUT_ANGLE, "GIMP_DYNAMICS_OUTPUT_ANGLE", "angle" }, + { GIMP_DYNAMICS_OUTPUT_COLOR, "GIMP_DYNAMICS_OUTPUT_COLOR", "color" }, + { GIMP_DYNAMICS_OUTPUT_HARDNESS, "GIMP_DYNAMICS_OUTPUT_HARDNESS", "hardness" }, + { GIMP_DYNAMICS_OUTPUT_FORCE, "GIMP_DYNAMICS_OUTPUT_FORCE", "force" }, + { GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO, "GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO", "aspect-ratio" }, + { GIMP_DYNAMICS_OUTPUT_SPACING, "GIMP_DYNAMICS_OUTPUT_SPACING", "spacing" }, + { GIMP_DYNAMICS_OUTPUT_RATE, "GIMP_DYNAMICS_OUTPUT_RATE", "rate" }, + { GIMP_DYNAMICS_OUTPUT_FLOW, "GIMP_DYNAMICS_OUTPUT_FLOW", "flow" }, + { GIMP_DYNAMICS_OUTPUT_JITTER, "GIMP_DYNAMICS_OUTPUT_JITTER", "jitter" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_DYNAMICS_OUTPUT_OPACITY, NC_("dynamics-output-type", "Opacity"), NULL }, + { GIMP_DYNAMICS_OUTPUT_SIZE, NC_("dynamics-output-type", "Size"), NULL }, + { GIMP_DYNAMICS_OUTPUT_ANGLE, NC_("dynamics-output-type", "Angle"), NULL }, + { GIMP_DYNAMICS_OUTPUT_COLOR, NC_("dynamics-output-type", "Color"), NULL }, + { GIMP_DYNAMICS_OUTPUT_HARDNESS, NC_("dynamics-output-type", "Hardness"), NULL }, + { GIMP_DYNAMICS_OUTPUT_FORCE, NC_("dynamics-output-type", "Force"), NULL }, + { GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO, NC_("dynamics-output-type", "Aspect ratio"), NULL }, + { GIMP_DYNAMICS_OUTPUT_SPACING, NC_("dynamics-output-type", "Spacing"), NULL }, + { GIMP_DYNAMICS_OUTPUT_RATE, NC_("dynamics-output-type", "Rate"), NULL }, + { GIMP_DYNAMICS_OUTPUT_FLOW, NC_("dynamics-output-type", "Flow"), NULL }, + { GIMP_DYNAMICS_OUTPUT_JITTER, NC_("dynamics-output-type", "Jitter"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpDynamicsOutputType", values); + gimp_type_set_translation_context (type, "dynamics-output-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_fill_style_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_FILL_STYLE_SOLID, "GIMP_FILL_STYLE_SOLID", "solid" }, + { GIMP_FILL_STYLE_PATTERN, "GIMP_FILL_STYLE_PATTERN", "pattern" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_FILL_STYLE_SOLID, NC_("fill-style", "Solid color"), NULL }, + { GIMP_FILL_STYLE_PATTERN, NC_("fill-style", "Pattern"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpFillStyle", values); + gimp_type_set_translation_context (type, "fill-style"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_filter_region_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_FILTER_REGION_SELECTION, "GIMP_FILTER_REGION_SELECTION", "selection" }, + { GIMP_FILTER_REGION_DRAWABLE, "GIMP_FILTER_REGION_DRAWABLE", "drawable" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_FILTER_REGION_SELECTION, NC_("filter-region", "Use the selection as input"), NULL }, + { GIMP_FILTER_REGION_DRAWABLE, NC_("filter-region", "Use the entire layer as input"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpFilterRegion", values); + gimp_type_set_translation_context (type, "filter-region"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_gradient_color_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_GRADIENT_COLOR_FIXED, "GIMP_GRADIENT_COLOR_FIXED", "fixed" }, + { GIMP_GRADIENT_COLOR_FOREGROUND, "GIMP_GRADIENT_COLOR_FOREGROUND", "foreground" }, + { GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, "GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT", "foreground-transparent" }, + { GIMP_GRADIENT_COLOR_BACKGROUND, "GIMP_GRADIENT_COLOR_BACKGROUND", "background" }, + { GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT, "GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT", "background-transparent" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_GRADIENT_COLOR_FIXED, NC_("gradient-color", "Fixed"), NULL }, + { GIMP_GRADIENT_COLOR_FOREGROUND, NC_("gradient-color", "Foreground color"), NULL }, + /* Translators: this is an abbreviated version of "Foreground color". + Keep it short. */ + { GIMP_GRADIENT_COLOR_FOREGROUND, NC_("gradient-color", "FG"), NULL }, + { GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, NC_("gradient-color", "Foreground color (transparent)"), NULL }, + /* Translators: this is an abbreviated version of "Foreground color (transparent)". + Keep it short. */ + { GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, NC_("gradient-color", "FG (t)"), NULL }, + { GIMP_GRADIENT_COLOR_BACKGROUND, NC_("gradient-color", "Background color"), NULL }, + /* Translators: this is an abbreviated version of "Background color". + Keep it short. */ + { GIMP_GRADIENT_COLOR_BACKGROUND, NC_("gradient-color", "BG"), NULL }, + { GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT, NC_("gradient-color", "Background color (transparent)"), NULL }, + /* Translators: this is an abbreviated version of "Background color (transparent)". + Keep it short. */ + { GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT, NC_("gradient-color", "BG (t)"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpGradientColor", values); + gimp_type_set_translation_context (type, "gradient-color"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_gravity_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_GRAVITY_NONE, "GIMP_GRAVITY_NONE", "none" }, + { GIMP_GRAVITY_NORTH_WEST, "GIMP_GRAVITY_NORTH_WEST", "north-west" }, + { GIMP_GRAVITY_NORTH, "GIMP_GRAVITY_NORTH", "north" }, + { GIMP_GRAVITY_NORTH_EAST, "GIMP_GRAVITY_NORTH_EAST", "north-east" }, + { GIMP_GRAVITY_WEST, "GIMP_GRAVITY_WEST", "west" }, + { GIMP_GRAVITY_CENTER, "GIMP_GRAVITY_CENTER", "center" }, + { GIMP_GRAVITY_EAST, "GIMP_GRAVITY_EAST", "east" }, + { GIMP_GRAVITY_SOUTH_WEST, "GIMP_GRAVITY_SOUTH_WEST", "south-west" }, + { GIMP_GRAVITY_SOUTH, "GIMP_GRAVITY_SOUTH", "south" }, + { GIMP_GRAVITY_SOUTH_EAST, "GIMP_GRAVITY_SOUTH_EAST", "south-east" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_GRAVITY_NONE, "GIMP_GRAVITY_NONE", NULL }, + { GIMP_GRAVITY_NORTH_WEST, "GIMP_GRAVITY_NORTH_WEST", NULL }, + { GIMP_GRAVITY_NORTH, "GIMP_GRAVITY_NORTH", NULL }, + { GIMP_GRAVITY_NORTH_EAST, "GIMP_GRAVITY_NORTH_EAST", NULL }, + { GIMP_GRAVITY_WEST, "GIMP_GRAVITY_WEST", NULL }, + { GIMP_GRAVITY_CENTER, "GIMP_GRAVITY_CENTER", NULL }, + { GIMP_GRAVITY_EAST, "GIMP_GRAVITY_EAST", NULL }, + { GIMP_GRAVITY_SOUTH_WEST, "GIMP_GRAVITY_SOUTH_WEST", NULL }, + { GIMP_GRAVITY_SOUTH, "GIMP_GRAVITY_SOUTH", NULL }, + { GIMP_GRAVITY_SOUTH_EAST, "GIMP_GRAVITY_SOUTH_EAST", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpGravityType", values); + gimp_type_set_translation_context (type, "gravity-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_guide_style_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_GUIDE_STYLE_NONE, "GIMP_GUIDE_STYLE_NONE", "none" }, + { GIMP_GUIDE_STYLE_NORMAL, "GIMP_GUIDE_STYLE_NORMAL", "normal" }, + { GIMP_GUIDE_STYLE_MIRROR, "GIMP_GUIDE_STYLE_MIRROR", "mirror" }, + { GIMP_GUIDE_STYLE_MANDALA, "GIMP_GUIDE_STYLE_MANDALA", "mandala" }, + { GIMP_GUIDE_STYLE_SPLIT_VIEW, "GIMP_GUIDE_STYLE_SPLIT_VIEW", "split-view" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_GUIDE_STYLE_NONE, "GIMP_GUIDE_STYLE_NONE", NULL }, + { GIMP_GUIDE_STYLE_NORMAL, "GIMP_GUIDE_STYLE_NORMAL", NULL }, + { GIMP_GUIDE_STYLE_MIRROR, "GIMP_GUIDE_STYLE_MIRROR", NULL }, + { GIMP_GUIDE_STYLE_MANDALA, "GIMP_GUIDE_STYLE_MANDALA", NULL }, + { GIMP_GUIDE_STYLE_SPLIT_VIEW, "GIMP_GUIDE_STYLE_SPLIT_VIEW", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpGuideStyle", values); + gimp_type_set_translation_context (type, "guide-style"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_histogram_channel_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HISTOGRAM_VALUE, "GIMP_HISTOGRAM_VALUE", "value" }, + { GIMP_HISTOGRAM_RED, "GIMP_HISTOGRAM_RED", "red" }, + { GIMP_HISTOGRAM_GREEN, "GIMP_HISTOGRAM_GREEN", "green" }, + { GIMP_HISTOGRAM_BLUE, "GIMP_HISTOGRAM_BLUE", "blue" }, + { GIMP_HISTOGRAM_ALPHA, "GIMP_HISTOGRAM_ALPHA", "alpha" }, + { GIMP_HISTOGRAM_LUMINANCE, "GIMP_HISTOGRAM_LUMINANCE", "luminance" }, + { GIMP_HISTOGRAM_RGB, "GIMP_HISTOGRAM_RGB", "rgb" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HISTOGRAM_VALUE, NC_("histogram-channel", "Value"), NULL }, + { GIMP_HISTOGRAM_RED, NC_("histogram-channel", "Red"), NULL }, + { GIMP_HISTOGRAM_GREEN, NC_("histogram-channel", "Green"), NULL }, + { GIMP_HISTOGRAM_BLUE, NC_("histogram-channel", "Blue"), NULL }, + { GIMP_HISTOGRAM_ALPHA, NC_("histogram-channel", "Alpha"), NULL }, + { GIMP_HISTOGRAM_LUMINANCE, NC_("histogram-channel", "Luminance"), NULL }, + { GIMP_HISTOGRAM_RGB, NC_("histogram-channel", "RGB"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpHistogramChannel", values); + gimp_type_set_translation_context (type, "histogram-channel"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_item_set_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ITEM_SET_NONE, "GIMP_ITEM_SET_NONE", "none" }, + { GIMP_ITEM_SET_ALL, "GIMP_ITEM_SET_ALL", "all" }, + { GIMP_ITEM_SET_IMAGE_SIZED, "GIMP_ITEM_SET_IMAGE_SIZED", "image-sized" }, + { GIMP_ITEM_SET_VISIBLE, "GIMP_ITEM_SET_VISIBLE", "visible" }, + { GIMP_ITEM_SET_LINKED, "GIMP_ITEM_SET_LINKED", "linked" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ITEM_SET_NONE, NC_("item-set", "None"), NULL }, + { GIMP_ITEM_SET_ALL, NC_("item-set", "All layers"), NULL }, + { GIMP_ITEM_SET_IMAGE_SIZED, NC_("item-set", "Image-sized layers"), NULL }, + { GIMP_ITEM_SET_VISIBLE, NC_("item-set", "All visible layers"), NULL }, + { GIMP_ITEM_SET_LINKED, NC_("item-set", "All linked layers"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpItemSet", values); + gimp_type_set_translation_context (type, "item-set"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_matting_engine_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_MATTING_ENGINE_GLOBAL, "GIMP_MATTING_ENGINE_GLOBAL", "global" }, + { GIMP_MATTING_ENGINE_LEVIN, "GIMP_MATTING_ENGINE_LEVIN", "levin" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_MATTING_ENGINE_GLOBAL, NC_("matting-engine", "Matting Global"), NULL }, + { GIMP_MATTING_ENGINE_LEVIN, NC_("matting-engine", "Matting Levin"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpMattingEngine", values); + gimp_type_set_translation_context (type, "matting-engine"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_message_severity_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_MESSAGE_INFO, "GIMP_MESSAGE_INFO", "info" }, + { GIMP_MESSAGE_WARNING, "GIMP_MESSAGE_WARNING", "warning" }, + { GIMP_MESSAGE_ERROR, "GIMP_MESSAGE_ERROR", "error" }, + { GIMP_MESSAGE_BUG_WARNING, "GIMP_MESSAGE_BUG_WARNING", "bug-warning" }, + { GIMP_MESSAGE_BUG_CRITICAL, "GIMP_MESSAGE_BUG_CRITICAL", "bug-critical" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_MESSAGE_INFO, NC_("message-severity", "Message"), NULL }, + { GIMP_MESSAGE_WARNING, NC_("message-severity", "Warning"), NULL }, + { GIMP_MESSAGE_ERROR, NC_("message-severity", "Error"), NULL }, + { GIMP_MESSAGE_BUG_WARNING, NC_("message-severity", "WARNING"), NULL }, + { GIMP_MESSAGE_BUG_CRITICAL, NC_("message-severity", "CRITICAL"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpMessageSeverity", values); + gimp_type_set_translation_context (type, "message-severity"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_paste_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_PASTE_TYPE_FLOATING, "GIMP_PASTE_TYPE_FLOATING", "floating" }, + { GIMP_PASTE_TYPE_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_FLOATING_IN_PLACE", "floating-in-place" }, + { GIMP_PASTE_TYPE_FLOATING_INTO, "GIMP_PASTE_TYPE_FLOATING_INTO", "floating-into" }, + { GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE, "GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE", "floating-into-in-place" }, + { GIMP_PASTE_TYPE_NEW_LAYER, "GIMP_PASTE_TYPE_NEW_LAYER", "new-layer" }, + { GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE", "new-layer-in-place" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_PASTE_TYPE_FLOATING, "GIMP_PASTE_TYPE_FLOATING", NULL }, + { GIMP_PASTE_TYPE_FLOATING_IN_PLACE, "GIMP_PASTE_TYPE_FLOATING_IN_PLACE", NULL }, + { GIMP_PASTE_TYPE_FLOATING_INTO, "GIMP_PASTE_TYPE_FLOATING_INTO", NULL }, + { GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE, "GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE", NULL }, + { GIMP_PASTE_TYPE_NEW_LAYER, "GIMP_PASTE_TYPE_NEW_LAYER", NULL }, + { GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE, "GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpPasteType", values); + gimp_type_set_translation_context (type, "paste-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_thumbnail_size_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_THUMBNAIL_SIZE_NONE, "GIMP_THUMBNAIL_SIZE_NONE", "none" }, + { GIMP_THUMBNAIL_SIZE_NORMAL, "GIMP_THUMBNAIL_SIZE_NORMAL", "normal" }, + { GIMP_THUMBNAIL_SIZE_LARGE, "GIMP_THUMBNAIL_SIZE_LARGE", "large" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_THUMBNAIL_SIZE_NONE, NC_("thumbnail-size", "No thumbnails"), NULL }, + { GIMP_THUMBNAIL_SIZE_NORMAL, NC_("thumbnail-size", "Normal (128x128)"), NULL }, + { GIMP_THUMBNAIL_SIZE_LARGE, NC_("thumbnail-size", "Large (256x256)"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpThumbnailSize", values); + gimp_type_set_translation_context (type, "thumbnail-size"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_undo_event_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_UNDO_EVENT_UNDO_PUSHED, "GIMP_UNDO_EVENT_UNDO_PUSHED", "undo-pushed" }, + { GIMP_UNDO_EVENT_UNDO_EXPIRED, "GIMP_UNDO_EVENT_UNDO_EXPIRED", "undo-expired" }, + { GIMP_UNDO_EVENT_REDO_EXPIRED, "GIMP_UNDO_EVENT_REDO_EXPIRED", "redo-expired" }, + { GIMP_UNDO_EVENT_UNDO, "GIMP_UNDO_EVENT_UNDO", "undo" }, + { GIMP_UNDO_EVENT_REDO, "GIMP_UNDO_EVENT_REDO", "redo" }, + { GIMP_UNDO_EVENT_UNDO_FREE, "GIMP_UNDO_EVENT_UNDO_FREE", "undo-free" }, + { GIMP_UNDO_EVENT_UNDO_FREEZE, "GIMP_UNDO_EVENT_UNDO_FREEZE", "undo-freeze" }, + { GIMP_UNDO_EVENT_UNDO_THAW, "GIMP_UNDO_EVENT_UNDO_THAW", "undo-thaw" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_UNDO_EVENT_UNDO_PUSHED, "GIMP_UNDO_EVENT_UNDO_PUSHED", NULL }, + { GIMP_UNDO_EVENT_UNDO_EXPIRED, "GIMP_UNDO_EVENT_UNDO_EXPIRED", NULL }, + { GIMP_UNDO_EVENT_REDO_EXPIRED, "GIMP_UNDO_EVENT_REDO_EXPIRED", NULL }, + { GIMP_UNDO_EVENT_UNDO, "GIMP_UNDO_EVENT_UNDO", NULL }, + { GIMP_UNDO_EVENT_REDO, "GIMP_UNDO_EVENT_REDO", NULL }, + { GIMP_UNDO_EVENT_UNDO_FREE, "GIMP_UNDO_EVENT_UNDO_FREE", NULL }, + { GIMP_UNDO_EVENT_UNDO_FREEZE, "GIMP_UNDO_EVENT_UNDO_FREEZE", NULL }, + { GIMP_UNDO_EVENT_UNDO_THAW, "GIMP_UNDO_EVENT_UNDO_THAW", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpUndoEvent", values); + gimp_type_set_translation_context (type, "undo-event"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_undo_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_UNDO_MODE_UNDO, "GIMP_UNDO_MODE_UNDO", "undo" }, + { GIMP_UNDO_MODE_REDO, "GIMP_UNDO_MODE_REDO", "redo" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_UNDO_MODE_UNDO, "GIMP_UNDO_MODE_UNDO", NULL }, + { GIMP_UNDO_MODE_REDO, "GIMP_UNDO_MODE_REDO", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpUndoMode", values); + gimp_type_set_translation_context (type, "undo-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_undo_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_UNDO_GROUP_NONE, "GIMP_UNDO_GROUP_NONE", "group-none" }, + { GIMP_UNDO_GROUP_IMAGE_SCALE, "GIMP_UNDO_GROUP_IMAGE_SCALE", "group-image-scale" }, + { GIMP_UNDO_GROUP_IMAGE_RESIZE, "GIMP_UNDO_GROUP_IMAGE_RESIZE", "group-image-resize" }, + { GIMP_UNDO_GROUP_IMAGE_FLIP, "GIMP_UNDO_GROUP_IMAGE_FLIP", "group-image-flip" }, + { GIMP_UNDO_GROUP_IMAGE_ROTATE, "GIMP_UNDO_GROUP_IMAGE_ROTATE", "group-image-rotate" }, + { GIMP_UNDO_GROUP_IMAGE_TRANSFORM, "GIMP_UNDO_GROUP_IMAGE_TRANSFORM", "group-image-transform" }, + { GIMP_UNDO_GROUP_IMAGE_CROP, "GIMP_UNDO_GROUP_IMAGE_CROP", "group-image-crop" }, + { GIMP_UNDO_GROUP_IMAGE_CONVERT, "GIMP_UNDO_GROUP_IMAGE_CONVERT", "group-image-convert" }, + { GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, "GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE", "group-image-item-remove" }, + { GIMP_UNDO_GROUP_IMAGE_ITEM_REORDER, "GIMP_UNDO_GROUP_IMAGE_ITEM_REORDER", "group-image-item-reorder" }, + { GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, "GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE", "group-image-layers-merge" }, + { GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, "GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE", "group-image-vectors-merge" }, + { GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, "GIMP_UNDO_GROUP_IMAGE_QUICK_MASK", "group-image-quick-mask" }, + { GIMP_UNDO_GROUP_IMAGE_GRID, "GIMP_UNDO_GROUP_IMAGE_GRID", "group-image-grid" }, + { GIMP_UNDO_GROUP_GUIDE, "GIMP_UNDO_GROUP_GUIDE", "group-guide" }, + { GIMP_UNDO_GROUP_SAMPLE_POINT, "GIMP_UNDO_GROUP_SAMPLE_POINT", "group-sample-point" }, + { GIMP_UNDO_GROUP_DRAWABLE, "GIMP_UNDO_GROUP_DRAWABLE", "group-drawable" }, + { GIMP_UNDO_GROUP_DRAWABLE_MOD, "GIMP_UNDO_GROUP_DRAWABLE_MOD", "group-drawable-mod" }, + { GIMP_UNDO_GROUP_MASK, "GIMP_UNDO_GROUP_MASK", "group-mask" }, + { GIMP_UNDO_GROUP_ITEM_VISIBILITY, "GIMP_UNDO_GROUP_ITEM_VISIBILITY", "group-item-visibility" }, + { GIMP_UNDO_GROUP_ITEM_LINKED, "GIMP_UNDO_GROUP_ITEM_LINKED", "group-item-linked" }, + { GIMP_UNDO_GROUP_ITEM_PROPERTIES, "GIMP_UNDO_GROUP_ITEM_PROPERTIES", "group-item-properties" }, + { GIMP_UNDO_GROUP_ITEM_DISPLACE, "GIMP_UNDO_GROUP_ITEM_DISPLACE", "group-item-displace" }, + { GIMP_UNDO_GROUP_ITEM_SCALE, "GIMP_UNDO_GROUP_ITEM_SCALE", "group-item-scale" }, + { GIMP_UNDO_GROUP_ITEM_RESIZE, "GIMP_UNDO_GROUP_ITEM_RESIZE", "group-item-resize" }, + { GIMP_UNDO_GROUP_LAYER_ADD, "GIMP_UNDO_GROUP_LAYER_ADD", "group-layer-add" }, + { GIMP_UNDO_GROUP_LAYER_ADD_MASK, "GIMP_UNDO_GROUP_LAYER_ADD_MASK", "group-layer-add-mask" }, + { GIMP_UNDO_GROUP_LAYER_APPLY_MASK, "GIMP_UNDO_GROUP_LAYER_APPLY_MASK", "group-layer-apply-mask" }, + { GIMP_UNDO_GROUP_FS_TO_LAYER, "GIMP_UNDO_GROUP_FS_TO_LAYER", "group-fs-to-layer" }, + { GIMP_UNDO_GROUP_FS_FLOAT, "GIMP_UNDO_GROUP_FS_FLOAT", "group-fs-float" }, + { GIMP_UNDO_GROUP_FS_ANCHOR, "GIMP_UNDO_GROUP_FS_ANCHOR", "group-fs-anchor" }, + { GIMP_UNDO_GROUP_EDIT_PASTE, "GIMP_UNDO_GROUP_EDIT_PASTE", "group-edit-paste" }, + { GIMP_UNDO_GROUP_EDIT_CUT, "GIMP_UNDO_GROUP_EDIT_CUT", "group-edit-cut" }, + { GIMP_UNDO_GROUP_TEXT, "GIMP_UNDO_GROUP_TEXT", "group-text" }, + { GIMP_UNDO_GROUP_TRANSFORM, "GIMP_UNDO_GROUP_TRANSFORM", "group-transform" }, + { GIMP_UNDO_GROUP_PAINT, "GIMP_UNDO_GROUP_PAINT", "group-paint" }, + { GIMP_UNDO_GROUP_PARASITE_ATTACH, "GIMP_UNDO_GROUP_PARASITE_ATTACH", "group-parasite-attach" }, + { GIMP_UNDO_GROUP_PARASITE_REMOVE, "GIMP_UNDO_GROUP_PARASITE_REMOVE", "group-parasite-remove" }, + { GIMP_UNDO_GROUP_VECTORS_IMPORT, "GIMP_UNDO_GROUP_VECTORS_IMPORT", "group-vectors-import" }, + { GIMP_UNDO_GROUP_MISC, "GIMP_UNDO_GROUP_MISC", "group-misc" }, + { GIMP_UNDO_IMAGE_TYPE, "GIMP_UNDO_IMAGE_TYPE", "image-type" }, + { GIMP_UNDO_IMAGE_PRECISION, "GIMP_UNDO_IMAGE_PRECISION", "image-precision" }, + { GIMP_UNDO_IMAGE_SIZE, "GIMP_UNDO_IMAGE_SIZE", "image-size" }, + { GIMP_UNDO_IMAGE_RESOLUTION, "GIMP_UNDO_IMAGE_RESOLUTION", "image-resolution" }, + { GIMP_UNDO_IMAGE_GRID, "GIMP_UNDO_IMAGE_GRID", "image-grid" }, + { GIMP_UNDO_IMAGE_METADATA, "GIMP_UNDO_IMAGE_METADATA", "image-metadata" }, + { GIMP_UNDO_IMAGE_COLORMAP, "GIMP_UNDO_IMAGE_COLORMAP", "image-colormap" }, + { GIMP_UNDO_IMAGE_COLOR_MANAGED, "GIMP_UNDO_IMAGE_COLOR_MANAGED", "image-color-managed" }, + { GIMP_UNDO_GUIDE, "GIMP_UNDO_GUIDE", "guide" }, + { GIMP_UNDO_SAMPLE_POINT, "GIMP_UNDO_SAMPLE_POINT", "sample-point" }, + { GIMP_UNDO_DRAWABLE, "GIMP_UNDO_DRAWABLE", "drawable" }, + { GIMP_UNDO_DRAWABLE_MOD, "GIMP_UNDO_DRAWABLE_MOD", "drawable-mod" }, + { GIMP_UNDO_MASK, "GIMP_UNDO_MASK", "mask" }, + { GIMP_UNDO_ITEM_REORDER, "GIMP_UNDO_ITEM_REORDER", "item-reorder" }, + { GIMP_UNDO_ITEM_RENAME, "GIMP_UNDO_ITEM_RENAME", "item-rename" }, + { GIMP_UNDO_ITEM_DISPLACE, "GIMP_UNDO_ITEM_DISPLACE", "item-displace" }, + { GIMP_UNDO_ITEM_VISIBILITY, "GIMP_UNDO_ITEM_VISIBILITY", "item-visibility" }, + { GIMP_UNDO_ITEM_LINKED, "GIMP_UNDO_ITEM_LINKED", "item-linked" }, + { GIMP_UNDO_ITEM_COLOR_TAG, "GIMP_UNDO_ITEM_COLOR_TAG", "item-color-tag" }, + { GIMP_UNDO_ITEM_LOCK_CONTENT, "GIMP_UNDO_ITEM_LOCK_CONTENT", "item-lock-content" }, + { GIMP_UNDO_ITEM_LOCK_POSITION, "GIMP_UNDO_ITEM_LOCK_POSITION", "item-lock-position" }, + { GIMP_UNDO_LAYER_ADD, "GIMP_UNDO_LAYER_ADD", "layer-add" }, + { GIMP_UNDO_LAYER_REMOVE, "GIMP_UNDO_LAYER_REMOVE", "layer-remove" }, + { GIMP_UNDO_LAYER_MODE, "GIMP_UNDO_LAYER_MODE", "layer-mode" }, + { GIMP_UNDO_LAYER_OPACITY, "GIMP_UNDO_LAYER_OPACITY", "layer-opacity" }, + { GIMP_UNDO_LAYER_LOCK_ALPHA, "GIMP_UNDO_LAYER_LOCK_ALPHA", "layer-lock-alpha" }, + { GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, "GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE", "group-layer-suspend-resize" }, + { GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, "GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE", "group-layer-resume-resize" }, + { GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, "GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK", "group-layer-suspend-mask" }, + { GIMP_UNDO_GROUP_LAYER_RESUME_MASK, "GIMP_UNDO_GROUP_LAYER_RESUME_MASK", "group-layer-resume-mask" }, + { GIMP_UNDO_GROUP_LAYER_START_TRANSFORM, "GIMP_UNDO_GROUP_LAYER_START_TRANSFORM", "group-layer-start-transform" }, + { GIMP_UNDO_GROUP_LAYER_END_TRANSFORM, "GIMP_UNDO_GROUP_LAYER_END_TRANSFORM", "group-layer-end-transform" }, + { GIMP_UNDO_GROUP_LAYER_CONVERT, "GIMP_UNDO_GROUP_LAYER_CONVERT", "group-layer-convert" }, + { GIMP_UNDO_TEXT_LAYER, "GIMP_UNDO_TEXT_LAYER", "text-layer" }, + { GIMP_UNDO_TEXT_LAYER_MODIFIED, "GIMP_UNDO_TEXT_LAYER_MODIFIED", "text-layer-modified" }, + { GIMP_UNDO_TEXT_LAYER_CONVERT, "GIMP_UNDO_TEXT_LAYER_CONVERT", "text-layer-convert" }, + { GIMP_UNDO_LAYER_MASK_ADD, "GIMP_UNDO_LAYER_MASK_ADD", "layer-mask-add" }, + { GIMP_UNDO_LAYER_MASK_REMOVE, "GIMP_UNDO_LAYER_MASK_REMOVE", "layer-mask-remove" }, + { GIMP_UNDO_LAYER_MASK_APPLY, "GIMP_UNDO_LAYER_MASK_APPLY", "layer-mask-apply" }, + { GIMP_UNDO_LAYER_MASK_SHOW, "GIMP_UNDO_LAYER_MASK_SHOW", "layer-mask-show" }, + { GIMP_UNDO_CHANNEL_ADD, "GIMP_UNDO_CHANNEL_ADD", "channel-add" }, + { GIMP_UNDO_CHANNEL_REMOVE, "GIMP_UNDO_CHANNEL_REMOVE", "channel-remove" }, + { GIMP_UNDO_CHANNEL_COLOR, "GIMP_UNDO_CHANNEL_COLOR", "channel-color" }, + { GIMP_UNDO_VECTORS_ADD, "GIMP_UNDO_VECTORS_ADD", "vectors-add" }, + { GIMP_UNDO_VECTORS_REMOVE, "GIMP_UNDO_VECTORS_REMOVE", "vectors-remove" }, + { GIMP_UNDO_VECTORS_MOD, "GIMP_UNDO_VECTORS_MOD", "vectors-mod" }, + { GIMP_UNDO_FS_TO_LAYER, "GIMP_UNDO_FS_TO_LAYER", "fs-to-layer" }, + { GIMP_UNDO_TRANSFORM_GRID, "GIMP_UNDO_TRANSFORM_GRID", "transform-grid" }, + { GIMP_UNDO_PAINT, "GIMP_UNDO_PAINT", "paint" }, + { GIMP_UNDO_INK, "GIMP_UNDO_INK", "ink" }, + { GIMP_UNDO_FOREGROUND_SELECT, "GIMP_UNDO_FOREGROUND_SELECT", "foreground-select" }, + { GIMP_UNDO_PARASITE_ATTACH, "GIMP_UNDO_PARASITE_ATTACH", "parasite-attach" }, + { GIMP_UNDO_PARASITE_REMOVE, "GIMP_UNDO_PARASITE_REMOVE", "parasite-remove" }, + { GIMP_UNDO_CANT, "GIMP_UNDO_CANT", "cant" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_UNDO_GROUP_NONE, NC_("undo-type", "<>"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_SCALE, NC_("undo-type", "Scale image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_RESIZE, NC_("undo-type", "Resize image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_FLIP, NC_("undo-type", "Flip image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_ROTATE, NC_("undo-type", "Rotate image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_TRANSFORM, NC_("undo-type", "Transform image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_CROP, NC_("undo-type", "Crop image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_CONVERT, NC_("undo-type", "Convert image"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, NC_("undo-type", "Remove item"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_ITEM_REORDER, NC_("undo-type", "Reorder item"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, NC_("undo-type", "Merge layers"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, NC_("undo-type", "Merge paths"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, NC_("undo-type", "Quick Mask"), NULL }, + { GIMP_UNDO_GROUP_IMAGE_GRID, NC_("undo-type", "Grid"), NULL }, + { GIMP_UNDO_GROUP_GUIDE, NC_("undo-type", "Guide"), NULL }, + { GIMP_UNDO_GROUP_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL }, + { GIMP_UNDO_GROUP_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL }, + { GIMP_UNDO_GROUP_DRAWABLE_MOD, NC_("undo-type", "Layer/Channel modification"), NULL }, + { GIMP_UNDO_GROUP_MASK, NC_("undo-type", "Selection mask"), NULL }, + { GIMP_UNDO_GROUP_ITEM_VISIBILITY, NC_("undo-type", "Item visibility"), NULL }, + { GIMP_UNDO_GROUP_ITEM_LINKED, NC_("undo-type", "Link/Unlink item"), NULL }, + { GIMP_UNDO_GROUP_ITEM_PROPERTIES, NC_("undo-type", "Item properties"), NULL }, + { GIMP_UNDO_GROUP_ITEM_DISPLACE, NC_("undo-type", "Move item"), NULL }, + { GIMP_UNDO_GROUP_ITEM_SCALE, NC_("undo-type", "Scale item"), NULL }, + { GIMP_UNDO_GROUP_ITEM_RESIZE, NC_("undo-type", "Resize item"), NULL }, + { GIMP_UNDO_GROUP_LAYER_ADD, NC_("undo-type", "Add layer"), NULL }, + { GIMP_UNDO_GROUP_LAYER_ADD_MASK, NC_("undo-type", "Add layer mask"), NULL }, + { GIMP_UNDO_GROUP_LAYER_APPLY_MASK, NC_("undo-type", "Apply layer mask"), NULL }, + { GIMP_UNDO_GROUP_FS_TO_LAYER, NC_("undo-type", "Floating selection to layer"), NULL }, + { GIMP_UNDO_GROUP_FS_FLOAT, NC_("undo-type", "Float selection"), NULL }, + { GIMP_UNDO_GROUP_FS_ANCHOR, NC_("undo-type", "Anchor floating selection"), NULL }, + { GIMP_UNDO_GROUP_EDIT_PASTE, NC_("undo-type", "Paste"), NULL }, + { GIMP_UNDO_GROUP_EDIT_CUT, NC_("undo-type", "Cut"), NULL }, + { GIMP_UNDO_GROUP_TEXT, NC_("undo-type", "Text"), NULL }, + { GIMP_UNDO_GROUP_TRANSFORM, NC_("undo-type", "Transform"), NULL }, + { GIMP_UNDO_GROUP_PAINT, NC_("undo-type", "Paint"), NULL }, + { GIMP_UNDO_GROUP_PARASITE_ATTACH, NC_("undo-type", "Attach parasite"), NULL }, + { GIMP_UNDO_GROUP_PARASITE_REMOVE, NC_("undo-type", "Remove parasite"), NULL }, + { GIMP_UNDO_GROUP_VECTORS_IMPORT, NC_("undo-type", "Import paths"), NULL }, + { GIMP_UNDO_GROUP_MISC, NC_("undo-type", "Plug-In"), NULL }, + { GIMP_UNDO_IMAGE_TYPE, NC_("undo-type", "Image type"), NULL }, + { GIMP_UNDO_IMAGE_PRECISION, NC_("undo-type", "Image precision"), NULL }, + { GIMP_UNDO_IMAGE_SIZE, NC_("undo-type", "Image size"), NULL }, + { GIMP_UNDO_IMAGE_RESOLUTION, NC_("undo-type", "Image resolution change"), NULL }, + { GIMP_UNDO_IMAGE_GRID, NC_("undo-type", "Grid"), NULL }, + { GIMP_UNDO_IMAGE_METADATA, NC_("undo-type", "Change metadata"), NULL }, + { GIMP_UNDO_IMAGE_COLORMAP, NC_("undo-type", "Change indexed palette"), NULL }, + { GIMP_UNDO_IMAGE_COLOR_MANAGED, NC_("undo-type", "Change color managed state"), NULL }, + { GIMP_UNDO_GUIDE, NC_("undo-type", "Guide"), NULL }, + { GIMP_UNDO_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL }, + { GIMP_UNDO_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL }, + { GIMP_UNDO_DRAWABLE_MOD, NC_("undo-type", "Layer/Channel modification"), NULL }, + { GIMP_UNDO_MASK, NC_("undo-type", "Selection mask"), NULL }, + { GIMP_UNDO_ITEM_REORDER, NC_("undo-type", "Reorder item"), NULL }, + { GIMP_UNDO_ITEM_RENAME, NC_("undo-type", "Rename item"), NULL }, + { GIMP_UNDO_ITEM_DISPLACE, NC_("undo-type", "Move item"), NULL }, + { GIMP_UNDO_ITEM_VISIBILITY, NC_("undo-type", "Item visibility"), NULL }, + { GIMP_UNDO_ITEM_LINKED, NC_("undo-type", "Link/Unlink item"), NULL }, + { GIMP_UNDO_ITEM_COLOR_TAG, NC_("undo-type", "Item color tag"), NULL }, + { GIMP_UNDO_ITEM_LOCK_CONTENT, NC_("undo-type", "Lock/Unlock content"), NULL }, + { GIMP_UNDO_ITEM_LOCK_POSITION, NC_("undo-type", "Lock/Unlock position"), NULL }, + { GIMP_UNDO_LAYER_ADD, NC_("undo-type", "New layer"), NULL }, + { GIMP_UNDO_LAYER_REMOVE, NC_("undo-type", "Delete layer"), NULL }, + { GIMP_UNDO_LAYER_MODE, NC_("undo-type", "Set layer mode"), NULL }, + { GIMP_UNDO_LAYER_OPACITY, NC_("undo-type", "Set layer opacity"), NULL }, + { GIMP_UNDO_LAYER_LOCK_ALPHA, NC_("undo-type", "Lock/Unlock alpha channel"), NULL }, + { GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, NC_("undo-type", "Suspend group layer resize"), NULL }, + { GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, NC_("undo-type", "Resume group layer resize"), NULL }, + { GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, NC_("undo-type", "Suspend group layer mask"), NULL }, + { GIMP_UNDO_GROUP_LAYER_RESUME_MASK, NC_("undo-type", "Resume group layer mask"), NULL }, + { GIMP_UNDO_GROUP_LAYER_START_TRANSFORM, NC_("undo-type", "Start transforming group layer"), NULL }, + { GIMP_UNDO_GROUP_LAYER_END_TRANSFORM, NC_("undo-type", "End transforming group layer"), NULL }, + { GIMP_UNDO_GROUP_LAYER_CONVERT, NC_("undo-type", "Convert group layer"), NULL }, + { GIMP_UNDO_TEXT_LAYER, NC_("undo-type", "Text layer"), NULL }, + { GIMP_UNDO_TEXT_LAYER_MODIFIED, NC_("undo-type", "Text layer modification"), NULL }, + { GIMP_UNDO_TEXT_LAYER_CONVERT, NC_("undo-type", "Convert text layer"), NULL }, + { GIMP_UNDO_LAYER_MASK_ADD, NC_("undo-type", "Add layer mask"), NULL }, + { GIMP_UNDO_LAYER_MASK_REMOVE, NC_("undo-type", "Delete layer mask"), NULL }, + { GIMP_UNDO_LAYER_MASK_APPLY, NC_("undo-type", "Apply layer mask"), NULL }, + { GIMP_UNDO_LAYER_MASK_SHOW, NC_("undo-type", "Show layer mask"), NULL }, + { GIMP_UNDO_CHANNEL_ADD, NC_("undo-type", "New channel"), NULL }, + { GIMP_UNDO_CHANNEL_REMOVE, NC_("undo-type", "Delete channel"), NULL }, + { GIMP_UNDO_CHANNEL_COLOR, NC_("undo-type", "Channel color"), NULL }, + { GIMP_UNDO_VECTORS_ADD, NC_("undo-type", "New path"), NULL }, + { GIMP_UNDO_VECTORS_REMOVE, NC_("undo-type", "Delete path"), NULL }, + { GIMP_UNDO_VECTORS_MOD, NC_("undo-type", "Path modification"), NULL }, + { GIMP_UNDO_FS_TO_LAYER, NC_("undo-type", "Floating selection to layer"), NULL }, + { GIMP_UNDO_TRANSFORM_GRID, NC_("undo-type", "Transform grid"), NULL }, + { GIMP_UNDO_PAINT, NC_("undo-type", "Paint"), NULL }, + { GIMP_UNDO_INK, NC_("undo-type", "Ink"), NULL }, + { GIMP_UNDO_FOREGROUND_SELECT, NC_("undo-type", "Select foreground"), NULL }, + { GIMP_UNDO_PARASITE_ATTACH, NC_("undo-type", "Attach parasite"), NULL }, + { GIMP_UNDO_PARASITE_REMOVE, NC_("undo-type", "Remove parasite"), NULL }, + { GIMP_UNDO_CANT, NC_("undo-type", "Not undoable"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpUndoType", values); + gimp_type_set_translation_context (type, "undo-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_view_size_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_VIEW_SIZE_TINY, "GIMP_VIEW_SIZE_TINY", "tiny" }, + { GIMP_VIEW_SIZE_EXTRA_SMALL, "GIMP_VIEW_SIZE_EXTRA_SMALL", "extra-small" }, + { GIMP_VIEW_SIZE_SMALL, "GIMP_VIEW_SIZE_SMALL", "small" }, + { GIMP_VIEW_SIZE_MEDIUM, "GIMP_VIEW_SIZE_MEDIUM", "medium" }, + { GIMP_VIEW_SIZE_LARGE, "GIMP_VIEW_SIZE_LARGE", "large" }, + { GIMP_VIEW_SIZE_EXTRA_LARGE, "GIMP_VIEW_SIZE_EXTRA_LARGE", "extra-large" }, + { GIMP_VIEW_SIZE_HUGE, "GIMP_VIEW_SIZE_HUGE", "huge" }, + { GIMP_VIEW_SIZE_ENORMOUS, "GIMP_VIEW_SIZE_ENORMOUS", "enormous" }, + { GIMP_VIEW_SIZE_GIGANTIC, "GIMP_VIEW_SIZE_GIGANTIC", "gigantic" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_VIEW_SIZE_TINY, NC_("view-size", "Tiny"), NULL }, + { GIMP_VIEW_SIZE_EXTRA_SMALL, NC_("view-size", "Very small"), NULL }, + { GIMP_VIEW_SIZE_SMALL, NC_("view-size", "Small"), NULL }, + { GIMP_VIEW_SIZE_MEDIUM, NC_("view-size", "Medium"), NULL }, + { GIMP_VIEW_SIZE_LARGE, NC_("view-size", "Large"), NULL }, + { GIMP_VIEW_SIZE_EXTRA_LARGE, NC_("view-size", "Very large"), NULL }, + { GIMP_VIEW_SIZE_HUGE, NC_("view-size", "Huge"), NULL }, + { GIMP_VIEW_SIZE_ENORMOUS, NC_("view-size", "Enormous"), NULL }, + { GIMP_VIEW_SIZE_GIGANTIC, NC_("view-size", "Gigantic"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpViewSize", values); + gimp_type_set_translation_context (type, "view-size"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_view_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_VIEW_TYPE_LIST, "GIMP_VIEW_TYPE_LIST", "list" }, + { GIMP_VIEW_TYPE_GRID, "GIMP_VIEW_TYPE_GRID", "grid" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_VIEW_TYPE_LIST, NC_("view-type", "View as list"), NULL }, + { GIMP_VIEW_TYPE_GRID, NC_("view-type", "View as grid"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpViewType", values); + gimp_type_set_translation_context (type, "view-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + + +/* Generated data ends here */ + diff --git a/app/core/core-enums.h b/app/core/core-enums.h new file mode 100644 index 0000000..8826145 --- /dev/null +++ b/app/core/core-enums.h @@ -0,0 +1,701 @@ +/* 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 . + */ + +#ifndef __CORE_ENUMS_H__ +#define __CORE_ENUMS_H__ + + +#if 0 + This file is parsed by two scripts, enumgen.pl in pdb, + and gimp-mkenums. All enums that are not marked with + /*< pdb-skip >*/ are exported to libgimp and the PDB. Enums that are + not marked with /*< skip >*/ are registered with the GType system. + If you want the enum to be skipped by both scripts, you have to use + /*< pdb-skip, skip >*/. + + The same syntax applies to enum values. +#endif + + +/* + * these enums are registered with the type system + */ + + +#define GIMP_TYPE_ALIGN_REFERENCE_TYPE (gimp_align_reference_type_get_type ()) + +GType gimp_align_reference_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_ALIGN_REFERENCE_FIRST, /*< desc="First item" >*/ + GIMP_ALIGN_REFERENCE_IMAGE, /*< desc="Image" >*/ + GIMP_ALIGN_REFERENCE_SELECTION, /*< desc="Selection" >*/ + GIMP_ALIGN_REFERENCE_ACTIVE_LAYER, /*< desc="Active layer" >*/ + GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL, /*< desc="Active channel" >*/ + GIMP_ALIGN_REFERENCE_ACTIVE_PATH /*< desc="Active path" >*/ +} GimpAlignReferenceType; + + +#define GIMP_TYPE_ALIGNMENT_TYPE (gimp_alignment_type_get_type ()) + +GType gimp_alignment_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_ALIGN_LEFT, + GIMP_ALIGN_HCENTER, + GIMP_ALIGN_RIGHT, + GIMP_ALIGN_TOP, + GIMP_ALIGN_VCENTER, + GIMP_ALIGN_BOTTOM, + GIMP_ARRANGE_LEFT, + GIMP_ARRANGE_HCENTER, + GIMP_ARRANGE_RIGHT, + GIMP_ARRANGE_TOP, + GIMP_ARRANGE_VCENTER, + GIMP_ARRANGE_BOTTOM, + GIMP_ARRANGE_HFILL, + GIMP_ARRANGE_VFILL +} GimpAlignmentType; + + +#define GIMP_TYPE_CHANNEL_BORDER_STYLE (gimp_channel_border_style_get_type ()) + +GType gimp_channel_border_style_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_CHANNEL_BORDER_STYLE_HARD, /*< desc="Hard" >*/ + GIMP_CHANNEL_BORDER_STYLE_SMOOTH, /*< desc="Smooth" >*/ + GIMP_CHANNEL_BORDER_STYLE_FEATHERED /*< desc="Feathered" >*/ +} GimpChannelBorderStyle; + + +/* Note: when appending values here, don't forget to update + * GimpColorFrame and other places use this enum to create combo + * boxes. + */ +#define GIMP_TYPE_COLOR_PICK_MODE (gimp_color_pick_mode_get_type ()) + +GType gimp_color_pick_mode_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_COLOR_PICK_MODE_PIXEL, /*< desc="Pixel" >*/ + GIMP_COLOR_PICK_MODE_RGB_PERCENT, /*< desc="RGB (%)" >*/ + GIMP_COLOR_PICK_MODE_RGB_U8, /*< desc="RGB (0..255)" >*/ + GIMP_COLOR_PICK_MODE_HSV, /*< desc="HSV" >*/ + GIMP_COLOR_PICK_MODE_LCH, /*< desc="CIE LCh" >*/ + GIMP_COLOR_PICK_MODE_LAB, /*< desc="CIE LAB" >*/ + GIMP_COLOR_PICK_MODE_CMYK, /*< desc="CMYK" >*/ + GIMP_COLOR_PICK_MODE_XYY, /*< desc="CIE xyY" >*/ + GIMP_COLOR_PICK_MODE_YUV, /*< desc="CIE Yu'v'" >*/ + + GIMP_COLOR_PICK_MODE_LAST = GIMP_COLOR_PICK_MODE_YUV /*< skip >*/ +} GimpColorPickMode; + + +#define GIMP_TYPE_COLOR_PROFILE_POLICY (gimp_color_profile_policy_get_type ()) + +GType gimp_color_profile_policy_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_COLOR_PROFILE_POLICY_ASK, /*< desc="Ask what to do" >*/ + GIMP_COLOR_PROFILE_POLICY_KEEP, /*< desc="Keep embedded profile" >*/ + GIMP_COLOR_PROFILE_POLICY_CONVERT /*< desc="Convert to built-in sRGB or grayscale profile" >*/ +} GimpColorProfilePolicy; + + +#define GIMP_TYPE_COMPONENT_MASK (gimp_component_mask_get_type ()) + +GType gimp_component_mask_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_COMPONENT_MASK_RED = 1 << 0, + GIMP_COMPONENT_MASK_GREEN = 1 << 1, + GIMP_COMPONENT_MASK_BLUE = 1 << 2, + GIMP_COMPONENT_MASK_ALPHA = 1 << 3, + + GIMP_COMPONENT_MASK_ALL = (GIMP_COMPONENT_MASK_RED | + GIMP_COMPONENT_MASK_GREEN | + GIMP_COMPONENT_MASK_BLUE | + GIMP_COMPONENT_MASK_ALPHA) +} GimpComponentMask; + + +#define GIMP_TYPE_CONTAINER_POLICY (gimp_container_policy_get_type ()) + +GType gimp_container_policy_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_CONTAINER_POLICY_STRONG, + GIMP_CONTAINER_POLICY_WEAK +} GimpContainerPolicy; + + +#define GIMP_TYPE_CONVERT_DITHER_TYPE (gimp_convert_dither_type_get_type ()) + +GType gimp_convert_dither_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CONVERT_DITHER_NONE, /*< desc="None" >*/ + GIMP_CONVERT_DITHER_FS, /*< desc="Floyd-Steinberg (normal)" >*/ + GIMP_CONVERT_DITHER_FS_LOWBLEED, /*< desc="Floyd-Steinberg (reduced color bleeding)" >*/ + GIMP_CONVERT_DITHER_FIXED, /*< desc="Positioned" >*/ + GIMP_CONVERT_DITHER_NODESTRUCT /*< pdb-skip, skip >*/ +} GimpConvertDitherType; + + +#define GIMP_TYPE_CONVOLUTION_TYPE (gimp_convolution_type_get_type ()) + +GType gimp_convolution_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_NORMAL_CONVOL, /* Negative numbers truncated */ + GIMP_ABSOLUTE_CONVOL, /* Absolute value */ + GIMP_NEGATIVE_CONVOL /* add 127 to values */ +} GimpConvolutionType; + + +#define GIMP_TYPE_CURVE_POINT_TYPE (gimp_curve_point_type_get_type ()) + +GType gimp_curve_point_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_CURVE_POINT_SMOOTH, /*< desc="Smooth" >*/ + GIMP_CURVE_POINT_CORNER /*< desc="Corner" >*/ +} GimpCurvePointType; + + +#define GIMP_TYPE_CURVE_TYPE (gimp_curve_type_get_type ()) + +GType gimp_curve_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_CURVE_SMOOTH, /*< desc="Smooth" >*/ + GIMP_CURVE_FREE /*< desc="Freehand" >*/ +} GimpCurveType; + + +#define GIMP_TYPE_DASH_PRESET (gimp_dash_preset_get_type ()) + +GType gimp_dash_preset_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_DASH_CUSTOM, /*< desc="Custom" >*/ + GIMP_DASH_LINE, /*< desc="Line" >*/ + GIMP_DASH_LONG_DASH, /*< desc="Long dashes" >*/ + GIMP_DASH_MEDIUM_DASH, /*< desc="Medium dashes" >*/ + GIMP_DASH_SHORT_DASH, /*< desc="Short dashes" >*/ + GIMP_DASH_SPARSE_DOTS, /*< desc="Sparse dots" >*/ + GIMP_DASH_NORMAL_DOTS, /*< desc="Normal dots" >*/ + GIMP_DASH_DENSE_DOTS, /*< desc="Dense dots" >*/ + GIMP_DASH_STIPPLES, /*< desc="Stipples" >*/ + GIMP_DASH_DASH_DOT, /*< desc="Dash, dot" >*/ + GIMP_DASH_DASH_DOT_DOT /*< desc="Dash, dot, dot" >*/ +} GimpDashPreset; + + +#define GIMP_TYPE_DEBUG_POLICY (gimp_debug_policy_get_type ()) + +GType gimp_debug_policy_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_DEBUG_POLICY_WARNING, /*< desc="Debug warnings, critical errors and crashes" >*/ + GIMP_DEBUG_POLICY_CRITICAL, /*< desc="Debug critical errors and crashes" >*/ + GIMP_DEBUG_POLICY_FATAL, /*< desc="Debug crashes only" >*/ + GIMP_DEBUG_POLICY_NEVER /*< desc="Never debug GIMP" >*/ +} GimpDebugPolicy; + + +#define GIMP_TYPE_DIRTY_MASK (gimp_dirty_mask_get_type ()) + +GType gimp_dirty_mask_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_DIRTY_NONE = 0, + + GIMP_DIRTY_IMAGE = 1 << 0, + GIMP_DIRTY_IMAGE_SIZE = 1 << 1, + GIMP_DIRTY_IMAGE_META = 1 << 2, + GIMP_DIRTY_IMAGE_STRUCTURE = 1 << 3, + GIMP_DIRTY_ITEM = 1 << 4, + GIMP_DIRTY_ITEM_META = 1 << 5, + GIMP_DIRTY_DRAWABLE = 1 << 6, + GIMP_DIRTY_VECTORS = 1 << 7, + GIMP_DIRTY_SELECTION = 1 << 8, + GIMP_DIRTY_ACTIVE_DRAWABLE = 1 << 9, + + GIMP_DIRTY_ALL = 0xffff +} GimpDirtyMask; + + +#define GIMP_TYPE_DYNAMICS_OUTPUT_TYPE (gimp_dynamics_output_type_get_type ()) + +GType gimp_dynamics_output_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_DYNAMICS_OUTPUT_OPACITY, /*< desc="Opacity" >*/ + GIMP_DYNAMICS_OUTPUT_SIZE, /*< desc="Size" >*/ + GIMP_DYNAMICS_OUTPUT_ANGLE, /*< desc="Angle" >*/ + GIMP_DYNAMICS_OUTPUT_COLOR, /*< desc="Color" >*/ + GIMP_DYNAMICS_OUTPUT_HARDNESS, /*< desc="Hardness" >*/ + GIMP_DYNAMICS_OUTPUT_FORCE, /*< desc="Force" >*/ + GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO, /*< desc="Aspect ratio" >*/ + GIMP_DYNAMICS_OUTPUT_SPACING, /*< desc="Spacing" >*/ + GIMP_DYNAMICS_OUTPUT_RATE, /*< desc="Rate" >*/ + GIMP_DYNAMICS_OUTPUT_FLOW, /*< desc="Flow" >*/ + GIMP_DYNAMICS_OUTPUT_JITTER, /*< desc="Jitter" >*/ +} GimpDynamicsOutputType; + + +#define GIMP_TYPE_FILL_STYLE (gimp_fill_style_get_type ()) + +GType gimp_fill_style_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_FILL_STYLE_SOLID, /*< desc="Solid color" >*/ + GIMP_FILL_STYLE_PATTERN /*< desc="Pattern" >*/ +} GimpFillStyle; + + +#define GIMP_TYPE_FILTER_REGION (gimp_filter_region_get_type ()) + +GType gimp_filter_region_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_FILTER_REGION_SELECTION, /*< desc="Use the selection as input" >*/ + GIMP_FILTER_REGION_DRAWABLE /*< desc="Use the entire layer as input" >*/ +} GimpFilterRegion; + + +#define GIMP_TYPE_GRADIENT_COLOR (gimp_gradient_color_get_type ()) + +GType gimp_gradient_color_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_GRADIENT_COLOR_FIXED, /*< desc="Fixed" >*/ + GIMP_GRADIENT_COLOR_FOREGROUND, /*< desc="Foreground color", abbrev="FG" >*/ + GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, /*< desc="Foreground color (transparent)", abbrev="FG (t)" >*/ + GIMP_GRADIENT_COLOR_BACKGROUND, /*< desc="Background color", abbrev="BG" >*/ + GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT /*< desc="Background color (transparent)", abbrev="BG (t)" >*/ +} GimpGradientColor; + + +#define GIMP_TYPE_GRAVITY_TYPE (gimp_gravity_type_get_type ()) + +GType gimp_gravity_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_GRAVITY_NONE, + GIMP_GRAVITY_NORTH_WEST, + GIMP_GRAVITY_NORTH, + GIMP_GRAVITY_NORTH_EAST, + GIMP_GRAVITY_WEST, + GIMP_GRAVITY_CENTER, + GIMP_GRAVITY_EAST, + GIMP_GRAVITY_SOUTH_WEST, + GIMP_GRAVITY_SOUTH, + GIMP_GRAVITY_SOUTH_EAST +} GimpGravityType; + + +#define GIMP_TYPE_GUIDE_STYLE (gimp_guide_style_get_type ()) + +GType gimp_guide_style_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_GUIDE_STYLE_NONE, + GIMP_GUIDE_STYLE_NORMAL, + GIMP_GUIDE_STYLE_MIRROR, + GIMP_GUIDE_STYLE_MANDALA, + GIMP_GUIDE_STYLE_SPLIT_VIEW +} GimpGuideStyle; + + +#define GIMP_TYPE_HISTOGRAM_CHANNEL (gimp_histogram_channel_get_type ()) + +GType gimp_histogram_channel_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HISTOGRAM_VALUE = 0, /*< desc="Value" >*/ + GIMP_HISTOGRAM_RED = 1, /*< desc="Red" >*/ + GIMP_HISTOGRAM_GREEN = 2, /*< desc="Green" >*/ + GIMP_HISTOGRAM_BLUE = 3, /*< desc="Blue" >*/ + GIMP_HISTOGRAM_ALPHA = 4, /*< desc="Alpha" >*/ + GIMP_HISTOGRAM_LUMINANCE = 5, /*< desc="Luminance" >*/ + GIMP_HISTOGRAM_RGB = 6 /*< desc="RGB", pdb-skip >*/ +} GimpHistogramChannel; + + +#define GIMP_TYPE_ITEM_SET (gimp_item_set_get_type ()) + +GType gimp_item_set_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_ITEM_SET_NONE, /*< desc="None" >*/ + GIMP_ITEM_SET_ALL, /*< desc="All layers" >*/ + GIMP_ITEM_SET_IMAGE_SIZED, /*< desc="Image-sized layers" >*/ + GIMP_ITEM_SET_VISIBLE, /*< desc="All visible layers" >*/ + GIMP_ITEM_SET_LINKED /*< desc="All linked layers" >*/ +} GimpItemSet; + + +#define GIMP_TYPE_MATTING_ENGINE (gimp_matting_engine_get_type ()) + +GType gimp_matting_engine_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_MATTING_ENGINE_GLOBAL, /*< desc="Matting Global" >*/ + GIMP_MATTING_ENGINE_LEVIN, /*< desc="Matting Levin" >*/ +} GimpMattingEngine; + + +#define GIMP_TYPE_MESSAGE_SEVERITY (gimp_message_severity_get_type ()) + +GType gimp_message_severity_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_MESSAGE_INFO, /*< desc="Message" >*/ + GIMP_MESSAGE_WARNING, /*< desc="Warning" >*/ + GIMP_MESSAGE_ERROR, /*< desc="Error" >*/ + GIMP_MESSAGE_BUG_WARNING, /*< desc="WARNING" >*/ + GIMP_MESSAGE_BUG_CRITICAL /*< desc="CRITICAL" >*/ +} GimpMessageSeverity; + + +#define GIMP_TYPE_PASTE_TYPE (gimp_paste_type_get_type ()) + +GType gimp_paste_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_PASTE_TYPE_FLOATING, + GIMP_PASTE_TYPE_FLOATING_IN_PLACE, + GIMP_PASTE_TYPE_FLOATING_INTO, + GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE, + GIMP_PASTE_TYPE_NEW_LAYER, + GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE +} GimpPasteType; + + +#define GIMP_TYPE_THUMBNAIL_SIZE (gimp_thumbnail_size_get_type ()) + +GType gimp_thumbnail_size_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_THUMBNAIL_SIZE_NONE = 0, /*< desc="No thumbnails" >*/ + GIMP_THUMBNAIL_SIZE_NORMAL = 128, /*< desc="Normal (128x128)" >*/ + GIMP_THUMBNAIL_SIZE_LARGE = 256 /*< desc="Large (256x256)" >*/ +} GimpThumbnailSize; + + +#define GIMP_TYPE_UNDO_EVENT (gimp_undo_event_get_type ()) + +GType gimp_undo_event_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_UNDO_EVENT_UNDO_PUSHED, /* a new undo has been added to the undo stack */ + GIMP_UNDO_EVENT_UNDO_EXPIRED, /* an undo has been freed from the undo stack */ + GIMP_UNDO_EVENT_REDO_EXPIRED, /* a redo has been freed from the redo stack */ + GIMP_UNDO_EVENT_UNDO, /* an undo has been executed */ + GIMP_UNDO_EVENT_REDO, /* a redo has been executed */ + GIMP_UNDO_EVENT_UNDO_FREE, /* all undo and redo info has been cleared */ + GIMP_UNDO_EVENT_UNDO_FREEZE, /* undo has been frozen */ + GIMP_UNDO_EVENT_UNDO_THAW /* undo has been thawn */ +} GimpUndoEvent; + + +#define GIMP_TYPE_UNDO_MODE (gimp_undo_mode_get_type ()) + +GType gimp_undo_mode_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_UNDO_MODE_UNDO, + GIMP_UNDO_MODE_REDO +} GimpUndoMode; + + +#define GIMP_TYPE_UNDO_TYPE (gimp_undo_type_get_type ()) + +GType gimp_undo_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + /* Type NO_UNDO_GROUP (0) is special - in the gimpimage structure it + * means there is no undo group currently being added to. + */ + GIMP_UNDO_GROUP_NONE = 0, /*< desc="<>" >*/ + + GIMP_UNDO_GROUP_FIRST = GIMP_UNDO_GROUP_NONE, /*< skip >*/ + + GIMP_UNDO_GROUP_IMAGE_SCALE, /*< desc="Scale image" >*/ + GIMP_UNDO_GROUP_IMAGE_RESIZE, /*< desc="Resize image" >*/ + GIMP_UNDO_GROUP_IMAGE_FLIP, /*< desc="Flip image" >*/ + GIMP_UNDO_GROUP_IMAGE_ROTATE, /*< desc="Rotate image" >*/ + GIMP_UNDO_GROUP_IMAGE_TRANSFORM, /*< desc="Transform image" >*/ + GIMP_UNDO_GROUP_IMAGE_CROP, /*< desc="Crop image" >*/ + GIMP_UNDO_GROUP_IMAGE_CONVERT, /*< desc="Convert image" >*/ + GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, /*< desc="Remove item" >*/ + GIMP_UNDO_GROUP_IMAGE_ITEM_REORDER, /*< desc="Reorder item" >*/ + GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, /*< desc="Merge layers" >*/ + GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, /*< desc="Merge paths" >*/ + GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, /*< desc="Quick Mask" >*/ + GIMP_UNDO_GROUP_IMAGE_GRID, /*< desc="Grid" >*/ + GIMP_UNDO_GROUP_GUIDE, /*< desc="Guide" >*/ + GIMP_UNDO_GROUP_SAMPLE_POINT, /*< desc="Sample Point" >*/ + GIMP_UNDO_GROUP_DRAWABLE, /*< desc="Layer/Channel" >*/ + GIMP_UNDO_GROUP_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/ + GIMP_UNDO_GROUP_MASK, /*< desc="Selection mask" >*/ + GIMP_UNDO_GROUP_ITEM_VISIBILITY, /*< desc="Item visibility" >*/ + GIMP_UNDO_GROUP_ITEM_LINKED, /*< desc="Link/Unlink item" >*/ + GIMP_UNDO_GROUP_ITEM_PROPERTIES, /*< desc="Item properties" >*/ + GIMP_UNDO_GROUP_ITEM_DISPLACE, /*< desc="Move item" >*/ + GIMP_UNDO_GROUP_ITEM_SCALE, /*< desc="Scale item" >*/ + GIMP_UNDO_GROUP_ITEM_RESIZE, /*< desc="Resize item" >*/ + GIMP_UNDO_GROUP_LAYER_ADD, /*< desc="Add layer" >*/ + GIMP_UNDO_GROUP_LAYER_ADD_MASK, /*< desc="Add layer mask" >*/ + GIMP_UNDO_GROUP_LAYER_APPLY_MASK, /*< desc="Apply layer mask" >*/ + GIMP_UNDO_GROUP_FS_TO_LAYER, /*< desc="Floating selection to layer" >*/ + GIMP_UNDO_GROUP_FS_FLOAT, /*< desc="Float selection" >*/ + GIMP_UNDO_GROUP_FS_ANCHOR, /*< desc="Anchor floating selection" >*/ + GIMP_UNDO_GROUP_EDIT_PASTE, /*< desc="Paste" >*/ + GIMP_UNDO_GROUP_EDIT_CUT, /*< desc="Cut" >*/ + GIMP_UNDO_GROUP_TEXT, /*< desc="Text" >*/ + GIMP_UNDO_GROUP_TRANSFORM, /*< desc="Transform" >*/ + GIMP_UNDO_GROUP_PAINT, /*< desc="Paint" >*/ + GIMP_UNDO_GROUP_PARASITE_ATTACH, /*< desc="Attach parasite" >*/ + GIMP_UNDO_GROUP_PARASITE_REMOVE, /*< desc="Remove parasite" >*/ + GIMP_UNDO_GROUP_VECTORS_IMPORT, /*< desc="Import paths" >*/ + GIMP_UNDO_GROUP_MISC, /*< desc="Plug-In" >*/ + + GIMP_UNDO_GROUP_LAST = GIMP_UNDO_GROUP_MISC, /*< skip >*/ + + /* Undo types which actually do something */ + + GIMP_UNDO_IMAGE_TYPE, /*< desc="Image type" >*/ + GIMP_UNDO_IMAGE_PRECISION, /*< desc="Image precision" >*/ + GIMP_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/ + GIMP_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/ + GIMP_UNDO_IMAGE_GRID, /*< desc="Grid" >*/ + GIMP_UNDO_IMAGE_METADATA, /*< desc="Change metadata" >*/ + GIMP_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/ + GIMP_UNDO_IMAGE_COLOR_MANAGED, /*< desc="Change color managed state" >*/ + GIMP_UNDO_GUIDE, /*< desc="Guide" >*/ + GIMP_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/ + GIMP_UNDO_DRAWABLE, /*< desc="Layer/Channel" >*/ + GIMP_UNDO_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/ + GIMP_UNDO_MASK, /*< desc="Selection mask" >*/ + GIMP_UNDO_ITEM_REORDER, /*< desc="Reorder item" >*/ + GIMP_UNDO_ITEM_RENAME, /*< desc="Rename item" >*/ + GIMP_UNDO_ITEM_DISPLACE, /*< desc="Move item" >*/ + GIMP_UNDO_ITEM_VISIBILITY, /*< desc="Item visibility" >*/ + GIMP_UNDO_ITEM_LINKED, /*< desc="Link/Unlink item" >*/ + GIMP_UNDO_ITEM_COLOR_TAG, /*< desc="Item color tag" >*/ + GIMP_UNDO_ITEM_LOCK_CONTENT, /*< desc="Lock/Unlock content" >*/ + GIMP_UNDO_ITEM_LOCK_POSITION, /*< desc="Lock/Unlock position" >*/ + GIMP_UNDO_LAYER_ADD, /*< desc="New layer" >*/ + GIMP_UNDO_LAYER_REMOVE, /*< desc="Delete layer" >*/ + GIMP_UNDO_LAYER_MODE, /*< desc="Set layer mode" >*/ + GIMP_UNDO_LAYER_OPACITY, /*< desc="Set layer opacity" >*/ + GIMP_UNDO_LAYER_LOCK_ALPHA, /*< desc="Lock/Unlock alpha channel" >*/ + GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, /*< desc="Suspend group layer resize" >*/ + GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, /*< desc="Resume group layer resize" >*/ + GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, /*< desc="Suspend group layer mask" >*/ + GIMP_UNDO_GROUP_LAYER_RESUME_MASK, /*< desc="Resume group layer mask" >*/ + GIMP_UNDO_GROUP_LAYER_START_TRANSFORM, /*< desc="Start transforming group layer" >*/ + GIMP_UNDO_GROUP_LAYER_END_TRANSFORM, /*< desc="End transforming group layer" >*/ + GIMP_UNDO_GROUP_LAYER_CONVERT, /*< desc="Convert group layer" >*/ + GIMP_UNDO_TEXT_LAYER, /*< desc="Text layer" >*/ + GIMP_UNDO_TEXT_LAYER_MODIFIED, /*< desc="Text layer modification" >*/ + GIMP_UNDO_TEXT_LAYER_CONVERT, /*< desc="Convert text layer" >*/ + GIMP_UNDO_LAYER_MASK_ADD, /*< desc="Add layer mask" >*/ + GIMP_UNDO_LAYER_MASK_REMOVE, /*< desc="Delete layer mask" >*/ + GIMP_UNDO_LAYER_MASK_APPLY, /*< desc="Apply layer mask" >*/ + GIMP_UNDO_LAYER_MASK_SHOW, /*< desc="Show layer mask" >*/ + GIMP_UNDO_CHANNEL_ADD, /*< desc="New channel" >*/ + GIMP_UNDO_CHANNEL_REMOVE, /*< desc="Delete channel" >*/ + GIMP_UNDO_CHANNEL_COLOR, /*< desc="Channel color" >*/ + GIMP_UNDO_VECTORS_ADD, /*< desc="New path" >*/ + GIMP_UNDO_VECTORS_REMOVE, /*< desc="Delete path" >*/ + GIMP_UNDO_VECTORS_MOD, /*< desc="Path modification" >*/ + GIMP_UNDO_FS_TO_LAYER, /*< desc="Floating selection to layer" >*/ + GIMP_UNDO_TRANSFORM_GRID, /*< desc="Transform grid" >*/ + GIMP_UNDO_PAINT, /*< desc="Paint" >*/ + GIMP_UNDO_INK, /*< desc="Ink" >*/ + GIMP_UNDO_FOREGROUND_SELECT, /*< desc="Select foreground" >*/ + GIMP_UNDO_PARASITE_ATTACH, /*< desc="Attach parasite" >*/ + GIMP_UNDO_PARASITE_REMOVE, /*< desc="Remove parasite" >*/ + + GIMP_UNDO_CANT /*< desc="Not undoable" >*/ +} GimpUndoType; + + +#define GIMP_TYPE_VIEW_SIZE (gimp_view_size_get_type ()) + +GType gimp_view_size_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_VIEW_SIZE_TINY = 12, /*< desc="Tiny" >*/ + GIMP_VIEW_SIZE_EXTRA_SMALL = 16, /*< desc="Very small" >*/ + GIMP_VIEW_SIZE_SMALL = 24, /*< desc="Small" >*/ + GIMP_VIEW_SIZE_MEDIUM = 32, /*< desc="Medium" >*/ + GIMP_VIEW_SIZE_LARGE = 48, /*< desc="Large" >*/ + GIMP_VIEW_SIZE_EXTRA_LARGE = 64, /*< desc="Very large" >*/ + GIMP_VIEW_SIZE_HUGE = 96, /*< desc="Huge" >*/ + GIMP_VIEW_SIZE_ENORMOUS = 128, /*< desc="Enormous" >*/ + GIMP_VIEW_SIZE_GIGANTIC = 192 /*< desc="Gigantic" >*/ +} GimpViewSize; + + +#define GIMP_TYPE_VIEW_TYPE (gimp_view_type_get_type ()) + +GType gimp_view_type_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_VIEW_TYPE_LIST, /*< desc="View as list" >*/ + GIMP_VIEW_TYPE_GRID /*< desc="View as grid" >*/ +} GimpViewType; + + +/* + * non-registered enums; register them if needed + */ + + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_CONTEXT_PROP_FIRST = 2, + + GIMP_CONTEXT_PROP_IMAGE = GIMP_CONTEXT_PROP_FIRST, + GIMP_CONTEXT_PROP_DISPLAY = 3, + GIMP_CONTEXT_PROP_TOOL = 4, + GIMP_CONTEXT_PROP_PAINT_INFO = 5, + GIMP_CONTEXT_PROP_FOREGROUND = 6, + GIMP_CONTEXT_PROP_BACKGROUND = 7, + GIMP_CONTEXT_PROP_OPACITY = 8, + GIMP_CONTEXT_PROP_PAINT_MODE = 9, + GIMP_CONTEXT_PROP_BRUSH = 10, + GIMP_CONTEXT_PROP_DYNAMICS = 11, + GIMP_CONTEXT_PROP_MYBRUSH = 12, + GIMP_CONTEXT_PROP_PATTERN = 13, + GIMP_CONTEXT_PROP_GRADIENT = 14, + GIMP_CONTEXT_PROP_PALETTE = 15, + GIMP_CONTEXT_PROP_FONT = 16, + GIMP_CONTEXT_PROP_TOOL_PRESET = 17, + GIMP_CONTEXT_PROP_BUFFER = 18, + GIMP_CONTEXT_PROP_IMAGEFILE = 19, + GIMP_CONTEXT_PROP_TEMPLATE = 20, + + GIMP_CONTEXT_PROP_LAST = GIMP_CONTEXT_PROP_TEMPLATE +} GimpContextPropType; + + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_CONTEXT_PROP_MASK_IMAGE = 1 << 2, + GIMP_CONTEXT_PROP_MASK_DISPLAY = 1 << 3, + GIMP_CONTEXT_PROP_MASK_TOOL = 1 << 4, + GIMP_CONTEXT_PROP_MASK_PAINT_INFO = 1 << 5, + GIMP_CONTEXT_PROP_MASK_FOREGROUND = 1 << 6, + GIMP_CONTEXT_PROP_MASK_BACKGROUND = 1 << 7, + GIMP_CONTEXT_PROP_MASK_OPACITY = 1 << 8, + GIMP_CONTEXT_PROP_MASK_PAINT_MODE = 1 << 9, + GIMP_CONTEXT_PROP_MASK_BRUSH = 1 << 10, + GIMP_CONTEXT_PROP_MASK_DYNAMICS = 1 << 11, + GIMP_CONTEXT_PROP_MASK_MYBRUSH = 1 << 12, + GIMP_CONTEXT_PROP_MASK_PATTERN = 1 << 13, + GIMP_CONTEXT_PROP_MASK_GRADIENT = 1 << 14, + GIMP_CONTEXT_PROP_MASK_PALETTE = 1 << 15, + GIMP_CONTEXT_PROP_MASK_FONT = 1 << 16, + GIMP_CONTEXT_PROP_MASK_TOOL_PRESET = 1 << 17, + GIMP_CONTEXT_PROP_MASK_BUFFER = 1 << 18, + GIMP_CONTEXT_PROP_MASK_IMAGEFILE = 1 << 19, + GIMP_CONTEXT_PROP_MASK_TEMPLATE = 1 << 20, + + /* aliases */ + GIMP_CONTEXT_PROP_MASK_PAINT = (GIMP_CONTEXT_PROP_MASK_FOREGROUND | + GIMP_CONTEXT_PROP_MASK_BACKGROUND | + GIMP_CONTEXT_PROP_MASK_OPACITY | + GIMP_CONTEXT_PROP_MASK_PAINT_MODE | + GIMP_CONTEXT_PROP_MASK_BRUSH | + GIMP_CONTEXT_PROP_MASK_DYNAMICS | + GIMP_CONTEXT_PROP_MASK_PATTERN | + GIMP_CONTEXT_PROP_MASK_GRADIENT), + + GIMP_CONTEXT_PROP_MASK_ALL = (GIMP_CONTEXT_PROP_MASK_IMAGE | + GIMP_CONTEXT_PROP_MASK_DISPLAY | + GIMP_CONTEXT_PROP_MASK_TOOL | + GIMP_CONTEXT_PROP_MASK_PAINT_INFO | + GIMP_CONTEXT_PROP_MASK_MYBRUSH | + GIMP_CONTEXT_PROP_MASK_PALETTE | + GIMP_CONTEXT_PROP_MASK_FONT | + GIMP_CONTEXT_PROP_MASK_TOOL_PRESET | + GIMP_CONTEXT_PROP_MASK_BUFFER | + GIMP_CONTEXT_PROP_MASK_IMAGEFILE | + GIMP_CONTEXT_PROP_MASK_TEMPLATE | + GIMP_CONTEXT_PROP_MASK_PAINT) +} GimpContextPropMask; + + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_IMAGE_SCALE_OK, + GIMP_IMAGE_SCALE_TOO_SMALL, + GIMP_IMAGE_SCALE_TOO_BIG +} GimpImageScaleCheckType; + + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_ITEM_TYPE_LAYERS = 1 << 0, + GIMP_ITEM_TYPE_CHANNELS = 1 << 1, + GIMP_ITEM_TYPE_VECTORS = 1 << 2, + + GIMP_ITEM_TYPE_ALL = (GIMP_ITEM_TYPE_LAYERS | + GIMP_ITEM_TYPE_CHANNELS | + GIMP_ITEM_TYPE_VECTORS) +} GimpItemTypeMask; + + +#endif /* __CORE_ENUMS_H__ */ diff --git a/app/core/core-types.h b/app/core/core-types.h new file mode 100644 index 0000000..bc331b4 --- /dev/null +++ b/app/core/core-types.h @@ -0,0 +1,303 @@ +/* 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 . + */ + +#ifndef __CORE_TYPES_H__ +#define __CORE_TYPES_H__ + + +#include "libgimpbase/gimpbasetypes.h" +#include "libgimpmath/gimpmathtypes.h" +#include "libgimpcolor/gimpcolortypes.h" +#include "libgimpmodule/gimpmoduletypes.h" +#include "libgimpthumb/gimpthumb-types.h" + +#include "config/config-types.h" + +#include "core/core-enums.h" + + +/* former base/ defines */ + +#define MAX_CHANNELS 4 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define GRAY 0 +#define ALPHA_G 1 + +#define INDEXED 0 +#define ALPHA_I 1 + + +/* defines */ + +#define GIMP_COORDS_MIN_PRESSURE 0.0 +#define GIMP_COORDS_MAX_PRESSURE 1.0 +#define GIMP_COORDS_DEFAULT_PRESSURE 1.0 + +#define GIMP_COORDS_MIN_TILT -1.0 +#define GIMP_COORDS_MAX_TILT 1.0 +#define GIMP_COORDS_DEFAULT_TILT 0.0 + +#define GIMP_COORDS_MIN_WHEEL 0.0 +#define GIMP_COORDS_MAX_WHEEL 1.0 +#define GIMP_COORDS_DEFAULT_WHEEL 0.5 + +#define GIMP_COORDS_DEFAULT_VELOCITY 0.0 + +#define GIMP_COORDS_DEFAULT_DIRECTION 0.0 + +#define GIMP_COORDS_DEFAULT_XSCALE 1.0 +#define GIMP_COORDS_DEFAULT_YSCALE 1.0 + +#define GIMP_COORDS_DEFAULT_VALUES { 0.0, 0.0, \ + GIMP_COORDS_DEFAULT_PRESSURE, \ + GIMP_COORDS_DEFAULT_TILT, \ + GIMP_COORDS_DEFAULT_TILT, \ + GIMP_COORDS_DEFAULT_WHEEL, \ + GIMP_COORDS_DEFAULT_VELOCITY, \ + GIMP_COORDS_DEFAULT_DIRECTION,\ + GIMP_COORDS_DEFAULT_XSCALE, \ + GIMP_COORDS_DEFAULT_YSCALE } + + +/* base classes */ + +typedef struct _GimpObject GimpObject; +typedef struct _GimpViewable GimpViewable; +typedef struct _GimpFilter GimpFilter; +typedef struct _GimpItem GimpItem; +typedef struct _GimpAuxItem GimpAuxItem; + +typedef struct _Gimp Gimp; +typedef struct _GimpImage GimpImage; + + +/* containers */ + +typedef struct _GimpContainer GimpContainer; +typedef struct _GimpList GimpList; +typedef struct _GimpDocumentList GimpDocumentList; +typedef struct _GimpDrawableStack GimpDrawableStack; +typedef struct _GimpFilteredContainer GimpFilteredContainer; +typedef struct _GimpFilterStack GimpFilterStack; +typedef struct _GimpItemStack GimpItemStack; +typedef struct _GimpLayerStack GimpLayerStack; +typedef struct _GimpTaggedContainer GimpTaggedContainer; +typedef struct _GimpTreeProxy GimpTreeProxy; + + +/* not really a container */ + +typedef struct _GimpItemTree GimpItemTree; + + +/* context objects */ + +typedef struct _GimpContext GimpContext; +typedef struct _GimpFillOptions GimpFillOptions; +typedef struct _GimpStrokeOptions GimpStrokeOptions; +typedef struct _GimpToolOptions GimpToolOptions; + + +/* info objects */ + +typedef struct _GimpPaintInfo GimpPaintInfo; +typedef struct _GimpToolGroup GimpToolGroup; +typedef struct _GimpToolInfo GimpToolInfo; +typedef struct _GimpToolItem GimpToolItem; + + +/* data objects */ + +typedef struct _GimpDataFactory GimpDataFactory; +typedef struct _GimpDataLoaderFactory GimpDataLoaderFactory; +typedef struct _GimpData GimpData; +typedef struct _GimpBrush GimpBrush; +typedef struct _GimpBrushCache GimpBrushCache; +typedef struct _GimpBrushClipboard GimpBrushClipboard; +typedef struct _GimpBrushGenerated GimpBrushGenerated; +typedef struct _GimpBrushPipe GimpBrushPipe; +typedef struct _GimpCurve GimpCurve; +typedef struct _GimpDynamics GimpDynamics; +typedef struct _GimpDynamicsOutput GimpDynamicsOutput; +typedef struct _GimpGradient GimpGradient; +typedef struct _GimpMybrush GimpMybrush; +typedef struct _GimpPalette GimpPalette; +typedef struct _GimpPaletteMru GimpPaletteMru; +typedef struct _GimpPattern GimpPattern; +typedef struct _GimpPatternClipboard GimpPatternClipboard; +typedef struct _GimpToolPreset GimpToolPreset; +typedef struct _GimpTagCache GimpTagCache; + + +/* drawable objects */ + +typedef struct _GimpDrawable GimpDrawable; +typedef struct _GimpChannel GimpChannel; +typedef struct _GimpLayerMask GimpLayerMask; +typedef struct _GimpSelection GimpSelection; +typedef struct _GimpLayer GimpLayer; +typedef struct _GimpGroupLayer GimpGroupLayer; + + +/* auxillary image items */ + +typedef struct _GimpGuide GimpGuide; +typedef struct _GimpSamplePoint GimpSamplePoint; + + +/* undo objects */ + +typedef struct _GimpUndo GimpUndo; +typedef struct _GimpUndoStack GimpUndoStack; +typedef struct _GimpUndoAccumulator GimpUndoAccumulator; + + +/* Symmetry transformations */ + +typedef struct _GimpSymmetry GimpSymmetry; +typedef struct _GimpMirror GimpMirror; +typedef struct _GimpTiling GimpTiling; +typedef struct _GimpMandala GimpMandala; + + +/* misc objects */ + +typedef struct _GimpAsync GimpAsync; +typedef struct _GimpAsyncSet GimpAsyncSet; +typedef struct _GimpBuffer GimpBuffer; +typedef struct _GimpDrawableFilter GimpDrawableFilter; +typedef struct _GimpEnvironTable GimpEnvironTable; +typedef struct _GimpHistogram GimpHistogram; +typedef struct _GimpIdTable GimpIdTable; +typedef struct _GimpImagefile GimpImagefile; +typedef struct _GimpImageProxy GimpImageProxy; +typedef struct _GimpInterpreterDB GimpInterpreterDB; +typedef struct _GimpLineArt GimpLineArt; +typedef struct _GimpObjectQueue GimpObjectQueue; +typedef struct _GimpParasiteList GimpParasiteList; +typedef struct _GimpPdbProgress GimpPdbProgress; +typedef struct _GimpProjection GimpProjection; +typedef struct _GimpSettings GimpSettings; +typedef struct _GimpSubProgress GimpSubProgress; +typedef struct _GimpTag GimpTag; +typedef struct _GimpTreeHandler GimpTreeHandler; +typedef struct _GimpTriviallyCancelableWaitable GimpTriviallyCancelableWaitable; +typedef struct _GimpUncancelableWaitable GimpUncancelableWaitable; + + +/* interfaces */ + +typedef struct _GimpCancelable GimpCancelable; /* dummy typedef */ +typedef struct _GimpPickable GimpPickable; /* dummy typedef */ +typedef struct _GimpProgress GimpProgress; /* dummy typedef */ +typedef struct _GimpProjectable GimpProjectable; /* dummy typedef */ +typedef struct _GimpTagged GimpTagged; /* dummy typedef */ +typedef struct _GimpWaitable GimpWaitable; /* dummy typedef */ + + +/* non-object types */ + +typedef struct _GimpBacktrace GimpBacktrace; +typedef struct _GimpBoundSeg GimpBoundSeg; +typedef struct _GimpChunkIterator GimpChunkIterator; +typedef struct _GimpCoords GimpCoords; +typedef struct _GimpGradientSegment GimpGradientSegment; +typedef struct _GimpPaletteEntry GimpPaletteEntry; +typedef struct _GimpScanConvert GimpScanConvert; +typedef struct _GimpTempBuf GimpTempBuf; +typedef guint32 GimpTattoo; + +/* The following hack is made so that we can reuse the definition + * the cairo definition of cairo_path_t without having to translate + * between our own version of a bezier description and cairos version. + * + * to avoid having to include in each and every file + * including this file we only use the "real" definition when cairo.h + * already has been included and use something else. + * + * Note that if you really want to work with GimpBezierDesc (except just + * passing pointers to it around) you also need to include . + */ +#ifdef CAIRO_VERSION +typedef cairo_path_t GimpBezierDesc; +#else +typedef void * GimpBezierDesc; +#endif + + +/* functions */ + +typedef void (* GimpInitStatusFunc) (const gchar *text1, + const gchar *text2, + gdouble percentage); + +typedef gboolean (* GimpObjectFilterFunc) (GimpObject *object, + gpointer user_data); + +typedef gint64 (* GimpMemsizeFunc) (gpointer instance, + gint64 *gui_size); + +typedef void (* GimpRunAsyncFunc) (GimpAsync *async, + gpointer user_data); + + +/* structs */ + +struct _GimpCoords +{ + gdouble x; + gdouble y; + gdouble pressure; + gdouble xtilt; + gdouble ytilt; + gdouble wheel; + gdouble velocity; + gdouble direction; + gdouble xscale; /* the view scale */ + gdouble yscale; + gdouble angle; /* the view rotation angle */ + gboolean reflect; /* whether the view is reflected */ + gboolean extended; +}; + +/* temp hack as replacement for GdkSegment */ + +typedef struct _GimpSegment GimpSegment; + +struct _GimpSegment +{ + gint x1; + gint y1; + gint x2; + gint y2; +}; + + +#include "gegl/gimp-gegl-types.h" +#include "paint/paint-types.h" +#include "text/text-types.h" +#include "vectors/vectors-types.h" +#include "pdb/pdb-types.h" +#include "plug-in/plug-in-types.h" + + +#endif /* __CORE_TYPES_H__ */ diff --git a/app/core/gimp-atomic.c b/app/core/gimp-atomic.c new file mode 100644 index 0000000..c5a54b8 --- /dev/null +++ b/app/core/gimp-atomic.c @@ -0,0 +1,95 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-atomic.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp-atomic.h" + + +/* GSList */ + + +static GSList gimp_atomic_slist_sentinel; + + +void +gimp_atomic_slist_push_head (GSList * volatile *list, + gpointer data) +{ + GSList *old_head; + GSList *new_head; + + g_return_if_fail (list != NULL); + + new_head = g_slist_alloc (); + + new_head->data = data; + + do + { + do + { + old_head = g_atomic_pointer_get (list); + } + while (old_head == &gimp_atomic_slist_sentinel); + + new_head->next = old_head; + } + while (! g_atomic_pointer_compare_and_exchange (list, old_head, new_head)); +} + +gpointer +gimp_atomic_slist_pop_head (GSList * volatile *list) +{ + GSList *old_head; + GSList *new_head; + gpointer data; + + g_return_val_if_fail (list != NULL, NULL); + + do + { + do + { + old_head = g_atomic_pointer_get (list); + } + while (old_head == &gimp_atomic_slist_sentinel); + + if (! old_head) + return NULL; + } + while (! g_atomic_pointer_compare_and_exchange (list, + old_head, + &gimp_atomic_slist_sentinel)); + + new_head = old_head->next; + data = old_head->data; + + g_atomic_pointer_set (list, new_head); + + g_slist_free1 (old_head); + + return data; +} diff --git a/app/core/gimp-atomic.h b/app/core/gimp-atomic.h new file mode 100644 index 0000000..c1d71cc --- /dev/null +++ b/app/core/gimp-atomic.h @@ -0,0 +1,32 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-atomic.h + * 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 . + */ + +#ifndef __GIMP_ATOMIC_H__ +#define __GIMP_ATOMIC_H__ + + +/* GSList */ + +void gimp_atomic_slist_push_head (GSList * volatile *list, + gpointer data); +gpointer gimp_atomic_slist_pop_head (GSList * volatile *list); + + +#endif /* __GIMP_ATOMIC_H__ */ diff --git a/app/core/gimp-batch.c b/app/core/gimp-batch.c new file mode 100644 index 0000000..e7908a2 --- /dev/null +++ b/app/core/gimp-batch.c @@ -0,0 +1,203 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-batch.h" +#include "gimpparamspecs.h" + +#include "pdb/gimppdb.h" +#include "pdb/gimpprocedure.h" + +#include "gimp-intl.h" + + +#define BATCH_DEFAULT_EVAL_PROC "plug-in-script-fu-eval" + + +static void gimp_batch_exit_after_callback (Gimp *gimp) G_GNUC_NORETURN; + +static void gimp_batch_run_cmd (Gimp *gimp, + const gchar *proc_name, + GimpProcedure *procedure, + GimpRunMode run_mode, + const gchar *cmd); + + +void +gimp_batch_run (Gimp *gimp, + const gchar *batch_interpreter, + const gchar **batch_commands) +{ + gulong exit_id; + + if (! batch_commands || ! batch_commands[0]) + return; + + exit_id = g_signal_connect_after (gimp, "exit", + G_CALLBACK (gimp_batch_exit_after_callback), + NULL); + + if (! batch_interpreter) + { + batch_interpreter = g_getenv ("GIMP_BATCH_INTERPRETER"); + + if (! batch_interpreter) + { + batch_interpreter = BATCH_DEFAULT_EVAL_PROC; + + if (gimp->be_verbose) + g_printerr ("No batch interpreter specified, using the default " + "'%s'.\n", batch_interpreter); + } + } + + /* script-fu text console, hardcoded for backward compatibility */ + + if (strcmp (batch_interpreter, "plug-in-script-fu-eval") == 0 && + strcmp (batch_commands[0], "-") == 0) + { + const gchar *proc_name = "plug-in-script-fu-text-console"; + GimpProcedure *procedure = gimp_pdb_lookup_procedure (gimp->pdb, + proc_name); + + if (procedure) + gimp_batch_run_cmd (gimp, proc_name, procedure, + GIMP_RUN_NONINTERACTIVE, NULL); + else + g_message (_("The batch interpreter '%s' is not available. " + "Batch mode disabled."), proc_name); + } + else + { + GimpProcedure *eval_proc = gimp_pdb_lookup_procedure (gimp->pdb, + batch_interpreter); + + if (eval_proc) + { + gint i; + + for (i = 0; batch_commands[i]; i++) + gimp_batch_run_cmd (gimp, batch_interpreter, eval_proc, + GIMP_RUN_NONINTERACTIVE, batch_commands[i]); + } + else + { + g_message (_("The batch interpreter '%s' is not available. " + "Batch mode disabled."), batch_interpreter); + } + } + + g_signal_handler_disconnect (gimp, exit_id); +} + + +/* + * The purpose of this handler is to exit GIMP cleanly when the batch + * procedure calls the gimp-exit procedure. Without this callback, the + * message "batch command experienced an execution error" would appear + * and gimp would hang forever. + */ +static void +gimp_batch_exit_after_callback (Gimp *gimp) +{ + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + gegl_exit (); + + exit (EXIT_SUCCESS); +} + +static void +gimp_batch_run_cmd (Gimp *gimp, + const gchar *proc_name, + GimpProcedure *procedure, + GimpRunMode run_mode, + const gchar *cmd) +{ + GimpValueArray *args; + GimpValueArray *return_vals; + GError *error = NULL; + gint i = 0; + + args = gimp_procedure_get_arguments (procedure); + + if (procedure->num_args > i && + GIMP_IS_PARAM_SPEC_INT32 (procedure->args[i])) + g_value_set_int (gimp_value_array_index (args, i++), run_mode); + + if (procedure->num_args > i && + GIMP_IS_PARAM_SPEC_STRING (procedure->args[i])) + g_value_set_static_string (gimp_value_array_index (args, i++), cmd); + + return_vals = + gimp_pdb_execute_procedure_by_name_args (gimp->pdb, + gimp_get_user_context (gimp), + NULL, &error, + proc_name, args); + + switch (g_value_get_enum (gimp_value_array_index (return_vals, 0))) + { + case GIMP_PDB_EXECUTION_ERROR: + if (error) + { + g_printerr ("batch command experienced an execution error:\n" + "%s\n", error->message); + } + else + { + g_printerr ("batch command experienced an execution error\n"); + } + break; + + case GIMP_PDB_CALLING_ERROR: + if (error) + { + g_printerr ("batch command experienced a calling error:\n" + "%s\n", error->message); + } + else + { + g_printerr ("batch command experienced a calling error\n"); + } + break; + + case GIMP_PDB_SUCCESS: + g_printerr ("batch command executed successfully\n"); + break; + } + + gimp_value_array_unref (return_vals); + gimp_value_array_unref (args); + + if (error) + g_error_free (error); + + return; +} diff --git a/app/core/gimp-batch.h b/app/core/gimp-batch.h new file mode 100644 index 0000000..3e6945c --- /dev/null +++ b/app/core/gimp-batch.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 . + */ + +#ifndef __GIMP_BATCH_H__ +#define __GIMP_BATCH_H__ + + +void gimp_batch_run (Gimp *gimp, + const gchar *batch_interpreter, + const gchar **batch_commands); + + +#endif /* __GIMP_BATCH_H__ */ diff --git a/app/core/gimp-cairo.c b/app/core/gimp-cairo.c new file mode 100644 index 0000000..9d5574f --- /dev/null +++ b/app/core/gimp-cairo.c @@ -0,0 +1,217 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-cairo.c + * Copyright (C) 2010-2012 Michael Natterer + * + * Some code here is based on code from librsvg that was originally + * written by Raph Levien for Gill. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp-cairo.h" + + +#define REV (2.0 * G_PI) + + +static cairo_user_data_key_t surface_data_key = { 0, }; + + +cairo_pattern_t * +gimp_cairo_pattern_create_stipple (const GimpRGB *fg, + const GimpRGB *bg, + gint index, + gdouble offset_x, + gdouble offset_y) +{ + cairo_surface_t *surface; + cairo_pattern_t *pattern; + guchar *data; + guchar *d; + guchar fg_r, fg_g, fg_b, fg_a; + guchar bg_r, bg_g, bg_b, bg_a; + gint x, y; + + g_return_val_if_fail (fg != NULL, NULL); + g_return_val_if_fail (bg != NULL, NULL); + + data = g_malloc (8 * 8 * 4); + + gimp_rgba_get_uchar (fg, &fg_r, &fg_g, &fg_b, &fg_a); + gimp_rgba_get_uchar (bg, &bg_r, &bg_g, &bg_b, &bg_a); + + d = data; + + for (y = 0; y < 8; y++) + { + for (x = 0; x < 8; x++) + { + if ((x + y + index) % 8 >= 4) + GIMP_CAIRO_ARGB32_SET_PIXEL (d, fg_r, fg_g, fg_b, fg_a); + else + GIMP_CAIRO_ARGB32_SET_PIXEL (d, bg_r, bg_g, bg_b, bg_a); + + d += 4; + } + } + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + 8, 8, 8 * 4); + cairo_surface_set_user_data (surface, &surface_data_key, + data, (cairo_destroy_func_t) g_free); + + pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + + cairo_surface_destroy (surface); + + if (offset_x != 0.0 || offset_y != 0.0) + { + cairo_matrix_t matrix; + + cairo_matrix_init_translate (&matrix, + fmod (offset_x, 8), + fmod (offset_y, 8)); + cairo_pattern_set_matrix (pattern, &matrix); + } + + return pattern; +} + +void +gimp_cairo_arc (cairo_t *cr, + gdouble center_x, + gdouble center_y, + gdouble radius, + gdouble start_angle, + gdouble slice_angle) +{ + g_return_if_fail (cr != NULL); + + if (slice_angle >= 0) + { + cairo_arc_negative (cr, center_x, center_y, radius, + - start_angle, + - start_angle - slice_angle); + } + else + { + cairo_arc (cr, center_x, center_y, radius, + - start_angle, + - start_angle - slice_angle); + } +} + +void +gimp_cairo_rounded_rectangle (cairo_t *cr, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble corner_radius) +{ + g_return_if_fail (cr != NULL); + + if (width < 0.0) + { + x += width; + width = -width; + } + + if (height < 0.0) + { + y += height; + height = -height; + } + + corner_radius = CLAMP (corner_radius, 0.0, MIN (width, height) / 2.0); + + if (corner_radius == 0.0) + { + cairo_rectangle (cr, x, y, width, height); + + return; + } + + cairo_new_sub_path (cr); + + cairo_arc (cr, + x + corner_radius, y + corner_radius, + corner_radius, + 0.50 * REV, 0.75 * REV); + cairo_line_to (cr, + x + width - corner_radius, y); + + cairo_arc (cr, + x + width - corner_radius, y + corner_radius, + corner_radius, + 0.75 * REV, 1.00 * REV); + cairo_line_to (cr, + x + width, y + height - corner_radius); + + cairo_arc (cr, + x + width - corner_radius, y + height - corner_radius, + corner_radius, + 0.00 * REV, 0.25 * REV); + cairo_line_to (cr, + x + corner_radius, y + height); + + cairo_arc (cr, + x + corner_radius, y + height - corner_radius, + corner_radius, + 0.25 * REV, 0.50 * REV); + cairo_line_to (cr, + x, y + corner_radius); + + cairo_close_path (cr); +} + +void +gimp_cairo_segments (cairo_t *cr, + GimpSegment *segs, + gint n_segs) +{ + gint i; + + g_return_if_fail (cr != NULL); + g_return_if_fail (segs != NULL && n_segs > 0); + + for (i = 0; i < n_segs; i++) + { + if (segs[i].x1 == segs[i].x2) + { + cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5); + cairo_line_to (cr, segs[i].x2 + 0.5, segs[i].y2 - 0.5); + } + else + { + cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5); + cairo_line_to (cr, segs[i].x2 - 0.5, segs[i].y2 + 0.5); + } + } +} diff --git a/app/core/gimp-cairo.h b/app/core/gimp-cairo.h new file mode 100644 index 0000000..b767740 --- /dev/null +++ b/app/core/gimp-cairo.h @@ -0,0 +1,51 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-cairo.h + * Copyright (C) 2010-2012 Michael Natterer + * + * Some code here is based on code from librsvg that was originally + * written by Raph Levien for Gill. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __APP_GIMP_CAIRO_H__ +#define __APP_GIMP_CAIRO_H__ + + +cairo_pattern_t * gimp_cairo_pattern_create_stipple (const GimpRGB *fg, + const GimpRGB *bg, + gint index, + gdouble offset_x, + gdouble offset_y); + +void gimp_cairo_arc (cairo_t *cr, + gdouble center_x, + gdouble center_y, + gdouble radius, + gdouble start_angle, + gdouble slice_angle); +void gimp_cairo_rounded_rectangle (cairo_t *cr, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble corner_radius); +void gimp_cairo_segments (cairo_t *cr, + GimpSegment *segs, + gint n_segs); + + +#endif /* __APP_GIMP_CAIRO_H__ */ diff --git a/app/core/gimp-contexts.c b/app/core/gimp-contexts.c new file mode 100644 index 0000000..59d1e53 --- /dev/null +++ b/app/core/gimp-contexts.c @@ -0,0 +1,161 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimp-contexts.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimperror.h" +#include "gimp-contexts.h" +#include "gimpcontext.h" + +#include "config/gimpconfig-file.h" + +#include "gimp-intl.h" + + +void +gimp_contexts_init (Gimp *gimp) +{ + GimpContext *context; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* the default context contains the user's saved preferences + * + * TODO: load from disk + */ + context = gimp_context_new (gimp, "Default", NULL); + gimp_set_default_context (gimp, context); + g_object_unref (context); + + /* the initial user_context is a straight copy of the default context + */ + context = gimp_context_new (gimp, "User", context); + gimp_set_user_context (gimp, context); + g_object_unref (context); +} + +void +gimp_contexts_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_set_user_context (gimp, NULL); + gimp_set_default_context (gimp, NULL); +} + +gboolean +gimp_contexts_load (Gimp *gimp, + GError **error) +{ + GFile *file; + GError *my_error = NULL; + gboolean success; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_directory_file ("contextrc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + success = gimp_config_deserialize_gfile (GIMP_CONFIG (gimp_get_user_context (gimp)), + file, + NULL, &my_error); + + g_object_unref (file); + + if (! success) + { + if (my_error->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) + { + g_clear_error (&my_error); + success = TRUE; + } + else + { + g_propagate_error (error, my_error); + } + } + + return success; +} + +gboolean +gimp_contexts_save (Gimp *gimp, + GError **error) +{ + GFile *file; + gboolean success; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_directory_file ("contextrc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + success = gimp_config_serialize_to_gfile (GIMP_CONFIG (gimp_get_user_context (gimp)), + file, + "GIMP user context", + "end of user context", + NULL, error); + + g_object_unref (file); + + return success; +} + +gboolean +gimp_contexts_clear (Gimp *gimp, + GError **error) +{ + GFile *file; + GError *my_error = NULL; + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + + file = gimp_directory_file ("contextrc", NULL); + + if (! g_file_delete (file, NULL, &my_error) && + my_error->code != G_IO_ERROR_NOT_FOUND) + { + success = FALSE; + + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("Deleting \"%s\" failed: %s"), + gimp_file_get_utf8_name (file), my_error->message); + } + + g_clear_error (&my_error); + g_object_unref (file); + + return success; +} diff --git a/app/core/gimp-contexts.h b/app/core/gimp-contexts.h new file mode 100644 index 0000000..9aa402b --- /dev/null +++ b/app/core/gimp-contexts.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-contexts.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 . + */ + +#ifndef __GIMP_CONTEXTS_H__ +#define __GIMP_CONTEXTS_H__ + + +void gimp_contexts_init (Gimp *gimp); +void gimp_contexts_exit (Gimp *gimp); + +gboolean gimp_contexts_load (Gimp *gimp, + GError **error); +gboolean gimp_contexts_save (Gimp *gimp, + GError **error); + +gboolean gimp_contexts_clear (Gimp *gimp, + GError **error); + + +#endif /* __GIMP_CONTEXTS_H__ */ diff --git a/app/core/gimp-data-factories.c b/app/core/gimp-data-factories.c new file mode 100644 index 0000000..69b5e2a --- /dev/null +++ b/app/core/gimp-data-factories.c @@ -0,0 +1,433 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimprc.h" + +#include "gimp.h" +#include "gimp-data-factories.h" +#include "gimp-gradients.h" +#include "gimp-memsize.h" +#include "gimp-palettes.h" +#include "gimpcontainer.h" +#include "gimpbrush-load.h" +#include "gimpbrush.h" +#include "gimpbrushclipboard.h" +#include "gimpbrushgenerated-load.h" +#include "gimpbrushpipe-load.h" +#include "gimpdataloaderfactory.h" +#include "gimpdynamics.h" +#include "gimpdynamics-load.h" +#include "gimpgradient-load.h" +#include "gimpgradient.h" +#include "gimpmybrush-load.h" +#include "gimpmybrush.h" +#include "gimppalette-load.h" +#include "gimppalette.h" +#include "gimppattern-load.h" +#include "gimppattern.h" +#include "gimppatternclipboard.h" +#include "gimptagcache.h" +#include "gimptoolpreset.h" +#include "gimptoolpreset-load.h" + +#include "text/gimpfontfactory.h" + +#include "gimp-intl.h" + + +void +gimp_data_factories_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp->brush_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_BRUSH, + "brush-path", + "brush-path-writable", + gimp_brush_new, + gimp_brush_get_standard); + gimp_object_set_static_name (GIMP_OBJECT (gimp->brush_factory), + "brush factory"); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "GIMP Brush", + gimp_brush_load, + GIMP_BRUSH_FILE_EXTENSION, + TRUE); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "GIMP Brush Pixmap", + gimp_brush_load, + GIMP_BRUSH_PIXMAP_FILE_EXTENSION, + FALSE); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "Photoshop ABR Brush", + gimp_brush_load_abr, + GIMP_BRUSH_PS_FILE_EXTENSION, + FALSE); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "Paint Shop Pro JBR Brush", + gimp_brush_load_abr, + GIMP_BRUSH_PSP_FILE_EXTENSION, + FALSE); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "GIMP Generated Brush", + gimp_brush_generated_load, + GIMP_BRUSH_GENERATED_FILE_EXTENSION, + TRUE); + gimp_data_loader_factory_add_loader (gimp->brush_factory, + "GIMP Brush Pipe", + gimp_brush_pipe_load, + GIMP_BRUSH_PIPE_FILE_EXTENSION, + TRUE); + + gimp->dynamics_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_DYNAMICS, + "dynamics-path", + "dynamics-path-writable", + gimp_dynamics_new, + gimp_dynamics_get_standard); + gimp_object_set_static_name (GIMP_OBJECT (gimp->dynamics_factory), + "dynamics factory"); + gimp_data_loader_factory_add_loader (gimp->dynamics_factory, + "GIMP Paint Dynamics", + gimp_dynamics_load, + GIMP_DYNAMICS_FILE_EXTENSION, + TRUE); + + gimp->mybrush_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_MYBRUSH, + "mypaint-brush-path", + "mypaint-brush-path-writable", + NULL, + NULL); + gimp_object_set_static_name (GIMP_OBJECT (gimp->mybrush_factory), + "mypaint brush factory"); + gimp_data_loader_factory_add_loader (gimp->mybrush_factory, + "MyPaint Brush", + gimp_mybrush_load, + GIMP_MYBRUSH_FILE_EXTENSION, + FALSE); + + gimp->pattern_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_PATTERN, + "pattern-path", + "pattern-path-writable", + NULL, + gimp_pattern_get_standard); + gimp_object_set_static_name (GIMP_OBJECT (gimp->pattern_factory), + "pattern factory"); + gimp_data_loader_factory_add_loader (gimp->pattern_factory, + "GIMP Pattern", + gimp_pattern_load, + GIMP_PATTERN_FILE_EXTENSION, + TRUE); + gimp_data_loader_factory_add_fallback (gimp->pattern_factory, + "Pattern from GdkPixbuf", + gimp_pattern_load_pixbuf); + + gimp->gradient_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_GRADIENT, + "gradient-path", + "gradient-path-writable", + gimp_gradient_new, + gimp_gradient_get_standard); + gimp_object_set_static_name (GIMP_OBJECT (gimp->gradient_factory), + "gradient factory"); + gimp_data_loader_factory_add_loader (gimp->gradient_factory, + "GIMP Gradient", + gimp_gradient_load, + GIMP_GRADIENT_FILE_EXTENSION, + TRUE); + gimp_data_loader_factory_add_loader (gimp->gradient_factory, + "SVG Gradient", + gimp_gradient_load_svg, + GIMP_GRADIENT_SVG_FILE_EXTENSION, + FALSE); + + gimp->palette_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_PALETTE, + "palette-path", + "palette-path-writable", + gimp_palette_new, + gimp_palette_get_standard); + gimp_object_set_static_name (GIMP_OBJECT (gimp->palette_factory), + "palette factory"); + gimp_data_loader_factory_add_loader (gimp->palette_factory, + "GIMP Palette", + gimp_palette_load, + GIMP_PALETTE_FILE_EXTENSION, + TRUE); + + gimp->font_factory = + gimp_font_factory_new (gimp, + "font-path"); + gimp_object_set_static_name (GIMP_OBJECT (gimp->font_factory), + "font factory"); + + gimp->tool_preset_factory = + gimp_data_loader_factory_new (gimp, + GIMP_TYPE_TOOL_PRESET, + "tool-preset-path", + "tool-preset-path-writable", + gimp_tool_preset_new, + NULL); + gimp_object_set_static_name (GIMP_OBJECT (gimp->tool_preset_factory), + "tool preset factory"); + gimp_data_loader_factory_add_loader (gimp->tool_preset_factory, + "GIMP Tool Preset", + gimp_tool_preset_load, + GIMP_TOOL_PRESET_FILE_EXTENSION, + TRUE); + + gimp->tag_cache = gimp_tag_cache_new (); +} + +void +gimp_data_factories_add_builtin (Gimp *gimp) +{ + GimpData *data; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* add the builtin FG -> BG etc. gradients */ + gimp_gradients_init (gimp); + + /* add the color history palette */ + gimp_palettes_init (gimp); + + /* add the clipboard brushes */ + data = gimp_brush_clipboard_new (gimp, FALSE); + gimp_data_make_internal (data, "gimp-brush-clipboard-image"); + gimp_container_add (gimp_data_factory_get_container (gimp->brush_factory), + GIMP_OBJECT (data)); + g_object_unref (data); + + data = gimp_brush_clipboard_new (gimp, TRUE); + gimp_data_make_internal (data, "gimp-brush-clipboard-mask"); + gimp_container_add (gimp_data_factory_get_container (gimp->brush_factory), + GIMP_OBJECT (data)); + g_object_unref (data); + + /* add the clipboard pattern */ + data = gimp_pattern_clipboard_new (gimp); + gimp_data_make_internal (data, "gimp-pattern-clipboard-image"); + gimp_container_add (gimp_data_factory_get_container (gimp->pattern_factory), + GIMP_OBJECT (data)); + g_object_unref (data); +} + +void +gimp_data_factories_clear (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->brush_factory) + gimp_data_factory_data_free (gimp->brush_factory); + + if (gimp->dynamics_factory) + gimp_data_factory_data_free (gimp->dynamics_factory); + + if (gimp->mybrush_factory) + gimp_data_factory_data_free (gimp->mybrush_factory); + + if (gimp->pattern_factory) + gimp_data_factory_data_free (gimp->pattern_factory); + + if (gimp->gradient_factory) + gimp_data_factory_data_free (gimp->gradient_factory); + + if (gimp->palette_factory) + gimp_data_factory_data_free (gimp->palette_factory); + + if (gimp->font_factory) + gimp_data_factory_data_free (gimp->font_factory); + + if (gimp->tool_preset_factory) + gimp_data_factory_data_free (gimp->tool_preset_factory); +} + +void +gimp_data_factories_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + g_clear_object (&gimp->brush_factory); + g_clear_object (&gimp->dynamics_factory); + g_clear_object (&gimp->mybrush_factory); + g_clear_object (&gimp->pattern_factory); + g_clear_object (&gimp->gradient_factory); + g_clear_object (&gimp->palette_factory); + g_clear_object (&gimp->font_factory); + g_clear_object (&gimp->tool_preset_factory); + g_clear_object (&gimp->tag_cache); +} + +gint64 +gimp_data_factories_get_memsize (Gimp *gimp, + gint64 *gui_size) +{ + gint64 memsize = 0; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->named_buffers), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->brush_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->dynamics_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->mybrush_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->pattern_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->gradient_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->palette_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->font_factory), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tool_preset_factory), + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tag_cache), + gui_size); + + return memsize; +} + +void +gimp_data_factories_data_clean (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_data_factory_data_clean (gimp->brush_factory); + gimp_data_factory_data_clean (gimp->dynamics_factory); + gimp_data_factory_data_clean (gimp->mybrush_factory); + gimp_data_factory_data_clean (gimp->pattern_factory); + gimp_data_factory_data_clean (gimp->gradient_factory); + gimp_data_factory_data_clean (gimp->palette_factory); + gimp_data_factory_data_clean (gimp->font_factory); + gimp_data_factory_data_clean (gimp->tool_preset_factory); +} + +void +gimp_data_factories_load (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* initialize the list of gimp brushes */ + status_callback (NULL, _("Brushes"), 0.1); + gimp_data_factory_data_init (gimp->brush_factory, gimp->user_context, + gimp->no_data); + + /* initialize the list of gimp dynamics */ + status_callback (NULL, _("Dynamics"), 0.15); + gimp_data_factory_data_init (gimp->dynamics_factory, gimp->user_context, + gimp->no_data); + + /* initialize the list of mypaint brushes */ + status_callback (NULL, _("MyPaint Brushes"), 0.2); + gimp_data_factory_data_init (gimp->mybrush_factory, gimp->user_context, + gimp->no_data); + + /* initialize the list of gimp patterns */ + status_callback (NULL, _("Patterns"), 0.3); + gimp_data_factory_data_init (gimp->pattern_factory, gimp->user_context, + gimp->no_data); + + /* initialize the list of gimp palettes */ + status_callback (NULL, _("Palettes"), 0.4); + gimp_data_factory_data_init (gimp->palette_factory, gimp->user_context, + gimp->no_data); + + /* initialize the list of gimp gradients */ + status_callback (NULL, _("Gradients"), 0.5); + gimp_data_factory_data_init (gimp->gradient_factory, gimp->user_context, + gimp->no_data); + + /* initialize the color history */ + status_callback (NULL, _("Color History"), 0.55); + gimp_palettes_load (gimp); + + /* initialize the list of gimp fonts */ + status_callback (NULL, _("Fonts"), 0.6); + gimp_data_factory_data_init (gimp->font_factory, gimp->user_context, + gimp->no_fonts); + + /* initialize the list of gimp tool presets if we have a GUI */ + if (! gimp->no_interface) + { + status_callback (NULL, _("Tool Presets"), 0.7); + gimp_data_factory_data_init (gimp->tool_preset_factory, gimp->user_context, + gimp->no_data); + } + + /* update tag cache */ + status_callback (NULL, _("Updating tag cache"), 0.75); + gimp_tag_cache_load (gimp->tag_cache); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->brush_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->dynamics_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->mybrush_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->pattern_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->gradient_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->palette_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->font_factory)); + gimp_tag_cache_add_container (gimp->tag_cache, + gimp_data_factory_get_container (gimp->tool_preset_factory)); +} + +void +gimp_data_factories_save (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_tag_cache_save (gimp->tag_cache); + + gimp_data_factory_data_save (gimp->brush_factory); + gimp_data_factory_data_save (gimp->dynamics_factory); + gimp_data_factory_data_save (gimp->mybrush_factory); + gimp_data_factory_data_save (gimp->pattern_factory); + gimp_data_factory_data_save (gimp->gradient_factory); + gimp_data_factory_data_save (gimp->palette_factory); + gimp_data_factory_data_save (gimp->font_factory); + gimp_data_factory_data_save (gimp->tool_preset_factory); + + gimp_palettes_save (gimp); +} diff --git a/app/core/gimp-data-factories.h b/app/core/gimp-data-factories.h new file mode 100644 index 0000000..d5b273d --- /dev/null +++ b/app/core/gimp-data-factories.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DATA_FACTORIES_H__ +#define __GIMP_DATA_FACTORIES_H__ + + +void gimp_data_factories_init (Gimp *gimp); +void gimp_data_factories_add_builtin (Gimp *gimp); +void gimp_data_factories_clear (Gimp *gimp); +void gimp_data_factories_exit (Gimp *gimp); + +gint64 gimp_data_factories_get_memsize (Gimp *gimp, + gint64 *gui_size); +void gimp_data_factories_data_clean (Gimp *gimp); + +void gimp_data_factories_load (Gimp *gimp, + GimpInitStatusFunc status_callback); +void gimp_data_factories_save (Gimp *gimp); + + +#endif /* __GIMP_DATA_FACTORIES_H__ */ diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c new file mode 100644 index 0000000..372ad2d --- /dev/null +++ b/app/core/gimp-edit.c @@ -0,0 +1,771 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-edit.h" +#include "gimpbuffer.h" +#include "gimpcontext.h" +#include "gimpgrouplayer.h" +#include "gimpimage.h" +#include "gimpimage-duplicate.h" +#include "gimpimage-new.h" +#include "gimpimage-undo.h" +#include "gimplayer-floating-selection.h" +#include "gimplayer-new.h" +#include "gimplist.h" +#include "gimppickable.h" +#include "gimpselection.h" + +#include "gimp-intl.h" + + +/* local function protypes */ + +static GimpBuffer * gimp_edit_extract (GimpImage *image, + GimpPickable *pickable, + GimpContext *context, + gboolean cut_pixels, + GError **error); + + +/* public functions */ + +GimpObject * +gimp_edit_cut (GimpImage *image, + GimpDrawable *drawable, + GimpContext *context, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (GIMP_IS_LAYER (drawable) && + gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpImage *clip_image; + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + clip_image = gimp_image_new_from_drawable (image->gimp, drawable); + g_object_set_data (G_OBJECT (clip_image), "offset-x", + GINT_TO_POINTER (off_x)); + g_object_set_data (G_OBJECT (clip_image), "offset-y", + GINT_TO_POINTER (off_y)); + gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image)); + gimp_set_clipboard_image (image->gimp, clip_image); + g_object_unref (clip_image); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT, + C_("undo-type", "Cut Layer")); + + gimp_image_remove_layer (image, GIMP_LAYER (drawable), + TRUE, NULL); + + gimp_image_undo_group_end (image); + + return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp)); + } + else + { + GimpBuffer *buffer; + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable), + context, TRUE, error); + + if (buffer) + { + gimp_set_clipboard_buffer (image->gimp, buffer); + g_object_unref (buffer); + + return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp)); + } + } + + return NULL; +} + +GimpObject * +gimp_edit_copy (GimpImage *image, + GimpDrawable *drawable, + GimpContext *context, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (GIMP_IS_LAYER (drawable) && + gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpImage *clip_image; + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + clip_image = gimp_image_new_from_drawable (image->gimp, drawable); + g_object_set_data (G_OBJECT (clip_image), "offset-x", + GINT_TO_POINTER (off_x)); + g_object_set_data (G_OBJECT (clip_image), "offset-y", + GINT_TO_POINTER (off_y)); + gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image)); + gimp_set_clipboard_image (image->gimp, clip_image); + g_object_unref (clip_image); + + return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp)); + } + else + { + GimpBuffer *buffer; + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable), + context, FALSE, error); + + if (buffer) + { + gimp_set_clipboard_buffer (image->gimp, buffer); + g_object_unref (buffer); + + return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp)); + } + } + + return NULL; +} + +GimpBuffer * +gimp_edit_copy_visible (GimpImage *image, + GimpContext *context, + GError **error) +{ + GimpBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (image), + context, FALSE, error); + + if (buffer) + { + gimp_set_clipboard_buffer (image->gimp, buffer); + g_object_unref (buffer); + + return gimp_get_clipboard_buffer (image->gimp); + } + + return NULL; +} + +static gboolean +gimp_edit_paste_is_in_place (GimpPasteType paste_type) +{ + switch (paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_NEW_LAYER: + return FALSE; + + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: + return TRUE; + } + + g_return_val_if_reached (FALSE); +} + +static gboolean +gimp_edit_paste_is_floating (GimpPasteType paste_type) +{ + switch (paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + return TRUE; + + case GIMP_PASTE_TYPE_NEW_LAYER: + case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: + return FALSE; + } + + g_return_val_if_reached (FALSE); +} + +static GimpLayer * +gimp_edit_paste_get_layer (GimpImage *image, + GimpDrawable *drawable, + GimpObject *paste, + GimpPasteType *paste_type) +{ + GimpLayer *layer = NULL; + const Babl *floating_format; + + /* change paste type to NEW_LAYER for cases where we can't attach a + * floating selection + */ + if (! drawable || + gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) || + gimp_item_is_content_locked (GIMP_ITEM (drawable))) + { + if (gimp_edit_paste_is_in_place (*paste_type)) + *paste_type = GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE; + else + *paste_type = GIMP_PASTE_TYPE_NEW_LAYER; + } + + /* floating pastes always have the pasted-to drawable's format with + * alpha; if drawable == NULL, user is pasting into an empty image + */ + if (drawable && gimp_edit_paste_is_floating (*paste_type)) + floating_format = gimp_drawable_get_format_with_alpha (drawable); + else + floating_format = gimp_image_get_layer_format (image, TRUE); + + if (GIMP_IS_IMAGE (paste)) + { + GType layer_type; + + layer = gimp_image_get_layer_iter (GIMP_IMAGE (paste))->data; + + switch (*paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + /* when pasting as floating make sure gimp_item_convert() + * will turn group layers into normal layers, otherwise use + * the same layer type so e.g. text information gets + * preserved. See issue #2667. + */ + if (GIMP_IS_GROUP_LAYER (layer)) + layer_type = GIMP_TYPE_LAYER; + else + layer_type = G_TYPE_FROM_INSTANCE (layer); + break; + + case GIMP_PASTE_TYPE_NEW_LAYER: + case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: + layer_type = G_TYPE_FROM_INSTANCE (layer); + break; + + default: + g_return_val_if_reached (NULL); + } + + layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (layer), + image, layer_type)); + + switch (*paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + /* when pasting as floating selection, get rid of the layer mask, + * and make sure the layer has the right format + */ + if (gimp_layer_get_mask (layer)) + gimp_layer_apply_mask (layer, GIMP_MASK_DISCARD, FALSE); + + if (gimp_drawable_get_format (GIMP_DRAWABLE (layer)) != + floating_format) + { + gimp_drawable_convert_type (GIMP_DRAWABLE (layer), image, + gimp_drawable_get_base_type (drawable), + gimp_drawable_get_precision (drawable), + TRUE, + NULL, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + FALSE, NULL); + } + break; + + default: + break; + } + } + else if (GIMP_IS_BUFFER (paste)) + { + layer = gimp_layer_new_from_buffer (GIMP_BUFFER (paste), image, + floating_format, + _("Pasted Layer"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + } + + return layer; +} + +static void +gimp_edit_paste_get_viewport_offset (GimpImage *image, + GimpDrawable *drawable, + GimpObject *paste, + gint viewport_x, + gint viewport_y, + gint viewport_width, + gint viewport_height, + gint *offset_x, + gint *offset_y) +{ + gint image_width; + gint image_height; + gint width; + gint height; + gboolean clamp_to_image = TRUE; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (drawable == NULL || + gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_VIEWABLE (paste)); + g_return_if_fail (offset_x != NULL); + g_return_if_fail (offset_y != NULL); + + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + + gimp_viewable_get_size (GIMP_VIEWABLE (paste), &width, &height); + + if (viewport_width == image_width && + viewport_height == image_height) + { + /* if the whole image is visible, act as if there was no viewport */ + + viewport_x = 0; + viewport_y = 0; + viewport_width = 0; + viewport_height = 0; + } + + if (drawable) + { + /* if pasting to a drawable */ + + GimpContainer *children; + gint off_x, off_y; + gint target_x, target_y; + gint target_width, target_height; + gint paste_x, paste_y; + gint paste_width, paste_height; + gboolean have_mask; + + have_mask = ! gimp_channel_is_empty (gimp_image_get_mask (image)); + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + children = gimp_viewable_get_children (GIMP_VIEWABLE (drawable)); + + if (children && gimp_container_get_n_children (children) == 0) + { + /* treat empty layer groups as image-sized, use the selection + * as target + */ + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &target_x, &target_y, + &target_width, &target_height); + } + else + { + gimp_item_mask_intersect (GIMP_ITEM (drawable), + &target_x, &target_y, + &target_width, &target_height); + } + + if (! have_mask && /* if we have no mask */ + viewport_width > 0 && /* and we have a viewport */ + viewport_height > 0 && + (width < target_width || /* and the paste is smaller than the target */ + height < target_height) && + + /* and the viewport intersects with the target */ + gimp_rectangle_intersect (viewport_x, viewport_y, + viewport_width, viewport_height, + off_x, off_y, /* target_x,y are 0 */ + target_width, target_height, + &paste_x, &paste_y, + &paste_width, &paste_height)) + { + /* center on the viewport */ + + *offset_x = paste_x + (paste_width - width) / 2; + *offset_y = paste_y + (paste_height- height) / 2; + } + else + { + /* otherwise center on the target */ + + *offset_x = off_x + target_x + (target_width - width) / 2; + *offset_y = off_y + target_y + (target_height - height) / 2; + + /* and keep it that way */ + clamp_to_image = FALSE; + } + } + else if (viewport_width > 0 && /* if we have a viewport */ + viewport_height > 0 && + (width < image_width || /* and the paste is */ + height < image_height)) /* smaller than the image */ + { + /* center on the viewport */ + + *offset_x = viewport_x + (viewport_width - width) / 2; + *offset_y = viewport_y + (viewport_height - height) / 2; + } + else + { + /* otherwise center on the image */ + + *offset_x = (image_width - width) / 2; + *offset_y = (image_height - height) / 2; + + /* and keep it that way */ + clamp_to_image = FALSE; + } + + if (clamp_to_image) + { + /* Ensure that the pasted layer is always within the image, if it + * fits and aligned at top left if it doesn't. (See bug #142944). + */ + *offset_x = MIN (*offset_x, image_width - width); + *offset_y = MIN (*offset_y, image_height - height); + *offset_x = MAX (*offset_x, 0); + *offset_y = MAX (*offset_y, 0); + } +} + +static void +gimp_edit_paste_get_paste_offset (GimpImage *image, + GimpDrawable *drawable, + GimpObject *paste, + gint *offset_x, + gint *offset_y) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (drawable == NULL || + gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_VIEWABLE (paste)); + g_return_if_fail (offset_x != NULL); + g_return_if_fail (offset_y != NULL); + + if (GIMP_IS_IMAGE (paste)) + { + *offset_x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste), + "offset-x")); + *offset_y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paste), + "offset-y")); + } + else if (GIMP_IS_BUFFER (paste)) + { + GimpBuffer *buffer = GIMP_BUFFER (paste); + + *offset_x = buffer->offset_x; + *offset_y = buffer->offset_y; + } +} + +static GimpLayer * +gimp_edit_paste_paste (GimpImage *image, + GimpDrawable *drawable, + GimpLayer *layer, + GimpPasteType paste_type, + gint offset_x, + gint offset_y) +{ + gimp_item_translate (GIMP_ITEM (layer), offset_x, offset_y, FALSE); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, + C_("undo-type", "Paste")); + + switch (paste_type) + { + case GIMP_PASTE_TYPE_FLOATING: + case GIMP_PASTE_TYPE_FLOATING_IN_PLACE: + /* if there is a selection mask clear it - this might not + * always be desired, but in general, it seems like the correct + * behavior + */ + if (! gimp_channel_is_empty (gimp_image_get_mask (image))) + gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); + + /* fall thru */ + + case GIMP_PASTE_TYPE_FLOATING_INTO: + case GIMP_PASTE_TYPE_FLOATING_INTO_IN_PLACE: + floating_sel_attach (layer, drawable); + break; + + case GIMP_PASTE_TYPE_NEW_LAYER: + case GIMP_PASTE_TYPE_NEW_LAYER_IN_PLACE: + { + GimpLayer *parent = NULL; + gint position = 0; + + /* always add on top of a passed layer, where we would attach + * a floating selection + */ + if (GIMP_IS_LAYER (drawable)) + { + parent = gimp_layer_get_parent (GIMP_LAYER (drawable)); + position = gimp_item_get_index (GIMP_ITEM (drawable)); + } + + gimp_image_add_layer (image, layer, parent, position, TRUE); + } + break; + } + + gimp_image_undo_group_end (image); + + return layer; +} + +GimpLayer * +gimp_edit_paste (GimpImage *image, + GimpDrawable *drawable, + GimpObject *paste, + GimpPasteType paste_type, + gint viewport_x, + gint viewport_y, + gint viewport_width, + gint viewport_height) +{ + GimpLayer *layer; + gint offset_x = 0; + gint offset_y = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (drawable == NULL || + gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (paste) || GIMP_IS_BUFFER (paste), NULL); + + layer = gimp_edit_paste_get_layer (image, drawable, paste, &paste_type); + + if (! layer) + return NULL; + + if (gimp_edit_paste_is_in_place (paste_type)) + { + gimp_edit_paste_get_paste_offset (image, drawable, paste, + &offset_x, + &offset_y); + } + else + { + gimp_edit_paste_get_viewport_offset (image, drawable, GIMP_OBJECT (layer), + viewport_x, + viewport_y, + viewport_width, + viewport_height, + &offset_x, + &offset_y); + } + + return gimp_edit_paste_paste (image, drawable, layer, paste_type, + offset_x, offset_y); +} + +GimpImage * +gimp_edit_paste_as_new_image (Gimp *gimp, + GimpObject *paste) +{ + GimpImage *image = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (paste) || GIMP_IS_BUFFER (paste), NULL); + + if (GIMP_IS_IMAGE (paste)) + { + image = gimp_image_duplicate (GIMP_IMAGE (paste)); + } + else if (GIMP_IS_BUFFER (paste)) + { + image = gimp_image_new_from_buffer (gimp, GIMP_BUFFER (paste)); + } + + return image; +} + +const gchar * +gimp_edit_named_cut (GimpImage *image, + const gchar *name, + GimpDrawable *drawable, + GimpContext *context, + GError **error) +{ + GimpBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable), + context, TRUE, error); + + if (buffer) + { + gimp_object_set_name (GIMP_OBJECT (buffer), name); + gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer)); + g_object_unref (buffer); + + return gimp_object_get_name (buffer); + } + + return NULL; +} + +const gchar * +gimp_edit_named_copy (GimpImage *image, + const gchar *name, + GimpDrawable *drawable, + GimpContext *context, + GError **error) +{ + GimpBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable), + context, FALSE, error); + + if (buffer) + { + gimp_object_set_name (GIMP_OBJECT (buffer), name); + gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer)); + g_object_unref (buffer); + + return gimp_object_get_name (buffer); + } + + return NULL; +} + +const gchar * +gimp_edit_named_copy_visible (GimpImage *image, + const gchar *name, + GimpContext *context, + GError **error) +{ + GimpBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + buffer = gimp_edit_extract (image, GIMP_PICKABLE (image), + context, FALSE, error); + + if (buffer) + { + gimp_object_set_name (GIMP_OBJECT (buffer), name); + gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer)); + g_object_unref (buffer); + + return gimp_object_get_name (buffer); + } + + return NULL; +} + + +/* private functions */ + +static GimpBuffer * +gimp_edit_extract (GimpImage *image, + GimpPickable *pickable, + GimpContext *context, + gboolean cut_pixels, + GError **error) +{ + GeglBuffer *buffer; + gint offset_x; + gint offset_y; + + if (cut_pixels) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT, + C_("undo-type", "Cut")); + + /* Cut/copy the mask portion from the image */ + buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)), + pickable, context, + cut_pixels, FALSE, FALSE, + &offset_x, &offset_y, error); + + if (cut_pixels) + gimp_image_undo_group_end (image); + + if (buffer) + { + GimpBuffer *gimp_buffer; + gdouble res_x; + gdouble res_y; + + gimp_buffer = gimp_buffer_new (buffer, _("Global Buffer"), + offset_x, offset_y, FALSE); + g_object_unref (buffer); + + gimp_image_get_resolution (image, &res_x, &res_y); + gimp_buffer_set_resolution (gimp_buffer, res_x, res_y); + gimp_buffer_set_unit (gimp_buffer, gimp_image_get_unit (image)); + + if (GIMP_IS_COLOR_MANAGED (pickable)) + { + GimpColorProfile *profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (pickable)); + + if (profile) + gimp_buffer_set_color_profile (gimp_buffer, profile); + } + + return gimp_buffer; + } + + return NULL; +} diff --git a/app/core/gimp-edit.h b/app/core/gimp-edit.h new file mode 100644 index 0000000..c3d8ca5 --- /dev/null +++ b/app/core/gimp-edit.h @@ -0,0 +1,61 @@ +/* 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 . + */ + +#ifndef __GIMP_EDIT_H__ +#define __GIMP_EDIT_H__ + + +GimpObject * gimp_edit_cut (GimpImage *image, + GimpDrawable *drawable, + GimpContext *context, + GError **error); +GimpObject * gimp_edit_copy (GimpImage *image, + GimpDrawable *drawable, + GimpContext *context, + GError **error); +GimpBuffer * gimp_edit_copy_visible (GimpImage *image, + GimpContext *context, + GError **error); + +GimpLayer * gimp_edit_paste (GimpImage *image, + GimpDrawable *drawable, + GimpObject *paste, + GimpPasteType paste_type, + gint viewport_x, + gint viewport_y, + gint viewport_width, + gint viewport_height); +GimpImage * gimp_edit_paste_as_new_image (Gimp *gimp, + GimpObject *paste); + +const gchar * gimp_edit_named_cut (GimpImage *image, + const gchar *name, + GimpDrawable *drawable, + GimpContext *context, + GError **error); +const gchar * gimp_edit_named_copy (GimpImage *image, + const gchar *name, + GimpDrawable *drawable, + GimpContext *context, + GError **error); +const gchar * gimp_edit_named_copy_visible (GimpImage *image, + const gchar *name, + GimpContext *context, + GError **error); + + +#endif /* __GIMP_EDIT_H__ */ diff --git a/app/core/gimp-filter-history.c b/app/core/gimp-filter-history.c new file mode 100644 index 0000000..afdd6ed --- /dev/null +++ b/app/core/gimp-filter-history.c @@ -0,0 +1,160 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2003 Spencer Kimball and Peter Mattis + * + * gimp-filter-history.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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimp-filter-history.h" + +#include "pdb/gimpprocedure.h" + + +/* local function prototypes */ + +static gint gimp_filter_history_compare (GimpProcedure *proc1, + GimpProcedure *proc2); + + +/* public functions */ + +gint +gimp_filter_history_size (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + + return MAX (1, gimp->config->filter_history_size); +} + +gint +gimp_filter_history_length (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + + return g_list_length (gimp->filter_history); +} + +GimpProcedure * +gimp_filter_history_nth (Gimp *gimp, + gint n) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_list_nth_data (gimp->filter_history, n); +} + +void +gimp_filter_history_add (Gimp *gimp, + GimpProcedure *procedure) +{ + GList *link; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_PROCEDURE (procedure)); + + /* return early if the procedure is already at the top */ + if (gimp->filter_history && + gimp_filter_history_compare (gimp->filter_history->data, procedure) == 0) + return; + + /* ref new first then unref old, they might be the same */ + g_object_ref (procedure); + + link = g_list_find_custom (gimp->filter_history, procedure, + (GCompareFunc) gimp_filter_history_compare); + + if (link) + { + g_object_unref (link->data); + gimp->filter_history = g_list_delete_link (gimp->filter_history, link); + } + + gimp->filter_history = g_list_prepend (gimp->filter_history, procedure); + + link = g_list_nth (gimp->filter_history, gimp_filter_history_size (gimp)); + + if (link) + { + g_object_unref (link->data); + gimp->filter_history = g_list_delete_link (gimp->filter_history, link); + } + + gimp_filter_history_changed (gimp); +} + +void +gimp_filter_history_remove (Gimp *gimp, + GimpProcedure *procedure) +{ + GList *link; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_PROCEDURE (procedure)); + + link = g_list_find_custom (gimp->filter_history, procedure, + (GCompareFunc) gimp_filter_history_compare); + + if (link) + { + g_object_unref (link->data); + gimp->filter_history = g_list_delete_link (gimp->filter_history, link); + + gimp_filter_history_changed (gimp); + } +} + +void +gimp_filter_history_clear (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->filter_history) + { + g_list_free_full (gimp->filter_history, (GDestroyNotify) g_object_unref); + gimp->filter_history = NULL; + + gimp_filter_history_changed (gimp); + } +} + + +/* private functions */ + +static gint +gimp_filter_history_compare (GimpProcedure *proc1, + GimpProcedure *proc2) +{ + /* the procedures can have the same name, but could still be two + * different filters using the same operation, so also compare + * their menu labels + */ + return (gimp_procedure_name_compare (proc1, proc2) || + strcmp (gimp_procedure_get_menu_label (proc1), + gimp_procedure_get_menu_label (proc2))); +} diff --git a/app/core/gimp-filter-history.h b/app/core/gimp-filter-history.h new file mode 100644 index 0000000..e74b820 --- /dev/null +++ b/app/core/gimp-filter-history.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-filter-history.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 . + */ + +#ifndef __GIMP_FILTER_HISTORY_H__ +#define __GIMP_FILTER_HISTORY_H__ + + +gint gimp_filter_history_size (Gimp *gimp); +gint gimp_filter_history_length (Gimp *gimp); +GimpProcedure * gimp_filter_history_nth (Gimp *gimp, + gint n); +void gimp_filter_history_add (Gimp *gimp, + GimpProcedure *procedure); +void gimp_filter_history_remove (Gimp *gimp, + GimpProcedure *procedure); +void gimp_filter_history_clear (Gimp *gimp); + + +#endif /* __GIMP_FILTER_HISTORY_H__ */ diff --git a/app/core/gimp-gradients.c b/app/core/gimp-gradients.c new file mode 100644 index 0000000..71e373f --- /dev/null +++ b/app/core/gimp-gradients.c @@ -0,0 +1,183 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-gradients.c + * Copyright (C) 2002 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-gradients.h" +#include "gimpcontext.h" +#include "gimpcontainer.h" +#include "gimpdatafactory.h" +#include "gimpgradient.h" + +#include "gimp-intl.h" + + +#define CUSTOM_KEY "gimp-gradient-custom" +#define FG_BG_RGB_KEY "gimp-gradient-fg-bg-rgb" +#define FG_BG_HARDEDGE_KEY "gimp-gradient-fg-bg-rgb-hardedge" +#define FG_BG_HSV_CCW_KEY "gimp-gradient-fg-bg-hsv-ccw" +#define FG_BG_HSV_CW_KEY "gimp-gradient-fg-bg-hsv-cw" +#define FG_TRANSPARENT_KEY "gimp-gradient-fg-transparent" +#define FG_TRANSPARENT_HARDEDGE_KEY "gimp-gradient-fg-transparent-hardedge" + + +/* local function prototypes */ + +static GimpGradient * gimp_gradients_add_gradient (Gimp *gimp, + const gchar *name, + const gchar *id); + + +/* public functions */ + +void +gimp_gradients_init (Gimp *gimp) +{ + GimpGradient *gradient; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* Custom */ + gradient = gimp_gradients_add_gradient (gimp, + _("Custom"), + CUSTOM_KEY); + g_object_set (gradient, + "writable", TRUE, + NULL); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_BACKGROUND; + + /* FG to BG (RGB) */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to BG (RGB)"), + FG_BG_RGB_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_BACKGROUND; + gimp_context_set_gradient (gimp->user_context, gradient); + + /* FG to BG (Hardedge) */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to BG (Hardedge)"), + FG_BG_HARDEDGE_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_BACKGROUND; + gradient->segments->type = GIMP_GRADIENT_SEGMENT_STEP; + + /* FG to BG (HSV counter-clockwise) */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to BG (HSV counter-clockwise)"), + FG_BG_HSV_CCW_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_BACKGROUND; + gradient->segments->color = GIMP_GRADIENT_SEGMENT_HSV_CCW; + + /* FG to BG (HSV clockwise hue) */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to BG (HSV clockwise hue)"), + FG_BG_HSV_CW_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_BACKGROUND; + gradient->segments->color = GIMP_GRADIENT_SEGMENT_HSV_CW; + + /* FG to Transparent */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to Transparent"), + FG_TRANSPARENT_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT; + + /* FG to Transparent (Hardedge) */ + gradient = gimp_gradients_add_gradient (gimp, + _("FG to Transparent (Hardedge)"), + FG_TRANSPARENT_HARDEDGE_KEY); + gradient->segments->left_color_type = GIMP_GRADIENT_COLOR_FOREGROUND; + gradient->segments->right_color_type = GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT; + gradient->segments->type = GIMP_GRADIENT_SEGMENT_STEP; +} + +GimpGradient * +gimp_gradients_get_custom (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), CUSTOM_KEY); +} + +GimpGradient * +gimp_gradients_get_fg_bg_rgb (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), FG_BG_RGB_KEY); +} + +GimpGradient * +gimp_gradients_get_fg_bg_hsv_ccw (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), FG_BG_HSV_CCW_KEY); +} + +GimpGradient * +gimp_gradients_get_fg_bg_hsv_cw (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), FG_BG_HSV_CW_KEY); +} + +GimpGradient * +gimp_gradients_get_fg_transparent (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), FG_TRANSPARENT_KEY); +} + + +/* private functions */ + +static GimpGradient * +gimp_gradients_add_gradient (Gimp *gimp, + const gchar *name, + const gchar *id) +{ + GimpGradient *gradient; + + gradient = GIMP_GRADIENT (gimp_gradient_new (gimp_get_user_context (gimp), + name)); + + gimp_data_make_internal (GIMP_DATA (gradient), id); + + gimp_container_add (gimp_data_factory_get_container (gimp->gradient_factory), + GIMP_OBJECT (gradient)); + g_object_unref (gradient); + + g_object_set_data (G_OBJECT (gimp), id, gradient); + + return gradient; +} diff --git a/app/core/gimp-gradients.h b/app/core/gimp-gradients.h new file mode 100644 index 0000000..72f21a6 --- /dev/null +++ b/app/core/gimp-gradients.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-gradients.h + * Copyright (C) 2002 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GRADIENTS__ +#define __GIMP_GRADIENTS__ + + +void gimp_gradients_init (Gimp *gimp); + +GimpGradient * gimp_gradients_get_custom (Gimp *gimp); +GimpGradient * gimp_gradients_get_fg_bg_rgb (Gimp *gimp); +GimpGradient * gimp_gradients_get_fg_bg_hsv_ccw (Gimp *gimp); +GimpGradient * gimp_gradients_get_fg_bg_hsv_cw (Gimp *gimp); +GimpGradient * gimp_gradients_get_fg_transparent (Gimp *gimp); + + +#endif /* __GIMP_GRADIENTS__ */ diff --git a/app/core/gimp-gui.c b/app/core/gimp-gui.c new file mode 100644 index 0000000..0b0fe42 --- /dev/null +++ b/app/core/gimp-gui.c @@ -0,0 +1,585 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-gui.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpimage.h" +#include "gimpprogress.h" +#include "gimpwaitable.h" + +#include "about.h" + +#include "gimp-intl.h" + + +void +gimp_gui_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp->gui.ungrab = NULL; + gimp->gui.threads_enter = NULL; + gimp->gui.threads_leave = NULL; + gimp->gui.set_busy = NULL; + gimp->gui.unset_busy = NULL; + gimp->gui.show_message = NULL; + gimp->gui.help = NULL; + gimp->gui.get_program_class = NULL; + gimp->gui.get_display_name = NULL; + gimp->gui.get_user_time = NULL; + gimp->gui.get_theme_dir = NULL; + gimp->gui.get_icon_theme_dir = NULL; + gimp->gui.display_get_by_id = NULL; + gimp->gui.display_get_id = NULL; + gimp->gui.display_get_window_id = NULL; + gimp->gui.display_create = NULL; + gimp->gui.display_delete = NULL; + gimp->gui.displays_reconnect = NULL; + gimp->gui.progress_new = NULL; + gimp->gui.progress_free = NULL; + gimp->gui.pdb_dialog_set = NULL; + gimp->gui.pdb_dialog_close = NULL; + gimp->gui.recent_list_add_file = NULL; + gimp->gui.recent_list_load = NULL; + gimp->gui.get_mount_operation = NULL; + gimp->gui.query_profile_policy = NULL; +} + +void +gimp_gui_ungrab (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->gui.ungrab) + gimp->gui.ungrab (gimp); +} + +void +gimp_threads_enter (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->gui.threads_enter) + gimp->gui.threads_enter (gimp); +} + +void +gimp_threads_leave (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->gui.threads_leave) + gimp->gui.threads_leave (gimp); +} + +void +gimp_set_busy (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* FIXME: gimp_busy HACK */ + gimp->busy++; + + if (gimp->busy == 1) + { + if (gimp->gui.set_busy) + gimp->gui.set_busy (gimp); + } +} + +static gboolean +gimp_idle_unset_busy (gpointer data) +{ + Gimp *gimp = data; + + gimp_unset_busy (gimp); + + gimp->busy_idle_id = 0; + + return FALSE; +} + +void +gimp_set_busy_until_idle (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (! gimp->busy_idle_id) + { + gimp_set_busy (gimp); + + gimp->busy_idle_id = g_idle_add_full (G_PRIORITY_HIGH, + gimp_idle_unset_busy, gimp, + NULL); + } +} + +void +gimp_unset_busy (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (gimp->busy > 0); + + /* FIXME: gimp_busy HACK */ + gimp->busy--; + + if (gimp->busy == 0) + { + if (gimp->gui.unset_busy) + gimp->gui.unset_busy (gimp); + } +} + +void +gimp_show_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + const gchar *desc = (severity == GIMP_MESSAGE_ERROR) ? "Error" : "Message"; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (handler == NULL || G_IS_OBJECT (handler)); + g_return_if_fail (message != NULL); + + if (! domain) + domain = GIMP_ACRONYM; + + if (! gimp->console_messages) + { + if (gimp->gui.show_message) + { + gimp->gui.show_message (gimp, handler, severity, + domain, message); + return; + } + else if (GIMP_IS_PROGRESS (handler) && + gimp_progress_message (GIMP_PROGRESS (handler), gimp, + severity, domain, message)) + { + /* message has been handled by GimpProgress */ + return; + } + } + + gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity, + NULL, NULL, &desc, NULL); + g_printerr ("%s-%s: %s\n\n", domain, desc, message); +} + +void +gimp_wait (Gimp *gimp, + GimpWaitable *waitable, + const gchar *format, + ...) +{ + va_list args; + gchar *message; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_WAITABLE (waitable)); + g_return_if_fail (format != NULL); + + if (gimp_waitable_wait_for (waitable, 0.5 * G_TIME_SPAN_SECOND)) + return; + + va_start (args, format); + + message = g_strdup_vprintf (format, args); + + va_end (args); + + if (! gimp->console_messages && + gimp->gui.wait && + gimp->gui.wait (gimp, waitable, message)) + { + return; + } + + /* Translator: This message is displayed while GIMP is waiting for + * some operation to finish. The %s argument is a message describing + * the operation. + */ + g_printerr (_("Please wait: %s\n"), message); + + gimp_waitable_wait (waitable); + + g_free (message); +} + +void +gimp_help (Gimp *gimp, + GimpProgress *progress, + const gchar *help_domain, + const gchar *help_id) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (gimp->gui.help) + gimp->gui.help (gimp, progress, help_domain, help_id); +} + +const gchar * +gimp_get_program_class (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.get_program_class) + return gimp->gui.get_program_class (gimp); + + return NULL; +} + +gchar * +gimp_get_display_name (Gimp *gimp, + gint display_ID, + GObject **screen, + gint *monitor) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (screen != NULL, NULL); + g_return_val_if_fail (monitor != NULL, NULL); + + if (gimp->gui.get_display_name) + return gimp->gui.get_display_name (gimp, display_ID, screen, monitor); + + *screen = NULL; + *monitor = 0; + + return NULL; +} + +/** + * gimp_get_user_time: + * @gimp: + * + * Returns the timestamp of the last user interaction. The timestamp is + * taken from events caused by user interaction such as key presses or + * pointer movements. See gdk_x11_display_get_user_time(). + * + * Return value: the timestamp of the last user interaction + */ +guint32 +gimp_get_user_time (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + + if (gimp->gui.get_user_time) + return gimp->gui.get_user_time (gimp); + + return 0; +} + +GFile * +gimp_get_theme_dir (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.get_theme_dir) + return gimp->gui.get_theme_dir (gimp); + + return NULL; +} + +GFile * +gimp_get_icon_theme_dir (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.get_icon_theme_dir) + return gimp->gui.get_icon_theme_dir (gimp); + + return NULL; +} + +GimpObject * +gimp_get_window_strategy (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.get_window_strategy) + return gimp->gui.get_window_strategy (gimp); + + return NULL; +} + +GimpObject * +gimp_get_empty_display (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.get_empty_display) + return gimp->gui.get_empty_display (gimp); + + return NULL; +} + +GimpObject * +gimp_get_display_by_ID (Gimp *gimp, + gint ID) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->gui.display_get_by_id) + return gimp->gui.display_get_by_id (gimp, ID); + + return NULL; +} + +gint +gimp_get_display_ID (Gimp *gimp, + GimpObject *display) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), -1); + g_return_val_if_fail (GIMP_IS_OBJECT (display), -1); + + if (gimp->gui.display_get_id) + return gimp->gui.display_get_id (display); + + return -1; +} + +guint32 +gimp_get_display_window_id (Gimp *gimp, + GimpObject *display) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), -1); + g_return_val_if_fail (GIMP_IS_OBJECT (display), -1); + + if (gimp->gui.display_get_window_id) + return gimp->gui.display_get_window_id (display); + + return -1; +} + +GimpObject * +gimp_create_display (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GObject *screen, + gint monitor) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (screen == NULL || G_IS_OBJECT (screen), NULL); + + if (gimp->gui.display_create) + return gimp->gui.display_create (gimp, image, unit, scale, screen, monitor); + + return NULL; +} + +void +gimp_delete_display (Gimp *gimp, + GimpObject *display) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_OBJECT (display)); + + if (gimp->gui.display_delete) + gimp->gui.display_delete (display); +} + +void +gimp_reconnect_displays (Gimp *gimp, + GimpImage *old_image, + GimpImage *new_image) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_IMAGE (old_image)); + g_return_if_fail (GIMP_IS_IMAGE (new_image)); + + if (gimp->gui.displays_reconnect) + gimp->gui.displays_reconnect (gimp, old_image, new_image); +} + +GimpProgress * +gimp_new_progress (Gimp *gimp, + GimpObject *display) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (display == NULL || GIMP_IS_OBJECT (display), NULL); + + if (gimp->gui.progress_new) + return gimp->gui.progress_new (gimp, display); + + return NULL; +} + +void +gimp_free_progress (Gimp *gimp, + GimpProgress *progress) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + + if (gimp->gui.progress_free) + gimp->gui.progress_free (gimp, progress); +} + +gboolean +gimp_pdb_dialog_new (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpContainer *container, + const gchar *title, + const gchar *callback_name, + const gchar *object_name, + ...) +{ + gboolean retval = FALSE; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (title != NULL, FALSE); + g_return_val_if_fail (callback_name != NULL, FALSE); + + if (gimp->gui.pdb_dialog_new) + { + va_list args; + + va_start (args, object_name); + + retval = gimp->gui.pdb_dialog_new (gimp, context, progress, + container, title, + callback_name, object_name, + args); + + va_end (args); + } + + return retval; +} + +gboolean +gimp_pdb_dialog_set (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name, + const gchar *object_name, + ...) +{ + gboolean retval = FALSE; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (callback_name != NULL, FALSE); + g_return_val_if_fail (object_name != NULL, FALSE); + + if (gimp->gui.pdb_dialog_set) + { + va_list args; + + va_start (args, object_name); + + retval = gimp->gui.pdb_dialog_set (gimp, container, callback_name, + object_name, args); + + va_end (args); + } + + return retval; +} + +gboolean +gimp_pdb_dialog_close (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (callback_name != NULL, FALSE); + + if (gimp->gui.pdb_dialog_close) + return gimp->gui.pdb_dialog_close (gimp, container, callback_name); + + return FALSE; +} + +gboolean +gimp_recent_list_add_file (Gimp *gimp, + GFile *file, + const gchar *mime_type) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + + if (gimp->gui.recent_list_add_file) + return gimp->gui.recent_list_add_file (gimp, file, mime_type); + + return FALSE; +} + +void +gimp_recent_list_load (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->gui.recent_list_load) + gimp->gui.recent_list_load (gimp); +} + +GMountOperation * +gimp_get_mount_operation (Gimp *gimp, + GimpProgress *progress) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + + if (gimp->gui.get_mount_operation) + return gimp->gui.get_mount_operation (gimp, progress); + + return g_mount_operation_new (); +} + +GimpColorProfilePolicy +gimp_query_profile_policy (Gimp *gimp, + GimpImage *image, + GimpContext *context, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (dest_profile != NULL, GIMP_COLOR_PROFILE_POLICY_KEEP); + + if (gimp->gui.query_profile_policy) + return gimp->gui.query_profile_policy (gimp, image, context, + dest_profile, + intent, bpc, + dont_ask); + + return GIMP_COLOR_PROFILE_POLICY_KEEP; +} diff --git a/app/core/gimp-gui.h b/app/core/gimp-gui.h new file mode 100644 index 0000000..49f2761 --- /dev/null +++ b/app/core/gimp-gui.h @@ -0,0 +1,211 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_GUI_H__ +#define __GIMP_GUI_H__ + + +typedef struct _GimpGui GimpGui; + +struct _GimpGui +{ + void (* ungrab) (Gimp *gimp); + + void (* threads_enter) (Gimp *gimp); + void (* threads_leave) (Gimp *gimp); + + void (* set_busy) (Gimp *gimp); + void (* unset_busy) (Gimp *gimp); + + void (* show_message) (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + void (* help) (Gimp *gimp, + GimpProgress *progress, + const gchar *help_domain, + const gchar *help_id); + + gboolean (* wait) (Gimp *gimp, + GimpWaitable *waitable, + const gchar *message); + + const gchar * (* get_program_class) (Gimp *gimp); + gchar * (* get_display_name) (Gimp *gimp, + gint display_ID, + GObject **screen, + gint *monitor); + guint32 (* get_user_time) (Gimp *gimp); + + GFile * (* get_theme_dir) (Gimp *gimp); + GFile * (* get_icon_theme_dir) (Gimp *gimp); + + GimpObject * (* get_window_strategy) (Gimp *gimp); + GimpObject * (* get_empty_display) (Gimp *gimp); + GimpObject * (* display_get_by_id) (Gimp *gimp, + gint ID); + gint (* display_get_id) (GimpObject *display); + guint32 (* display_get_window_id) (GimpObject *display); + GimpObject * (* display_create) (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GObject *screen, + gint monitor); + void (* display_delete) (GimpObject *display); + void (* displays_reconnect) (Gimp *gimp, + GimpImage *old_image, + GimpImage *new_image); + + GimpProgress * (* progress_new) (Gimp *gimp, + GimpObject *display); + void (* progress_free) (Gimp *gimp, + GimpProgress *progress); + + gboolean (* pdb_dialog_new) (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpContainer *container, + const gchar *title, + const gchar *callback_name, + const gchar *object_name, + va_list args); + gboolean (* pdb_dialog_set) (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name, + const gchar *object_name, + va_list args); + gboolean (* pdb_dialog_close) (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name); + gboolean (* recent_list_add_file) (Gimp *gimp, + GFile *file, + const gchar *mime_type); + void (* recent_list_load) (Gimp *gimp); + + GMountOperation + * (* get_mount_operation) (Gimp *gimp, + GimpProgress *progress); + + GimpColorProfilePolicy + (* query_profile_policy) (Gimp *gimp, + GimpImage *image, + GimpContext *context, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask); +}; + + +void gimp_gui_init (Gimp *gimp); + +void gimp_gui_ungrab (Gimp *gimp); + +void gimp_threads_enter (Gimp *gimp); +void gimp_threads_leave (Gimp *gimp); + +GimpObject * gimp_get_window_strategy (Gimp *gimp); +GimpObject * gimp_get_empty_display (Gimp *gimp); +GimpObject * gimp_get_display_by_ID (Gimp *gimp, + gint ID); +gint gimp_get_display_ID (Gimp *gimp, + GimpObject *display); +guint32 gimp_get_display_window_id (Gimp *gimp, + GimpObject *display); +GimpObject * gimp_create_display (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GObject *screen, + gint monitor); +void gimp_delete_display (Gimp *gimp, + GimpObject *display); +void gimp_reconnect_displays (Gimp *gimp, + GimpImage *old_image, + GimpImage *new_image); + +void gimp_set_busy (Gimp *gimp); +void gimp_set_busy_until_idle (Gimp *gimp); +void gimp_unset_busy (Gimp *gimp); + +void gimp_show_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); +void gimp_help (Gimp *gimp, + GimpProgress *progress, + const gchar *help_domain, + const gchar *help_id); + +void gimp_wait (Gimp *gimp, + GimpWaitable *waitable, + const gchar *format, + ...) G_GNUC_PRINTF (3, 4); + +GimpProgress * gimp_new_progress (Gimp *gimp, + GimpObject *display); +void gimp_free_progress (Gimp *gimp, + GimpProgress *progress); + +const gchar * gimp_get_program_class (Gimp *gimp); +gchar * gimp_get_display_name (Gimp *gimp, + gint display_ID, + GObject **screen, + gint *monitor); +guint32 gimp_get_user_time (Gimp *gimp); +GFile * gimp_get_theme_dir (Gimp *gimp); +GFile * gimp_get_icon_theme_dir (Gimp *gimp); + +gboolean gimp_pdb_dialog_new (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpContainer *container, + const gchar *title, + const gchar *callback_name, + const gchar *object_name, + ...) G_GNUC_NULL_TERMINATED; +gboolean gimp_pdb_dialog_set (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name, + const gchar *object_name, + ...) G_GNUC_NULL_TERMINATED; +gboolean gimp_pdb_dialog_close (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name); +gboolean gimp_recent_list_add_file (Gimp *gimp, + GFile *file, + const gchar *mime_type); +void gimp_recent_list_load (Gimp *gimp); + +GMountOperation + * gimp_get_mount_operation (Gimp *gimp, + GimpProgress *progress); + +GimpColorProfilePolicy + gimp_query_profile_policy (Gimp *gimp, + GimpImage *image, + GimpContext *context, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask); + + +#endif /* __GIMP_GUI_H__ */ diff --git a/app/core/gimp-internal-data.c b/app/core/gimp-internal-data.c new file mode 100644 index 0000000..6653946 --- /dev/null +++ b/app/core/gimp-internal-data.c @@ -0,0 +1,346 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-internal-data.c + * Copyright (C) 2017 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-gradients.h" +#include "gimp-internal-data.h" +#include "gimpdata.h" +#include "gimpdataloaderfactory.h" +#include "gimperror.h" +#include "gimpgradient-load.h" + +#include "gimp-intl.h" + + +#define GIMP_INTERNAL_DATA_DIRECTORY "internal-data" + + +typedef GimpData * (* GimpDataGetFunc) (Gimp *gimp); + + +typedef struct _GimpInternalDataFile GimpInternalDataFile; + +struct _GimpInternalDataFile +{ + const gchar *name; + GimpDataGetFunc get_func; + GimpDataLoadFunc load_func; +}; + + +/* local function prototypes */ + +static gboolean gimp_internal_data_create_directory (GError **error); + +static GFile * gimp_internal_data_get_file (const GimpInternalDataFile *data_file); + +static gboolean gimp_internal_data_load_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error); +static gboolean gimp_internal_data_save_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error); +static gboolean gimp_internal_data_delete_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error); + +/* static variables */ + +static const GimpInternalDataFile internal_data_files[] = +{ + /* Custom gradient */ + { + .name = "custom" GIMP_GRADIENT_FILE_EXTENSION, + .get_func = (GimpDataGetFunc) gimp_gradients_get_custom, + .load_func = gimp_gradient_load + } +}; + + +/* public functions */ + +gboolean +gimp_internal_data_load (Gimp *gimp, + GError **error) +{ + gint i; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++) + { + const GimpInternalDataFile *data_file = &internal_data_files[i]; + + if (! gimp_internal_data_load_data_file (gimp, data_file, error)) + return FALSE; + } + + return TRUE; +} + +gboolean +gimp_internal_data_save (Gimp *gimp, + GError **error) +{ + gint i; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (! gimp_internal_data_create_directory (error)) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++) + { + const GimpInternalDataFile *data_file = &internal_data_files[i]; + + if (! gimp_internal_data_save_data_file (gimp, data_file, error)) + return FALSE; + } + + return TRUE; +} + +gboolean +gimp_internal_data_clear (Gimp *gimp, + GError **error) +{ + gint i; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++) + { + const GimpInternalDataFile *data_file = &internal_data_files[i]; + + if (! gimp_internal_data_delete_data_file (gimp, data_file, error)) + return FALSE; + } + + return TRUE; +} + + +/* private functions */ + +static gboolean +gimp_internal_data_create_directory (GError **error) +{ + GFile *directory; + GError *my_error = NULL; + gboolean success; + + directory = gimp_directory_file (GIMP_INTERNAL_DATA_DIRECTORY, NULL); + + success = g_file_make_directory (directory, NULL, &my_error); + + g_object_unref (directory); + + if (! success) + { + if (my_error->code == G_IO_ERROR_EXISTS) + { + g_clear_error (&my_error); + success = TRUE; + } + else + { + g_propagate_error (error, my_error); + } + } + + return success; +} + +static GFile * +gimp_internal_data_get_file (const GimpInternalDataFile *data_file) +{ + return gimp_directory_file (GIMP_INTERNAL_DATA_DIRECTORY, + data_file->name, + NULL); +} + +static gboolean +gimp_internal_data_load_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error) +{ + GFile *file; + GInputStream *input; + GimpData *data; + GList *list; + GError *my_error = NULL; + + file = gimp_internal_data_get_file (data_file); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + + if (! input) + { + g_object_unref (file); + + if (my_error->code == G_IO_ERROR_NOT_FOUND) + { + g_clear_error (&my_error); + return TRUE; + } + else + { + g_propagate_error (error, my_error); + return FALSE; + } + } + + list = data_file->load_func (gimp->user_context, file, input, error); + + g_object_unref (input); + g_object_unref (file); + + if (! list) + return FALSE; + + data = data_file->get_func (gimp); + + gimp_data_copy (data, GIMP_DATA (list->data)); + + g_list_free_full (list, g_object_unref); + + return TRUE; +} + +static gboolean +gimp_internal_data_save_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error) +{ + GFile *file; + GOutputStream *output; + GimpData *data; + gboolean success; + + file = gimp_internal_data_get_file (data_file); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, + G_FILE_CREATE_NONE, + NULL, error)); + + if (! output) + { + g_object_unref (file); + + return FALSE; + } + + data = data_file->get_func (gimp); + + /* we bypass gimp_data_save() and call the data's save() virtual function + * directly, since gimp_data_save() is a nop for internal data. + * + * FIXME: we save the data whether it's dirty or not, since it might not + * exist on disk. currently, we only use this for cheap data, such as + * gradients, so this is not a big concern, but if we save more expensive + * data in the future, we should fix this. + */ + gimp_assert (GIMP_DATA_GET_CLASS (data)->save); + success = GIMP_DATA_GET_CLASS (data)->save (data, output, error); + + if (success) + { + if (! g_output_stream_close (output, NULL, error)) + { + g_prefix_error (error, + _("Error saving '%s': "), + gimp_file_get_utf8_name (file)); + success = FALSE; + } + } + else + { + GCancellable *cancellable = g_cancellable_new (); + + g_cancellable_cancel (cancellable); + if (error && *error) + { + g_prefix_error (error, + _("Error saving '%s': "), + gimp_file_get_utf8_name (file)); + } + else + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_WRITE, + _("Error saving '%s'"), + gimp_file_get_utf8_name (file)); + } + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + } + + g_object_unref (output); + g_object_unref (file); + + return success; +} + +static gboolean +gimp_internal_data_delete_data_file (Gimp *gimp, + const GimpInternalDataFile *data_file, + GError **error) +{ + GFile *file; + GError *my_error = NULL; + gboolean success = TRUE; + + file = gimp_internal_data_get_file (data_file); + + if (gimp->be_verbose) + g_print ("Deleting '%s'\n", gimp_file_get_utf8_name (file)); + + if (! g_file_delete (file, NULL, &my_error) && + my_error->code != G_IO_ERROR_NOT_FOUND) + { + success = FALSE; + + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("Deleting \"%s\" failed: %s"), + gimp_file_get_utf8_name (file), my_error->message); + } + + g_clear_error (&my_error); + g_object_unref (file); + + return success; +} diff --git a/app/core/gimp-internal-data.h b/app/core/gimp-internal-data.h new file mode 100644 index 0000000..93f42dd --- /dev/null +++ b/app/core/gimp-internal-data.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-internal-data.h + * Copyright (C) 2017 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 . + */ + +#ifndef __GIMP_INTERNAL_DATA__ +#define __GIMP_INTERNAL_DATA__ + + +gboolean gimp_internal_data_load (Gimp *gimp, + GError **error); +gboolean gimp_internal_data_save (Gimp *gimp, + GError **error); + +gboolean gimp_internal_data_clear (Gimp *gimp, + GError **error); + + +#endif /* __GIMP_INTERNAL_DATA__ */ diff --git a/app/core/gimp-memsize.c b/app/core/gimp-memsize.c new file mode 100644 index 0000000..df71737 --- /dev/null +++ b/app/core/gimp-memsize.c @@ -0,0 +1,341 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpparamspecs.h" + + +gint64 +gimp_g_type_instance_get_memsize (GTypeInstance *instance) +{ + if (instance) + { + GTypeQuery type_query; + + g_type_query (G_TYPE_FROM_INSTANCE (instance), &type_query); + + return type_query.instance_size; + } + + return 0; +} + +gint64 +gimp_g_object_get_memsize (GObject *object) +{ + if (object) + return gimp_g_type_instance_get_memsize ((GTypeInstance *) object); + + return 0; +} + +gint64 +gimp_g_hash_table_get_memsize (GHashTable *hash, + gint64 data_size) +{ + if (hash) + return (2 * sizeof (gint) + + 5 * sizeof (gpointer) + + g_hash_table_size (hash) * (3 * sizeof (gpointer) + data_size)); + + return 0; +} + +typedef struct +{ + GimpMemsizeFunc func; + gint64 memsize; + gint64 gui_size; +} HashMemsize; + +static void +hash_memsize_foreach (gpointer key, + gpointer value, + HashMemsize *memsize) +{ + gint64 gui_size = 0; + + memsize->memsize += memsize->func (value, &gui_size); + memsize->gui_size += gui_size; +} + +gint64 +gimp_g_hash_table_get_memsize_foreach (GHashTable *hash, + GimpMemsizeFunc func, + gint64 *gui_size) +{ + HashMemsize memsize; + + g_return_val_if_fail (func != NULL, 0); + + if (! hash) + return 0; + + memsize.func = func; + memsize.memsize = 0; + memsize.gui_size = 0; + + g_hash_table_foreach (hash, (GHFunc) hash_memsize_foreach, &memsize); + + if (gui_size) + *gui_size = memsize.gui_size; + + return memsize.memsize + gimp_g_hash_table_get_memsize (hash, 0); +} + +gint64 +gimp_g_slist_get_memsize (GSList *slist, + gint64 data_size) +{ + return g_slist_length (slist) * (data_size + sizeof (GSList)); +} + +gint64 +gimp_g_slist_get_memsize_foreach (GSList *slist, + GimpMemsizeFunc func, + gint64 *gui_size) +{ + GSList *l; + gint64 memsize = 0; + + g_return_val_if_fail (func != NULL, 0); + + for (l = slist; l; l = g_slist_next (l)) + memsize += sizeof (GSList) + func (l->data, gui_size); + + return memsize; +} + +gint64 +gimp_g_list_get_memsize (GList *list, + gint64 data_size) +{ + return g_list_length (list) * (data_size + sizeof (GList)); +} + +gint64 +gimp_g_list_get_memsize_foreach (GList *list, + GimpMemsizeFunc func, + gint64 *gui_size) +{ + GList *l; + gint64 memsize = 0; + + g_return_val_if_fail (func != NULL, 0); + + for (l = list; l; l = g_list_next (l)) + memsize += sizeof (GList) + func (l->data, gui_size); + + return memsize; +} + +gint64 +gimp_g_queue_get_memsize (GQueue *queue, + gint64 data_size) +{ + if (queue) + { + return sizeof (GQueue) + + g_queue_get_length (queue) * (data_size + sizeof (GList)); + } + + return 0; +} + +gint64 +gimp_g_queue_get_memsize_foreach (GQueue *queue, + GimpMemsizeFunc func, + gint64 *gui_size) +{ + gint64 memsize = 0; + + g_return_val_if_fail (func != NULL, 0); + + if (queue) + { + GList *l; + + memsize = sizeof (GQueue); + + for (l = queue->head; l; l = g_list_next (l)) + memsize += sizeof (GList) + func (l->data, gui_size); + } + + return memsize; +} + +gint64 +gimp_g_value_get_memsize (GValue *value) +{ + gint64 memsize = 0; + + if (! value) + return 0; + + if (G_VALUE_HOLDS_STRING (value)) + { + memsize += gimp_string_get_memsize (g_value_get_string (value)); + } + else if (G_VALUE_HOLDS_BOXED (value)) + { + if (GIMP_VALUE_HOLDS_RGB (value)) + { + memsize += sizeof (GimpRGB); + } + else if (GIMP_VALUE_HOLDS_MATRIX2 (value)) + { + memsize += sizeof (GimpMatrix2); + } + else if (GIMP_VALUE_HOLDS_PARASITE (value)) + { + memsize += gimp_parasite_get_memsize (g_value_get_boxed (value), + NULL); + } + else if (GIMP_VALUE_HOLDS_ARRAY (value) || + GIMP_VALUE_HOLDS_INT8_ARRAY (value) || + GIMP_VALUE_HOLDS_INT16_ARRAY (value) || + GIMP_VALUE_HOLDS_INT32_ARRAY (value) || + GIMP_VALUE_HOLDS_FLOAT_ARRAY (value)) + { + GimpArray *array = g_value_get_boxed (value); + + if (array) + memsize += sizeof (GimpArray) + + (array->static_data ? 0 : array->length); + } + else if (GIMP_VALUE_HOLDS_STRING_ARRAY (value)) + { + GimpArray *array = g_value_get_boxed (value); + + if (array) + { + memsize += sizeof (GimpArray); + + if (! array->static_data) + { + gchar **tmp = (gchar **) array->data; + gint i; + + memsize += array->length * sizeof (gchar *); + + for (i = 0; i < array->length; i++) + memsize += gimp_string_get_memsize (tmp[i]); + } + } + } + else + { + g_printerr ("%s: unhandled boxed value type: %s\n", + G_STRFUNC, G_VALUE_TYPE_NAME (value)); + } + } + else if (G_VALUE_HOLDS_OBJECT (value)) + { + g_printerr ("%s: unhandled object value type: %s\n", + G_STRFUNC, G_VALUE_TYPE_NAME (value)); + } + + return memsize + sizeof (GValue); +} + +gint64 +gimp_g_param_spec_get_memsize (GParamSpec *pspec) +{ + gint64 memsize = 0; + + if (! pspec) + return 0; + + if (! (pspec->flags & G_PARAM_STATIC_NAME)) + memsize += gimp_string_get_memsize (g_param_spec_get_name (pspec)); + + if (! (pspec->flags & G_PARAM_STATIC_NICK)) + memsize += gimp_string_get_memsize (g_param_spec_get_nick (pspec)); + + if (! (pspec->flags & G_PARAM_STATIC_BLURB)) + memsize += gimp_string_get_memsize (g_param_spec_get_blurb (pspec)); + + return memsize + gimp_g_type_instance_get_memsize ((GTypeInstance *) pspec); +} + +gint64 +gimp_gegl_buffer_get_memsize (GeglBuffer *buffer) +{ + if (buffer) + { + const Babl *format = gegl_buffer_get_format (buffer); + + return ((gint64) babl_format_get_bytes_per_pixel (format) * + (gint64) gegl_buffer_get_width (buffer) * + (gint64) gegl_buffer_get_height (buffer) + + gimp_g_object_get_memsize (G_OBJECT (buffer))); + } + + return 0; +} + +gint64 +gimp_gegl_pyramid_get_memsize (GeglBuffer *buffer) +{ + if (buffer) + { + const Babl *format = gegl_buffer_get_format (buffer); + + /* The pyramid levels constitute a geometric sum with a ratio of 1/4. */ + return ((gint64) babl_format_get_bytes_per_pixel (format) * + (gint64) gegl_buffer_get_width (buffer) * + (gint64) gegl_buffer_get_height (buffer) * 1.33 + + gimp_g_object_get_memsize (G_OBJECT (buffer))); + } + + return 0; +} + +gint64 +gimp_string_get_memsize (const gchar *string) +{ + if (string) + return strlen (string) + 1; + + return 0; +} + +gint64 +gimp_parasite_get_memsize (GimpParasite *parasite, + gint64 *gui_size) +{ + if (parasite) + return (sizeof (GimpParasite) + + gimp_string_get_memsize (parasite->name) + + parasite->size); + + return 0; +} diff --git a/app/core/gimp-memsize.h b/app/core/gimp-memsize.h new file mode 100644 index 0000000..139dc02 --- /dev/null +++ b/app/core/gimp-memsize.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 . + */ + +#ifndef __APP_GIMP_MEMSIZE_H__ +#define __APP_GIMP_MEMSIZE_H__ + + +gint64 gimp_g_type_instance_get_memsize (GTypeInstance *instance); +gint64 gimp_g_object_get_memsize (GObject *object); + +gint64 gimp_g_hash_table_get_memsize (GHashTable *hash, + gint64 data_size); +gint64 gimp_g_hash_table_get_memsize_foreach (GHashTable *hash, + GimpMemsizeFunc func, + gint64 *gui_size); + +gint64 gimp_g_slist_get_memsize (GSList *slist, + gint64 data_size); +gint64 gimp_g_slist_get_memsize_foreach (GSList *slist, + GimpMemsizeFunc func, + gint64 *gui_size); + +gint64 gimp_g_list_get_memsize (GList *list, + gint64 data_size); +gint64 gimp_g_list_get_memsize_foreach (GList *list, + GimpMemsizeFunc func, + gint64 *gui_size); + +gint64 gimp_g_queue_get_memsize (GQueue *queue, + gint64 data_size); +gint64 gimp_g_queue_get_memsize_foreach (GQueue *queue, + GimpMemsizeFunc func, + gint64 *gui_size); + +gint64 gimp_g_value_get_memsize (GValue *value); +gint64 gimp_g_param_spec_get_memsize (GParamSpec *pspec); + +gint64 gimp_gegl_buffer_get_memsize (GeglBuffer *buffer); +gint64 gimp_gegl_pyramid_get_memsize (GeglBuffer *buffer); + +gint64 gimp_string_get_memsize (const gchar *string); +gint64 gimp_parasite_get_memsize (GimpParasite *parasite, + gint64 *gui_size); + + +#endif /* __APP_GIMP_MEMSIZE_H__ */ diff --git a/app/core/gimp-modules.c b/app/core/gimp-modules.c new file mode 100644 index 0000000..6a7b30d --- /dev/null +++ b/app/core/gimp-modules.c @@ -0,0 +1,227 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmodules.c + * (C) 1999 Austin Donnelly + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmodule/gimpmodule.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimp-modules.h" + +#include "gimp-intl.h" + + +void +gimp_modules_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (! gimp->no_interface) + { + gimp->module_db = gimp_module_db_new (gimp->be_verbose); + gimp->write_modulerc = FALSE; + } +} + +void +gimp_modules_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + g_clear_object (&gimp->module_db); +} + +void +gimp_modules_load (Gimp *gimp) +{ + GFile *file; + GScanner *scanner; + gchar *module_load_inhibit = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->no_interface) + return; + + /* FIXME, gimp->be_verbose is not yet initialized in init() */ + gimp->module_db->verbose = gimp->be_verbose; + + file = gimp_directory_file ("modulerc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + scanner = gimp_scanner_new_gfile (file, NULL); + g_object_unref (file); + + if (scanner) + { + GTokenType token; + GError *error = NULL; + +#define MODULE_LOAD_INHIBIT 1 + + g_scanner_scope_add_symbol (scanner, 0, "module-load-inhibit", + GINT_TO_POINTER (MODULE_LOAD_INHIBIT)); + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + if (scanner->value.v_symbol == GINT_TO_POINTER (MODULE_LOAD_INHIBIT)) + { + token = G_TOKEN_STRING; + + if (! gimp_scanner_parse_string_no_validate (scanner, + &module_load_inhibit)) + goto error; + } + token = G_TOKEN_RIGHT_PAREN; + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + +#undef MODULE_LOAD_INHIBIT + + if (token != G_TOKEN_LEFT_PAREN) + { + g_scanner_get_next_token (scanner); + g_scanner_unexp_token (scanner, token, NULL, NULL, NULL, + _("fatal parse error"), TRUE); + } + + error: + + if (error) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } + + gimp_scanner_destroy (scanner); + } + + if (module_load_inhibit) + { + gimp_module_db_set_load_inhibit (gimp->module_db, module_load_inhibit); + g_free (module_load_inhibit); + } + + gimp_module_db_load (gimp->module_db, gimp->config->module_path); +} + +static void +add_to_inhibit_string (gpointer data, + gpointer user_data) +{ + GimpModule *module = data; + GString *str = user_data; + + if (module->load_inhibit) + { + g_string_append_c (str, G_SEARCHPATH_SEPARATOR); + g_string_append (str, module->filename); + } +} + +void +gimp_modules_unload (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (! gimp->no_interface && gimp->write_modulerc) + { + GimpConfigWriter *writer; + GString *str; + const gchar *p; + GFile *file; + GError *error = NULL; + + str = g_string_new (NULL); + g_list_foreach (gimp->module_db->modules, add_to_inhibit_string, str); + if (str->len > 0) + p = str->str + 1; + else + p = ""; + + file = gimp_directory_file ("modulerc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + writer = gimp_config_writer_new_gfile (file, TRUE, + "GIMP modulerc", &error); + g_object_unref (file); + + if (writer) + { + gimp_config_writer_open (writer, "module-load-inhibit"); + gimp_config_writer_string (writer, p); + gimp_config_writer_close (writer); + + gimp_config_writer_finish (writer, "end of modulerc", &error); + + gimp->write_modulerc = FALSE; + } + + g_string_free (str, TRUE); + + if (error) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } + } +} + +void +gimp_modules_refresh (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (! gimp->no_interface) + { + gimp_module_db_refresh (gimp->module_db, gimp->config->module_path); + } +} diff --git a/app/core/gimp-modules.h b/app/core/gimp-modules.h new file mode 100644 index 0000000..fc90d69 --- /dev/null +++ b/app/core/gimp-modules.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmodules.h + * (C) 1999 Austin Donnelly + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_MODULES_H__ +#define __GIMP_MODULES_H__ + + +void gimp_modules_init (Gimp *gimp); +void gimp_modules_exit (Gimp *gimp); + +void gimp_modules_load (Gimp *gimp); +void gimp_modules_unload (Gimp *gimp); + +void gimp_modules_refresh (Gimp *gimp); + + +#endif /* __GIMP_MODULES_H__ */ diff --git a/app/core/gimp-palettes.c b/app/core/gimp-palettes.c new file mode 100644 index 0000000..4f189a4 --- /dev/null +++ b/app/core/gimp-palettes.c @@ -0,0 +1,143 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-gradients.c + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-palettes.h" +#include "gimpcontext.h" +#include "gimpcontainer.h" +#include "gimpdatafactory.h" +#include "gimppalettemru.h" + +#include "gimp-intl.h" + + +#define COLOR_HISTORY_KEY "gimp-palette-color-history" + + +/* local function prototypes */ + +static GimpPalette * gimp_palettes_add_palette (Gimp *gimp, + const gchar *name, + const gchar *id); + + +/* public functions */ + +void +gimp_palettes_init (Gimp *gimp) +{ + GimpPalette *palette; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + palette = gimp_palettes_add_palette (gimp, + _("Color History"), + COLOR_HISTORY_KEY); + gimp_context_set_palette (gimp->user_context, palette); +} + +void +gimp_palettes_load (Gimp *gimp) +{ + GimpPalette *palette; + GFile *file; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + palette = gimp_palettes_get_color_history (gimp); + + file = gimp_directory_file ("colorrc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + gimp_palette_mru_load (GIMP_PALETTE_MRU (palette), file); + + g_object_unref (file); +} + +void +gimp_palettes_save (Gimp *gimp) +{ + GimpPalette *palette; + GFile *file; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + palette = gimp_palettes_get_color_history (gimp); + + file = gimp_directory_file ("colorrc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + gimp_palette_mru_save (GIMP_PALETTE_MRU (palette), file); + + g_object_unref (file); +} + +GimpPalette * +gimp_palettes_get_color_history (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_get_data (G_OBJECT (gimp), COLOR_HISTORY_KEY); +} + +void +gimp_palettes_add_color_history (Gimp *gimp, + const GimpRGB *color) +{ + GimpPalette *history; + + history = gimp_palettes_get_color_history (gimp); + gimp_palette_mru_add (GIMP_PALETTE_MRU (history), color); +} + +/* private functions */ + +static GimpPalette * +gimp_palettes_add_palette (Gimp *gimp, + const gchar *name, + const gchar *id) +{ + GimpData *palette; + + palette = gimp_palette_mru_new (name); + + gimp_data_make_internal (palette, id); + + gimp_container_add (gimp_data_factory_get_container (gimp->palette_factory), + GIMP_OBJECT (palette)); + g_object_unref (palette); + + g_object_set_data (G_OBJECT (gimp), id, palette); + + return GIMP_PALETTE (palette); +} diff --git a/app/core/gimp-palettes.h b/app/core/gimp-palettes.h new file mode 100644 index 0000000..7858c86 --- /dev/null +++ b/app/core/gimp-palettes.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * gimp-palettes.h + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PALETTES__ +#define __GIMP_PALETTES__ + + +void gimp_palettes_init (Gimp *gimp); + +void gimp_palettes_load (Gimp *gimp); +void gimp_palettes_save (Gimp *gimp); + +GimpPalette * gimp_palettes_get_color_history (Gimp *gimp); +void gimp_palettes_add_color_history (Gimp *gimp, + const GimpRGB *color); + + +#endif /* __GIMP_PALETTES__ */ diff --git a/app/core/gimp-parallel.cc b/app/core/gimp-parallel.cc new file mode 100644 index 0000000..208bcfe --- /dev/null +++ b/app/core/gimp-parallel.cc @@ -0,0 +1,553 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-parallel.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 . + */ + + +#include "config.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef G_OS_WIN32 +#include +#endif + +extern "C" +{ + +#include "core-types.h" + +#include "config/gimpgeglconfig.h" + +#include "gimp.h" +#include "gimp-parallel.h" +#include "gimpasync.h" +#include "gimpcancelable.h" + + +#define GIMP_PARALLEL_MAX_THREADS 64 +#define GIMP_PARALLEL_RUN_ASYNC_MAX_THREADS 1 + + +typedef struct +{ + GimpAsync *async; + gint priority; + GimpRunAsyncFunc func; + gpointer user_data; + GDestroyNotify user_data_destroy_func; +} GimpParallelRunAsyncTask; + +typedef struct +{ + GThread *thread; + + gboolean quit; + + GimpAsync *current_async; +} GimpParallelRunAsyncThread; + + +/* local function prototypes */ + +static void gimp_parallel_notify_num_processors (GimpGeglConfig *config); + +static void gimp_parallel_set_n_threads (gint n_threads, + gboolean finish_tasks); + +static void gimp_parallel_run_async_set_n_threads (gint n_threads, + gboolean finish_tasks); +static gpointer gimp_parallel_run_async_thread_func (GimpParallelRunAsyncThread *thread); +static void gimp_parallel_run_async_enqueue_task (GimpParallelRunAsyncTask *task); +static GimpParallelRunAsyncTask * gimp_parallel_run_async_dequeue_task (void); +static gboolean gimp_parallel_run_async_execute_task (GimpParallelRunAsyncTask *task); +static void gimp_parallel_run_async_abort_task (GimpParallelRunAsyncTask *task); +static void gimp_parallel_run_async_cancel (GimpAsync *async); +static void gimp_parallel_run_async_waiting (GimpAsync *async); + + +/* local variables */ + +static gint gimp_parallel_run_async_n_threads = 0; +static GimpParallelRunAsyncThread gimp_parallel_run_async_threads[GIMP_PARALLEL_RUN_ASYNC_MAX_THREADS]; + +static GMutex gimp_parallel_run_async_mutex; +static GCond gimp_parallel_run_async_cond; +static GQueue gimp_parallel_run_async_queue = G_QUEUE_INIT; + + +/* public functions */ + + +void +gimp_parallel_init (Gimp *gimp) +{ + GimpGeglConfig *config; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + config = GIMP_GEGL_CONFIG (gimp->config); + + g_signal_connect (config, "notify::num-processors", + G_CALLBACK (gimp_parallel_notify_num_processors), + NULL); + + gimp_parallel_notify_num_processors (config); +} + +void +gimp_parallel_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + g_signal_handlers_disconnect_by_func (gimp->config, + (gpointer) gimp_parallel_notify_num_processors, + NULL); + + /* stop all threads */ + gimp_parallel_set_n_threads (0, /* finish_tasks = */ FALSE); +} + +GimpAsync * +gimp_parallel_run_async (GimpRunAsyncFunc func, + gpointer user_data) +{ + return gimp_parallel_run_async_full (0, func, user_data, NULL); +} + +GimpAsync * +gimp_parallel_run_async_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy_func) +{ + GimpAsync *async; + GimpParallelRunAsyncTask *task; + + g_return_val_if_fail (func != NULL, NULL); + + async = gimp_async_new (); + + task = g_slice_new (GimpParallelRunAsyncTask); + + task->async = GIMP_ASYNC (g_object_ref (async)); + task->priority = priority; + task->func = func; + task->user_data = user_data; + task->user_data_destroy_func = user_data_destroy_func; + + if (gimp_parallel_run_async_n_threads > 0) + { + g_signal_connect_after (async, "cancel", + G_CALLBACK (gimp_parallel_run_async_cancel), + NULL); + g_signal_connect_after (async, "waiting", + G_CALLBACK (gimp_parallel_run_async_waiting), + NULL); + + g_mutex_lock (&gimp_parallel_run_async_mutex); + + gimp_parallel_run_async_enqueue_task (task); + + g_cond_signal (&gimp_parallel_run_async_cond); + + g_mutex_unlock (&gimp_parallel_run_async_mutex); + } + else + { + while (gimp_parallel_run_async_execute_task (task)); + } + + return async; +} + +GimpAsync * +gimp_parallel_run_async_independent (GimpRunAsyncFunc func, + gpointer user_data) +{ + return gimp_parallel_run_async_independent_full (0, func, user_data); +} + +GimpAsync * +gimp_parallel_run_async_independent_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data) +{ + GimpAsync *async; + GimpParallelRunAsyncTask *task; + GThread *thread; + + g_return_val_if_fail (func != NULL, NULL); + + async = gimp_async_new (); + + task = g_slice_new0 (GimpParallelRunAsyncTask); + + task->async = GIMP_ASYNC (g_object_ref (async)); + task->priority = priority; + task->func = func; + task->user_data = user_data; + + thread = g_thread_new ( + "async-ind", + [] (gpointer data) -> gpointer + { + GimpParallelRunAsyncTask *task = (GimpParallelRunAsyncTask *) data; + + /* adjust the thread's priority */ +#if defined (G_OS_WIN32) + if (task->priority < 0) + { + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_ABOVE_NORMAL); + } + else if (task->priority > 0) + { + SetThreadPriority (GetCurrentThread (), THREAD_MODE_BACKGROUND_BEGIN); + } +#elif defined (HAVE_UNISTD_H) && defined (__gnu_linux__) + if (task->priority) + { + (nice (task->priority) != -1); + /* ^-- avoid "unused result" warning */ + } +#endif + + while (gimp_parallel_run_async_execute_task (task)); + + return NULL; + }, + task); + + gimp_async_add_callback (async, + [] (GimpAsync *async, + gpointer thread) + { + g_thread_join ((GThread *) thread); + }, + thread); + + return async; +} + + +/* private functions */ + + +static void +gimp_parallel_notify_num_processors (GimpGeglConfig *config) +{ + gimp_parallel_set_n_threads (config->num_processors, + /* finish_tasks = */ TRUE); +} + +static void +gimp_parallel_set_n_threads (gint n_threads, + gboolean finish_tasks) +{ + gimp_parallel_run_async_set_n_threads (n_threads, finish_tasks); +} + +static void +gimp_parallel_run_async_set_n_threads (gint n_threads, + gboolean finish_tasks) +{ + gint i; + + n_threads = CLAMP (n_threads, 0, GIMP_PARALLEL_RUN_ASYNC_MAX_THREADS); + + if (n_threads > gimp_parallel_run_async_n_threads) /* need more threads */ + { + for (i = gimp_parallel_run_async_n_threads; i < n_threads; i++) + { + GimpParallelRunAsyncThread *thread = + &gimp_parallel_run_async_threads[i]; + + thread->quit = FALSE; + + thread->thread = g_thread_new ( + "async", + (GThreadFunc) gimp_parallel_run_async_thread_func, + thread); + } + } + else if (n_threads < gimp_parallel_run_async_n_threads) /* need less threads */ + { + g_mutex_lock (&gimp_parallel_run_async_mutex); + + for (i = n_threads; i < gimp_parallel_run_async_n_threads; i++) + { + GimpParallelRunAsyncThread *thread = + &gimp_parallel_run_async_threads[i]; + + thread->quit = TRUE; + + if (thread->current_async && ! finish_tasks) + gimp_cancelable_cancel (GIMP_CANCELABLE (thread->current_async)); + } + + g_cond_broadcast (&gimp_parallel_run_async_cond); + + g_mutex_unlock (&gimp_parallel_run_async_mutex); + + for (i = n_threads; i < gimp_parallel_run_async_n_threads; i++) + { + GimpParallelRunAsyncThread *thread = + &gimp_parallel_run_async_threads[i]; + + g_thread_join (thread->thread); + } + } + + gimp_parallel_run_async_n_threads = n_threads; + + if (n_threads == 0) + { + GimpParallelRunAsyncTask *task; + + /* finish remaining tasks */ + while ((task = gimp_parallel_run_async_dequeue_task ())) + { + if (finish_tasks) + while (gimp_parallel_run_async_execute_task (task)); + else + gimp_parallel_run_async_abort_task (task); + } + } +} + +static gpointer +gimp_parallel_run_async_thread_func (GimpParallelRunAsyncThread *thread) +{ + g_mutex_lock (&gimp_parallel_run_async_mutex); + + while (TRUE) + { + GimpParallelRunAsyncTask *task; + + while (! thread->quit && + (task = gimp_parallel_run_async_dequeue_task ())) + { + gboolean resume; + + thread->current_async = GIMP_ASYNC (g_object_ref (task->async)); + + do + { + g_mutex_unlock (&gimp_parallel_run_async_mutex); + + resume = gimp_parallel_run_async_execute_task (task); + + g_mutex_lock (&gimp_parallel_run_async_mutex); + } + while (resume && + (g_queue_is_empty (&gimp_parallel_run_async_queue) || + task->priority < + ((GimpParallelRunAsyncTask *) + g_queue_peek_head ( + &gimp_parallel_run_async_queue))->priority)); + + g_clear_object (&thread->current_async); + + if (resume) + gimp_parallel_run_async_enqueue_task (task); + } + + if (thread->quit) + break; + + g_cond_wait (&gimp_parallel_run_async_cond, + &gimp_parallel_run_async_mutex); + } + + g_mutex_unlock (&gimp_parallel_run_async_mutex); + + return NULL; +} + +static void +gimp_parallel_run_async_enqueue_task (GimpParallelRunAsyncTask *task) +{ + GList *link; + GList *iter; + + if (gimp_async_is_canceled (task->async)) + { + gimp_parallel_run_async_abort_task (task); + + return; + } + + link = g_list_alloc (); + link->data = task; + + g_object_set_data (G_OBJECT (task->async), + "gimp-parallel-run-async-link", link); + + for (iter = g_queue_peek_tail_link (&gimp_parallel_run_async_queue); + iter; + iter = g_list_previous (iter)) + { + GimpParallelRunAsyncTask *other_task = + (GimpParallelRunAsyncTask *) iter->data; + + if (other_task->priority <= task->priority) + break; + } + + if (iter) + { + link->prev = iter; + link->next = iter->next; + + iter->next = link; + + if (link->next) + link->next->prev = link; + else + gimp_parallel_run_async_queue.tail = link; + + gimp_parallel_run_async_queue.length++; + } + else + { + g_queue_push_head_link (&gimp_parallel_run_async_queue, link); + } +} + +static GimpParallelRunAsyncTask * +gimp_parallel_run_async_dequeue_task (void) +{ + GimpParallelRunAsyncTask *task; + + task = (GimpParallelRunAsyncTask *) g_queue_pop_head ( + &gimp_parallel_run_async_queue); + + if (task) + { + g_object_set_data (G_OBJECT (task->async), + "gimp-parallel-run-async-link", NULL); + } + + return task; +} + +static gboolean +gimp_parallel_run_async_execute_task (GimpParallelRunAsyncTask *task) +{ + if (gimp_async_is_canceled (task->async)) + { + gimp_parallel_run_async_abort_task (task); + + return FALSE; + } + + task->func (task->async, task->user_data); + + if (gimp_async_is_stopped (task->async)) + { + g_object_unref (task->async); + + g_slice_free (GimpParallelRunAsyncTask, task); + + return FALSE; + } + + return TRUE; +} + +static void +gimp_parallel_run_async_abort_task (GimpParallelRunAsyncTask *task) +{ + if (task->user_data && task->user_data_destroy_func) + task->user_data_destroy_func (task->user_data); + + gimp_async_abort (task->async); + + g_object_unref (task->async); + + g_slice_free (GimpParallelRunAsyncTask, task); +} + +static void +gimp_parallel_run_async_cancel (GimpAsync *async) +{ + GList *link; + GimpParallelRunAsyncTask *task = NULL; + + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); + + if (! link) + return; + + g_mutex_lock (&gimp_parallel_run_async_mutex); + + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); + + if (link) + { + g_object_set_data (G_OBJECT (async), + "gimp-parallel-run-async-link", NULL); + + task = (GimpParallelRunAsyncTask *) link->data; + + g_queue_delete_link (&gimp_parallel_run_async_queue, link); + } + + g_mutex_unlock (&gimp_parallel_run_async_mutex); + + if (task) + gimp_parallel_run_async_abort_task (task); +} + +static void +gimp_parallel_run_async_waiting (GimpAsync *async) +{ + GList *link; + + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); + + if (! link) + return; + + g_mutex_lock (&gimp_parallel_run_async_mutex); + + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); + + if (link) + { + GimpParallelRunAsyncTask *task = (GimpParallelRunAsyncTask *) link->data; + + task->priority = G_MININT; + + g_queue_unlink (&gimp_parallel_run_async_queue, link); + g_queue_push_head_link (&gimp_parallel_run_async_queue, link); + } + + g_mutex_unlock (&gimp_parallel_run_async_mutex); +} + +} /* extern "C" */ diff --git a/app/core/gimp-parallel.h b/app/core/gimp-parallel.h new file mode 100644 index 0000000..76abc1d --- /dev/null +++ b/app/core/gimp-parallel.h @@ -0,0 +1,157 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-parallel.h + * 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 . + */ + +#ifndef __GIMP_PARALLEL_H__ +#define __GIMP_PARALLEL_H__ + + +void gimp_parallel_init (Gimp *gimp); +void gimp_parallel_exit (Gimp *gimp); + +GimpAsync * gimp_parallel_run_async (GimpRunAsyncFunc func, + gpointer user_data); +GimpAsync * gimp_parallel_run_async_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy_func); +GimpAsync * gimp_parallel_run_async_independent (GimpRunAsyncFunc func, + gpointer user_data); +GimpAsync * gimp_parallel_run_async_independent_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data); + + +#ifdef __cplusplus + +extern "C++" +{ + +#include + +template +inline GimpAsync * +gimp_parallel_run_async (RunAsyncFunc func) +{ + RunAsyncFunc *func_copy = g_new (RunAsyncFunc, 1); + + new (func_copy) RunAsyncFunc (func); + + return gimp_parallel_run_async_full (0, + [] (GimpAsync *async, + gpointer user_data) + { + RunAsyncFunc *func_copy = + (RunAsyncFunc *) user_data; + + (*func_copy) (async); + + func_copy->~RunAsyncFunc (); + g_free (func_copy); + }, + func_copy, + [] (gpointer user_data) + { + RunAsyncFunc *func_copy = + (RunAsyncFunc *) user_data; + + func_copy->~RunAsyncFunc (); + g_free (func_copy); + }); +} + +template +inline GimpAsync * +gimp_parallel_run_async_full (gint priority, + RunAsyncFunc func, + DestroyFunc destroy_func) +{ + typedef struct + { + RunAsyncFunc func; + DestroyFunc destroy_func; + } Funcs; + + Funcs *funcs_copy = g_new (Funcs, 1); + + new (funcs_copy) Funcs {func, destroy_func}; + + return gimp_parallel_run_async_full (priority, + [] (GimpAsync *async, + gpointer user_data) + { + Funcs *funcs_copy = + (Funcs *) user_data; + + funcs_copy->func (async); + + funcs_copy->~Funcs (); + g_free (funcs_copy); + }, + funcs_copy, + [] (gpointer user_data) + { + Funcs *funcs_copy = + (Funcs *) user_data; + + funcs_copy->destroy_func (); + + funcs_copy->~Funcs (); + g_free (funcs_copy); + }); +} + +template +inline GimpAsync * +gimp_parallel_run_async_independent_full (gint priority, + RunAsyncFunc func) +{ + RunAsyncFunc *func_copy = g_new (RunAsyncFunc, 1); + + new (func_copy) RunAsyncFunc (func); + + return gimp_parallel_run_async_independent_full (priority, + [] (GimpAsync *async, + gpointer user_data) + { + RunAsyncFunc *func_copy = + (RunAsyncFunc *) user_data; + + (*func_copy) (async); + + func_copy->~RunAsyncFunc (); + g_free (func_copy); + }, + func_copy); +} + +template +inline GimpAsync * +gimp_parallel_run_async_independent (RunAsyncFunc func) +{ + return gimp_parallel_run_async_independent_full (0, func); +} + +} + +#endif /* __cplusplus */ + + +#endif /* __GIMP_PARALLEL_H__ */ diff --git a/app/core/gimp-parasites.c b/app/core/gimp-parasites.c new file mode 100644 index 0000000..e781a54 --- /dev/null +++ b/app/core/gimp-parasites.c @@ -0,0 +1,170 @@ +/* gimpparasite.c: Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-parasites.h" +#include "gimpparasitelist.h" + + +gboolean +gimp_parasite_validate (Gimp *gimp, + const GimpParasite *parasite, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (parasite != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return TRUE; +} + +void +gimp_parasite_attach (Gimp *gimp, + const GimpParasite *parasite) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (parasite != NULL); + + gimp_parasite_list_add (gimp->parasites, parasite); +} + +void +gimp_parasite_detach (Gimp *gimp, + const gchar *name) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (name != NULL); + + gimp_parasite_list_remove (gimp->parasites, name); +} + +const GimpParasite * +gimp_parasite_find (Gimp *gimp, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return gimp_parasite_list_find (gimp->parasites, name); +} + +static void +list_func (const gchar *key, + GimpParasite *parasite, + gchar ***current) +{ + *(*current)++ = g_strdup (key); +} + +gchar ** +gimp_parasite_list (Gimp *gimp, + gint *count) +{ + gchar **list; + gchar **current; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (count != NULL, NULL); + + *count = gimp_parasite_list_length (gimp->parasites); + + list = current = g_new (gchar *, *count); + + gimp_parasite_list_foreach (gimp->parasites, (GHFunc) list_func, ¤t); + + return list; +} + + +/* FIXME: this doesn't belong here */ + +void +gimp_parasite_shift_parent (GimpParasite *parasite) +{ + g_return_if_fail (parasite != NULL); + + parasite->flags = (parasite->flags >> 8); +} + + +/* parasiterc functions */ + +void +gimp_parasiterc_load (Gimp *gimp) +{ + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = gimp_directory_file ("parasiterc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (gimp->parasites), + file, NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + + g_error_free (error); + } + + g_object_unref (file); +} + +void +gimp_parasiterc_save (Gimp *gimp) +{ + const gchar *header = + "GIMP parasiterc\n" + "\n" + "This file will be entirely rewritten each time you exit."; + const gchar *footer = + "end of parasiterc"; + + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_PARASITE_LIST (gimp->parasites)); + + file = gimp_directory_file ("parasiterc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (gimp->parasites), + file, + header, footer, NULL, + &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_error_free (error); + } + + g_object_unref (file); +} diff --git a/app/core/gimp-parasites.h b/app/core/gimp-parasites.h new file mode 100644 index 0000000..06d38c8 --- /dev/null +++ b/app/core/gimp-parasites.h @@ -0,0 +1,41 @@ +/* gimpparasite.h: Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PARASITES_H__ +#define __GIMP_PARASITES_H__ + + +/* some wrappers to access gimp->parasites, mainly for the PDB */ + +gboolean gimp_parasite_validate (Gimp *gimp, + const GimpParasite *parasite, + GError **error); +void gimp_parasite_attach (Gimp *gimp, + const GimpParasite *parasite); +void gimp_parasite_detach (Gimp *gimp, + const gchar *name); +const GimpParasite * gimp_parasite_find (Gimp *gimp, + const gchar *name); +gchar ** gimp_parasite_list (Gimp *gimp, + gint *count); + +void gimp_parasite_shift_parent (GimpParasite *parasite); + +void gimp_parasiterc_load (Gimp *gimp); +void gimp_parasiterc_save (Gimp *gimp); + + +#endif /* __GIMP_PARASITES_H__ */ diff --git a/app/core/gimp-spawn.c b/app/core/gimp-spawn.c new file mode 100644 index 0000000..bfa08e8 --- /dev/null +++ b/app/core/gimp-spawn.c @@ -0,0 +1,250 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-spawn.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 . + */ + +#include "config.h" + +#include + +#ifdef HAVE_VFORK +#include +#include +#include +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#endif + +#include "core-types.h" + +#include "gimp-spawn.h" + +#include "gimp-intl.h" + + +#ifdef HAVE_VFORK + +/* copied from glib */ +static gint +exec_err_to_g_error (gint en) +{ + switch (en) + { +#ifdef EACCES + case EACCES: + return G_SPAWN_ERROR_ACCES; + break; +#endif + +#ifdef EPERM + case EPERM: + return G_SPAWN_ERROR_PERM; + break; +#endif + +#ifdef E2BIG + case E2BIG: + return G_SPAWN_ERROR_TOO_BIG; + break; +#endif + +#ifdef ENOEXEC + case ENOEXEC: + return G_SPAWN_ERROR_NOEXEC; + break; +#endif + +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + return G_SPAWN_ERROR_NAMETOOLONG; + break; +#endif + +#ifdef ENOENT + case ENOENT: + return G_SPAWN_ERROR_NOENT; + break; +#endif + +#ifdef ENOMEM + case ENOMEM: + return G_SPAWN_ERROR_NOMEM; + break; +#endif + +#ifdef ENOTDIR + case ENOTDIR: + return G_SPAWN_ERROR_NOTDIR; + break; +#endif + +#ifdef ELOOP + case ELOOP: + return G_SPAWN_ERROR_LOOP; + break; +#endif + +#ifdef ETXTBUSY + case ETXTBUSY: + return G_SPAWN_ERROR_TXTBUSY; + break; +#endif + +#ifdef EIO + case EIO: + return G_SPAWN_ERROR_IO; + break; +#endif + +#ifdef ENFILE + case ENFILE: + return G_SPAWN_ERROR_NFILE; + break; +#endif + +#ifdef EMFILE + case EMFILE: + return G_SPAWN_ERROR_MFILE; + break; +#endif + +#ifdef EINVAL + case EINVAL: + return G_SPAWN_ERROR_INVAL; + break; +#endif + +#ifdef EISDIR + case EISDIR: + return G_SPAWN_ERROR_ISDIR; + break; +#endif + +#ifdef ELIBBAD + case ELIBBAD: + return G_SPAWN_ERROR_LIBBAD; + break; +#endif + + default: + return G_SPAWN_ERROR_FAILED; + break; + } +} + +#endif /* HAVE_VFORK */ + +gboolean +gimp_spawn_async (gchar **argv, + gchar **envp, + GSpawnFlags flags, + GPid *child_pid, + GError **error) +{ + g_return_val_if_fail (argv != NULL, FALSE); + g_return_val_if_fail (argv[0] != NULL, FALSE); + +#ifdef HAVE_VFORK + if (flags == (G_SPAWN_LEAVE_DESCRIPTORS_OPEN | + G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_CHILD_INHERITS_STDIN)) + { + pid_t pid; + + pid = vfork (); + + if (pid < 0) + { + gint errsv = errno; + + g_set_error (error, + G_SPAWN_ERROR, + G_SPAWN_ERROR_FORK, + _("Failed to fork (%s)"), + g_strerror (errsv)); + + return FALSE; + } + else if (pid == 0) + { + if (envp) + execve (argv[0], argv, envp); + else + execv (argv[0], argv); + + _exit (errno); + } + else + { + int status = -1; + pid_t result; + + result = waitpid (pid, &status, WNOHANG); + + if (result) + { + if (result < 0) + { + g_warning ("waitpid() should not fail in " + "gimp_spawn_async()"); + } + + if (WIFEXITED (status)) + status = WEXITSTATUS (status); + else + status = -1; + + g_set_error (error, + G_SPAWN_ERROR, + exec_err_to_g_error (status), + _("Failed to execute child process “%s” (%s)"), + argv[0], + g_strerror (status)); + + return FALSE; + } + + if (child_pid) *child_pid = pid; + + return TRUE; + } + } +#endif /* HAVE_VFORK */ + + return g_spawn_async (NULL, argv, envp, flags, NULL, NULL, child_pid, error); +} + +void +gimp_spawn_set_cloexec (gint fd) +{ +#if defined (G_OS_WIN32) + SetHandleInformation ((HANDLE) _get_osfhandle (fd), HANDLE_FLAG_INHERIT, 0); +#elif defined (HAVE_FCNTL_H) + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); +#elif defined (__GNUC__) +#warning gimp_spawn_set_cloexec() is not implemented for the target platform +#endif +} diff --git a/app/core/gimp-spawn.h b/app/core/gimp-spawn.h new file mode 100644 index 0000000..f81cf7d --- /dev/null +++ b/app/core/gimp-spawn.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-spawn.h + * 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 . + */ + +#ifndef __GIMP_SPAWN_H__ +#define __GIMP_SPAWN_H__ + + +gboolean gimp_spawn_async (gchar **argv, + gchar **envp, + GSpawnFlags flags, + GPid *child_pid, + GError **error); + +void gimp_spawn_set_cloexec (gint fd); + + +#endif /* __GIMP_SPAWN_H__ */ diff --git a/app/core/gimp-tags.c b/app/core/gimp-tags.c new file mode 100644 index 0000000..5efedb9 --- /dev/null +++ b/app/core/gimp-tags.c @@ -0,0 +1,271 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-tags.c + * Copyright (C) 2009 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "config/gimpxmlparser.h" + +#include "gimp-utils.h" +#include "gimp-tags.h" + +#include "gimp-intl.h" + + +#define GIMP_TAGS_FILE "tags.xml" + +typedef struct +{ + const gchar *locale; + GString *buf; + gboolean locale_matches; +} GimpTagsInstaller; + + +static void gimp_tags_installer_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void gimp_tags_installer_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void gimp_tags_installer_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); +static const gchar* attribute_name_to_value (const gchar **attribute_names, + const gchar **attribute_values, + const gchar *name); + + +gboolean +gimp_tags_user_install (void) +{ + GFile *file; + GOutputStream *output; + GMarkupParser markup_parser; + GimpXmlParser *xml_parser; + const char *tags_locale; + GimpTagsInstaller tags_installer = { 0, }; + GError *error = NULL; + gboolean result = TRUE; + + /* This is a special string to specify the language identifier to + * look for in the gimp-tags-default.xml file. Please translate the + * C in it according to the name of the po file used for + * gimp-tags-default.xml. E.g. lithuanian for the translation, + * that would be "tags-locale:lt". + */ + tags_locale = _("tags-locale:C"); + + if (g_str_has_prefix (tags_locale, "tags-locale:")) + { + tags_locale += strlen ("tags-locale:"); + + if (*tags_locale && *tags_locale != 'C') + tags_installer.locale = tags_locale; + } + else + { + g_warning ("Wrong translation for 'tags-locale:', fix the translation!"); + } + + tags_installer.buf = g_string_new (NULL); + + g_string_append (tags_installer.buf, "\n"); + g_string_append (tags_installer.buf, "\n"); + + markup_parser.start_element = gimp_tags_installer_load_start_element; + markup_parser.end_element = gimp_tags_installer_load_end_element; + markup_parser.text = gimp_tags_installer_load_text; + markup_parser.passthrough = NULL; + markup_parser.error = NULL; + + xml_parser = gimp_xml_parser_new (&markup_parser, &tags_installer); + + file = gimp_data_directory_file ("tags", "gimp-tags-default.xml", NULL); + result = gimp_xml_parser_parse_gfile (xml_parser, file, &error); + g_object_unref (file); + + gimp_xml_parser_free (xml_parser); + + if (! result) + { + g_string_free (tags_installer.buf, TRUE); + return FALSE; + } + + g_string_append (tags_installer.buf, "\n\n"); + + file = gimp_directory_file (GIMP_TAGS_FILE, NULL); + + output = G_OUTPUT_STREAM (g_file_replace (file, + NULL, FALSE, G_FILE_CREATE_NONE, + NULL, &error)); + if (! output) + { + g_printerr ("%s\n", error->message); + result = FALSE; + } + else if (! g_output_stream_write_all (output, + tags_installer.buf->str, + tags_installer.buf->len, + NULL, NULL, &error)) + { + GCancellable *cancellable = g_cancellable_new (); + + g_printerr (_("Error writing '%s': %s"), + gimp_file_get_utf8_name (file), error->message); + result = FALSE; + + /* Cancel the overwrite initiated by g_file_replace(). */ + g_cancellable_cancel (cancellable); + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + } + else if (! g_output_stream_close (output, NULL, &error)) + { + g_printerr (_("Error closing '%s': %s"), + gimp_file_get_utf8_name (file), error->message); + result = FALSE; + } + + if (output) + g_object_unref (output); + + g_clear_error (&error); + g_object_unref (file); + g_string_free (tags_installer.buf, TRUE); + + return result; +} + +static void +gimp_tags_installer_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + GimpTagsInstaller *tags_installer = user_data; + + if (! strcmp (element_name, "resource")) + { + g_string_append_printf (tags_installer->buf, "\n buf, " %s=\"%s\"", + *attribute_names, *attribute_values); + + attribute_names++; + attribute_values++; + } + + g_string_append_printf (tags_installer->buf, ">\n"); + } + else if (! strcmp (element_name, "thetag")) + { + const char *current_locale; + + current_locale = attribute_name_to_value (attribute_names, attribute_values, + "xml:lang"); + + if (current_locale && tags_installer->locale) + { + tags_installer->locale_matches = ! strcmp (current_locale, + tags_installer->locale); + } + else + { + tags_installer->locale_matches = (current_locale == + tags_installer->locale); + } + } +} + +static void +gimp_tags_installer_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + GimpTagsInstaller *tags_installer = user_data; + + if (strcmp (element_name, "resource") == 0) + { + g_string_append (tags_installer->buf, " \n"); + } +} + +static void +gimp_tags_installer_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + GimpTagsInstaller *tags_installer = user_data; + const gchar *current_element; + gchar *tag_string; + + current_element = g_markup_parse_context_get_element (context); + + if (tags_installer->locale_matches && + current_element && + strcmp (current_element, "thetag") == 0) + { + tag_string = g_markup_escape_text (text, text_len); + g_string_append_printf (tags_installer->buf, " %s\n", + tag_string); + g_free (tag_string); + } +} + +static const gchar * +attribute_name_to_value (const gchar **attribute_names, + const gchar **attribute_values, + const gchar *name) +{ + while (*attribute_names) + { + if (! strcmp (*attribute_names, name)) + { + return *attribute_values; + } + + attribute_names++; + attribute_values++; + } + + return NULL; +} diff --git a/app/core/gimp-tags.h b/app/core/gimp-tags.h new file mode 100644 index 0000000..8f52dce --- /dev/null +++ b/app/core/gimp-tags.h @@ -0,0 +1,25 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_TAGS_H__ +#define __GIMP_TAGS_H__ + + +gboolean gimp_tags_user_install (void); + + +#endif /* __GIMP_TAGS_H__ */ diff --git a/app/core/gimp-templates.c b/app/core/gimp-templates.c new file mode 100644 index 0000000..ba0800d --- /dev/null +++ b/app/core/gimp-templates.c @@ -0,0 +1,213 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-templates.h" +#include "gimplist.h" +#include "gimptemplate.h" + + +/* functions to load and save the gimp templates files */ + +void +gimp_templates_load (Gimp *gimp) +{ + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_LIST (gimp->templates)); + + file = gimp_directory_file ("templaterc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (gimp->templates), + file, NULL, &error)) + { + if (error->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) + { + g_clear_error (&error); + g_object_unref (file); + + file = gimp_sysconf_directory_file ("templaterc", NULL); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (gimp->templates), + file, NULL, &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, + error->message); + } + } + else + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + } + + g_clear_error (&error); + } + + gimp_list_reverse (GIMP_LIST (gimp->templates)); + + g_object_unref (file); +} + +void +gimp_templates_save (Gimp *gimp) +{ + const gchar *header = + "GIMP templaterc\n" + "\n" + "This file will be entirely rewritten each time you exit."; + const gchar *footer = + "end of templaterc"; + + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_LIST (gimp->templates)); + + file = gimp_directory_file ("templaterc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (gimp->templates), + file, + header, footer, NULL, + &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_error_free (error); + } + + g_object_unref (file); +} + + +/* just like gimp_list_get_child_by_name() but matches case-insensitive + * and dpi/ppi-insensitive + */ +static GimpObject * +gimp_templates_migrate_get_child_by_name (GimpContainer *container, + const gchar *name) +{ + GimpList *list = GIMP_LIST (container); + GimpObject *retval = NULL; + GList *glist; + + for (glist = list->queue->head; glist; glist = g_list_next (glist)) + { + GimpObject *object = glist->data; + gchar *str1 = g_ascii_strdown (gimp_object_get_name (object), -1); + gchar *str2 = g_ascii_strdown (name, -1); + + if (! strcmp (str1, str2)) + { + retval = object; + } + else + { + gchar *dpi = strstr (str1, "dpi"); + + if (dpi) + { + memcpy (dpi, "ppi", 3); + + g_print ("replaced: %s\n", str1); + + if (! strcmp (str1, str2)) + retval = object; + } + } + + g_free (str1); + g_free (str2); + } + + return retval; +} + +/** + * gimp_templates_migrate: + * @olddir: the old user directory + * + * Migrating the templaterc from GIMP 2.0 to GIMP 2.2 needs this special + * hack since we changed the way that units are handled. This function + * merges the user's templaterc with the systemwide templaterc. The goal + * is to replace the unit for a couple of default templates with "pixels". + **/ +void +gimp_templates_migrate (const gchar *olddir) +{ + GimpContainer *templates = gimp_list_new (GIMP_TYPE_TEMPLATE, TRUE); + GFile *file = gimp_directory_file ("templaterc", NULL); + + if (gimp_config_deserialize_gfile (GIMP_CONFIG (templates), file, + NULL, NULL)) + { + GFile *sysconf_file; + + sysconf_file = gimp_sysconf_directory_file ("templaterc", NULL); + + if (olddir && (strstr (olddir, "2.0") || strstr (olddir, "2.2"))) + { + /* We changed the spelling of a couple of template names: + * + * - from upper to lower case between 2.0 and 2.2 + * - from "dpi" to "ppi" between 2.2 and 2.4 + */ + GimpContainerClass *class = GIMP_CONTAINER_GET_CLASS (templates); + gpointer func = class->get_child_by_name; + + class->get_child_by_name = gimp_templates_migrate_get_child_by_name; + + gimp_config_deserialize_gfile (GIMP_CONFIG (templates), + sysconf_file, NULL, NULL); + + class->get_child_by_name = func; + } + else + { + gimp_config_deserialize_gfile (GIMP_CONFIG (templates), + sysconf_file, NULL, NULL); + } + + g_object_unref (sysconf_file); + + gimp_list_reverse (GIMP_LIST (templates)); + + gimp_config_serialize_to_gfile (GIMP_CONFIG (templates), file, + NULL, NULL, NULL, NULL); + } + + g_object_unref (file); +} diff --git a/app/core/gimp-templates.h b/app/core/gimp-templates.h new file mode 100644 index 0000000..3dc0fde --- /dev/null +++ b/app/core/gimp-templates.h @@ -0,0 +1,28 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_TEMPLATES_H__ +#define __GIMP_TEMPLATES_H__ + + +void gimp_templates_load (Gimp *gimp); +void gimp_templates_save (Gimp *gimp); + +void gimp_templates_migrate (const gchar *olddir); + + +#endif /* __GIMP_TEMPLATES_H__ */ diff --git a/app/core/gimp-transform-3d-utils.c b/app/core/gimp-transform-3d-utils.c new file mode 100644 index 0000000..5ea9f81 --- /dev/null +++ b/app/core/gimp-transform-3d-utils.c @@ -0,0 +1,359 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-3d-transform-utils.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp-transform-3d-utils.h" + + +#define MIN_FOCAL_LENGTH 0.01 + + +gdouble +gimp_transform_3d_angle_of_view_to_focal_length (gdouble angle_of_view, + gdouble width, + gdouble height) +{ + return MAX (width, height) / (2.0 * tan (angle_of_view / 2.0)); +} + +gdouble +gimp_transform_3d_focal_length_to_angle_of_view (gdouble focal_length, + gdouble width, + gdouble height) +{ + return 2.0 * atan (MAX (width, height) / (2.0 * focal_length)); +} + +gint +gimp_transform_3d_permutation_to_rotation_order (const gint permutation[3]) +{ + if (permutation[1] == (permutation[0] + 1) % 3) + return permutation[0] << 1; + else + return (permutation[2] << 1) + 1; +} + +void +gimp_transform_3d_rotation_order_to_permutation (gint rotation_order, + gint permutation[3]) +{ + gboolean reverse = rotation_order & 1; + gint shift = rotation_order >> 1; + gint i; + + for (i = 0; i < 3; i++) + permutation[reverse ? 2 - i : i] = (i + shift) % 3; +} + +gint +gimp_transform_3d_rotation_order_reverse (gint rotation_order) +{ + return rotation_order ^ 1; +} + +void +gimp_transform_3d_vector3_rotate (GimpVector3 *vector, + const GimpVector3 *axis) +{ + GimpVector3 normal; + GimpVector3 proj; + GimpVector3 u, v; + gdouble angle; + + angle = gimp_vector3_length (axis); + + if (angle == 0.0) + return; + + normal = gimp_vector3_mul_val (*axis, 1.0 / angle); + + proj = gimp_vector3_mul_val (normal, + gimp_vector3_inner_product_val (*vector, + normal)); + + u = gimp_vector3_sub_val (*vector, proj); + v = gimp_vector3_cross_product_val (u, normal); + + gimp_vector3_mul (&u, cos (angle)); + gimp_vector3_mul (&v, sin (angle)); + + *vector = proj; + + gimp_vector3_add (vector, vector, &u); + gimp_vector3_add (vector, vector, &v); +} + +GimpVector3 +gimp_transform_3d_vector3_rotate_val (GimpVector3 vector, + GimpVector3 axis) +{ + gimp_transform_3d_vector3_rotate (&vector, &axis); + + return vector; +} + +void +gimp_transform_3d_matrix3_to_matrix4 (const GimpMatrix3 *matrix3, + GimpMatrix4 *matrix4, + gint axis) +{ + gint i, j; + gint k, l; + + for (i = 0; i < 4; i++) + { + if (i == axis) + { + matrix4->coeff[i][i] = 1.0; + } + else + { + matrix4->coeff[axis][i] = 0.0; + matrix4->coeff[i][axis] = 0.0; + } + } + + for (i = 0; i < 3; i++) + { + k = i + (i >= axis); + + for (j = 0; j < 3; j++) + { + l = j + (j >= axis); + + matrix4->coeff[k][l] = matrix3->coeff[i][j]; + } + } +} + +void +gimp_transform_3d_matrix4_to_matrix3 (const GimpMatrix4 *matrix4, + GimpMatrix3 *matrix3, + gint axis) +{ + gint i, j; + gint k, l; + + for (i = 0; i < 3; i++) + { + k = i + (i >= axis); + + for (j = 0; j < 3; j++) + { + l = j + (j >= axis); + + matrix3->coeff[i][j] = matrix4->coeff[k][l]; + } + } +} + +void +gimp_transform_3d_matrix4_translate (GimpMatrix4 *matrix, + gdouble x, + gdouble y, + gdouble z) +{ + gint i; + + for (i = 0; i < 4; i++) + matrix->coeff[0][i] += x * matrix->coeff[3][i]; + + for (i = 0; i < 4; i++) + matrix->coeff[1][i] += y * matrix->coeff[3][i]; + + for (i = 0; i < 4; i++) + matrix->coeff[2][i] += z * matrix->coeff[3][i]; +} + +void +gimp_transform_3d_matrix4_rotate (GimpMatrix4 *matrix, + const GimpVector3 *axis) +{ + GimpMatrix4 rotation; + GimpVector3 v; + + v = gimp_transform_3d_vector3_rotate_val ((GimpVector3) {1.0, 0.0, 0.0}, + *axis); + + rotation.coeff[0][0] = v.x; + rotation.coeff[1][0] = v.y; + rotation.coeff[2][0] = v.z; + rotation.coeff[3][0] = 0.0; + + v = gimp_transform_3d_vector3_rotate_val ((GimpVector3) {0.0, 1.0, 0.0}, + *axis); + + rotation.coeff[0][1] = v.x; + rotation.coeff[1][1] = v.y; + rotation.coeff[2][1] = v.z; + rotation.coeff[3][1] = 0.0; + + v = gimp_transform_3d_vector3_rotate_val ((GimpVector3) {0.0, 0.0, 1.0}, + *axis); + + rotation.coeff[0][2] = v.x; + rotation.coeff[1][2] = v.y; + rotation.coeff[2][2] = v.z; + rotation.coeff[3][2] = 0.0; + + rotation.coeff[0][3] = 0.0; + rotation.coeff[1][3] = 0.0; + rotation.coeff[2][3] = 0.0; + rotation.coeff[3][3] = 1.0; + + gimp_matrix4_mult (&rotation, matrix); +} + +void +gimp_transform_3d_matrix4_rotate_standard (GimpMatrix4 *matrix, + gint axis, + gdouble angle) +{ + gdouble v[3] = {}; + + v[axis] = angle; + + gimp_transform_3d_matrix4_rotate (matrix, &(GimpVector3) {v[0], v[1], v[2]}); +} + +void +gimp_transform_3d_matrix4_rotate_euler (GimpMatrix4 *matrix, + gint rotation_order, + gdouble angle_x, + gdouble angle_y, + gdouble angle_z, + gdouble pivot_x, + gdouble pivot_y, + gdouble pivot_z) +{ + const gdouble angles[3] = {angle_x, angle_y, angle_z}; + gint permutation[3]; + gint i; + + gimp_transform_3d_rotation_order_to_permutation (rotation_order, permutation); + + gimp_transform_3d_matrix4_translate (matrix, -pivot_x, -pivot_y, -pivot_z); + + for (i = 0; i < 3; i++) + { + gimp_transform_3d_matrix4_rotate_standard (matrix, + permutation[i], + angles[permutation[i]]); + } + + gimp_transform_3d_matrix4_translate (matrix, +pivot_x, +pivot_y, +pivot_z); +} + +void +gimp_transform_3d_matrix4_rotate_euler_decompose (GimpMatrix4 *matrix, + gint rotation_order, + gdouble *angle_x, + gdouble *angle_y, + gdouble *angle_z) +{ + GimpMatrix4 m = *matrix; + gdouble * const angles[3] = {angle_x, angle_y, angle_z}; + gint permutation[3]; + gboolean forward; + + gimp_transform_3d_rotation_order_to_permutation (rotation_order, permutation); + + forward = permutation[1] == (permutation[0] + 1) % 3; + + *angles[permutation[2]] = atan2 (m.coeff[permutation[1]][permutation[0]], + m.coeff[permutation[0]][permutation[0]]); + + if (forward) + *angles[permutation[2]] *= -1.0; + + gimp_transform_3d_matrix4_rotate_standard (&m, + permutation[2], + -*angles[permutation[2]]); + + *angles[permutation[1]] = atan2 (m.coeff[permutation[2]][permutation[0]], + m.coeff[permutation[0]][permutation[0]]); + + if (! forward) + *angles[permutation[1]] *= -1.0; + + gimp_transform_3d_matrix4_rotate_standard (&m, + permutation[1], + -*angles[permutation[1]]); + + *angles[permutation[0]] = atan2 (m.coeff[permutation[2]][permutation[1]], + m.coeff[permutation[1]][permutation[1]]); + + if (forward) + *angles[permutation[0]] *= -1.0; +} + +void +gimp_transform_3d_matrix4_perspective (GimpMatrix4 *matrix, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z) +{ + gint i; + + camera_z = MIN (camera_z, -MIN_FOCAL_LENGTH); + + gimp_transform_3d_matrix4_translate (matrix, -camera_x, -camera_y, 0.0); + + for (i = 0; i < 4; i++) + matrix->coeff[3][i] += matrix->coeff[2][i] / -camera_z; + + gimp_transform_3d_matrix4_translate (matrix, +camera_x, +camera_y, 0.0); +} + +void +gimp_transform_3d_matrix (GimpMatrix3 *matrix, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z, + gdouble offset_x, + gdouble offset_y, + gdouble offset_z, + gint rotation_order, + gdouble angle_x, + gdouble angle_y, + gdouble angle_z, + gdouble pivot_x, + gdouble pivot_y, + gdouble pivot_z) +{ + GimpMatrix4 m; + + gimp_matrix4_identity (&m); + gimp_transform_3d_matrix4_rotate_euler (&m, + rotation_order, + angle_x, angle_y, angle_z, + pivot_x, pivot_y, pivot_z); + gimp_transform_3d_matrix4_translate (&m, offset_x, offset_y, offset_z); + gimp_transform_3d_matrix4_perspective (&m, camera_x, camera_y, camera_z); + + gimp_transform_3d_matrix4_to_matrix3 (&m, matrix, 2); +} diff --git a/app/core/gimp-transform-3d-utils.h b/app/core/gimp-transform-3d-utils.h new file mode 100644 index 0000000..92f6795 --- /dev/null +++ b/app/core/gimp-transform-3d-utils.h @@ -0,0 +1,95 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-3d-transform-utils.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_TRANSFORM_3D_UTILS_H__ +#define __GIMP_TRANSFORM_3D_UTILS_H__ + + +gdouble gimp_transform_3d_angle_of_view_to_focal_length (gdouble angle_of_view, + gdouble width, + gdouble height); +gdouble gimp_transform_3d_focal_length_to_angle_of_view (gdouble focal_length, + gdouble width, + gdouble height); + +gint gimp_transform_3d_permutation_to_rotation_order (const gint permutation[3]); +void gimp_transform_3d_rotation_order_to_permutation (gint rotation_order, + gint permutation[3]); +gint gimp_transform_3d_rotation_order_reverse (gint rotation_order); + +void gimp_transform_3d_vector3_rotate (GimpVector3 *vector, + const GimpVector3 *axis); +GimpVector3 gimp_transform_3d_vector3_rotate_val (GimpVector3 vector, + GimpVector3 axis); + +void gimp_transform_3d_matrix3_to_matrix4 (const GimpMatrix3 *matrix3, + GimpMatrix4 *matrix4, + gint axis); +void gimp_transform_3d_matrix4_to_matrix3 (const GimpMatrix4 *matrix4, + GimpMatrix3 *matrix3, + gint axis); + +void gimp_transform_3d_matrix4_translate (GimpMatrix4 *matrix, + gdouble x, + gdouble y, + gdouble z); + +void gimp_transform_3d_matrix4_rotate (GimpMatrix4 *matrix, + const GimpVector3 *axis); +void gimp_transform_3d_matrix4_rotate_standard (GimpMatrix4 *matrix, + gint axis, + gdouble angle); + +void gimp_transform_3d_matrix4_rotate_euler (GimpMatrix4 *matrix, + gint rotation_order, + gdouble angle_x, + gdouble angle_y, + gdouble angle_z, + gdouble pivot_x, + gdouble pivot_y, + gdouble pivot_z); +void gimp_transform_3d_matrix4_rotate_euler_decompose (GimpMatrix4 *matrix, + gint rotation_order, + gdouble *angle_x, + gdouble *angle_y, + gdouble *angle_z); + +void gimp_transform_3d_matrix4_perspective (GimpMatrix4 *matrix, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z); + +void gimp_transform_3d_matrix (GimpMatrix3 *matrix, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z, + gdouble offset_x, + gdouble offset_y, + gdouble offset_z, + gint rotation_order, + gdouble angle_x, + gdouble angle_y, + gdouble angle_z, + gdouble pivot_x, + gdouble pivot_y, + gdouble pivot_z); + + +#endif /* __GIMP_TRANSFORM_3D_UTILS_H__ */ diff --git a/app/core/gimp-transform-resize.c b/app/core/gimp-transform-resize.c new file mode 100644 index 0000000..7aabd67 --- /dev/null +++ b/app/core/gimp-transform-resize.c @@ -0,0 +1,841 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp-transform-resize.h" +#include "gimp-transform-utils.h" +#include "gimp-utils.h" + + +#if defined (HAVE_ISFINITE) +#define FINITE(x) isfinite(x) +#elif defined (HAVE_FINITE) +#define FINITE(x) finite(x) +#elif defined (G_OS_WIN32) +#define FINITE(x) _finite(x) +#else +#error "no FINITE() implementation available?!" +#endif + +#define EPSILON 0.00000001 + + +typedef struct +{ + GimpVector2 a, b, c, d; + gdouble area; + gdouble aspect; +} Rectangle; + + +static void gimp_transform_resize_adjust (const GimpVector2 *points, + gint n_points, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +static void gimp_transform_resize_crop (const GimpVector2 *points, + gint n_points, + gdouble aspect, + gint *x1, + gint *y1, + gint *x2, + gint *y2); + +static void add_rectangle (const GimpVector2 *points, + gint n_points, + Rectangle *r, + GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 d); +static gboolean intersect (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 d, + GimpVector2 *i); +static gboolean intersect_x (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 *i); +static gboolean intersect_y (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 *i); +static gboolean in_poly (const GimpVector2 *points, + gint n_points, + GimpVector2 p); +static gboolean point_on_border (const GimpVector2 *points, + gint n_points, + GimpVector2 p); + +static void find_two_point_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p); +static void find_three_point_rectangle_corner (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p); +static void find_three_point_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p); +static void find_three_point_rectangle_triangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p); +static void find_maximum_aspect_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p); + + +/* + * This function wants to be passed the inverse transformation matrix!! + */ +gboolean +gimp_transform_resize_boundary (const GimpMatrix3 *inv, + GimpTransformResize resize, + gdouble u1, + gdouble v1, + gdouble u2, + gdouble v2, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + GimpVector2 bounds[4]; + GimpVector2 points[5]; + gint n_points; + gboolean valid; + gint i; + + g_return_val_if_fail (inv != NULL, FALSE); + + /* initialize with the original boundary */ + *x1 = floor (u1); + *y1 = floor (v1); + *x2 = ceil (u2); + *y2 = ceil (v2); + + /* if clipping then just return the original rectangle */ + if (resize == GIMP_TRANSFORM_RESIZE_CLIP) + return TRUE; + + bounds[0] = (GimpVector2) { u1, v1 }; + bounds[1] = (GimpVector2) { u2, v1 }; + bounds[2] = (GimpVector2) { u2, v2 }; + bounds[3] = (GimpVector2) { u1, v2 }; + + gimp_transform_polygon (inv, bounds, 4, TRUE, + points, &n_points); + + valid = (n_points >= 2); + + /* check if the transformation matrix is valid at all */ + for (i = 0; i < n_points && valid; i++) + valid = (FINITE (points[i].x) && FINITE (points[i].y)); + + if (! valid) + { + /* since there is no sensible way to deal with this, just do the same as + * with GIMP_TRANSFORM_RESIZE_CLIP: return + */ + return FALSE; + } + + switch (resize) + { + case GIMP_TRANSFORM_RESIZE_ADJUST: + /* return smallest rectangle (with sides parallel to x- and y-axis) + * that surrounds the new points */ + gimp_transform_resize_adjust (points, n_points, + x1, y1, x2, y2); + break; + + case GIMP_TRANSFORM_RESIZE_CROP: + gimp_transform_resize_crop (points, n_points, + 0.0, + x1, y1, x2, y2); + break; + + case GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT: + gimp_transform_resize_crop (points, n_points, + (u2 - u1) / (v2 - v1), + x1, y1, x2, y2); + break; + + case GIMP_TRANSFORM_RESIZE_CLIP: + /* Remove warning about not handling all enum values. We handle + * this case in the beginning of the function + */ + break; + } + + /* ensure that resulting rectangle has at least area 1 */ + if (*x1 == *x2) + (*x2)++; + + if (*y1 == *y2) + (*y2)++; + + return TRUE; +} + +/* this calculates the smallest rectangle (with sides parallel to x- and + * y-axis) that contains the points d1 to d4 + */ +static void +gimp_transform_resize_adjust (const GimpVector2 *points, + gint n_points, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + GimpVector2 top_left; + GimpVector2 bottom_right; + gint i; + + top_left = bottom_right = points[0]; + + for (i = 1; i < n_points; i++) + { + top_left.x = MIN (top_left.x, points[i].x); + top_left.y = MIN (top_left.y, points[i].y); + + bottom_right.x = MAX (bottom_right.x, points[i].x); + bottom_right.y = MAX (bottom_right.y, points[i].y); + } + + *x1 = (gint) floor (top_left.x + EPSILON); + *y1 = (gint) floor (top_left.y + EPSILON); + + *x2 = (gint) ceil (bottom_right.x - EPSILON); + *y2 = (gint) ceil (bottom_right.y - EPSILON); +} + +static void +gimp_transform_resize_crop (const GimpVector2 *orig_points, + gint n_points, + gdouble aspect, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + GimpVector2 points[5]; + Rectangle r; + GimpVector2 t,a; + gint i, j; + gint min; + + memcpy (points, orig_points, sizeof (GimpVector2) * n_points); + + /* find lowest, rightmost corner of surrounding rectangle */ + a.x = 0; + a.y = 0; + for (i = 0; i < 4; i++) + { + if (points[i].x < a.x) + a.x = points[i].x; + + if (points[i].y < a.y) + a.y = points[i].y; + } + + /* and translate all the points to the first quadrant */ + for (i = 0; i < n_points; i++) + { + points[i].x += (-a.x) * 2; + points[i].y += (-a.y) * 2; + } + + /* find the convex hull using Jarvis's March as the points are passed + * in different orders due to gimp_matrix3_transform_point() + */ + min = 0; + for (i = 0; i < n_points; i++) + { + if (points[i].y < points[min].y) + min = i; + } + + t = points[0]; + points[0] = points[min]; + points[min] = t; + + for (i = 1; i < n_points - 1; i++) + { + gdouble min_theta; + gdouble min_mag; + int next; + + next = n_points - 1; + min_theta = 2.0 * G_PI; + min_mag = DBL_MAX; + + for (j = i; j < n_points; j++) + { + gdouble theta; + gdouble sy; + gdouble sx; + gdouble mag; + + sy = points[j].y - points[i - 1].y; + sx = points[j].x - points[i - 1].x; + + if ((sx == 0.0) && (sy == 0.0)) + { + next = j; + break; + } + + theta = atan2 (-sy, -sx); + mag = (sx * sx) + (sy * sy); + + if ((theta < min_theta) || + ((theta == min_theta) && (mag < min_mag))) + { + min_theta = theta; + min_mag = mag; + next = j; + } + } + + t = points[i]; + points[i] = points[next]; + points[next] = t; + } + + /* reverse the order of points */ + for (i = 0; i < n_points / 2; i++) + { + t = points[i]; + points[i] = points[n_points - i - 1]; + points[n_points - i - 1] = t; + } + + r.a.x = r.a.y = r.b.x = r.b.y = r.c.x = r.c.y = r.d.x = r.d.y = r.area = 0; + r.aspect = aspect; + + if (aspect != 0) + { + for (i = 0; i < n_points; i++) + find_maximum_aspect_rectangle (&r, points, n_points, i); + } + else + { + for (i = 0; i < n_points; i++) + { + find_three_point_rectangle (&r, points, n_points, i); + find_three_point_rectangle_corner (&r, points, n_points, i); + find_two_point_rectangle (&r, points, n_points, i); + find_three_point_rectangle_triangle (&r, points, n_points, i); + } + } + + if (r.area == 0) + { + /* saveguard if something went wrong, adjust and give warning */ + gimp_transform_resize_adjust (orig_points, n_points, + x1, y1, x2, y2); + g_printerr ("no rectangle found by algorithm, no cropping done\n"); + return; + } + else + { + /* round and translate the calculated points back */ + *x1 = floor (r.a.x + 0.5); + *y1 = floor (r.a.y + 0.5); + *x2 = ceil (r.c.x - 0.5); + *y2 = ceil (r.c.y - 0.5); + + *x1 = *x1 - ((-a.x) * 2); + *y1 = *y1 - ((-a.y) * 2); + *x2 = *x2 - ((-a.x) * 2); + *y2 = *y2 - ((-a.y) * 2); + return; + } +} + +static void +find_three_point_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p) +{ + GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */ + GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */ + GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */ + GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */ + GimpVector2 i1; /* intersection point */ + GimpVector2 i2; /* intersection point */ + GimpVector2 i3; /* intersection point */ + + if (intersect_x (b, c, a, &i1) && + intersect_y (c, d, i1, &i2) && + intersect_x (d, a, i2, &i3)) + add_rectangle (points, n_points, r, i3, i3, i1, i1); + + if (intersect_y (b, c, a, &i1) && + intersect_x (c, d, i1, &i2) && + intersect_y (d, a, i2, &i3)) + add_rectangle (points, n_points, r, i3, i3, i1, i1); + + if (intersect_x (d, c, a, &i1) && + intersect_y (c, b, i1, &i2) && + intersect_x (b, a, i2, &i3)) + add_rectangle (points, n_points, r, i3, i3, i1, i1); + + if (intersect_y (d, c, a, &i1) && + intersect_x (c, b, i1, &i2) && + intersect_y (b, a, i2, &i3)) + add_rectangle (points, n_points, r, i3, i3, i1, i1); +} + +static void +find_three_point_rectangle_corner (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p) +{ + GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */ + GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */ + GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 2 */ + GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 2 1 */ + GimpVector2 i1; /* intersection point */ + GimpVector2 i2; /* intersection point */ + + if (intersect_x (b, c, a , &i1) && + intersect_y (c, d, i1, &i2)) + add_rectangle (points, n_points, r, a, a, i1, i2); + + if (intersect_y (b, c, a , &i1) && + intersect_x (c, d, i1, &i2)) + add_rectangle (points, n_points, r, a, a, i1, i2); + + if (intersect_x (c, d, a , &i1) && + intersect_y (b, c, i1, &i2)) + add_rectangle (points, n_points, r, a, a, i1, i2); + + if (intersect_y (c, d, a , &i1) && + intersect_x (b, c, i1, &i2)) + add_rectangle (points, n_points, r, a, a, i1, i2); +} + +static void +find_two_point_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p) +{ + GimpVector2 a = points[ p % n_points]; /* 0 1 2 3 */ + GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */ + GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */ + GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */ + GimpVector2 i1; /* intersection point */ + GimpVector2 i2; /* intersection point */ + GimpVector2 mid; /* Mid point */ + + add_rectangle (points, n_points, r, a, a, c, c); + add_rectangle (points, n_points, r, b, b, d, d); + + if (intersect_x (c, b, a, &i1) && + intersect_y (c, b, a, &i2)) + { + mid.x = ( i1.x + i2.x ) / 2.0; + mid.y = ( i1.y + i2.y ) / 2.0; + + add_rectangle (points, n_points, r, a, a, mid, mid); + } +} + +static void +find_three_point_rectangle_triangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p) +{ + GimpVector2 a = points[p % n_points]; /* 0 1 2 3 */ + GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */ + GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */ + GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */ + GimpVector2 i1; /* intersection point */ + GimpVector2 i2; /* intersection point */ + GimpVector2 mid; + + mid.x = (a.x + b.x) / 2.0; + mid.y = (a.y + b.y) / 2.0; + + if (intersect_x (b, c, mid, &i1) && + intersect_y (a, d, mid, &i2)) + add_rectangle (points, n_points, r, mid, mid, i1, i2); + + if (intersect_y (b, c, mid, &i1) && + intersect_x (a, d, mid, &i2)) + add_rectangle (points, n_points, r, mid, mid, i1, i2); + + if (intersect_x (a, d, mid, &i1) && + intersect_y (b, c, mid, &i2)) + add_rectangle (points, n_points, r, mid, mid, i1, i2); + + if (intersect_y (a, d, mid, &i1) && + intersect_x (b, c, mid, &i2)) + add_rectangle (points, n_points, r, mid, mid, i1, i2); +} + +static void +find_maximum_aspect_rectangle (Rectangle *r, + const GimpVector2 *points, + gint n_points, + gint p) +{ + GimpVector2 a = points[ p % n_points]; /* 0 1 2 3 */ + GimpVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */ + GimpVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */ + GimpVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */ + GimpVector2 i1; /* intersection point */ + GimpVector2 i2; /* intersection point */ + GimpVector2 i3; /* intersection point */ + + if (intersect_x (b, c, a, &i1)) + { + i2.x = i1.x + 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (c, d, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + i2.x = i1.x - 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (c, d, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + } + + if (intersect_y (b, c, a, &i1)) + { + i2.x = i1.x + 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (c, d, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + i2.x = i1.x - 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (c, d, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + } + + if (intersect_x (c, d, a, &i1)) + { + i2.x = i1.x + 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (b, c, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + i2.x = i1.x - 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (b, c, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + } + + if (intersect_y (c, d, a, &i1)) + { + i2.x = i1.x + 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (b, c, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + i2.x = i1.x - 1.0 * r->aspect; + i2.y = i1.y + 1.0; + + if (intersect (d, a, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (a, b, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + + if (intersect (b, c, i1, i2, &i3)) + add_rectangle (points, n_points, r, i1, i3, i1, i3); + } +} + +/* check if point is inside the polygon "points", if point is on border + * its still inside. + */ +static gboolean +in_poly (const GimpVector2 *points, + gint n_points, + GimpVector2 p) +{ + GimpVector2 p1, p2; + gint counter = 0; + gint i; + + p1 = points[0]; + + for (i = 1; i <= n_points; i++) + { + p2 = points[i % n_points]; + + if (p.y > MIN (p1.y, p2.y)) + { + if (p.y <= MAX (p1.y, p2.y)) + { + if (p.x <= MAX (p1.x, p2.x)) + { + if (p1.y != p2.y) + { + gdouble xinters = ((p.y - p1.y) * (p2.x - p1.x) / + (p2.y - p1.y) + p1.x); + + if (p1.x == p2.x || p.x <= xinters) + counter++; + } + } + } + } + + p1 = p2; + } + + /* border check */ + if (point_on_border (points, n_points, p)) + return TRUE; + + return (counter % 2 != 0); +} + +/* check if the point p lies on the polygon "points" + */ +static gboolean +point_on_border (const GimpVector2 *points, + gint n_points, + GimpVector2 p) +{ + gint i; + + for (i = 0; i <= n_points; i++) + { + GimpVector2 a = points[i % n_points]; + GimpVector2 b = points[(i + 1) % n_points]; + gdouble a1 = (b.y - a.y); + gdouble b1 = (a.x - b.x); + gdouble c1 = a1 * a.x + b1 * a.y; + gdouble c2 = a1 * p.x + b1 * p.y; + + if (ABS (c1 - c2) < EPSILON && + MIN (a.x, b.x) <= p.x && + MAX (a.x, b.x) >= p.x && + MIN (a.y, b.y) <= p.y && + MAX (a.y, b.y) >= p.y) + return TRUE; + } + + return FALSE; +} + +/* calculate the intersection point of the line a-b with the line c-d + * and write it to i, if existing. + */ +static gboolean +intersect (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 d, + GimpVector2 *i) +{ + gdouble a1 = (b.y - a.y); + gdouble b1 = (a.x - b.x); + gdouble c1 = a1 * a.x + b1 * a.y; + + gdouble a2 = (d.y - c.y); + gdouble b2 = (c.x - d.x); + gdouble c2 = a2 * c.x + b2 * c.y; + gdouble det = a1 * b2 - a2 * b1; + + if (det == 0) + return FALSE; + + i->x = (b2 * c1 - b1 * c2) / det; + i->y = (a1 * c2 - a2 * c1) / det; + + return TRUE; +} + +/* calculate the intersection point of the line a-b with the vertical line + * through c and write it to i, if existing. + */ +static gboolean +intersect_x (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 *i) +{ + GimpVector2 d = c; + d.y += 1; + + return intersect(a,b,c,d,i); +} + +/* calculate the intersection point of the line a-b with the horizontal line + * through c and write it to i, if existing. + */ +static gboolean +intersect_y (GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 *i) +{ + GimpVector2 d = c; + d.x += 1; + + return intersect(a,b,c,d,i); +} + +/* this takes the smallest ortho-aligned (the sides of the rectangle are + * parallel to the x- and y-axis) rectangle fitting around the points a to d, + * checks if the whole rectangle is inside the polygon described by points and + * writes it to r if the area is bigger than the rectangle already stored in r. + */ +static void +add_rectangle (const GimpVector2 *points, + gint n_points, + Rectangle *r, + GimpVector2 a, + GimpVector2 b, + GimpVector2 c, + GimpVector2 d) +{ + gdouble width; + gdouble height; + gdouble minx, maxx; + gdouble miny, maxy; + + /* get the orthoaligned (the sides of the rectangle are parallel to the x- + * and y-axis) rectangle surrounding the points a to d. + */ + minx = MIN4 (a.x, b.x, c.x, d.x); + maxx = MAX4 (a.x, b.x, c.x, d.x); + miny = MIN4 (a.y, b.y, c.y, d.y); + maxy = MAX4 (a.y, b.y, c.y, d.y); + + a.x = minx; + a.y = miny; + + b.x = maxx; + b.y = miny; + + c.x = maxx; + c.y = maxy; + + d.x = minx; + d.y = maxy; + + width = maxx - minx; + height = maxy - miny; + + /* check if this rectangle is inside the polygon "points" */ + if (in_poly (points, n_points, a) && + in_poly (points, n_points, b) && + in_poly (points, n_points, c) && + in_poly (points, n_points, d)) + { + gdouble area = width * height; + + /* check if the new rectangle is larger (in terms of area) + * than the currently stored rectangle in r, if yes store + * new rectangle to r + */ + if (r->area <= area) + { + r->a.x = a.x; + r->a.y = a.y; + + r->b.x = b.x; + r->b.y = b.y; + + r->c.x = c.x; + r->c.y = c.y; + + r->d.x = d.x; + r->d.y = d.y; + + r->area = area; + } + } +} diff --git a/app/core/gimp-transform-resize.h b/app/core/gimp-transform-resize.h new file mode 100644 index 0000000..4ebb53f --- /dev/null +++ b/app/core/gimp-transform-resize.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TRANSFORM_RESIZE_H__ +#define __GIMP_TRANSFORM_RESIZE_H__ + + +gboolean gimp_transform_resize_boundary (const GimpMatrix3 *inv, + GimpTransformResize resize, + gdouble u1, + gdouble v1, + gdouble u2, + gdouble v2, + gint *x1, + gint *y1, + gint *x2, + gint *y2); + + +#endif /* __GIMP_TRANSFORM_RESIZE_H__ */ diff --git a/app/core/gimp-transform-utils.c b/app/core/gimp-transform-utils.c new file mode 100644 index 0000000..555ff09 --- /dev/null +++ b/app/core/gimp-transform-utils.c @@ -0,0 +1,1211 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp-transform-utils.h" +#include "gimpcoords.h" +#include "gimpcoords-interpolate.h" + + +#define EPSILON 1e-6 + + +void +gimp_transform_get_rotate_center (gint x, + gint y, + gint width, + gint height, + gboolean auto_center, + gdouble *center_x, + gdouble *center_y) +{ + g_return_if_fail (center_x != NULL); + g_return_if_fail (center_y != NULL); + + if (auto_center) + { + *center_x = (gdouble) x + (gdouble) width / 2.0; + *center_y = (gdouble) y + (gdouble) height / 2.0; + } +} + +void +gimp_transform_get_flip_axis (gint x, + gint y, + gint width, + gint height, + GimpOrientationType flip_type, + gboolean auto_center, + gdouble *axis) +{ + g_return_if_fail (axis != NULL); + + if (auto_center) + { + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + *axis = ((gdouble) x + (gdouble) width / 2.0); + break; + + case GIMP_ORIENTATION_VERTICAL: + *axis = ((gdouble) y + (gdouble) height / 2.0); + break; + + default: + g_return_if_reached (); + break; + } + } +} + +void +gimp_transform_matrix_flip (GimpMatrix3 *matrix, + GimpOrientationType flip_type, + gdouble axis) +{ + g_return_if_fail (matrix != NULL); + + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_matrix3_translate (matrix, - axis, 0.0); + gimp_matrix3_scale (matrix, -1.0, 1.0); + gimp_matrix3_translate (matrix, axis, 0.0); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_matrix3_translate (matrix, 0.0, - axis); + gimp_matrix3_scale (matrix, 1.0, -1.0); + gimp_matrix3_translate (matrix, 0.0, axis); + break; + + case GIMP_ORIENTATION_UNKNOWN: + break; + } +} + +void +gimp_transform_matrix_flip_free (GimpMatrix3 *matrix, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + gdouble angle; + + g_return_if_fail (matrix != NULL); + + angle = atan2 (y2 - y1, x2 - x1); + + gimp_matrix3_identity (matrix); + gimp_matrix3_translate (matrix, -x1, -y1); + gimp_matrix3_rotate (matrix, -angle); + gimp_matrix3_scale (matrix, 1.0, -1.0); + gimp_matrix3_rotate (matrix, angle); + gimp_matrix3_translate (matrix, x1, y1); +} + +void +gimp_transform_matrix_rotate (GimpMatrix3 *matrix, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y) +{ + gdouble angle = 0; + + switch (rotate_type) + { + case GIMP_ROTATE_90: + angle = G_PI_2; + break; + case GIMP_ROTATE_180: + angle = G_PI; + break; + case GIMP_ROTATE_270: + angle = - G_PI_2; + break; + } + + gimp_transform_matrix_rotate_center (matrix, center_x, center_y, angle); +} + +void +gimp_transform_matrix_rotate_rect (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble angle) +{ + gdouble center_x; + gdouble center_y; + + g_return_if_fail (matrix != NULL); + + center_x = (gdouble) x + (gdouble) width / 2.0; + center_y = (gdouble) y + (gdouble) height / 2.0; + + gimp_matrix3_translate (matrix, -center_x, -center_y); + gimp_matrix3_rotate (matrix, angle); + gimp_matrix3_translate (matrix, +center_x, +center_y); +} + +void +gimp_transform_matrix_rotate_center (GimpMatrix3 *matrix, + gdouble center_x, + gdouble center_y, + gdouble angle) +{ + g_return_if_fail (matrix != NULL); + + gimp_matrix3_translate (matrix, -center_x, -center_y); + gimp_matrix3_rotate (matrix, angle); + gimp_matrix3_translate (matrix, +center_x, +center_y); +} + +void +gimp_transform_matrix_scale (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble t_x, + gdouble t_y, + gdouble t_width, + gdouble t_height) +{ + gdouble scale_x = 1.0; + gdouble scale_y = 1.0; + + g_return_if_fail (matrix != NULL); + + if (width > 0) + scale_x = t_width / (gdouble) width; + + if (height > 0) + scale_y = t_height / (gdouble) height; + + gimp_matrix3_identity (matrix); + gimp_matrix3_translate (matrix, -x, -y); + gimp_matrix3_scale (matrix, scale_x, scale_y); + gimp_matrix3_translate (matrix, t_x, t_y); +} + +void +gimp_transform_matrix_shear (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + GimpOrientationType orientation, + gdouble amount) +{ + gdouble center_x; + gdouble center_y; + + g_return_if_fail (matrix != NULL); + + if (width == 0) + width = 1; + + if (height == 0) + height = 1; + + center_x = (gdouble) x + (gdouble) width / 2.0; + center_y = (gdouble) y + (gdouble) height / 2.0; + + gimp_matrix3_identity (matrix); + gimp_matrix3_translate (matrix, -center_x, -center_y); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + gimp_matrix3_xshear (matrix, amount / height); + else + gimp_matrix3_yshear (matrix, amount / width); + + gimp_matrix3_translate (matrix, +center_x, +center_y); +} + +void +gimp_transform_matrix_perspective (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble t_x1, + gdouble t_y1, + gdouble t_x2, + gdouble t_y2, + gdouble t_x3, + gdouble t_y3, + gdouble t_x4, + gdouble t_y4) +{ + GimpMatrix3 trafo; + gdouble scalex; + gdouble scaley; + + g_return_if_fail (matrix != NULL); + + scalex = scaley = 1.0; + + if (width > 0) + scalex = 1.0 / (gdouble) width; + + if (height > 0) + scaley = 1.0 / (gdouble) height; + + gimp_matrix3_translate (matrix, -x, -y); + gimp_matrix3_scale (matrix, scalex, scaley); + + /* Determine the perspective transform that maps from + * the unit cube to the transformed coordinates + */ + { + gdouble dx1, dx2, dx3, dy1, dy2, dy3; + + dx1 = t_x2 - t_x4; + dx2 = t_x3 - t_x4; + dx3 = t_x1 - t_x2 + t_x4 - t_x3; + + dy1 = t_y2 - t_y4; + dy2 = t_y3 - t_y4; + dy3 = t_y1 - t_y2 + t_y4 - t_y3; + + /* Is the mapping affine? */ + if ((dx3 == 0.0) && (dy3 == 0.0)) + { + trafo.coeff[0][0] = t_x2 - t_x1; + trafo.coeff[0][1] = t_x4 - t_x2; + trafo.coeff[0][2] = t_x1; + trafo.coeff[1][0] = t_y2 - t_y1; + trafo.coeff[1][1] = t_y4 - t_y2; + trafo.coeff[1][2] = t_y1; + trafo.coeff[2][0] = 0.0; + trafo.coeff[2][1] = 0.0; + } + else + { + gdouble det1, det2; + + det1 = dx3 * dy2 - dy3 * dx2; + det2 = dx1 * dy2 - dy1 * dx2; + + trafo.coeff[2][0] = (det2 == 0.0) ? 1.0 : det1 / det2; + + det1 = dx1 * dy3 - dy1 * dx3; + + trafo.coeff[2][1] = (det2 == 0.0) ? 1.0 : det1 / det2; + + trafo.coeff[0][0] = t_x2 - t_x1 + trafo.coeff[2][0] * t_x2; + trafo.coeff[0][1] = t_x3 - t_x1 + trafo.coeff[2][1] * t_x3; + trafo.coeff[0][2] = t_x1; + + trafo.coeff[1][0] = t_y2 - t_y1 + trafo.coeff[2][0] * t_y2; + trafo.coeff[1][1] = t_y3 - t_y1 + trafo.coeff[2][1] * t_y3; + trafo.coeff[1][2] = t_y1; + } + + trafo.coeff[2][2] = 1.0; + } + + gimp_matrix3_mult (&trafo, matrix); +} + +/* modified gaussian algorithm + * solves a system of linear equations + * + * Example: + * 1x + 2y + 4z = 25 + * 2x + 1y = 4 + * 3x + 5y + 2z = 23 + * Solution: x=1, y=2, z=5 + * + * Input: + * matrix = { 1,2,4,25,2,1,0,4,3,5,2,23 } + * s = 3 (Number of variables) + * Output: + * return value == TRUE (TRUE, if there is a single unique solution) + * solution == { 1,2,5 } (if the return value is FALSE, the content + * of solution is of no use) + */ +static gboolean +mod_gauss (gdouble matrix[], + gdouble solution[], + gint s) +{ + gint p[s]; /* row permutation */ + gint i, j, r, temp; + gdouble q; + gint t = s + 1; + + for (i = 0; i < s; i++) + { + p[i] = i; + } + + for (r = 0; r < s; r++) + { + /* make sure that (r,r) is not 0 */ + if (fabs (matrix[p[r] * t + r]) <= EPSILON) + { + /* we need to permutate rows */ + for (i = r + 1; i <= s; i++) + { + if (i == s) + { + /* if this happens, the linear system has zero or + * more than one solutions. + */ + return FALSE; + } + + if (fabs (matrix[p[i] * t + r]) > EPSILON) + break; + } + + temp = p[r]; + p[r] = p[i]; + p[i] = temp; + } + + /* make (r,r) == 1 */ + q = 1.0 / matrix[p[r] * t + r]; + matrix[p[r] * t + r] = 1.0; + + for (j = r + 1; j < t; j++) + { + matrix[p[r] * t + j] *= q; + } + + /* make that all entries in column r are 0 (except (r,r)) */ + for (i = 0; i < s; i++) + { + if (i == r) + continue; + + for (j = r + 1; j < t ; j++) + { + matrix[p[i] * t + j] -= matrix[p[r] * t + j] * matrix[p[i] * t + r]; + } + + /* we don't need to execute the following line + * since we won't access this element again: + * + * matrix[p[i] * t + r] = 0.0; + */ + } + } + + for (i = 0; i < s; i++) + { + solution[i] = matrix[p[i] * t + s]; + } + + return TRUE; +} + +/* multiplies 'matrix' by the matrix that transforms a set of 4 'input_points' + * to corresponding 'output_points', if such matrix exists, and is valid (i.e., + * keeps the output points in front of the camera). + * + * returns TRUE if successful. + */ +gboolean +gimp_transform_matrix_generic (GimpMatrix3 *matrix, + const GimpVector2 input_points[4], + const GimpVector2 output_points[4]) +{ + GimpMatrix3 trafo; + gdouble coeff[8 * 9]; + gboolean negative = -1; + gint i; + gboolean result = TRUE; + + g_return_val_if_fail (matrix != NULL, FALSE); + g_return_val_if_fail (input_points != NULL, FALSE); + g_return_val_if_fail (output_points != NULL, FALSE); + + /* find the matrix that transforms 'input_points' to 'output_points', whose + * (3, 3) coeffcient is 1, by solving a system of linear equations whose + * solution is the remaining 8 coefficients. + */ + for (i = 0; i < 4; i++) + { + coeff[i * 9 + 0] = input_points[i].x; + coeff[i * 9 + 1] = input_points[i].y; + coeff[i * 9 + 2] = 1.0; + coeff[i * 9 + 3] = 0.0; + coeff[i * 9 + 4] = 0.0; + coeff[i * 9 + 5] = 0.0; + coeff[i * 9 + 6] = -input_points[i].x * output_points[i].x; + coeff[i * 9 + 7] = -input_points[i].y * output_points[i].x; + coeff[i * 9 + 8] = output_points[i].x; + + coeff[(i + 4) * 9 + 0] = 0.0; + coeff[(i + 4) * 9 + 1] = 0.0; + coeff[(i + 4) * 9 + 2] = 0.0; + coeff[(i + 4) * 9 + 3] = input_points[i].x; + coeff[(i + 4) * 9 + 4] = input_points[i].y; + coeff[(i + 4) * 9 + 5] = 1.0; + coeff[(i + 4) * 9 + 6] = -input_points[i].x * output_points[i].y; + coeff[(i + 4) * 9 + 7] = -input_points[i].y * output_points[i].y; + coeff[(i + 4) * 9 + 8] = output_points[i].y; + } + + /* if there is no solution, bail */ + if (! mod_gauss (coeff, (gdouble *) trafo.coeff, 8)) + return FALSE; + + trafo.coeff[2][2] = 1.0; + + /* make sure that none of the input points maps to a point at infinity, and + * that all output points are on the same side of the camera. + */ + for (i = 0; i < 4; i++) + { + gdouble w; + gboolean neg; + + w = trafo.coeff[2][0] * input_points[i].x + + trafo.coeff[2][1] * input_points[i].y + + trafo.coeff[2][2]; + + if (fabs (w) <= EPSILON) + result = FALSE; + + neg = (w < 0.0); + + if (negative < 0) + { + negative = neg; + } + else if (neg != negative) + { + result = FALSE; + break; + } + } + + /* if the output points are all behind the camera, negate the matrix, which + * would map the input points to the corresponding points in front of the + * camera. + */ + if (negative > 0) + { + gint r; + gint c; + + for (r = 0; r < 3; r++) + { + for (c = 0; c < 3; c++) + { + trafo.coeff[r][c] = -trafo.coeff[r][c]; + } + } + } + + /* append the transformation to 'matrix' */ + gimp_matrix3_mult (&trafo, matrix); + + return result; +} + +gboolean +gimp_transform_polygon_is_convex (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble x3, + gdouble y3, + gdouble x4, + gdouble y4) +{ + gdouble z1, z2, z3, z4; + + /* We test if the transformed polygon is convex. if z1 and z2 have + * the same sign as well as z3 and z4 the polygon is convex. + */ + z1 = ((x2 - x1) * (y4 - y1) - + (x4 - x1) * (y2 - y1)); + z2 = ((x4 - x1) * (y3 - y1) - + (x3 - x1) * (y4 - y1)); + z3 = ((x4 - x2) * (y3 - y2) - + (x3 - x2) * (y4 - y2)); + z4 = ((x3 - x2) * (y1 - y2) - + (x1 - x2) * (y3 - y2)); + + return (z1 * z2 > 0) && (z3 * z4 > 0); +} + +/* transforms the polygon or polyline, whose vertices are given by 'vertices', + * by 'matrix', performing clipping by the near plane. 'closed' indicates + * whether the vertices represent a polygon ('closed == TRUE') or a polyline + * ('closed == FALSE'). + * + * returns the transformed vertices in 't_vertices', and their count in + * 'n_t_vertices'. the minimal possible number of transformed vertices is 0, + * which happens when the entire input is clipped. in general, the maximal + * possible number of transformed vertices is '3 * n_vertices / 2' (rounded + * down), however, for convex polygons the number is 'n_vertices + 1', and for + * a single line segment ('n_vertices == 2' and 'closed == FALSE') the number + * is 2. + * + * 't_vertices' may not alias 'vertices', except when transforming a single + * line segment. + */ +void +gimp_transform_polygon (const GimpMatrix3 *matrix, + const GimpVector2 *vertices, + gint n_vertices, + gboolean closed, + GimpVector2 *t_vertices, + gint *n_t_vertices) +{ + GimpVector3 curr; + gboolean curr_visible; + gint i; + + g_return_if_fail (matrix != NULL); + g_return_if_fail (vertices != NULL); + g_return_if_fail (n_vertices >= 0); + g_return_if_fail (t_vertices != NULL); + g_return_if_fail (n_t_vertices != NULL); + + *n_t_vertices = 0; + + if (n_vertices == 0) + return; + + curr.x = matrix->coeff[0][0] * vertices[0].x + + matrix->coeff[0][1] * vertices[0].y + + matrix->coeff[0][2]; + curr.y = matrix->coeff[1][0] * vertices[0].x + + matrix->coeff[1][1] * vertices[0].y + + matrix->coeff[1][2]; + curr.z = matrix->coeff[2][0] * vertices[0].x + + matrix->coeff[2][1] * vertices[0].y + + matrix->coeff[2][2]; + + curr_visible = (curr.z >= GIMP_TRANSFORM_NEAR_Z); + + for (i = 0; i < n_vertices; i++) + { + if (curr_visible) + { + t_vertices[(*n_t_vertices)++] = (GimpVector2) { curr.x / curr.z, + curr.y / curr.z }; + } + + if (i < n_vertices - 1 || closed) + { + GimpVector3 next; + gboolean next_visible; + gint j = (i + 1) % n_vertices; + + next.x = matrix->coeff[0][0] * vertices[j].x + + matrix->coeff[0][1] * vertices[j].y + + matrix->coeff[0][2]; + next.y = matrix->coeff[1][0] * vertices[j].x + + matrix->coeff[1][1] * vertices[j].y + + matrix->coeff[1][2]; + next.z = matrix->coeff[2][0] * vertices[j].x + + matrix->coeff[2][1] * vertices[j].y + + matrix->coeff[2][2]; + + next_visible = (next.z >= GIMP_TRANSFORM_NEAR_Z); + + if (next_visible != curr_visible) + { + gdouble ratio = (curr.z - GIMP_TRANSFORM_NEAR_Z) / (curr.z - next.z); + + t_vertices[(*n_t_vertices)++] = + (GimpVector2) { (curr.x + (next.x - curr.x) * ratio) / GIMP_TRANSFORM_NEAR_Z, + (curr.y + (next.y - curr.y) * ratio) / GIMP_TRANSFORM_NEAR_Z }; + } + + curr = next; + curr_visible = next_visible; + } + } +} + +/* same as gimp_transform_polygon(), but using GimpCoords as the vertex type, + * instead of GimpVector2. + */ +void +gimp_transform_polygon_coords (const GimpMatrix3 *matrix, + const GimpCoords *vertices, + gint n_vertices, + gboolean closed, + GimpCoords *t_vertices, + gint *n_t_vertices) +{ + GimpVector3 curr; + gboolean curr_visible; + gint i; + + g_return_if_fail (matrix != NULL); + g_return_if_fail (vertices != NULL); + g_return_if_fail (n_vertices >= 0); + g_return_if_fail (t_vertices != NULL); + g_return_if_fail (n_t_vertices != NULL); + + *n_t_vertices = 0; + + if (n_vertices == 0) + return; + + curr.x = matrix->coeff[0][0] * vertices[0].x + + matrix->coeff[0][1] * vertices[0].y + + matrix->coeff[0][2]; + curr.y = matrix->coeff[1][0] * vertices[0].x + + matrix->coeff[1][1] * vertices[0].y + + matrix->coeff[1][2]; + curr.z = matrix->coeff[2][0] * vertices[0].x + + matrix->coeff[2][1] * vertices[0].y + + matrix->coeff[2][2]; + + curr_visible = (curr.z >= GIMP_TRANSFORM_NEAR_Z); + + for (i = 0; i < n_vertices; i++) + { + if (curr_visible) + { + t_vertices[*n_t_vertices] = vertices[i]; + t_vertices[*n_t_vertices].x = curr.x / curr.z; + t_vertices[*n_t_vertices].y = curr.y / curr.z; + + (*n_t_vertices)++; + } + + if (i < n_vertices - 1 || closed) + { + GimpVector3 next; + gboolean next_visible; + gint j = (i + 1) % n_vertices; + + next.x = matrix->coeff[0][0] * vertices[j].x + + matrix->coeff[0][1] * vertices[j].y + + matrix->coeff[0][2]; + next.y = matrix->coeff[1][0] * vertices[j].x + + matrix->coeff[1][1] * vertices[j].y + + matrix->coeff[1][2]; + next.z = matrix->coeff[2][0] * vertices[j].x + + matrix->coeff[2][1] * vertices[j].y + + matrix->coeff[2][2]; + + next_visible = (next.z >= GIMP_TRANSFORM_NEAR_Z); + + if (next_visible != curr_visible) + { + gdouble ratio = (curr.z - GIMP_TRANSFORM_NEAR_Z) / (curr.z - next.z); + + gimp_coords_mix (1.0 - ratio, &vertices[i], + ratio, &vertices[j], + &t_vertices[*n_t_vertices]); + + t_vertices[*n_t_vertices].x = (curr.x + (next.x - curr.x) * ratio) / + GIMP_TRANSFORM_NEAR_Z; + t_vertices[*n_t_vertices].y = (curr.y + (next.y - curr.y) * ratio) / + GIMP_TRANSFORM_NEAR_Z; + + (*n_t_vertices)++; + } + + curr = next; + curr_visible = next_visible; + } + } +} + +/* returns the value of the polynomial 'poly', of degree 'degree', at 'x'. the + * coefficients of 'poly' should be specified in descending-degree order. + */ +static gdouble +polynomial_eval (const gdouble *poly, + gint degree, + gdouble x) +{ + gdouble y = poly[0]; + gint i; + + for (i = 1; i <= degree; i++) + y = y * x + poly[i]; + + return y; +} + +/* derives the polynomial 'poly', of degree 'degree'. + * + * returns the derivative in 'result'. + */ +static void +polynomial_derive (const gdouble *poly, + gint degree, + gdouble *result) +{ + while (degree) + *result++ = *poly++ * degree--; +} + +/* finds the real odd-multiplicity root of the polynomial 'poly', of degree + * 'degree', inside the range '(x1, x2)'. + * + * returns TRUE if such a root exists, and stores its value in '*root'. + * + * 'poly' shall be monotonic in the range '(x1, x2)'. + */ +static gboolean +polynomial_odd_root (const gdouble *poly, + gint degree, + gdouble x1, + gdouble x2, + gdouble *root) +{ + gdouble y1; + gdouble y2; + gint i; + + y1 = polynomial_eval (poly, degree, x1); + y2 = polynomial_eval (poly, degree, x2); + + if (y1 * y2 > -EPSILON) + { + /* the two endpoints have the same sign, or one of them is zero. there's + * no root inside the range. + */ + return FALSE; + } + else if (y1 > 0.0) + { + gdouble t; + + /* if the first endpoint is positive, swap the endpoints, so that the + * first endpoint is always negative, and the second endpoint is always + * positive. + */ + + t = x1; + x1 = x2; + x2 = t; + } + + /* approximate the root using binary search */ + for (i = 0; i < 53; i++) + { + gdouble x = (x1 + x2) / 2.0; + gdouble y = polynomial_eval (poly, degree, x); + + if (y > 0.0) + x2 = x; + else + x1 = x; + } + + *root = (x1 + x2) / 2.0; + + return TRUE; +} + +/* finds the real odd-multiplicity roots of the polynomial 'poly', of degree + * 'degree', inside the range '(x1, x2)'. + * + * returns the roots in 'roots', in ascending order, and their count in + * 'n_roots'. + */ +static void +polynomial_odd_roots (const gdouble *poly, + gint degree, + gdouble x1, + gdouble x2, + gdouble *roots, + gint *n_roots) +{ + *n_roots = 0; + + /* find the real degree of the polynomial (skip any leading coefficients that + * are 0) + */ + for (; degree && fabs (*poly) < EPSILON; poly++, degree--); + + #define ADD_ROOT(root) \ + do \ + { \ + gdouble r = (root); \ + \ + if (r > x1 && r < x2) \ + roots[(*n_roots)++] = r; \ + } \ + while (FALSE) + + switch (degree) + { + /* constant case */ + case 0: + break; + + /* linear case */ + case 1: + ADD_ROOT (-poly[1] / poly[0]); + break; + + /* quadratic case */ + case 2: + { + gdouble s = SQR (poly[1]) - 4 * poly[0] * poly[2]; + + if (s > EPSILON) + { + s = sqrt (s); + + if (poly[0] < 0.0) + s = -s; + + ADD_ROOT ((-poly[1] - s) / (2.0 * poly[0])); + ADD_ROOT ((-poly[1] + s) / (2.0 * poly[0])); + } + + break; + } + + /* general case */ + default: + { + gdouble deriv[degree]; + gdouble deriv_roots[degree - 1]; + gint n_deriv_roots; + gdouble a; + gdouble b; + gint i; + + /* find the odd roots of the derivative, i.e., the local extrema of the + * polynomial + */ + polynomial_derive (poly, degree, deriv); + polynomial_odd_roots (deriv, degree - 1, x1, x2, + deriv_roots, &n_deriv_roots); + + /* search for roots between each consecutive pair of extrema, including + * the endpoints + */ + a = x1; + + for (i = 0; i <= n_deriv_roots; i++) + { + if (i < n_deriv_roots) + b = deriv_roots[i]; + else + b = x2; + + *n_roots += polynomial_odd_root (poly, degree, a, b, + &roots[*n_roots]); + + a = b; + } + + break; + } + } + + #undef ADD_ROOT +} + +/* clips the cubic bezier segment, defined by the four control points 'bezier', + * to the halfplane 'ax + by + c >= 0'. + * + * returns the clipped set of bezier segments in 'c_bezier', and their count in + * 'n_c_bezier'. the minimal possible number of clipped segments is 0, which + * happens when the entire segment is clipped. the maximal possible number of + * clipped segments is 2. + * + * if the first clipped segment is an initial segment of 'bezier', sets + * '*start_in' to TRUE, otherwise to FALSE. if the last clipped segment is a + * final segment of 'bezier', sets '*end_in' to TRUE, otherwise to FALSE. + * + * 'c_bezier' may not alias 'bezier'. + */ +static void +clip_bezier (const GimpCoords bezier[4], + gdouble a, + gdouble b, + gdouble c, + GimpCoords c_bezier[2][4], + gint *n_c_bezier, + gboolean *start_in, + gboolean *end_in) +{ + gdouble dot[4]; + gdouble poly[4]; + gdouble roots[5]; + gint n_roots; + gint n_positive; + gint i; + + n_positive = 0; + + for (i = 0; i < 4; i++) + { + dot[i] = a * bezier[i].x + b * bezier[i].y + c; + + n_positive += (dot[i] >= 0.0); + } + + if (n_positive == 0) + { + /* all points are out -- the entire segment is out */ + + *n_c_bezier = 0; + *start_in = FALSE; + *end_in = FALSE; + + return; + } + else if (n_positive == 4) + { + /* all points are in -- the entire segment is in */ + + memcpy (c_bezier[0], bezier, sizeof (GimpCoords[4])); + + *n_c_bezier = 1; + *start_in = TRUE; + *end_in = TRUE; + + return; + } + + /* find the points of intersection of the segment with the 'ax + by + c = 0' + * line + */ + poly[0] = dot[3] - 3.0 * dot[2] + 3.0 * dot[1] - dot[0]; + poly[1] = 3.0 * (dot[2] - 2.0 * dot[1] + dot[0]); + poly[2] = 3.0 * (dot[1] - dot[0]); + poly[3] = dot[0]; + + roots[0] = 0.0; + polynomial_odd_roots (poly, 3, 0.0, 1.0, roots + 1, &n_roots); + roots[++n_roots] = 1.0; + + /* construct the list of segments that are inside the halfplane */ + *n_c_bezier = 0; + *start_in = (polynomial_eval (poly, 3, roots[1] / 2.0) > 0.0); + *end_in = (*start_in + n_roots + 1) % 2; + + for (i = ! *start_in; i < n_roots; i += 2) + { + gdouble t0 = roots[i]; + gdouble t1 = roots[i + 1]; + + gimp_coords_interpolate_bezier_at (bezier, t0, + &c_bezier[*n_c_bezier][0], + &c_bezier[*n_c_bezier][1]); + gimp_coords_interpolate_bezier_at (bezier, t1, + &c_bezier[*n_c_bezier][3], + &c_bezier[*n_c_bezier][2]); + + gimp_coords_mix (1.0, &c_bezier[*n_c_bezier][0], + (t1 - t0) / 3.0, &c_bezier[*n_c_bezier][1], + &c_bezier[*n_c_bezier][1]); + gimp_coords_mix (1.0, &c_bezier[*n_c_bezier][3], + (t0 - t1) / 3.0, &c_bezier[*n_c_bezier][2], + &c_bezier[*n_c_bezier][2]); + + (*n_c_bezier)++; + } +} + +/* transforms the cubic bezier segment, defined by the four control points + * 'bezier', by 'matrix', subdividing it as necessary to avoid diverging too + * much from the real transformed curve. at most 'depth' subdivisions are + * performed. + * + * appends the transformed sequence of bezier segments to 't_beziers'. + * + * 'bezier' shall be fully clipped to the near plane. + */ +static void +transform_bezier_coords (const GimpMatrix3 *matrix, + const GimpCoords bezier[4], + GQueue *t_beziers, + gint depth) +{ + GimpCoords *t_bezier; + gint n; + + /* check if we need to split the segment */ + if (depth > 0) + { + GimpVector2 v[4]; + GimpVector2 c[2]; + GimpVector2 b; + gint i; + + for (i = 0; i < 4; i++) + v[i] = (GimpVector2) { bezier[i].x, bezier[i].y }; + + gimp_vector2_sub (&c[0], &v[1], &v[0]); + gimp_vector2_sub (&c[1], &v[2], &v[3]); + + gimp_vector2_sub (&b, &v[3], &v[0]); + gimp_vector2_mul (&b, 1.0 / gimp_vector2_inner_product (&b, &b)); + + for (i = 0; i < 2; i++) + { + /* split the segment if one of the control points is too far from the + * line connecting the anchors + */ + if (fabs (gimp_vector2_cross_product (&c[i], &b).x) > 0.5) + { + GimpCoords mid_position; + GimpCoords mid_velocity; + GimpCoords sub[4]; + + gimp_coords_interpolate_bezier_at (bezier, 0.5, + &mid_position, &mid_velocity); + + /* first half */ + sub[0] = bezier[0]; + sub[3] = mid_position; + + gimp_coords_mix (0.5, &sub[0], + 0.5, &bezier[1], + &sub[1]); + gimp_coords_mix (1.0, &sub[3], + -1.0 / 6.0, &mid_velocity, + &sub[2]); + + transform_bezier_coords (matrix, sub, t_beziers, depth - 1); + + /* second half */ + sub[0] = mid_position; + sub[3] = bezier[3]; + + gimp_coords_mix (1.0, &sub[0], + +1.0 / 6.0, &mid_velocity, + &sub[1]); + gimp_coords_mix (0.5, &sub[3], + 0.5, &bezier[2], + &sub[2]); + + transform_bezier_coords (matrix, sub, t_beziers, depth - 1); + + return; + } + } + } + + /* transform the segment by transforming each of the individual points. note + * that, for non-affine transforms, this is only an approximation of the real + * transformed curve, but due to subdivision it should be good enough. + */ + t_bezier = g_new (GimpCoords, 4); + + /* note that while the segments themselves are clipped to the near plane, + * their control points may still get transformed behind the camera. we + * therefore clip the control points to the near plane as well, which is not + * too meaningful, but avoids erroneously transforming them behind the + * camera. + */ + gimp_transform_polygon_coords (matrix, bezier, 2, FALSE, + t_bezier, &n); + gimp_transform_polygon_coords (matrix, bezier + 2, 2, FALSE, + t_bezier + 2, &n); + + g_queue_push_tail (t_beziers, t_bezier); +} + +/* transforms the cubic bezier segment, defined by the four control points + * 'bezier', by 'matrix', performing clipping by the near plane and subdividing + * as necessary. + * + * returns the transformed set of bezier-segment sequences in 't_beziers', as + * GQueues of GimpCoords[4] bezier-segments, and the number of sequences in + * 'n_t_beziers'. the minimal possible number of transformed sequences is 0, + * which happens when the entire segment is clipped. the maximal possible + * number of transformed sequences is 2. each sequence has at least one + * segment. + * + * if the first transformed segment is an initial segment of 'bezier', sets + * '*start_in' to TRUE, otherwise to FALSE. if the last transformed segment is + * a final segment of 'bezier', sets '*end_in' to TRUE, otherwise to FALSE. + */ +void +gimp_transform_bezier_coords (const GimpMatrix3 *matrix, + const GimpCoords bezier[4], + GQueue *t_beziers[2], + gint *n_t_beziers, + gboolean *start_in, + gboolean *end_in) +{ + GimpCoords c_bezier[2][4]; + gint i; + + g_return_if_fail (matrix != NULL); + g_return_if_fail (bezier != NULL); + g_return_if_fail (t_beziers != NULL); + g_return_if_fail (n_t_beziers != NULL); + g_return_if_fail (start_in != NULL); + g_return_if_fail (end_in != NULL); + + /* if the matrix is affine, transform the easy way */ + if (gimp_matrix3_is_affine (matrix)) + { + GimpCoords *t_bezier; + + t_beziers[0] = g_queue_new (); + *n_t_beziers = 1; + + t_bezier = g_new (GimpCoords, 1); + g_queue_push_tail (t_beziers[0], t_bezier); + + for (i = 0; i < 4; i++) + { + t_bezier[i] = bezier[i]; + + gimp_matrix3_transform_point (matrix, + bezier[i].x, bezier[i].y, + &t_bezier[i].x, &t_bezier[i].y); + } + + return; + } + + /* clip the segment to the near plane */ + clip_bezier (bezier, + matrix->coeff[2][0], + matrix->coeff[2][1], + matrix->coeff[2][2] - GIMP_TRANSFORM_NEAR_Z, + c_bezier, n_t_beziers, + start_in, end_in); + + /* transform each of the resulting segments */ + for (i = 0; i < *n_t_beziers; i++) + { + t_beziers[i] = g_queue_new (); + + transform_bezier_coords (matrix, c_bezier[i], t_beziers[i], 3); + } +} diff --git a/app/core/gimp-transform-utils.h b/app/core/gimp-transform-utils.h new file mode 100644 index 0000000..c2c1252 --- /dev/null +++ b/app/core/gimp-transform-utils.h @@ -0,0 +1,125 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TRANSFORM_UTILS_H__ +#define __GIMP_TRANSFORM_UTILS_H__ + + +#define GIMP_TRANSFORM_NEAR_Z 0.02 + + +void gimp_transform_get_rotate_center (gint x, + gint y, + gint width, + gint height, + gboolean auto_center, + gdouble *center_x, + gdouble *center_y); +void gimp_transform_get_flip_axis (gint x, + gint y, + gint width, + gint height, + GimpOrientationType flip_type, + gboolean auto_center, + gdouble *axis); + +void gimp_transform_matrix_flip (GimpMatrix3 *matrix, + GimpOrientationType flip_type, + gdouble axis); +void gimp_transform_matrix_flip_free (GimpMatrix3 *matrix, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); +void gimp_transform_matrix_rotate (GimpMatrix3 *matrix, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y); +void gimp_transform_matrix_rotate_rect (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble angle); +void gimp_transform_matrix_rotate_center (GimpMatrix3 *matrix, + gdouble center_x, + gdouble center_y, + gdouble angle); +void gimp_transform_matrix_scale (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble t_x, + gdouble t_y, + gdouble t_width, + gdouble t_height); +void gimp_transform_matrix_shear (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + GimpOrientationType orientation, + gdouble amount); +void gimp_transform_matrix_perspective (GimpMatrix3 *matrix, + gint x, + gint y, + gint width, + gint height, + gdouble t_x1, + gdouble t_y1, + gdouble t_x2, + gdouble t_y2, + gdouble t_x3, + gdouble t_y3, + gdouble t_x4, + gdouble t_y4); +gboolean gimp_transform_matrix_generic (GimpMatrix3 *matrix, + const GimpVector2 input_points[4], + const GimpVector2 output_points[4]); + +gboolean gimp_transform_polygon_is_convex (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble x3, + gdouble y3, + gdouble x4, + gdouble y4); + +void gimp_transform_polygon (const GimpMatrix3 *matrix, + const GimpVector2 *vertices, + gint n_vertices, + gboolean closed, + GimpVector2 *t_vertices, + gint *n_t_vertices); +void gimp_transform_polygon_coords (const GimpMatrix3 *matrix, + const GimpCoords *vertices, + gint n_vertices, + gboolean closed, + GimpCoords *t_vertices, + gint *n_t_vertices); + +void gimp_transform_bezier_coords (const GimpMatrix3 *matrix, + const GimpCoords bezier[4], + GQueue *t_beziers[2], + gint *n_t_beziers, + gboolean *start_in, + gboolean *end_in); + + +#endif /* __GIMP_TRANSFORM_UTILS_H__ */ diff --git a/app/core/gimp-units.c b/app/core/gimp-units.c new file mode 100644 index 0000000..22caca1 --- /dev/null +++ b/app/core/gimp-units.c @@ -0,0 +1,488 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpunit.c + * Copyright (C) 1999-2000 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains functions to load & save the file containing the + * user-defined size units, when the application starts/finished. + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpbase/gimpbase-private.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-units.h" +#include "gimpunit.h" + +#include "config/gimpconfig-file.h" + +#include "gimp-intl.h" + + +/* + * All deserialize functions return G_TOKEN_LEFT_PAREN on success, + * or the GTokenType they would have expected but didn't get. + */ + +static GTokenType gimp_unitrc_unit_info_deserialize (GScanner *scanner, + Gimp *gimp); + + +static Gimp *the_unit_gimp = NULL; + + +static gint +gimp_units_get_number_of_units (void) +{ + return _gimp_unit_get_number_of_units (the_unit_gimp); +} + +static gint +gimp_units_get_number_of_built_in_units (void) +{ + return GIMP_UNIT_END; +} + +static GimpUnit +gimp_units_unit_new (gchar *identifier, + gdouble factor, + gint digits, + gchar *symbol, + gchar *abbreviation, + gchar *singular, + gchar *plural) +{ + return _gimp_unit_new (the_unit_gimp, + identifier, + factor, + digits, + symbol, + abbreviation, + singular, + plural); +} + +static gboolean +gimp_units_unit_get_deletion_flag (GimpUnit unit) +{ + return _gimp_unit_get_deletion_flag (the_unit_gimp, unit); +} + +static void +gimp_units_unit_set_deletion_flag (GimpUnit unit, + gboolean deletion_flag) +{ + _gimp_unit_set_deletion_flag (the_unit_gimp, unit, deletion_flag); +} + +static gdouble +gimp_units_unit_get_factor (GimpUnit unit) +{ + return _gimp_unit_get_factor (the_unit_gimp, unit); +} + +static gint +gimp_units_unit_get_digits (GimpUnit unit) +{ + return _gimp_unit_get_digits (the_unit_gimp, unit); +} + +static const gchar * +gimp_units_unit_get_identifier (GimpUnit unit) +{ + return _gimp_unit_get_identifier (the_unit_gimp, unit); +} + +static const gchar * +gimp_units_unit_get_symbol (GimpUnit unit) +{ + return _gimp_unit_get_symbol (the_unit_gimp, unit); +} + +static const gchar * +gimp_units_unit_get_abbreviation (GimpUnit unit) +{ + return _gimp_unit_get_abbreviation (the_unit_gimp, unit); +} + +static const gchar * +gimp_units_unit_get_singular (GimpUnit unit) +{ + return _gimp_unit_get_singular (the_unit_gimp, unit); +} + +static const gchar * +gimp_units_unit_get_plural (GimpUnit unit) +{ + return _gimp_unit_get_plural (the_unit_gimp, unit); +} + +void +gimp_units_init (Gimp *gimp) +{ + GimpUnitVtable vtable; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (the_unit_gimp == NULL); + + the_unit_gimp = gimp; + + vtable.unit_get_number_of_units = gimp_units_get_number_of_units; + vtable.unit_get_number_of_built_in_units = gimp_units_get_number_of_built_in_units; + vtable.unit_new = gimp_units_unit_new; + vtable.unit_get_deletion_flag = gimp_units_unit_get_deletion_flag; + vtable.unit_set_deletion_flag = gimp_units_unit_set_deletion_flag; + vtable.unit_get_factor = gimp_units_unit_get_factor; + vtable.unit_get_digits = gimp_units_unit_get_digits; + vtable.unit_get_identifier = gimp_units_unit_get_identifier; + vtable.unit_get_symbol = gimp_units_unit_get_symbol; + vtable.unit_get_abbreviation = gimp_units_unit_get_abbreviation; + vtable.unit_get_singular = gimp_units_unit_get_singular; + vtable.unit_get_plural = gimp_units_unit_get_plural; + + gimp_base_init (&vtable); + + gimp->user_units = NULL; + gimp->n_user_units = 0; +} + +void +gimp_units_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_user_units_free (gimp); +} + + +/* unitrc functions **********/ + +enum +{ + UNIT_INFO = 1, + UNIT_FACTOR, + UNIT_DIGITS, + UNIT_SYMBOL, + UNIT_ABBREV, + UNIT_SINGULAR, + UNIT_PLURAL +}; + +void +gimp_unitrc_load (Gimp *gimp) +{ + GFile *file; + GScanner *scanner; + GTokenType token; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = gimp_directory_file ("unitrc", NULL); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + scanner = gimp_scanner_new_gfile (file, &error); + + if (! scanner && error->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) + { + g_clear_error (&error); + g_object_unref (file); + + file = gimp_sysconf_directory_file ("unitrc", NULL); + + scanner = gimp_scanner_new_gfile (file, NULL); + } + + if (! scanner) + { + g_clear_error (&error); + g_object_unref (file); + return; + } + + g_scanner_scope_add_symbol (scanner, 0, + "unit-info", GINT_TO_POINTER (UNIT_INFO)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "factor", GINT_TO_POINTER (UNIT_FACTOR)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "digits", GINT_TO_POINTER (UNIT_DIGITS)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "symbol", GINT_TO_POINTER (UNIT_SYMBOL)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "abbreviation", GINT_TO_POINTER (UNIT_ABBREV)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "singular", GINT_TO_POINTER (UNIT_SINGULAR)); + g_scanner_scope_add_symbol (scanner, UNIT_INFO, + "plural", GINT_TO_POINTER (UNIT_PLURAL)); + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + if (scanner->value.v_symbol == GINT_TO_POINTER (UNIT_INFO)) + { + g_scanner_set_scope (scanner, UNIT_INFO); + token = gimp_unitrc_unit_info_deserialize (scanner, gimp); + + if (token == G_TOKEN_RIGHT_PAREN) + g_scanner_set_scope (scanner, 0); + } + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + if (token != G_TOKEN_LEFT_PAREN) + { + g_scanner_get_next_token (scanner); + g_scanner_unexp_token (scanner, token, NULL, NULL, NULL, + _("fatal parse error"), TRUE); + + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + + gimp_config_file_backup_on_error (file, "unitrc", NULL); + } + + gimp_scanner_destroy (scanner); + g_object_unref (file); +} + +void +gimp_unitrc_save (Gimp *gimp) +{ + GimpConfigWriter *writer; + GFile *file; + gint i; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = gimp_directory_file ("unitrc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + writer = + gimp_config_writer_new_gfile (file, + TRUE, + "GIMP units\n\n" + "This file contains the user unit database. " + "You can edit this list with the unit " + "editor. You are not supposed to edit it " + "manually, but of course you can do.\n" + "This file will be entirely rewritten each " + "time you exit.", + NULL); + + g_object_unref (file); + + if (!writer) + return; + + /* save user defined units */ + for (i = _gimp_unit_get_number_of_built_in_units (gimp); + i < _gimp_unit_get_number_of_units (gimp); + i++) + { + if (_gimp_unit_get_deletion_flag (gimp, i) == FALSE) + { + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + + gimp_config_writer_open (writer, "unit-info"); + gimp_config_writer_string (writer, + _gimp_unit_get_identifier (gimp, i)); + + gimp_config_writer_open (writer, "factor"); + gimp_config_writer_print (writer, + g_ascii_dtostr (buf, sizeof (buf), + _gimp_unit_get_factor (gimp, i)), + -1); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "digits"); + gimp_config_writer_printf (writer, + "%d", _gimp_unit_get_digits (gimp, i)); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "symbol"); + gimp_config_writer_string (writer, + _gimp_unit_get_symbol (gimp, i)); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "abbreviation"); + gimp_config_writer_string (writer, + _gimp_unit_get_abbreviation (gimp, i)); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "singular"); + gimp_config_writer_string (writer, + _gimp_unit_get_singular (gimp, i)); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "plural"); + gimp_config_writer_string (writer, + _gimp_unit_get_plural (gimp, i)); + gimp_config_writer_close (writer); + + gimp_config_writer_close (writer); + } + } + + if (! gimp_config_writer_finish (writer, "end of units", &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } +} + + +/* private functions */ + +static GTokenType +gimp_unitrc_unit_info_deserialize (GScanner *scanner, + Gimp *gimp) +{ + gchar *identifier = NULL; + gdouble factor = 1.0; + gint digits = 2.0; + gchar *symbol = NULL; + gchar *abbreviation = NULL; + gchar *singular = NULL; + gchar *plural = NULL; + GTokenType token; + + if (! gimp_scanner_parse_string (scanner, &identifier)) + return G_TOKEN_STRING; + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + switch (GPOINTER_TO_INT (scanner->value.v_symbol)) + { + case UNIT_FACTOR: + token = G_TOKEN_FLOAT; + if (! gimp_scanner_parse_float (scanner, &factor)) + goto cleanup; + break; + + case UNIT_DIGITS: + token = G_TOKEN_INT; + if (! gimp_scanner_parse_int (scanner, &digits)) + goto cleanup; + break; + + case UNIT_SYMBOL: + token = G_TOKEN_STRING; + if (! gimp_scanner_parse_string (scanner, &symbol)) + goto cleanup; + break; + + case UNIT_ABBREV: + token = G_TOKEN_STRING; + if (! gimp_scanner_parse_string (scanner, &abbreviation)) + goto cleanup; + break; + + case UNIT_SINGULAR: + token = G_TOKEN_STRING; + if (! gimp_scanner_parse_string (scanner, &singular)) + goto cleanup; + break; + + case UNIT_PLURAL: + token = G_TOKEN_STRING; + if (! gimp_scanner_parse_string (scanner, &plural)) + goto cleanup; + break; + + default: + break; + } + token = G_TOKEN_RIGHT_PAREN; + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: + break; + } + } + + if (token == G_TOKEN_LEFT_PAREN) + { + token = G_TOKEN_RIGHT_PAREN; + + if (g_scanner_peek_next_token (scanner) == token) + { + GimpUnit unit = _gimp_unit_new (gimp, + identifier, factor, digits, + symbol, abbreviation, + singular, plural); + + /* make the unit definition persistent */ + _gimp_unit_set_deletion_flag (gimp, unit, FALSE); + } + } + + cleanup: + + g_free (identifier); + g_free (symbol); + g_free (abbreviation); + g_free (singular); + g_free (plural); + + return token; +} diff --git a/app/core/gimp-units.h b/app/core/gimp-units.h new file mode 100644 index 0000000..64f819a --- /dev/null +++ b/app/core/gimp-units.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 . + */ + +#ifndef __GIMP_UNITS_H__ +#define __GIMP_UNITS_H__ + + +void gimp_units_init (Gimp *gimp); +void gimp_units_exit (Gimp *gimp); + +void gimp_unitrc_load (Gimp *gimp); +void gimp_unitrc_save (Gimp *gimp); + + +#endif /* __GIMP_UNITS_H__ */ diff --git a/app/core/gimp-user-install.c b/app/core/gimp-user-install.c new file mode 100644 index 0000000..2b1c910 --- /dev/null +++ b/app/core/gimp-user-install.c @@ -0,0 +1,977 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-user-install.c + * Copyright (C) 2000-2008 Michael Natterer and 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 . + */ + +/* This file contains functions to help migrate the settings from a + * previous GIMP version to be used with the current (newer) version. + */ + +#include "config.h" + +#include +#include +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef PLATFORM_OSX +#include +#endif + +#include +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "config/gimpconfig-file.h" +#include "config/gimprc.h" + +#include "gimp-templates.h" +#include "gimp-tags.h" +#include "gimp-user-install.h" + +#include "gimp-intl.h" + + +struct _GimpUserInstall +{ + GObject *gimp; + + gboolean verbose; + + gchar *old_dir; + gint old_major; + gint old_minor; + + const gchar *migrate; + + GimpUserInstallLogFunc log; + gpointer log_data; +}; + +typedef enum +{ + USER_INSTALL_MKDIR, /* Create the directory */ + USER_INSTALL_COPY /* Copy from sysconf directory */ +} GimpUserInstallAction; + +static const struct +{ + const gchar *name; + GimpUserInstallAction action; +} +gimp_user_install_items[] = +{ + { "menurc", USER_INSTALL_COPY }, + { "brushes", USER_INSTALL_MKDIR }, + { "dynamics", USER_INSTALL_MKDIR }, + { "fonts", USER_INSTALL_MKDIR }, + { "gradients", USER_INSTALL_MKDIR }, + { "palettes", USER_INSTALL_MKDIR }, + { "patterns", USER_INSTALL_MKDIR }, + { "tool-presets", USER_INSTALL_MKDIR }, + { "plug-ins", USER_INSTALL_MKDIR }, + { "modules", USER_INSTALL_MKDIR }, + { "interpreters", USER_INSTALL_MKDIR }, + { "environ", USER_INSTALL_MKDIR }, + { "scripts", USER_INSTALL_MKDIR }, + { "templates", USER_INSTALL_MKDIR }, + { "themes", USER_INSTALL_MKDIR }, + { "icons", USER_INSTALL_MKDIR }, + { "tmp", USER_INSTALL_MKDIR }, + { "curves", USER_INSTALL_MKDIR }, + { "levels", USER_INSTALL_MKDIR }, + { "filters", USER_INSTALL_MKDIR }, + { "fractalexplorer", USER_INSTALL_MKDIR }, + { "gfig", USER_INSTALL_MKDIR }, + { "gflare", USER_INSTALL_MKDIR }, + { "gimpressionist", USER_INSTALL_MKDIR } +}; + + +static gboolean user_install_detect_old (GimpUserInstall *install, + const gchar *gimp_dir); +static gchar * user_install_old_style_gimpdir (void); + +static void user_install_log (GimpUserInstall *install, + const gchar *format, + ...) G_GNUC_PRINTF (2, 3); +static void user_install_log_newline (GimpUserInstall *install); +static void user_install_log_error (GimpUserInstall *install, + GError **error); + +static gboolean user_install_mkdir (GimpUserInstall *install, + const gchar *dirname); +static gboolean user_install_mkdir_with_parents (GimpUserInstall *install, + const gchar *dirname); +static gboolean user_install_file_copy (GimpUserInstall *install, + const gchar *source, + const gchar *dest, + const gchar *old_options_regexp, + GRegexEvalCallback update_callback); +static gboolean user_install_dir_copy (GimpUserInstall *install, + gint level, + const gchar *source, + const gchar *base, + const gchar *update_pattern, + GRegexEvalCallback update_callback); + +static gboolean user_install_create_files (GimpUserInstall *install); +static gboolean user_install_migrate_files (GimpUserInstall *install); + + +/* public functions */ + +GimpUserInstall * +gimp_user_install_new (GObject *gimp, + gboolean verbose) +{ + GimpUserInstall *install = g_slice_new0 (GimpUserInstall); + + install->gimp = gimp; + install->verbose = verbose; + + user_install_detect_old (install, gimp_directory ()); + +#ifdef PLATFORM_OSX + /* The config path on OSX has for a very short time frame (2.8.2 only) + been "~/Library/GIMP". It changed to "~/Library/Application Support" + in 2.8.4 and was in the home folder (as was other UNIX) before. */ + + if (! install->old_dir) + { + gchar *dir; + NSAutoreleasePool *pool; + NSArray *path; + NSString *library_dir; + + pool = [[NSAutoreleasePool alloc] init]; + + path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, + NSUserDomainMask, YES); + library_dir = [path objectAtIndex:0]; + + dir = g_build_filename ([library_dir UTF8String], + GIMPDIR, GIMP_USER_VERSION, NULL); + + [pool drain]; + + user_install_detect_old (install, dir); + g_free (dir); + } + +#endif + + if (! install->old_dir) + { + /* if the default XDG-style config directory was not found, try + * the "old-style" path in the home folder. + */ + gchar *dir = user_install_old_style_gimpdir (); + user_install_detect_old (install, dir); + g_free (dir); + } + + return install; +} + +gboolean +gimp_user_install_run (GimpUserInstall *install) +{ + gchar *dirname; + + g_return_val_if_fail (install != NULL, FALSE); + + dirname = g_filename_display_name (gimp_directory ()); + + if (install->migrate) + user_install_log (install, + _("It seems you have used GIMP %s before. " + "GIMP will now migrate your user settings to '%s'."), + install->migrate, dirname); + else + user_install_log (install, + _("It appears that you are using GIMP for the " + "first time. GIMP will now create a folder " + "named '%s' and copy some files to it."), + dirname); + + g_free (dirname); + + user_install_log_newline (install); + + if (! user_install_mkdir_with_parents (install, gimp_directory ())) + return FALSE; + + if (install->migrate) + if (! user_install_migrate_files (install)) + return FALSE; + + return user_install_create_files (install); +} + +void +gimp_user_install_free (GimpUserInstall *install) +{ + g_return_if_fail (install != NULL); + + g_free (install->old_dir); + + g_slice_free (GimpUserInstall, install); +} + +void +gimp_user_install_set_log_handler (GimpUserInstall *install, + GimpUserInstallLogFunc log, + gpointer user_data) +{ + g_return_if_fail (install != NULL); + + install->log = log; + install->log_data = user_data; +} + + +/* Local functions */ + +static gboolean +user_install_detect_old (GimpUserInstall *install, + const gchar *gimp_dir) +{ + gchar *dir = g_strdup (gimp_dir); + gchar *version; + gboolean migrate = FALSE; + + version = strstr (dir, GIMP_APP_VERSION); + + if (version) + { + gint i; + + for (i = (GIMP_MINOR_VERSION & ~1); i >= 0; i -= 2) + { + /* we assume that GIMP_APP_VERSION is in the form '2.x' */ + g_snprintf (version + 2, 2, "%d", i); + + migrate = g_file_test (dir, G_FILE_TEST_IS_DIR); + + if (migrate) + { +#ifdef GIMP_UNSTABLE + g_printerr ("gimp-user-install: migrating from %s\n", dir); +#endif + install->old_major = 2; + install->old_minor = i; + + break; + } + } + } + + if (migrate) + { + install->old_dir = dir; + install->migrate = (const gchar *) version; + } + else + { + g_free (dir); + } + + return migrate; +} + +static gchar * +user_install_old_style_gimpdir (void) +{ + const gchar *home_dir = g_get_home_dir (); + gchar *gimp_dir = NULL; + + if (home_dir) + { + gimp_dir = g_build_filename (home_dir, ".gimp-" GIMP_APP_VERSION, NULL); + } + else + { + gchar *user_name = g_strdup (g_get_user_name ()); + gchar *subdir_name; + +#ifdef G_OS_WIN32 + gchar *p = user_name; + + while (*p) + { + /* Replace funny characters in the user name with an + * underscore. The code below also replaces some + * characters that in fact are legal in file names, but + * who cares, as long as the definitely illegal ones are + * caught. + */ + if (!g_ascii_isalnum (*p) && !strchr ("-.,@=", *p)) + *p = '_'; + p++; + } +#endif + +#ifndef G_OS_WIN32 + g_message ("warning: no home directory."); +#endif + subdir_name = g_strconcat (".gimp-" GIMP_APP_VERSION ".", user_name, NULL); + gimp_dir = g_build_filename (gimp_data_directory (), + subdir_name, + NULL); + g_free (user_name); + g_free (subdir_name); + } + + return gimp_dir; +} + +static void +user_install_log (GimpUserInstall *install, + const gchar *format, + ...) +{ + va_list args; + + va_start (args, format); + + if (format) + { + gchar *message = g_strdup_vprintf (format, args); + + if (install->verbose) + g_print ("%s\n", message); + + if (install->log) + install->log (message, FALSE, install->log_data); + + g_free (message); + } + + va_end (args); +} + +static void +user_install_log_newline (GimpUserInstall *install) +{ + if (install->verbose) + g_print ("\n"); + + if (install->log) + install->log (NULL, FALSE, install->log_data); +} + +static void +user_install_log_error (GimpUserInstall *install, + GError **error) +{ + if (error && *error) + { + const gchar *message = ((*error)->message ? + (*error)->message : "(unknown error)"); + + if (install->log) + install->log (message, TRUE, install->log_data); + else + g_print ("error: %s\n", message); + + g_clear_error (error); + } +} + +static gboolean +user_install_file_copy (GimpUserInstall *install, + const gchar *source, + const gchar *dest, + const gchar *old_options_regexp, + GRegexEvalCallback update_callback) +{ + GError *error = NULL; + gboolean success; + + user_install_log (install, _("Copying file '%s' from '%s'..."), + gimp_filename_to_utf8 (dest), + gimp_filename_to_utf8 (source)); + + success = gimp_config_file_copy (source, dest, old_options_regexp, update_callback, &error); + + user_install_log_error (install, &error); + + return success; +} + +static gboolean +user_install_mkdir (GimpUserInstall *install, + const gchar *dirname) +{ + user_install_log (install, _("Creating folder '%s'..."), + gimp_filename_to_utf8 (dirname)); + + if (g_mkdir (dirname, + S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH) == -1) + { + GError *error = NULL; + + g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Cannot create folder '%s': %s"), + gimp_filename_to_utf8 (dirname), g_strerror (errno)); + + user_install_log_error (install, &error); + + return FALSE; + } + + return TRUE; +} + +static gboolean +user_install_mkdir_with_parents (GimpUserInstall *install, + const gchar *dirname) +{ + user_install_log (install, _("Creating folder '%s'..."), + gimp_filename_to_utf8 (dirname)); + + if (g_mkdir_with_parents (dirname, + S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH) == -1) + { + GError *error = NULL; + + g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Cannot create folder '%s': %s"), + gimp_filename_to_utf8 (dirname), g_strerror (errno)); + + user_install_log_error (install, &error); + + return FALSE; + } + + return TRUE; +} + +/* The regexp pattern of all options changed from menurc of GIMP 2.8. + * Add any pattern that we want to recognize for replacement in the menurc of + * the next release + */ +#define MENURC_OVER20_UPDATE_PATTERN \ + "\"/buffers/buffers-paste-as-new\"" "|" \ + "\"/edit/edit-paste-as-new\"" "|" \ + "\"/file/file-export\"" "|" \ + "\"/file/file-export-to\"" "|" \ + "\"/layers/layers-text-tool\"" "|" \ + "\"/plug-in/plug-in-gauss\"" "|" \ + "\"/tools/tools-value-[1-4]-.*\"" "|" \ + "\"/vectors/vectors-path-tool\"" "|" \ + "\"/tools/tools-blend\"" + +/** + * callback to use for updating a menurc from GIMP over 2.0. + * data is unused (always NULL). + * The updated value will be matched line by line. + */ +static gboolean +user_update_menurc_over20 (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + gchar *match = g_match_info_fetch (matched_value, 0); + + /* "*-paste-as-new" renamed to "*-paste-as-new-image" + */ + if (g_strcmp0 (match, "\"/buffers/buffers-paste-as-new\"") == 0) + { + g_string_append (new_value, "\"/buffers/buffers-paste-as-new-image\""); + } + else if (g_strcmp0 (match, "\"/edit/edit-paste-as-new\"") == 0) + { + g_string_append (new_value, "\"/edit/edit-paste-as-new-image\""); + } + /* file-export-* changes to follow file-save-* patterns. Actions + * available since GIMP 2.8, changed for 2.10 in commit 4b14ed2. + */ + else if (g_strcmp0 (match, "\"/file/file-export\"") == 0) + { + g_string_append (new_value, "\"/file/file-export-as\""); + } + else if (g_strcmp0 (match, "\"/file/file-export-to\"") == 0) + { + g_string_append (new_value, "\"/file/file-export\""); + } + else if (g_strcmp0 (match, "\"/layers/layers-text-tool\"") == 0) + { + g_string_append (new_value, "\"/layers/layers-edit\""); + } + /* plug-in-gauss doesn't exist anymore since commit ff59aebbe88. + * The expected replacement would be filters-gaussian-blur which is + * gegl:gaussian-blur operation. See also bug 775931. + */ + else if (g_strcmp0 (match, "\"/plug-in/plug-in-gauss\"") == 0) + { + g_string_append (new_value, "\"/filters/filters-gaussian-blur\""); + } + /* Tools settings renamed more user-friendly. Actions available + * since GIMP 2.4, changed for 2.10 in commit 0bdb747. + */ + else if (g_str_has_prefix (match, "\"/tools/tools-value-1-")) + { + g_string_append (new_value, "\"/tools/tools-opacity-"); + g_string_append (new_value, match + 31); + } + else if (g_str_has_prefix (match, "\"/tools/tools-value-2-")) + { + g_string_append (new_value, "\"/tools/tools-size-"); + g_string_append (new_value, match + 31); + } + else if (g_str_has_prefix (match, "\"/tools/tools-value-3-")) + { + g_string_append (new_value, "\"/tools/tools-aspect-"); + g_string_append (new_value, match + 31); + } + else if (g_str_has_prefix (match, "\"/tools/tools-value-4-")) + { + g_string_append (new_value, "\"/tools/tools-angle-"); + g_string_append (new_value, match + 31); + } + else if (g_strcmp0 (match, "\"/vectors/vectors-path-tool\"") == 0) + { + g_string_append (new_value, "\"/vectors/vectors-edit\""); + } + else if (g_strcmp0 (match, "\"/tools/tools-blend\"") == 0) + { + g_string_append (new_value, "\"/tools/tools-gradient\""); + } + /* Should not happen. Just in case we match something unexpected by + * mistake. + */ + else + { + g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match); + g_string_append (new_value, match); + } + + g_free (match); + return FALSE; +} + +#define CONTROLLERRC_UPDATE_PATTERN \ + "\\(map \"(scroll|cursor)-[^\"]*\\bcontrol\\b[^\"]*\"" + +static gboolean +user_update_controllerrc (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + gchar *original; + gchar *replacement; + GRegex *regexp = NULL; + + /* No need of a complicated pattern here. + * CONTROLLERRC_UPDATE_PATTERN took care of it first. + */ + regexp = g_regex_new ("\\bcontrol\\b", 0, 0, NULL); + original = g_match_info_fetch (matched_value, 0); + + replacement = g_regex_replace (regexp, original, -1, 0, + "primary", 0, NULL); + g_string_append (new_value, replacement); + + g_free (original); + g_free (replacement); + g_regex_unref (regexp); + + return FALSE; +} + +#define GIMPRC_UPDATE_PATTERN \ + "\\(theme [^)]*\\)" "|" \ + "\\(.*-path [^)]*\\)" + +static gboolean +user_update_gimprc (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + /* Do not migrate paths and themes from GIMP < 2.10. */ + return FALSE; +} + +#define GIMPRESSIONIST_UPDATE_PATTERN \ + "selectedbrush=Brushes/paintbrush.pgm" + +static gboolean +user_update_gimpressionist (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + gchar *match = g_match_info_fetch (matched_value, 0); + + /* See bug 791934: both brushes are identical. */ + if (g_strcmp0 (match, "selectedbrush=Brushes/paintbrush.pgm") == 0) + { + g_string_append (new_value, "selectedbrush=Brushes/paintbrush01.pgm"); + } + else + { + g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match); + g_string_append (new_value, match); + } + + g_free (match); + return FALSE; +} + +#define TOOL_PRESETS_UPDATE_PATTERN \ + "GimpImageMapOptions" "|" \ + "GimpBlendOptions" "|" \ + "gimp-blend-tool" "|" \ + "gimp-tool-blend" + +static gboolean +user_update_tool_presets (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + gchar *match = g_match_info_fetch (matched_value, 0); + + if (g_strcmp0 (match, "GimpImageMapOptions") == 0) + { + g_string_append (new_value, "GimpFilterOptions"); + } + else if (g_strcmp0 (match, "GimpBlendOptions") == 0) + { + g_string_append (new_value, "GimpGradientOptions"); + } + else if (g_strcmp0 (match, "gimp-blend-tool") == 0) + { + g_string_append (new_value, "gimp-gradient-tool"); + } + else if (g_strcmp0 (match, "gimp-tool-blend") == 0) + { + g_string_append (new_value, "gimp-tool-gradient"); + } + else + { + g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match); + g_string_append (new_value, match); + } + + g_free (match); + return FALSE; +} + +/* Actually not only for contextrc, but all other files where + * gimp-blend-tool may appear. Apparently that is also "devicerc", as + * well as "toolrc" (but this one is skipped anyway). + */ +#define CONTEXTRC_UPDATE_PATTERN "gimp-blend-tool" + +static gboolean +user_update_contextrc_over20 (const GMatchInfo *matched_value, + GString *new_value, + gpointer data) +{ + gchar *match = g_match_info_fetch (matched_value, 0); + + if (g_strcmp0 (match, "gimp-blend-tool") == 0) + { + g_string_append (new_value, "gimp-gradient-tool"); + } + else + { + g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match); + g_string_append (new_value, match); + } + + g_free (match); + return FALSE; +} + +static gboolean +user_install_dir_copy (GimpUserInstall *install, + gint level, + const gchar *source, + const gchar *base, + const gchar *update_pattern, + GRegexEvalCallback update_callback) +{ + GDir *source_dir = NULL; + GDir *dest_dir = NULL; + gchar dest[1024]; + const gchar *basename; + gchar *dirname = NULL; + gchar *name; + GError *error = NULL; + gboolean success = FALSE; + + if (level >= 5) + { + /* Config migration is recursive, but we can't go on forever, + * since we may fall into recursive symlinks in particular (which + * is a security risk to fill a disk, and would also block GIMP + * forever at migration stage). + * Let's just break the recursivity at 5 levels, which is just an + * arbitrary value (but I don't think there should be any data + * deeper than this). + */ + goto error; + } + + name = g_path_get_basename (source); + dirname = g_build_filename (base, name, NULL); + g_free (name); + + success = user_install_mkdir (install, dirname); + if (! success) + goto error; + + success = (dest_dir = g_dir_open (dirname, 0, &error)) != NULL; + if (! success) + goto error; + + success = (source_dir = g_dir_open (source, 0, &error)) != NULL; + if (! success) + goto error; + + while ((basename = g_dir_read_name (source_dir)) != NULL) + { + name = g_build_filename (source, basename, NULL); + + if (g_file_test (name, G_FILE_TEST_IS_REGULAR)) + { + g_snprintf (dest, sizeof (dest), "%s%c%s", + dirname, G_DIR_SEPARATOR, basename); + + success = user_install_file_copy (install, name, dest, + update_pattern, + update_callback); + if (! success) + { + g_free (name); + goto error; + } + } + else + { + user_install_dir_copy (install, level + 1, name, dirname, + update_pattern, update_callback); + } + + g_free (name); + } + + error: + user_install_log_error (install, &error); + + if (source_dir) + g_dir_close (source_dir); + + if (dest_dir) + g_dir_close (dest_dir); + + if (dirname) + g_free (dirname); + + return success; +} + +static gboolean +user_install_create_files (GimpUserInstall *install) +{ + gchar dest[1024]; + gchar source[1024]; + gint i; + + for (i = 0; i < G_N_ELEMENTS (gimp_user_install_items); i++) + { + g_snprintf (dest, sizeof (dest), "%s%c%s", + gimp_directory (), + G_DIR_SEPARATOR, + gimp_user_install_items[i].name); + + if (g_file_test (dest, G_FILE_TEST_EXISTS)) + continue; + + switch (gimp_user_install_items[i].action) + { + case USER_INSTALL_MKDIR: + if (! user_install_mkdir (install, dest)) + return FALSE; + break; + + case USER_INSTALL_COPY: + g_snprintf (source, sizeof (source), "%s%c%s", + gimp_sysconf_directory (), G_DIR_SEPARATOR, + gimp_user_install_items[i].name); + + if (! user_install_file_copy (install, source, dest, NULL, NULL)) + return FALSE; + break; + } + } + + g_snprintf (dest, sizeof (dest), "%s%c%s", + gimp_directory (), G_DIR_SEPARATOR, "tags.xml"); + + if (! g_file_test (dest, G_FILE_TEST_IS_REGULAR)) + { + /* if there was no tags.xml, install it with default tag set. + */ + if (! gimp_tags_user_install ()) + { + return FALSE; + } + } + + return TRUE; +} + +static gboolean +user_install_migrate_files (GimpUserInstall *install) +{ + GDir *dir; + const gchar *basename; + gchar dest[1024]; + GimpRc *gimprc; + GError *error = NULL; + + dir = g_dir_open (install->old_dir, 0, &error); + + if (! dir) + { + user_install_log_error (install, &error); + return FALSE; + } + + while ((basename = g_dir_read_name (dir)) != NULL) + { + gchar *source = g_build_filename (install->old_dir, basename, NULL); + + if (g_file_test (source, G_FILE_TEST_IS_REGULAR)) + { + const gchar *update_pattern = NULL; + GRegexEvalCallback update_callback = NULL; + + /* skip these files for all old versions */ + if (strcmp (basename, "documents") == 0 || + g_str_has_prefix (basename, "gimpswap.") || + strcmp (basename, "pluginrc") == 0 || + strcmp (basename, "themerc") == 0 || + strcmp (basename, "toolrc") == 0) + { + goto next_file; + } + else if (strcmp (basename, "menurc") == 0) + { + switch (install->old_minor) + { + case 0: + /* skip menurc for gimp 2.0 as the format has changed */ + goto next_file; + break; + default: + update_pattern = MENURC_OVER20_UPDATE_PATTERN; + update_callback = user_update_menurc_over20; + break; + } + } + else if (strcmp (basename, "controllerrc") == 0) + { + update_pattern = CONTROLLERRC_UPDATE_PATTERN; + update_callback = user_update_controllerrc; + } + else if (strcmp (basename, "gimprc") == 0) + { + update_pattern = GIMPRC_UPDATE_PATTERN; + update_callback = user_update_gimprc; + } + else if (strcmp (basename, "contextrc") == 0 || + strcmp (basename, "devicerc") == 0) + { + update_pattern = CONTEXTRC_UPDATE_PATTERN; + update_callback = user_update_contextrc_over20; + } + + g_snprintf (dest, sizeof (dest), "%s%c%s", + gimp_directory (), G_DIR_SEPARATOR, basename); + + user_install_file_copy (install, source, dest, + update_pattern, update_callback); + } + else if (g_file_test (source, G_FILE_TEST_IS_DIR)) + { + const gchar *update_pattern = NULL; + GRegexEvalCallback update_callback = NULL; + + /* skip these directories for all old versions */ + if (strcmp (basename, "tmp") == 0 || + strcmp (basename, "tool-options") == 0 || + strcmp (basename, "themes") == 0) + { + goto next_file; + } + + if (strcmp (basename, "gimpressionist") == 0) + { + update_pattern = GIMPRESSIONIST_UPDATE_PATTERN; + update_callback = user_update_gimpressionist; + } + else if (strcmp (basename, "tool-presets") == 0) + { + update_pattern = TOOL_PRESETS_UPDATE_PATTERN; + update_callback = user_update_tool_presets; + } + user_install_dir_copy (install, 0, source, gimp_directory (), + update_pattern, update_callback); + } + + next_file: + g_free (source); + } + + /* create the tmp directory that was explicitly not copied */ + + g_snprintf (dest, sizeof (dest), "%s%c%s", + gimp_directory (), G_DIR_SEPARATOR, "tmp"); + + user_install_mkdir (install, dest); + g_dir_close (dir); + + gimp_templates_migrate (install->old_dir); + + gimprc = gimp_rc_new (install->gimp, NULL, NULL, FALSE); + gimp_rc_migrate (gimprc); + gimp_rc_save (gimprc); + g_object_unref (gimprc); + + return TRUE; +} diff --git a/app/core/gimp-user-install.h b/app/core/gimp-user-install.h new file mode 100644 index 0000000..1bac686 --- /dev/null +++ b/app/core/gimp-user-install.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 . + */ + +#ifndef __GIMP_USER_INSTALL_H__ +#define __GIMP_USER_INSTALL_H__ + + +typedef struct _GimpUserInstall GimpUserInstall; + +typedef void (* GimpUserInstallLogFunc) (const gchar *message, + gboolean error, + gpointer user_data); + + +GimpUserInstall * gimp_user_install_new (GObject *gimp, + gboolean verbose); +gboolean gimp_user_install_run (GimpUserInstall *install); +void gimp_user_install_free (GimpUserInstall *install); + +void gimp_user_install_set_log_handler (GimpUserInstall *install, + GimpUserInstallLogFunc log, + gpointer user_data); + + +#endif /* __USER_INSTALL_H__ */ diff --git a/app/core/gimp-utils.c b/app/core/gimp-utils.c new file mode 100644 index 0000000..d3df098 --- /dev/null +++ b/app/core/gimp-utils.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 . + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#define _WIN32_WINNT 0x0500 +#include +#include +#endif + +#if defined(G_OS_UNIX) && defined(HAVE_EXECINFO_H) +/* For get_backtrace() */ +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpasync.h" +#include "gimpcontext.h" +#include "gimperror.h" + +#include "gimp-intl.h" + + +#define MAX_FUNC 100 + + +gint +gimp_get_pid (void) +{ + return (gint) getpid (); +} + +guint64 +gimp_get_physical_memory_size (void) +{ +#ifdef G_OS_UNIX +#if defined(HAVE_UNISTD_H) && defined(_SC_PHYS_PAGES) && defined (_SC_PAGE_SIZE) + return (guint64) sysconf (_SC_PHYS_PAGES) * sysconf (_SC_PAGE_SIZE); +#endif +#endif + +#ifdef G_OS_WIN32 +# if defined(_MSC_VER) && (_MSC_VER <= 1200) + MEMORYSTATUS memory_status; + memory_status.dwLength = sizeof (memory_status); + + GlobalMemoryStatus (&memory_status); + return memory_status.dwTotalPhys; +# else + /* requires w2k and newer SDK than provided with msvc6 */ + MEMORYSTATUSEX memory_status; + + memory_status.dwLength = sizeof (memory_status); + + if (GlobalMemoryStatusEx (&memory_status)) + return memory_status.ullTotalPhys; +# endif +#endif + + return 0; +} + +/* + * basically copied from gtk_get_default_language() + */ +gchar * +gimp_get_default_language (const gchar *category) +{ + gchar *lang; + gchar *p; + gint cat = LC_CTYPE; + + if (! category) + category = "LC_CTYPE"; + +#ifdef G_OS_WIN32 + + p = getenv ("LC_ALL"); + if (p != NULL) + lang = g_strdup (p); + else + { + p = getenv ("LANG"); + if (p != NULL) + lang = g_strdup (p); + else + { + p = getenv (category); + if (p != NULL) + lang = g_strdup (p); + else + lang = g_win32_getlocale (); + } + } + +#else + + if (strcmp (category, "LC_CTYPE") == 0) + cat = LC_CTYPE; + else if (strcmp (category, "LC_MESSAGES") == 0) + cat = LC_MESSAGES; + else + g_warning ("unsupported category used with gimp_get_default_language()"); + + lang = g_strdup (setlocale (cat, NULL)); + +#endif + + p = strchr (lang, '.'); + if (p) + *p = '\0'; + p = strchr (lang, '@'); + if (p) + *p = '\0'; + + return lang; +} + +GimpUnit +gimp_get_default_unit (void) +{ +#if defined (HAVE__NL_MEASUREMENT_MEASUREMENT) + const gchar *measurement = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT); + + switch (*((guchar *) measurement)) + { + case 1: /* metric */ + return GIMP_UNIT_MM; + + case 2: /* imperial */ + return GIMP_UNIT_INCH; + } + +#elif defined (G_OS_WIN32) + DWORD measurement; + int ret; + + ret = GetLocaleInfo(LOCALE_USER_DEFAULT, + LOCALE_IMEASURE | LOCALE_RETURN_NUMBER, + (LPTSTR)&measurement, + sizeof(measurement) / sizeof(TCHAR) ); + + if (ret != 0) /* GetLocaleInfo succeeded */ + { + switch ((guint) measurement) + { + case 0: /* metric */ + return GIMP_UNIT_MM; + + case 1: /* imperial */ + return GIMP_UNIT_INCH; + } + } +#endif + + return GIMP_UNIT_MM; +} + +gchar ** +gimp_properties_append (GType object_type, + gint *n_properties, + gchar **names, + GValue **values, + ...) +{ + va_list args; + + g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_OBJECT), NULL); + g_return_val_if_fail (n_properties != NULL, NULL); + g_return_val_if_fail (names != NULL || *n_properties == 0, NULL); + g_return_val_if_fail (values != NULL || *n_properties == 0, NULL); + + va_start (args, values); + names = gimp_properties_append_valist (object_type, n_properties, + names, values, args); + va_end (args); + + return names; +} + +gchar ** +gimp_properties_append_valist (GType object_type, + gint *n_properties, + gchar **names, + GValue **values, + va_list args) +{ + GObjectClass *object_class; + gchar *param_name; + + g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_OBJECT), NULL); + g_return_val_if_fail (n_properties != NULL, NULL); + g_return_val_if_fail (names != NULL || *n_properties == 0, NULL); + g_return_val_if_fail (values != NULL || *n_properties == 0, NULL); + + object_class = g_type_class_ref (object_type); + + param_name = va_arg (args, gchar *); + + while (param_name) + { + GValue *value; + gchar *error = NULL; + GParamSpec *pspec = g_object_class_find_property (object_class, + param_name); + + if (! pspec) + { + g_warning ("%s: object class `%s' has no property named `%s'", + G_STRFUNC, g_type_name (object_type), param_name); + break; + } + + names = g_renew (gchar *, names, *n_properties + 1); + *values = g_renew (GValue, *values, *n_properties + 1); + + value = &((*values)[*n_properties]); + + names[*n_properties] = g_strdup (param_name); + value->g_type = 0; + + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + G_VALUE_COLLECT (value, args, 0, &error); + + if (error) + { + g_warning ("%s: %s", G_STRFUNC, error); + g_free (error); + g_free (names[*n_properties]); + g_value_unset (value); + break; + } + + *n_properties = *n_properties + 1; + + param_name = va_arg (args, gchar *); + } + + g_type_class_unref (object_class); + + return names; +} + +void +gimp_properties_free (gint n_properties, + gchar **names, + GValue *values) +{ + g_return_if_fail (names != NULL || n_properties == 0); + g_return_if_fail (values != NULL || n_properties == 0); + + if (names && values) + { + gint i; + + for (i = 0; i < n_properties; i++) + { + g_free (names[i]); + g_value_unset (&values[i]); + } + + g_free (names); + g_free (values); + } +} + +/* markup unescape code stolen and adapted from gmarkup.c + */ +static gchar * +char_str (gunichar c, + gchar *buf) +{ + memset (buf, 0, 8); + g_unichar_to_utf8 (c, buf); + return buf; +} + +static gboolean +unescape_gstring (GString *string) +{ + const gchar *from; + gchar *to; + + /* + * Meeks' theorum: unescaping can only shrink text. + * for < etc. this is obvious, for ￿ more + * thought is required, but this is patently so. + */ + for (from = to = string->str; *from != '\0'; from++, to++) + { + *to = *from; + + if (*to == '\r') + { + *to = '\n'; + if (from[1] == '\n') + from++; + } + if (*from == '&') + { + from++; + if (*from == '#') + { + gboolean is_hex = FALSE; + gulong l; + gchar *end = NULL; + + from++; + + if (*from == 'x') + { + is_hex = TRUE; + from++; + } + + /* digit is between start and p */ + errno = 0; + if (is_hex) + l = strtoul (from, &end, 16); + else + l = strtoul (from, &end, 10); + + if (end == from || errno != 0) + { + return FALSE; + } + else if (*end != ';') + { + return FALSE; + } + else + { + /* characters XML 1.1 permits */ + if ((0 < l && l <= 0xD7FF) || + (0xE000 <= l && l <= 0xFFFD) || + (0x10000 <= l && l <= 0x10FFFF)) + { + gchar buf[8]; + char_str (l, buf); + strcpy (to, buf); + to += strlen (buf) - 1; + from = end; + } + else + { + return FALSE; + } + } + } + + else if (strncmp (from, "lt;", 3) == 0) + { + *to = '<'; + from += 2; + } + else if (strncmp (from, "gt;", 3) == 0) + { + *to = '>'; + from += 2; + } + else if (strncmp (from, "amp;", 4) == 0) + { + *to = '&'; + from += 3; + } + else if (strncmp (from, "quot;", 5) == 0) + { + *to = '"'; + from += 4; + } + else if (strncmp (from, "apos;", 5) == 0) + { + *to = '\''; + from += 4; + } + else + { + return FALSE; + } + } + } + + gimp_assert (to - string->str <= string->len); + if (to - string->str != string->len) + g_string_truncate (string, to - string->str); + + return TRUE; +} + +gchar * +gimp_markup_extract_text (const gchar *markup) +{ + GString *string; + const gchar *p; + gboolean in_tag = FALSE; + + if (! markup) + return NULL; + + string = g_string_new (NULL); + + for (p = markup; *p; p++) + { + if (in_tag) + { + if (*p == '>') + in_tag = FALSE; + } + else + { + if (*p == '<') + in_tag = TRUE; + else + g_string_append_c (string, *p); + } + } + + unescape_gstring (string); + + return g_string_free (string, FALSE); +} + +/** + * gimp_enum_get_value_name: + * @enum_type: Enum type + * @value: Enum value + * + * Returns the value name for a given value of a given enum + * type. Useful to have inline in GIMP_LOG() messages for example. + * + * Returns: The value name. + **/ +const gchar * +gimp_enum_get_value_name (GType enum_type, + gint value) +{ + const gchar *value_name = NULL; + + gimp_enum_get_value (enum_type, + value, + &value_name, + NULL /*value_nick*/, + NULL /*value_desc*/, + NULL /*value_help*/); + + return value_name; +} + +gboolean +gimp_get_fill_params (GimpContext *context, + GimpFillType fill_type, + GimpRGB *color, + GimpPattern **pattern, + GError **error) + +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (color != NULL, FALSE); + g_return_val_if_fail (pattern != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + *pattern = NULL; + + switch (fill_type) + { + case GIMP_FILL_FOREGROUND: + gimp_context_get_foreground (context, color); + break; + + case GIMP_FILL_BACKGROUND: + gimp_context_get_background (context, color); + break; + + case GIMP_FILL_WHITE: + gimp_rgba_set (color, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + break; + + case GIMP_FILL_TRANSPARENT: + gimp_rgba_set (color, 0.0, 0.0, 0.0, GIMP_OPACITY_TRANSPARENT); + break; + + case GIMP_FILL_PATTERN: + *pattern = gimp_context_get_pattern (context); + + if (! *pattern) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No patterns available for this operation.")); + + /* fall back to BG fill */ + gimp_context_get_background (context, color); + + return FALSE; + } + break; + + default: + g_warning ("%s: invalid fill_type %d", G_STRFUNC, fill_type); + return FALSE; + } + + return TRUE; +} + +/** + * gimp_constrain_line: + * @start_x: + * @start_y: + * @end_x: + * @end_y: + * @n_snap_lines: Number evenly disributed lines to snap to. + * @offset_angle: The angle by which to offset the lines, in degrees. + * @xres: The horizontal resolution. + * @yres: The vertical resolution. + * + * Projects a line onto the specified subset of evenly radially + * distributed lines. @n_lines of 2 makes the line snap horizontally + * or vertically. @n_lines of 4 snaps on 45 degree steps. @n_lines of + * 12 on 15 degree steps. etc. + **/ +void +gimp_constrain_line (gdouble start_x, + gdouble start_y, + gdouble *end_x, + gdouble *end_y, + gint n_snap_lines, + gdouble offset_angle, + gdouble xres, + gdouble yres) +{ + GimpVector2 diff; + GimpVector2 dir; + gdouble angle; + + offset_angle *= G_PI / 180.0; + + diff.x = (*end_x - start_x) / xres; + diff.y = (*end_y - start_y) / yres; + + angle = (atan2 (diff.y, diff.x) - offset_angle) * n_snap_lines / G_PI; + angle = RINT (angle) * G_PI / n_snap_lines + offset_angle; + + dir.x = cos (angle); + dir.y = sin (angle); + + gimp_vector2_mul (&dir, gimp_vector2_inner_product (&dir, &diff)); + + *end_x = start_x + dir.x * xres; + *end_y = start_y + dir.y * yres; +} + +gint +gimp_file_compare (GFile *file1, + GFile *file2) +{ + if (g_file_equal (file1, file2)) + { + return 0; + } + else + { + gchar *uri1 = g_file_get_uri (file1); + gchar *uri2 = g_file_get_uri (file2); + gint result = strcmp (uri1, uri2); + + g_free (uri1); + g_free (uri2); + + return result; + } +} + +static inline gboolean +is_script (const gchar *filename) +{ +#ifdef G_OS_WIN32 + /* On Windows there is no concept like the Unix executable flag. + * There is a weak emulation provided by the MS C Runtime using file + * extensions (com, exe, cmd, bat). This needs to be extended to + * treat scripts (Python, Perl, ...) as executables, too. We use the + * PATHEXT variable, which is also used by cmd.exe. + */ + static gchar **exts = NULL; + + const gchar *ext = strrchr (filename, '.'); + const gchar *pathext; + gint i; + + if (exts == NULL) + { + pathext = g_getenv ("PATHEXT"); + if (pathext != NULL) + { + exts = g_strsplit (pathext, G_SEARCHPATH_SEPARATOR_S, 100); + } + else + { + exts = g_new (gchar *, 1); + exts[0] = NULL; + } + } + + for (i = 0; exts[i]; i++) + { + if (g_ascii_strcasecmp (ext, exts[i]) == 0) + return TRUE; + } +#endif /* G_OS_WIN32 */ + + return FALSE; +} + +gboolean +gimp_file_is_executable (GFile *file) +{ + GFileInfo *info; + gboolean executable = FALSE; + + g_return_val_if_fail (G_IS_FILE (file), FALSE); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ",", + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (info) + { + GFileType file_type = g_file_info_get_file_type (info); + const gchar *filename = g_file_info_get_name (info); + + if (file_type == G_FILE_TYPE_REGULAR && + (g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) || + is_script (filename))) + { + executable = TRUE; + } + + g_object_unref (info); + } + + return executable; +} + +/** + * gimp_file_get_extension: + * @file: A #GFile + * + * Returns @file's extension (including the .), or NULL if there is no + * extension. Note that this function handles compressed files too, + * e.g. for "file.png.gz" it will return ".png.gz". + * + * Returns: The @file's extension. Free with g_free() when no longer needed. + **/ +gchar * +gimp_file_get_extension (GFile *file) +{ + gchar *uri; + gint uri_len; + gchar *ext = NULL; + gint search_len; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + + uri = g_file_get_uri (file); + uri_len = strlen (uri); + + if (g_str_has_suffix (uri, ".gz")) + search_len = uri_len - 3; + else if (g_str_has_suffix (uri, ".bz2")) + search_len = uri_len - 4; + else if (g_str_has_suffix (uri, ".xz")) + search_len = uri_len - 3; + else + search_len = uri_len; + + ext = g_strrstr_len (uri, search_len, "."); + + if (ext) + ext = g_strdup (ext); + + g_free (uri); + + return ext; +} + +GFile * +gimp_file_with_new_extension (GFile *file, + GFile *ext_file) +{ + gchar *uri; + gchar *file_ext; + gint file_ext_len = 0; + gchar *ext_file_ext = NULL; + gchar *uri_without_ext; + gchar *new_uri; + GFile *ret; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (ext_file == NULL || G_IS_FILE (ext_file), NULL); + + uri = g_file_get_uri (file); + file_ext = gimp_file_get_extension (file); + + if (file_ext) + { + file_ext_len = strlen (file_ext); + g_free (file_ext); + } + + if (ext_file) + ext_file_ext = gimp_file_get_extension (ext_file); + + uri_without_ext = g_strndup (uri, strlen (uri) - file_ext_len); + + g_free (uri); + + new_uri = g_strconcat (uri_without_ext, ext_file_ext, NULL); + + ret = g_file_new_for_uri (new_uri); + + g_free (ext_file_ext); + g_free (uri_without_ext); + g_free (new_uri); + + return ret; +} + +gchar * +gimp_data_input_stream_read_line_always (GDataInputStream *stream, + gsize *length, + GCancellable *cancellable, + GError **error) +{ + GError *temp_error = NULL; + gchar *result; + + g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (! error) + error = &temp_error; + + result = g_data_input_stream_read_line (stream, length, cancellable, error); + + if (! result && ! *error) + { + result = g_strdup (""); + + if (length) *length = 0; + } + + g_clear_error (&temp_error); + + return result; +} + +gboolean +gimp_ascii_strtoi (const gchar *nptr, + gchar **endptr, + gint base, + gint *result) +{ + gchar *temp_endptr; + gint64 temp_result; + + g_return_val_if_fail (nptr != NULL, FALSE); + g_return_val_if_fail (base == 0 || (base >= 2 && base <= 36), FALSE); + + if (! endptr) + endptr = &temp_endptr; + + temp_result = g_ascii_strtoll (nptr, endptr, base); + + if (*endptr == nptr || errno == ERANGE || + temp_result < G_MININT || temp_result > G_MAXINT) + { + errno = 0; + + return FALSE; + } + + if (result) *result = temp_result; + + return TRUE; +} + +gboolean +gimp_ascii_strtod (const gchar *nptr, + gchar **endptr, + gdouble *result) +{ + gchar *temp_endptr; + gdouble temp_result; + + g_return_val_if_fail (nptr != NULL, FALSE); + + if (! endptr) + endptr = &temp_endptr; + + temp_result = g_ascii_strtod (nptr, endptr); + + if (*endptr == nptr || errno == ERANGE) + { + errno = 0; + + return FALSE; + } + + if (result) *result = temp_result; + + return TRUE; +} + +gint +gimp_g_list_compare (GList *list1, + GList *list2) +{ + while (list1 && list2) + { + if (list1->data < list2->data) + return -1; + else if (list1->data > list2->data) + return +1; + + list1 = g_list_next (list1); + list2 = g_list_next (list2); + } + + if (! list1) + return -1; + else if (! list2) + return +1; + + return 0; +} + +typedef struct +{ + gint ref_count; + + GimpAsync *async; + gint idle_id; + + GimpRunAsyncFunc func; + gpointer user_data; + GDestroyNotify user_data_destroy_func; +} GimpIdleRunAsyncData; + +static GimpIdleRunAsyncData * +gimp_idle_run_async_data_new (void) +{ + GimpIdleRunAsyncData *data; + + data = g_slice_new0 (GimpIdleRunAsyncData); + + data->ref_count = 1; + + return data; +} + +static void +gimp_idle_run_async_data_inc_ref (GimpIdleRunAsyncData *data) +{ + data->ref_count++; +} + +static void +gimp_idle_run_async_data_dec_ref (GimpIdleRunAsyncData *data) +{ + data->ref_count--; + + if (data->ref_count == 0) + { + g_signal_handlers_disconnect_by_data (data->async, data); + + if (! gimp_async_is_stopped (data->async)) + gimp_async_abort (data->async); + + g_object_unref (data->async); + + if (data->user_data && data->user_data_destroy_func) + data->user_data_destroy_func (data->user_data); + + g_slice_free (GimpIdleRunAsyncData, data); + } +} + +static void +gimp_idle_run_async_cancel (GimpAsync *async, + GimpIdleRunAsyncData *data) +{ + gimp_idle_run_async_data_inc_ref (data); + + if (data->idle_id) + { + g_source_remove (data->idle_id); + + data->idle_id = 0; + } + + gimp_idle_run_async_data_dec_ref (data); +} + +static void +gimp_idle_run_async_waiting (GimpAsync *async, + GimpIdleRunAsyncData *data) +{ + gimp_idle_run_async_data_inc_ref (data); + + if (data->idle_id) + { + g_source_remove (data->idle_id); + + data->idle_id = 0; + } + + g_signal_handlers_block_by_func (data->async, + gimp_idle_run_async_cancel, + data); + + while (! gimp_async_is_stopped (data->async)) + data->func (data->async, data->user_data); + + g_signal_handlers_unblock_by_func (data->async, + gimp_idle_run_async_cancel, + data); + + data->user_data = NULL; + + gimp_idle_run_async_data_dec_ref (data); +} + +static gboolean +gimp_idle_run_async_idle (GimpIdleRunAsyncData *data) +{ + gimp_idle_run_async_data_inc_ref (data); + + g_signal_handlers_block_by_func (data->async, + gimp_idle_run_async_cancel, + data); + + data->func (data->async, data->user_data); + + g_signal_handlers_unblock_by_func (data->async, + gimp_idle_run_async_cancel, + data); + + if (gimp_async_is_stopped (data->async)) + { + data->user_data = NULL; + + gimp_idle_run_async_data_dec_ref (data); + + return G_SOURCE_REMOVE; + } + + gimp_idle_run_async_data_dec_ref (data); + + return G_SOURCE_CONTINUE; +} + +GimpAsync * +gimp_idle_run_async (GimpRunAsyncFunc func, + gpointer user_data) +{ + return gimp_idle_run_async_full (G_PRIORITY_DEFAULT_IDLE, func, + user_data, NULL); +} + +GimpAsync * +gimp_idle_run_async_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy_func) +{ + GimpIdleRunAsyncData *data; + + g_return_val_if_fail (func != NULL, NULL); + + data = gimp_idle_run_async_data_new (); + + data->func = func; + data->user_data = user_data; + data->user_data_destroy_func = user_data_destroy_func; + + data->async = gimp_async_new (); + + g_signal_connect (data->async, "cancel", + G_CALLBACK (gimp_idle_run_async_cancel), + data); + g_signal_connect (data->async, "waiting", + G_CALLBACK (gimp_idle_run_async_waiting), + data); + + data->idle_id = g_idle_add_full ( + priority, + (GSourceFunc) gimp_idle_run_async_idle, + data, + (GDestroyNotify) gimp_idle_run_async_data_dec_ref); + + return g_object_ref (data->async); +} + + +/* debug stuff */ + +#include "gegl/gimp-babl.h" +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayer-new.h" + +GimpImage * +gimp_create_image_from_buffer (Gimp *gimp, + GeglBuffer *buffer, + const gchar *image_name) +{ + GimpImage *image; + GimpLayer *layer; + const Babl *format; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + if (! image_name) + image_name = "Debug Image"; + + format = gegl_buffer_get_format (buffer); + + image = gimp_create_image (gimp, + gegl_buffer_get_width (buffer), + gegl_buffer_get_height (buffer), + gimp_babl_format_get_base_type (format), + gimp_babl_format_get_precision (format), + FALSE); + + layer = gimp_layer_new_from_gegl_buffer (buffer, image, format, + image_name, + GIMP_OPACITY_OPAQUE, + GIMP_LAYER_MODE_NORMAL, + NULL /* same image */); + gimp_image_add_layer (image, layer, NULL, -1, FALSE); + + gimp_create_display (gimp, image, GIMP_UNIT_PIXEL, 1.0, NULL, 0); + + /* unref the image unconditionally, even when no display was created */ + g_object_add_weak_pointer (G_OBJECT (image), (gpointer) &image); + g_object_unref (image); + + return image; +} diff --git a/app/core/gimp-utils.h b/app/core/gimp-utils.h new file mode 100644 index 0000000..8373de7 --- /dev/null +++ b/app/core/gimp-utils.h @@ -0,0 +1,116 @@ +/* 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 . + */ + +#ifndef __APP_GIMP_UTILS_H__ +#define __APP_GIMP_UTILS_H__ + + +#define GIMP_TIMER_START() \ + { GTimer *_timer = g_timer_new (); + +#define GIMP_TIMER_END(message) \ + g_printerr ("%s: %s took %0.4f seconds\n", \ + G_STRFUNC, message, g_timer_elapsed (_timer, NULL)); \ + g_timer_destroy (_timer); } + + +#define MIN4(a,b,c,d) MIN (MIN ((a), (b)), MIN ((c), (d))) +#define MAX4(a,b,c,d) MAX (MAX ((a), (b)), MAX ((c), (d))) + + +gint gimp_get_pid (void); +guint64 gimp_get_physical_memory_size (void); +gchar * gimp_get_default_language (const gchar *category); +GimpUnit gimp_get_default_unit (void); + +gchar ** gimp_properties_append (GType object_type, + gint *n_properties, + gchar **names, + GValue **values, + ...) G_GNUC_NULL_TERMINATED; +gchar ** gimp_properties_append_valist (GType object_type, + gint *n_properties, + gchar **names, + GValue **values, + va_list args); +void gimp_properties_free (gint n_properties, + gchar **names, + GValue *values); + +gchar * gimp_markup_extract_text (const gchar *markup); + +const gchar* gimp_enum_get_value_name (GType enum_type, + gint value); + +gboolean gimp_get_fill_params (GimpContext *context, + GimpFillType fill_type, + GimpRGB *color, + GimpPattern **pattern, + GError **error); + +/* Common values for the n_snap_lines parameter of + * gimp_constrain_line. + */ +#define GIMP_CONSTRAIN_LINE_90_DEGREES 2 +#define GIMP_CONSTRAIN_LINE_45_DEGREES 4 +#define GIMP_CONSTRAIN_LINE_15_DEGREES 12 + +void gimp_constrain_line (gdouble start_x, + gdouble start_y, + gdouble *end_x, + gdouble *end_y, + gint n_snap_lines, + gdouble offset_angle, + gdouble xres, + gdouble yres); + +gint gimp_file_compare (GFile *file1, + GFile *file2); +gboolean gimp_file_is_executable (GFile *file); +gchar * gimp_file_get_extension (GFile *file); +GFile * gimp_file_with_new_extension (GFile *file, + GFile *ext_file); + +gchar * gimp_data_input_stream_read_line_always (GDataInputStream *stream, + gsize *length, + GCancellable *cancellable, + GError **error); + +gboolean gimp_ascii_strtoi (const gchar *nptr, + gchar **endptr, + gint base, + gint *result); +gboolean gimp_ascii_strtod (const gchar *nptr, + gchar **endptr, + gdouble *result); + +gint gimp_g_list_compare (GList *list1, + GList *list2); + +GimpAsync * gimp_idle_run_async (GimpRunAsyncFunc func, + gpointer user_data); +GimpAsync * gimp_idle_run_async_full (gint priority, + GimpRunAsyncFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy_func); + +GimpImage * gimp_create_image_from_buffer (Gimp *gimp, + GeglBuffer *buffer, + const gchar *image_name); + + +#endif /* __APP_GIMP_UTILS_H__ */ diff --git a/app/core/gimp.c b/app/core/gimp.c new file mode 100644 index 0000000..ede44fe --- /dev/null +++ b/app/core/gimp.c @@ -0,0 +1,1224 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include /* strlen */ + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimprc.h" + +#include "gegl/gimp-babl.h" + +#include "pdb/gimppdb.h" +#include "pdb/gimp-pdb-compat.h" +#include "pdb/internal-procs.h" + +#include "plug-in/gimppluginmanager.h" +#include "plug-in/gimppluginmanager-restore.h" + +#include "paint/gimp-paint.h" + +#include "xcf/xcf.h" +#include "file-data/file-data.h" + +#include "gimp.h" +#include "gimp-contexts.h" +#include "gimp-data-factories.h" +#include "gimp-filter-history.h" +#include "gimp-memsize.h" +#include "gimp-modules.h" +#include "gimp-parasites.h" +#include "gimp-templates.h" +#include "gimp-units.h" +#include "gimp-utils.h" +#include "gimpbrush.h" +#include "gimpbuffer.h" +#include "gimpcontext.h" +#include "gimpdynamics.h" +#include "gimpdocumentlist.h" +#include "gimpgradient.h" +#include "gimpidtable.h" +#include "gimpimage.h" +#include "gimpimagefile.h" +#include "gimplist.h" +#include "gimpmarshal.h" +#include "gimpmybrush.h" +#include "gimppalette.h" +#include "gimpparasitelist.h" +#include "gimppattern.h" +#include "gimptemplate.h" +#include "gimptoolinfo.h" +#include "gimptreeproxy.h" + +#include "gimp-intl.h" + + +enum +{ + INITIALIZE, + RESTORE, + EXIT, + CLIPBOARD_CHANGED, + FILTER_HISTORY_CHANGED, + IMAGE_OPENED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_VERBOSE +}; + + +static void gimp_constructed (GObject *object); +static void gimp_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_dispose (GObject *object); +static void gimp_finalize (GObject *object); + +static gint64 gimp_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_real_initialize (Gimp *gimp, + GimpInitStatusFunc status_callback); +static void gimp_real_restore (Gimp *gimp, + GimpInitStatusFunc status_callback); +static gboolean gimp_real_exit (Gimp *gimp, + gboolean force); + +static void gimp_global_config_notify (GObject *global_config, + GParamSpec *param_spec, + GObject *edit_config); +static void gimp_edit_config_notify (GObject *edit_config, + GParamSpec *param_spec, + GObject *global_config); + + +G_DEFINE_TYPE (Gimp, gimp, GIMP_TYPE_OBJECT) + +#define parent_class gimp_parent_class + +static guint gimp_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_class_init (GimpClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + gimp_signals[INITIALIZE] = + g_signal_new ("initialize", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, initialize), + NULL, NULL, + gimp_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + gimp_signals[RESTORE] = + g_signal_new ("restore", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, restore), + NULL, NULL, + gimp_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + gimp_signals[EXIT] = + g_signal_new ("exit", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, exit), + g_signal_accumulator_true_handled, NULL, + gimp_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, + G_TYPE_BOOLEAN); + + gimp_signals[CLIPBOARD_CHANGED] = + g_signal_new ("clipboard-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, clipboard_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_signals[FILTER_HISTORY_CHANGED] = + g_signal_new ("filter-history-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, + filter_history_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_signals[IMAGE_OPENED] = + g_signal_new ("image-opened", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpClass, image_opened), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_FILE); + + object_class->constructed = gimp_constructed; + object_class->set_property = gimp_set_property; + object_class->get_property = gimp_get_property; + object_class->dispose = gimp_dispose; + object_class->finalize = gimp_finalize; + + gimp_object_class->get_memsize = gimp_get_memsize; + + klass->initialize = gimp_real_initialize; + klass->restore = gimp_real_restore; + klass->exit = gimp_real_exit; + klass->clipboard_changed = NULL; + + g_object_class_install_property (object_class, PROP_VERBOSE, + g_param_spec_boolean ("verbose", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_init (Gimp *gimp) +{ + gimp->be_verbose = FALSE; + gimp->no_data = FALSE; + gimp->no_interface = FALSE; + gimp->show_gui = TRUE; + gimp->use_shm = FALSE; + gimp->use_cpu_accel = TRUE; + gimp->message_handler = GIMP_CONSOLE; + gimp->show_playground = FALSE; + gimp->stack_trace_mode = GIMP_STACK_TRACE_NEVER; + gimp->pdb_compat_mode = GIMP_PDB_COMPAT_OFF; + + gimp_gui_init (gimp); + + gimp->parasites = gimp_parasite_list_new (); + + gimp_units_init (gimp); + + gimp->images = gimp_list_new_weak (GIMP_TYPE_IMAGE, FALSE); + gimp_object_set_static_name (GIMP_OBJECT (gimp->images), "images"); + + gimp->next_guide_ID = 1; + gimp->next_sample_point_ID = 1; + gimp->image_table = gimp_id_table_new (); + gimp->item_table = gimp_id_table_new (); + + gimp->displays = g_object_new (GIMP_TYPE_LIST, + "children-type", GIMP_TYPE_OBJECT, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "append", TRUE, + NULL); + gimp_object_set_static_name (GIMP_OBJECT (gimp->displays), "displays"); + gimp->next_display_ID = 1; + + gimp->named_buffers = gimp_list_new (GIMP_TYPE_BUFFER, TRUE); + gimp_object_set_static_name (GIMP_OBJECT (gimp->named_buffers), + "named buffers"); + + gimp_data_factories_init (gimp); + + gimp->tool_info_list = g_object_new (GIMP_TYPE_LIST, + "children-type", GIMP_TYPE_TOOL_INFO, + "append", TRUE, + NULL); + gimp_object_set_static_name (GIMP_OBJECT (gimp->tool_info_list), + "tool infos"); + + gimp->tool_item_list = g_object_new (GIMP_TYPE_LIST, + "children-type", GIMP_TYPE_TOOL_ITEM, + "append", TRUE, + NULL); + gimp_object_set_static_name (GIMP_OBJECT (gimp->tool_item_list), + "tool items"); + + gimp->tool_item_ui_list = gimp_tree_proxy_new_for_container ( + gimp->tool_item_list); + gimp_object_set_static_name (GIMP_OBJECT (gimp->tool_item_ui_list), + "ui tool items"); + + gimp->documents = gimp_document_list_new (gimp); + + gimp->templates = gimp_list_new (GIMP_TYPE_TEMPLATE, TRUE); + gimp_object_set_static_name (GIMP_OBJECT (gimp->templates), "templates"); +} + +static void +gimp_constructed (GObject *object) +{ + Gimp *gimp = GIMP (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_modules_init (gimp); + + gimp_paint_init (gimp); + + gimp->plug_in_manager = gimp_plug_in_manager_new (gimp); + gimp->pdb = gimp_pdb_new (gimp); + + xcf_init (gimp); + file_data_init (gimp); + + /* create user and default context */ + gimp_contexts_init (gimp); +} + +static void +gimp_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + Gimp *gimp = GIMP (object); + + switch (property_id) + { + case PROP_VERBOSE: + gimp->be_verbose = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + Gimp *gimp = GIMP (object); + + switch (property_id) + { + case PROP_VERBOSE: + g_value_set_boolean (value, gimp->be_verbose); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dispose (GObject *object) +{ + Gimp *gimp = GIMP (object); + + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + gimp_data_factories_clear (gimp); + + gimp_filter_history_clear (gimp); + + g_clear_object (&gimp->edit_config); + g_clear_object (&gimp->config); + + gimp_contexts_exit (gimp); + + g_clear_object (&gimp->image_new_last_template); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_finalize (GObject *object) +{ + Gimp *gimp = GIMP (object); + GList *standards = NULL; + + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + standards = g_list_prepend (standards, + gimp_brush_get_standard (gimp->user_context)); + standards = g_list_prepend (standards, + gimp_dynamics_get_standard (gimp->user_context)); + standards = g_list_prepend (standards, + gimp_mybrush_get_standard (gimp->user_context)); + standards = g_list_prepend (standards, + gimp_pattern_get_standard (gimp->user_context)); + standards = g_list_prepend (standards, + gimp_gradient_get_standard (gimp->user_context)); + standards = g_list_prepend (standards, + gimp_palette_get_standard (gimp->user_context)); + + g_clear_object (&gimp->templates); + g_clear_object (&gimp->documents); + + gimp_tool_info_set_standard (gimp, NULL); + + g_clear_object (&gimp->tool_item_list); + g_clear_object (&gimp->tool_item_ui_list); + + if (gimp->tool_info_list) + { + gimp_container_foreach (gimp->tool_info_list, + (GFunc) g_object_run_dispose, NULL); + g_clear_object (&gimp->tool_info_list); + } + + file_data_exit (gimp); + xcf_exit (gimp); + + g_clear_object (&gimp->pdb); + + gimp_data_factories_exit (gimp); + + g_clear_object (&gimp->named_buffers); + g_clear_object (&gimp->clipboard_buffer); + g_clear_object (&gimp->clipboard_image); + g_clear_object (&gimp->displays); + g_clear_object (&gimp->item_table); + g_clear_object (&gimp->image_table); + g_clear_object (&gimp->images); + g_clear_object (&gimp->plug_in_manager); + + if (gimp->module_db) + gimp_modules_exit (gimp); + + gimp_paint_exit (gimp); + + g_clear_object (&gimp->parasites); + g_clear_object (&gimp->default_folder); + + g_clear_pointer (&gimp->session_name, g_free); + + if (gimp->context_list) + { + GList *list; + + g_warning ("%s: list of contexts not empty upon exit (%d contexts left)\n", + G_STRFUNC, g_list_length (gimp->context_list)); + + for (list = gimp->context_list; list; list = g_list_next (list)) + g_printerr ("stale context: %s\n", gimp_object_get_name (list->data)); + + g_list_free (gimp->context_list); + gimp->context_list = NULL; + } + + g_list_foreach (standards, (GFunc) g_object_unref, NULL); + g_list_free (standards); + + gimp_units_exit (gimp); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + Gimp *gimp = GIMP (object); + gint64 memsize = 0; + + memsize += gimp_g_list_get_memsize (gimp->user_units, 0 /* FIXME */); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->parasites), + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->paint_info_list), + gui_size); + + memsize += gimp_g_object_get_memsize (G_OBJECT (gimp->module_db)); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->plug_in_manager), + gui_size); + + memsize += gimp_g_list_get_memsize_foreach (gimp->filter_history, + (GimpMemsizeFunc) + gimp_object_get_memsize, + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->image_table), 0); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->item_table), 0); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->displays), gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->clipboard_image), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->clipboard_buffer), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->named_buffers), + gui_size); + + memsize += gimp_data_factories_get_memsize (gimp, gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->pdb), gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tool_info_list), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->standard_tool_info), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->documents), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->templates), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->image_new_last_template), + gui_size); + + memsize += gimp_g_list_get_memsize (gimp->context_list, 0); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->default_context), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->user_context), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_real_initialize (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + status_callback (_("Initialization"), NULL, 0.0); + + /* set the last values used to default values */ + gimp->image_new_last_template = + gimp_config_duplicate (GIMP_CONFIG (gimp->config->default_image)); + + /* add data objects that need the user context */ + gimp_data_factories_add_builtin (gimp); + + /* register all internal procedures */ + status_callback (NULL, _("Internal Procedures"), 0.2); + internal_procs_init (gimp->pdb); + gimp_pdb_compat_procs_register (gimp->pdb, gimp->pdb_compat_mode); + + gimp_plug_in_manager_initialize (gimp->plug_in_manager, status_callback); + + status_callback (NULL, "", 1.0); +} + +static void +gimp_real_restore (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + gimp_plug_in_manager_restore (gimp->plug_in_manager, + gimp_get_user_context (gimp), status_callback); + + /* initialize babl fishes */ + status_callback (_("Initialization"), "Babl Fishes", 0.0); + gimp_babl_init_fishes (status_callback); + + gimp->restored = TRUE; +} + +static gboolean +gimp_real_exit (Gimp *gimp, + gboolean force) +{ + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + gimp_plug_in_manager_exit (gimp->plug_in_manager); + gimp_modules_unload (gimp); + + gimp_data_factories_save (gimp); + + gimp_templates_save (gimp); + gimp_parasiterc_save (gimp); + gimp_unitrc_save (gimp); + + return FALSE; /* continue exiting */ +} + +Gimp * +gimp_new (const gchar *name, + const gchar *session_name, + GFile *default_folder, + gboolean be_verbose, + gboolean no_data, + gboolean no_fonts, + gboolean no_interface, + gboolean use_shm, + gboolean use_cpu_accel, + gboolean console_messages, + gboolean show_playground, + gboolean show_debug_menu, + GimpStackTraceMode stack_trace_mode, + GimpPDBCompatMode pdb_compat_mode) +{ + Gimp *gimp; + + g_return_val_if_fail (name != NULL, NULL); + + gimp = g_object_new (GIMP_TYPE_GIMP, + "name", name, + "verbose", be_verbose ? TRUE : FALSE, + NULL); + + if (default_folder) + gimp->default_folder = g_object_ref (default_folder); + + gimp->session_name = g_strdup (session_name); + gimp->no_data = no_data ? TRUE : FALSE; + gimp->no_fonts = no_fonts ? TRUE : FALSE; + gimp->no_interface = no_interface ? TRUE : FALSE; + gimp->use_shm = use_shm ? TRUE : FALSE; + gimp->use_cpu_accel = use_cpu_accel ? TRUE : FALSE; + gimp->console_messages = console_messages ? TRUE : FALSE; + gimp->show_playground = show_playground ? TRUE : FALSE; + gimp->show_debug_menu = show_debug_menu ? TRUE : FALSE; + gimp->stack_trace_mode = stack_trace_mode; + gimp->pdb_compat_mode = pdb_compat_mode; + + return gimp; +} + +/** + * gimp_set_show_gui: + * @gimp: + * @show: + * + * Test cases that tests the UI typically don't want any windows to be + * presented during the test run. Allow them to set this. + **/ +void +gimp_set_show_gui (Gimp *gimp, + gboolean show_gui) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp->show_gui = show_gui; +} + +/** + * gimp_get_show_gui: + * @gimp: + * + * Returns: %TRUE if the GUI should be shown, %FALSE otherwise. + **/ +gboolean +gimp_get_show_gui (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + + return gimp->show_gui; +} + +static void +gimp_global_config_notify (GObject *global_config, + GParamSpec *param_spec, + GObject *edit_config) +{ + GValue global_value = G_VALUE_INIT; + GValue edit_value = G_VALUE_INIT; + + g_value_init (&global_value, param_spec->value_type); + g_value_init (&edit_value, param_spec->value_type); + + g_object_get_property (global_config, param_spec->name, &global_value); + g_object_get_property (edit_config, param_spec->name, &edit_value); + + if (g_param_values_cmp (param_spec, &global_value, &edit_value)) + { + g_signal_handlers_block_by_func (edit_config, + gimp_edit_config_notify, + global_config); + + g_object_set_property (edit_config, param_spec->name, &global_value); + + g_signal_handlers_unblock_by_func (edit_config, + gimp_edit_config_notify, + global_config); + } + + g_value_unset (&global_value); + g_value_unset (&edit_value); +} + +static void +gimp_edit_config_notify (GObject *edit_config, + GParamSpec *param_spec, + GObject *global_config) +{ + GValue edit_value = G_VALUE_INIT; + GValue global_value = G_VALUE_INIT; + + g_value_init (&edit_value, param_spec->value_type); + g_value_init (&global_value, param_spec->value_type); + + g_object_get_property (edit_config, param_spec->name, &edit_value); + g_object_get_property (global_config, param_spec->name, &global_value); + + if (g_param_values_cmp (param_spec, &edit_value, &global_value)) + { + if (param_spec->flags & GIMP_CONFIG_PARAM_RESTART) + { +#ifdef GIMP_CONFIG_DEBUG + g_print ("NOT Applying edit_config change of '%s' to global_config " + "because it needs restart\n", + param_spec->name); +#endif + } + else + { +#ifdef GIMP_CONFIG_DEBUG + g_print ("Applying edit_config change of '%s' to global_config\n", + param_spec->name); +#endif + g_signal_handlers_block_by_func (global_config, + gimp_global_config_notify, + edit_config); + + g_object_set_property (global_config, param_spec->name, &edit_value); + + g_signal_handlers_unblock_by_func (global_config, + gimp_global_config_notify, + edit_config); + } + } + + g_value_unset (&edit_value); + g_value_unset (&global_value); +} + +void +gimp_load_config (Gimp *gimp, + GFile *alternate_system_gimprc, + GFile *alternate_gimprc) +{ + GimpRc *gimprc; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (alternate_system_gimprc == NULL || + G_IS_FILE (alternate_system_gimprc)); + g_return_if_fail (alternate_gimprc == NULL || + G_IS_FILE (alternate_gimprc)); + g_return_if_fail (gimp->config == NULL); + g_return_if_fail (gimp->edit_config == NULL); + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + /* this needs to be done before gimprc loading because gimprc can + * use user defined units + */ + gimp_unitrc_load (gimp); + + gimprc = gimp_rc_new (G_OBJECT (gimp), + alternate_system_gimprc, + alternate_gimprc, + gimp->be_verbose); + + gimp->config = GIMP_CORE_CONFIG (gimprc); + + gimp->edit_config = gimp_config_duplicate (GIMP_CONFIG (gimp->config)); + + g_signal_connect_object (gimp->config, "notify", + G_CALLBACK (gimp_global_config_notify), + gimp->edit_config, 0); + g_signal_connect_object (gimp->edit_config, "notify", + G_CALLBACK (gimp_edit_config_notify), + gimp->config, 0); + + if (! gimp->show_playground) + { + gboolean use_opencl; + gboolean use_npd_tool; + gboolean use_seamless_clone_tool; + + /* Playground preferences is shown by default for unstable + * versions and if the associated CLI option was set. Additionally + * we want to show it if any of the playground options had been + * enabled. Otherwise you might end up getting blocked with a + * playground feature and forget where you can even disable it. + * + * Also we check this once at start when loading config, and not + * inside preferences-dialog.c because we don't want to end up + * with inconsistent behavior where you open once the Preferences, + * deactivate features, then back to preferences and the tab is + * gone. + */ + + g_object_get (gimp->edit_config, + "use-opencl", &use_opencl, + "playground-npd-tool", &use_npd_tool, + "playground-seamless-clone-tool", &use_seamless_clone_tool, + NULL); + if (use_opencl || use_npd_tool || use_seamless_clone_tool) + gimp->show_playground = TRUE; + } +} + +void +gimp_initialize (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (status_callback != NULL); + g_return_if_fail (GIMP_IS_CORE_CONFIG (gimp->config)); + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + g_signal_emit (gimp, gimp_signals[INITIALIZE], 0, status_callback); +} + +/** + * gimp_restore: + * @gimp: a #Gimp object + * @error: a #GError for uncessful loading. + * + * This function always succeeds. If present, @error may be filled for + * possible feedback on data which failed to load. It doesn't imply any + * fatale error. + **/ +void +gimp_restore (Gimp *gimp, + GimpInitStatusFunc status_callback, + GError **error) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (status_callback != NULL); + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + /* initialize the global parasite table */ + status_callback (_("Looking for data files"), _("Parasites"), 0.0); + gimp_parasiterc_load (gimp); + + /* initialize the lists of gimp brushes, dynamics, patterns etc. */ + gimp_data_factories_load (gimp, status_callback); + + /* initialize the template list */ + status_callback (NULL, _("Templates"), 0.8); + gimp_templates_load (gimp); + + /* initialize the module list */ + status_callback (NULL, _("Modules"), 0.9); + gimp_modules_load (gimp); + + g_signal_emit (gimp, gimp_signals[RESTORE], 0, status_callback); + + /* when done, make sure everything is clean, to clean out dirty + * states from data objects which reference each other and got + * dirtied by loading the referenced object + */ + gimp_data_factories_data_clean (gimp); +} + +/** + * gimp_is_restored: + * @gimp: a #Gimp object + * + * Return value: %TRUE if GIMP is completely started, %FALSE otherwise. + **/ +gboolean +gimp_is_restored (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + + return gimp->restored; +} + +/** + * gimp_exit: + * @gimp: a #Gimp object + * @force: whether to force the application to quit + * + * Exit this GIMP session. Unless @force is %TRUE, the user is queried + * whether unsaved images should be saved and can cancel the operation. + **/ +void +gimp_exit (Gimp *gimp, + gboolean force) +{ + gboolean handled; + GList *image_iter; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + g_signal_emit (gimp, gimp_signals[EXIT], 0, + force ? TRUE : FALSE, + &handled); + + if (handled) + return; + + /* Get rid of images without display. We do this *after* handling the + * usual exit callbacks, because the things that are torn down there + * might have references to these images (for instance GimpActions + * in the UI manager). + */ + while ((image_iter = gimp_get_image_iter (gimp))) + { + GimpImage *image = image_iter->data; + + g_object_unref (image); + } +} + +GList * +gimp_get_image_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->images)->queue->head; +} + +GList * +gimp_get_display_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->displays)->queue->head; +} + +GList * +gimp_get_image_windows (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_list_copy (gimp->image_windows); +} + +GList * +gimp_get_paint_info_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->paint_info_list)->queue->head; +} + +GList * +gimp_get_tool_info_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->tool_info_list)->queue->head; +} + +GList * +gimp_get_tool_item_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->tool_item_list)->queue->head; +} + +GList * +gimp_get_tool_item_ui_iter (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return GIMP_LIST (gimp->tool_item_ui_list)->queue->head; +} + +GimpObject * +gimp_get_clipboard_object (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->clipboard_image) + return GIMP_OBJECT (gimp->clipboard_image); + + return GIMP_OBJECT (gimp->clipboard_buffer); +} + +void +gimp_set_clipboard_image (Gimp *gimp, + GimpImage *image) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + + g_clear_object (&gimp->clipboard_buffer); + g_set_object (&gimp->clipboard_image, image); + + /* we want the signal emission */ + g_signal_emit (gimp, gimp_signals[CLIPBOARD_CHANGED], 0); +} + +GimpImage * +gimp_get_clipboard_image (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->clipboard_image; +} + +void +gimp_set_clipboard_buffer (Gimp *gimp, + GimpBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (buffer == NULL || GIMP_IS_BUFFER (buffer)); + + g_clear_object (&gimp->clipboard_image); + g_set_object (&gimp->clipboard_buffer, buffer); + + /* we want the signal emission */ + g_signal_emit (gimp, gimp_signals[CLIPBOARD_CHANGED], 0); +} + +GimpBuffer * +gimp_get_clipboard_buffer (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->clipboard_buffer; +} + +GimpImage * +gimp_create_image (Gimp *gimp, + gint width, + gint height, + GimpImageBaseType type, + GimpPrecision precision, + gboolean attach_comment) +{ + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + image = gimp_image_new (gimp, width, height, type, precision); + + if (attach_comment) + { + const gchar *comment; + + comment = gimp_template_get_comment (gimp->config->default_image); + + if (comment) + { + GimpParasite *parasite = gimp_parasite_new ("gimp-comment", + GIMP_PARASITE_PERSISTENT, + strlen (comment) + 1, + comment); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + } + } + + return image; +} + +void +gimp_set_default_context (Gimp *gimp, + GimpContext *context) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context)); + + g_set_object (&gimp->default_context, context); +} + +GimpContext * +gimp_get_default_context (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->default_context; +} + +void +gimp_set_user_context (Gimp *gimp, + GimpContext *context) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context)); + + g_set_object (&gimp->user_context, context); +} + +GimpContext * +gimp_get_user_context (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->user_context; +} + +GimpToolInfo * +gimp_get_tool_info (Gimp *gimp, + const gchar *tool_id) +{ + gpointer info; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (tool_id != NULL, NULL); + + info = gimp_container_get_child_by_name (gimp->tool_info_list, tool_id); + + return (GimpToolInfo *) info; +} + +/** + * gimp_message: + * @gimp: a pointer to the %Gimp object + * @handler: either a %GimpProgress or a %GtkWidget pointer + * @severity: severity of the message + * @format: printf-like format string + * @...: arguments to use with @format + * + * Present a message to the user. How exactly the message is displayed + * depends on the @severity, the @handler object and user preferences. + **/ +void +gimp_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *format, + ...) +{ + va_list args; + + va_start (args, format); + + gimp_message_valist (gimp, handler, severity, format, args); + + va_end (args); +} + +/** + * gimp_message_valist: + * @gimp: a pointer to the %Gimp object + * @handler: either a %GimpProgress or a %GtkWidget pointer + * @severity: severity of the message + * @format: printf-like format string + * @args: arguments to use with @format + * + * See documentation for gimp_message(). + **/ +void +gimp_message_valist (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *format, + va_list args) +{ + gchar *message; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (handler == NULL || G_IS_OBJECT (handler)); + g_return_if_fail (format != NULL); + + message = g_strdup_vprintf (format, args); + + gimp_show_message (gimp, handler, severity, NULL, message); + + g_free (message); +} + +void +gimp_message_literal (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *message) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (handler == NULL || G_IS_OBJECT (handler)); + g_return_if_fail (message != NULL); + + gimp_show_message (gimp, handler, severity, NULL, message); +} + +void +gimp_filter_history_changed (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + g_signal_emit (gimp, gimp_signals[FILTER_HISTORY_CHANGED], 0); +} + +void +gimp_image_opened (Gimp *gimp, + GFile *file) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (G_IS_FILE (file)); + + g_signal_emit (gimp, gimp_signals[IMAGE_OPENED], 0, file); +} + +GFile * +gimp_get_temp_file (Gimp *gimp, + const gchar *extension) +{ + static gint id = 0; + static gint pid; + gchar *basename; + GFile *dir; + GFile *file; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (id == 0) + pid = gimp_get_pid (); + + if (extension) + basename = g_strdup_printf ("gimp-temp-%d%d.%s", pid, id++, extension); + else + basename = g_strdup_printf ("gimp-temp-%d%d", pid, id++); + + dir = gimp_file_new_for_config_path (GIMP_GEGL_CONFIG (gimp->config)->temp_path, + NULL); + if (! g_file_query_exists (dir, NULL)) + { + /* Try to make the temp directory if it doesn't exist. + * Ignore any error. + */ + g_file_make_directory_with_parents (dir, NULL, NULL); + } + file = g_file_get_child (dir, basename); + g_free (basename); + g_object_unref (dir); + + return file; +} diff --git a/app/core/gimp.h b/app/core/gimp.h new file mode 100644 index 0000000..d4b1e39 --- /dev/null +++ b/app/core/gimp.h @@ -0,0 +1,248 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_H__ +#define __GIMP_H__ + + +#include "gimpobject.h" +#include "gimp-gui.h" + + +#define GIMP_TYPE_GIMP (gimp_get_type ()) +#define GIMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GIMP, Gimp)) +#define GIMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GIMP, GimpClass)) +#define GIMP_IS_GIMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GIMP)) +#define GIMP_IS_GIMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GIMP)) + + +typedef struct _GimpClass GimpClass; + +struct _Gimp +{ + GimpObject parent_instance; + + GimpCoreConfig *config; + GimpCoreConfig *edit_config; /* don't use this one, it's just + * for the preferences dialog + */ + gchar *session_name; + GFile *default_folder; + + gboolean be_verbose; + gboolean no_data; + gboolean no_fonts; + gboolean no_interface; + gboolean show_gui; + gboolean use_shm; + gboolean use_cpu_accel; + GimpMessageHandlerType message_handler; + gboolean console_messages; + gboolean show_playground; + gboolean show_debug_menu; + GimpStackTraceMode stack_trace_mode; + GimpPDBCompatMode pdb_compat_mode; + + GimpGui gui; /* gui vtable */ + + gboolean restored; /* becomes TRUE in gimp_restore() */ + gboolean initialized; /* Fully initialized (only set once at start). */ + + gint busy; + guint busy_idle_id; + + GList *user_units; + gint n_user_units; + + GimpParasiteList *parasites; + + GimpContainer *paint_info_list; + GimpPaintInfo *standard_paint_info; + + GimpModuleDB *module_db; + gboolean write_modulerc; + + GimpPlugInManager *plug_in_manager; + + GList *filter_history; + + GimpContainer *images; + guint32 next_guide_ID; + guint32 next_sample_point_ID; + GimpIdTable *image_table; + GimpIdTable *item_table; + + GimpContainer *displays; + gint next_display_ID; + + GList *image_windows; + + GimpImage *clipboard_image; + GimpBuffer *clipboard_buffer; + GimpContainer *named_buffers; + + GimpDataFactory *brush_factory; + GimpDataFactory *dynamics_factory; + GimpDataFactory *mybrush_factory; + GimpDataFactory *pattern_factory; + GimpDataFactory *gradient_factory; + GimpDataFactory *palette_factory; + GimpDataFactory *font_factory; + GimpDataFactory *tool_preset_factory; + + GimpTagCache *tag_cache; + + GimpPDB *pdb; + + GimpContainer *tool_info_list; + GimpToolInfo *standard_tool_info; + + GimpContainer *tool_item_list; + GimpContainer *tool_item_ui_list; + + /* the opened and saved images in MRU order */ + GimpContainer *documents; + + /* image_new values */ + GimpContainer *templates; + GimpTemplate *image_new_last_template; + + /* the list of all contexts */ + GList *context_list; + + /* the default context which is initialized from gimprc */ + GimpContext *default_context; + + /* the context used by the interface */ + GimpContext *user_context; +}; + +struct _GimpClass +{ + GimpObjectClass parent_class; + + void (* initialize) (Gimp *gimp, + GimpInitStatusFunc status_callback); + void (* restore) (Gimp *gimp, + GimpInitStatusFunc status_callback); + gboolean (* exit) (Gimp *gimp, + gboolean force); + + void (* clipboard_changed) (Gimp *gimp); + + void (* filter_history_changed) (Gimp *gimp); + + /* emitted if an image is loaded and opened with a display */ + void (* image_opened) (Gimp *gimp, + GFile *file); +}; + + +GType gimp_get_type (void) G_GNUC_CONST; + +Gimp * gimp_new (const gchar *name, + const gchar *session_name, + GFile *default_folder, + gboolean be_verbose, + gboolean no_data, + gboolean no_fonts, + gboolean no_interface, + gboolean use_shm, + gboolean use_cpu_accel, + gboolean console_messages, + gboolean show_playground, + gboolean show_debug_menu, + GimpStackTraceMode stack_trace_mode, + GimpPDBCompatMode pdb_compat_mode); +void gimp_set_show_gui (Gimp *gimp, + gboolean show_gui); +gboolean gimp_get_show_gui (Gimp *gimp); + +void gimp_load_config (Gimp *gimp, + GFile *alternate_system_gimprc, + GFile *alternate_gimprc); +void gimp_initialize (Gimp *gimp, + GimpInitStatusFunc status_callback); +void gimp_restore (Gimp *gimp, + GimpInitStatusFunc status_callback, + GError **error); +gboolean gimp_is_restored (Gimp *gimp); + +void gimp_exit (Gimp *gimp, + gboolean force); + +GList * gimp_get_image_iter (Gimp *gimp); +GList * gimp_get_display_iter (Gimp *gimp); +GList * gimp_get_image_windows (Gimp *gimp); +GList * gimp_get_paint_info_iter (Gimp *gimp); +GList * gimp_get_tool_info_iter (Gimp *gimp); +GList * gimp_get_tool_item_iter (Gimp *gimp); +GList * gimp_get_tool_item_ui_iter (Gimp *gimp); + +GimpObject * gimp_get_clipboard_object (Gimp *gimp); + +void gimp_set_clipboard_image (Gimp *gimp, + GimpImage *image); +GimpImage * gimp_get_clipboard_image (Gimp *gimp); + +void gimp_set_clipboard_buffer (Gimp *gimp, + GimpBuffer *buffer); +GimpBuffer * gimp_get_clipboard_buffer (Gimp *gimp); + +GimpImage * gimp_create_image (Gimp *gimp, + gint width, + gint height, + GimpImageBaseType type, + GimpPrecision precision, + gboolean attach_comment); + +void gimp_set_default_context (Gimp *gimp, + GimpContext *context); +GimpContext * gimp_get_default_context (Gimp *gimp); + +void gimp_set_user_context (Gimp *gimp, + GimpContext *context); +GimpContext * gimp_get_user_context (Gimp *gimp); + +GimpToolInfo * gimp_get_tool_info (Gimp *gimp, + const gchar *tool_name); + +void gimp_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *format, + ...) G_GNUC_PRINTF (4, 5); +void gimp_message_valist (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *format, + va_list args) G_GNUC_PRINTF (4, 0); +void gimp_message_literal (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *message); + +void gimp_filter_history_changed (Gimp *gimp); + +void gimp_image_opened (Gimp *gimp, + GFile *file); + +GFile * gimp_get_temp_file (Gimp *gimp, + const gchar *extension); + + +#endif /* __GIMP_H__ */ diff --git a/app/core/gimpasync.c b/app/core/gimpasync.c new file mode 100644 index 0000000..322d57d --- /dev/null +++ b/app/core/gimpasync.c @@ -0,0 +1,752 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpasync.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpasync.h" +#include "gimpcancelable.h" +#include "gimpmarshal.h" +#include "gimpwaitable.h" + + +/* GimpAsync represents an asynchronous task. Both the public and the + * protected interfaces are intentionally minimal at this point, to keep things + * simple. They may be extended in the future as needed. + * + * GimpAsync implements the GimpWaitable and GimpCancelable interfaces. + * + * Upon creation, a GimpAsync object is in the "running" state. Once the task + * is complete (and before the object's destruction), it should be transitioned + * to the "stopped" state, using either 'gimp_async_finish()' or + * 'gimp_async_abort()'. + * + * Similarly, upon creation, a GimpAsync object is said to be "unsynced". It + * becomes synced once the execution of any of the completion callbacks added + * through 'gimp_async_add_callback()' had started, or after a successful call + * to one of the 'gimp_waitable_wait()' family of functions. + * + * Note that certain GimpAsync functions may only be called during a certain + * state, on a certain thread, or depending on whether or not the object is + * synced, as detailed for each function. When referring to threads, the "main + * thread" is the thread running the main loop, or any thread whose execution + * is synchronized with the main thread, and the "async thread" is the thread + * calling 'gimp_async_finish()' or 'gimp_async_abort()' (which may also be the + * main thread), or any thread whose execution is synchronized with the async + * thread. + */ + + +/* #define TIME_ASYNC_OPS */ + + +enum +{ + WAITING, + LAST_SIGNAL +}; + + +typedef struct _GimpAsyncCallbackInfo GimpAsyncCallbackInfo; + + +struct _GimpAsyncCallbackInfo +{ + GimpAsync *async; + GimpAsyncCallback callback; + gpointer data; + gpointer gobject; +}; + +struct _GimpAsyncPrivate +{ + GMutex mutex; + GCond cond; + + GQueue callbacks; + + gpointer result; + GDestroyNotify result_destroy_func; + + guint idle_id; + + gboolean stopped; + gboolean finished; + gboolean synced; + gboolean canceled; + +#ifdef TIME_ASYNC_OPS + guint64 start_time; +#endif +}; + + +/* local function prototypes */ + +static void gimp_async_waitable_iface_init (GimpWaitableInterface *iface); + +static void gimp_async_cancelable_iface_init (GimpCancelableInterface *iface); + +static void gimp_async_finalize (GObject *object); + +static void gimp_async_wait (GimpWaitable *waitable); +static gboolean gimp_async_try_wait (GimpWaitable *waitable); +static gboolean gimp_async_wait_until (GimpWaitable *waitable, + gint64 end_time); + +static void gimp_async_cancel (GimpCancelable *cancelable); + +static gboolean gimp_async_idle (GimpAsync *async); + +static void gimp_async_callback_weak_notify (GimpAsyncCallbackInfo *callback_info, + GObject *gobject); + +static void gimp_async_stop (GimpAsync *async); +static void gimp_async_run_callbacks (GimpAsync *async); + + +G_DEFINE_TYPE_WITH_CODE (GimpAsync, gimp_async, G_TYPE_OBJECT, + G_ADD_PRIVATE (GimpAsync) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_WAITABLE, + gimp_async_waitable_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CANCELABLE, + gimp_async_cancelable_iface_init)) + +#define parent_class gimp_async_parent_class + +static guint async_signals[LAST_SIGNAL] = { 0 }; + + +/* local variables */ + +static volatile gint gimp_async_n_running = 0; + + +/* private functions */ + + +static void +gimp_async_class_init (GimpAsyncClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + async_signals[WAITING] = + g_signal_new ("waiting", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpAsyncClass, waiting), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_async_finalize; +} + +static void +gimp_async_waitable_iface_init (GimpWaitableInterface *iface) +{ + iface->wait = gimp_async_wait; + iface->try_wait = gimp_async_try_wait; + iface->wait_until = gimp_async_wait_until; +} + +static void +gimp_async_cancelable_iface_init (GimpCancelableInterface *iface) +{ + iface->cancel = gimp_async_cancel; +} + +static void +gimp_async_init (GimpAsync *async) +{ + async->priv = gimp_async_get_instance_private (async); + + g_mutex_init (&async->priv->mutex); + g_cond_init (&async->priv->cond); + + g_queue_init (&async->priv->callbacks); + + g_atomic_int_inc (&gimp_async_n_running); + +#ifdef TIME_ASYNC_OPS + async->priv->start_time = g_get_monotonic_time (); +#endif +} + +static void +gimp_async_finalize (GObject *object) +{ + GimpAsync *async = GIMP_ASYNC (object); + + g_warn_if_fail (async->priv->stopped); + g_warn_if_fail (async->priv->idle_id == 0); + g_warn_if_fail (g_queue_is_empty (&async->priv->callbacks)); + + if (async->priv->finished && + async->priv->result && + async->priv->result_destroy_func) + { + async->priv->result_destroy_func (async->priv->result); + + async->priv->result = NULL; + } + + g_cond_clear (&async->priv->cond); + g_mutex_clear (&async->priv->mutex); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* waits for 'waitable' to transition to the "stopped" state. if 'waitable' is + * already stopped, returns immediately. + * + * after the call, all callbacks previously added through + * 'gimp_async_add_callback()' are guaranteed to have been called. + * + * may only be called on the main thread. + */ +static void +gimp_async_wait (GimpWaitable *waitable) +{ + GimpAsync *async = GIMP_ASYNC (waitable); + + g_mutex_lock (&async->priv->mutex); + + if (! async->priv->stopped) + { + g_signal_emit (async, async_signals[WAITING], 0); + + while (! async->priv->stopped) + g_cond_wait (&async->priv->cond, &async->priv->mutex); + } + + g_mutex_unlock (&async->priv->mutex); + + gimp_async_run_callbacks (async); +} + +/* same as 'gimp_async_wait()', but returns immediately if 'waitable' is not in + * the "stopped" state. + * + * returns TRUE if 'waitable' has transitioned to the "stopped" state, or FALSE + * otherwise. + */ +static gboolean +gimp_async_try_wait (GimpWaitable *waitable) +{ + GimpAsync *async = GIMP_ASYNC (waitable); + + g_mutex_lock (&async->priv->mutex); + + if (! async->priv->stopped) + { + g_mutex_unlock (&async->priv->mutex); + + return FALSE; + } + + g_mutex_unlock (&async->priv->mutex); + + gimp_async_run_callbacks (async); + + return TRUE; +} + +/* same as 'gimp_async_wait()', taking an additional 'end_time' parameter, + * specifying the maximal monotonic time until which to wait for 'waitable' to + * stop. + * + * returns TRUE if 'waitable' has transitioned to the "stopped" state, or FALSE + * if the wait was interrupted before the transition. + */ +static gboolean +gimp_async_wait_until (GimpWaitable *waitable, + gint64 end_time) +{ + GimpAsync *async = GIMP_ASYNC (waitable); + + g_mutex_lock (&async->priv->mutex); + + if (! async->priv->stopped) + { + g_signal_emit (async, async_signals[WAITING], 0); + + while (! async->priv->stopped) + { + if (! g_cond_wait_until (&async->priv->cond, &async->priv->mutex, + end_time)) + { + g_mutex_unlock (&async->priv->mutex); + + return FALSE; + } + } + } + + g_mutex_unlock (&async->priv->mutex); + + gimp_async_run_callbacks (async); + + return TRUE; +} + +/* requests the cancellation of the task managed by 'cancelable'. + * + * note that 'gimp_async_cancel()' doesn't directly cause 'cancelable' to be + * stopped, nor synced. furthermore, 'cancelable' may still complete + * successfully even when cancellation has been requested. + * + * may only be called on the main thread. + */ +static void +gimp_async_cancel (GimpCancelable *cancelable) +{ + GimpAsync *async = GIMP_ASYNC (cancelable); + + async->priv->canceled = TRUE; +} + +static gboolean +gimp_async_idle (GimpAsync *async) +{ + gimp_waitable_wait (GIMP_WAITABLE (async)); + + return G_SOURCE_REMOVE; +} + +static void +gimp_async_callback_weak_notify (GimpAsyncCallbackInfo *callback_info, + GObject *gobject) +{ + GimpAsync *async = callback_info->async; + gboolean unref_async = FALSE; + + g_mutex_lock (&async->priv->mutex); + + g_queue_remove (&async->priv->callbacks, callback_info); + + g_slice_free (GimpAsyncCallbackInfo, callback_info); + + if (g_queue_is_empty (&async->priv->callbacks) && async->priv->idle_id) + { + g_source_remove (async->priv->idle_id); + async->priv->idle_id = 0; + + unref_async = TRUE; + } + + g_mutex_unlock (&async->priv->mutex); + + if (unref_async) + g_object_unref (async); +} + +static void +gimp_async_stop (GimpAsync *async) +{ +#ifdef TIME_ASYNC_OPS + { + guint64 time = g_get_monotonic_time (); + + g_printerr ("Asynchronous operation took %g seconds%s\n", + (time - async->priv->start_time) / 1000000.0, + async->priv->finished ? "" : " (aborted)"); + } +#endif + + g_atomic_int_dec_and_test (&gimp_async_n_running); + + if (! g_queue_is_empty (&async->priv->callbacks)) + { + g_object_ref (async); + + async->priv->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT, + (GSourceFunc) gimp_async_idle, + async, NULL); + } + + async->priv->stopped = TRUE; + + g_cond_broadcast (&async->priv->cond); +} + +static void +gimp_async_run_callbacks (GimpAsync *async) +{ + GimpAsyncCallbackInfo *callback_info; + gboolean unref_async = FALSE; + + if (async->priv->idle_id) + { + g_source_remove (async->priv->idle_id); + async->priv->idle_id = 0; + + unref_async = TRUE; + } + + async->priv->synced = TRUE; + + while ((callback_info = g_queue_pop_head (&async->priv->callbacks))) + { + if (callback_info->gobject) + { + g_object_ref (callback_info->gobject); + + g_object_weak_unref (callback_info->gobject, + (GWeakNotify) gimp_async_callback_weak_notify, + callback_info); + } + + callback_info->callback (async, callback_info->data); + + if (callback_info->gobject) + g_object_unref (callback_info->gobject); + + g_slice_free (GimpAsyncCallbackInfo, callback_info); + } + + if (unref_async) + g_object_unref (async); +} + + +/* public functions */ + + +/* creates a new GimpAsync object, initially unsynced and placed in the + * "running" state. + */ +GimpAsync * +gimp_async_new (void) +{ + return g_object_new (GIMP_TYPE_ASYNC, + NULL); +} + +/* checks if 'async' is synced. + * + * may only be called on the main thread. + */ +gboolean +gimp_async_is_synced (GimpAsync *async) +{ + g_return_val_if_fail (GIMP_IS_ASYNC (async), FALSE); + + return async->priv->synced; +} + +/* registers a callback to be called when 'async' transitions to the "stopped" + * state. if 'async' is already stopped, the callback may be called directly. + * + * callbacks are called in the order in which they were added. 'async' is + * guaranteed to be kept alive, even without an external reference, between the + * point where it was stopped, and until all callbacks added while 'async' was + * externally referenced have been called. + * + * the callback is guaranteed to be called on the main thread. + * + * may only be called on the main thread. + */ +void +gimp_async_add_callback (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data) +{ + GimpAsyncCallbackInfo *callback_info; + + g_return_if_fail (GIMP_IS_ASYNC (async)); + g_return_if_fail (callback != NULL); + + g_mutex_lock (&async->priv->mutex); + + if (async->priv->stopped && g_queue_is_empty (&async->priv->callbacks)) + { + async->priv->synced = TRUE; + + g_mutex_unlock (&async->priv->mutex); + + callback (async, data); + + return; + } + + callback_info = g_slice_new0 (GimpAsyncCallbackInfo); + callback_info->async = async; + callback_info->callback = callback; + callback_info->data = data; + + g_queue_push_tail (&async->priv->callbacks, callback_info); + + g_mutex_unlock (&async->priv->mutex); +} + +/* same as 'gimp_async_add_callback()', however, takes an additional 'gobject' + * argument, which should be a GObject. + * + * 'gobject' is guaranteed to remain alive for the duration of the callback. + * + * When 'gobject' is destroyed, the callback is automatically removed. + */ +void +gimp_async_add_callback_for_object (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data, + gpointer gobject) +{ + GimpAsyncCallbackInfo *callback_info; + + g_return_if_fail (GIMP_IS_ASYNC (async)); + g_return_if_fail (callback != NULL); + g_return_if_fail (G_IS_OBJECT (gobject)); + + g_mutex_lock (&async->priv->mutex); + + if (async->priv->stopped && g_queue_is_empty (&async->priv->callbacks)) + { + async->priv->synced = TRUE; + + g_mutex_unlock (&async->priv->mutex); + + g_object_ref (gobject); + + callback (async, data); + + g_object_unref (gobject); + + return; + } + + callback_info = g_slice_new0 (GimpAsyncCallbackInfo); + callback_info->async = async; + callback_info->callback = callback; + callback_info->data = data; + callback_info->gobject = gobject; + + g_queue_push_tail (&async->priv->callbacks, callback_info); + + g_object_weak_ref (gobject, + (GWeakNotify) gimp_async_callback_weak_notify, + callback_info); + + g_mutex_unlock (&async->priv->mutex); +} + +/* removes all callbacks previously registered through + * 'gimp_async_add_callback()', matching 'callback' and 'data', which hasn't + * been called yet. + * + * may only be called on the main thread. + */ +void +gimp_async_remove_callback (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data) +{ + GList *iter; + gboolean unref_async = FALSE; + + g_return_if_fail (GIMP_IS_ASYNC (async)); + g_return_if_fail (callback != NULL); + + g_mutex_lock (&async->priv->mutex); + + iter = g_queue_peek_head_link (&async->priv->callbacks); + + while (iter) + { + GimpAsyncCallbackInfo *callback_info = iter->data; + GList *next = g_list_next (iter); + + if (callback_info->callback == callback && + callback_info->data == data) + { + if (callback_info->gobject) + { + g_object_weak_unref ( + callback_info->gobject, + (GWeakNotify) gimp_async_callback_weak_notify, + callback_info); + } + + g_queue_delete_link (&async->priv->callbacks, iter); + + g_slice_free (GimpAsyncCallbackInfo, callback_info); + } + + iter = next; + } + + if (g_queue_is_empty (&async->priv->callbacks) && async->priv->idle_id) + { + g_source_remove (async->priv->idle_id); + async->priv->idle_id = 0; + + unref_async = TRUE; + } + + g_mutex_unlock (&async->priv->mutex); + + if (unref_async) + g_object_unref (async); +} + +/* checks if 'async' is in the "stopped" state. + * + * may only be called on the async thread. + */ +gboolean +gimp_async_is_stopped (GimpAsync *async) +{ + g_return_val_if_fail (GIMP_IS_ASYNC (async), FALSE); + + return async->priv->stopped; +} + +/* transitions 'async' to the "stopped" state, indicating that the task + * completed normally, possibly providing a result. + * + * 'async' shall be in the "running" state. + * + * may only be called on the async thread. + */ +void +gimp_async_finish (GimpAsync *async, + gpointer result) +{ + gimp_async_finish_full (async, result, NULL); +} + +/* same as 'gimp_async_finish()', taking an additional GDestroyNotify function, + * used for freeing the result when 'async' is destroyed. + */ +void +gimp_async_finish_full (GimpAsync *async, + gpointer result, + GDestroyNotify result_destroy_func) +{ + g_return_if_fail (GIMP_IS_ASYNC (async)); + g_return_if_fail (! async->priv->stopped); + + g_mutex_lock (&async->priv->mutex); + + async->priv->finished = TRUE; + async->priv->result = result; + async->priv->result_destroy_func = result_destroy_func; + + gimp_async_stop (async); + + g_mutex_unlock (&async->priv->mutex); +} + +/* checks if 'async' completed normally, using 'gimp_async_finish()' (in + * contrast to 'gimp_async_abort()'). + * + * 'async' shall be in the "stopped" state. + * + * may only be called on the async thread, or on the main thread when 'async' + * is synced. + */ +gboolean +gimp_async_is_finished (GimpAsync *async) +{ + g_return_val_if_fail (GIMP_IS_ASYNC (async), FALSE); + g_return_val_if_fail (async->priv->stopped, FALSE); + + return async->priv->finished; +} + +/* returns the result of 'async', as passed to 'gimp_async_finish()'. + * + * 'async' shall be in the "stopped" state, and should have completed normally. + * + * may only be called on the async thread, or on the main thread when 'async' + * is synced. + */ +gpointer +gimp_async_get_result (GimpAsync *async) +{ + g_return_val_if_fail (GIMP_IS_ASYNC (async), NULL); + g_return_val_if_fail (async->priv->stopped, NULL); + g_return_val_if_fail (async->priv->finished, NULL); + + return async->priv->result; +} + +/* transitions 'async' to the "stopped" state, indicating that the task + * was stopped before completion (normally, in response to a + * 'gimp_cancelable_cancel()' call). + * + * 'async' shall be in the "running" state. + * + * may only be called on the async thread. + */ +void +gimp_async_abort (GimpAsync *async) +{ + g_return_if_fail (GIMP_IS_ASYNC (async)); + g_return_if_fail (! async->priv->stopped); + + g_mutex_lock (&async->priv->mutex); + + gimp_async_stop (async); + + g_mutex_unlock (&async->priv->mutex); +} + +/* checks if cancellation of 'async' has been requested. + * + * note that a return value of TRUE only indicates that + * 'gimp_cancelable_cancel()' has been called for 'async', and not that 'async' + * is stopped, or, if it is stopped, that it was aborted. + */ +gboolean +gimp_async_is_canceled (GimpAsync *async) +{ + g_return_val_if_fail (GIMP_IS_ASYNC (async), FALSE); + + return async->priv->canceled; +} + +/* a convenience function, canceling 'async' and waiting for it to stop. + * + * may only be called on the main thread. + */ +void +gimp_async_cancel_and_wait (GimpAsync *async) +{ + g_return_if_fail (GIMP_IS_ASYNC (async)); + + gimp_cancelable_cancel (GIMP_CANCELABLE (async)); + gimp_waitable_wait (GIMP_WAITABLE (async)); +} + + +/* public functions (stats) */ + + +gint +gimp_async_get_n_running (void) +{ + return gimp_async_n_running; +} diff --git a/app/core/gimpasync.h b/app/core/gimpasync.h new file mode 100644 index 0000000..0c2a671 --- /dev/null +++ b/app/core/gimpasync.h @@ -0,0 +1,95 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpasync.h + * 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 . + */ + +#ifndef __GIMP_ASYNC_H__ +#define __GIMP_ASYNC_H__ + + +#define GIMP_TYPE_ASYNC (gimp_async_get_type ()) +#define GIMP_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ASYNC, GimpAsync)) +#define GIMP_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ASYNC, GimpAsyncClass)) +#define GIMP_IS_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ASYNC)) +#define GIMP_IS_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ASYNC)) +#define GIMP_ASYNC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ASYNC, GimpAsyncClass)) + + +typedef void (* GimpAsyncCallback) (GimpAsync *async, + gpointer data); + + +typedef struct _GimpAsyncPrivate GimpAsyncPrivate; +typedef struct _GimpAsyncClass GimpAsyncClass; + +struct _GimpAsync +{ + GObject parent_instance; + + GimpAsyncPrivate *priv; +}; + +struct _GimpAsyncClass +{ + GObjectClass parent_class; + + /* signals */ + void (* waiting) (GimpAsync *async); +}; + + +GType gimp_async_get_type (void) G_GNUC_CONST; + +GimpAsync * gimp_async_new (void); + +gboolean gimp_async_is_synced (GimpAsync *async); + +void gimp_async_add_callback (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data); +void gimp_async_add_callback_for_object (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data, + gpointer gobject); +void gimp_async_remove_callback (GimpAsync *async, + GimpAsyncCallback callback, + gpointer data); + +gboolean gimp_async_is_stopped (GimpAsync *async); + +void gimp_async_finish (GimpAsync *async, + gpointer result); +void gimp_async_finish_full (GimpAsync *async, + gpointer result, + GDestroyNotify result_destroy_func); +gboolean gimp_async_is_finished (GimpAsync *async); +gpointer gimp_async_get_result (GimpAsync *async); + +void gimp_async_abort (GimpAsync *async); + +gboolean gimp_async_is_canceled (GimpAsync *async); + +void gimp_async_cancel_and_wait (GimpAsync *async); + + +/* stats */ + +gint gimp_async_get_n_running (void); + + +#endif /* __GIMP_ASYNC_H__ */ diff --git a/app/core/gimpasyncset.c b/app/core/gimpasyncset.c new file mode 100644 index 0000000..f1b611e --- /dev/null +++ b/app/core/gimpasyncset.c @@ -0,0 +1,348 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpasyncset.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpasync.h" +#include "gimpasyncset.h" +#include "gimpcancelable.h" +#include "gimpwaitable.h" + + +enum +{ + PROP_0, + PROP_EMPTY +}; + + +struct _GimpAsyncSetPrivate +{ + GHashTable *asyncs; +}; + + +/* local function prototypes */ + +static void gimp_async_set_waitable_iface_init (GimpWaitableInterface *iface); + +static void gimp_async_set_cancelable_iface_init (GimpCancelableInterface *iface); + +static void gimp_async_set_dispose (GObject *object); +static void gimp_async_set_finalize (GObject *object); +static void gimp_async_set_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_async_set_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_async_set_wait (GimpWaitable *waitable); +static gboolean gimp_async_set_try_wait (GimpWaitable *waitable); +static gboolean gimp_async_set_wait_until (GimpWaitable *waitable, + gint64 end_time); + +static void gimp_async_set_cancel (GimpCancelable *cancelable); + +static void gimp_async_set_async_callback (GimpAsync *async, + GimpAsyncSet *async_set); + + +G_DEFINE_TYPE_WITH_CODE (GimpAsyncSet, gimp_async_set, G_TYPE_OBJECT, + G_ADD_PRIVATE (GimpAsyncSet) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_WAITABLE, + gimp_async_set_waitable_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CANCELABLE, + gimp_async_set_cancelable_iface_init)) + +#define parent_class gimp_async_set_parent_class + + +/* private functions */ + + +static void +gimp_async_set_class_init (GimpAsyncSetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_async_set_dispose; + object_class->finalize = gimp_async_set_finalize; + object_class->set_property = gimp_async_set_set_property; + object_class->get_property = gimp_async_set_get_property; + + g_object_class_install_property (object_class, PROP_EMPTY, + g_param_spec_boolean ("empty", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_async_set_waitable_iface_init (GimpWaitableInterface *iface) +{ + iface->wait = gimp_async_set_wait; + iface->try_wait = gimp_async_set_try_wait; + iface->wait_until = gimp_async_set_wait_until; +} + +static void +gimp_async_set_cancelable_iface_init (GimpCancelableInterface *iface) +{ + iface->cancel = gimp_async_set_cancel; +} + +static void +gimp_async_set_init (GimpAsyncSet *async_set) +{ + async_set->priv = gimp_async_set_get_instance_private (async_set); + + async_set->priv->asyncs = g_hash_table_new (NULL, NULL); +} + +static void +gimp_async_set_dispose (GObject *object) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (object); + + gimp_async_set_clear (async_set); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_async_set_finalize (GObject *object) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (object); + + g_hash_table_unref (async_set->priv->asyncs); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_async_set_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_async_set_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (object); + + switch (property_id) + { + case PROP_EMPTY: + g_value_set_boolean (value, gimp_async_set_is_empty (async_set)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_async_set_wait (GimpWaitable *waitable) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable); + + while (! gimp_async_set_is_empty (async_set)) + { + GimpAsync *async; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, async_set->priv->asyncs); + + g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); + + gimp_waitable_wait (GIMP_WAITABLE (async)); + } +} + +static gboolean +gimp_async_set_try_wait (GimpWaitable *waitable) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable); + + while (! gimp_async_set_is_empty (async_set)) + { + GimpAsync *async; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, async_set->priv->asyncs); + + g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); + + if (! gimp_waitable_try_wait (GIMP_WAITABLE (async))) + return FALSE; + } + + return TRUE; +} + +static gboolean +gimp_async_set_wait_until (GimpWaitable *waitable, + gint64 end_time) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable); + + while (! gimp_async_set_is_empty (async_set)) + { + GimpAsync *async; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, async_set->priv->asyncs); + + g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); + + if (! gimp_waitable_wait_until (GIMP_WAITABLE (async), end_time)) + return FALSE; + } + + return TRUE; +} + +static void +gimp_async_set_cancel (GimpCancelable *cancelable) +{ + GimpAsyncSet *async_set = GIMP_ASYNC_SET (cancelable); + GList *list; + + list = g_hash_table_get_keys (async_set->priv->asyncs); + + g_list_foreach (list, (GFunc) g_object_ref, NULL); + + g_list_foreach (list, (GFunc) gimp_cancelable_cancel, NULL); + + g_list_free_full (list, g_object_unref); +} + +static void +gimp_async_set_async_callback (GimpAsync *async, + GimpAsyncSet *async_set) +{ + g_hash_table_remove (async_set->priv->asyncs, async); + + if (gimp_async_set_is_empty (async_set)) + g_object_notify (G_OBJECT (async_set), "empty"); +} + + +/* public functions */ + + +GimpAsyncSet * +gimp_async_set_new (void) +{ + return g_object_new (GIMP_TYPE_ASYNC_SET, + NULL); +} + +void +gimp_async_set_add (GimpAsyncSet *async_set, + GimpAsync *async) +{ + g_return_if_fail (GIMP_IS_ASYNC_SET (async_set)); + g_return_if_fail (GIMP_IS_ASYNC (async)); + + if (g_hash_table_add (async_set->priv->asyncs, async)) + { + if (g_hash_table_size (async_set->priv->asyncs) == 1) + g_object_notify (G_OBJECT (async_set), "empty"); + + gimp_async_add_callback ( + async, + (GimpAsyncCallback) gimp_async_set_async_callback, + async_set); + } +} + +void +gimp_async_set_remove (GimpAsyncSet *async_set, + GimpAsync *async) +{ + g_return_if_fail (GIMP_IS_ASYNC_SET (async_set)); + g_return_if_fail (GIMP_IS_ASYNC (async)); + + if (g_hash_table_remove (async_set->priv->asyncs, async)) + { + gimp_async_remove_callback ( + async, + (GimpAsyncCallback) gimp_async_set_async_callback, + async_set); + + if (g_hash_table_size (async_set->priv->asyncs) == 0) + g_object_notify (G_OBJECT (async_set), "empty"); + } +} + +void +gimp_async_set_clear (GimpAsyncSet *async_set) +{ + GimpAsync *async; + GHashTableIter iter; + + g_return_if_fail (GIMP_IS_ASYNC_SET (async_set)); + + if (gimp_async_set_is_empty (async_set)) + return; + + g_hash_table_iter_init (&iter, async_set->priv->asyncs); + + while (g_hash_table_iter_next (&iter, (gpointer *) &async, NULL)) + { + gimp_async_remove_callback ( + async, + (GimpAsyncCallback) gimp_async_set_async_callback, + async_set); + } + + g_hash_table_remove_all (async_set->priv->asyncs); + + g_object_notify (G_OBJECT (async_set), "empty"); +} + +gboolean +gimp_async_set_is_empty (GimpAsyncSet *async_set) +{ + g_return_val_if_fail (GIMP_IS_ASYNC_SET (async_set), FALSE); + + return g_hash_table_size (async_set->priv->asyncs) == 0; +} diff --git a/app/core/gimpasyncset.h b/app/core/gimpasyncset.h new file mode 100644 index 0000000..62cc524 --- /dev/null +++ b/app/core/gimpasyncset.h @@ -0,0 +1,61 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpasyncset.h + * 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 . + */ + +#ifndef __GIMP_ASYNC_SET_H__ +#define __GIMP_ASYNC_SET_H__ + + +#define GIMP_TYPE_ASYNC_SET (gimp_async_set_get_type ()) +#define GIMP_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ASYNC_SET, GimpAsyncSet)) +#define GIMP_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ASYNC_SET, GimpAsyncSetClass)) +#define GIMP_IS_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ASYNC_SET)) +#define GIMP_IS_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ASYNC_SET)) +#define GIMP_ASYNC_SET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ASYNC_SET, GimpAsyncSetClass)) + + +typedef struct _GimpAsyncSetPrivate GimpAsyncSetPrivate; +typedef struct _GimpAsyncSetClass GimpAsyncSetClass; + +struct _GimpAsyncSet +{ + GObject parent_instance; + + GimpAsyncSetPrivate *priv; +}; + +struct _GimpAsyncSetClass +{ + GObjectClass parent_class; +}; + + +GType gimp_async_set_get_type (void) G_GNUC_CONST; + +GimpAsyncSet * gimp_async_set_new (void); + +void gimp_async_set_add (GimpAsyncSet *async_set, + GimpAsync *async); +void gimp_async_set_remove (GimpAsyncSet *async_set, + GimpAsync *async); +void gimp_async_set_clear (GimpAsyncSet *async_set); +gboolean gimp_async_set_is_empty (GimpAsyncSet *async_set); + + +#endif /* __GIMP_ASYNC_SET_H__ */ diff --git a/app/core/gimpauxitem.c b/app/core/gimpauxitem.c new file mode 100644 index 0000000..cc96149 --- /dev/null +++ b/app/core/gimpauxitem.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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpauxitem.h" + + +enum +{ + REMOVED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_ID +}; + + +struct _GimpAuxItemPrivate +{ + guint32 aux_item_ID; +}; + + +static void gimp_aux_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_aux_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpAuxItem, gimp_aux_item, G_TYPE_OBJECT) + +static guint gimp_aux_item_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_aux_item_class_init (GimpAuxItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gimp_aux_item_signals[REMOVED] = + g_signal_new ("removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpAuxItemClass, removed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->get_property = gimp_aux_item_get_property; + object_class->set_property = gimp_aux_item_set_property; + + klass->removed = NULL; + + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_uint ("id", NULL, NULL, + 0, G_MAXUINT32, 0, + G_PARAM_CONSTRUCT_ONLY | + GIMP_PARAM_READWRITE)); +} + +static void +gimp_aux_item_init (GimpAuxItem *aux_item) +{ + aux_item->priv = gimp_aux_item_get_instance_private (aux_item); +} + +static void +gimp_aux_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpAuxItem *aux_item = GIMP_AUX_ITEM (object); + + switch (property_id) + { + case PROP_ID: + g_value_set_uint (value, aux_item->priv->aux_item_ID); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_aux_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpAuxItem *aux_item = GIMP_AUX_ITEM (object); + + switch (property_id) + { + case PROP_ID: + aux_item->priv->aux_item_ID = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +guint32 +gimp_aux_item_get_ID (GimpAuxItem *aux_item) +{ + g_return_val_if_fail (GIMP_IS_AUX_ITEM (aux_item), 0); + + return aux_item->priv->aux_item_ID; +} + +void +gimp_aux_item_removed (GimpAuxItem *aux_item) +{ + g_return_if_fail (GIMP_IS_AUX_ITEM (aux_item)); + + g_signal_emit (aux_item, gimp_aux_item_signals[REMOVED], 0); +} diff --git a/app/core/gimpauxitem.h b/app/core/gimpauxitem.h new file mode 100644 index 0000000..6907b39 --- /dev/null +++ b/app/core/gimpauxitem.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 . + */ + +#ifndef __GIMP_AUX_ITEM_H__ +#define __GIMP_AUX_ITEM_H__ + + +#define GIMP_TYPE_AUX_ITEM (gimp_aux_item_get_type ()) +#define GIMP_AUX_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_AUX_ITEM, GimpAuxItem)) +#define GIMP_AUX_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_AUX_ITEM, GimpAuxItemClass)) +#define GIMP_IS_AUX_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_AUX_ITEM)) +#define GIMP_IS_AUX_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_AUX_ITEM)) +#define GIMP_AUX_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_AUX_ITEM, GimpAuxItemClass)) + + +typedef struct _GimpAuxItemPrivate GimpAuxItemPrivate; +typedef struct _GimpAuxItemClass GimpAuxItemClass; + +struct _GimpAuxItem +{ + GObject parent_instance; + + GimpAuxItemPrivate *priv; +}; + +struct _GimpAuxItemClass +{ + GObjectClass parent_class; + + /* signals */ + void (* removed) (GimpAuxItem *aux_item); +}; + + +GType gimp_aux_item_get_type (void) G_GNUC_CONST; + +guint32 gimp_aux_item_get_ID (GimpAuxItem *aux_item); + +void gimp_aux_item_removed (GimpAuxItem *aux_item); + + +#endif /* __GIMP_AUX_ITEM_H__ */ diff --git a/app/core/gimpauxitemundo.c b/app/core/gimpauxitemundo.c new file mode 100644 index 0000000..e0253ab --- /dev/null +++ b/app/core/gimpauxitemundo.c @@ -0,0 +1,138 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpauxitem.h" +#include "gimpauxitemundo.h" + + +enum +{ + PROP_0, + PROP_AUX_ITEM +}; + + +static void gimp_aux_item_undo_constructed (GObject *object); +static void gimp_aux_item_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_aux_item_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_aux_item_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_ABSTRACT_TYPE (GimpAuxItemUndo, gimp_aux_item_undo, GIMP_TYPE_UNDO) + +#define parent_class gimp_aux_item_undo_parent_class + + +static void +gimp_aux_item_undo_class_init (GimpAuxItemUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_aux_item_undo_constructed; + object_class->set_property = gimp_aux_item_undo_set_property; + object_class->get_property = gimp_aux_item_undo_get_property; + + undo_class->free = gimp_aux_item_undo_free; + + g_object_class_install_property (object_class, PROP_AUX_ITEM, + g_param_spec_object ("aux-item", NULL, NULL, + GIMP_TYPE_AUX_ITEM, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_aux_item_undo_init (GimpAuxItemUndo *undo) +{ +} + +static void +gimp_aux_item_undo_constructed (GObject *object) +{ + GimpAuxItemUndo *aux_item_undo = GIMP_AUX_ITEM_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_AUX_ITEM (aux_item_undo->aux_item)); +} + +static void +gimp_aux_item_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpAuxItemUndo *aux_item_undo = GIMP_AUX_ITEM_UNDO (object); + + switch (property_id) + { + case PROP_AUX_ITEM: + aux_item_undo->aux_item = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_aux_item_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpAuxItemUndo *aux_item_undo = GIMP_AUX_ITEM_UNDO (object); + + switch (property_id) + { + case PROP_AUX_ITEM: + g_value_set_object (value, aux_item_undo->aux_item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_aux_item_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpAuxItemUndo *aux_item_undo = GIMP_AUX_ITEM_UNDO (undo); + + g_clear_object (&aux_item_undo->aux_item); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpauxitemundo.h b/app/core/gimpauxitemundo.h new file mode 100644 index 0000000..2858f4d --- /dev/null +++ b/app/core/gimpauxitemundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_AUX_ITEM_UNDO_H__ +#define __GIMP_AUX_ITEM_UNDO_H__ + + +#include "gimpundo.h" + + +#define GIMP_TYPE_AUX_ITEM_UNDO (gimp_aux_item_undo_get_type ()) +#define GIMP_AUX_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_AUX_ITEM_UNDO, GimpAuxItemUndo)) +#define GIMP_AUX_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_AUX_ITEM_UNDO, GimpAuxItemUndoClass)) +#define GIMP_IS_AUX_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_AUX_ITEM_UNDO)) +#define GIMP_IS_AUX_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_AUX_ITEM_UNDO)) +#define GIMP_AUX_ITEM_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_AUX_ITEM_UNDO, GimpAuxItemUndoClass)) + + +typedef struct _GimpAuxItemUndo GimpAuxItemUndo; +typedef struct _GimpAuxItemUndoClass GimpAuxItemUndoClass; + +struct _GimpAuxItemUndo +{ + GimpUndo parent_instance; + + GimpAuxItem *aux_item; +}; + +struct _GimpAuxItemUndoClass +{ + GimpUndoClass parent_class; +}; + + +GType gimp_aux_item_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_AUX_ITEM_UNDO_H__ */ diff --git a/app/core/gimpbacktrace-backend.h b/app/core/gimpbacktrace-backend.h new file mode 100644 index 0000000..4bfb1c9 --- /dev/null +++ b/app/core/gimpbacktrace-backend.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbacktrace-backend.h + * 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 . + */ + +#ifndef __GIMP_BACKTRACE_BACKEND_H__ +#define __GIMP_BACKTRACE_BACKEND_H__ + + +#ifdef __gnu_linux__ +# define GIMP_BACKTRACE_BACKEND_LINUX +#elif defined (G_OS_WIN32) && defined (ARCH_X86) +# define GIMP_BACKTRACE_BACKEND_WINDOWS +#else +# define GIMP_BACKTRACE_BACKEND_NONE +#endif + + +#endif /* __GIMP_BACKTRACE_BACKEND_H__ */ diff --git a/app/core/gimpbacktrace-linux.c b/app/core/gimpbacktrace-linux.c new file mode 100644 index 0000000..a8593f8 --- /dev/null +++ b/app/core/gimpbacktrace-linux.c @@ -0,0 +1,725 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpbacktrace-linux.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 . + */ + + +#define _GNU_SOURCE + + +#include "config.h" + +#include + +#include "gimpbacktrace-backend.h" + + +#ifdef GIMP_BACKTRACE_BACKEND_LINUX + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBBACKTRACE +#include +#endif + +#ifdef HAVE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#endif + +#include "core-types.h" + +#include "gimpbacktrace.h" + + +#define MAX_N_THREADS 256 +#define MAX_N_FRAMES 256 +#define MAX_THREAD_NAME_SIZE 32 +#define N_SKIPPED_FRAMES 2 +#define MAX_WAIT_TIME (G_TIME_SPAN_SECOND / 20) +#define BACKTRACE_SIGNAL SIGUSR1 + + +typedef struct _GimpBacktraceThread GimpBacktraceThread; + + +struct _GimpBacktraceThread +{ + pid_t tid; + gchar name[MAX_THREAD_NAME_SIZE]; + gchar state; + + guintptr frames[MAX_N_FRAMES]; + gint n_frames; +}; + +struct _GimpBacktrace +{ + GimpBacktraceThread *threads; + gint n_threads; +}; + + +/* local function prototypes */ + +static inline gint gimp_backtrace_normalize_frame (GimpBacktrace *backtrace, + gint thread, + gint frame); + +static gint gimp_backtrace_enumerate_threads (gboolean include_current_thread, + pid_t *threads, + gint size); +static void gimp_backtrace_read_thread_name (pid_t tid, + gchar *name, + gint size); +static gchar gimp_backtrace_read_thread_state (pid_t tid); + +static void gimp_backtrace_signal_handler (gint signum); + + +/* static variables */ + +static GMutex mutex; +static gint n_initializations; +static gboolean initialized; +static struct sigaction orig_action; +static pid_t blacklisted_threads[MAX_N_THREADS]; +static gint n_blacklisted_threads; +static GimpBacktrace *handler_backtrace; +static gint handler_n_remaining_threads; +static gint handler_lock; + +#ifdef HAVE_LIBBACKTRACE +static struct backtrace_state *backtrace_state; +#endif + +static const gchar * const blacklisted_thread_names[] = +{ + "gmain", + "threaded-ml" +}; + + +/* private functions */ + + +static inline gint +gimp_backtrace_normalize_frame (GimpBacktrace *backtrace, + gint thread, + gint frame) +{ + if (frame >= 0) + return frame + N_SKIPPED_FRAMES; + else + return backtrace->threads[thread].n_frames + frame; +} + +static gint +gimp_backtrace_enumerate_threads (gboolean include_current_thread, + pid_t *threads, + gint size) +{ + DIR *dir; + struct dirent *dirent; + pid_t tid; + gint n_threads; + + dir = opendir ("/proc/self/task"); + + if (! dir) + return 0; + + tid = syscall (SYS_gettid); + + n_threads = 0; + + while (n_threads < size && (dirent = readdir (dir))) + { + pid_t id = g_ascii_strtoull (dirent->d_name, NULL, 10); + + if (id) + { + if (! include_current_thread && id == tid) + id = 0; + } + + if (id) + { + gint i; + + for (i = 0; i < n_blacklisted_threads; i++) + { + if (id == blacklisted_threads[i]) + { + id = 0; + + break; + } + } + } + + if (id) + threads[n_threads++] = id; + } + + closedir (dir); + + return n_threads; +} + +static void +gimp_backtrace_read_thread_name (pid_t tid, + gchar *name, + gint size) +{ + gchar filename[64]; + gint fd; + + if (size <= 0) + return; + + name[0] = '\0'; + + g_snprintf (filename, sizeof (filename), + "/proc/self/task/%llu/comm", + (unsigned long long) tid); + + fd = open (filename, O_RDONLY); + + if (fd >= 0) + { + gint n = read (fd, name, size); + + if (n > 0) + name[n - 1] = '\0'; + + close (fd); + } +} + +static gchar +gimp_backtrace_read_thread_state (pid_t tid) +{ + gchar buffer[64]; + gint fd; + gchar state = '\0'; + + g_snprintf (buffer, sizeof (buffer), + "/proc/self/task/%llu/stat", + (unsigned long long) tid); + + fd = open (buffer, O_RDONLY); + + if (fd >= 0) + { + gint n = read (fd, buffer, sizeof (buffer)); + + if (n > 0) + buffer[n - 1] = '\0'; + + sscanf (buffer, "%*d %*s %c", &state); + + close (fd); + } + + return state; +} + +static void +gimp_backtrace_signal_handler (gint signum) +{ + GimpBacktrace *curr_backtrace; + gint lock; + + do + { + lock = g_atomic_int_get (&handler_lock); + + if (lock < 0) + continue; + } + while (! g_atomic_int_compare_and_exchange (&handler_lock, lock, lock + 1)); + + curr_backtrace = g_atomic_pointer_get (&handler_backtrace); + + if (curr_backtrace) + { + pid_t tid = syscall (SYS_gettid); + gint i; + + for (i = 0; i < curr_backtrace->n_threads; i++) + { + GimpBacktraceThread *thread = &curr_backtrace->threads[i]; + + if (thread->tid == tid) + { + thread->n_frames = backtrace ((gpointer *) thread->frames, + MAX_N_FRAMES); + + g_atomic_int_dec_and_test (&handler_n_remaining_threads); + + break; + } + } + } + + g_atomic_int_dec_and_test (&handler_lock); +} + + +/* public functions */ + + +void +gimp_backtrace_init (void) +{ +#ifdef HAVE_LIBBACKTRACE + backtrace_state = backtrace_create_state (NULL, 0, NULL, NULL); +#endif +} + +gboolean +gimp_backtrace_start (void) +{ + g_mutex_lock (&mutex); + + if (n_initializations == 0) + { + struct sigaction action = {}; + + action.sa_handler = gimp_backtrace_signal_handler; + action.sa_flags = SA_RESTART; + + sigemptyset (&action.sa_mask); + + if (sigaction (BACKTRACE_SIGNAL, &action, &orig_action) == 0) + { + pid_t *threads; + gint n_threads; + gint i; + + n_blacklisted_threads = 0; + + threads = g_new (pid_t, MAX_N_THREADS); + + n_threads = gimp_backtrace_enumerate_threads (TRUE, + threads, MAX_N_THREADS); + + for (i = 0; i < n_threads; i++) + { + gchar name[MAX_THREAD_NAME_SIZE]; + gint j; + + gimp_backtrace_read_thread_name (threads[i], + name, MAX_THREAD_NAME_SIZE); + + for (j = 0; j < G_N_ELEMENTS (blacklisted_thread_names); j++) + { + if (! strcmp (name, blacklisted_thread_names[j])) + { + blacklisted_threads[n_blacklisted_threads++] = threads[i]; + } + } + } + + g_free (threads); + + initialized = TRUE; + } + } + + n_initializations++; + + g_mutex_unlock (&mutex); + + return initialized; +} + +void +gimp_backtrace_stop (void) +{ + g_return_if_fail (n_initializations > 0); + + g_mutex_lock (&mutex); + + n_initializations--; + + if (n_initializations == 0 && initialized) + { + if (sigaction (BACKTRACE_SIGNAL, &orig_action, NULL) < 0) + g_warning ("failed to restore origianl backtrace signal handler"); + + initialized = FALSE; + } + + g_mutex_unlock (&mutex); +} + +GimpBacktrace * +gimp_backtrace_new (gboolean include_current_thread) +{ + GimpBacktrace *backtrace; + pid_t pid; + pid_t *threads; + gint n_threads; + gint64 start_time; + gint i; + + if (! initialized) + return NULL; + + pid = getpid (); + + threads = g_new (pid_t, MAX_N_THREADS); + + n_threads = gimp_backtrace_enumerate_threads (include_current_thread, + threads, MAX_N_THREADS); + + if (n_threads == 0) + { + g_free (threads); + + return NULL; + } + + g_mutex_lock (&mutex); + + backtrace = g_slice_new (GimpBacktrace); + + backtrace->threads = g_new (GimpBacktraceThread, n_threads); + backtrace->n_threads = n_threads; + + while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1)); + + g_atomic_pointer_set (&handler_backtrace, backtrace); + g_atomic_int_set (&handler_n_remaining_threads, n_threads); + + g_atomic_int_set (&handler_lock, 0); + + for (i = 0; i < n_threads; i++) + { + GimpBacktraceThread *thread = &backtrace->threads[i]; + + thread->tid = threads[i]; + thread->n_frames = 0; + + gimp_backtrace_read_thread_name (thread->tid, + thread->name, MAX_THREAD_NAME_SIZE); + + thread->state = gimp_backtrace_read_thread_state (thread->tid); + + syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL); + } + + g_free (threads); + + start_time = g_get_monotonic_time (); + + while (g_atomic_int_get (&handler_n_remaining_threads) > 0) + { + gint64 time = g_get_monotonic_time (); + + if (time - start_time > MAX_WAIT_TIME) + break; + + g_usleep (1000); + } + + while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1)); + + g_atomic_pointer_set (&handler_backtrace, NULL); + + g_atomic_int_set (&handler_lock, 0); + +#if 0 + if (handler_n_remaining_threads > 0) + { + gint j = 0; + + for (i = 0; i < n_threads; i++) + { + if (backtrace->threads[i].n_frames == 0) + { + if (n_blacklisted_threads < MAX_N_THREADS) + { + blacklisted_threads[n_blacklisted_threads++] = + backtrace->threads[i].tid; + } + } + else + { + if (j < i) + backtrace->threads[j] = backtrace->threads[i]; + + j++; + } + } + + n_threads = j; + } +#endif + + g_mutex_unlock (&mutex); + + if (n_threads == 0) + { + gimp_backtrace_free (backtrace); + + return NULL; + } + + return backtrace; +} + +void +gimp_backtrace_free (GimpBacktrace *backtrace) +{ + if (! backtrace) + return; + + g_free (backtrace->threads); + + g_slice_free (GimpBacktrace, backtrace); +} + +gint +gimp_backtrace_get_n_threads (GimpBacktrace *backtrace) +{ + g_return_val_if_fail (backtrace != NULL, 0); + + return backtrace->n_threads; +} + +guintptr +gimp_backtrace_get_thread_id (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + return backtrace->threads[thread].tid; +} + +const gchar * +gimp_backtrace_get_thread_name (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, NULL); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL); + + if (backtrace->threads[thread].name[0]) + return backtrace->threads[thread].name; + else + return NULL; +} + +gboolean +gimp_backtrace_is_thread_running (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, FALSE); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE); + + return backtrace->threads[thread].state == 'R'; +} + +gint +gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace, + guintptr thread_id, + gint thread_hint) +{ + pid_t tid = thread_id; + gint i; + + g_return_val_if_fail (backtrace != NULL, -1); + + if (thread_hint >= 0 && + thread_hint < backtrace->n_threads && + backtrace->threads[thread_hint].tid == tid) + { + return thread_hint; + } + + for (i = 0; i < backtrace->n_threads; i++) + { + if (backtrace->threads[i].tid == tid) + return i; + } + + return -1; +} + +gint +gimp_backtrace_get_n_frames (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + return MAX (backtrace->threads[thread].n_frames - N_SKIPPED_FRAMES, 0); +} + +guintptr +gimp_backtrace_get_frame_address (GimpBacktrace *backtrace, + gint thread, + gint frame) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + frame = gimp_backtrace_normalize_frame (backtrace, thread, frame); + + g_return_val_if_fail (frame >= N_SKIPPED_FRAMES && + frame < backtrace->threads[thread].n_frames, 0); + + return backtrace->threads[thread].frames[frame]; +} + +#ifdef HAVE_LIBBACKTRACE +static void +gimp_backtrace_syminfo_callback (GimpBacktraceAddressInfo *info, + guintptr pc, + const gchar *symname, + guintptr symval, + guintptr symsize) +{ + if (symname) + g_strlcpy (info->symbol_name, symname, sizeof (info->symbol_name)); + + info->symbol_address = symval; +} + +static gint +gimp_backtrace_pcinfo_callback (GimpBacktraceAddressInfo *info, + guintptr pc, + const gchar *filename, + gint lineno, + const gchar *function) +{ + if (function) + g_strlcpy (info->symbol_name, function, sizeof (info->symbol_name)); + + if (filename) + g_strlcpy (info->source_file, filename, sizeof (info->source_file)); + + info->source_line = lineno; + + return 0; +} +#endif /* HAVE_LIBBACKTRACE */ + +gboolean +gimp_backtrace_get_address_info (guintptr address, + GimpBacktraceAddressInfo *info) +{ + Dl_info dl_info; + gboolean result = FALSE; + + g_return_val_if_fail (info != NULL, FALSE); + + info->object_name[0] = '\0'; + + info->symbol_name[0] = '\0'; + info->symbol_address = 0; + + info->source_file[0] = '\0'; + info->source_line = 0; + + if (dladdr ((gpointer) address, &dl_info)) + { + if (dl_info.dli_fname) + { + g_strlcpy (info->object_name, dl_info.dli_fname, + sizeof (info->object_name)); + } + + if (dl_info.dli_sname) + { + g_strlcpy (info->symbol_name, dl_info.dli_sname, + sizeof (info->symbol_name)); + } + + info->symbol_address = (guintptr) dl_info.dli_saddr; + + result = TRUE; + } + +#ifdef HAVE_LIBBACKTRACE + if (backtrace_state) + { + backtrace_syminfo ( + backtrace_state, address, + (backtrace_syminfo_callback) gimp_backtrace_syminfo_callback, + NULL, + info); + + backtrace_pcinfo ( + backtrace_state, address, + (backtrace_full_callback) gimp_backtrace_pcinfo_callback, + NULL, + info); + + result = TRUE; + } +#endif /* HAVE_LIBBACKTRACE */ + +#ifdef HAVE_LIBUNWIND +/* we use libunwind to get the symbol name, when available, even if dladdr() or + * libbacktrace already found one, since it provides more descriptive names in + * some cases, and, in particular, full symbol names for C++ lambdas. + * + * note that, in some cases, this can result in a discrepancy between the + * symbol name, and the corresponding source location. + */ +#if 0 + if (! info->symbol_name[0]) +#endif + { + unw_context_t context = {}; + unw_cursor_t cursor; + unw_word_t offset; + + if (unw_init_local (&cursor, &context) == 0 && + unw_set_reg (&cursor, UNW_REG_IP, address) == 0 && + unw_get_proc_name (&cursor, + info->symbol_name, sizeof (info->symbol_name), + &offset) == 0) + { + info->symbol_address = address - offset; + + result = TRUE; + } + } +#endif /* HAVE_LIBUNWIND */ + + return result; +} + + +#endif /* GIMP_BACKTRACE_BACKEND_LINUX */ diff --git a/app/core/gimpbacktrace-none.c b/app/core/gimpbacktrace-none.c new file mode 100644 index 0000000..087e8cb --- /dev/null +++ b/app/core/gimpbacktrace-none.c @@ -0,0 +1,126 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpbacktrace-none.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 . + */ + + +#include "config.h" + +#include + +#include "gimpbacktrace-backend.h" + + +#ifdef GIMP_BACKTRACE_BACKEND_NONE + + +#include "core-types.h" + +#include "gimpbacktrace.h" + + +/* public functions */ + + +void +gimp_backtrace_init (void) +{ +} + +gboolean +gimp_backtrace_start (void) +{ + return FALSE; +} + +void +gimp_backtrace_stop (void) +{ +} + +GimpBacktrace * +gimp_backtrace_new (gboolean include_current_thread) +{ + return NULL; +} + +void +gimp_backtrace_free (GimpBacktrace *backtrace) +{ + g_return_if_fail (backtrace == NULL); +} + +gint +gimp_backtrace_get_n_threads (GimpBacktrace *backtrace) +{ + g_return_val_if_reached (0); +} + +guintptr +gimp_backtrace_get_thread_id (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_reached (0); +} + +const gchar * +gimp_backtrace_get_thread_name (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_reached (NULL); +} + +gboolean +gimp_backtrace_is_thread_running (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_reached (FALSE); +} + +gint +gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace, + guintptr thread_id, + gint thread_hint) +{ + g_return_val_if_reached (-1); +} + +gint +gimp_backtrace_get_n_frames (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_reached (0); +} + +guintptr +gimp_backtrace_get_frame_address (GimpBacktrace *backtrace, + gint thread, + gint frame) +{ + g_return_val_if_reached (0); +} + +gboolean +gimp_backtrace_get_address_info (guintptr address, + GimpBacktraceAddressInfo *info) +{ + return FALSE; +} + + +#endif /* GIMP_BACKTRACE_BACKEND_NONE */ diff --git a/app/core/gimpbacktrace-windows.c b/app/core/gimpbacktrace-windows.c new file mode 100644 index 0000000..da1fb73 --- /dev/null +++ b/app/core/gimpbacktrace-windows.c @@ -0,0 +1,706 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpbacktrace-windows.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 . + */ + + +#include "config.h" + +#include + +#include "gimpbacktrace-backend.h" + + +#ifdef GIMP_BACKTRACE_BACKEND_WINDOWS + + +#include +#include +#include +#include + +#include + +#include "core-types.h" + +#include "gimpbacktrace.h" + + +#define MAX_N_THREADS 256 +#define MAX_N_FRAMES 256 +#define THREAD_ENUMERATION_INTERVAL G_TIME_SPAN_SECOND + + +typedef struct _Thread Thread; +typedef struct _GimpBacktraceThread GimpBacktraceThread; + + +struct _Thread +{ + DWORD tid; + + union + { + gchar *name; + guint64 time; + }; +}; + +struct _GimpBacktraceThread +{ + DWORD tid; + const gchar *name; + guint64 time; + guint64 last_time; + + guintptr frames[MAX_N_FRAMES]; + gint n_frames; +}; + +struct _GimpBacktrace +{ + GimpBacktraceThread *threads; + gint n_threads; +}; + + +/* local function prototypes */ + +static inline gint gimp_backtrace_normalize_frame (GimpBacktrace *backtrace, + gint thread, + gint frame); + +static void gimp_backtrace_set_thread_name (DWORD tid, + const gchar *name); + +static gboolean gimp_backtrace_enumerate_threads (void); + +static LONG WINAPI gimp_backtrace_exception_handler (PEXCEPTION_POINTERS info); + + +/* static variables */ + +static GMutex mutex; +static gint n_initializations; +static gboolean initialized; +Thread threads[MAX_N_THREADS]; +gint n_threads; +gint64 last_thread_enumeration_time; +Thread thread_names[MAX_N_THREADS]; +gint n_thread_names; +gint thread_names_spinlock; +Thread thread_times[MAX_N_THREADS]; +gint n_thread_times; + +DWORD WINAPI (* gimp_backtrace_SymSetOptions) (DWORD SymOptions); +BOOL WINAPI (* gimp_backtrace_SymInitialize) (HANDLE hProcess, + PCSTR UserSearchPath, + BOOL fInvadeProcess); +BOOL WINAPI (* gimp_backtrace_SymCleanup) (HANDLE hProcess); +BOOL WINAPI (* gimp_backtrace_SymFromAddr) (HANDLE hProcess, + DWORD64 Address, + PDWORD64 Displacement, + PSYMBOL_INFO Symbol); +BOOL WINAPI (* gimp_backtrace_SymGetLineFromAddr64) (HANDLE hProcess, + DWORD64 qwAddr, + PDWORD pdwDisplacement, + PIMAGEHLP_LINE64 Line64); + + +/* private functions */ + + +static inline gint +gimp_backtrace_normalize_frame (GimpBacktrace *backtrace, + gint thread, + gint frame) +{ + if (frame >= 0) + return frame; + else + return backtrace->threads[thread].n_frames + frame; +} + +static void +gimp_backtrace_set_thread_name (DWORD tid, + const gchar *name) +{ + while (! g_atomic_int_compare_and_exchange (&thread_names_spinlock, + 0, 1)); + + if (n_thread_names < MAX_N_THREADS) + { + Thread *thread = &thread_names[n_thread_names++]; + + thread->tid = tid; + thread->name = g_strdup (name); + } + + g_atomic_int_set (&thread_names_spinlock, 0); +} + +static gboolean +gimp_backtrace_enumerate_threads (void) +{ + HANDLE hThreadSnap; + THREADENTRY32 te32; + DWORD pid; + gint64 time; + + time = g_get_monotonic_time (); + + if (time - last_thread_enumeration_time < THREAD_ENUMERATION_INTERVAL) + return n_threads > 0; + + last_thread_enumeration_time = time; + + n_threads = 0; + + hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0); + + if (hThreadSnap == INVALID_HANDLE_VALUE) + return FALSE; + + te32.dwSize = sizeof (te32); + + if (! Thread32First (hThreadSnap, &te32)) + { + CloseHandle (hThreadSnap); + + return FALSE; + } + + pid = GetCurrentProcessId (); + + while (! g_atomic_int_compare_and_exchange (&thread_names_spinlock, 0, 1)); + + do + { + if (n_threads == MAX_N_THREADS) + break; + + if (te32.th32OwnerProcessID == pid) + { + Thread *thread = &threads[n_threads++]; + gint i; + + thread->tid = te32.th32ThreadID; + thread->name = NULL; + + for (i = n_thread_names - 1; i >= 0; i--) + { + if (thread->tid == thread_names[i].tid) + { + thread->name = thread_names[i].name; + + break; + } + } + } + } + while (Thread32Next (hThreadSnap, &te32)); + + g_atomic_int_set (&thread_names_spinlock, 0); + + CloseHandle (hThreadSnap); + + return n_threads > 0; +} + +static LONG WINAPI +gimp_backtrace_exception_handler (PEXCEPTION_POINTERS info) +{ + #define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388) + + typedef struct _THREADNAME_INFO + { + DWORD dwType; /* must be 0x1000 */ + LPCSTR szName; /* pointer to name (in user addr space) */ + DWORD dwThreadID; /* thread ID (-1=caller thread) */ + DWORD dwFlags; /* reserved for future use, must be zero */ + } THREADNAME_INFO; + + if (info->ExceptionRecord != NULL && + info->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME && + info->ExceptionRecord->NumberParameters * + sizeof (ULONG_PTR) == sizeof (THREADNAME_INFO)) + { + THREADNAME_INFO name_info; + + memcpy (&name_info, info->ExceptionRecord->ExceptionInformation, + sizeof (name_info)); + + if (name_info.dwType == 0x1000) + { + DWORD tid = name_info.dwThreadID; + + if (tid == -1) + tid = GetCurrentThreadId (); + + gimp_backtrace_set_thread_name (tid, name_info.szName); + + return EXCEPTION_CONTINUE_EXECUTION; + } + } + + return EXCEPTION_CONTINUE_SEARCH; + + #undef EXCEPTION_SET_THREAD_NAME +} + + +/* public functions */ + + +void +gimp_backtrace_init (void) +{ + gimp_backtrace_set_thread_name (GetCurrentThreadId (), g_get_prgname ()); + + AddVectoredExceptionHandler (TRUE, gimp_backtrace_exception_handler); +} + +gboolean +gimp_backtrace_start (void) +{ + g_mutex_lock (&mutex); + + if (n_initializations == 0) + { + HMODULE hModule; + DWORD options; + + hModule = LoadLibraryA ("mgwhelp.dll"); + + #define INIT_PROC(name) \ + G_STMT_START \ + { \ + gimp_backtrace_##name = name; \ + \ + if (hModule) \ + { \ + gpointer proc = GetProcAddress (hModule, #name); \ + \ + if (proc) \ + gimp_backtrace_##name = proc; \ + } \ + } \ + G_STMT_END + + INIT_PROC (SymSetOptions); + INIT_PROC (SymInitialize); + INIT_PROC (SymCleanup); + INIT_PROC (SymFromAddr); + INIT_PROC (SymGetLineFromAddr64); + + #undef INIT_PROC + + options = SymGetOptions (); + + options &= ~SYMOPT_UNDNAME; + options |= SYMOPT_OMAP_FIND_NEAREST | + SYMOPT_DEFERRED_LOADS | + SYMOPT_DEBUG; + +#ifdef ARCH_X86_64 + options |= SYMOPT_INCLUDE_32BIT_MODULES; +#endif + + gimp_backtrace_SymSetOptions (options); + + if (gimp_backtrace_SymInitialize (GetCurrentProcess (), NULL, TRUE)) + { + n_threads = 0; + last_thread_enumeration_time = 0; + n_thread_times = 0; + + initialized = TRUE; + } + } + + n_initializations++; + + g_mutex_unlock (&mutex); + + return initialized; +} + +void +gimp_backtrace_stop (void) +{ + g_return_if_fail (n_initializations > 0); + + g_mutex_lock (&mutex); + + n_initializations--; + + if (n_initializations == 0) + { + if (initialized) + { + gimp_backtrace_SymCleanup (GetCurrentProcess ()); + + initialized = FALSE; + } + } + + g_mutex_unlock (&mutex); +} + +GimpBacktrace * +gimp_backtrace_new (gboolean include_current_thread) +{ + GimpBacktrace *backtrace; + HANDLE hProcess; + DWORD tid; + gint i; + + if (! initialized) + return NULL; + + g_mutex_lock (&mutex); + + if (! gimp_backtrace_enumerate_threads ()) + { + g_mutex_unlock (&mutex); + + return NULL; + } + + hProcess = GetCurrentProcess (); + tid = GetCurrentThreadId (); + + backtrace = g_slice_new (GimpBacktrace); + + backtrace->threads = g_new (GimpBacktraceThread, n_threads); + backtrace->n_threads = 0; + + for (i = 0; i < n_threads; i++) + { + GimpBacktraceThread *thread = &backtrace->threads[backtrace->n_threads]; + HANDLE hThread; + CONTEXT context = {}; + STACKFRAME64 frame = {}; + DWORD machine_type; + FILETIME creation_time; + FILETIME exit_time; + FILETIME kernel_time; + FILETIME user_time; + + if (! include_current_thread && threads[i].tid == tid) + continue; + + hThread = OpenThread (THREAD_QUERY_INFORMATION | + THREAD_GET_CONTEXT | + THREAD_SUSPEND_RESUME, + FALSE, + threads[i].tid); + + if (hThread == INVALID_HANDLE_VALUE) + continue; + + if (threads[i].tid != tid && SuspendThread (hThread) == (DWORD) -1) + { + CloseHandle (hThread); + + continue; + } + + context.ContextFlags = CONTEXT_FULL; + + if (! GetThreadContext (hThread, &context)) + { + if (threads[i].tid != tid) + ResumeThread (hThread); + + CloseHandle (hThread); + + continue; + } + +#ifdef ARCH_X86_64 + machine_type = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = context.Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Rsp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Rbp; + frame.AddrFrame.Mode = AddrModeFlat; +#elif defined (ARCH_X86) + machine_type = IMAGE_FILE_MACHINE_I386; + frame.AddrPC.Offset = context.Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Esp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Ebp; + frame.AddrFrame.Mode = AddrModeFlat; +#else +#error unsupported architecture +#endif + + thread->tid = threads[i].tid; + thread->name = threads[i].name; + thread->last_time = 0; + thread->time = 0; + + thread->n_frames = 0; + + while (thread->n_frames < MAX_N_FRAMES && + StackWalk64 (machine_type, hProcess, hThread, &frame, &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + thread->frames[thread->n_frames++] = frame.AddrPC.Offset; + + if (frame.AddrPC.Offset == frame.AddrReturn.Offset) + break; + } + + if (GetThreadTimes (hThread, + &creation_time, &exit_time, + &kernel_time, &user_time)) + { + thread->time = (((guint64) kernel_time.dwHighDateTime << 32) | + ((guint64) kernel_time.dwLowDateTime)) + + (((guint64) user_time.dwHighDateTime << 32) | + ((guint64) user_time.dwLowDateTime)); + + if (i < n_thread_times && thread->tid == thread_times[i].tid) + { + thread->last_time = thread_times[i].time; + } + else + { + gint j; + + for (j = 0; j < n_thread_times; j++) + { + if (thread->tid == thread_times[j].tid) + { + thread->last_time = thread_times[j].time; + + break; + } + } + } + } + + if (threads[i].tid != tid) + ResumeThread (hThread); + + CloseHandle (hThread); + + if (thread->n_frames > 0) + backtrace->n_threads++; + } + + n_thread_times = backtrace->n_threads; + + for (i = 0; i < backtrace->n_threads; i++) + { + thread_times[i].tid = backtrace->threads[i].tid; + thread_times[i].time = backtrace->threads[i].time; + } + + g_mutex_unlock (&mutex); + + if (backtrace->n_threads == 0) + { + gimp_backtrace_free (backtrace); + + return NULL; + } + + return backtrace; +} + +void +gimp_backtrace_free (GimpBacktrace *backtrace) +{ + if (backtrace) + { + g_free (backtrace->threads); + + g_slice_free (GimpBacktrace, backtrace); + } +} + +gint +gimp_backtrace_get_n_threads (GimpBacktrace *backtrace) +{ + g_return_val_if_fail (backtrace != NULL, 0); + + return backtrace->n_threads; +} + +guintptr +gimp_backtrace_get_thread_id (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + return backtrace->threads[thread].tid; +} + +const gchar * +gimp_backtrace_get_thread_name (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, NULL); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL); + + return backtrace->threads[thread].name; +} + +gboolean +gimp_backtrace_is_thread_running (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, FALSE); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE); + + return backtrace->threads[thread].time > + backtrace->threads[thread].last_time; +} + +gint +gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace, + guintptr thread_id, + gint thread_hint) +{ + DWORD tid = thread_id; + gint i; + + g_return_val_if_fail (backtrace != NULL, -1); + + if (thread_hint >= 0 && + thread_hint < backtrace->n_threads && + backtrace->threads[thread_hint].tid == tid) + { + return thread_hint; + } + + for (i = 0; i < backtrace->n_threads; i++) + { + if (backtrace->threads[i].tid == tid) + return i; + } + + return -1; +} + +gint +gimp_backtrace_get_n_frames (GimpBacktrace *backtrace, + gint thread) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + return backtrace->threads[thread].n_frames; +} + +guintptr +gimp_backtrace_get_frame_address (GimpBacktrace *backtrace, + gint thread, + gint frame) +{ + g_return_val_if_fail (backtrace != NULL, 0); + g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0); + + frame = gimp_backtrace_normalize_frame (backtrace, thread, frame); + + g_return_val_if_fail (frame >= 0 && + frame < backtrace->threads[thread].n_frames, 0); + + return backtrace->threads[thread].frames[frame]; +} + +gboolean +gimp_backtrace_get_address_info (guintptr address, + GimpBacktraceAddressInfo *info) +{ + SYMBOL_INFO *symbol_info; + HANDLE hProcess; + HMODULE hModule; + DWORD64 offset = 0; + IMAGEHLP_LINE64 line = {}; + DWORD line_offset = 0; + gboolean result = FALSE; + + hProcess = GetCurrentProcess (); + hModule = (HMODULE) (guintptr) SymGetModuleBase64 (hProcess, address); + + if (hModule && GetModuleFileNameExA (hProcess, hModule, + info->object_name, + sizeof (info->object_name))) + { + result = TRUE; + } + else + { + info->object_name[0] = '\0'; + } + + symbol_info = g_malloc (sizeof (SYMBOL_INFO) + + sizeof (info->symbol_name) - 1); + + symbol_info->SizeOfStruct = sizeof (SYMBOL_INFO); + symbol_info->MaxNameLen = sizeof (info->symbol_name); + + if (gimp_backtrace_SymFromAddr (hProcess, address, + &offset, symbol_info)) + { + g_strlcpy (info->symbol_name, symbol_info->Name, + sizeof (info->symbol_name)); + + info->symbol_address = offset ? address - offset : 0; + + result = TRUE; + } + else + { + info->symbol_name[0] = '\0'; + info->symbol_address = 0; + } + + g_free (symbol_info); + + if (gimp_backtrace_SymGetLineFromAddr64 (hProcess, address, + &line_offset, &line)) + { + g_strlcpy (info->source_file, line.FileName, + sizeof (info->source_file)); + + info->source_line = line.LineNumber; + + result = TRUE; + } + else + { + info->source_file[0] = '\0'; + info->source_line = 0; + } + + return result; +} + + +#endif /* GIMP_BACKTRACE_BACKEND_WINDOWS */ diff --git a/app/core/gimpbacktrace.h b/app/core/gimpbacktrace.h new file mode 100644 index 0000000..8c172b2 --- /dev/null +++ b/app/core/gimpbacktrace.h @@ -0,0 +1,70 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbacktrace.h + * 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 . + */ + +#ifndef __GIMP_BACKTRACE_H__ +#define __GIMP_BACKTRACE_H__ + + +typedef struct _GimpBacktraceAddressInfo GimpBacktraceAddressInfo; + + +struct _GimpBacktraceAddressInfo +{ + gchar object_name[256]; + + gchar symbol_name[256]; + guintptr symbol_address; + + gchar source_file[256]; + gint source_line; +}; + + +void gimp_backtrace_init (void); + +gboolean gimp_backtrace_start (void); +void gimp_backtrace_stop (void); + +GimpBacktrace * gimp_backtrace_new (gboolean include_current_thread); +void gimp_backtrace_free (GimpBacktrace *backtrace); + +gint gimp_backtrace_get_n_threads (GimpBacktrace *backtrace); +guintptr gimp_backtrace_get_thread_id (GimpBacktrace *backtrace, + gint thread); +const gchar * gimp_backtrace_get_thread_name (GimpBacktrace *backtrace, + gint thread); +gboolean gimp_backtrace_is_thread_running (GimpBacktrace *backtrace, + gint thread); + +gint gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace, + guintptr thread_id, + gint thread_hint); + +gint gimp_backtrace_get_n_frames (GimpBacktrace *backtrace, + gint thread); +guintptr gimp_backtrace_get_frame_address (GimpBacktrace *backtrace, + gint thread, + gint frame); + +gboolean gimp_backtrace_get_address_info (guintptr address, + GimpBacktraceAddressInfo *info); + + +#endif /* __GIMP_BACKTRACE_H__ */ diff --git a/app/core/gimpbezierdesc.c b/app/core/gimpbezierdesc.c new file mode 100644 index 0000000..6047544 --- /dev/null +++ b/app/core/gimpbezierdesc.c @@ -0,0 +1,202 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbezierdesc.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpbezierdesc.h" +#include "gimpboundary.h" + + +GType +gimp_bezier_desc_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpBezierDesc", + (GBoxedCopyFunc) gimp_bezier_desc_copy, + (GBoxedFreeFunc) gimp_bezier_desc_free); + + return type; +} + +GimpBezierDesc * +gimp_bezier_desc_new (cairo_path_data_t *data, + gint n_data) +{ + GimpBezierDesc *desc; + + g_return_val_if_fail (n_data == 0 || data != NULL, NULL); + + desc = g_slice_new (GimpBezierDesc); + + desc->status = CAIRO_STATUS_SUCCESS; + desc->num_data = n_data; + desc->data = data; + + return desc; +} + +static void +add_polyline (GArray *path_data, + const GimpVector2 *points, + gint n_points, + gboolean closed) +{ + GimpVector2 prev = { 0.0, 0.0, }; + cairo_path_data_t pd; + gint i; + + for (i = 0; i < n_points; i++) + { + /* compress multiple identical coordinates */ + if (i == 0 || + prev.x != points[i].x || + prev.y != points[i].y) + { + pd.header.type = (i == 0) ? CAIRO_PATH_MOVE_TO : CAIRO_PATH_LINE_TO; + pd.header.length = 2; + + g_array_append_val (path_data, pd); + + pd.point.x = points[i].x; + pd.point.y = points[i].y; + + g_array_append_val (path_data, pd); + + prev = points[i]; + } + } + + /* close the polyline when needed */ + if (closed) + { + pd.header.type = CAIRO_PATH_CLOSE_PATH; + pd.header.length = 1; + + g_array_append_val (path_data, pd); + } +} + +GimpBezierDesc * +gimp_bezier_desc_new_from_bound_segs (GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint n_bound_groups) +{ + GArray *path_data; + GimpVector2 *points; + gint n_points; + gint seg; + gint i; + guint path_data_len; + + g_return_val_if_fail (bound_segs != NULL, NULL); + g_return_val_if_fail (n_bound_segs > 0, NULL); + + path_data = g_array_new (FALSE, FALSE, sizeof (cairo_path_data_t)); + + points = g_new0 (GimpVector2, n_bound_segs + 4); + + seg = 0; + n_points = 0; + + points[n_points].x = (gdouble) (bound_segs[0].x1); + points[n_points].y = (gdouble) (bound_segs[0].y1); + + n_points++; + + for (i = 0; i < n_bound_groups; i++) + { + while (bound_segs[seg].x1 != -1 || + bound_segs[seg].x2 != -1 || + bound_segs[seg].y1 != -1 || + bound_segs[seg].y2 != -1) + { + points[n_points].x = (gdouble) (bound_segs[seg].x1); + points[n_points].y = (gdouble) (bound_segs[seg].y1); + + n_points++; + seg++; + } + + /* Close the stroke points up */ + points[n_points] = points[0]; + + n_points++; + + add_polyline (path_data, points, n_points, TRUE); + + n_points = 0; + seg++; + + points[n_points].x = (gdouble) (bound_segs[seg].x1); + points[n_points].y = (gdouble) (bound_segs[seg].y1); + + n_points++; + } + + g_free (points); + + path_data_len = path_data->len; + + return gimp_bezier_desc_new ((cairo_path_data_t *) g_array_free (path_data, FALSE), + path_data_len); +} + +void +gimp_bezier_desc_translate (GimpBezierDesc *desc, + gdouble offset_x, + gdouble offset_y) +{ + gint i, j; + + g_return_if_fail (desc != NULL); + + for (i = 0; i < desc->num_data; i += desc->data[i].header.length) + for (j = 1; j < desc->data[i].header.length; ++j) + { + desc->data[i+j].point.x += offset_x; + desc->data[i+j].point.y += offset_y; + } +} + +GimpBezierDesc * +gimp_bezier_desc_copy (const GimpBezierDesc *desc) +{ + g_return_val_if_fail (desc != NULL, NULL); + + return gimp_bezier_desc_new (g_memdup (desc->data, + desc->num_data * sizeof (cairo_path_data_t)), + desc->num_data); +} + +void +gimp_bezier_desc_free (GimpBezierDesc *desc) +{ + g_return_if_fail (desc != NULL); + + g_free (desc->data); + g_slice_free (GimpBezierDesc, desc); +} diff --git a/app/core/gimpbezierdesc.h b/app/core/gimpbezierdesc.h new file mode 100644 index 0000000..3d09dc2 --- /dev/null +++ b/app/core/gimpbezierdesc.h @@ -0,0 +1,47 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbezierdesc.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BEZIER_DESC_H__ +#define __GIMP_BEZIER_DESC_H__ + + +#define GIMP_TYPE_BEZIER_DESC (gimp_bezier_desc_get_type ()) + +GType gimp_bezier_desc_get_type (void) G_GNUC_CONST; + + +/* takes ownership of "data" */ +GimpBezierDesc * gimp_bezier_desc_new (cairo_path_data_t *data, + gint n_data); + +/* expects sorted GimpBoundSegs */ +GimpBezierDesc * gimp_bezier_desc_new_from_bound_segs (GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint n_bound_groups); + +void gimp_bezier_desc_translate (GimpBezierDesc *desc, + gdouble offset_x, + gdouble offset_y); + +GimpBezierDesc * gimp_bezier_desc_copy (const GimpBezierDesc *desc); +void gimp_bezier_desc_free (GimpBezierDesc *desc); + + +#endif /* __GIMP_BEZIER_DESC_H__ */ diff --git a/app/core/gimpboundary.c b/app/core/gimpboundary.c new file mode 100644 index 0000000..995242c --- /dev/null +++ b/app/core/gimpboundary.c @@ -0,0 +1,1016 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpboundary.h" + + +/* GimpBoundSeg array growth parameter */ +#define MAX_SEGS_INC 2048 + + +typedef struct _GimpBoundary GimpBoundary; + +struct _GimpBoundary +{ + /* The array of segments */ + GimpBoundSeg *segs; + gint num_segs; + gint max_segs; + + /* The array of vertical segments */ + gint *vert_segs; + + /* The empty segment arrays */ + gint *empty_segs_n; + gint *empty_segs_c; + gint *empty_segs_l; + gint max_empty_segs; +}; + + +/* local function prototypes */ + +static GimpBoundary * gimp_boundary_new (const GeglRectangle *region); +static GimpBoundSeg * gimp_boundary_free (GimpBoundary *boundary, + gboolean free_segs); + +static void gimp_boundary_add_seg (GimpBoundary *bounrady, + gint x1, + gint y1, + gint x2, + gint y2, + gboolean open); + +static void find_empty_segs (const GeglRectangle *region, + const gfloat *line_data, + gint scanline, + gint empty_segs[], + gint max_empty, + gint *num_empty, + GimpBoundaryType type, + gint x1, + gint y1, + gint x2, + gint y2, + gfloat threshold); +static void process_horiz_seg (GimpBoundary *boundary, + gint x1, + gint y1, + gint x2, + gint y2, + gboolean open); +static void make_horiz_segs (GimpBoundary *boundary, + gint start, + gint end, + gint scanline, + gint empty[], + gint num_empty, + gint top); +static GimpBoundary * generate_boundary (GeglBuffer *buffer, + const GeglRectangle *region, + const Babl *format, + GimpBoundaryType type, + gint x1, + gint y1, + gint x2, + gint y2, + gfloat threshold); + +static gint cmp_segptr_xy1_addr (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b); +static gint cmp_segptr_xy2_addr (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b); + +static gint cmp_segptr_xy1 (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b); +static gint cmp_segptr_xy2 (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b); + +static const GimpBoundSeg * find_segment (const GimpBoundSeg **segs_by_xy1, + const GimpBoundSeg **segs_by_xy2, + gint num_segs, + gint x, + gint y); + +static const GimpBoundSeg * find_segment_with_func (const GimpBoundSeg **segs, + gint num_segs, + const GimpBoundSeg *search_seg, + GCompareFunc cmp_func); + +static void simplify_subdivide (const GimpBoundSeg *segs, + gint start_idx, + gint end_idx, + GArray **ret_points); + + +/* public functions */ + +/** + * gimp_boundary_find: + * @buffer: a #GeglBuffer + * @format: a #Babl float format representing the component to analyze + * @type: type of bounds + * @x1: left side of bounds + * @y1: top side of bounds + * @x2: right side of bounds + * @y2: bottom side of bounds + * @threshold: pixel value of boundary line + * @num_segs: number of returned #GimpBoundSeg's + * + * This function returns an array of #GimpBoundSeg's which describe all + * outlines along pixel value @threahold, optionally within specified + * bounds instead of the whole region. + * + * The @maskPR parameter can be any PixelRegion. If the region has + * more than 1 bytes/pixel, the last byte of each pixel is used to + * determine the boundary outline. + * + * Return value: the boundary array. + **/ +GimpBoundSeg * +gimp_boundary_find (GeglBuffer *buffer, + const GeglRectangle *region, + const Babl *format, + GimpBoundaryType type, + int x1, + int y1, + int x2, + int y2, + gfloat threshold, + int *num_segs) +{ + GimpBoundary *boundary; + GeglRectangle rect = { 0, }; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (num_segs != NULL, NULL); + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (babl_format_get_bytes_per_pixel (format) == + sizeof (gfloat), NULL); + + if (region) + { + rect = *region; + } + else + { + rect.width = gegl_buffer_get_width (buffer); + rect.height = gegl_buffer_get_height (buffer); + } + + boundary = generate_boundary (buffer, &rect, format, type, + x1, y1, x2, y2, threshold); + + *num_segs = boundary->num_segs; + + return gimp_boundary_free (boundary, FALSE); +} + +/** + * gimp_boundary_sort: + * @segs: unsorted input segs. + * @num_segs: number of input segs + * @num_groups: number of groups in the sorted segs + * + * This function takes an array of #GimpBoundSeg's as returned by + * gimp_boundary_find() and sorts it by contiguous groups. The returned + * array contains markers consisting of -1 coordinates and is + * @num_groups elements longer than @segs. + * + * Return value: the sorted segs + **/ +GimpBoundSeg * +gimp_boundary_sort (const GimpBoundSeg *segs, + gint num_segs, + gint *num_groups) +{ + GimpBoundary *boundary; + const GimpBoundSeg **segs_ptrs_by_xy1; + const GimpBoundSeg **segs_ptrs_by_xy2; + gint index; + gint x, y; + gint startx, starty; + + g_return_val_if_fail ((segs == NULL && num_segs == 0) || + (segs != NULL && num_segs > 0), NULL); + g_return_val_if_fail (num_groups != NULL, NULL); + + *num_groups = 0; + + if (num_segs == 0) + return NULL; + + /* prepare arrays with GimpBoundSeg pointers sorted by xy1 and xy2 + * accordingly + */ + segs_ptrs_by_xy1 = g_new (const GimpBoundSeg *, num_segs); + segs_ptrs_by_xy2 = g_new (const GimpBoundSeg *, num_segs); + + for (index = 0; index < num_segs; index++) + { + segs_ptrs_by_xy1[index] = segs + index; + segs_ptrs_by_xy2[index] = segs + index; + } + + qsort (segs_ptrs_by_xy1, num_segs, sizeof (GimpBoundSeg *), + (GCompareFunc) cmp_segptr_xy1_addr); + qsort (segs_ptrs_by_xy2, num_segs, sizeof (GimpBoundSeg *), + (GCompareFunc) cmp_segptr_xy2_addr); + + for (index = 0; index < num_segs; index++) + ((GimpBoundSeg *) segs)[index].visited = FALSE; + + boundary = gimp_boundary_new (NULL); + + for (index = 0; index < num_segs; index++) + { + const GimpBoundSeg *cur_seg; + + if (segs[index].visited) + continue; + + gimp_boundary_add_seg (boundary, + segs[index].x1, segs[index].y1, + segs[index].x2, segs[index].y2, + segs[index].open); + + ((GimpBoundSeg *) segs)[index].visited = TRUE; + + startx = segs[index].x1; + starty = segs[index].y1; + x = segs[index].x2; + y = segs[index].y2; + + while ((cur_seg = find_segment (segs_ptrs_by_xy1, segs_ptrs_by_xy2, + num_segs, x, y)) != NULL) + { + /* make sure ordering is correct */ + if (x == cur_seg->x1 && y == cur_seg->y1) + { + gimp_boundary_add_seg (boundary, + cur_seg->x1, cur_seg->y1, + cur_seg->x2, cur_seg->y2, + cur_seg->open); + x = cur_seg->x2; + y = cur_seg->y2; + } + else + { + gimp_boundary_add_seg (boundary, + cur_seg->x2, cur_seg->y2, + cur_seg->x1, cur_seg->y1, + cur_seg->open); + x = cur_seg->x1; + y = cur_seg->y1; + } + + ((GimpBoundSeg *) cur_seg)->visited = TRUE; + } + + if (G_UNLIKELY (x != startx || y != starty)) + g_warning ("sort_boundary(): Unconnected boundary group!"); + + /* Mark the end of a group */ + *num_groups = *num_groups + 1; + gimp_boundary_add_seg (boundary, -1, -1, -1, -1, 0); + } + + g_free (segs_ptrs_by_xy1); + g_free (segs_ptrs_by_xy2); + + return gimp_boundary_free (boundary, FALSE); +} + +/** + * gimp_boundary_simplify: + * @sorted_segs: sorted input segs + * @num_groups: number of groups in the sorted segs + * @num_segs: number of returned segs. + * + * This function takes an array of #GimpBoundSeg's which has been sorted + * with gimp_boundary_sort() and reduces the number of segments while + * preserving the general shape as close as possible. + * + * Return value: the simplified segs. + **/ +GimpBoundSeg * +gimp_boundary_simplify (GimpBoundSeg *sorted_segs, + gint num_groups, + gint *num_segs) +{ + GArray *new_bounds; + gint i, seg; + + g_return_val_if_fail ((sorted_segs == NULL && num_groups == 0) || + (sorted_segs != NULL && num_groups > 0), NULL); + g_return_val_if_fail (num_segs != NULL, NULL); + + new_bounds = g_array_new (FALSE, FALSE, sizeof (GimpBoundSeg)); + + seg = 0; + + for (i = 0; i < num_groups; i++) + { + gint start = seg; + gint n_points = 0; + + while (sorted_segs[seg].x1 != -1 || + sorted_segs[seg].x2 != -1 || + sorted_segs[seg].y1 != -1 || + sorted_segs[seg].y2 != -1) + { + n_points++; + seg++; + } + + if (n_points > 0) + { + GArray *tmp_points; + GimpBoundSeg tmp_seg; + gint j; + + tmp_points = g_array_new (FALSE, FALSE, sizeof (gint)); + + /* temporarily use the delimiter to close the polygon */ + tmp_seg = sorted_segs[seg]; + sorted_segs[seg] = sorted_segs[start]; + simplify_subdivide (sorted_segs, + start, start + n_points, &tmp_points); + sorted_segs[seg] = tmp_seg; + + for (j = 0; j < tmp_points->len; j++) + g_array_append_val (new_bounds, + sorted_segs[g_array_index (tmp_points, + gint, j)]); + + g_array_append_val (new_bounds, sorted_segs[seg]); + + g_array_free (tmp_points, TRUE); + } + + seg++; + } + + *num_segs = new_bounds->len; + + return (GimpBoundSeg *) g_array_free (new_bounds, FALSE); +} + +void +gimp_boundary_offset (GimpBoundSeg *segs, + gint num_segs, + gint off_x, + gint off_y) +{ + gint i; + + g_return_if_fail ((segs == NULL && num_segs == 0) || + (segs != NULL && num_segs > 0)); + + for (i = 0; i < num_segs; i++) + { + /* don't offset sorting sentinels */ + if (!(segs[i].x1 == -1 && + segs[i].y1 == -1 && + segs[i].x2 == -1 && + segs[i].y2 == -1)) + { + segs[i].x1 += off_x; + segs[i].y1 += off_y; + segs[i].x2 += off_x; + segs[i].y2 += off_y; + } + } +} + + +/* private functions */ + +static GimpBoundary * +gimp_boundary_new (const GeglRectangle *region) +{ + GimpBoundary *boundary = g_slice_new0 (GimpBoundary); + + if (region) + { + gint i; + + /* array for determining the vertical line segments + * which must be drawn + */ + boundary->vert_segs = g_new (gint, region->width + region->x + 1); + + for (i = 0; i <= (region->width + region->x); i++) + boundary->vert_segs[i] = -1; + + /* find the maximum possible number of empty segments + * given the current mask + */ + boundary->max_empty_segs = region->width + 3; + + boundary->empty_segs_n = g_new (gint, boundary->max_empty_segs); + boundary->empty_segs_c = g_new (gint, boundary->max_empty_segs); + boundary->empty_segs_l = g_new (gint, boundary->max_empty_segs); + } + + return boundary; +} + +static GimpBoundSeg * +gimp_boundary_free (GimpBoundary *boundary, + gboolean free_segs) +{ + GimpBoundSeg *segs = NULL; + + if (free_segs) + g_free (boundary->segs); + else + segs = boundary->segs; + + g_free (boundary->vert_segs); + g_free (boundary->empty_segs_n); + g_free (boundary->empty_segs_c); + g_free (boundary->empty_segs_l); + + g_slice_free (GimpBoundary, boundary); + + return segs; +} + +static void +gimp_boundary_add_seg (GimpBoundary *boundary, + gint x1, + gint y1, + gint x2, + gint y2, + gboolean open) +{ + if (boundary->num_segs >= boundary->max_segs) + { + boundary->max_segs += MAX_SEGS_INC; + + boundary->segs = g_renew (GimpBoundSeg, boundary->segs, boundary->max_segs); + } + + boundary->segs[boundary->num_segs].x1 = x1; + boundary->segs[boundary->num_segs].y1 = y1; + boundary->segs[boundary->num_segs].x2 = x2; + boundary->segs[boundary->num_segs].y2 = y2; + boundary->segs[boundary->num_segs].open = open; + + boundary->num_segs ++; +} + +static void +find_empty_segs (const GeglRectangle *region, + const gfloat *line_data, + gint scanline, + gint empty_segs[], + gint max_empty, + gint *num_empty, + GimpBoundaryType type, + gint x1, + gint y1, + gint x2, + gint y2, + gfloat threshold) +{ + gint start = 0; + gint end = 0; + gint endx = 0; + gint last = -1; + gint l_num_empty; + gint x; + + *num_empty = 0; + + if (scanline < region->y || scanline >= (region->y + region->height)) + { + empty_segs[(*num_empty)++] = 0; + empty_segs[(*num_empty)++] = G_MAXINT; + return; + } + + if (type == GIMP_BOUNDARY_WITHIN_BOUNDS) + { + if (scanline < y1 || scanline >= y2) + { + empty_segs[(*num_empty)++] = 0; + empty_segs[(*num_empty)++] = G_MAXINT; + return; + } + + start = x1; + end = x2; + } + else if (type == GIMP_BOUNDARY_IGNORE_BOUNDS) + { + start = region->x; + end = region->x + region->width; + if (scanline < y1 || scanline >= y2) + x2 = -1; + } + + empty_segs[(*num_empty)++] = 0; + + l_num_empty = *num_empty; + + endx = end; + + line_data += start; + + for (x = start; x < end;) + { + if (type == GIMP_BOUNDARY_IGNORE_BOUNDS && (endx > x1 || x < x2)) + { + for (; x < endx; x++) + { + gint val; + + if (*line_data > threshold) + { + if (x >= x1 && x < x2) + val = -1; + else + val = 1; + } + else + { + val = -1; + } + + line_data++; + + if (last != val) + empty_segs[l_num_empty++] = x; + + last = val; + } + } + else + { + for (; x < endx; x++) + { + gint val; + + if (*line_data > threshold) + val = 1; + else + val = -1; + + line_data++; + + if (last != val) + empty_segs[l_num_empty++] = x; + + last = val; + } + } + } + + *num_empty = l_num_empty; + + if (last > 0) + empty_segs[(*num_empty)++] = x; + + empty_segs[(*num_empty)++] = G_MAXINT; +} + +static void +process_horiz_seg (GimpBoundary *boundary, + gint x1, + gint y1, + gint x2, + gint y2, + gboolean open) +{ + /* This procedure accounts for any vertical segments that must be + drawn to close in the horizontal segments. */ + + if (boundary->vert_segs[x1] >= 0) + { + gimp_boundary_add_seg (boundary, x1, boundary->vert_segs[x1], x1, y1, !open); + boundary->vert_segs[x1] = -1; + } + else + boundary->vert_segs[x1] = y1; + + if (boundary->vert_segs[x2] >= 0) + { + gimp_boundary_add_seg (boundary, x2, boundary->vert_segs[x2], x2, y2, open); + boundary->vert_segs[x2] = -1; + } + else + boundary->vert_segs[x2] = y2; + + gimp_boundary_add_seg (boundary, x1, y1, x2, y2, open); +} + +static void +make_horiz_segs (GimpBoundary *boundary, + gint start, + gint end, + gint scanline, + gint empty[], + gint num_empty, + gint top) +{ + gint empty_index; + gint e_s, e_e; /* empty segment start and end values */ + + for (empty_index = 0; empty_index < num_empty; empty_index += 2) + { + e_s = *empty++; + e_e = *empty++; + + if (e_s <= start && e_e >= end) + { + process_horiz_seg (boundary, + start, scanline, end, scanline, top); + } + else if ((e_s > start && e_s < end) || + (e_e < end && e_e > start)) + { + process_horiz_seg (boundary, + MAX (e_s, start), scanline, + MIN (e_e, end), scanline, top); + } + } +} + +static GimpBoundary * +generate_boundary (GeglBuffer *buffer, + const GeglRectangle *region, + const Babl *format, + GimpBoundaryType type, + gint x1, + gint y1, + gint x2, + gint y2, + gfloat threshold) +{ + GimpBoundary *boundary; + GeglRectangle line_rect = { 0, }; + gfloat *line_data; + gint scanline; + gint i; + gint start, end; + gint *tmp_segs; + + gint num_empty_n = 0; + gint num_empty_c = 0; + gint num_empty_l = 0; + + boundary = gimp_boundary_new (region); + + line_rect.width = gegl_buffer_get_width (buffer); + line_rect.height = 1; + + line_data = g_alloca (sizeof (gfloat) * line_rect.width); + + start = 0; + end = 0; + + if (type == GIMP_BOUNDARY_WITHIN_BOUNDS) + { + start = y1; + end = y2; + } + else if (type == GIMP_BOUNDARY_IGNORE_BOUNDS) + { + start = region->y; + end = region->y + region->height; + } + + /* Find the empty segments for the previous and current scanlines */ + find_empty_segs (region, NULL, + start - 1, boundary->empty_segs_l, + boundary->max_empty_segs, &num_empty_l, + type, x1, y1, x2, y2, + threshold); + + line_rect.y = start; + gegl_buffer_get (buffer, &line_rect, 1.0, format, + line_data, GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + + find_empty_segs (region, line_data, + start, boundary->empty_segs_c, + boundary->max_empty_segs, &num_empty_c, + type, x1, y1, x2, y2, + threshold); + + for (scanline = start; scanline < end; scanline++) + { + /* find the empty segment list for the next scanline */ + line_rect.y = scanline + 1; + if (scanline + 1 == end) + line_data = NULL; + else + gegl_buffer_get (buffer, &line_rect, 1.0, format, + line_data, GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + + find_empty_segs (region, line_data, + scanline + 1, boundary->empty_segs_n, + boundary->max_empty_segs, &num_empty_n, + type, x1, y1, x2, y2, + threshold); + + /* process the segments on the current scanline */ + for (i = 1; i < num_empty_c - 1; i += 2) + { + make_horiz_segs (boundary, + boundary->empty_segs_c [i], + boundary->empty_segs_c [i+1], + scanline, + boundary->empty_segs_l, num_empty_l, 1); + make_horiz_segs (boundary, + boundary->empty_segs_c [i], + boundary->empty_segs_c [i+1], + scanline + 1, + boundary->empty_segs_n, num_empty_n, 0); + } + + /* get the next scanline of empty segments, swap others */ + tmp_segs = boundary->empty_segs_l; + boundary->empty_segs_l = boundary->empty_segs_c; + num_empty_l = num_empty_c; + boundary->empty_segs_c = boundary->empty_segs_n; + num_empty_c = num_empty_n; + boundary->empty_segs_n = tmp_segs; + } + + return boundary; +} + +/* sorting utility functions */ + +static inline gint +cmp_xy (const gint ax, + const gint ay, + const gint bx, + const gint by) +{ + if (ay < by) + { + return -1; + } + else if (ay > by) + { + return 1; + } + else if (ax < bx) + { + return -1; + } + else if (ax > bx) + { + return 1; + } + else + { + return 0; + } +} + + +/* + * Compares (x1, y1) pairs in specified segments, using their addresses if + * (x1, y1) pairs are equal. + */ +static gint +cmp_segptr_xy1_addr (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b) +{ + const GimpBoundSeg *seg_a = *seg_ptr_a; + const GimpBoundSeg *seg_b = *seg_ptr_b; + + gint result = cmp_xy (seg_a->x1, seg_a->y1, seg_b->x1, seg_b->y1); + + if (result == 0) + { + if (seg_a < seg_b) + result = -1; + else if (seg_a > seg_b) + result = 1; + } + + return result; +} + +/* + * Compares (x2, y2) pairs in specified segments, using their addresses if + * (x2, y2) pairs are equal. + */ +static gint +cmp_segptr_xy2_addr (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b) +{ + const GimpBoundSeg *seg_a = *seg_ptr_a; + const GimpBoundSeg *seg_b = *seg_ptr_b; + + gint result = cmp_xy (seg_a->x2, seg_a->y2, seg_b->x2, seg_b->y2); + + if (result == 0) + { + if (seg_a < seg_b) + result = -1; + else if (seg_a > seg_b) + result = 1; + } + + return result; +} + + +/* + * Compares (x1, y1) pairs in specified segments. + */ +static gint +cmp_segptr_xy1 (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b) +{ + const GimpBoundSeg *seg_a = *seg_ptr_a, *seg_b = *seg_ptr_b; + + return cmp_xy (seg_a->x1, seg_a->y1, seg_b->x1, seg_b->y1); +} + +/* + * Compares (x2, y2) pairs in specified segments. + */ +static gint +cmp_segptr_xy2 (const GimpBoundSeg **seg_ptr_a, + const GimpBoundSeg **seg_ptr_b) +{ + const GimpBoundSeg *seg_a = *seg_ptr_a; + const GimpBoundSeg *seg_b = *seg_ptr_b; + + return cmp_xy (seg_a->x2, seg_a->y2, seg_b->x2, seg_b->y2); +} + + +static const GimpBoundSeg * +find_segment (const GimpBoundSeg **segs_by_xy1, + const GimpBoundSeg **segs_by_xy2, + gint num_segs, + gint x, + gint y) +{ + const GimpBoundSeg *segptr_xy1; + const GimpBoundSeg *segptr_xy2; + GimpBoundSeg search_seg; + + search_seg.x1 = search_seg.x2 = x; + search_seg.y1 = search_seg.y2 = y; + + segptr_xy1 = find_segment_with_func (segs_by_xy1, num_segs, &search_seg, + (GCompareFunc) cmp_segptr_xy1); + segptr_xy2 = find_segment_with_func (segs_by_xy2, num_segs, &search_seg, + (GCompareFunc) cmp_segptr_xy2); + + /* return segment with smaller address */ + if (segptr_xy1 != NULL && segptr_xy2 != NULL) + return MIN(segptr_xy1, segptr_xy2); + else if (segptr_xy1 != NULL) + return segptr_xy1; + else if (segptr_xy2 != NULL) + return segptr_xy2; + + return NULL; +} + + +static const GimpBoundSeg * +find_segment_with_func (const GimpBoundSeg **segs, + gint num_segs, + const GimpBoundSeg *search_seg, + GCompareFunc cmp_func) +{ + const GimpBoundSeg **seg; + const GimpBoundSeg *found_seg = NULL; + + seg = bsearch (&search_seg, segs, num_segs, sizeof (GimpBoundSeg *), cmp_func); + + if (seg != NULL) + { + /* find first matching segment */ + while (seg > segs && cmp_func (seg - 1, &search_seg) == 0) + seg--; + + /* find first non-visited segment */ + while (seg != segs + num_segs && cmp_func (seg, &search_seg) == 0) + if (!(*seg)->visited) + { + found_seg = *seg; + break; + } + else + seg++; + } + + return found_seg; +} + + +/* simplifying utility functions */ + +static void +simplify_subdivide (const GimpBoundSeg *segs, + gint start_idx, + gint end_idx, + GArray **ret_points) +{ + gint maxdist_idx; + gint maxdist; + gint threshold; + gint i, dx, dy; + + if (end_idx - start_idx < 2) + { + *ret_points = g_array_append_val (*ret_points, start_idx); + return; + } + + maxdist = 0; + + if (segs[start_idx].x1 == segs[end_idx].x1 && + segs[start_idx].y1 == segs[end_idx].y1) + { + /* start and endpoint are at the same coordinates */ + for (i = start_idx + 1; i < end_idx; i++) + { + /* compare the sqared distances */ + gint dist = (SQR (segs[i].x1 - segs[start_idx].x1) + + SQR (segs[i].y1 - segs[start_idx].y1)); + + if (dist > maxdist) + { + maxdist = dist; + } + } + + threshold = 1; + } + else + { + dx = segs[end_idx].x1 - segs[start_idx].x1; + dy = segs[end_idx].y1 - segs[start_idx].y1; + + for (i = start_idx + 1; i < end_idx; i++) + { + /* this is not really the euclidic distance, but is + * proportional for this part of the line + * (for the real distance we'd have to divide by + * (SQR(dx)+SQR(dy))) + */ + gint dist = abs (dx * (segs[start_idx].y1 - segs[i].y1) - + dy * (segs[start_idx].x1 - segs[i].x1)); + + if (dist > maxdist) + { + maxdist = dist; + } + } + + /* threshold is chosen to catch 45 degree stairs */ + threshold = SQR (dx) + SQR (dy); + } + + if (maxdist <= threshold) + { + *ret_points = g_array_append_val (*ret_points, start_idx); + return; + } + + /* Simons hack */ + maxdist_idx = (start_idx + end_idx) / 2; + + simplify_subdivide (segs, start_idx, maxdist_idx, ret_points); + simplify_subdivide (segs, maxdist_idx, end_idx, ret_points); +} diff --git a/app/core/gimpboundary.h b/app/core/gimpboundary.h new file mode 100644 index 0000000..1e05eec --- /dev/null +++ b/app/core/gimpboundary.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 . + */ + +#ifndef __GIMP_BOUNDARY_H__ +#define __GIMP_BOUNDARY_H__ + + +/* half intensity for mask */ +#define GIMP_BOUNDARY_HALF_WAY 0.5 + + +typedef enum +{ + GIMP_BOUNDARY_WITHIN_BOUNDS, + GIMP_BOUNDARY_IGNORE_BOUNDS +} GimpBoundaryType; + + +struct _GimpBoundSeg +{ + gint x1; + gint y1; + gint x2; + gint y2; + guint open : 1; + guint visited : 1; +}; + + +GimpBoundSeg * gimp_boundary_find (GeglBuffer *buffer, + const GeglRectangle *region, + const Babl *format, + GimpBoundaryType type, + gint x1, + gint y1, + gint x2, + gint y2, + gfloat threshold, + gint *num_segs); +GimpBoundSeg * gimp_boundary_sort (const GimpBoundSeg *segs, + gint num_segs, + gint *num_groups); +GimpBoundSeg * gimp_boundary_simplify (GimpBoundSeg *sorted_segs, + gint num_groups, + gint *num_segs); + +/* offsets in-place */ +void gimp_boundary_offset (GimpBoundSeg *segs, + gint num_segs, + gint off_x, + gint off_y); + + +#endif /* __GIMP_BOUNDARY_H__ */ diff --git a/app/core/gimpbrush-boundary.c b/app/core/gimpbrush-boundary.c new file mode 100644 index 0000000..9e5cbaa --- /dev/null +++ b/app/core/gimpbrush-boundary.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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "core-types.h" + +#include "gimpbezierdesc.h" +#include "gimpboundary.h" +#include "gimpbrush.h" +#include "gimpbrush-boundary.h" +#include "gimptempbuf.h" + + +static GimpBezierDesc * +gimp_brush_transform_boundary_exact (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + const GimpTempBuf *mask; + + mask = gimp_brush_transform_mask (brush, + scale, aspect_ratio, + angle, reflect, hardness); + + if (mask) + { + GeglBuffer *buffer; + GimpBoundSeg *bound_segs; + gint n_bound_segs; + + buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask); + + bound_segs = gimp_boundary_find (buffer, NULL, + babl_format ("Y float"), + GIMP_BOUNDARY_WITHIN_BOUNDS, + 0, 0, + gegl_buffer_get_width (buffer), + gegl_buffer_get_height (buffer), + 0.0, + &n_bound_segs); + + g_object_unref (buffer); + + if (bound_segs) + { + GimpBoundSeg *stroke_segs; + gint n_stroke_groups; + + stroke_segs = gimp_boundary_sort (bound_segs, n_bound_segs, + &n_stroke_groups); + + g_free (bound_segs); + + if (stroke_segs) + { + GimpBezierDesc *path; + + path = gimp_bezier_desc_new_from_bound_segs (stroke_segs, + n_bound_segs, + n_stroke_groups); + + g_free (stroke_segs); + + return path; + } + } + } + + return NULL; +} + +static GimpBezierDesc * +gimp_brush_transform_boundary_approx (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + return gimp_brush_transform_boundary_exact (brush, + scale, aspect_ratio, + angle, reflect, hardness); +} + +GimpBezierDesc * +gimp_brush_real_transform_boundary (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness, + gint *width, + gint *height) +{ + gimp_brush_transform_size (brush, scale, aspect_ratio, angle, reflect, + width, height); + + if (*width < 256 && *height < 256) + { + return gimp_brush_transform_boundary_exact (brush, + scale, aspect_ratio, + angle, reflect, hardness); + } + + return gimp_brush_transform_boundary_approx (brush, + scale, aspect_ratio, + angle, reflect, hardness); +} diff --git a/app/core/gimpbrush-boundary.h b/app/core/gimpbrush-boundary.h new file mode 100644 index 0000000..0bc99db --- /dev/null +++ b/app/core/gimpbrush-boundary.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 . + */ + +#ifndef __GIMP_BRUSH_BOUNDARY_H__ +#define __GIMP_BRUSH_BOUNDARY_H__ + + +GimpBezierDesc * gimp_brush_real_transform_boundary (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness, + gint *width, + gint *height); + + +#endif /* __GIMP_BRUSH_BOUNDARY_H__ */ diff --git a/app/core/gimpbrush-header.h b/app/core/gimpbrush-header.h new file mode 100644 index 0000000..0e66715 --- /dev/null +++ b/app/core/gimpbrush-header.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 . + */ + +#ifndef __GIMP_BRUSH_HEADER_H__ +#define __GIMP_BRUSH_HEADER_H__ + + +#define GIMP_BRUSH_MAGIC (('G' << 24) + ('I' << 16) + \ + ('M' << 8) + ('P' << 0)) +#define GIMP_BRUSH_MAX_SIZE 10000 /* Max size in either dimension in px */ +#define GIMP_BRUSH_MAX_NAME 256 /* Max length of the brush's name */ + + +/* All field entries are MSB */ + +typedef struct _GimpBrushHeader GimpBrushHeader; + +struct _GimpBrushHeader +{ + guint32 header_size; /* = sizeof (GimpBrushHeader) + brush name */ + guint32 version; /* brush file version # */ + guint32 width; /* width of brush */ + guint32 height; /* height of brush */ + guint32 bytes; /* depth of brush in bytes */ + guint32 magic_number; /* GIMP brush magic number */ + guint32 spacing; /* brush spacing */ +}; + +/* In a brush file, next comes the brush name, null-terminated. + * After that comes the brush data -- width * height * bytes bytes of + * it... + */ + + +#endif /* __GIMP_BRUSH_HEADER_H__ */ diff --git a/app/core/gimpbrush-load.c b/app/core/gimpbrush-load.c new file mode 100644 index 0000000..51bf1da --- /dev/null +++ b/app/core/gimpbrush-load.c @@ -0,0 +1,1213 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrush-load.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpbrush.h" +#include "gimpbrush-header.h" +#include "gimpbrush-load.h" +#include "gimpbrush-private.h" +#include "gimppattern-header.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + +/* stuff from abr2gbr Copyright (C) 2001 Marco Lamberto */ +/* the above is GPL see http://the.sunnyspot.org/gimp/ */ + +typedef struct _AbrHeader AbrHeader; +typedef struct _AbrBrushHeader AbrBrushHeader; +typedef struct _AbrSampledBrushHeader AbrSampledBrushHeader; + +struct _AbrHeader +{ + gint16 version; + gint16 count; +}; + +struct _AbrBrushHeader +{ + gint16 type; + gint32 size; +}; + +struct _AbrSampledBrushHeader +{ + gint32 misc; + gint16 spacing; + gchar antialiasing; + gint16 bounds[4]; + gint32 bounds_long[4]; + gint16 depth; + gboolean wide; +}; + + +/* local function prototypes */ + +static GList * gimp_brush_load_abr_v12 (GDataInputStream *input, + AbrHeader *abr_hdr, + GFile *file, + GError **error); +static GList * gimp_brush_load_abr_v6 (GDataInputStream *input, + AbrHeader *abr_hdr, + GFile *file, + GError **error); +static GimpBrush * gimp_brush_load_abr_brush_v12 (GDataInputStream *input, + AbrHeader *abr_hdr, + gint index, + GFile *file, + GError **error); +static GimpBrush * gimp_brush_load_abr_brush_v6 (GDataInputStream *input, + AbrHeader *abr_hdr, + gint32 max_offset, + gint index, + GFile *file, + GError **error); + +static gchar abr_read_char (GDataInputStream *input, + GError **error); +static gint16 abr_read_short (GDataInputStream *input, + GError **error); +static gint32 abr_read_long (GDataInputStream *input, + GError **error); +static gchar * abr_read_ucs2_text (GDataInputStream *input, + GError **error); +static gboolean abr_supported (AbrHeader *abr_hdr, + GError **error); +static gboolean abr_reach_8bim_section (GDataInputStream *input, + const gchar *name, + GError **error); +static gboolean abr_rle_decode (GDataInputStream *input, + gchar *buffer, + gsize buffer_size, + gint32 height, + GError **error); + + +/* public functions */ + +GList * +gimp_brush_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpBrush *brush; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + brush = gimp_brush_load_brush (context, file, input, error); + if (! brush) + return NULL; + + return g_list_prepend (NULL, brush); +} + +GimpBrush * +gimp_brush_load_brush (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpBrush *brush; + gsize bn_size; + GimpBrushHeader header; + gchar *name = NULL; + guchar *mask; + gsize bytes_read; + gssize i, size; + gboolean success = TRUE; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* read the header */ + if (! g_input_stream_read_all (input, &header, sizeof (header), + &bytes_read, NULL, error) || + bytes_read != sizeof (header)) + { + return NULL; + } + + /* rearrange the bytes in each unsigned int */ + header.header_size = g_ntohl (header.header_size); + header.version = g_ntohl (header.version); + header.width = g_ntohl (header.width); + header.height = g_ntohl (header.height); + header.bytes = g_ntohl (header.bytes); + header.magic_number = g_ntohl (header.magic_number); + header.spacing = g_ntohl (header.spacing); + + /* Check for correct file format */ + + if (header.width == 0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: Width = 0.")); + return NULL; + } + + if (header.height == 0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: Height = 0.")); + return NULL; + } + + if (header.bytes == 0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: Bytes = 0.")); + return NULL; + } + + if (header.width > GIMP_BRUSH_MAX_SIZE || + header.height > GIMP_BRUSH_MAX_SIZE || + G_MAXSIZE / header.width / header.height / MAX (4, header.bytes) < 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: %dx%d over max size."), + header.width, header.height); + return NULL; + } + + switch (header.version) + { + case 1: + /* If this is a version 1 brush, set the fp back 8 bytes */ + if (! g_seekable_seek (G_SEEKABLE (input), -8, G_SEEK_CUR, + NULL, error)) + return NULL; + + header.header_size += 8; + /* spacing is not defined in version 1 */ + header.spacing = 25; + break; + + case 3: /* cinepaint brush */ + if (header.bytes == 18 /* FLOAT16_GRAY_GIMAGE */) + { + header.bytes = 2; + } + else + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: Unknown depth %d."), + header.bytes); + return NULL; + } + /* fallthrough */ + + case 2: + if (header.magic_number == GIMP_BRUSH_MAGIC) + break; + + default: + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: Unknown version %d."), + header.version); + return NULL; + } + + if (header.header_size < sizeof (GimpBrushHeader)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unsupported brush format")); + return NULL; + } + + /* Read in the brush name */ + if ((bn_size = (header.header_size - sizeof (header)))) + { + gchar *utf8; + + if (bn_size > GIMP_BRUSH_MAX_NAME) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid header data in '%s': " + "Brush name is too long: %lu"), + gimp_file_get_utf8_name (file), + (gulong) bn_size); + return NULL; + } + + name = g_new0 (gchar, bn_size + 1); + + if (! g_input_stream_read_all (input, name, bn_size, + &bytes_read, NULL, error) || + bytes_read != bn_size) + { + g_free (name); + return NULL; + } + + utf8 = gimp_any_to_utf8 (name, bn_size - 1, + _("Invalid UTF-8 string in brush file '%s'."), + gimp_file_get_utf8_name (file)); + g_free (name); + name = utf8; + } + + if (! name) + name = g_strdup (_("Unnamed")); + + brush = g_object_new (GIMP_TYPE_BRUSH, + "name", name, + "mime-type", "image/x-gimp-gbr", + NULL); + g_free (name); + + brush->priv->mask = gimp_temp_buf_new (header.width, header.height, + babl_format ("Y u8")); + + mask = gimp_temp_buf_get_data (brush->priv->mask); + size = header.width * header.height * header.bytes; + + switch (header.bytes) + { + case 1: + success = (g_input_stream_read_all (input, mask, size, + &bytes_read, NULL, error) && + bytes_read == size); + + /* For backwards-compatibility, check if a pattern follows. + * The obsolete .gpb format did it this way. + */ + if (success) + { + GimpPatternHeader ph; + goffset rewind; + + rewind = g_seekable_tell (G_SEEKABLE (input)); + + if (g_input_stream_read_all (input, &ph, sizeof (GimpPatternHeader), + &bytes_read, NULL, NULL) && + bytes_read == sizeof (GimpPatternHeader)) + { + /* rearrange the bytes in each unsigned int */ + ph.header_size = g_ntohl (ph.header_size); + ph.version = g_ntohl (ph.version); + ph.width = g_ntohl (ph.width); + ph.height = g_ntohl (ph.height); + ph.bytes = g_ntohl (ph.bytes); + ph.magic_number = g_ntohl (ph.magic_number); + + if (ph.magic_number == GIMP_PATTERN_MAGIC && + ph.version == 1 && + ph.header_size > sizeof (GimpPatternHeader) && + ph.bytes == 3 && + ph.width == header.width && + ph.height == header.height && + g_input_stream_skip (input, + ph.header_size - + sizeof (GimpPatternHeader), + NULL, NULL) == + ph.header_size - sizeof (GimpPatternHeader)) + { + guchar *pixmap; + gssize pixmap_size; + + brush->priv->pixmap = + gimp_temp_buf_new (header.width, header.height, + babl_format ("R'G'B' u8")); + + pixmap = gimp_temp_buf_get_data (brush->priv->pixmap); + + pixmap_size = gimp_temp_buf_get_data_size (brush->priv->pixmap); + + success = (g_input_stream_read_all (input, pixmap, + pixmap_size, + &bytes_read, NULL, + error) && + bytes_read == pixmap_size); + } + else + { + /* seek back if pattern wasn't found */ + success = g_seekable_seek (G_SEEKABLE (input), + rewind, G_SEEK_SET, + NULL, error); + } + } + } + break; + + case 2: /* cinepaint brush, 16 bit floats */ + { + guchar buf[8 * 1024]; + + for (i = 0; success && i < size;) + { + gssize bytes = MIN (size - i, sizeof (buf)); + + success = (g_input_stream_read_all (input, buf, bytes, + &bytes_read, NULL, error) && + bytes_read == bytes); + + if (success) + { + guint16 *b = (guint16 *) buf; + + i += bytes; + + for (; bytes > 0; bytes -= 2, mask++, b++) + { + union + { + guint16 u[2]; + gfloat f; + } short_float; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + short_float.u[0] = 0; + short_float.u[1] = GUINT16_FROM_BE (*b); +#else + short_float.u[0] = GUINT16_FROM_BE (*b); + short_float.u[1] = 0; +#endif + + *mask = (guchar) (short_float.f * 255.0 + 0.5); + } + } + } + } + break; + + case 4: + { + guchar *pixmap; + guchar buf[8 * 1024]; + + brush->priv->pixmap = gimp_temp_buf_new (header.width, header.height, + babl_format ("R'G'B' u8")); + pixmap = gimp_temp_buf_get_data (brush->priv->pixmap); + + for (i = 0; success && i < size;) + { + gssize bytes = MIN (size - i, sizeof (buf)); + + success = (g_input_stream_read_all (input, buf, bytes, + &bytes_read, NULL, error) && + bytes_read == bytes); + + if (success) + { + guchar *b = buf; + + i += bytes; + + for (; bytes > 0; bytes -= 4, pixmap += 3, mask++, b += 4) + { + pixmap[0] = b[0]; + pixmap[1] = b[1]; + pixmap[2] = b[2]; + + mask[0] = b[3]; + } + } + } + } + break; + + default: + g_object_unref (brush); + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file:\n" + "Unsupported brush depth %d\n" + "GIMP brushes must be GRAY or RGBA."), + header.bytes); + return NULL; + } + + if (! success) + { + g_object_unref (brush); + return NULL; + } + + brush->priv->spacing = header.spacing; + brush->priv->x_axis.x = header.width / 2.0; + brush->priv->x_axis.y = 0.0; + brush->priv->y_axis.x = 0.0; + brush->priv->y_axis.y = header.height / 2.0; + + return brush; +} + +GList * +gimp_brush_load_abr (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GDataInputStream *data_input; + AbrHeader abr_hdr; + GList *brush_list = NULL; + GError *my_error = NULL; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data_input = g_data_input_stream_new (input); + + g_data_input_stream_set_byte_order (data_input, + G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + + abr_hdr.version = abr_read_short (data_input, &my_error); + if (my_error) + goto done; + + /* sub-version for ABR v6 */ + abr_hdr.count = abr_read_short (data_input, &my_error); + if (my_error) + goto done; + + if (abr_supported (&abr_hdr, &my_error)) + { + switch (abr_hdr.version) + { + case 1: + case 2: + brush_list = gimp_brush_load_abr_v12 (data_input, &abr_hdr, + file, &my_error); + break; + + case 10: + case 6: + brush_list = gimp_brush_load_abr_v6 (data_input, &abr_hdr, + file, &my_error); + break; + } + } + + done: + + g_object_unref (data_input); + + if (! brush_list) + { + if (! my_error) + g_set_error (&my_error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unable to decode abr format version %d."), + abr_hdr.version); + } + + if (my_error) + g_propagate_error (error, my_error); + + return g_list_reverse (brush_list); +} + + +/* private functions */ + +static GList * +gimp_brush_load_abr_v12 (GDataInputStream *input, + AbrHeader *abr_hdr, + GFile *file, + GError **error) +{ + GList *brush_list = NULL; + gint i; + + for (i = 0; i < abr_hdr->count; i++) + { + GimpBrush *brush; + GError *my_error = NULL; + + brush = gimp_brush_load_abr_brush_v12 (input, abr_hdr, i, + file, &my_error); + + /* a NULL brush without an error means an unsupported brush + * type was encountered, silently skip it and try the next one + */ + + if (brush) + { + brush_list = g_list_prepend (brush_list, brush); + } + else if (my_error) + { + g_propagate_error (error, my_error); + break; + } + } + + return brush_list; +} + +static GList * +gimp_brush_load_abr_v6 (GDataInputStream *input, + AbrHeader *abr_hdr, + GFile *file, + GError **error) +{ + GList *brush_list = NULL; + gint32 sample_section_size; + goffset sample_section_end; + gint i = 1; + + if (! abr_reach_8bim_section (input, "samp", error)) + return brush_list; + + sample_section_size = abr_read_long (input, error); + if (error && *error) + return brush_list; + + sample_section_end = (sample_section_size + + g_seekable_tell (G_SEEKABLE (input))); + + while (g_seekable_tell (G_SEEKABLE (input)) < sample_section_end) + { + GimpBrush *brush; + GError *my_error = NULL; + + brush = gimp_brush_load_abr_brush_v6 (input, abr_hdr, sample_section_end, + i, file, &my_error); + + /* a NULL brush without an error means an unsupported brush + * type was encountered, silently skip it and try the next one + */ + + if (brush) + { + brush_list = g_list_prepend (brush_list, brush); + } + else if (my_error) + { + g_propagate_error (error, my_error); + break; + } + + i++; + } + + return brush_list; +} + +static GimpBrush * +gimp_brush_load_abr_brush_v12 (GDataInputStream *input, + AbrHeader *abr_hdr, + gint index, + GFile *file, + GError **error) +{ + GimpBrush *brush = NULL; + AbrBrushHeader abr_brush_hdr; + + abr_brush_hdr.type = abr_read_short (input, error); + if (error && *error) + return NULL; + + abr_brush_hdr.size = abr_read_long (input, error); + if (error && *error) + return NULL; + + if (abr_brush_hdr.size < 0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Brush size value corrupt.")); + return NULL; + } + + /* g_print(" + BRUSH\n | << type: %i block size: %i bytes\n", + * abr_brush_hdr.type, abr_brush_hdr.size); + */ + + switch (abr_brush_hdr.type) + { + case 1: /* computed brush */ + /* FIXME: support it! + * + * We can probabaly feed the info into the generated brush code + * and get a usable brush back. It seems to support the same + * types -akl + */ + g_printerr ("WARNING: computed brush unsupported, skipping.\n"); + g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size, + G_SEEK_CUR, NULL, NULL); + break; + + case 2: /* sampled brush */ + { + AbrSampledBrushHeader abr_sampled_brush_hdr; + gint width, height; + gint bytes; + gint size; + guchar *mask; + gint i; + gchar *name; + gchar *sample_name = NULL; + gchar *tmp; + gshort compress; + + abr_sampled_brush_hdr.misc = abr_read_long (input, error); + if (error && *error) + break; + + abr_sampled_brush_hdr.spacing = abr_read_short (input, error); + if (error && *error) + break; + + if (abr_hdr->version == 2) + { + sample_name = abr_read_ucs2_text (input, error); + if (error && *error) + break; + } + + abr_sampled_brush_hdr.antialiasing = abr_read_char (input, error); + if (error && *error) + break; + + for (i = 0; i < 4; i++) + { + abr_sampled_brush_hdr.bounds[i] = abr_read_short (input, error); + if (error && *error) + break; + } + + for (i = 0; i < 4; i++) + { + abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (input, error); + if (error && *error) + break; + } + + abr_sampled_brush_hdr.depth = abr_read_short (input, error); + if (error && *error) + break; + + height = (abr_sampled_brush_hdr.bounds_long[2] - + abr_sampled_brush_hdr.bounds_long[0]); /* bottom - top */ + width = (abr_sampled_brush_hdr.bounds_long[3] - + abr_sampled_brush_hdr.bounds_long[1]); /* right - left */ + bytes = abr_sampled_brush_hdr.depth >> 3; + + /* g_print ("width %i height %i bytes %i\n", width, height, bytes); */ + + if (width < 1 || width > 10000 || + height < 1 || height > 10000 || + bytes < 1 || bytes > 1 || + G_MAXSIZE / width / height / bytes < 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Brush dimensions out of range.")); + break; + } + + abr_sampled_brush_hdr.wide = height > 16384; + + if (abr_sampled_brush_hdr.wide) + { + /* FIXME: support wide brushes */ + + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Wide brushes are not supported.")); + break; + } + + tmp = g_path_get_basename (gimp_file_get_utf8_name (file)); + if (! sample_name) + { + /* build name from filename and index */ + name = g_strdup_printf ("%s-%03d", tmp, index); + } + else + { + /* build name from filename and sample name */ + name = g_strdup_printf ("%s-%s", tmp, sample_name); + g_free (sample_name); + } + g_free (tmp); + + brush = g_object_new (GIMP_TYPE_BRUSH, + "name", name, + /* FIXME: MIME type!! */ + "mime-type", "application/x-photoshop-abr", + NULL); + + g_free (name); + + brush->priv->spacing = abr_sampled_brush_hdr.spacing; + brush->priv->x_axis.x = width / 2.0; + brush->priv->x_axis.y = 0.0; + brush->priv->y_axis.x = 0.0; + brush->priv->y_axis.y = height / 2.0; + brush->priv->mask = gimp_temp_buf_new (width, height, + babl_format ("Y u8")); + + mask = gimp_temp_buf_get_data (brush->priv->mask); + size = width * height * bytes; + + compress = abr_read_char (input, error); + if (error && *error) + { + g_object_unref (brush); + brush = NULL; + break; + } + + /* g_print(" | << size: %dx%d %d bit (%d bytes) %s\n", + * width, height, abr_sampled_brush_hdr.depth, size, + * comppres ? "compressed" : "raw"); + */ + + if (! compress) + { + gsize bytes_read; + + if (! g_input_stream_read_all (G_INPUT_STREAM (input), + mask, size, + &bytes_read, NULL, error) || + bytes_read != size) + { + g_object_unref (brush); + brush = NULL; + break; + } + } + else + { + if (! abr_rle_decode (input, (gchar *) mask, size, height, error)) + { + g_object_unref (brush); + brush = NULL; + break; + } + } + } + break; + + default: + g_printerr ("WARNING: unknown brush type, skipping.\n"); + g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size, + G_SEEK_CUR, NULL, NULL); + break; + } + + return brush; +} + +static GimpBrush * +gimp_brush_load_abr_brush_v6 (GDataInputStream *input, + AbrHeader *abr_hdr, + gint32 max_offset, + gint index, + GFile *file, + GError **error) +{ + GimpBrush *brush = NULL; + guchar *mask; + + gint32 brush_size; + gint32 brush_end; + goffset next_brush; + + gint32 top, left, bottom, right; + gint16 depth; + gchar compress; + + gint32 width, height; + gint32 size; + + gchar *tmp; + gchar *name; + gboolean r; + + brush_size = abr_read_long (input, error); + if (error && *error) + return NULL; + + if (brush_size < 0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Brush size value corrupt.")); + return NULL; + } + + brush_end = brush_size; + + /* complement to 4 */ + while (brush_end % 4 != 0) + brush_end++; + + next_brush = (brush_end + g_seekable_tell (G_SEEKABLE (input))); + + if (abr_hdr->count == 1) + { + /* discard key and short coordinates and unknown short */ + r = g_seekable_seek (G_SEEKABLE (input), 47, G_SEEK_CUR, + NULL, error); + } + else + { + /* discard key and unknown bytes */ + r = g_seekable_seek (G_SEEKABLE (input), 301, G_SEEK_CUR, + NULL, error); + } + + if (! r) + { + g_prefix_error (error, + _("Fatal parse error in brush file: " + "File appears truncated: ")); + return NULL; + } + + top = abr_read_long (input, error); if (error && *error) return NULL; + left = abr_read_long (input, error); if (error && *error) return NULL; + bottom = abr_read_long (input, error); if (error && *error) return NULL; + right = abr_read_long (input, error); if (error && *error) return NULL; + depth = abr_read_short (input, error); if (error && *error) return NULL; + compress = abr_read_char (input, error); if (error && *error) return NULL; + + depth = depth >> 3; + + width = right - left; + height = bottom - top; + size = width * depth * height; + +#if 0 + g_printerr ("width %i height %i depth %i compress %i\n", + width, height, depth, compress); +#endif + + if (width < 1 || width > 10000 || + height < 1 || height > 10000 || + depth < 1 || depth > 1 || + G_MAXSIZE / width / height / depth < 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Brush dimensions out of range.")); + return NULL; + } + + if (compress < 0 || compress > 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Unknown compression method.")); + return NULL; + } + + tmp = g_path_get_basename (gimp_file_get_utf8_name (file)); + name = g_strdup_printf ("%s-%03d", tmp, index); + g_free (tmp); + + brush = g_object_new (GIMP_TYPE_BRUSH, + "name", name, + /* FIXME: MIME type!! */ + "mime-type", "application/x-photoshop-abr", + NULL); + + g_free (name); + + brush->priv->spacing = 25; /* real value needs 8BIMdesc section parser */ + brush->priv->x_axis.x = width / 2.0; + brush->priv->x_axis.y = 0.0; + brush->priv->y_axis.x = 0.0; + brush->priv->y_axis.y = height / 2.0; + brush->priv->mask = gimp_temp_buf_new (width, height, + babl_format ("Y u8")); + + mask = gimp_temp_buf_get_data (brush->priv->mask); + + /* data decoding */ + if (! compress) + { + /* not compressed - read raw bytes as brush data */ + gsize bytes_read; + + if (! g_input_stream_read_all (G_INPUT_STREAM (input), + mask, size, + &bytes_read, NULL, error) || + bytes_read != size) + { + g_object_unref (brush); + return NULL; + } + } + else + { + if (! abr_rle_decode (input, (gchar *) mask, size, height, error)) + { + g_object_unref (brush); + return NULL; + } + } + + if (g_seekable_tell (G_SEEKABLE (input)) <= next_brush) + g_seekable_seek (G_SEEKABLE (input), next_brush, G_SEEK_SET, + NULL, NULL); + + return brush; +} + +static gchar +abr_read_char (GDataInputStream *input, + GError **error) +{ + return g_data_input_stream_read_byte (input, NULL, error); +} + +static gint16 +abr_read_short (GDataInputStream *input, + GError **error) +{ + return g_data_input_stream_read_int16 (input, NULL, error); +} + +static gint32 +abr_read_long (GDataInputStream *input, + GError **error) +{ + return g_data_input_stream_read_int32 (input, NULL, error); +} + +static gchar * +abr_read_ucs2_text (GDataInputStream *input, + GError **error) +{ + gchar *name_ucs2; + gchar *name_utf8; + gint len; + gint i; + + /* two-bytes characters encoded (UCS-2) + * format: + * long : number of characters in string + * data : zero terminated UCS-2 string + */ + + len = 2 * abr_read_long (input, error); + if (len <= 0) + return NULL; + + name_ucs2 = g_new (gchar, len); + + for (i = 0; i < len; i++) + { + name_ucs2[i] = abr_read_char (input, error); + if (error && *error) + { + g_free (name_ucs2); + return NULL; + } + } + + name_utf8 = g_convert (name_ucs2, len, + "UTF-8", "UCS-2BE", + NULL, NULL, NULL); + + g_free (name_ucs2); + + return name_utf8; +} + +static gboolean +abr_supported (AbrHeader *abr_hdr, + GError **error) +{ + switch (abr_hdr->version) + { + case 1: + case 2: + return TRUE; + break; + + case 10: + case 6: + /* in this case, count contains format sub-version */ + if (abr_hdr->count == 1 || abr_hdr->count == 2) + return TRUE; + + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "Unable to decode abr format version %d."), + + /* horrid subversion display, but better than + * having yet another translatable string for + * this + */ + abr_hdr->version * 10 + abr_hdr->count); + break; + } + + return FALSE; +} + +static gboolean +abr_reach_8bim_section (GDataInputStream *input, + const gchar *name, + GError **error) +{ + while (TRUE) + { + gchar tag[4]; + gchar tagname[5]; + guint32 section_size; + gsize bytes_read; + + if (! g_input_stream_read_all (G_INPUT_STREAM (input), + tag, 4, + &bytes_read, NULL, error) || + bytes_read != 4) + return FALSE; + + if (strncmp (tag, "8BIM", 4)) + return FALSE; + + if (! g_input_stream_read_all (G_INPUT_STREAM (input), + tagname, 4, + &bytes_read, NULL, error) || + bytes_read != 4) + return FALSE; + + tagname[4] = '\0'; + + if (! strncmp (tagname, name, 4)) + return TRUE; + + section_size = abr_read_long (input, error); + if (error && *error) + return FALSE; + + if (! g_seekable_seek (G_SEEKABLE (input), section_size, G_SEEK_CUR, + NULL, error)) + return FALSE; + } + + return FALSE; +} + +static gboolean +abr_rle_decode (GDataInputStream *input, + gchar *buffer, + gsize buffer_size, + gint32 height, + GError **error) +{ + gint i, j; + gshort *cscanline_len = NULL; + gchar *cdata = NULL; + gchar *data = buffer; + + /* read compressed size foreach scanline */ + cscanline_len = gegl_scratch_new (gshort, height); + for (i = 0; i < height; i++) + { + cscanline_len[i] = abr_read_short (input, error); + if ((error && *error) || cscanline_len[i] <= 0) + goto err; + } + + /* unpack each scanline data */ + for (i = 0; i < height; i++) + { + gint len; + gsize bytes_read; + + len = cscanline_len[i]; + + cdata = gegl_scratch_alloc (len); + + if (! g_input_stream_read_all (G_INPUT_STREAM (input), + cdata, len, + &bytes_read, NULL, error) || + bytes_read != len) + { + goto err; + } + + for (j = 0; j < len;) + { + gint32 n = cdata[j++]; + + if (n >= 128) /* force sign */ + n -= 256; + + if (n < 0) + { + /* copy the following char -n + 1 times */ + + if (n == -128) /* it's a nop */ + continue; + + n = -n + 1; + + if (j + 1 > len || (data - buffer) + n > buffer_size) + goto err; + + memset (data, cdata[j], n); + + j += 1; + data += n; + } + else + { + /* read the following n + 1 chars (no compr) */ + + n = n + 1; + + if (j + n > len || (data - buffer) + n > buffer_size) + goto err; + + memcpy (data, &cdata[j], n); + + j += n; + data += n; + } + } + + g_clear_pointer (&cdata, gegl_scratch_free); + } + + g_clear_pointer (&cscanline_len, gegl_scratch_free); + + return TRUE; + +err: + g_clear_pointer (&cdata, gegl_scratch_free); + g_clear_pointer (&cscanline_len, gegl_scratch_free); + if (error && ! *error) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file: " + "RLE compressed brush data corrupt.")); + } + return FALSE; +} diff --git a/app/core/gimpbrush-load.h b/app/core/gimpbrush-load.h new file mode 100644 index 0000000..6c4f0af --- /dev/null +++ b/app/core/gimpbrush-load.h @@ -0,0 +1,43 @@ +/* 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 . + */ + +#ifndef __GIMP_BRUSH_LOAD_H__ +#define __GIMP_BRUSH_LOAD_H__ + + +#define GIMP_BRUSH_FILE_EXTENSION ".gbr" +#define GIMP_BRUSH_PIXMAP_FILE_EXTENSION ".gpb" +#define GIMP_BRUSH_PS_FILE_EXTENSION ".abr" +#define GIMP_BRUSH_PSP_FILE_EXTENSION ".jbr" + + +GList * gimp_brush_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GimpBrush * gimp_brush_load_brush (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + +GList * gimp_brush_load_abr (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_BRUSH_LOAD_H__ */ diff --git a/app/core/gimpbrush-mipmap.cc b/app/core/gimpbrush-mipmap.cc new file mode 100644 index 0000000..7c3b061 --- /dev/null +++ b/app/core/gimpbrush-mipmap.cc @@ -0,0 +1,514 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrush-mipmap.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +extern "C" +{ + +#include "core-types.h" + +#include "gimpbrush.h" +#include "gimpbrush-mipmap.h" +#include "gimpbrush-private.h" +#include "gimptempbuf.h" + +} /* extern "C" */ + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + +#define GIMP_BRUSH_MIPMAP(brush, mipmaps, x, y) \ + ((*(mipmaps))[(y) * (brush)->priv->n_horz_mipmaps + (x)]) + + +/* local function prototypes */ + +static void gimp_brush_mipmap_clear (GimpBrush *brush, + GimpTempBuf ***mipmaps); + +static const GimpTempBuf * gimp_brush_mipmap_get (GimpBrush *brush, + const GimpTempBuf *source, + GimpTempBuf ***mipmaps, + gdouble *scale_x, + gdouble *scale_y); + +static GimpTempBuf * gimp_brush_mipmap_downscale (const GimpTempBuf *source); +static GimpTempBuf * gimp_brush_mipmap_downscale_horz (const GimpTempBuf *source); +static GimpTempBuf * gimp_brush_mipmap_downscale_vert (const GimpTempBuf *source); + + +/* private functions */ + +static void +gimp_brush_mipmap_clear (GimpBrush *brush, + GimpTempBuf ***mipmaps) +{ + if (*mipmaps) + { + gint i; + + for (i = 0; + i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps; + i++) + { + g_clear_pointer (&(*mipmaps)[i], gimp_temp_buf_unref); + } + + g_clear_pointer (mipmaps, g_free); + } +} + +static const GimpTempBuf * +gimp_brush_mipmap_get (GimpBrush *brush, + const GimpTempBuf *source, + GimpTempBuf ***mipmaps, + gdouble *scale_x, + gdouble *scale_y) +{ + gint x; + gint y; + gint i; + + if (! source) + return NULL; + + if (! *mipmaps) + { + gint width = gimp_temp_buf_get_width (source); + gint height = gimp_temp_buf_get_height (source); + + brush->priv->n_horz_mipmaps = floor (log (width) / M_LN2) + 1; + brush->priv->n_vert_mipmaps = floor (log (height) / M_LN2) + 1; + + *mipmaps = g_new0 (GimpTempBuf *, brush->priv->n_horz_mipmaps * + brush->priv->n_vert_mipmaps); + + GIMP_BRUSH_MIPMAP (brush, mipmaps, 0, 0) = gimp_temp_buf_ref (source); + } + + x = floor (SAFE_CLAMP (log (1.0 / MAX (*scale_x, 0.0)) / M_LN2, + 0, brush->priv->n_horz_mipmaps - 1)); + y = floor (SAFE_CLAMP (log (1.0 / MAX (*scale_y, 0.0)) / M_LN2, + 0, brush->priv->n_vert_mipmaps - 1)); + + *scale_x *= pow (2.0, x); + *scale_y *= pow (2.0, y); + + if (GIMP_BRUSH_MIPMAP (brush, mipmaps, x, y)) + return GIMP_BRUSH_MIPMAP (brush, mipmaps, x, y); + + g_return_val_if_fail (x >= 0 || y >= 0, NULL); + + for (i = 1; i <= x + y; i++) + { + gint u = x - i; + gint v = y; + + if (u < 0) + { + v += u; + u = 0; + } + + while (u <= x && v >= 0) + { + if (GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v)) + { + for (; x - u > y - v; u++) + { + GIMP_BRUSH_MIPMAP (brush, mipmaps, u + 1, v) = + gimp_brush_mipmap_downscale_horz ( + GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v)); + } + + for (; y - v > x - u; v++) + { + GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v + 1) = + gimp_brush_mipmap_downscale_vert ( + GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v)); + } + + for (; u < x; u++, v++) + { + GIMP_BRUSH_MIPMAP (brush, mipmaps, u + 1, v + 1) = + gimp_brush_mipmap_downscale ( + GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v)); + } + + return GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v); + } + + u++; + v--; + } + } + + g_return_val_if_reached (NULL); +} + +template +struct MipmapTraits; + +template <> +struct MipmapTraits +{ + static guint8 + mix (guint8 a, + guint8 b) + { + return ((guint32) a + (guint32) b + 1) / 2; + } + + static guint8 + mix (guint8 a, + guint8 b, + guint8 c, + guint8 d) + { + return ((guint32) a + (guint32) b + (guint32) c + (guint32) d + 2) / 4; + } +}; + +template <> +struct MipmapTraits +{ + static gfloat + mix (gfloat a, + gfloat b) + { + return (a + b) / 2.0; + } + + static gfloat + mix (gfloat a, + gfloat b, + gfloat c, + gfloat d) + { + return (a + b + c + d) / 4.0; + } +}; + +template +struct MipmapAlgorithms +{ + static GimpTempBuf * + downscale (const GimpTempBuf *source) + { + GimpTempBuf *destination; + gint width = gimp_temp_buf_get_width (source); + gint height = gimp_temp_buf_get_height (source); + + width /= 2; + height /= 2; + + destination = gimp_temp_buf_new (width, height, + gimp_temp_buf_get_format (source)); + + gegl_parallel_distribute_area ( + GEGL_RECTANGLE (0, 0, width, height), PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + const T *src0 = (const T *) gimp_temp_buf_get_data (source); + T *dest0 = (T *) gimp_temp_buf_get_data (destination); + gint src_stride = N * gimp_temp_buf_get_width (source); + gint dest_stride = N * gimp_temp_buf_get_width (destination); + gint y; + + src0 += 2 * (area->y * src_stride + N * area->x); + dest0 += area->y * dest_stride + N * area->x; + + for (y = 0; y < area->height; y++) + { + const T *src = src0; + T *dest = dest0; + gint x; + + for (x = 0; x < area->width; x++) + { + gint c; + + for (c = 0; c < N; c++) + { + dest[c] = MipmapTraits::mix (src[c], + src[N + c], + src[src_stride + c], + src[src_stride + N + c]); + } + + src += 2 * N; + dest += N; + } + + src0 += 2 * src_stride; + dest0 += dest_stride; + } + }); + + return destination; + } + + static GimpTempBuf * + downscale_horz (const GimpTempBuf *source) + { + GimpTempBuf *destination; + gint width = gimp_temp_buf_get_width (source); + gint height = gimp_temp_buf_get_height (source); + + width /= 2; + + destination = gimp_temp_buf_new (width, height, + gimp_temp_buf_get_format (source)); + + gegl_parallel_distribute_range ( + height, PIXELS_PER_THREAD / width, + [=] (gint offset, + gint size) + { + const T *src0 = (const T *) gimp_temp_buf_get_data (source); + T *dest0 = (T *) gimp_temp_buf_get_data (destination); + gint src_stride = N * gimp_temp_buf_get_width (source); + gint dest_stride = N * gimp_temp_buf_get_width (destination); + gint y; + + src0 += offset * src_stride; + dest0 += offset * dest_stride; + + for (y = 0; y < size; y++) + { + const T *src = src0; + T *dest = dest0; + gint x; + + for (x = 0; x < width; x++) + { + gint c; + + for (c = 0; c < N; c++) + dest[c] = MipmapTraits::mix (src[c], src[N + c]); + + src += 2 * N; + dest += N; + } + + src0 += src_stride; + dest0 += dest_stride; + } + }); + + return destination; + } + + static GimpTempBuf * + downscale_vert (const GimpTempBuf *source) + { + GimpTempBuf *destination; + gint width = gimp_temp_buf_get_width (source); + gint height = gimp_temp_buf_get_height (source); + + height /= 2; + + destination = gimp_temp_buf_new (width, height, + gimp_temp_buf_get_format (source)); + + gegl_parallel_distribute_range ( + width, PIXELS_PER_THREAD / height, + [=] (gint offset, + gint size) + { + const T *src0 = (const T *) gimp_temp_buf_get_data (source); + T *dest0 = (T *) gimp_temp_buf_get_data (destination); + gint src_stride = N * gimp_temp_buf_get_width (source); + gint dest_stride = N * gimp_temp_buf_get_width (destination); + gint x; + + src0 += offset * N; + dest0 += offset * N; + + for (x = 0; x < size; x++) + { + const T *src = src0; + T *dest = dest0; + gint y; + + for (y = 0; y < height; y++) + { + gint c; + + for (c = 0; c < N; c++) + dest[c] = MipmapTraits::mix (src[c], src[src_stride + c]); + + src += 2 * src_stride; + dest += dest_stride; + } + + src0 += N; + dest0 += N; + } + }); + + return destination; + } +}; + +template +static GimpTempBuf * +gimp_brush_mipmap_dispatch (const GimpTempBuf *source, + Func func) +{ + const Babl *format = gimp_temp_buf_get_format (source); + const Babl *type; + gint n_components; + + type = babl_format_get_type (format, 0); + n_components = babl_format_get_n_components (format); + + if (type == babl_type ("u8")) + { + switch (n_components) + { + case 1: + return func (MipmapAlgorithms ()); + + case 3: + return func (MipmapAlgorithms ()); + } + } + else if (type == babl_type ("float")) + { + switch (n_components) + { + case 1: + return func (MipmapAlgorithms ()); + + case 3: + return func (MipmapAlgorithms ()); + } + } + + g_return_val_if_reached (NULL); +} + +static GimpTempBuf * +gimp_brush_mipmap_downscale (const GimpTempBuf *source) +{ + return gimp_brush_mipmap_dispatch ( + source, + [&] (auto algorithms) + { + return algorithms.downscale (source); + }); +} + +static GimpTempBuf * +gimp_brush_mipmap_downscale_horz (const GimpTempBuf *source) +{ + return gimp_brush_mipmap_dispatch ( + source, + [&] (auto algorithms) + { + return algorithms.downscale_horz (source); + }); +} + +static GimpTempBuf * +gimp_brush_mipmap_downscale_vert (const GimpTempBuf *source) +{ + return gimp_brush_mipmap_dispatch ( + source, + [&] (auto algorithms) + { + return algorithms.downscale_vert (source); + }); +} + + +/* public functions */ + +void +gimp_brush_mipmap_clear (GimpBrush *brush) +{ + gimp_brush_mipmap_clear (brush, &brush->priv->mask_mipmaps); + gimp_brush_mipmap_clear (brush, &brush->priv->pixmap_mipmaps); +} + +const GimpTempBuf * +gimp_brush_mipmap_get_mask (GimpBrush *brush, + gdouble *scale_x, + gdouble *scale_y) +{ + return gimp_brush_mipmap_get (brush, + brush->priv->mask, + &brush->priv->mask_mipmaps, + scale_x, scale_y); +} + +const GimpTempBuf * +gimp_brush_mipmap_get_pixmap (GimpBrush *brush, + gdouble *scale_x, + gdouble *scale_y) +{ + return gimp_brush_mipmap_get (brush, + brush->priv->pixmap, + &brush->priv->pixmap_mipmaps, + scale_x, scale_y); +} + +gsize +gimp_brush_mipmap_get_memsize (GimpBrush *brush) +{ + gsize memsize = 0; + + if (brush->priv->mask_mipmaps) + { + gint i; + + for (i = 1; + i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps; + i++) + { + memsize += gimp_temp_buf_get_memsize (brush->priv->mask_mipmaps[i]); + } + } + + if (brush->priv->pixmap_mipmaps) + { + gint i; + + for (i = 1; + i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps; + i++) + { + memsize += gimp_temp_buf_get_memsize (brush->priv->pixmap_mipmaps[i]); + } + } + + return memsize; +} diff --git a/app/core/gimpbrush-mipmap.h b/app/core/gimpbrush-mipmap.h new file mode 100644 index 0000000..b2b8fd3 --- /dev/null +++ b/app/core/gimpbrush-mipmap.h @@ -0,0 +1,38 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrush-mipmap.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_BRUSH_MIPMAP_H__ +#define __GIMP_BRUSH_MIPMAP_H__ + + +void gimp_brush_mipmap_clear (GimpBrush *brush); + +const GimpTempBuf * gimp_brush_mipmap_get_mask (GimpBrush *brush, + gdouble *scale_x, + gdouble *scale_y); + +const GimpTempBuf * gimp_brush_mipmap_get_pixmap (GimpBrush *brush, + gdouble *scale_x, + gdouble *scale_y); + +gsize gimp_brush_mipmap_get_memsize (GimpBrush *brush); + + +#endif /* __GIMP_BRUSH_MIPMAP_H__ */ diff --git a/app/core/gimpbrush-private.h b/app/core/gimpbrush-private.h new file mode 100644 index 0000000..c8d4d29 --- /dev/null +++ b/app/core/gimpbrush-private.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 . + */ + +#ifndef __GIMP_BRUSH_PRIVATE_H__ +#define __GIMP_BRUSH_PRIVATE_H__ + + +struct _GimpBrushPrivate +{ + GimpTempBuf *mask; /* the actual mask */ + GimpTempBuf *blurred_mask; /* blurred actual mask cached */ + GimpTempBuf *pixmap; /* optional pixmap data */ + GimpTempBuf *blurred_pixmap; /* optional pixmap data blurred cache */ + + gdouble blur_hardness; + + gint n_horz_mipmaps; + gint n_vert_mipmaps; + GimpTempBuf **mask_mipmaps; + GimpTempBuf **pixmap_mipmaps; + + gint spacing; /* brush's spacing */ + GimpVector2 x_axis; /* for calculating brush spacing */ + GimpVector2 y_axis; /* for calculating brush spacing */ + + gint use_count; /* for keeping the caches alive */ + GimpBrushCache *mask_cache; + GimpBrushCache *pixmap_cache; + GimpBrushCache *boundary_cache; +}; + + +#endif /* __GIMP_BRUSH_PRIVATE_H__ */ diff --git a/app/core/gimpbrush-save.c b/app/core/gimpbrush-save.c new file mode 100644 index 0000000..38d4bcd --- /dev/null +++ b/app/core/gimpbrush-save.c @@ -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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpbrush.h" +#include "gimpbrush-header.h" +#include "gimpbrush-save.h" +#include "gimptempbuf.h" + + +gboolean +gimp_brush_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpBrush *brush = GIMP_BRUSH (data); + GimpTempBuf *mask = gimp_brush_get_mask (brush); + GimpTempBuf *pixmap = gimp_brush_get_pixmap (brush); + GimpBrushHeader header; + const gchar *name; + gint width; + gint height; + + name = gimp_object_get_name (brush); + width = gimp_temp_buf_get_width (mask); + height = gimp_temp_buf_get_height (mask); + + header.header_size = g_htonl (sizeof (GimpBrushHeader) + + strlen (name) + 1); + header.version = g_htonl (2); + header.width = g_htonl (width); + header.height = g_htonl (height); + header.bytes = g_htonl (pixmap ? 4 : 1); + header.magic_number = g_htonl (GIMP_BRUSH_MAGIC); + header.spacing = g_htonl (gimp_brush_get_spacing (brush)); + + if (! g_output_stream_write_all (output, &header, sizeof (header), + NULL, NULL, error)) + { + return FALSE; + } + + if (! g_output_stream_write_all (output, name, strlen (name) + 1, + NULL, NULL, error)) + { + return FALSE; + } + + if (pixmap) + { + gsize size = width * height * 4; + guchar *data = g_malloc (size); + guchar *p = gimp_temp_buf_get_data (pixmap); + guchar *m = gimp_temp_buf_get_data (mask); + guchar *d = data; + gint i; + + for (i = 0; i < width * height; i++) + { + *d++ = *p++; + *d++ = *p++; + *d++ = *p++; + *d++ = *m++; + } + + if (! g_output_stream_write_all (output, data, size, + NULL, NULL, error)) + { + g_free (data); + + return FALSE; + } + + g_free (data); + } + else + { + if (! g_output_stream_write_all (output, + gimp_temp_buf_get_data (mask), + gimp_temp_buf_get_data_size (mask), + NULL, NULL, error)) + { + return FALSE; + } + } + + return TRUE; +} diff --git a/app/core/gimpbrush-save.h b/app/core/gimpbrush-save.h new file mode 100644 index 0000000..f400cf0 --- /dev/null +++ b/app/core/gimpbrush-save.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 . + */ + +#ifndef __GIMP_BRUSH_SAVE_H__ +#define __GIMP_BRUSH_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_brush_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_BRUSH_SAVE_H__ */ diff --git a/app/core/gimpbrush-transform.cc b/app/core/gimpbrush-transform.cc new file mode 100644 index 0000000..45673cd --- /dev/null +++ b/app/core/gimpbrush-transform.cc @@ -0,0 +1,1043 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrush-transform.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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +extern "C" +{ + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "gimpbrush.h" +#include "gimpbrush-mipmap.h" +#include "gimpbrush-transform.h" +#include "gimptempbuf.h" + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + + +/* local function prototypes */ + +static void gimp_brush_transform_bounding_box (const GimpTempBuf *temp_buf, + const GimpMatrix3 *matrix, + gint *x, + gint *y, + gint *width, + gint *height); + +static void gimp_brush_transform_blur (GimpTempBuf *buf, + gint r); +static gint gimp_brush_transform_blur_radius (gint height, + gint width, + gdouble hardness); +static void gimp_brush_transform_adjust_hardness_matrix (gdouble width, + gdouble height, + gdouble blur_radius, + GimpMatrix3 *matrix); + + +/* public functions */ + +void +gimp_brush_real_transform_size (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height) +{ + const GimpTempBuf *source; + GimpMatrix3 matrix; + gdouble scale_x, scale_y; + gint x, y; + + gimp_brush_transform_get_scale (scale, aspect_ratio, + &scale_x, &scale_y); + + source = gimp_brush_mipmap_get_mask (brush, &scale_x, &scale_y); + + gimp_brush_transform_matrix (gimp_temp_buf_get_width (source), + gimp_temp_buf_get_height (source), + scale_x, scale_y, angle, reflect, &matrix); + + gimp_brush_transform_bounding_box (source, &matrix, &x, &y, width, height); +} + +/* + * Transforms the brush mask with bilinear interpolation. + * + * Rather than calculating the inverse transform for each point in the + * transformed image, this algorithm uses the inverse transformed + * corner points of the destination image to work out the starting + * position in the source image and the U and V deltas in the source + * image space. It then uses a scan-line approach, looping through + * rows and columns in the transformed (destination) image while + * walking along the corresponding rows and columns (named U and V) in + * the source image. + * + * The horizontal in destination space (transform result) is reverse + * transformed into source image space to get U. The vertical in + * destination space (transform result) is reverse transformed into + * source image space to get V. + * + * The strength of this particular algorithm is that calculation work + * should depend more upon the final transformed brush size rather + * than the input brush size. + * + * There are no floating point calculations in the inner loop for speed. + * + * Some variables end with the suffix _i to indicate they have been + * premultiplied by int_multiple + */ +GimpTempBuf * +gimp_brush_real_transform_mask (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + GimpTempBuf *result; + const GimpTempBuf *source; + const guchar *src; + GimpMatrix3 matrix; + gdouble scale_x, scale_y; + gint src_width; + gint src_height; + gint src_width_minus_one; + gint src_height_minus_one; + gint dest_width; + gint dest_height; + gint blur_radius; + gint x, y; + gdouble b_lx, b_rx, t_lx, t_rx; + gdouble b_ly, b_ry, t_ly, t_ry; + gdouble src_tl_to_tr_delta_x; + gdouble src_tl_to_tr_delta_y; + gdouble src_tl_to_bl_delta_x; + gdouble src_tl_to_bl_delta_y; + gint src_walk_ux_i; + gint src_walk_uy_i; + gint src_walk_vx_i; + gint src_walk_vy_i; + gint src_x_min_i; + gint src_y_min_i; + gint src_x_max_i; + gint src_y_max_i; + + /* + * tl, tr etc are used because it is easier to visualize top left, + * top right etc corners of the forward transformed source image + * rectangle. + */ + const gint fraction_bits = 12; + const gint int_multiple = pow (2, fraction_bits); + + /* In inner loop's bilinear calculation, two numbers that were each + * previously multiplied by int_multiple are multiplied together. + * To get back the right result, the multiplication result must be + * divided *twice* by 2^fraction_bits, equivalent to bit shift right + * by 2 * fraction_bits + */ + const gint recovery_bits = 2 * fraction_bits; + + /* + * example: suppose fraction_bits = 9 + * a 9-bit mask looks like this: 0001 1111 1111 + * and is given by: 2^fraction_bits - 1 + * demonstration: + * 2^0 = 0000 0000 0001 + * 2^1 = 0000 0000 0010 + * : + * 2^8 = 0001 0000 0000 + * 2^9 = 0010 0000 0000 + * 2^9 - 1 = 0001 1111 1111 + */ + const guint fraction_bitmask = pow(2, fraction_bits) - 1 ; + + gimp_brush_transform_get_scale (scale, aspect_ratio, + &scale_x, &scale_y); + + source = gimp_brush_mipmap_get_mask (brush, &scale_x, &scale_y); + + src_width = gimp_temp_buf_get_width (source); + src_height = gimp_temp_buf_get_height (source); + + gimp_brush_transform_matrix (src_width, src_height, + scale_x, scale_y, angle, reflect, &matrix); + + if (gimp_matrix3_is_identity (&matrix) && hardness == 1.0) + return gimp_temp_buf_copy (source); + + src_width_minus_one = src_width - 1; + src_height_minus_one = src_height - 1; + + gimp_brush_transform_bounding_box (source, &matrix, + &x, &y, &dest_width, &dest_height); + + blur_radius = 0; + + if (hardness < 1.0) + { + GimpMatrix3 unrotated_matrix; + gint unrotated_x; + gint unrotated_y; + gint unrotated_dest_width; + gint unrotated_dest_height; + + gimp_brush_transform_matrix (src_width, src_height, + scale_x, scale_y, 0.0, FALSE, + &unrotated_matrix); + + gimp_brush_transform_bounding_box (source, &unrotated_matrix, + &unrotated_x, &unrotated_y, + &unrotated_dest_width, + &unrotated_dest_height); + + blur_radius = gimp_brush_transform_blur_radius (unrotated_dest_width, + unrotated_dest_height, + hardness); + + gimp_brush_transform_adjust_hardness_matrix (dest_width, dest_height, + blur_radius, &matrix); + } + + gimp_matrix3_translate (&matrix, -x, -y); + gimp_matrix3_invert (&matrix); + gimp_matrix3_translate (&matrix, -0.5, -0.5); + + result = gimp_temp_buf_new (dest_width, dest_height, + gimp_temp_buf_get_format (source)); + + src = gimp_temp_buf_get_data (source); + + /* prevent disappearance of 1x1 pixel brush at some rotations when + scaling < 1 */ + /* + if (src_width == 1 && src_height == 1 && scale_x < 1 && scale_y < 1 ) + { + *dest = src[0]; + return result; + }*/ + + gimp_matrix3_transform_point (&matrix, + 0.5, 0.5, + &t_lx, &t_ly); + gimp_matrix3_transform_point (&matrix, + dest_width - 0.5, 0.5, + &t_rx, &t_ry); + gimp_matrix3_transform_point (&matrix, + 0.5, dest_height - 0.5, + &b_lx, &b_ly); + gimp_matrix3_transform_point (&matrix, + dest_width - 0.5, dest_height - 0.5, + &b_rx, &b_ry); + + /* in image space, calc U (what was horizontal originally) + * note: double precision + */ + src_tl_to_tr_delta_x = t_rx - t_lx; + src_tl_to_tr_delta_y = t_ry - t_ly; + + /* in image space, calc V (what was vertical originally) + * note: double precision + */ + src_tl_to_bl_delta_x = b_lx - t_lx; + src_tl_to_bl_delta_y = b_ly - t_ly; + + /* speed optimized, note conversion to int precision */ + src_walk_ux_i = (gint) ((src_tl_to_tr_delta_x / MAX (dest_width - 1, 1)) * + int_multiple); + src_walk_uy_i = (gint) ((src_tl_to_tr_delta_y / MAX (dest_width - 1, 1)) * + int_multiple); + src_walk_vx_i = (gint) ((src_tl_to_bl_delta_x / MAX (dest_height - 1, 1)) * + int_multiple); + src_walk_vy_i = (gint) ((src_tl_to_bl_delta_y / MAX (dest_height - 1, 1)) * + int_multiple); + + src_x_min_i = -int_multiple / 2; + src_y_min_i = -int_multiple / 2; + + src_x_max_i = src_width * int_multiple - int_multiple / 2; + src_y_max_i = src_height * int_multiple - int_multiple / 2; + + gegl_parallel_distribute_area ( + GEGL_RECTANGLE (0, 0, dest_width, dest_height), PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + guchar *dest; + gint src_space_cur_pos_x; + gint src_space_cur_pos_y; + gint src_space_cur_pos_x_i; + gint src_space_cur_pos_y_i; + gint src_space_row_start_x_i; + gint src_space_row_start_y_i; + const guchar *src_walker; + const guchar *pixel_next; + const guchar *pixel_below; + const guchar *pixel_below_next; + gint opposite_x, distance_from_true_x; + gint opposite_y, distance_from_true_y; + gint u, v; + + dest = gimp_temp_buf_get_data (result) + + dest_width * area->y + area->x; + + /* initialize current position in source space to the start position (tl) + * speed optimized, note conversion to int precision + */ + src_space_row_start_x_i = (gint) (t_lx * int_multiple) + + src_walk_vx_i * area->y + + src_walk_ux_i * area->x; + src_space_row_start_y_i = (gint) (t_ly * int_multiple) + + src_walk_vy_i * area->y + + src_walk_uy_i * area->x; + + for (v = 0; v < area->height; v++) + { + src_space_cur_pos_x_i = src_space_row_start_x_i; + src_space_cur_pos_y_i = src_space_row_start_y_i; + + for (u = 0; u < area->width; u++) + { + if (src_space_cur_pos_x_i < src_x_min_i || + src_space_cur_pos_x_i >= src_x_max_i || + src_space_cur_pos_y_i < src_y_min_i || + src_space_cur_pos_y_i >= src_y_max_i) + /* no corresponding pixel in source space */ + { + *dest = 0; + } + else /* reverse transformed point hits source pixel */ + { + src_space_cur_pos_x = src_space_cur_pos_x_i >> fraction_bits; + src_space_cur_pos_y = src_space_cur_pos_y_i >> fraction_bits; + + src_walker = src + + src_space_cur_pos_y * src_width + + src_space_cur_pos_x; + + pixel_next = src_walker + 1; + pixel_below = src_walker + src_width; + pixel_below_next = pixel_below + 1; + + if (src_space_cur_pos_x < 0) + { + src_walker = pixel_next; + pixel_below = pixel_below_next; + } + else if (src_space_cur_pos_x >= src_width_minus_one) + { + pixel_next = src_walker; + pixel_below_next = pixel_below; + } + + if (src_space_cur_pos_y < 0) + { + src_walker = pixel_below; + pixel_next = pixel_below_next; + } + else if (src_space_cur_pos_y >= src_height_minus_one) + { + pixel_below = src_walker; + pixel_below_next = pixel_next; + } + + distance_from_true_x = src_space_cur_pos_x_i & fraction_bitmask; + distance_from_true_y = src_space_cur_pos_y_i & fraction_bitmask; + opposite_x = int_multiple - distance_from_true_x; + opposite_y = int_multiple - distance_from_true_y; + + *dest = ((src_walker[0] * opposite_x + + pixel_next[0] * distance_from_true_x) * opposite_y + + (pixel_below[0] * opposite_x + + pixel_below_next[0] *distance_from_true_x) * distance_from_true_y + ) >> recovery_bits; + } + + src_space_cur_pos_x_i += src_walk_ux_i; + src_space_cur_pos_y_i += src_walk_uy_i; + + dest++; + } /* end for x */ + + src_space_row_start_x_i += src_walk_vx_i; + src_space_row_start_y_i += src_walk_vy_i; + + dest += dest_width - area->width; + } /* end for y */ + }); + + gimp_brush_transform_blur (result, blur_radius); + + return result; +} + +/* + * Transforms the brush pixmap with bilinear interpolation. + * + * The algorithm used is exactly the same as for the brush mask + * (gimp_brush_real_transform_mask) except it accounts for 3 color channels + * instead of 1 grayscale channel. + * + * Rather than calculating the inverse transform for each point in the + * transformed image, this algorithm uses the inverse transformed + * corner points of the destination image to work out the starting + * position in the source image and the U and V deltas in the source + * image space. It then uses a scan-line approach, looping through + * rows and columns in the transformed (destination) image while + * walking along the corresponding rows and columns (named U and V) in + * the source image. + * + * The horizontal in destination space (transform result) is reverse + * transformed into source image space to get U. The vertical in + * destination space (transform result) is reverse transformed into + * source image space to get V. + * + * The strength of this particular algorithm is that calculation work + * should depend more upon the final transformed brush size rather + * than the input brush size. + * + * There are no floating point calculations in the inner loop for speed. + * + * Some variables end with the suffix _i to indicate they have been + * premultiplied by int_multiple + */ +GimpTempBuf * +gimp_brush_real_transform_pixmap (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + GimpTempBuf *result; + const GimpTempBuf *source; + const guchar *src; + GimpMatrix3 matrix; + gdouble scale_x, scale_y; + gint src_width; + gint src_height; + gint src_width_minus_one; + gint src_height_minus_one; + gint dest_width; + gint dest_height; + gint blur_radius; + gint x, y; + gdouble b_lx, b_rx, t_lx, t_rx; + gdouble b_ly, b_ry, t_ly, t_ry; + gdouble src_tl_to_tr_delta_x; + gdouble src_tl_to_tr_delta_y; + gdouble src_tl_to_bl_delta_x; + gdouble src_tl_to_bl_delta_y; + gint src_walk_ux_i; + gint src_walk_uy_i; + gint src_walk_vx_i; + gint src_walk_vy_i; + gint src_x_min_i; + gint src_y_min_i; + gint src_x_max_i; + gint src_y_max_i; + + /* + * tl, tr etc are used because it is easier to visualize top left, + * top right etc corners of the forward transformed source image + * rectangle. + */ + const gint fraction_bits = 12; + const gint int_multiple = pow (2, fraction_bits); + + /* In inner loop's bilinear calculation, two numbers that were each + * previously multiplied by int_multiple are multiplied together. + * To get back the right result, the multiplication result must be + * divided *twice* by 2^fraction_bits, equivalent to bit shift right + * by 2 * fraction_bits + */ + const gint recovery_bits = 2 * fraction_bits; + + /* + * example: suppose fraction_bits = 9 + * a 9-bit mask looks like this: 0001 1111 1111 + * and is given by: 2^fraction_bits - 1 + * demonstration: + * 2^0 = 0000 0000 0001 + * 2^1 = 0000 0000 0010 + * : + * 2^8 = 0001 0000 0000 + * 2^9 = 0010 0000 0000 + * 2^9 - 1 = 0001 1111 1111 + */ + const guint fraction_bitmask = pow(2, fraction_bits) - 1 ; + + gimp_brush_transform_get_scale (scale, aspect_ratio, + &scale_x, &scale_y); + + source = gimp_brush_mipmap_get_pixmap (brush, &scale_x, &scale_y); + + src_width = gimp_temp_buf_get_width (source); + src_height = gimp_temp_buf_get_height (source); + + gimp_brush_transform_matrix (src_width, src_height, + scale_x, scale_y, angle, reflect, &matrix); + + if (gimp_matrix3_is_identity (&matrix) && hardness == 1.0) + return gimp_temp_buf_copy (source); + + src_width_minus_one = src_width - 1; + src_height_minus_one = src_height - 1; + + gimp_brush_transform_bounding_box (source, &matrix, + &x, &y, &dest_width, &dest_height); + + blur_radius = 0; + + if (hardness < 1.0) + { + GimpMatrix3 unrotated_matrix; + gint unrotated_x; + gint unrotated_y; + gint unrotated_dest_width; + gint unrotated_dest_height; + + gimp_brush_transform_matrix (src_width, src_height, + scale_x, scale_y, 0.0, FALSE, + &unrotated_matrix); + + gimp_brush_transform_bounding_box (source, &unrotated_matrix, + &unrotated_x, &unrotated_y, + &unrotated_dest_width, + &unrotated_dest_height); + + blur_radius = gimp_brush_transform_blur_radius (unrotated_dest_width, + unrotated_dest_height, + hardness); + + gimp_brush_transform_adjust_hardness_matrix (dest_width, dest_height, + blur_radius, &matrix); + } + + gimp_matrix3_translate (&matrix, -x, -y); + gimp_matrix3_invert (&matrix); + gimp_matrix3_translate (&matrix, -0.5, -0.5); + + result = gimp_temp_buf_new (dest_width, dest_height, + gimp_temp_buf_get_format (source)); + + src = gimp_temp_buf_get_data (source); + + /* prevent disappearance of 1x1 pixel brush at some rotations when + scaling < 1 */ + /* + if (src_width == 1 && src_height == 1 && scale_x < 1 && scale_y < 1 ) + { + *dest = src[0]; + return result; + }*/ + + gimp_matrix3_transform_point (&matrix, + 0.5, 0.5, + &t_lx, &t_ly); + gimp_matrix3_transform_point (&matrix, + dest_width - 0.5, 0.5, + &t_rx, &t_ry); + gimp_matrix3_transform_point (&matrix, + 0.5, dest_height - 0.5, + &b_lx, &b_ly); + gimp_matrix3_transform_point (&matrix, + dest_width - 0.5, dest_height - 0.5, + &b_rx, &b_ry); + + /* in image space, calc U (what was horizontal originally) + * note: double precision + */ + src_tl_to_tr_delta_x = t_rx - t_lx; + src_tl_to_tr_delta_y = t_ry - t_ly; + + /* in image space, calc V (what was vertical originally) + * note: double precision + */ + src_tl_to_bl_delta_x = b_lx - t_lx; + src_tl_to_bl_delta_y = b_ly - t_ly; + + /* speed optimized, note conversion to int precision */ + src_walk_ux_i = (gint) ((src_tl_to_tr_delta_x / MAX (dest_width - 1, 1)) * + int_multiple); + src_walk_uy_i = (gint) ((src_tl_to_tr_delta_y / MAX (dest_width - 1, 1)) * + int_multiple); + src_walk_vx_i = (gint) ((src_tl_to_bl_delta_x / MAX (dest_height - 1, 1)) * + int_multiple); + src_walk_vy_i = (gint) ((src_tl_to_bl_delta_y / MAX (dest_height - 1, 1)) * + int_multiple); + + src_x_min_i = -int_multiple / 2; + src_y_min_i = -int_multiple / 2; + + src_x_max_i = src_width * int_multiple - int_multiple / 2; + src_y_max_i = src_height * int_multiple - int_multiple / 2; + + gegl_parallel_distribute_area ( + GEGL_RECTANGLE (0, 0, dest_width, dest_height), PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + guchar *dest; + gint src_space_cur_pos_x; + gint src_space_cur_pos_y; + gint src_space_cur_pos_x_i; + gint src_space_cur_pos_y_i; + gint src_space_row_start_x_i; + gint src_space_row_start_y_i; + const guchar *src_walker; + const guchar *pixel_next; + const guchar *pixel_below; + const guchar *pixel_below_next; + gint opposite_x, distance_from_true_x; + gint opposite_y, distance_from_true_y; + gint u, v; + + dest = gimp_temp_buf_get_data (result) + + 3 * (dest_width * area->y + area->x); + + /* initialize current position in source space to the start position (tl) + * speed optimized, note conversion to int precision + */ + src_space_row_start_x_i = (gint) (t_lx * int_multiple) + + src_walk_vx_i * area->y + + src_walk_ux_i * area->x; + src_space_row_start_y_i = (gint) (t_ly * int_multiple) + + src_walk_vy_i * area->y + + src_walk_uy_i * area->x; + + for (v = 0; v < area->height; v++) + { + src_space_cur_pos_x_i = src_space_row_start_x_i; + src_space_cur_pos_y_i = src_space_row_start_y_i; + + for (u = 0; u < area->width; u++) + { + if (src_space_cur_pos_x_i < src_x_min_i || + src_space_cur_pos_x_i >= src_x_max_i || + src_space_cur_pos_y_i < src_y_min_i || + src_space_cur_pos_y_i >= src_y_max_i) + /* no corresponding pixel in source space */ + { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + } + else /* reverse transformed point hits source pixel */ + { + src_space_cur_pos_x = src_space_cur_pos_x_i >> fraction_bits; + src_space_cur_pos_y = src_space_cur_pos_y_i >> fraction_bits; + + src_walker = src + + 3 * (src_space_cur_pos_y * src_width + + src_space_cur_pos_x); + + pixel_next = src_walker + 3; + pixel_below = src_walker + 3 * src_width; + pixel_below_next = pixel_below + 3; + + if (src_space_cur_pos_x < 0) + { + src_walker = pixel_next; + pixel_below = pixel_below_next; + } + else if (src_space_cur_pos_x >= src_width_minus_one) + { + pixel_next = src_walker; + pixel_below_next = pixel_below; + } + + if (src_space_cur_pos_y < 0) + { + src_walker = pixel_below; + pixel_next = pixel_below_next; + } + else if (src_space_cur_pos_y >= src_height_minus_one) + { + pixel_below = src_walker; + pixel_below_next = pixel_next; + } + + distance_from_true_x = src_space_cur_pos_x_i & fraction_bitmask; + distance_from_true_y = src_space_cur_pos_y_i & fraction_bitmask; + opposite_x = int_multiple - distance_from_true_x; + opposite_y = int_multiple - distance_from_true_y; + + dest[0] = ((src_walker[0] * opposite_x + + pixel_next[0] * distance_from_true_x) * opposite_y + + (pixel_below[0] * opposite_x + + pixel_below_next[0] *distance_from_true_x) * distance_from_true_y + ) >> recovery_bits; + + dest[1] = ((src_walker[1] * opposite_x + + pixel_next[1] * distance_from_true_x) * opposite_y + + (pixel_below[1] * opposite_x + + pixel_below_next[1] *distance_from_true_x) * distance_from_true_y + ) >> recovery_bits; + + dest[2] = ((src_walker[2] * opposite_x + + pixel_next[2] * distance_from_true_x) * opposite_y + + (pixel_below[2] * opposite_x + + pixel_below_next[2] *distance_from_true_x) * distance_from_true_y + ) >> recovery_bits; + } + + src_space_cur_pos_x_i += src_walk_ux_i; + src_space_cur_pos_y_i += src_walk_uy_i; + + dest += 3; + } /* end for x */ + + src_space_row_start_x_i += src_walk_vx_i; + src_space_row_start_y_i += src_walk_vy_i; + + dest += 3 * (dest_width - area->width); + } /* end for y */ + }); + + gimp_brush_transform_blur (result, blur_radius); + + return result; +} + +void +gimp_brush_transform_get_scale (gdouble scale, + gdouble aspect_ratio, + gdouble *scale_x, + gdouble *scale_y) +{ + if (aspect_ratio < 0.0) + { + *scale_x = scale * (1.0 + (aspect_ratio / 20.0)); + *scale_y = scale; + } + else + { + *scale_x = scale; + *scale_y = scale * (1.0 - (aspect_ratio / 20.0)); + } +} + +void +gimp_brush_transform_matrix (gdouble width, + gdouble height, + gdouble scale_x, + gdouble scale_y, + gdouble angle, + gboolean reflect, + GimpMatrix3 *matrix) +{ + const gdouble center_x = width / 2; + const gdouble center_y = height / 2; + + gimp_matrix3_identity (matrix); + gimp_matrix3_scale (matrix, scale_x, scale_y); + gimp_matrix3_translate (matrix, - center_x * scale_x, - center_y * scale_y); + gimp_matrix3_rotate (matrix, -2 * G_PI * angle); + if (reflect) + gimp_matrix3_scale (matrix, -1.0, 1.0); + gimp_matrix3_translate (matrix, center_x * scale_x, center_y * scale_y); +} + +/* private functions */ + +static void +gimp_brush_transform_bounding_box (const GimpTempBuf *temp_buf, + const GimpMatrix3 *matrix, + gint *x, + gint *y, + gint *width, + gint *height) +{ + const gdouble w = gimp_temp_buf_get_width (temp_buf); + const gdouble h = gimp_temp_buf_get_height (temp_buf); + gdouble x1, x2, x3, x4; + gdouble y1, y2, y3, y4; + + gimp_matrix3_transform_point (matrix, 0, 0, &x1, &y1); + gimp_matrix3_transform_point (matrix, w, 0, &x2, &y2); + gimp_matrix3_transform_point (matrix, 0, h, &x3, &y3); + gimp_matrix3_transform_point (matrix, w, h, &x4, &y4); + + *x = (gint) ceil (MIN (MIN (x1, x2), MIN (x3, x4)) - 0.5); + *y = (gint) ceil (MIN (MIN (y1, y2), MIN (y3, y4)) - 0.5); + + *width = (gint) ceil (MAX (MAX (x1, x2), MAX (x3, x4)) - 0.5) - *x; + *height = (gint) ceil (MAX (MAX (y1, y2), MAX (y3, y4)) - 0.5) - *y; + + /* Transform size can not be less than 1 px */ + *width = MAX (1, *width); + *height = MAX (1, *height); +} + +/* Blurs the brush mask/pixmap, in place, using a convolution of the form: + * + * 12 11 10 9 8 + * 7 6 5 4 3 + * 2 1 0 1 2 + * 3 4 5 6 7 + * 8 9 10 11 12 + * + * (i.e., an array, wrapped into a matrix, whose i-th element is + * `abs (i - a / 2)`, where `a` is the length of the array.) `r` specifies the + * convolution kernel's radius. + */ +static void +gimp_brush_transform_blur (GimpTempBuf *buf, + gint r) +{ + typedef struct + { + gint sum; + gint weighted_sum; + gint middle_sum; + } Sums; + + const Babl *format = gimp_temp_buf_get_format (buf); + gint components = babl_format_get_n_components (format); + gint components_r = components * r; + gint width = gimp_temp_buf_get_width (buf); + gint height = gimp_temp_buf_get_height (buf); + gint stride = components * width; + gint stride_r = stride * r; + guchar *data = gimp_temp_buf_get_data (buf); + gint rw = MIN (r, width - 1); + gint rh = MIN (r, height - 1); + gfloat n = 2 * r + 1; + gfloat n_r = n * r; + gfloat weight = floor (n * n / 2) * (floor (n * n / 2) + 1); + gfloat weight_inv = 1 / weight; + Sums *sums; + + if (rw <= 0 || rh <= 0) + return; + + sums = g_new (Sums, width * height * components); + + gegl_parallel_distribute_range ( + height, PIXELS_PER_THREAD / width, + [=] (gint y0, gint height) + { + gint x; + gint y; + gint c; + const guchar *d; + Sums *s; + + d = data + y0 * stride; + s = sums + y0 * stride; + + for (y = 0; y < height; y++) + { + const guchar *p; + + struct + { + gint sum; + gint weighted_sum; + gint leading_sum; + gint leading_weighted_sum; + } acc[components]; + + memset (acc, 0, sizeof (acc)); + + p = d; + + for (x = 0; x <= rw; x++) + { + for (c = 0; c < components; c++) + { + acc[c].sum += *p; + acc[c].weighted_sum += -x * *p; + + p++; + } + } + + for (x = 0; x < width; x++) + { + for (c = 0; c < components; c++) + { + if (x > 0) + { + acc[c].weighted_sum += acc[c].sum; + acc[c].leading_weighted_sum += acc[c].leading_sum; + + if (x < width - r) + { + acc[c].sum += d[components_r]; + acc[c].weighted_sum += -r * d[components_r]; + } + } + + acc[c].leading_sum += d[0]; + + s->sum = acc[c].sum; + s->weighted_sum = acc[c].weighted_sum; + s->middle_sum = 2 * acc[c].leading_weighted_sum - + acc[c].weighted_sum; + + if (x >= r) + { + acc[c].sum -= d[-components_r]; + acc[c].weighted_sum -= r * d[-components_r]; + acc[c].leading_sum -= d[-components_r]; + acc[c].leading_weighted_sum -= r * d[-components_r]; + } + + d++; + s++; + } + } + } + }); + + gegl_parallel_distribute_range ( + width, PIXELS_PER_THREAD / height, + [=] (gint x0, gint width) + { + gint x; + gint y; + gint c; + guchar *d0; + const Sums *s0; + guchar *d; + const Sums *s; + + d0 = data + x0 * components; + s0 = sums + x0 * components; + + for (x = 0; x < width; x++) + { + const Sums *p; + gfloat n_y; + + struct + { + gfloat weighted_sum; + gint leading_sum; + gint trailing_sum; + } acc[components]; + + memset (acc, 0, sizeof (acc)); + + d = d0 + components * x; + s = s0 + components * x; + + p = s + stride; + + for (y = 1, n_y = n; y <= rh; y++, n_y += n) + { + for (c = 0; c < components; c++) + { + acc[c].weighted_sum += n_y * p->sum - p->weighted_sum; + acc[c].trailing_sum += p->sum; + + p++; + } + + p += stride - components; + } + + for (y = 0; y < height; y++) + { + for (c = 0; c < components; c++) + { + if (y > 0) + { + acc[c].weighted_sum += s->weighted_sum + + n * (acc[c].leading_sum - + acc[c].trailing_sum); + acc[c].trailing_sum -= s->sum; + + if (y < height - r) + { + acc[c].weighted_sum += n_r * s[stride_r].sum - + s[stride_r].weighted_sum; + acc[c].trailing_sum += s[stride_r].sum; + } + } + + acc[c].leading_sum += s->sum; + + *d = (acc[c].weighted_sum + s->middle_sum) * weight_inv + 0.5f; + + acc[c].weighted_sum += s->weighted_sum; + + if (y >= r) + { + acc[c].weighted_sum -= n_r * s[-stride_r].sum + + s[-stride_r].weighted_sum; + acc[c].leading_sum -= s[-stride_r].sum; + } + + d++; + s++; + } + + d += stride - components; + s += stride - components; + } + } + }); + + g_free (sums); +} + +static gint +gimp_brush_transform_blur_radius (gint height, + gint width, + gdouble hardness) +{ + return floor ((1.0 - hardness) * (sqrt (0.5) - 0.5) * MIN (width, height)); +} + +static void +gimp_brush_transform_adjust_hardness_matrix (gdouble width, + gdouble height, + gdouble blur_radius, + GimpMatrix3 *matrix) +{ + gdouble scale; + + if (blur_radius == 0.0) + return; + + scale = (MIN (width, height) - 2.0 * blur_radius) / MIN (width, height); + + gimp_matrix3_scale (matrix, scale, scale); + gimp_matrix3_translate (matrix, + (1.0 - scale) * width / 2.0, + (1.0 - scale) * height / 2.0); +} + +} /* extern "C" */ diff --git a/app/core/gimpbrush-transform.h b/app/core/gimpbrush-transform.h new file mode 100644 index 0000000..5f7cbcb --- /dev/null +++ b/app/core/gimpbrush-transform.h @@ -0,0 +1,59 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrush-transform.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 . + */ + +#ifndef __GIMP_BRUSH_TRANSFORM_H__ +#define __GIMP_BRUSH_TRANSFORM_H__ + + +/* virtual functions of GimpBrush, don't call directly */ + +void gimp_brush_real_transform_size (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *scaled_width, + gint *scaled_height); +GimpTempBuf * gimp_brush_real_transform_mask (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); +GimpTempBuf * gimp_brush_real_transform_pixmap (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); + +void gimp_brush_transform_get_scale (gdouble scale, + gdouble aspect_ratio, + gdouble *scale_x, + gdouble *scale_y); +void gimp_brush_transform_matrix (gdouble width, + gdouble height, + gdouble scale_x, + gdouble scale_y, + gdouble angle, + gboolean reflect, + GimpMatrix3 *matrix); + + +#endif /* __GIMP_BRUSH_TRANSFORM_H__ */ diff --git a/app/core/gimpbrush.c b/app/core/gimpbrush.c new file mode 100644 index 0000000..b81cdf0 --- /dev/null +++ b/app/core/gimpbrush.c @@ -0,0 +1,945 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpbezierdesc.h" +#include "gimpbrush.h" +#include "gimpbrush-boundary.h" +#include "gimpbrush-load.h" +#include "gimpbrush-mipmap.h" +#include "gimpbrush-private.h" +#include "gimpbrush-save.h" +#include "gimpbrush-transform.h" +#include "gimpbrushcache.h" +#include "gimpbrushgenerated.h" +#include "gimpbrushpipe.h" +#include "gimpmarshal.h" +#include "gimptagged.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +enum +{ + SPACING_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_SPACING +}; + + +static void gimp_brush_tagged_iface_init (GimpTaggedInterface *iface); + +static void gimp_brush_finalize (GObject *object); +static void gimp_brush_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_brush_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_brush_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_brush_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static GimpTempBuf * gimp_brush_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_brush_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_brush_dirty (GimpData *data); +static const gchar * gimp_brush_get_extension (GimpData *data); +static void gimp_brush_copy (GimpData *data, + GimpData *src_data); + +static void gimp_brush_real_begin_use (GimpBrush *brush); +static void gimp_brush_real_end_use (GimpBrush *brush); +static GimpBrush * gimp_brush_real_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); +static gboolean gimp_brush_real_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); + +static gchar * gimp_brush_get_checksum (GimpTagged *tagged); + + +G_DEFINE_TYPE_WITH_CODE (GimpBrush, gimp_brush, GIMP_TYPE_DATA, + G_ADD_PRIVATE (GimpBrush) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_brush_tagged_iface_init)) + +#define parent_class gimp_brush_parent_class + +static guint brush_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_brush_class_init (GimpBrushClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + brush_signals[SPACING_CHANGED] = + g_signal_new ("spacing-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpBrushClass, spacing_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_brush_finalize; + object_class->get_property = gimp_brush_get_property; + object_class->set_property = gimp_brush_set_property; + + gimp_object_class->get_memsize = gimp_brush_get_memsize; + + viewable_class->default_icon_name = "gimp-tool-paintbrush"; + viewable_class->get_size = gimp_brush_get_size; + viewable_class->get_new_preview = gimp_brush_get_new_preview; + viewable_class->get_description = gimp_brush_get_description; + + data_class->dirty = gimp_brush_dirty; + data_class->save = gimp_brush_save; + data_class->get_extension = gimp_brush_get_extension; + data_class->copy = gimp_brush_copy; + + klass->begin_use = gimp_brush_real_begin_use; + klass->end_use = gimp_brush_real_end_use; + klass->select_brush = gimp_brush_real_select_brush; + klass->want_null_motion = gimp_brush_real_want_null_motion; + klass->transform_size = gimp_brush_real_transform_size; + klass->transform_mask = gimp_brush_real_transform_mask; + klass->transform_pixmap = gimp_brush_real_transform_pixmap; + klass->transform_boundary = gimp_brush_real_transform_boundary; + klass->spacing_changed = NULL; + + g_object_class_install_property (object_class, PROP_SPACING, + g_param_spec_double ("spacing", NULL, + _("Brush Spacing"), + 1.0, 5000.0, 20.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_brush_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->get_checksum = gimp_brush_get_checksum; +} + +static void +gimp_brush_init (GimpBrush *brush) +{ + brush->priv = gimp_brush_get_instance_private (brush); + + brush->priv->spacing = 20; + brush->priv->x_axis.x = 15.0; + brush->priv->x_axis.y = 0.0; + brush->priv->y_axis.x = 0.0; + brush->priv->y_axis.y = 15.0; + + brush->priv->blur_hardness = 1.0; +} + +static void +gimp_brush_finalize (GObject *object) +{ + GimpBrush *brush = GIMP_BRUSH (object); + + g_clear_pointer (&brush->priv->mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->pixmap, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref); + + gimp_brush_mipmap_clear (brush); + + g_clear_object (&brush->priv->mask_cache); + g_clear_object (&brush->priv->pixmap_cache); + g_clear_object (&brush->priv->boundary_cache); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_brush_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpBrush *brush = GIMP_BRUSH (object); + + switch (property_id) + { + case PROP_SPACING: + gimp_brush_set_spacing (brush, g_value_get_double (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brush_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpBrush *brush = GIMP_BRUSH (object); + + switch (property_id) + { + case PROP_SPACING: + g_value_set_double (value, gimp_brush_get_spacing (brush)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_brush_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpBrush *brush = GIMP_BRUSH (object); + gint64 memsize = 0; + + memsize += gimp_temp_buf_get_memsize (brush->priv->mask); + memsize += gimp_temp_buf_get_memsize (brush->priv->pixmap); + + memsize += gimp_brush_mipmap_get_memsize (brush); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_brush_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpBrush *brush = GIMP_BRUSH (viewable); + + *width = gimp_temp_buf_get_width (brush->priv->mask); + *height = gimp_temp_buf_get_height (brush->priv->mask); + + return TRUE; +} + +static GimpTempBuf * +gimp_brush_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpBrush *brush = GIMP_BRUSH (viewable); + const GimpTempBuf *mask_buf = brush->priv->mask; + const GimpTempBuf *pixmap_buf = brush->priv->pixmap; + GimpTempBuf *return_buf = NULL; + gint mask_width; + gint mask_height; + guchar *mask_data; + guchar *mask; + guchar *buf; + gint x, y; + gboolean free_mask = FALSE; + gdouble scale = 1.0; + + mask_width = gimp_temp_buf_get_width (mask_buf); + mask_height = gimp_temp_buf_get_height (mask_buf); + + if (mask_width > width || mask_height > height) + { + gdouble ratio_x = (gdouble) width / (gdouble) mask_width; + gdouble ratio_y = (gdouble) height / (gdouble) mask_height; + + scale = MIN (ratio_x, ratio_y); + } + + if (GIMP_IS_BRUSH_GENERATED (brush) || scale != 1.0) + { + gimp_brush_begin_use (brush); + + if (GIMP_IS_BRUSH_GENERATED (brush)) + { + GimpBrushGenerated *gen_brush = GIMP_BRUSH_GENERATED (brush); + + mask_buf = gimp_brush_transform_mask (brush, scale, + (gimp_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0, + gimp_brush_generated_get_angle (gen_brush) / -360.0, + FALSE, + gimp_brush_generated_get_hardness (gen_brush)); + } + else + { + mask_buf = gimp_brush_transform_mask (brush, scale, 0.0, 0.0, FALSE, 1.0); + } + + if (! mask_buf) + { + mask_buf = gimp_temp_buf_new (1, 1, babl_format ("Y u8")); + gimp_temp_buf_data_clear ((GimpTempBuf *) mask_buf); + } + else + { + gimp_temp_buf_ref ((GimpTempBuf *) mask_buf); + } + + if (pixmap_buf) + pixmap_buf = gimp_brush_transform_pixmap (brush, scale, + 0.0, 0.0, FALSE, 1.0); + + mask_width = gimp_temp_buf_get_width (mask_buf); + mask_height = gimp_temp_buf_get_height (mask_buf); + + free_mask = TRUE; + } + + return_buf = gimp_temp_buf_new (mask_width, mask_height, + babl_format ("R'G'B'A u8")); + + mask = mask_data = gimp_temp_buf_lock (mask_buf, babl_format ("Y u8"), + GEGL_ACCESS_READ); + buf = gimp_temp_buf_get_data (return_buf); + + if (pixmap_buf) + { + guchar *pixmap_data; + guchar *pixmap; + + pixmap = pixmap_data = gimp_temp_buf_lock (pixmap_buf, + babl_format ("R'G'B' u8"), + GEGL_ACCESS_READ); + + for (y = 0; y < mask_height; y++) + { + for (x = 0; x < mask_width ; x++) + { + *buf++ = *pixmap++; + *buf++ = *pixmap++; + *buf++ = *pixmap++; + *buf++ = *mask++; + } + } + + gimp_temp_buf_unlock (pixmap_buf, pixmap_data); + } + else + { + for (y = 0; y < mask_height; y++) + { + for (x = 0; x < mask_width ; x++) + { + *buf++ = 0; + *buf++ = 0; + *buf++ = 0; + *buf++ = *mask++; + } + } + } + + gimp_temp_buf_unlock (mask_buf, mask_data); + + if (free_mask) + { + gimp_temp_buf_unref ((GimpTempBuf *) mask_buf); + + gimp_brush_end_use (brush); + } + + return return_buf; +} + +static gchar * +gimp_brush_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpBrush *brush = GIMP_BRUSH (viewable); + + return g_strdup_printf ("%s (%d × %d)", + gimp_object_get_name (brush), + gimp_temp_buf_get_width (brush->priv->mask), + gimp_temp_buf_get_height (brush->priv->mask)); +} + +static void +gimp_brush_dirty (GimpData *data) +{ + GimpBrush *brush = GIMP_BRUSH (data); + + if (brush->priv->mask_cache) + gimp_brush_cache_clear (brush->priv->mask_cache); + + if (brush->priv->pixmap_cache) + gimp_brush_cache_clear (brush->priv->pixmap_cache); + + if (brush->priv->boundary_cache) + gimp_brush_cache_clear (brush->priv->boundary_cache); + + gimp_brush_mipmap_clear (brush); + + g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref); + + GIMP_DATA_CLASS (parent_class)->dirty (data); +} + +static const gchar * +gimp_brush_get_extension (GimpData *data) +{ + return GIMP_BRUSH_FILE_EXTENSION; +} + +static void +gimp_brush_copy (GimpData *data, + GimpData *src_data) +{ + GimpBrush *brush = GIMP_BRUSH (data); + GimpBrush *src_brush = GIMP_BRUSH (src_data); + + g_clear_pointer (&brush->priv->mask, gimp_temp_buf_unref); + if (src_brush->priv->mask) + brush->priv->mask = gimp_temp_buf_copy (src_brush->priv->mask); + + g_clear_pointer (&brush->priv->pixmap, gimp_temp_buf_unref); + if (src_brush->priv->pixmap) + brush->priv->pixmap = gimp_temp_buf_copy (src_brush->priv->pixmap); + + brush->priv->spacing = src_brush->priv->spacing; + brush->priv->x_axis = src_brush->priv->x_axis; + brush->priv->y_axis = src_brush->priv->y_axis; + + gimp_data_dirty (data); +} + +static void +gimp_brush_real_begin_use (GimpBrush *brush) +{ + brush->priv->mask_cache = + gimp_brush_cache_new ((GDestroyNotify) gimp_temp_buf_unref, 'M', 'm'); + + brush->priv->pixmap_cache = + gimp_brush_cache_new ((GDestroyNotify) gimp_temp_buf_unref, 'P', 'p'); + + brush->priv->boundary_cache = + gimp_brush_cache_new ((GDestroyNotify) gimp_bezier_desc_free, 'B', 'b'); +} + +static void +gimp_brush_real_end_use (GimpBrush *brush) +{ + g_clear_object (&brush->priv->mask_cache); + g_clear_object (&brush->priv->pixmap_cache); + g_clear_object (&brush->priv->boundary_cache); + + g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref); +} + +static GimpBrush * +gimp_brush_real_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + return brush; +} + +static gboolean +gimp_brush_real_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + return TRUE; +} + +static gchar * +gimp_brush_get_checksum (GimpTagged *tagged) +{ + GimpBrush *brush = GIMP_BRUSH (tagged); + gchar *checksum_string = NULL; + + if (brush->priv->mask) + { + GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5); + + g_checksum_update (checksum, + gimp_temp_buf_get_data (brush->priv->mask), + gimp_temp_buf_get_data_size (brush->priv->mask)); + if (brush->priv->pixmap) + g_checksum_update (checksum, + gimp_temp_buf_get_data (brush->priv->pixmap), + gimp_temp_buf_get_data_size (brush->priv->pixmap)); + g_checksum_update (checksum, + (const guchar *) &brush->priv->spacing, + sizeof (brush->priv->spacing)); + g_checksum_update (checksum, + (const guchar *) &brush->priv->x_axis, + sizeof (brush->priv->x_axis)); + g_checksum_update (checksum, + (const guchar *) &brush->priv->y_axis, + sizeof (brush->priv->y_axis)); + + checksum_string = g_strdup (g_checksum_get_string (checksum)); + + g_checksum_free (checksum); + } + + return checksum_string; +} + +/* public functions */ + +GimpData * +gimp_brush_new (GimpContext *context, + const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + + return gimp_brush_generated_new (name, + GIMP_BRUSH_GENERATED_CIRCLE, + 5.0, 2, 0.5, 1.0, 0.0); +} + +GimpData * +gimp_brush_get_standard (GimpContext *context) +{ + static GimpData *standard_brush = NULL; + + if (! standard_brush) + { + standard_brush = gimp_brush_new (context, "Standard"); + + gimp_data_clean (standard_brush); + gimp_data_make_internal (standard_brush, "gimp-brush-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_brush), + (gpointer *) &standard_brush); + } + + return standard_brush; +} + +void +gimp_brush_begin_use (GimpBrush *brush) +{ + g_return_if_fail (GIMP_IS_BRUSH (brush)); + + brush->priv->use_count++; + + if (brush->priv->use_count == 1) + GIMP_BRUSH_GET_CLASS (brush)->begin_use (brush); +} + +void +gimp_brush_end_use (GimpBrush *brush) +{ + g_return_if_fail (GIMP_IS_BRUSH (brush)); + g_return_if_fail (brush->priv->use_count > 0); + + brush->priv->use_count--; + + if (brush->priv->use_count == 0) + GIMP_BRUSH_GET_CLASS (brush)->end_use (brush); +} + +GimpBrush * +gimp_brush_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + g_return_val_if_fail (last_coords != NULL, NULL); + g_return_val_if_fail (current_coords != NULL, NULL); + + return GIMP_BRUSH_GET_CLASS (brush)->select_brush (brush, + last_coords, + current_coords); +} + +gboolean +gimp_brush_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), FALSE); + g_return_val_if_fail (last_coords != NULL, FALSE); + g_return_val_if_fail (current_coords != NULL, FALSE); + + return GIMP_BRUSH_GET_CLASS (brush)->want_null_motion (brush, + last_coords, + current_coords); +} + +void +gimp_brush_transform_size (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height) +{ + g_return_if_fail (GIMP_IS_BRUSH (brush)); + g_return_if_fail (scale > 0.0); + g_return_if_fail (width != NULL); + g_return_if_fail (height != NULL); + + if (scale == 1.0 && + aspect_ratio == 0.0 && + fmod (angle, 0.5) == 0.0) + { + *width = gimp_temp_buf_get_width (brush->priv->mask); + *height = gimp_temp_buf_get_height (brush->priv->mask); + + return; + } + + GIMP_BRUSH_GET_CLASS (brush)->transform_size (brush, + scale, aspect_ratio, angle, reflect, + width, height); +} + +const GimpTempBuf * +gimp_brush_transform_mask (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + const GimpTempBuf *mask; + gint width; + gint height; + gdouble effective_hardness = hardness; + + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + g_return_val_if_fail (scale > 0.0, NULL); + + gimp_brush_transform_size (brush, + scale, aspect_ratio, angle, reflect, + &width, &height); + + mask = gimp_brush_cache_get (brush->priv->mask_cache, + width, height, + scale, aspect_ratio, angle, reflect, hardness); + + if (! mask) + { +#if 0 + /* This code makes sure that brushes using blur for hardness + * (all of them but generated) are blurred once and no more. + * It also makes hardnes dynamics not work for these brushes. + * This is intentional. Confoliving for each stamp is too expensive.*/ + if (! brush->priv->blurred_mask && + ! GIMP_IS_BRUSH_GENERATED(brush) && + ! GIMP_IS_BRUSH_PIPE(brush) && /*Can't cache pipes. Sanely anyway*/ + hardness < 1.0) + { + brush->priv->blurred_mask = GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush, + 1.0, + 0.0, + 0.0, + FALSE, + hardness); + brush->priv->blur_hardness = hardness; + } + + if (brush->priv->blurred_mask) + { + effective_hardness = 1.0; /*Hardness has already been applied*/ + } +#endif + + mask = GIMP_BRUSH_GET_CLASS (brush)->transform_mask (brush, + scale, + aspect_ratio, + angle, + reflect, + effective_hardness); + + gimp_brush_cache_add (brush->priv->mask_cache, + (gpointer) mask, + width, height, + scale, aspect_ratio, angle, reflect, effective_hardness); + } + + return mask; +} + +const GimpTempBuf * +gimp_brush_transform_pixmap (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + const GimpTempBuf *pixmap; + gint width; + gint height; + gdouble effective_hardness = hardness; + + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + g_return_val_if_fail (brush->priv->pixmap != NULL, NULL); + g_return_val_if_fail (scale > 0.0, NULL); + + gimp_brush_transform_size (brush, + scale, aspect_ratio, angle, reflect, + &width, &height); + + pixmap = gimp_brush_cache_get (brush->priv->pixmap_cache, + width, height, + scale, aspect_ratio, angle, reflect, hardness); + + if (! pixmap) + { +#if 0 + if (! brush->priv->blurred_pixmap && + ! GIMP_IS_BRUSH_GENERATED(brush) && + ! GIMP_IS_BRUSH_PIPE(brush) /*Can't cache pipes. Sanely anyway*/ + && hardness < 1.0) + { + brush->priv->blurred_pixmap = GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush, + 1.0, + 0.0, + 0.0, + FALSE, + hardness); + brush->priv->blur_hardness = hardness; + } + + if (brush->priv->blurred_pixmap) { + effective_hardness = 1.0; /*Hardness has already been applied*/ + } +#endif + + pixmap = GIMP_BRUSH_GET_CLASS (brush)->transform_pixmap (brush, + scale, + aspect_ratio, + angle, + reflect, + effective_hardness); + + gimp_brush_cache_add (brush->priv->pixmap_cache, + (gpointer) pixmap, + width, height, + scale, aspect_ratio, angle, reflect, effective_hardness); + } + + return pixmap; +} + +const GimpBezierDesc * +gimp_brush_transform_boundary (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness, + gint *width, + gint *height) +{ + const GimpBezierDesc *boundary; + + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + g_return_val_if_fail (scale > 0.0, NULL); + g_return_val_if_fail (width != NULL, NULL); + g_return_val_if_fail (height != NULL, NULL); + + gimp_brush_transform_size (brush, + scale, aspect_ratio, angle, reflect, + width, height); + + boundary = gimp_brush_cache_get (brush->priv->boundary_cache, + *width, *height, + scale, aspect_ratio, angle, reflect, hardness); + + if (! boundary) + { + boundary = GIMP_BRUSH_GET_CLASS (brush)->transform_boundary (brush, + scale, + aspect_ratio, + angle, + reflect, + hardness, + width, + height); + + /* while the brush mask is always at least 1x1 pixels, its + * outline can correctly be NULL + * + * FIXME: make the cache handle NULL things when it is + * properly implemented + */ + if (boundary) + gimp_brush_cache_add (brush->priv->boundary_cache, + (gpointer) boundary, + *width, *height, + scale, aspect_ratio, angle, reflect, hardness); + } + + return boundary; +} + +GimpTempBuf * +gimp_brush_get_mask (GimpBrush *brush) +{ + g_return_val_if_fail (brush != NULL, NULL); + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + + if (brush->priv->blurred_mask) + { + return brush->priv->blurred_mask; + } + return brush->priv->mask; +} + +GimpTempBuf * +gimp_brush_get_pixmap (GimpBrush *brush) +{ + g_return_val_if_fail (brush != NULL, NULL); + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + + if(brush->priv->blurred_pixmap) + { + return brush->priv->blurred_pixmap; + } + return brush->priv->pixmap; +} + +void +gimp_brush_flush_blur_caches (GimpBrush *brush) +{ +#if 0 + g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref); + + if (brush->priv->mask_cache) + gimp_brush_cache_clear (brush->priv->mask_cache); + + if (brush->priv->pixmap_cache) + gimp_brush_cache_clear (brush->priv->pixmap_cache); + + if (brush->priv->boundary_cache) + gimp_brush_cache_clear (brush->priv->boundary_cache); +#endif +} + +gdouble +gimp_brush_get_blur_hardness (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0); + + return brush->priv->blur_hardness; +} + +gint +gimp_brush_get_width (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0); + + if (brush->priv->blurred_mask) + return gimp_temp_buf_get_width (brush->priv->blurred_mask); + + if (brush->priv->blurred_pixmap) + return gimp_temp_buf_get_width (brush->priv->blurred_pixmap); + + return gimp_temp_buf_get_width (brush->priv->mask); +} + +gint +gimp_brush_get_height (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0); + + if (brush->priv->blurred_mask) + return gimp_temp_buf_get_height (brush->priv->blurred_mask); + + if (brush->priv->blurred_pixmap) + return gimp_temp_buf_get_height (brush->priv->blurred_pixmap); + + return gimp_temp_buf_get_height (brush->priv->mask); +} + +gint +gimp_brush_get_spacing (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), 0); + + return brush->priv->spacing; +} + +void +gimp_brush_set_spacing (GimpBrush *brush, + gint spacing) +{ + g_return_if_fail (GIMP_IS_BRUSH (brush)); + + if (brush->priv->spacing != spacing) + { + brush->priv->spacing = spacing; + + g_signal_emit (brush, brush_signals[SPACING_CHANGED], 0); + g_object_notify (G_OBJECT (brush), "spacing"); + } +} + +static const GimpVector2 fail = { 0.0, 0.0 }; + +GimpVector2 +gimp_brush_get_x_axis (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), fail); + + return brush->priv->x_axis; +} + +GimpVector2 +gimp_brush_get_y_axis (GimpBrush *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH (brush), fail); + + return brush->priv->y_axis; +} diff --git a/app/core/gimpbrush.h b/app/core/gimpbrush.h new file mode 100644 index 0000000..b5735f0 --- /dev/null +++ b/app/core/gimpbrush.h @@ -0,0 +1,152 @@ +/* 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 . + */ + +#ifndef __GIMP_BRUSH_H__ +#define __GIMP_BRUSH_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_BRUSH (gimp_brush_get_type ()) +#define GIMP_BRUSH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH, GimpBrush)) +#define GIMP_BRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH, GimpBrushClass)) +#define GIMP_IS_BRUSH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH)) +#define GIMP_IS_BRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH)) +#define GIMP_BRUSH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH, GimpBrushClass)) + + +typedef struct _GimpBrushPrivate GimpBrushPrivate; +typedef struct _GimpBrushClass GimpBrushClass; + +struct _GimpBrush +{ + GimpData parent_instance; + + GimpBrushPrivate *priv; +}; + +struct _GimpBrushClass +{ + GimpDataClass parent_class; + + /* virtual functions */ + void (* begin_use) (GimpBrush *brush); + void (* end_use) (GimpBrush *brush); + GimpBrush * (* select_brush) (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); + gboolean (* want_null_motion) (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); + void (* transform_size) (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height); + GimpTempBuf * (* transform_mask) (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); + GimpTempBuf * (* transform_pixmap) (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); + GimpBezierDesc * (* transform_boundary) (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness, + gint *width, + gint *height); + + /* signals */ + void (* spacing_changed) (GimpBrush *brush); +}; + + +GType gimp_brush_get_type (void) G_GNUC_CONST; + +GimpData * gimp_brush_new (GimpContext *context, + const gchar *name); +GimpData * gimp_brush_get_standard (GimpContext *context); + +void gimp_brush_begin_use (GimpBrush *brush); +void gimp_brush_end_use (GimpBrush *brush); + +GimpBrush * gimp_brush_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); +gboolean gimp_brush_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); + +/* Gets width and height of a transformed mask of the brush, for + * provided parameters. + */ +void gimp_brush_transform_size (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height); +const GimpTempBuf * gimp_brush_transform_mask (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); +const GimpTempBuf * gimp_brush_transform_pixmap (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); +const GimpBezierDesc * gimp_brush_transform_boundary (GimpBrush *brush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness, + gint *width, + gint *height); + +GimpTempBuf * gimp_brush_get_mask (GimpBrush *brush); +GimpTempBuf * gimp_brush_get_pixmap (GimpBrush *brush); + +gint gimp_brush_get_width (GimpBrush *brush); +gint gimp_brush_get_height (GimpBrush *brush); + +gint gimp_brush_get_spacing (GimpBrush *brush); +void gimp_brush_set_spacing (GimpBrush *brush, + gint spacing); + +GimpVector2 gimp_brush_get_x_axis (GimpBrush *brush); +GimpVector2 gimp_brush_get_y_axis (GimpBrush *brush); + +void gimp_brush_flush_blur_caches (GimpBrush *brush); +gdouble gimp_brush_get_blur_hardness (GimpBrush *brush); + +#endif /* __GIMP_BRUSH_H__ */ diff --git a/app/core/gimpbrushcache.c b/app/core/gimpbrushcache.c new file mode 100644 index 0000000..07ad008 --- /dev/null +++ b/app/core/gimpbrushcache.c @@ -0,0 +1,299 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrushcache.c + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpbrushcache.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +#define MAX_CACHED_DATA 20 + + +enum +{ + PROP_0, + PROP_DATA_DESTROY +}; + + +typedef struct _GimpBrushCacheUnit GimpBrushCacheUnit; + +struct _GimpBrushCacheUnit +{ + gpointer data; + + gint width; + gint height; + gdouble scale; + gdouble aspect_ratio; + gdouble angle; + gboolean reflect; + gdouble hardness; +}; + + +static void gimp_brush_cache_constructed (GObject *object); +static void gimp_brush_cache_finalize (GObject *object); +static void gimp_brush_cache_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_brush_cache_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpBrushCache, gimp_brush_cache, GIMP_TYPE_OBJECT) + +#define parent_class gimp_brush_cache_parent_class + + +static void +gimp_brush_cache_class_init (GimpBrushCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = gimp_brush_cache_constructed; + object_class->finalize = gimp_brush_cache_finalize; + object_class->set_property = gimp_brush_cache_set_property; + object_class->get_property = gimp_brush_cache_get_property; + + g_object_class_install_property (object_class, PROP_DATA_DESTROY, + g_param_spec_pointer ("data-destroy", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_brush_cache_init (GimpBrushCache *brush) +{ +} + +static void +gimp_brush_cache_constructed (GObject *object) +{ + GimpBrushCache *cache = GIMP_BRUSH_CACHE (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (cache->data_destroy != NULL); +} + +static void +gimp_brush_cache_finalize (GObject *object) +{ + GimpBrushCache *cache = GIMP_BRUSH_CACHE (object); + + gimp_brush_cache_clear (cache); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_brush_cache_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpBrushCache *cache = GIMP_BRUSH_CACHE (object); + + switch (property_id) + { + case PROP_DATA_DESTROY: + cache->data_destroy = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brush_cache_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpBrushCache *cache = GIMP_BRUSH_CACHE (object); + + switch (property_id) + { + case PROP_DATA_DESTROY: + g_value_set_pointer (value, cache->data_destroy); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +GimpBrushCache * +gimp_brush_cache_new (GDestroyNotify data_destroy, + gchar debug_hit, + gchar debug_miss) +{ + GimpBrushCache *cache; + + g_return_val_if_fail (data_destroy != NULL, NULL); + + cache = g_object_new (GIMP_TYPE_BRUSH_CACHE, + "data-destroy", data_destroy, + NULL); + + cache->debug_hit = debug_hit; + cache->debug_miss = debug_miss; + + return cache; +} + +void +gimp_brush_cache_clear (GimpBrushCache *cache) +{ + g_return_if_fail (GIMP_IS_BRUSH_CACHE (cache)); + + if (cache->cached_units) + { + GList *iter; + + for (iter = cache->cached_units; iter; iter = g_list_next (iter)) + { + GimpBrushCacheUnit *unit = iter->data; + + cache->data_destroy (unit->data); + } + + g_list_free_full (cache->cached_units, g_free); + cache->cached_units = NULL; + } +} + +gconstpointer +gimp_brush_cache_get (GimpBrushCache *cache, + gint width, + gint height, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + GList *iter; + + g_return_val_if_fail (GIMP_IS_BRUSH_CACHE (cache), NULL); + + for (iter = cache->cached_units; iter; iter = g_list_next (iter)) + { + GimpBrushCacheUnit *unit = iter->data; + + if (unit->data && + unit->width == width && + unit->height == height && + unit->scale == scale && + unit->aspect_ratio == aspect_ratio && + unit->angle == angle && + unit->reflect == reflect && + unit->hardness == hardness) + { + if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE) + g_printerr ("%c", cache->debug_hit); + + /* Make the returned cached brush first in the list. */ + cache->cached_units = g_list_remove_link (cache->cached_units, iter); + iter->next = cache->cached_units; + if (cache->cached_units) + cache->cached_units->prev = iter; + cache->cached_units = iter; + + return (gconstpointer) unit->data; + } + } + + if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE) + g_printerr ("%c", cache->debug_miss); + + return NULL; +} + +void +gimp_brush_cache_add (GimpBrushCache *cache, + gpointer data, + gint width, + gint height, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + GList *iter; + GimpBrushCacheUnit *unit; + GList *last = NULL; + gint length = 0; + + g_return_if_fail (GIMP_IS_BRUSH_CACHE (cache)); + g_return_if_fail (data != NULL); + + for (iter = cache->cached_units; iter; iter = g_list_next (iter)) + { + unit = iter->data; + + if (data == unit->data) + return; + + length++; + last = iter; + } + + if (length > MAX_CACHED_DATA) + { + unit = last->data; + + cache->data_destroy (unit->data); + cache->cached_units = g_list_delete_link (cache->cached_units, last); + g_free (unit); + } + + unit = g_new0 (GimpBrushCacheUnit, 1); + + unit->data = data; + unit->width = width; + unit->height = height; + unit->scale = scale; + unit->aspect_ratio = aspect_ratio; + unit->angle = angle; + unit->reflect = reflect; + unit->hardness = hardness; + + cache->cached_units = g_list_prepend (cache->cached_units, unit); +} diff --git a/app/core/gimpbrushcache.h b/app/core/gimpbrushcache.h new file mode 100644 index 0000000..7722117 --- /dev/null +++ b/app/core/gimpbrushcache.h @@ -0,0 +1,83 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrushcache.h + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRUSH_CACHE_H__ +#define __GIMP_BRUSH_CACHE_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_BRUSH_CACHE (gimp_brush_cache_get_type ()) +#define GIMP_BRUSH_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_CACHE, GimpBrushCache)) +#define GIMP_BRUSH_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_CACHE, GimpBrushCacheClass)) +#define GIMP_IS_BRUSH_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_CACHE)) +#define GIMP_IS_BRUSH_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_CACHE)) +#define GIMP_BRUSH_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_CACHE, GimpBrushCacheClass)) + + +typedef struct _GimpBrushCacheClass GimpBrushCacheClass; + +struct _GimpBrushCache +{ + GimpObject parent_instance; + + GDestroyNotify data_destroy; + + GList *cached_units; + + gchar debug_hit; + gchar debug_miss; +}; + +struct _GimpBrushCacheClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_brush_cache_get_type (void) G_GNUC_CONST; + +GimpBrushCache * gimp_brush_cache_new (GDestroyNotify data_destory, + gchar debug_hit, + gchar debug_miss); + +void gimp_brush_cache_clear (GimpBrushCache *cache); + +gconstpointer gimp_brush_cache_get (GimpBrushCache *cache, + gint width, + gint height, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); +void gimp_brush_cache_add (GimpBrushCache *cache, + gpointer data, + gint width, + gint height, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); + + +#endif /* __GIMP_BRUSH_CACHE_H__ */ diff --git a/app/core/gimpbrushclipboard.c b/app/core/gimpbrushclipboard.c new file mode 100644 index 0000000..2d4a5e1 --- /dev/null +++ b/app/core/gimpbrushclipboard.c @@ -0,0 +1,298 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrushclipboard.c + * Copyright (C) 2006 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpbuffer.h" +#include "gimpbrush-private.h" +#include "gimpbrushclipboard.h" +#include "gimpimage.h" +#include "gimppickable.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +#define BRUSH_MAX_SIZE 1024 + +enum +{ + PROP_0, + PROP_GIMP, + PROP_MASK_ONLY +}; + + +/* local function prototypes */ + +static void gimp_brush_clipboard_constructed (GObject *object); +static void gimp_brush_clipboard_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_brush_clipboard_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static GimpData * gimp_brush_clipboard_duplicate (GimpData *data); + +static void gimp_brush_clipboard_changed (Gimp *gimp, + GimpBrush *brush); + + +G_DEFINE_TYPE (GimpBrushClipboard, gimp_brush_clipboard, GIMP_TYPE_BRUSH) + +#define parent_class gimp_brush_clipboard_parent_class + + +static void +gimp_brush_clipboard_class_init (GimpBrushClipboardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->constructed = gimp_brush_clipboard_constructed; + object_class->set_property = gimp_brush_clipboard_set_property; + object_class->get_property = gimp_brush_clipboard_get_property; + + data_class->duplicate = gimp_brush_clipboard_duplicate; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_MASK_ONLY, + g_param_spec_boolean ("mask-only", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_brush_clipboard_init (GimpBrushClipboard *brush) +{ +} + +static void +gimp_brush_clipboard_constructed (GObject *object) +{ + GimpBrushClipboard *brush = GIMP_BRUSH_CLIPBOARD (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (brush->gimp)); + + g_signal_connect_object (brush->gimp, "clipboard-changed", + G_CALLBACK (gimp_brush_clipboard_changed), + brush, 0); + + gimp_brush_clipboard_changed (brush->gimp, GIMP_BRUSH (brush)); +} + +static void +gimp_brush_clipboard_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpBrushClipboard *brush = GIMP_BRUSH_CLIPBOARD (object); + + switch (property_id) + { + case PROP_GIMP: + brush->gimp = g_value_get_object (value); + break; + + case PROP_MASK_ONLY: + brush->mask_only = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brush_clipboard_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpBrushClipboard *brush = GIMP_BRUSH_CLIPBOARD (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, brush->gimp); + break; + + case PROP_MASK_ONLY: + g_value_set_boolean (value, brush->mask_only); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GimpData * +gimp_brush_clipboard_duplicate (GimpData *data) +{ + GimpData *new = g_object_new (GIMP_TYPE_BRUSH, NULL); + + gimp_data_copy (new, data); + + return new; +} + +GimpData * +gimp_brush_clipboard_new (Gimp *gimp, + gboolean mask_only) +{ + const gchar *name; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (mask_only) + name = _("Clipboard Mask"); + else + name = _("Clipboard Image"); + + return g_object_new (GIMP_TYPE_BRUSH_CLIPBOARD, + "name", name, + "gimp", gimp, + "mask-only", mask_only, + NULL); +} + + +/* private functions */ + +static void +gimp_brush_clipboard_changed (Gimp *gimp, + GimpBrush *brush) +{ + GimpObject *paste; + GeglBuffer *buffer = NULL; + gint width; + gint height; + + g_clear_pointer (&brush->priv->mask, gimp_temp_buf_unref); + g_clear_pointer (&brush->priv->pixmap, gimp_temp_buf_unref); + + paste = gimp_get_clipboard_object (gimp); + + if (GIMP_IS_IMAGE (paste)) + { + gimp_pickable_flush (GIMP_PICKABLE (paste)); + buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (paste)); + } + else if (GIMP_IS_BUFFER (paste)) + { + buffer = gimp_buffer_get_buffer (GIMP_BUFFER (paste)); + } + + if (buffer) + { + const Babl *format = gegl_buffer_get_format (buffer); + + width = MIN (gegl_buffer_get_width (buffer), BRUSH_MAX_SIZE); + height = MIN (gegl_buffer_get_height (buffer), BRUSH_MAX_SIZE); + + brush->priv->mask = gimp_temp_buf_new (width, height, + babl_format ("Y u8")); + + if (GIMP_BRUSH_CLIPBOARD (brush)->mask_only) + { + guchar *p; + gint i; + + gegl_buffer_get (buffer, + GEGL_RECTANGLE (0, 0, width, height), 1.0, + babl_format ("Y u8"), + gimp_temp_buf_get_data (brush->priv->mask), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + /* invert the mask, it's more intuitive to think + * "black on white" than the other way around + */ + for (i = 0, p = gimp_temp_buf_get_data (brush->priv->mask); + i < width * height; + i++, p++) + { + *p = 255 - *p; + } + } + else + { + brush->priv->pixmap = gimp_temp_buf_new (width, height, + babl_format ("R'G'B' u8")); + + /* copy the alpha channel into the brush's mask */ + if (babl_format_has_alpha (format)) + { + gegl_buffer_get (buffer, + GEGL_RECTANGLE (0, 0, width, height), 1.0, + babl_format ("A u8"), + gimp_temp_buf_get_data (brush->priv->mask), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + else + { + memset (gimp_temp_buf_get_data (brush->priv->mask), 255, + width * height); + } + + /* copy the color channels into the brush's pixmap */ + gegl_buffer_get (buffer, + GEGL_RECTANGLE (0, 0, width, height), 1.0, + babl_format ("R'G'B' u8"), + gimp_temp_buf_get_data (brush->priv->pixmap), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + } + else + { + width = 17; + height = 17; + + brush->priv->mask = gimp_temp_buf_new (width, height, + babl_format ("Y u8")); + gimp_temp_buf_data_clear (brush->priv->mask); + } + + brush->priv->x_axis.x = width / 2; + brush->priv->x_axis.y = 0; + brush->priv->y_axis.x = 0; + brush->priv->y_axis.y = height / 2; + + gimp_data_dirty (GIMP_DATA (brush)); +} diff --git a/app/core/gimpbrushclipboard.h b/app/core/gimpbrushclipboard.h new file mode 100644 index 0000000..95af7e1 --- /dev/null +++ b/app/core/gimpbrushclipboard.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrushclipboard.h + * Copyright (C) 2006 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRUSH_CLIPBOARD_H__ +#define __GIMP_BRUSH_CLIPBOARD_H__ + + +#include "gimpbrush.h" + + +#define GIMP_TYPE_BRUSH_CLIPBOARD (gimp_brush_clipboard_get_type ()) +#define GIMP_BRUSH_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_CLIPBOARD, GimpBrushClipboard)) +#define GIMP_BRUSH_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_CLIPBOARD, GimpBrushClipboardClass)) +#define GIMP_IS_BRUSH_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_CLIPBOARD)) +#define GIMP_IS_BRUSH_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_CLIPBOARD)) +#define GIMP_BRUSH_CLIPBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_CLIPBOARD, GimpBrushClipboardClass)) + + +typedef struct _GimpBrushClipboardClass GimpBrushClipboardClass; + +struct _GimpBrushClipboard +{ + GimpBrush parent_instance; + + Gimp *gimp; + gboolean mask_only; +}; + +struct _GimpBrushClipboardClass +{ + GimpBrushClass parent_class; +}; + + +GType gimp_brush_clipboard_get_type (void) G_GNUC_CONST; + +GimpData * gimp_brush_clipboard_new (Gimp *gimp, + gboolean mask_only); + + +#endif /* __GIMP_BRUSH_CLIPBOARD_H__ */ diff --git a/app/core/gimpbrushgenerated-load.c b/app/core/gimpbrushgenerated-load.c new file mode 100644 index 0000000..a526ae6 --- /dev/null +++ b/app/core/gimpbrushgenerated-load.c @@ -0,0 +1,284 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp_brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp-utils.h" +#include "gimpbrushgenerated.h" +#include "gimpbrushgenerated-load.h" + +#include "gimp-intl.h" + + +GList * +gimp_brush_generated_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpBrush *brush; + GDataInputStream *data_input; + gchar *string; + gsize string_len; + gint linenum; + gchar *name = NULL; + GimpBrushGeneratedShape shape = GIMP_BRUSH_GENERATED_CIRCLE; + gboolean have_shape = FALSE; + gint spikes = 2; + gdouble spacing; + gdouble radius; + gdouble hardness; + gdouble aspect_ratio; + gdouble angle; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data_input = g_data_input_stream_new (input); + + /* make sure the file we are reading is the right type */ + linenum = 1; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + + if (! g_str_has_prefix (string, "GIMP-VBR")) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Not a GIMP brush file.")); + g_free (string); + goto failed; + } + + g_free (string); + + /* make sure we are reading a compatible version */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + + if (! g_str_has_prefix (string, "1.0")) + { + if (! g_str_has_prefix (string, "1.5")) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unknown GIMP brush version.")); + g_free (string); + goto failed; + } + else + { + have_shape = TRUE; + } + } + + g_free (string); + + /* read name */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + + g_strstrip (string); + + /* the empty string is not an allowed name */ + if (strlen (string) < 1) + { + name = g_strdup (_("Untitled")); + } + else + { + name = gimp_any_to_utf8 (string, -1, + _("Invalid UTF-8 string in brush file '%s'."), + gimp_file_get_utf8_name (file)); + } + + g_free (string); + + if (have_shape) + { + GEnumClass *enum_class; + GEnumValue *shape_val; + + enum_class = g_type_class_peek (GIMP_TYPE_BRUSH_GENERATED_SHAPE); + + /* read shape */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + + g_strstrip (string); + shape_val = g_enum_get_value_by_nick (enum_class, string); + + if (! shape_val) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unknown GIMP brush shape.")); + g_free (string); + goto failed; + } + + g_free (string); + + shape = shape_val->value; + } + + /* read brush spacing */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtod (string, NULL, &spacing)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush spacing.")); + g_free (string); + goto failed; + } + g_free (string); + + + /* read brush radius */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtod (string, NULL, &radius)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush radius.")); + g_free (string); + goto failed; + } + g_free (string); + + if (have_shape) + { + /* read number of spikes */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtoi (string, NULL, 10, &spikes) || + spikes < 2 || spikes > 20) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush spike count.")); + g_free (string); + goto failed; + } + g_free (string); + } + + /* read brush hardness */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtod (string, NULL, &hardness)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush hardness.")); + g_free (string); + goto failed; + } + g_free (string); + + /* read brush aspect_ratio */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtod (string, NULL, &aspect_ratio)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush aspect ratio.")); + g_free (string); + goto failed; + } + g_free (string); + + /* read brush angle */ + linenum++; + string_len = 256; + string = gimp_data_input_stream_read_line_always (data_input, &string_len, + NULL, error); + if (! string) + goto failed; + if (! gimp_ascii_strtod (string, NULL, &angle)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid brush angle.")); + g_free (string); + goto failed; + } + g_free (string); + + g_object_unref (data_input); + + brush = GIMP_BRUSH (gimp_brush_generated_new (name, shape, radius, spikes, + hardness, aspect_ratio, angle)); + g_free (name); + + gimp_brush_set_spacing (brush, spacing); + + return g_list_prepend (NULL, brush); + + failed: + + g_object_unref (data_input); + + if (name) + g_free (name); + + g_prefix_error (error, _("In line %d of brush file: "), linenum); + + return NULL; +} diff --git a/app/core/gimpbrushgenerated-load.h b/app/core/gimpbrushgenerated-load.h new file mode 100644 index 0000000..afecb3b --- /dev/null +++ b/app/core/gimpbrushgenerated-load.h @@ -0,0 +1,33 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRUSH_GENERATED_LOAD_H__ +#define __GIMP_BRUSH_GENERATED_LOAD_H__ + + +#define GIMP_BRUSH_GENERATED_FILE_EXTENSION ".vbr" + + +GList * gimp_brush_generated_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_BRUSH_GENERATED_LOAD_H__ */ diff --git a/app/core/gimpbrushgenerated-save.c b/app/core/gimpbrushgenerated-save.c new file mode 100644 index 0000000..2b9f8a0 --- /dev/null +++ b/app/core/gimpbrushgenerated-save.c @@ -0,0 +1,119 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp_brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpbrushgenerated.h" +#include "gimpbrushgenerated-save.h" + +#include "gimp-intl.h" + + +gboolean +gimp_brush_generated_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data); + const gchar *name = gimp_object_get_name (data); + GString *string; + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + gboolean have_shape = FALSE; + + g_return_val_if_fail (name != NULL && *name != '\0', FALSE); + + /* write magic header */ + string = g_string_new ("GIMP-VBR\n"); + + /* write version */ + if (brush->shape != GIMP_BRUSH_GENERATED_CIRCLE || brush->spikes > 2) + { + g_string_append (string, "1.5\n"); + have_shape = TRUE; + } + else + { + g_string_append (string, "1.0\n"); + } + + /* write name */ + g_string_append_printf (string, "%.255s\n", name); + + if (have_shape) + { + GEnumClass *enum_class; + GEnumValue *shape_val; + + enum_class = g_type_class_peek (GIMP_TYPE_BRUSH_GENERATED_SHAPE); + + /* write shape */ + shape_val = g_enum_get_value (enum_class, brush->shape); + g_string_append_printf (string, "%s\n", shape_val->value_nick); + } + + /* write brush spacing */ + g_string_append_printf (string, "%s\n", + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + gimp_brush_get_spacing (GIMP_BRUSH (brush)))); + + /* write brush radius */ + g_string_append_printf (string, "%s\n", + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + brush->radius)); + + if (have_shape) + { + /* write brush spikes */ + g_string_append_printf (string, "%d\n", brush->spikes); + } + + /* write brush hardness */ + g_string_append_printf (string, "%s\n", + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + brush->hardness)); + + /* write brush aspect_ratio */ + g_string_append_printf (string, "%s\n", + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + brush->aspect_ratio)); + + /* write brush angle */ + g_string_append_printf (string, "%s\n", + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + brush->angle)); + + if (! 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; +} diff --git a/app/core/gimpbrushgenerated-save.h b/app/core/gimpbrushgenerated-save.h new file mode 100644 index 0000000..b33d82e --- /dev/null +++ b/app/core/gimpbrushgenerated-save.h @@ -0,0 +1,30 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRUSH_GENERATED_SAVE_H__ +#define __GIMP_BRUSH_GENERATED_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_brush_generated_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_BRUSH_GENERATED_SAVE_H__ */ diff --git a/app/core/gimpbrushgenerated.c b/app/core/gimpbrushgenerated.c new file mode 100644 index 0000000..dacdaa8 --- /dev/null +++ b/app/core/gimpbrushgenerated.c @@ -0,0 +1,875 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp_brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpbrush-private.h" +#include "gimpbrushgenerated.h" +#include "gimpbrushgenerated-load.h" +#include "gimpbrushgenerated-save.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +#define OVERSAMPLING 4 + + +enum +{ + PROP_0, + PROP_SHAPE, + PROP_RADIUS, + PROP_SPIKES, + PROP_HARDNESS, + PROP_ASPECT_RATIO, + PROP_ANGLE +}; + + +/* local function prototypes */ + +static void gimp_brush_generated_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_brush_generated_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_brush_generated_dirty (GimpData *data); +static const gchar * gimp_brush_generated_get_extension (GimpData *data); +static void gimp_brush_generated_copy (GimpData *data, + GimpData *src_data); + +static void gimp_brush_generated_transform_size(GimpBrush *gbrush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height); +static GimpTempBuf * gimp_brush_generated_transform_mask(GimpBrush *gbrush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness); + +static GimpTempBuf * gimp_brush_generated_calc (GimpBrushGenerated *brush, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gfloat angle, + gboolean reflect, + GimpVector2 *xaxis, + GimpVector2 *yaxis); +static void gimp_brush_generated_get_size (GimpBrushGenerated *gbrush, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gdouble angle_in_degrees, + gboolean reflect, + gint *width, + gint *height, + gdouble *_s, + gdouble *_c, + GimpVector2 *_x_axis, + GimpVector2 *_y_axis); + + +G_DEFINE_TYPE (GimpBrushGenerated, gimp_brush_generated, GIMP_TYPE_BRUSH) + +#define parent_class gimp_brush_generated_parent_class + + +static void +gimp_brush_generated_class_init (GimpBrushGeneratedClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + GimpBrushClass *brush_class = GIMP_BRUSH_CLASS (klass); + + object_class->set_property = gimp_brush_generated_set_property; + object_class->get_property = gimp_brush_generated_get_property; + + data_class->save = gimp_brush_generated_save; + data_class->dirty = gimp_brush_generated_dirty; + data_class->get_extension = gimp_brush_generated_get_extension; + data_class->copy = gimp_brush_generated_copy; + + brush_class->transform_size = gimp_brush_generated_transform_size; + brush_class->transform_mask = gimp_brush_generated_transform_mask; + + g_object_class_install_property (object_class, PROP_SHAPE, + g_param_spec_enum ("shape", NULL, + _("Brush Shape"), + GIMP_TYPE_BRUSH_GENERATED_SHAPE, + GIMP_BRUSH_GENERATED_CIRCLE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RADIUS, + g_param_spec_double ("radius", NULL, + _("Brush Radius"), + 0.1, 4000.0, 5.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SPIKES, + g_param_spec_int ("spikes", NULL, + _("Brush Spikes"), + 2, 20, 2, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_HARDNESS, + g_param_spec_double ("hardness", NULL, + _("Brush Hardness"), + 0.0, 1.0, 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ASPECT_RATIO, + g_param_spec_double ("aspect-ratio", + NULL, + _("Brush Aspect Ratio"), + 1.0, 20.0, 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE, + g_param_spec_double ("angle", NULL, + _("Brush Angle"), + 0.0, 180.0, 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_brush_generated_init (GimpBrushGenerated *brush) +{ + brush->shape = GIMP_BRUSH_GENERATED_CIRCLE; + brush->radius = 5.0; + brush->hardness = 0.0; + brush->aspect_ratio = 1.0; + brush->angle = 0.0; +} + +static void +gimp_brush_generated_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (object); + + switch (property_id) + { + case PROP_SHAPE: + gimp_brush_generated_set_shape (brush, g_value_get_enum (value)); + break; + case PROP_RADIUS: + gimp_brush_generated_set_radius (brush, g_value_get_double (value)); + break; + case PROP_SPIKES: + gimp_brush_generated_set_spikes (brush, g_value_get_int (value)); + break; + case PROP_HARDNESS: + gimp_brush_generated_set_hardness (brush, g_value_get_double (value)); + break; + case PROP_ASPECT_RATIO: + gimp_brush_generated_set_aspect_ratio (brush, g_value_get_double (value)); + break; + case PROP_ANGLE: + gimp_brush_generated_set_angle (brush, g_value_get_double (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brush_generated_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (object); + + switch (property_id) + { + case PROP_SHAPE: + g_value_set_enum (value, brush->shape); + break; + case PROP_RADIUS: + g_value_set_double (value, brush->radius); + break; + case PROP_SPIKES: + g_value_set_int (value, brush->spikes); + break; + case PROP_HARDNESS: + g_value_set_double (value, brush->hardness); + break; + case PROP_ASPECT_RATIO: + g_value_set_double (value, brush->aspect_ratio); + break; + case PROP_ANGLE: + g_value_set_double (value, brush->angle); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brush_generated_dirty (GimpData *data) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data); + GimpBrush *gbrush = GIMP_BRUSH (brush); + + g_clear_pointer (&gbrush->priv->mask, gimp_temp_buf_unref); + + gbrush->priv->mask = gimp_brush_generated_calc (brush, + brush->shape, + brush->radius, + brush->spikes, + brush->hardness, + brush->aspect_ratio, + brush->angle, + FALSE, + &gbrush->priv->x_axis, + &gbrush->priv->y_axis); + + GIMP_DATA_CLASS (parent_class)->dirty (data); +} + +static const gchar * +gimp_brush_generated_get_extension (GimpData *data) +{ + return GIMP_BRUSH_GENERATED_FILE_EXTENSION; +} + +static void +gimp_brush_generated_copy (GimpData *data, + GimpData *src_data) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data); + GimpBrushGenerated *src_brush = GIMP_BRUSH_GENERATED (src_data); + + gimp_data_freeze (data); + + gimp_brush_set_spacing (GIMP_BRUSH (brush), + gimp_brush_get_spacing (GIMP_BRUSH (src_brush))); + + brush->shape = src_brush->shape; + brush->radius = src_brush->radius; + brush->spikes = src_brush->spikes; + brush->hardness = src_brush->hardness; + brush->aspect_ratio = src_brush->aspect_ratio; + brush->angle = src_brush->angle; + + gimp_data_thaw (data); +} + +static void +gimp_brush_generated_transform_size (GimpBrush *gbrush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gint *width, + gint *height) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (gbrush); + gdouble ratio; + + ratio = fabs (aspect_ratio) * 19.0 / 20.0 + 1.0; + ratio = MIN (ratio, 20); + + /* Since generated brushes are symmetric they don't have aspect + * ratios < 1.0. it's the same as rotating by 90 degrees and 1 / + * ratio, so we fix the input for this case. + */ + if (aspect_ratio < 0.0) + angle = angle + 0.25; + + angle = fmod (fmod (angle, 1.0) + 1.0, 1.0); + angle *= 360; + + gimp_brush_generated_get_size (brush, + brush->shape, + brush->radius * scale, + brush->spikes, + brush->hardness, + ratio, + angle, + reflect, + width, height, + NULL, NULL, NULL, NULL); +} + +static GimpTempBuf * +gimp_brush_generated_transform_mask (GimpBrush *gbrush, + gdouble scale, + gdouble aspect_ratio, + gdouble angle, + gboolean reflect, + gdouble hardness) +{ + GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (gbrush); + gdouble ratio; + + ratio = fabs (aspect_ratio) * 19.0 / 20.0 + 1.0; + ratio = MIN (ratio, 20); + + /* Since generated brushes are symmetric they don't have aspect + * ratios < 1.0. it's the same as rotating by 90 degrees and 1 / + * ratio, so we fix the input for this case. + */ + if (aspect_ratio < 0.0) + angle = angle + 0.25; + + angle = fmod (fmod (angle, 1.0) + 1.0, 1.0); + angle *= 360; + + if (scale == 1.0 && + ratio == brush->aspect_ratio && + angle == brush->angle && + reflect == FALSE && + hardness == brush->hardness) + { + return gimp_temp_buf_copy (gimp_brush_get_mask (gbrush)); + } + + return gimp_brush_generated_calc (brush, + brush->shape, + brush->radius * scale, + brush->spikes, + hardness, + ratio, + angle, + reflect, + NULL, NULL); +} + + +/* the actual brush rendering functions */ + +static gdouble +gauss (gdouble f) +{ + /* this aint' a real gauss function */ + if (f < -0.5) + { + f = -1.0 - f; + return (2.0 * f*f); + } + + if (f < 0.5) + return (1.0 - 2.0 * f*f); + + f = 1.0 - f; + return (2.0 * f*f); +} + +/* set up lookup table */ +static gfloat * +gimp_brush_generated_calc_lut (gdouble radius, + gdouble hardness) +{ + gfloat *lookup; + gint length; + gint x; + gdouble d; + gdouble sum; + gdouble exponent; + gdouble buffer[OVERSAMPLING]; + + length = OVERSAMPLING * ceil (1 + sqrt (2 * SQR (ceil (radius + 1.0)))); + + lookup = gegl_scratch_new (gfloat, length); + sum = 0.0; + + if ((1.0 - hardness) < 0.0000004) + exponent = 1000000.0; + else + exponent = 0.4 / (1.0 - hardness); + + for (x = 0; x < OVERSAMPLING; x++) + { + d = fabs ((x + 0.5) / OVERSAMPLING - 0.5); + + if (d > radius) + buffer[x] = 0.0; + else + buffer[x] = gauss (pow (d / radius, exponent)); + + sum += buffer[x]; + } + + for (x = 0; d < radius || sum > 0.00001; d += 1.0 / OVERSAMPLING) + { + sum -= buffer[x % OVERSAMPLING]; + + if (d > radius) + buffer[x % OVERSAMPLING] = 0.0; + else + buffer[x % OVERSAMPLING] = gauss (pow (d / radius, exponent)); + + sum += buffer[x % OVERSAMPLING]; + lookup[x++] = sum / OVERSAMPLING; + } + + while (x < length) + { + lookup[x++] = 0.0f; + } + + return lookup; +} + +static GimpTempBuf * +gimp_brush_generated_calc (GimpBrushGenerated *brush, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gfloat angle, + gboolean reflect, + GimpVector2 *xaxis, + GimpVector2 *yaxis) +{ + gfloat *centerp; + gfloat *lookup; + gfloat a; + gint x, y; + gdouble c, s, cs, ss; + GimpVector2 x_axis; + GimpVector2 y_axis; + GimpTempBuf *mask; + gint width; + gint height; + gint half_width; + gint half_height; + + gimp_brush_generated_get_size (brush, + shape, + radius, + spikes, + hardness, + aspect_ratio, + angle, + reflect, + &width, &height, + &s, &c, &x_axis, &y_axis); + + mask = gimp_temp_buf_new (width, height, + babl_format ("Y float")); + + half_width = width / 2; + half_height = height / 2; + + centerp = (gfloat *) gimp_temp_buf_get_data (mask) + + half_height * width + half_width; + + lookup = gimp_brush_generated_calc_lut (radius, hardness); + + cs = cos (- 2 * G_PI / spikes); + ss = sin (- 2 * G_PI / spikes); + + /* for an even number of spikes compute one half and mirror it */ + for (y = ((spikes % 2) ? -half_height : 0); y <= half_height; y++) + { + for (x = -half_width; x <= half_width; x++) + { + gdouble d = 0; + gdouble tx = c * x - s * y; + gdouble ty = fabs (s * x + c * y); + + if (spikes > 2) + { + gdouble angle = atan2 (ty, tx); + + while (angle > G_PI / spikes) + { + gdouble sx = tx; + gdouble sy = ty; + + tx = cs * sx - ss * sy; + ty = ss * sx + cs * sy; + + angle -= 2 * G_PI / spikes; + } + } + + ty *= aspect_ratio; + + switch (shape) + { + case GIMP_BRUSH_GENERATED_CIRCLE: + d = sqrt (SQR (tx) + SQR (ty)); + break; + case GIMP_BRUSH_GENERATED_SQUARE: + d = MAX (fabs (tx), fabs (ty)); + break; + case GIMP_BRUSH_GENERATED_DIAMOND: + d = fabs (tx) + fabs (ty); + break; + } + + if (d < radius + 1) + a = lookup[(gint) RINT (d * OVERSAMPLING)]; + else + a = 0.0f; + + centerp[y * width + x] = a; + + if (spikes % 2 == 0) + centerp[-1 * y * width - x] = a; + } + } + + gegl_scratch_free (lookup); + + if (xaxis) + *xaxis = x_axis; + + if (yaxis) + *yaxis = y_axis; + + return mask; +} + +/* This function is shared between gimp_brush_generated_transform_size and + * gimp_brush_generated_calc, therefore we provide a bunch of optional + * pointers for returnvalues. + */ +static void +gimp_brush_generated_get_size (GimpBrushGenerated *gbrush, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gdouble angle_in_degrees, + gboolean reflect, + gint *width, + gint *height, + gdouble *_s, + gdouble *_c, + GimpVector2 *_x_axis, + GimpVector2 *_y_axis) +{ + gdouble half_width = 0.0; + gdouble half_height = 0.0; + gint w, h; + gdouble c, s; + gdouble short_radius; + GimpVector2 x_axis; + GimpVector2 y_axis; + + /* Since floatongpoint is not really accurate, + * we need to round to limit the errors. + * Errors in some border cases resulted in + * different height and width reported for + * the same input value on calling procedure side. + * This became problem at the rise of dynamics that + * allows for any angle to turn up. + **/ + + angle_in_degrees = RINT (angle_in_degrees * 1000.0) / 1000.0; + + s = sin (gimp_deg_to_rad (angle_in_degrees)); + c = cos (gimp_deg_to_rad (angle_in_degrees)); + + if (reflect) + c = -c; + + short_radius = radius / aspect_ratio; + + x_axis.x = c * radius; + x_axis.y = -1.0 * s * radius; + y_axis.x = s * short_radius; + y_axis.y = c * short_radius; + + switch (shape) + { + case GIMP_BRUSH_GENERATED_CIRCLE: + half_width = sqrt (x_axis.x * x_axis.x + y_axis.x * y_axis.x); + half_height = sqrt (x_axis.y * x_axis.y + y_axis.y * y_axis.y); + break; + + case GIMP_BRUSH_GENERATED_SQUARE: + half_width = fabs (x_axis.x) + fabs (y_axis.x); + half_height = fabs (x_axis.y) + fabs (y_axis.y); + break; + + case GIMP_BRUSH_GENERATED_DIAMOND: + half_width = MAX (fabs (x_axis.x), fabs (y_axis.x)); + half_height = MAX (fabs (x_axis.y), fabs (y_axis.y)); + break; + } + + if (spikes > 2) + { + /* could be optimized by respecting the angle */ + half_width = half_height = sqrt (radius * radius + + short_radius * short_radius); + y_axis.x = s * radius; + y_axis.y = c * radius; + } + + w = MAX (1, ceil (half_width * 2)); + h = MAX (1, ceil (half_height * 2)); + + if (! (w & 0x1)) w++; + if (! (h & 0x1)) h++; + + *width = w; + *height = h; + + /* These will typically be set then this function is called by + * gimp_brush_generated_calc, which needs the values in its algorithms. + */ + if (_s != NULL) + *_s = s; + + if (_c != NULL) + *_c = c; + + if (_x_axis != NULL) + *_x_axis = x_axis; + + if (_y_axis != NULL) + *_y_axis = y_axis; +} + + +/* public functions */ + +GimpData * +gimp_brush_generated_new (const gchar *name, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gfloat angle) +{ + GimpBrushGenerated *brush; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + brush = g_object_new (GIMP_TYPE_BRUSH_GENERATED, + "name", name, + "mime-type", "application/x-gimp-brush-generated", + "spacing", 20.0, + "shape", shape, + "radius", radius, + "spikes", spikes, + "hardness", hardness, + "aspect-ratio", aspect_ratio, + "angle", angle, + NULL); + + return GIMP_DATA (brush); +} + +GimpBrushGeneratedShape +gimp_brush_generated_set_shape (GimpBrushGenerated *brush, + GimpBrushGeneratedShape shape) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), + GIMP_BRUSH_GENERATED_CIRCLE); + + if (brush->shape != shape) + { + brush->shape = shape; + + g_object_notify (G_OBJECT (brush), "shape"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->shape; +} + +gfloat +gimp_brush_generated_set_radius (GimpBrushGenerated *brush, + gfloat radius) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + radius = CLAMP (radius, 0.0, 32767.0); + + if (brush->radius != radius) + { + brush->radius = radius; + + g_object_notify (G_OBJECT (brush), "radius"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->radius; +} + +gint +gimp_brush_generated_set_spikes (GimpBrushGenerated *brush, + gint spikes) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1); + + spikes = CLAMP (spikes, 2, 20); + + if (brush->spikes != spikes) + { + brush->spikes = spikes; + + g_object_notify (G_OBJECT (brush), "spikes"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->spikes; +} + +gfloat +gimp_brush_generated_set_hardness (GimpBrushGenerated *brush, + gfloat hardness) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + hardness = CLAMP (hardness, 0.0, 1.0); + + if (brush->hardness != hardness) + { + brush->hardness = hardness; + + g_object_notify (G_OBJECT (brush), "hardness"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->hardness; +} + +gfloat +gimp_brush_generated_set_aspect_ratio (GimpBrushGenerated *brush, + gfloat ratio) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + ratio = CLAMP (ratio, 1.0, 1000.0); + + if (brush->aspect_ratio != ratio) + { + brush->aspect_ratio = ratio; + + g_object_notify (G_OBJECT (brush), "aspect-ratio"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->aspect_ratio; +} + +gfloat +gimp_brush_generated_set_angle (GimpBrushGenerated *brush, + gfloat angle) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + if (angle < 0.0) + angle = -1.0 * fmod (angle, 180.0); + else if (angle > 180.0) + angle = fmod (angle, 180.0); + + if (brush->angle != angle) + { + brush->angle = angle; + + g_object_notify (G_OBJECT (brush), "angle"); + gimp_data_dirty (GIMP_DATA (brush)); + } + + return brush->angle; +} + +GimpBrushGeneratedShape +gimp_brush_generated_get_shape (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), + GIMP_BRUSH_GENERATED_CIRCLE); + + return brush->shape; +} + +gfloat +gimp_brush_generated_get_radius (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + return brush->radius; +} + +gint +gimp_brush_generated_get_spikes (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1); + + return brush->spikes; +} + +gfloat +gimp_brush_generated_get_hardness (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + return brush->hardness; +} + +gfloat +gimp_brush_generated_get_aspect_ratio (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + return brush->aspect_ratio; +} + +gfloat +gimp_brush_generated_get_angle (GimpBrushGenerated *brush) +{ + g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0); + + return brush->angle; +} diff --git a/app/core/gimpbrushgenerated.h b/app/core/gimpbrushgenerated.h new file mode 100644 index 0000000..2df3578 --- /dev/null +++ b/app/core/gimpbrushgenerated.h @@ -0,0 +1,88 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * brush_generated module Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRUSH_GENERATED_H__ +#define __GIMP_BRUSH_GENERATED_H__ + + +#include "gimpbrush.h" + + +#define GIMP_TYPE_BRUSH_GENERATED (gimp_brush_generated_get_type ()) +#define GIMP_BRUSH_GENERATED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_GENERATED, GimpBrushGenerated)) +#define GIMP_BRUSH_GENERATED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_GENERATED, GimpBrushGeneratedClass)) +#define GIMP_IS_BRUSH_GENERATED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_GENERATED)) +#define GIMP_IS_BRUSH_GENERATED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_GENERATED)) +#define GIMP_BRUSH_GENERATED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_GENERATED, GimpBrushGeneratedClass)) + + +typedef struct _GimpBrushGeneratedClass GimpBrushGeneratedClass; + +struct _GimpBrushGenerated +{ + GimpBrush parent_instance; + + GimpBrushGeneratedShape shape; + gfloat radius; + gint spikes; /* 2 - 20 */ + gfloat hardness; /* 0.0 - 1.0 */ + gfloat aspect_ratio; /* y/x */ + gfloat angle; /* in degrees */ +}; + +struct _GimpBrushGeneratedClass +{ + GimpBrushClass parent_class; +}; + + +GType gimp_brush_generated_get_type (void) G_GNUC_CONST; + +GimpData * gimp_brush_generated_new (const gchar *name, + GimpBrushGeneratedShape shape, + gfloat radius, + gint spikes, + gfloat hardness, + gfloat aspect_ratio, + gfloat angle); + +GimpBrushGeneratedShape + gimp_brush_generated_set_shape (GimpBrushGenerated *brush, + GimpBrushGeneratedShape shape); +gfloat gimp_brush_generated_set_radius (GimpBrushGenerated *brush, + gfloat radius); +gint gimp_brush_generated_set_spikes (GimpBrushGenerated *brush, + gint spikes); +gfloat gimp_brush_generated_set_hardness (GimpBrushGenerated *brush, + gfloat hardness); +gfloat gimp_brush_generated_set_aspect_ratio (GimpBrushGenerated *brush, + gfloat ratio); +gfloat gimp_brush_generated_set_angle (GimpBrushGenerated *brush, + gfloat angle); + +GimpBrushGeneratedShape + gimp_brush_generated_get_shape (GimpBrushGenerated *brush); +gfloat gimp_brush_generated_get_radius (GimpBrushGenerated *brush); +gint gimp_brush_generated_get_spikes (GimpBrushGenerated *brush); +gfloat gimp_brush_generated_get_hardness (GimpBrushGenerated *brush); +gfloat gimp_brush_generated_get_aspect_ratio (GimpBrushGenerated *brush); +gfloat gimp_brush_generated_get_angle (GimpBrushGenerated *brush); + + +#endif /* __GIMP_BRUSH_GENERATED_H__ */ diff --git a/app/core/gimpbrushpipe-load.c b/app/core/gimpbrushpipe-load.c new file mode 100644 index 0000000..18ad898 --- /dev/null +++ b/app/core/gimpbrushpipe-load.c @@ -0,0 +1,163 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * Copyright (C) 1999 Adrian Likins and 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 . + */ + +#include "config.h" + +#include + +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpbrush-load.h" +#include "gimpbrush-private.h" +#include "gimpbrushpipe.h" +#include "gimpbrushpipe-load.h" + +#include "gimp-intl.h" + + +GList * +gimp_brush_pipe_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpBrushPipe *pipe = NULL; + gint n_brushes = 0; + GString *buffer; + gchar *paramstring; + gchar c; + gsize bytes_read; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* The file format starts with a painfully simple text header */ + + /* get the name */ + buffer = g_string_new (NULL); + while (g_input_stream_read_all (input, &c, 1, &bytes_read, NULL, NULL) && + bytes_read == 1 && + c != '\n' && + buffer->len < 1024) + { + g_string_append_c (buffer, c); + } + + if (buffer->len > 0 && buffer->len < 1024) + { + gchar *utf8 = + gimp_any_to_utf8 (buffer->str, buffer->len, + _("Invalid UTF-8 string in brush file '%s'."), + gimp_file_get_utf8_name (file)); + + pipe = g_object_new (GIMP_TYPE_BRUSH_PIPE, + "name", utf8, + "mime-type", "image/x-gimp-gih", + NULL); + + g_free (utf8); + } + + g_string_free (buffer, TRUE); + + if (! pipe) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file '%s': " + "File is corrupt."), + gimp_file_get_utf8_name (file)); + return NULL; + } + + /* get the number of brushes */ + buffer = g_string_new (NULL); + while (g_input_stream_read_all (input, &c, 1, &bytes_read, NULL, NULL) && + bytes_read == 1 && + c != '\n' && + buffer->len < 1024) + { + g_string_append_c (buffer, c); + } + + if (buffer->len > 0 && buffer->len < 1024) + { + n_brushes = strtol (buffer->str, ¶mstring, 10); + } + + if (n_brushes < 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file '%s': " + "File is corrupt."), + gimp_file_get_utf8_name (file)); + g_object_unref (pipe); + g_string_free (buffer, TRUE); + return NULL; + } + + while (*paramstring && g_ascii_isspace (*paramstring)) + paramstring++; + + pipe->brushes = g_new0 (GimpBrush *, n_brushes); + + while (pipe->n_brushes < n_brushes) + { + pipe->brushes[pipe->n_brushes] = gimp_brush_load_brush (context, + file, input, + error); + + if (! pipe->brushes[pipe->n_brushes]) + { + g_object_unref (pipe); + g_string_free (buffer, TRUE); + return NULL; + } + + pipe->n_brushes++; + } + + if (! gimp_brush_pipe_set_params (pipe, paramstring)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Fatal parse error in brush file '%s': " + "Inconsistent parameters."), + gimp_file_get_utf8_name (file)); + g_object_unref (pipe); + g_string_free (buffer, TRUE); + return NULL; + } + + g_string_free (buffer, TRUE); + + /* Current brush is the first one. */ + pipe->current = pipe->brushes[0]; + + /* just to satisfy the code that relies on this crap */ + GIMP_BRUSH (pipe)->priv->spacing = pipe->current->priv->spacing; + GIMP_BRUSH (pipe)->priv->x_axis = pipe->current->priv->x_axis; + GIMP_BRUSH (pipe)->priv->y_axis = pipe->current->priv->y_axis; + GIMP_BRUSH (pipe)->priv->mask = pipe->current->priv->mask; + GIMP_BRUSH (pipe)->priv->pixmap = pipe->current->priv->pixmap; + + return g_list_prepend (NULL, pipe); +} diff --git a/app/core/gimpbrushpipe-load.h b/app/core/gimpbrushpipe-load.h new file mode 100644 index 0000000..7426fcb --- /dev/null +++ b/app/core/gimpbrushpipe-load.h @@ -0,0 +1,32 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * Copyright (C) 1999 Adrian Likins and 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 . + */ + +#ifndef __GIMP_BRUSH_PIPE_LOAD_H__ +#define __GIMP_BRUSH_PIPE_LOAD_H__ + + +#define GIMP_BRUSH_PIPE_FILE_EXTENSION ".gih" + + +GList * gimp_brush_pipe_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_BRUSH_PIPE_LOAD_H__ */ diff --git a/app/core/gimpbrushpipe-save.c b/app/core/gimpbrushpipe-save.c new file mode 100644 index 0000000..1b8e7fb --- /dev/null +++ b/app/core/gimpbrushpipe-save.c @@ -0,0 +1,59 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpbrushpipe.h" +#include "gimpbrushpipe-save.h" + + +gboolean +gimp_brush_pipe_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (data); + const gchar *name; + gint i; + + name = gimp_object_get_name (pipe); + + if (! g_output_stream_printf (output, NULL, NULL, error, + "%s\n%d %s\n", + name, pipe->n_brushes, pipe->params)) + { + return FALSE; + } + + for (i = 0; i < pipe->n_brushes; i++) + { + GimpBrush *brush = pipe->brushes[i]; + + if (brush && + ! GIMP_DATA_GET_CLASS (brush)->save (GIMP_DATA (brush), + output, error)) + { + return FALSE; + } + } + + return TRUE; +} diff --git a/app/core/gimpbrushpipe-save.h b/app/core/gimpbrushpipe-save.h new file mode 100644 index 0000000..df76855 --- /dev/null +++ b/app/core/gimpbrushpipe-save.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 . + */ + +#ifndef __GIMP_BRUSH_PIPE_SAVE_H__ +#define __GIMP_BRUSH_PIPE_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_brush_pipe_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_BRUSH_PIPE_SAVE_H__ */ diff --git a/app/core/gimpbrushpipe.c b/app/core/gimpbrushpipe.c new file mode 100644 index 0000000..cca68ad --- /dev/null +++ b/app/core/gimpbrushpipe.c @@ -0,0 +1,420 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * Copyright (C) 1999 Adrian Likins and 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpparasiteio.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpbrush-private.h" +#include "gimpbrushpipe.h" +#include "gimpbrushpipe-load.h" +#include "gimpbrushpipe-save.h" +#include "gimptempbuf.h" + + +static void gimp_brush_pipe_finalize (GObject *object); + +static gint64 gimp_brush_pipe_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_brush_pipe_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); + +static const gchar * gimp_brush_pipe_get_extension (GimpData *data); +static void gimp_brush_pipe_copy (GimpData *data, + GimpData *src_data); + +static void gimp_brush_pipe_begin_use (GimpBrush *brush); +static void gimp_brush_pipe_end_use (GimpBrush *brush); +static GimpBrush * gimp_brush_pipe_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); +static gboolean gimp_brush_pipe_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords); + + +G_DEFINE_TYPE (GimpBrushPipe, gimp_brush_pipe, GIMP_TYPE_BRUSH); + +#define parent_class gimp_brush_pipe_parent_class + + +static void +gimp_brush_pipe_class_init (GimpBrushPipeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + GimpBrushClass *brush_class = GIMP_BRUSH_CLASS (klass); + + object_class->finalize = gimp_brush_pipe_finalize; + + gimp_object_class->get_memsize = gimp_brush_pipe_get_memsize; + + viewable_class->get_popup_size = gimp_brush_pipe_get_popup_size; + + data_class->save = gimp_brush_pipe_save; + data_class->get_extension = gimp_brush_pipe_get_extension; + data_class->copy = gimp_brush_pipe_copy; + + brush_class->begin_use = gimp_brush_pipe_begin_use; + brush_class->end_use = gimp_brush_pipe_end_use; + brush_class->select_brush = gimp_brush_pipe_select_brush; + brush_class->want_null_motion = gimp_brush_pipe_want_null_motion; +} + +static void +gimp_brush_pipe_init (GimpBrushPipe *pipe) +{ + pipe->current = NULL; + pipe->dimension = 0; + pipe->rank = NULL; + pipe->stride = NULL; + pipe->n_brushes = 0; + pipe->brushes = NULL; + pipe->select = NULL; + pipe->index = NULL; +} + +static void +gimp_brush_pipe_finalize (GObject *object) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (object); + + g_clear_pointer (&pipe->rank, g_free); + g_clear_pointer (&pipe->stride, g_free); + g_clear_pointer (&pipe->select, g_free); + g_clear_pointer (&pipe->index, g_free); + g_clear_pointer (&pipe->params, g_free); + + if (pipe->brushes) + { + gint i; + + for (i = 0; i < pipe->n_brushes; i++) + if (pipe->brushes[i]) + g_object_unref (pipe->brushes[i]); + + g_clear_pointer (&pipe->brushes, g_free); + } + + GIMP_BRUSH (pipe)->priv->mask = NULL; + GIMP_BRUSH (pipe)->priv->pixmap = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_brush_pipe_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (object); + gint64 memsize = 0; + gint i; + + memsize += pipe->dimension * (sizeof (gint) /* rank */ + + sizeof (gint) /* stride */ + + sizeof (PipeSelectModes)); + + for (i = 0; i < pipe->n_brushes; i++) + memsize += gimp_object_get_memsize (GIMP_OBJECT (pipe->brushes[i]), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_brush_pipe_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + return gimp_viewable_get_size (viewable, popup_width, popup_height); +} + +static const gchar * +gimp_brush_pipe_get_extension (GimpData *data) +{ + return GIMP_BRUSH_PIPE_FILE_EXTENSION; +} + +static void +gimp_brush_pipe_copy (GimpData *data, + GimpData *src_data) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (data); + GimpBrushPipe *src_pipe = GIMP_BRUSH_PIPE (src_data); + gint i; + + pipe->dimension = src_pipe->dimension; + + g_clear_pointer (&pipe->rank, g_free); + pipe->rank = g_memdup (src_pipe->rank, + pipe->dimension * sizeof (gint)); + + g_clear_pointer (&pipe->stride, g_free); + pipe->stride = g_memdup (src_pipe->stride, + pipe->dimension * sizeof (gint)); + + g_clear_pointer (&pipe->select, g_free); + pipe->select = g_memdup (src_pipe->select, + pipe->dimension * sizeof (PipeSelectModes)); + + g_clear_pointer (&pipe->index, g_free); + pipe->index = g_memdup (src_pipe->index, + pipe->dimension * sizeof (gint)); + + for (i = 0; i < pipe->n_brushes; i++) + if (pipe->brushes[i]) + g_object_unref (pipe->brushes[i]); + g_clear_pointer (&pipe->brushes, g_free); + + pipe->n_brushes = src_pipe->n_brushes; + + pipe->brushes = g_new0 (GimpBrush *, pipe->n_brushes); + for (i = 0; i < pipe->n_brushes; i++) + if (src_pipe->brushes[i]) + { + pipe->brushes[i] = + GIMP_BRUSH (gimp_data_duplicate (GIMP_DATA (src_pipe->brushes[i]))); + gimp_object_set_name (GIMP_OBJECT (pipe->brushes[i]), + gimp_object_get_name (src_pipe->brushes[i])); + } + + g_clear_pointer (&pipe->params, g_free); + pipe->params = g_strdup (src_pipe->params); + + pipe->current = pipe->brushes[0]; + + GIMP_BRUSH (pipe)->priv->spacing = pipe->current->priv->spacing; + GIMP_BRUSH (pipe)->priv->x_axis = pipe->current->priv->x_axis; + GIMP_BRUSH (pipe)->priv->y_axis = pipe->current->priv->y_axis; + GIMP_BRUSH (pipe)->priv->mask = pipe->current->priv->mask; + GIMP_BRUSH (pipe)->priv->pixmap = pipe->current->priv->pixmap; + + gimp_data_dirty (data); +} + +static void +gimp_brush_pipe_begin_use (GimpBrush *brush) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (brush); + gint i; + + GIMP_BRUSH_CLASS (parent_class)->begin_use (brush); + + for (i = 0; i < pipe->n_brushes; i++) + if (pipe->brushes[i]) + gimp_brush_begin_use (pipe->brushes[i]); +} + +static void +gimp_brush_pipe_end_use (GimpBrush *brush) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (brush); + gint i; + + GIMP_BRUSH_CLASS (parent_class)->end_use (brush); + + for (i = 0; i < pipe->n_brushes; i++) + if (pipe->brushes[i]) + gimp_brush_end_use (pipe->brushes[i]); +} + +static GimpBrush * +gimp_brush_pipe_select_brush (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (brush); + gint i, brushix, ix; + + if (pipe->n_brushes == 1) + return GIMP_BRUSH (pipe->current); + + brushix = 0; + for (i = 0; i < pipe->dimension; i++) + { + switch (pipe->select[i]) + { + case PIPE_SELECT_INCREMENTAL: + ix = (pipe->index[i] + 1) % pipe->rank[i]; + break; + + case PIPE_SELECT_ANGULAR: + /* Coords angle is already nomalized, + * offset by 90 degrees is still needed + * because hoses were made PS compatible*/ + ix = (gint) RINT ((1.0 - current_coords->direction + 0.25) * pipe->rank[i]) % pipe->rank[i]; + break; + + case PIPE_SELECT_VELOCITY: + ix = ROUND (current_coords->velocity * pipe->rank[i]); + break; + + case PIPE_SELECT_RANDOM: + /* This probably isn't the right way */ + ix = g_random_int_range (0, pipe->rank[i]); + break; + + case PIPE_SELECT_PRESSURE: + ix = RINT (current_coords->pressure * (pipe->rank[i] - 1)); + break; + + case PIPE_SELECT_TILT_X: + ix = RINT (current_coords->xtilt / 2.0 * pipe->rank[i]) + pipe->rank[i] / 2; + break; + + case PIPE_SELECT_TILT_Y: + ix = RINT (current_coords->ytilt / 2.0 * pipe->rank[i]) + pipe->rank[i] / 2; + break; + + case PIPE_SELECT_CONSTANT: + default: + ix = pipe->index[i]; + break; + } + + pipe->index[i] = CLAMP (ix, 0, pipe->rank[i] - 1); + brushix += pipe->stride[i] * pipe->index[i]; + } + + /* Make sure is inside bounds */ + brushix = CLAMP (brushix, 0, pipe->n_brushes - 1); + + pipe->current = pipe->brushes[brushix]; + + return GIMP_BRUSH (pipe->current); +} + +static gboolean +gimp_brush_pipe_want_null_motion (GimpBrush *brush, + const GimpCoords *last_coords, + const GimpCoords *current_coords) +{ + GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (brush); + gint i; + + if (pipe->n_brushes == 1) + return TRUE; + + for (i = 0; i < pipe->dimension; i++) + if (pipe->select[i] == PIPE_SELECT_ANGULAR) + return FALSE; + + return TRUE; +} + + +/* public functions */ + +gboolean +gimp_brush_pipe_set_params (GimpBrushPipe *pipe, + const gchar *paramstring) +{ + gint totalcells; + gint i; + + g_return_val_if_fail (GIMP_IS_BRUSH_PIPE (pipe), FALSE); + g_return_val_if_fail (pipe->dimension == 0, FALSE); /* only on a new pipe! */ + + if (paramstring && *paramstring) + { + GimpPixPipeParams params; + + gimp_pixpipe_params_init (¶ms); + gimp_pixpipe_params_parse (paramstring, ¶ms); + + pipe->dimension = params.dim; + pipe->rank = g_new0 (gint, pipe->dimension); + pipe->select = g_new0 (PipeSelectModes, pipe->dimension); + pipe->index = g_new0 (gint, pipe->dimension); + /* placement is not used at all ?? */ + + for (i = 0; i < pipe->dimension; i++) + { + pipe->rank[i] = MAX (1, params.rank[i]); + + if (strcmp (params.selection[i], "incremental") == 0) + pipe->select[i] = PIPE_SELECT_INCREMENTAL; + else if (strcmp (params.selection[i], "angular") == 0) + pipe->select[i] = PIPE_SELECT_ANGULAR; + else if (strcmp (params.selection[i], "velocity") == 0) + pipe->select[i] = PIPE_SELECT_VELOCITY; + else if (strcmp (params.selection[i], "random") == 0) + pipe->select[i] = PIPE_SELECT_RANDOM; + else if (strcmp (params.selection[i], "pressure") == 0) + pipe->select[i] = PIPE_SELECT_PRESSURE; + else if (strcmp (params.selection[i], "xtilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_X; + else if (strcmp (params.selection[i], "ytilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_Y; + else + pipe->select[i] = PIPE_SELECT_CONSTANT; + + pipe->index[i] = 0; + } + + gimp_pixpipe_params_free (¶ms); + + pipe->params = g_strdup (paramstring); + } + else + { + pipe->dimension = 1; + pipe->rank = g_new (gint, 1); + pipe->rank[0] = pipe->n_brushes; + pipe->select = g_new (PipeSelectModes, 1); + pipe->select[0] = PIPE_SELECT_INCREMENTAL; + pipe->index = g_new (gint, 1); + pipe->index[0] = 0; + } + + totalcells = 1; /* Not all necessarily present, maybe */ + for (i = 0; i < pipe->dimension; i++) + totalcells *= pipe->rank[i]; + + pipe->stride = g_new0 (gint, pipe->dimension); + + for (i = 0; i < pipe->dimension; i++) + { + if (i == 0) + pipe->stride[i] = totalcells / pipe->rank[i]; + else + pipe->stride[i] = pipe->stride[i-1] / pipe->rank[i]; + } + + if (pipe->stride[pipe->dimension - 1] != 1) + return FALSE; + + return TRUE; +} diff --git a/app/core/gimpbrushpipe.h b/app/core/gimpbrushpipe.h new file mode 100644 index 0000000..9615210 --- /dev/null +++ b/app/core/gimpbrushpipe.h @@ -0,0 +1,80 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * Copyright (C) 1999 Adrian Likins and 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 . + */ + +#ifndef __GIMP_BRUSH_PIPE_H__ +#define __GIMP_BRUSH_PIPE_H__ + + +#include "gimpbrush.h" + + +#define GIMP_TYPE_BRUSH_PIPE (gimp_brush_pipe_get_type ()) +#define GIMP_BRUSH_PIPE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_PIPE, GimpBrushPipe)) +#define GIMP_BRUSH_PIPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_PIPE, GimpBrushPipeClass)) +#define GIMP_IS_BRUSH_PIPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_PIPE)) +#define GIMP_IS_BRUSH_PIPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_PIPE)) +#define GIMP_BRUSH_PIPE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_PIPE, GimpBrushPipeClass)) + + +typedef enum +{ + PIPE_SELECT_CONSTANT, + PIPE_SELECT_INCREMENTAL, + PIPE_SELECT_ANGULAR, + PIPE_SELECT_VELOCITY, + PIPE_SELECT_RANDOM, + PIPE_SELECT_PRESSURE, + PIPE_SELECT_TILT_X, + PIPE_SELECT_TILT_Y +} PipeSelectModes; + + +typedef struct _GimpBrushPipeClass GimpBrushPipeClass; + +struct _GimpBrushPipe +{ + GimpBrush parent_instance; + + gint dimension; + gint *rank; /* Size in each dimension */ + gint *stride; /* Aux for indexing */ + PipeSelectModes *select; /* One mode per dimension */ + + gint *index; /* Current index for incremental dimensions */ + + gint n_brushes; /* Might be less than the product of the + * ranks in some odd special case */ + GimpBrush **brushes; + GimpBrush *current; /* Currently selected brush */ + + gchar *params; /* For pipe <-> image conversion */ +}; + +struct _GimpBrushPipeClass +{ + GimpBrushClass parent_class; +}; + + +GType gimp_brush_pipe_get_type (void) G_GNUC_CONST; + +gboolean gimp_brush_pipe_set_params (GimpBrushPipe *pipe, + const gchar *paramstring); + + +#endif /* __GIMP_BRUSH_PIPE_H__ */ diff --git a/app/core/gimpbuffer.c b/app/core/gimpbuffer.c new file mode 100644 index 0000000..c713ed8 --- /dev/null +++ b/app/core/gimpbuffer.c @@ -0,0 +1,541 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp-memsize.h" +#include "gimpbuffer.h" +#include "gimpimage.h" +#include "gimptempbuf.h" + + +static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface); + +static void gimp_buffer_finalize (GObject *object); + +static gint64 gimp_buffer_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_buffer_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static void gimp_buffer_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static gboolean gimp_buffer_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_buffer_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static GdkPixbuf * gimp_buffer_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_buffer_get_description (GimpViewable *viewable, + gchar **tooltip); + +static const guint8 * + gimp_buffer_color_managed_get_icc_profile (GimpColorManaged *managed, + gsize *len); +static GimpColorProfile * + gimp_buffer_color_managed_get_color_profile (GimpColorManaged *managed); +static void + gimp_buffer_color_managed_profile_changed (GimpColorManaged *managed); + + +G_DEFINE_TYPE_WITH_CODE (GimpBuffer, gimp_buffer, GIMP_TYPE_VIEWABLE, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_color_managed_iface_init)) + +#define parent_class gimp_buffer_parent_class + + +static void +gimp_buffer_class_init (GimpBufferClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->finalize = gimp_buffer_finalize; + + gimp_object_class->get_memsize = gimp_buffer_get_memsize; + + viewable_class->default_icon_name = "edit-paste"; + viewable_class->name_editable = TRUE; + viewable_class->get_size = gimp_buffer_get_size; + viewable_class->get_preview_size = gimp_buffer_get_preview_size; + viewable_class->get_popup_size = gimp_buffer_get_popup_size; + viewable_class->get_new_preview = gimp_buffer_get_new_preview; + viewable_class->get_new_pixbuf = gimp_buffer_get_new_pixbuf; + viewable_class->get_description = gimp_buffer_get_description; +} + +static void +gimp_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_icc_profile = gimp_buffer_color_managed_get_icc_profile; + iface->get_color_profile = gimp_buffer_color_managed_get_color_profile; + iface->profile_changed = gimp_buffer_color_managed_profile_changed; +} + +static void +gimp_buffer_init (GimpBuffer *buffer) +{ +} + +static void +gimp_buffer_finalize (GObject *object) +{ + GimpBuffer *buffer = GIMP_BUFFER (object); + + g_clear_object (&buffer->buffer); + + gimp_buffer_set_color_profile (buffer, NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_buffer_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpBuffer *buffer = GIMP_BUFFER (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (buffer->buffer); + memsize += gimp_g_object_get_memsize (G_OBJECT (buffer->color_profile)); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_buffer_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpBuffer *buffer = GIMP_BUFFER (viewable); + + *width = gimp_buffer_get_width (buffer); + *height = gimp_buffer_get_height (buffer); + + return TRUE; +} + +static void +gimp_buffer_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + GimpBuffer *buffer = GIMP_BUFFER (viewable); + + gimp_viewable_calc_preview_size (gimp_buffer_get_width (buffer), + gimp_buffer_get_height (buffer), + size, + size, + dot_for_dot, 1.0, 1.0, + width, + height, + NULL); +} + +static gboolean +gimp_buffer_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + GimpBuffer *buffer; + gint buffer_width; + gint buffer_height; + + buffer = GIMP_BUFFER (viewable); + buffer_width = gimp_buffer_get_width (buffer); + buffer_height = gimp_buffer_get_height (buffer); + + if (buffer_width > width || buffer_height > height) + { + gboolean scaling_up; + + gimp_viewable_calc_preview_size (buffer_width, + buffer_height, + width * 2, + height * 2, + dot_for_dot, 1.0, 1.0, + popup_width, + popup_height, + &scaling_up); + + if (scaling_up) + { + *popup_width = buffer_width; + *popup_height = buffer_height; + } + + return TRUE; + } + + return FALSE; +} + +static GimpTempBuf * +gimp_buffer_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpBuffer *buffer = GIMP_BUFFER (viewable); + const Babl *format = gimp_buffer_get_format (buffer); + GimpTempBuf *preview; + + if (babl_format_is_palette (format)) + format = gimp_babl_format (GIMP_RGB, GIMP_PRECISION_U8_GAMMA, + babl_format_has_alpha (format)); + else + format = gimp_babl_format (gimp_babl_format_get_base_type (format), + gimp_babl_precision (GIMP_COMPONENT_TYPE_U8, + gimp_babl_format_get_linear (format)), + babl_format_has_alpha (format)); + + preview = gimp_temp_buf_new (width, height, format); + + gegl_buffer_get (buffer->buffer, GEGL_RECTANGLE (0, 0, width, height), + MIN ((gdouble) width / (gdouble) gimp_buffer_get_width (buffer), + (gdouble) height / (gdouble) gimp_buffer_get_height (buffer)), + format, + gimp_temp_buf_get_data (preview), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + return preview; +} + +static GdkPixbuf * +gimp_buffer_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpBuffer *buffer = GIMP_BUFFER (viewable); + GdkPixbuf *pixbuf; + gdouble scale; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + width, height); + + scale = MIN ((gdouble) width / (gdouble) gimp_buffer_get_width (buffer), + (gdouble) height / (gdouble) gimp_buffer_get_height (buffer)); + + if (buffer->color_profile) + { + GimpColorProfile *srgb_profile; + GimpTempBuf *temp_buf; + GeglBuffer *src_buf; + GeglBuffer *dest_buf; + + srgb_profile = gimp_color_profile_new_rgb_srgb (); + + temp_buf = gimp_temp_buf_new (width, height, + gimp_buffer_get_format (buffer)); + + gegl_buffer_get (buffer->buffer, + GEGL_RECTANGLE (0, 0, width, height), + scale, + gimp_temp_buf_get_format (temp_buf), + gimp_temp_buf_get_data (temp_buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + src_buf = gimp_temp_buf_create_buffer (temp_buf); + dest_buf = gimp_pixbuf_create_buffer (pixbuf); + + gimp_temp_buf_unref (temp_buf); + + gimp_gegl_convert_color_profile (src_buf, + GEGL_RECTANGLE (0, 0, width, height), + buffer->color_profile, + dest_buf, + GEGL_RECTANGLE (0, 0, 0, 0), + srgb_profile, + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + TRUE, + NULL); + + g_object_unref (src_buf); + g_object_unref (dest_buf); + + g_object_unref (srgb_profile); + } + else + { + gegl_buffer_get (buffer->buffer, + GEGL_RECTANGLE (0, 0, width, height), + scale, + gimp_pixbuf_get_format (pixbuf), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + GEGL_ABYSS_CLAMP); + } + + return pixbuf; +} + +static gchar * +gimp_buffer_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpBuffer *buffer = GIMP_BUFFER (viewable); + + return g_strdup_printf ("%s (%d × %d)", + gimp_object_get_name (buffer), + gimp_buffer_get_width (buffer), + gimp_buffer_get_height (buffer)); +} + +static const guint8 * +gimp_buffer_color_managed_get_icc_profile (GimpColorManaged *managed, + gsize *len) +{ + GimpBuffer *buffer = GIMP_BUFFER (managed); + + if (buffer->color_profile) + return gimp_color_profile_get_icc_profile (buffer->color_profile, len); + + return NULL; +} + +static GimpColorProfile * +gimp_buffer_color_managed_get_color_profile (GimpColorManaged *managed) +{ + GimpBuffer *buffer = GIMP_BUFFER (managed); + + if (buffer->color_profile) + return buffer->color_profile; + + return gimp_babl_format_get_color_profile (gimp_buffer_get_format (buffer)); +} + +static void +gimp_buffer_color_managed_profile_changed (GimpColorManaged *managed) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (managed)); +} + + +/* public functions */ + +GimpBuffer * +gimp_buffer_new (GeglBuffer *buffer, + const gchar *name, + gint offset_x, + gint offset_y, + gboolean copy_pixels) +{ + GimpBuffer *gimp_buffer; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (name != NULL, NULL); + + gimp_buffer = g_object_new (GIMP_TYPE_BUFFER, + "name", name, + NULL); + + if (copy_pixels) + gimp_buffer->buffer = gimp_gegl_buffer_dup (buffer); + else + gimp_buffer->buffer = g_object_ref (buffer); + + gimp_buffer->offset_x = offset_x; + gimp_buffer->offset_y = offset_y; + + return gimp_buffer; +} + +GimpBuffer * +gimp_buffer_new_from_pixbuf (GdkPixbuf *pixbuf, + const gchar *name, + gint offset_x, + gint offset_y) +{ + GimpBuffer *gimp_buffer; + GeglBuffer *buffer; + guint8 *icc_data; + gsize icc_len; + GimpColorProfile *profile = NULL; + + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); + g_return_val_if_fail (name != NULL, NULL); + + buffer = gimp_pixbuf_create_buffer (pixbuf); + + gimp_buffer = gimp_buffer_new (buffer, name, + offset_x, offset_y, FALSE); + + icc_data = gimp_pixbuf_get_icc_profile (pixbuf, &icc_len); + if (icc_data) + { + profile = gimp_color_profile_new_from_icc_profile (icc_data, icc_len, + NULL); + g_free (icc_data); + } + + if (! profile && gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB) + { + profile = gimp_color_profile_new_rgb_srgb (); + } + + if (profile) + { + gimp_buffer_set_color_profile (gimp_buffer, profile); + g_object_unref (profile); + } + + g_object_unref (buffer); + + return gimp_buffer; +} + +gint +gimp_buffer_get_width (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), 0); + + return gegl_buffer_get_width (buffer->buffer); +} + +gint +gimp_buffer_get_height (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), 0); + + return gegl_buffer_get_height (buffer->buffer); +} + +const Babl * +gimp_buffer_get_format (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); + + return gegl_buffer_get_format (buffer->buffer); +} + +GeglBuffer * +gimp_buffer_get_buffer (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); + + return buffer->buffer; +} + +void +gimp_buffer_set_resolution (GimpBuffer *buffer, + gdouble resolution_x, + gdouble resolution_y) +{ + g_return_if_fail (GIMP_IS_BUFFER (buffer)); + g_return_if_fail (resolution_x >= 0.0 && resolution_x <= GIMP_MAX_RESOLUTION); + g_return_if_fail (resolution_y >= 0.0 && resolution_y <= GIMP_MAX_RESOLUTION); + + buffer->resolution_x = resolution_x; + buffer->resolution_y = resolution_y; +} + +gboolean +gimp_buffer_get_resolution (GimpBuffer *buffer, + gdouble *resolution_x, + gdouble *resolution_y) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), FALSE); + + if (buffer->resolution_x > 0.0 && + buffer->resolution_y > 0.0) + { + if (resolution_x) *resolution_x = buffer->resolution_x; + if (resolution_y) *resolution_y = buffer->resolution_y; + + return TRUE; + } + + return FALSE; +} + +void +gimp_buffer_set_unit (GimpBuffer *buffer, + GimpUnit unit) +{ + g_return_if_fail (GIMP_IS_BUFFER (buffer)); + g_return_if_fail (unit > GIMP_UNIT_PIXEL); + + buffer->unit = unit; +} + +GimpUnit +gimp_buffer_get_unit (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), GIMP_UNIT_PIXEL); + + return buffer->unit; +} + +void +gimp_buffer_set_color_profile (GimpBuffer *buffer, + GimpColorProfile *profile) +{ + g_return_if_fail (GIMP_IS_BUFFER (buffer)); + g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile)); + + if (profile != buffer->color_profile) + { + g_clear_object (&buffer->color_profile); + + if (profile) + buffer->color_profile = g_object_ref (profile); + } +} + +GimpColorProfile * +gimp_buffer_get_color_profile (GimpBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); + + return buffer->color_profile; +} diff --git a/app/core/gimpbuffer.h b/app/core/gimpbuffer.h new file mode 100644 index 0000000..2949bae --- /dev/null +++ b/app/core/gimpbuffer.h @@ -0,0 +1,90 @@ +/* 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 . + */ + +#ifndef __GIMP_BUFFER_H__ +#define __GIMP_BUFFER_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_BUFFER (gimp_buffer_get_type ()) +#define GIMP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BUFFER, GimpBuffer)) +#define GIMP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BUFFER, GimpBufferClass)) +#define GIMP_IS_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BUFFER)) +#define GIMP_IS_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BUFFER)) +#define GIMP_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BUFFER, GimpBufferClass)) + + +typedef struct _GimpBufferClass GimpBufferClass; + +struct _GimpBuffer +{ + GimpViewable parent_instance; + + GeglBuffer *buffer; + gint offset_x; + gint offset_y; + + gdouble resolution_x; + gdouble resolution_y; + GimpUnit unit; + + GimpColorProfile *color_profile; +}; + +struct _GimpBufferClass +{ + GimpViewableClass parent_class; +}; + + +GType gimp_buffer_get_type (void) G_GNUC_CONST; + +GimpBuffer * gimp_buffer_new (GeglBuffer *buffer, + const gchar *name, + gint offset_x, + gint offset_y, + gboolean copy_pixels); +GimpBuffer * gimp_buffer_new_from_pixbuf (GdkPixbuf *pixbuf, + const gchar *name, + gint offset_x, + gint offset_y); + +gint gimp_buffer_get_width (GimpBuffer *buffer); +gint gimp_buffer_get_height (GimpBuffer *buffer); +const Babl * gimp_buffer_get_format (GimpBuffer *buffer); + +GeglBuffer * gimp_buffer_get_buffer (GimpBuffer *buffer); + +void gimp_buffer_set_resolution (GimpBuffer *buffer, + gdouble resolution_x, + gdouble resolution_y); +gboolean gimp_buffer_get_resolution (GimpBuffer *buffer, + gdouble *resolution_x, + gdouble *resolution_y); + +void gimp_buffer_set_unit (GimpBuffer *buffer, + GimpUnit unit); +GimpUnit gimp_buffer_get_unit (GimpBuffer *buffer); + +void gimp_buffer_set_color_profile (GimpBuffer *buffer, + GimpColorProfile *profile); +GimpColorProfile * gimp_buffer_get_color_profile (GimpBuffer *buffer); + + +#endif /* __GIMP_BUFFER_H__ */ diff --git a/app/core/gimpcancelable.c b/app/core/gimpcancelable.c new file mode 100644 index 0000000..9cb5f12 --- /dev/null +++ b/app/core/gimpcancelable.c @@ -0,0 +1,72 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcancelable.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 . + */ + + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpcancelable.h" +#include "gimpmarshal.h" + + +enum +{ + CANCEL, + LAST_SIGNAL +}; + + +G_DEFINE_INTERFACE (GimpCancelable, gimp_cancelable, G_TYPE_OBJECT) + + +static guint cancelable_signals[LAST_SIGNAL] = { 0 }; + + +/* private functions */ + + +static void +gimp_cancelable_default_init (GimpCancelableInterface *iface) +{ + cancelable_signals[CANCEL] = + g_signal_new ("cancel", + G_TYPE_FROM_CLASS (iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpCancelableInterface, cancel), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + +/* public functions */ + + +void +gimp_cancelable_cancel (GimpCancelable *cancelable) +{ + g_return_if_fail (GIMP_IS_CANCELABLE (cancelable)); + + g_signal_emit (cancelable, cancelable_signals[CANCEL], 0); +} diff --git a/app/core/gimpcancelable.h b/app/core/gimpcancelable.h new file mode 100644 index 0000000..dc2a655 --- /dev/null +++ b/app/core/gimpcancelable.h @@ -0,0 +1,47 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcancelable.h + * 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 . + */ + +#ifndef __GIMP_CANCELABLE_H__ +#define __GIMP_CANCELABLE_H__ + + +#define GIMP_TYPE_CANCELABLE (gimp_cancelable_get_type ()) +#define GIMP_IS_CANCELABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANCELABLE)) +#define GIMP_CANCELABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANCELABLE, GimpCancelable)) +#define GIMP_CANCELABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_CANCELABLE, GimpCancelableInterface)) + + +typedef struct _GimpCancelableInterface GimpCancelableInterface; + +struct _GimpCancelableInterface +{ + GTypeInterface base_iface; + + /* signals */ + void (* cancel) (GimpCancelable *cancelable); +}; + + +GType gimp_cancelable_get_type (void) G_GNUC_CONST; + +void gimp_cancelable_cancel (GimpCancelable *cancelable); + + +#endif /* __GIMP_CANCELABLE_H__ */ diff --git a/app/core/gimpchannel-combine.c b/app/core/gimpchannel-combine.c new file mode 100644 index 0000000..8326f65 --- /dev/null +++ b/app/core/gimpchannel-combine.c @@ -0,0 +1,471 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-mask-combine.h" + +#include "gimpchannel.h" +#include "gimpchannel-combine.h" + + +typedef struct +{ + GeglRectangle rect; + + gboolean bounds_known; + gboolean empty; + GeglRectangle bounds; +} GimpChannelCombineData; + + +/* local function prototypes */ + +static void gimp_channel_combine_clear (GimpChannel *mask, + const GeglRectangle *rect); +static void gimp_channel_combine_clear_complement (GimpChannel *mask, + const GeglRectangle *rect); + +static gboolean gimp_channel_combine_start (GimpChannel *mask, + GimpChannelOps op, + const GeglRectangle *rect, + gboolean full_extent, + gboolean full_value, + GimpChannelCombineData *data); +static void gimp_channel_combine_end (GimpChannel *mask, + GimpChannelCombineData *data); + + +/* private functions */ + +static void +gimp_channel_combine_clear (GimpChannel *mask, + const GeglRectangle *rect) +{ + GeglBuffer *buffer; + GeglRectangle area; + GeglRectangle update_area; + + if (mask->bounds_known && mask->empty) + return; + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + if (rect) + { + if (rect->width <= 0 || rect->height <= 0) + return; + + if (mask->bounds_known) + { + if (! gegl_rectangle_intersect (&area, + GEGL_RECTANGLE (mask->x1, + mask->y1, + mask->x2 - mask->x1, + mask->y2 - mask->y1), + rect)) + { + return; + } + } + else + { + area = *rect; + } + + update_area = area; + } + else + { + if (mask->bounds_known) + { + area.x = mask->x1; + area.y = mask->y1; + area.width = mask->x2 - mask->x1; + area.height = mask->y2 - mask->y1; + } + else + { + area.x = 0; + area.y = 0; + area.width = gimp_item_get_width (GIMP_ITEM (mask)); + area.height = gimp_item_get_height (GIMP_ITEM (mask)); + } + + update_area = area; + + gegl_rectangle_align_to_buffer (&area, &area, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + } + + gegl_buffer_clear (buffer, &area); + + gimp_drawable_update (GIMP_DRAWABLE (mask), + update_area.x, update_area.y, + update_area.width, update_area.height); +} + +static void +gimp_channel_combine_clear_complement (GimpChannel *mask, + const GeglRectangle *rect) +{ + gint width = gimp_item_get_width (GIMP_ITEM (mask)); + gint height = gimp_item_get_height (GIMP_ITEM (mask)); + + gimp_channel_combine_clear ( + mask, + GEGL_RECTANGLE (0, + 0, + width, + rect->y)); + + gimp_channel_combine_clear ( + mask, + GEGL_RECTANGLE (0, + rect->y + rect->height, + width, + height - (rect->y + rect->height))); + + gimp_channel_combine_clear ( + mask, + GEGL_RECTANGLE (0, + rect->y, + rect->x, + rect->height)); + + gimp_channel_combine_clear ( + mask, + GEGL_RECTANGLE (rect->x + rect->width, + rect->y, + width - (rect->x + rect->width), + rect->height)); +} + +static gboolean +gimp_channel_combine_start (GimpChannel *mask, + GimpChannelOps op, + const GeglRectangle *rect, + gboolean full_extent, + gboolean full_value, + GimpChannelCombineData *data) +{ + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + GeglRectangle extent; + gboolean intersects; + + extent.x = 0; + extent.y = 0; + extent.width = gimp_item_get_width (GIMP_ITEM (mask)); + extent.height = gimp_item_get_height (GIMP_ITEM (mask)); + + intersects = gegl_rectangle_intersect (&data->rect, rect, &extent); + + data->bounds_known = mask->bounds_known; + data->empty = mask->empty; + + data->bounds.x = mask->x1; + data->bounds.y = mask->y1; + data->bounds.width = mask->x2 - mask->x1; + data->bounds.height = mask->y2 - mask->y1; + + gegl_buffer_freeze_changed (buffer); + + /* Determine new boundary */ + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + gimp_channel_combine_clear (mask, NULL); + + if (! intersects) + { + data->bounds_known = TRUE; + data->empty = TRUE; + + return FALSE; + } + + data->bounds_known = FALSE; + + if (full_extent) + { + data->bounds_known = TRUE; + data->empty = FALSE; + data->bounds = data->rect; + } + break; + + case GIMP_CHANNEL_OP_ADD: + if (! intersects) + return FALSE; + + data->bounds_known = FALSE; + + if (full_extent && (mask->bounds_known || + gegl_rectangle_equal (&data->rect, &extent))) + { + data->bounds_known = TRUE; + data->empty = FALSE; + + if (mask->bounds_known && ! mask->empty) + { + gegl_rectangle_bounding_box (&data->bounds, + &data->bounds, &data->rect); + } + else + { + data->bounds = data->rect; + } + } + break; + + case GIMP_CHANNEL_OP_SUBTRACT: + if (intersects && mask->bounds_known) + { + if (mask->empty) + { + intersects = FALSE; + } + else + { + intersects = gegl_rectangle_intersect (&data->rect, + &data->rect, + &data->bounds); + } + } + + if (! intersects) + return FALSE; + + if (full_value && + gegl_rectangle_contains (&data->rect, + mask->bounds_known ? &data->bounds : + &extent)) + { + gimp_channel_combine_clear (mask, NULL); + + data->bounds_known = TRUE; + data->empty = TRUE; + + return FALSE; + } + + data->bounds_known = FALSE; + + gegl_buffer_set_abyss (buffer, &data->rect); + break; + + case GIMP_CHANNEL_OP_INTERSECT: + if (intersects && mask->bounds_known) + { + if (mask->empty) + { + intersects = FALSE; + } + else + { + intersects = gegl_rectangle_intersect (&data->rect, + &data->rect, + &data->bounds); + } + } + + if (! intersects) + { + gimp_channel_combine_clear (mask, NULL); + + data->bounds_known = TRUE; + data->empty = TRUE; + + return FALSE; + } + + if (full_value && mask->bounds_known && + gegl_rectangle_contains (&data->rect, &data->bounds)) + { + return FALSE; + } + + data->bounds_known = FALSE; + + gimp_channel_combine_clear_complement (mask, &data->rect); + + gegl_buffer_set_abyss (buffer, &data->rect); + break; + } + + return TRUE; +} + +static void +gimp_channel_combine_end (GimpChannel *mask, + GimpChannelCombineData *data) +{ + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gegl_buffer_set_abyss (buffer, gegl_buffer_get_extent (buffer)); + + gegl_buffer_thaw_changed (buffer); + + mask->bounds_known = data->bounds_known; + + if (data->bounds_known) + { + mask->empty = data->empty; + + if (data->empty) + { + mask->x1 = 0; + mask->y1 = 0; + mask->x2 = gimp_item_get_width (GIMP_ITEM (mask)); + mask->y2 = gimp_item_get_height (GIMP_ITEM (mask)); + } + else + { + mask->x1 = data->bounds.x; + mask->y1 = data->bounds.y; + mask->x2 = data->bounds.x + data->bounds.width; + mask->y2 = data->bounds.y + data->bounds.height; + } + } + + gimp_drawable_update (GIMP_DRAWABLE (mask), + data->rect.x, data->rect.y, + data->rect.width, data->rect.height); +} + + +/* public functions */ + +void +gimp_channel_combine_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h) +{ + GimpChannelCombineData data; + + g_return_if_fail (GIMP_IS_CHANNEL (mask)); + + if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h), + TRUE, TRUE, &data)) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_gegl_mask_combine_rect (buffer, op, x, y, w, h); + } + + gimp_channel_combine_end (mask, &data); +} + +void +gimp_channel_combine_ellipse (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias) +{ + gimp_channel_combine_ellipse_rect (mask, op, x, y, w, h, + w / 2.0, h / 2.0, antialias); +} + +void +gimp_channel_combine_ellipse_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gdouble rx, + gdouble ry, + gboolean antialias) +{ + GimpChannelCombineData data; + + g_return_if_fail (GIMP_IS_CHANNEL (mask)); + + if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h), + TRUE, FALSE, &data)) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h, + rx, ry, antialias); + } + + gimp_channel_combine_end (mask, &data); +} + +void +gimp_channel_combine_mask (GimpChannel *mask, + GimpChannel *add_on, + GimpChannelOps op, + gint off_x, + gint off_y) +{ + GeglBuffer *add_on_buffer; + + g_return_if_fail (GIMP_IS_CHANNEL (mask)); + g_return_if_fail (GIMP_IS_CHANNEL (add_on)); + + add_on_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (add_on)); + + gimp_channel_combine_buffer (mask, add_on_buffer, + op, off_x, off_y); +} + +void +gimp_channel_combine_buffer (GimpChannel *mask, + GeglBuffer *add_on_buffer, + GimpChannelOps op, + gint off_x, + gint off_y) +{ + GimpChannelCombineData data; + + g_return_if_fail (GIMP_IS_CHANNEL (mask)); + g_return_if_fail (GEGL_IS_BUFFER (add_on_buffer)); + + if (gimp_channel_combine_start (mask, op, + GEGL_RECTANGLE ( + off_x + gegl_buffer_get_x (add_on_buffer), + off_y + gegl_buffer_get_y (add_on_buffer), + gegl_buffer_get_width (add_on_buffer), + gegl_buffer_get_height (add_on_buffer)), + FALSE, FALSE, &data)) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_gegl_mask_combine_buffer (buffer, add_on_buffer, op, + off_x, off_y); + } + + gimp_channel_combine_end (mask, &data); +} diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h new file mode 100644 index 0000000..de1f125 --- /dev/null +++ b/app/core/gimpchannel-combine.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 . + */ + +#ifndef __GIMP_CHANNEL_COMBINE_H__ +#define __GIMP_CHANNEL_COMBINE_H__ + + +void gimp_channel_combine_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h); +void gimp_channel_combine_ellipse (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias); +void gimp_channel_combine_ellipse_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gdouble rx, + gdouble ry, + gboolean antialias); +void gimp_channel_combine_mask (GimpChannel *mask, + GimpChannel *add_on, + GimpChannelOps op, + gint off_x, + gint off_y); +void gimp_channel_combine_buffer (GimpChannel *mask, + GeglBuffer *add_on_buffer, + GimpChannelOps op, + gint off_x, + gint off_y); + + +#endif /* __GIMP_CHANNEL_COMBINE_H__ */ diff --git a/app/core/gimpchannel-select.c b/app/core/gimpchannel-select.c new file mode 100644 index 0000000..b76280e --- /dev/null +++ b/app/core/gimpchannel-select.c @@ -0,0 +1,605 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-mask-combine.h" + +#include "gimpchannel.h" +#include "gimpchannel-select.h" +#include "gimpchannel-combine.h" +#include "gimppickable.h" +#include "gimppickable-contiguous-region.h" +#include "gimpscanconvert.h" + +#include "vectors/gimpstroke.h" +#include "vectors/gimpvectors.h" + +#include "gimp-intl.h" + + +/* basic selection functions */ + +void +gimp_channel_select_rectangle (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + + if (push_undo) + gimp_channel_push_undo (channel, C_("undo-type", "Rectangle Select")); + + /* if feathering for rect, make a new mask with the + * rectangle and feather that with the old mask + */ + if (feather) + { + GimpItem *item = GIMP_ITEM (channel); + GeglBuffer *add_on; + + add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + babl_format ("Y float")); + + gimp_gegl_mask_combine_rect (add_on, GIMP_CHANNEL_OP_REPLACE, x, y, w, h); + + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y, + TRUE); + + gimp_channel_combine_buffer (channel, add_on, op, 0, 0); + g_object_unref (add_on); + } + else + { + gimp_channel_combine_rect (channel, op, x, y, w, h); + } +} + +void +gimp_channel_select_ellipse (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + + if (push_undo) + gimp_channel_push_undo (channel, C_("undo-type", "Ellipse Select")); + + /* if feathering for rect, make a new mask with the + * rectangle and feather that with the old mask + */ + if (feather) + { + GimpItem *item = GIMP_ITEM (channel); + GeglBuffer *add_on; + + add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + babl_format ("Y float")); + + gimp_gegl_mask_combine_ellipse (add_on, GIMP_CHANNEL_OP_REPLACE, + x, y, w, h, antialias); + + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y, + TRUE); + + gimp_channel_combine_buffer (channel, add_on, op, 0, 0); + g_object_unref (add_on); + } + else + { + gimp_channel_combine_ellipse (channel, op, x, y, w, h, antialias); + } +} + +void +gimp_channel_select_round_rect (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + gdouble corner_radius_x, + gdouble corner_radius_y, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + + if (push_undo) + gimp_channel_push_undo (channel, C_("undo-type", "Rounded Rectangle Select")); + + /* if feathering for rect, make a new mask with the + * rectangle and feather that with the old mask + */ + if (feather) + { + GimpItem *item = GIMP_ITEM (channel); + GeglBuffer *add_on; + + add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + babl_format ("Y float")); + + gimp_gegl_mask_combine_ellipse_rect (add_on, GIMP_CHANNEL_OP_REPLACE, + x, y, w, h, + corner_radius_x, corner_radius_y, + antialias); + + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y, + TRUE); + + gimp_channel_combine_buffer (channel, add_on, op, 0, 0); + g_object_unref (add_on); + } + else + { + gimp_channel_combine_ellipse_rect (channel, op, x, y, w, h, + corner_radius_x, corner_radius_y, + antialias); + } +} + +/* select by GimpScanConvert functions */ + +void +gimp_channel_select_scan_convert (GimpChannel *channel, + const gchar *undo_desc, + GimpScanConvert *scan_convert, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + GimpItem *item; + GeglBuffer *add_on; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (scan_convert != NULL); + + if (push_undo) + gimp_channel_push_undo (channel, undo_desc); + + item = GIMP_ITEM (channel); + + add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + babl_format ("Y float")); + + gimp_scan_convert_render (scan_convert, add_on, + offset_x, offset_y, antialias); + + if (feather) + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y, + TRUE); + + gimp_channel_combine_buffer (channel, add_on, op, 0, 0); + g_object_unref (add_on); +} + +void +gimp_channel_select_polygon (GimpChannel *channel, + const gchar *undo_desc, + gint n_points, + const GimpVector2 *points, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + GimpScanConvert *scan_convert; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (undo_desc != NULL); + + scan_convert = gimp_scan_convert_new (); + + gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE); + + gimp_channel_select_scan_convert (channel, undo_desc, scan_convert, 0, 0, + op, antialias, feather, + feather_radius_x, feather_radius_y, + push_undo); + + gimp_scan_convert_free (scan_convert); +} + +void +gimp_channel_select_vectors (GimpChannel *channel, + const gchar *undo_desc, + GimpVectors *vectors, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo) +{ + const GimpBezierDesc *bezier; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (GIMP_IS_VECTORS (vectors)); + + bezier = gimp_vectors_get_bezier (vectors); + + if (bezier && bezier->num_data > 4) + { + GimpScanConvert *scan_convert; + + scan_convert = gimp_scan_convert_new (); + gimp_scan_convert_add_bezier (scan_convert, bezier); + + gimp_channel_select_scan_convert (channel, undo_desc, scan_convert, 0, 0, + op, antialias, feather, + feather_radius_x, feather_radius_y, + push_undo); + + gimp_scan_convert_free (scan_convert); + } +} + + +/* select by GimpChannel functions */ + +void +gimp_channel_select_buffer (GimpChannel *channel, + const gchar *undo_desc, + GeglBuffer *add_on, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (GEGL_IS_BUFFER (add_on)); + + gimp_channel_push_undo (channel, undo_desc); + + if (feather) + { + GimpItem *item = GIMP_ITEM (channel); + GeglBuffer *add_on2; + + add_on2 = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + babl_format ("Y float")); + + gimp_gegl_mask_combine_buffer (add_on2, add_on, + GIMP_CHANNEL_OP_REPLACE, + offset_x, offset_y); + + gimp_gegl_apply_feather (add_on2, NULL, NULL, add_on2, NULL, + feather_radius_x, + feather_radius_y, + TRUE); + + gimp_channel_combine_buffer (channel, add_on2, op, 0, 0); + g_object_unref (add_on2); + } + else + { + gimp_channel_combine_buffer (channel, add_on, op, offset_x, offset_y); + } +} + +void +gimp_channel_select_channel (GimpChannel *channel, + const gchar *undo_desc, + GimpChannel *add_on, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (GIMP_IS_CHANNEL (add_on)); + + gimp_channel_select_buffer (channel, undo_desc, + gimp_drawable_get_buffer (GIMP_DRAWABLE (add_on)), + offset_x, offset_y, op, + feather, + feather_radius_x, feather_radius_y); +} + +void +gimp_channel_select_alpha (GimpChannel *channel, + GimpDrawable *drawable, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpItem *item; + GimpChannel *add_on; + gint off_x, off_y; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + item = GIMP_ITEM (channel); + + if (gimp_drawable_has_alpha (drawable)) + { + add_on = gimp_channel_new_from_alpha (gimp_item_get_image (item), + drawable, NULL, NULL); + } + else + { + /* no alpha is equivalent to completely opaque alpha, + * so simply select the whole layer's extents. --mitch + */ + add_on = gimp_channel_new_mask (gimp_item_get_image (item), + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))); + gimp_channel_all (add_on, FALSE); + } + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + gimp_channel_select_channel (channel, C_("undo-type", "Alpha to Selection"), add_on, + off_x, off_y, + op, feather, + feather_radius_x, + feather_radius_y); + g_object_unref (add_on); +} + +void +gimp_channel_select_component (GimpChannel *channel, + GimpChannelType component, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpItem *item; + GimpChannel *add_on; + const gchar *desc; + gchar *undo_desc; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + + item = GIMP_ITEM (channel); + + add_on = gimp_channel_new_from_component (gimp_item_get_image (item), + component, NULL, NULL); + + if (feather) + gimp_channel_feather (add_on, + feather_radius_x, + feather_radius_y, + TRUE, + FALSE /* no undo */); + + gimp_enum_get_value (GIMP_TYPE_CHANNEL_TYPE, component, + NULL, NULL, &desc, NULL); + + undo_desc = g_strdup_printf (C_("undo-type", "%s Channel to Selection"), desc); + + gimp_channel_select_channel (channel, undo_desc, add_on, + 0, 0, op, + FALSE, 0.0, 0.0); + + g_free (undo_desc); + g_object_unref (add_on); +} + +void +gimp_channel_select_fuzzy (GimpChannel *channel, + GimpDrawable *drawable, + gboolean sample_merged, + gint x, + gint y, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean diagonal_neighbors, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpPickable *pickable; + GeglBuffer *add_on; + gint add_on_x = 0; + gint add_on_y = 0; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + if (sample_merged) + pickable = GIMP_PICKABLE (gimp_item_get_image (GIMP_ITEM (drawable))); + else + pickable = GIMP_PICKABLE (drawable); + + add_on = gimp_pickable_contiguous_region_by_seed (pickable, + antialias, + threshold, + select_transparent, + select_criterion, + diagonal_neighbors, + x, y); + + if (! sample_merged) + gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y); + + gimp_channel_select_buffer (channel, C_("undo-type", "Fuzzy Select"), + add_on, add_on_x, add_on_y, + op, + feather, + feather_radius_x, + feather_radius_y); + g_object_unref (add_on); +} + +void +gimp_channel_select_by_color (GimpChannel *channel, + GimpDrawable *drawable, + gboolean sample_merged, + const GimpRGB *color, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpPickable *pickable; + GeglBuffer *add_on; + gint add_on_x = 0; + gint add_on_y = 0; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (color != NULL); + + if (sample_merged) + pickable = GIMP_PICKABLE (gimp_item_get_image (GIMP_ITEM (drawable))); + else + pickable = GIMP_PICKABLE (drawable); + + add_on = gimp_pickable_contiguous_region_by_color (pickable, + antialias, + threshold, + select_transparent, + select_criterion, + color); + + if (! sample_merged) + gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y); + + gimp_channel_select_buffer (channel, C_("undo-type", "Select by Color"), + add_on, add_on_x, add_on_y, + op, + feather, + feather_radius_x, + feather_radius_y); + g_object_unref (add_on); +} + +void +gimp_channel_select_by_index (GimpChannel *channel, + GimpDrawable *drawable, + gint index, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GeglBuffer *add_on; + gint add_on_x = 0; + gint add_on_y = 0; + + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_drawable_is_indexed (drawable)); + + add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))), + babl_format ("Y float")); + + gimp_gegl_index_to_mask (gimp_drawable_get_buffer (drawable), NULL, + gimp_drawable_get_format_without_alpha (drawable), + add_on, NULL, + index); + + gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y); + + gimp_channel_select_buffer (channel, C_("undo-type", "Select by Indexed Color"), + add_on, add_on_x, add_on_y, + op, + feather, + feather_radius_x, + feather_radius_y); + g_object_unref (add_on); +} diff --git a/app/core/gimpchannel-select.h b/app/core/gimpchannel-select.h new file mode 100644 index 0000000..ca2680d --- /dev/null +++ b/app/core/gimpchannel-select.h @@ -0,0 +1,160 @@ +/* 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 . + */ + +#ifndef __GIMP_CHANNEL_SELECT_H__ +#define __GIMP_CHANNEL_SELECT_H__ + + +/* basic selection functions */ + +void gimp_channel_select_rectangle (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); +void gimp_channel_select_ellipse (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); +void gimp_channel_select_round_rect (GimpChannel *channel, + gint x, + gint y, + gint w, + gint h, + gdouble corner_radius_y, + gdouble corner_radius_x, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); + +/* select by GimpScanConvert functions */ + +void gimp_channel_select_scan_convert (GimpChannel *channel, + const gchar *undo_desc, + GimpScanConvert *scan_convert, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); +void gimp_channel_select_polygon (GimpChannel *channel, + const gchar *undo_desc, + gint n_points, + const GimpVector2 *points, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); +void gimp_channel_select_vectors (GimpChannel *channel, + const gchar *undo_desc, + GimpVectors *vectors, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y, + gboolean push_undo); +void gimp_channel_select_buffer (GimpChannel *channel, + const gchar *undo_desc, + GeglBuffer *add_on, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + + +/* select by GimpChannel functions */ + +void gimp_channel_select_channel (GimpChannel *channel, + const gchar *undo_desc, + GimpChannel *add_on, + gint offset_x, + gint offset_y, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_channel_select_alpha (GimpChannel *channel, + GimpDrawable *drawable, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_channel_select_component (GimpChannel *channel, + GimpChannelType component, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_channel_select_fuzzy (GimpChannel *channel, + GimpDrawable *drawable, + gboolean sample_merged, + gint x, + gint y, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean diagonal_neighbors, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_channel_select_by_color (GimpChannel *channel, + GimpDrawable *drawable, + gboolean sample_merged, + const GimpRGB *color, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_channel_select_by_index (GimpChannel *channel, + GimpDrawable *drawable, + gint index, + GimpChannelOps op, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + + +#endif /* __GIMP_CHANNEL_SELECT_H__ */ diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c new file mode 100644 index 0000000..784551a --- /dev/null +++ b/app/core/gimpchannel.c @@ -0,0 +1,1956 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "paint/gimppaintcore-stroke.h" +#include "paint/gimppaintoptions.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-mask.h" +#include "gegl/gimp-gegl-nodes.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpboundary.h" +#include "gimpcontainer.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-quick-mask.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpchannel.h" +#include "gimpchannel-select.h" +#include "gimpcontext.h" +#include "gimpdrawable-fill.h" +#include "gimpdrawable-stroke.h" +#include "gimpmarshal.h" +#include "gimppaintinfo.h" +#include "gimppickable.h" +#include "gimpstrokeoptions.h" + +#include "gimp-intl.h" + + +#define RGBA_EPSILON 1e-6 + +enum +{ + COLOR_CHANGED, + LAST_SIGNAL +}; + + +static void gimp_channel_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_channel_finalize (GObject *object); + +static gint64 gimp_channel_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gchar * gimp_channel_get_description (GimpViewable *viewable, + gchar **tooltip); + +static GeglNode * gimp_channel_get_node (GimpFilter *filter); + +static gboolean gimp_channel_is_attached (GimpItem *item); +static GimpItemTree * gimp_channel_get_tree (GimpItem *item); +static gboolean gimp_channel_bounds (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height); +static GimpItem * gimp_channel_duplicate (GimpItem *item, + GType new_type); +static void gimp_channel_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type); +static void gimp_channel_translate (GimpItem *item, + gdouble off_x, + gdouble off_y, + gboolean push_undo); +static void gimp_channel_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_channel_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static GimpTransformResize + gimp_channel_get_clip (GimpItem *item, + GimpTransformResize clip_result); +static gboolean gimp_channel_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); +static gboolean gimp_channel_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpStrokeOptions *stroke_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); +static void gimp_channel_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + +static void gimp_channel_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); +static void gimp_channel_invalidate_boundary (GimpDrawable *drawable); +static void gimp_channel_get_active_components (GimpDrawable *drawable, + gboolean *active); + +static void gimp_channel_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds); + +static gdouble gimp_channel_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); + +static gboolean gimp_channel_real_boundary (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +static gboolean gimp_channel_real_is_empty (GimpChannel *channel); +static void gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo); +static void gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); +static void gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo); +static void gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); +static void gimp_channel_real_flood (GimpChannel *channel, + gboolean push_undo); + + +static void gimp_channel_buffer_changed (GeglBuffer *buffer, + const GeglRectangle *rect, + GimpChannel *channel); + + +G_DEFINE_TYPE_WITH_CODE (GimpChannel, gimp_channel, GIMP_TYPE_DRAWABLE, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_channel_pickable_iface_init)) + +#define parent_class gimp_channel_parent_class + +static guint channel_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_channel_class_init (GimpChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpFilterClass *filter_class = GIMP_FILTER_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + + channel_signals[COLOR_CHANGED] = + g_signal_new ("color-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpChannelClass, color_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_channel_finalize; + + gimp_object_class->get_memsize = gimp_channel_get_memsize; + + viewable_class->get_description = gimp_channel_get_description; + viewable_class->default_icon_name = "gimp-channel"; + + filter_class->get_node = gimp_channel_get_node; + + item_class->is_attached = gimp_channel_is_attached; + item_class->get_tree = gimp_channel_get_tree; + item_class->bounds = gimp_channel_bounds; + item_class->duplicate = gimp_channel_duplicate; + item_class->convert = gimp_channel_convert; + item_class->translate = gimp_channel_translate; + item_class->scale = gimp_channel_scale; + item_class->resize = gimp_channel_resize; + item_class->get_clip = gimp_channel_get_clip; + item_class->fill = gimp_channel_fill; + item_class->stroke = gimp_channel_stroke; + item_class->to_selection = gimp_channel_to_selection; + item_class->default_name = _("Channel"); + item_class->rename_desc = C_("undo-type", "Rename Channel"); + item_class->translate_desc = C_("undo-type", "Move Channel"); + item_class->scale_desc = C_("undo-type", "Scale Channel"); + item_class->resize_desc = C_("undo-type", "Resize Channel"); + item_class->flip_desc = C_("undo-type", "Flip Channel"); + item_class->rotate_desc = C_("undo-type", "Rotate Channel"); + item_class->transform_desc = C_("undo-type", "Transform Channel"); + item_class->fill_desc = C_("undo-type", "Fill Channel"); + item_class->stroke_desc = C_("undo-type", "Stroke Channel"); + item_class->to_selection_desc = C_("undo-type", "Channel to Selection"); + item_class->reorder_desc = C_("undo-type", "Reorder Channel"); + item_class->raise_desc = C_("undo-type", "Raise Channel"); + item_class->raise_to_top_desc = C_("undo-type", "Raise Channel to Top"); + item_class->lower_desc = C_("undo-type", "Lower Channel"); + item_class->lower_to_bottom_desc = C_("undo-type", "Lower Channel to Bottom"); + item_class->raise_failed = _("Channel cannot be raised higher."); + item_class->lower_failed = _("Channel cannot be lowered more."); + + drawable_class->convert_type = gimp_channel_convert_type; + drawable_class->invalidate_boundary = gimp_channel_invalidate_boundary; + drawable_class->get_active_components = gimp_channel_get_active_components; + drawable_class->set_buffer = gimp_channel_set_buffer; + + klass->boundary = gimp_channel_real_boundary; + klass->is_empty = gimp_channel_real_is_empty; + klass->feather = gimp_channel_real_feather; + klass->sharpen = gimp_channel_real_sharpen; + klass->clear = gimp_channel_real_clear; + klass->all = gimp_channel_real_all; + klass->invert = gimp_channel_real_invert; + klass->border = gimp_channel_real_border; + klass->grow = gimp_channel_real_grow; + klass->shrink = gimp_channel_real_shrink; + klass->flood = gimp_channel_real_flood; + + klass->feather_desc = C_("undo-type", "Feather Channel"); + klass->sharpen_desc = C_("undo-type", "Sharpen Channel"); + klass->clear_desc = C_("undo-type", "Clear Channel"); + klass->all_desc = C_("undo-type", "Fill Channel"); + klass->invert_desc = C_("undo-type", "Invert Channel"); + klass->border_desc = C_("undo-type", "Border Channel"); + klass->grow_desc = C_("undo-type", "Grow Channel"); + klass->shrink_desc = C_("undo-type", "Shrink Channel"); + klass->flood_desc = C_("undo-type", "Flood Channel"); +} + +static void +gimp_channel_init (GimpChannel *channel) +{ + gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); + + channel->show_masked = FALSE; + + /* Selection mask variables */ + channel->boundary_known = FALSE; + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + channel->empty = FALSE; + channel->bounds_known = FALSE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = 0; + channel->y2 = 0; +} + +static void +gimp_channel_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->get_opacity_at = gimp_channel_get_opacity_at; +} + +static void +gimp_channel_finalize (GObject *object) +{ + GimpChannel *channel = GIMP_CHANNEL (object); + + g_clear_pointer (&channel->segs_in, g_free); + g_clear_pointer (&channel->segs_out, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_channel_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpChannel *channel = GIMP_CHANNEL (object); + + *gui_size += channel->num_segs_in * sizeof (GimpBoundSeg); + *gui_size += channel->num_segs_out * sizeof (GimpBoundSeg); + + return GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); +} + +static gchar * +gimp_channel_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME, + gimp_object_get_name (viewable))) + { + return g_strdup (_("Quick Mask")); + } + + return GIMP_VIEWABLE_CLASS (parent_class)->get_description (viewable, + tooltip); +} + +static GeglNode * +gimp_channel_get_node (GimpFilter *filter) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (filter); + GimpChannel *channel = GIMP_CHANNEL (filter); + GeglNode *node; + GeglNode *source; + GeglNode *mode_node; + const Babl *color_format; + + node = GIMP_FILTER_CLASS (parent_class)->get_node (filter); + + source = gimp_drawable_get_source_node (drawable); + gegl_node_add_child (node, source); + + g_warn_if_fail (channel->color_node == NULL); + + if (gimp_drawable_get_linear (drawable)) + color_format = babl_format ("RGBA float"); + else + color_format = babl_format ("R'G'B'A float"); + + channel->color_node = gegl_node_new_child (node, + "operation", "gegl:color", + "format", color_format, + NULL); + gimp_gegl_node_set_color (channel->color_node, + &channel->color); + + g_warn_if_fail (channel->mask_node == NULL); + + channel->mask_node = gegl_node_new_child (node, + "operation", "gegl:opacity", + NULL); + gegl_node_connect_to (channel->color_node, "output", + channel->mask_node, "input"); + + g_warn_if_fail (channel->invert_node == NULL); + + channel->invert_node = gegl_node_new_child (node, + "operation", "gegl:invert-linear", + NULL); + + if (channel->show_masked) + { + gegl_node_connect_to (source, "output", + channel->invert_node, "input"); + gegl_node_connect_to (channel->invert_node, "output", + channel->mask_node, "aux"); + } + else + { + gegl_node_connect_to (source, "output", + channel->mask_node, "aux"); + } + + mode_node = gimp_drawable_get_mode_node (drawable); + + gegl_node_connect_to (channel->mask_node, "output", + mode_node, "aux"); + + return node; +} + +static gboolean +gimp_channel_is_attached (GimpItem *item) +{ + GimpImage *image = gimp_item_get_image (item); + + return (GIMP_IS_IMAGE (image) && + gimp_container_have (gimp_image_get_channels (image), + GIMP_OBJECT (item))); +} + +static GimpItemTree * +gimp_channel_get_tree (GimpItem *item) +{ + if (gimp_item_is_attached (item)) + { + GimpImage *image = gimp_item_get_image (item); + + return gimp_image_get_channel_tree (image); + } + + return NULL; +} + +static gboolean +gimp_channel_bounds (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + + if (! channel->bounds_known) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + channel->empty = ! gimp_gegl_mask_bounds (buffer, + &channel->x1, + &channel->y1, + &channel->x2, + &channel->y2); + + channel->bounds_known = TRUE; + } + + *x = channel->x1; + *y = channel->y1; + *width = channel->x2 - channel->x1; + *height = channel->y2 - channel->y1; + + return ! channel->empty; +} + +static GimpItem * +gimp_channel_duplicate (GimpItem *item, + GType new_type) +{ + GimpItem *new_item; + + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL); + + new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type); + + if (GIMP_IS_CHANNEL (new_item)) + { + GimpChannel *channel = GIMP_CHANNEL (item); + GimpChannel *new_channel = GIMP_CHANNEL (new_item); + + new_channel->color = channel->color; + new_channel->show_masked = channel->show_masked; + + /* selection mask variables */ + new_channel->bounds_known = channel->bounds_known; + new_channel->empty = channel->empty; + new_channel->x1 = channel->x1; + new_channel->y1 = channel->y1; + new_channel->x2 = channel->x2; + new_channel->y2 = channel->y2; + + if (new_type == GIMP_TYPE_CHANNEL) + { + /* 8-bit channel hack: make sure pixels between all sorts + * of channels of an image is always copied without any + * gamma conversion + */ + GimpDrawable *new_drawable = GIMP_DRAWABLE (new_item); + GimpImage *image = gimp_item_get_image (item); + const Babl *format = gimp_image_get_channel_format (image); + + if (format != gimp_drawable_get_format (new_drawable)) + { + GeglBuffer *new_buffer; + + new_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (new_item), + gimp_item_get_height (new_item)), + format); + + gegl_buffer_set_format (new_buffer, + gimp_drawable_get_format (new_drawable)); + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (new_drawable), + NULL, GEGL_ABYSS_NONE, + new_buffer, NULL); + gegl_buffer_set_format (new_buffer, NULL); + + gimp_drawable_set_buffer (new_drawable, FALSE, NULL, new_buffer); + g_object_unref (new_buffer); + } + } + } + + return new_item; +} + +static void +gimp_channel_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + GimpDrawable *drawable = GIMP_DRAWABLE (item); + + if (! gimp_drawable_is_gray (drawable)) + { + gimp_drawable_convert_type (drawable, dest_image, + GIMP_GRAY, + gimp_image_get_precision (dest_image), + gimp_drawable_has_alpha (drawable), + NULL, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + FALSE, NULL); + } + + if (gimp_drawable_has_alpha (drawable)) + { + GeglBuffer *new_buffer; + const Babl *format; + GimpRGB background; + + format = gimp_drawable_get_format_without_alpha (drawable); + + new_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + format); + + gimp_rgba_set (&background, 0.0, 0.0, 0.0, 0.0); + + gimp_gegl_apply_flatten (gimp_drawable_get_buffer (drawable), + NULL, NULL, + new_buffer, &background, + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR); + + gimp_drawable_set_buffer_full (drawable, FALSE, NULL, + new_buffer, + GEGL_RECTANGLE ( + gimp_item_get_offset_x (item), + gimp_item_get_offset_y (item), + 0, 0), + TRUE); + g_object_unref (new_buffer); + } + + if (G_TYPE_FROM_INSTANCE (channel) == GIMP_TYPE_CHANNEL) + { + gint width = gimp_image_get_width (dest_image); + gint height = gimp_image_get_height (dest_image); + + gimp_item_set_offset (item, 0, 0); + + if (gimp_item_get_width (item) != width || + gimp_item_get_height (item) != height) + { + gimp_item_resize (item, gimp_get_user_context (dest_image->gimp), + GIMP_FILL_TRANSPARENT, + width, height, 0, 0); + } + } + + GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type); +} + +static void +gimp_channel_translate (GimpItem *item, + gdouble off_x, + gdouble off_y, + gboolean push_undo) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + gint x, y, width, height; + + gimp_item_bounds (GIMP_ITEM (channel), &x, &y, &width, &height); + + /* update the old area */ + gimp_drawable_update (GIMP_DRAWABLE (item), x, y, width, height); + + if (push_undo) + gimp_channel_push_undo (channel, NULL); + + if (gimp_rectangle_intersect (x + SIGNED_ROUND (off_x), + y + SIGNED_ROUND (off_y), + width, height, + 0, 0, + gimp_item_get_width (GIMP_ITEM (channel)), + gimp_item_get_height (GIMP_ITEM (channel)), + &x, &y, &width, &height)) + { + /* copy the portion of the mask we will keep to a temporary + * buffer + */ + GeglBuffer *tmp_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), + gimp_drawable_get_format (GIMP_DRAWABLE (channel))); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x - SIGNED_ROUND (off_x), + y - SIGNED_ROUND (off_y), + width, height), + GEGL_ABYSS_NONE, + tmp_buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + + /* clear the mask */ + gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL); + + /* copy the temp mask back to the mask */ + gimp_gegl_buffer_copy (tmp_buffer, NULL, GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x, y, 0, 0)); + + /* free the temporary mask */ + g_object_unref (tmp_buffer); + + channel->x1 = x; + channel->y1 = y; + channel->x2 = x + width; + channel->y2 = y + height; + } + else + { + /* clear the mask */ + gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL); + + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); + channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); + } + + /* update the new area */ + gimp_drawable_update (GIMP_DRAWABLE (item), + channel->x1, channel->y1, + channel->x2 - channel->x1, + channel->y2 - channel->y1); +} + +static void +gimp_channel_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + + if (G_TYPE_FROM_INSTANCE (item) == GIMP_TYPE_CHANNEL) + { + new_offset_x = 0; + new_offset_y = 0; + } + + /* don't waste CPU cycles scaling an empty channel */ + if (channel->bounds_known && channel->empty) + { + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *new_buffer; + + new_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), + gimp_drawable_get_format (drawable)); + + gimp_drawable_set_buffer_full (drawable, + gimp_item_is_attached (item), NULL, + new_buffer, + GEGL_RECTANGLE (new_offset_x, new_offset_y, + 0, 0), + TRUE); + g_object_unref (new_buffer); + + gimp_channel_clear (GIMP_CHANNEL (item), NULL, FALSE); + } + else + { + GIMP_ITEM_CLASS (parent_class)->scale (item, new_width, new_height, + new_offset_x, new_offset_y, + interpolation_type, progress); + } +} + +static void +gimp_channel_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GIMP_ITEM_CLASS (parent_class)->resize (item, context, GIMP_FILL_TRANSPARENT, + new_width, new_height, + offset_x, offset_y); + + if (G_TYPE_FROM_INSTANCE (item) == GIMP_TYPE_CHANNEL) + { + gimp_item_set_offset (item, 0, 0); + } +} + +static GimpTransformResize +gimp_channel_get_clip (GimpItem *item, + GimpTransformResize clip_result) +{ + return GIMP_TRANSFORM_RESIZE_CLIP; +} + +static gboolean +gimp_channel_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + const GimpBoundSeg *segs_in; + const GimpBoundSeg *segs_out; + gint n_segs_in; + gint n_segs_out; + gint offset_x, offset_y; + + if (! gimp_channel_boundary (channel, &segs_in, &segs_out, + &n_segs_in, &n_segs_out, + 0, 0, 0, 0)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot fill empty channel.")); + return FALSE; + } + + gimp_item_get_offset (item, &offset_x, &offset_y); + + gimp_drawable_fill_boundary (drawable, + fill_options, + segs_in, n_segs_in, + offset_x, offset_y, + push_undo); + + return TRUE; +} + +static gboolean +gimp_channel_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpStrokeOptions *stroke_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + const GimpBoundSeg *segs_in; + const GimpBoundSeg *segs_out; + gint n_segs_in; + gint n_segs_out; + gboolean retval = FALSE; + gint offset_x, offset_y; + + if (! gimp_channel_boundary (channel, &segs_in, &segs_out, + &n_segs_in, &n_segs_out, + 0, 0, 0, 0)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot stroke empty channel.")); + return FALSE; + } + + gimp_item_get_offset (item, &offset_x, &offset_y); + + switch (gimp_stroke_options_get_method (stroke_options)) + { + case GIMP_STROKE_LINE: + gimp_drawable_stroke_boundary (drawable, + stroke_options, + segs_in, n_segs_in, + offset_x, offset_y, + push_undo); + retval = TRUE; + break; + + case GIMP_STROKE_PAINT_METHOD: + { + GimpPaintInfo *paint_info; + GimpPaintCore *core; + GimpPaintOptions *paint_options; + gboolean emulate_dynamics; + + paint_info = gimp_context_get_paint_info (GIMP_CONTEXT (stroke_options)); + + core = g_object_new (paint_info->paint_type, NULL); + + paint_options = gimp_stroke_options_get_paint_options (stroke_options); + emulate_dynamics = gimp_stroke_options_get_emulate_dynamics (stroke_options); + + retval = gimp_paint_core_stroke_boundary (core, drawable, + paint_options, + emulate_dynamics, + segs_in, n_segs_in, + offset_x, offset_y, + push_undo, error); + + g_object_unref (core); + } + break; + + default: + g_return_val_if_reached (FALSE); + } + + return retval; +} + +static void +gimp_channel_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpChannel *channel = GIMP_CHANNEL (item); + GimpImage *image = gimp_item_get_image (item); + gint off_x, off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + gimp_channel_select_channel (gimp_image_get_mask (image), + GIMP_ITEM_GET_CLASS (item)->to_selection_desc, + channel, off_x, off_y, + op, + feather, feather_radius_x, feather_radius_x); +} + +static void +gimp_channel_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + GeglBuffer *dest_buffer; + + dest_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))), + new_format); + + if (mask_dither_type == GEGL_DITHER_NONE) + { + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL, + GEGL_ABYSS_NONE, + dest_buffer, NULL); + } + else + { + gint bits; + + bits = (babl_format_get_bytes_per_pixel (new_format) * 8 / + babl_format_get_n_components (new_format)); + + gimp_gegl_apply_dither (gimp_drawable_get_buffer (drawable), + NULL, NULL, + dest_buffer, 1 << bits, mask_dither_type); + } + + gimp_drawable_set_buffer (drawable, push_undo, NULL, dest_buffer); + g_object_unref (dest_buffer); +} + +static void +gimp_channel_invalidate_boundary (GimpDrawable *drawable) +{ + GimpChannel *channel = GIMP_CHANNEL (drawable); + + channel->boundary_known = FALSE; + channel->bounds_known = FALSE; +} + +static void +gimp_channel_get_active_components (GimpDrawable *drawable, + gboolean *active) +{ + /* Make sure that the alpha channel is not valid. */ + active[GRAY] = TRUE; + active[ALPHA_G] = FALSE; +} + +static void +gimp_channel_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds) +{ + GimpChannel *channel = GIMP_CHANNEL (drawable); + GeglBuffer *old_buffer = gimp_drawable_get_buffer (drawable); + + if (old_buffer) + { + g_signal_handlers_disconnect_by_func (old_buffer, + gimp_channel_buffer_changed, + channel); + } + + GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable, + push_undo, undo_desc, + buffer, bounds); + + gegl_buffer_signal_connect (buffer, "changed", + G_CALLBACK (gimp_channel_buffer_changed), + channel); + + if (gimp_filter_peek_node (GIMP_FILTER (channel))) + { + const Babl *color_format; + + if (gimp_drawable_get_linear (drawable)) + color_format = babl_format ("RGBA float"); + else + color_format = babl_format ("R'G'B'A float"); + + gegl_node_set (channel->color_node, + "format", color_format, + NULL); + } +} + +static gdouble +gimp_channel_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + GimpChannel *channel = GIMP_CHANNEL (pickable); + gdouble value = GIMP_OPACITY_TRANSPARENT; + + if (x >= 0 && x < gimp_item_get_width (GIMP_ITEM (channel)) && + y >= 0 && y < gimp_item_get_height (GIMP_ITEM (channel))) + { + if (! channel->bounds_known || + (! channel->empty && + x >= channel->x1 && + x < channel->x2 && + y >= channel->y1 && + y < channel->y2)) + { + gegl_buffer_sample (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + x, y, NULL, &value, babl_format ("Y double"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + } + } + + return value; +} + +static gboolean +gimp_channel_real_boundary (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2) +{ + if (! channel->boundary_known) + { + gint x3, y3, x4, y4; + + /* free the out of date boundary segments */ + g_free (channel->segs_in); + g_free (channel->segs_out); + + if (gimp_item_bounds (GIMP_ITEM (channel), &x3, &y3, &x4, &y4)) + { + GeglBuffer *buffer; + GeglRectangle rect = { x3, y3, x4, y4 }; + + x4 += x3; + y4 += y3; + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + channel->segs_out = gimp_boundary_find (buffer, &rect, + babl_format ("Y float"), + GIMP_BOUNDARY_IGNORE_BOUNDS, + x1, y1, x2, y2, + GIMP_BOUNDARY_HALF_WAY, + &channel->num_segs_out); + x1 = MAX (x1, x3); + y1 = MAX (y1, y3); + x2 = MIN (x2, x4); + y2 = MIN (y2, y4); + + if (x2 > x1 && y2 > y1) + { + channel->segs_in = gimp_boundary_find (buffer, NULL, + babl_format ("Y float"), + GIMP_BOUNDARY_WITHIN_BOUNDS, + x1, y1, x2, y2, + GIMP_BOUNDARY_HALF_WAY, + &channel->num_segs_in); + } + else + { + channel->segs_in = NULL; + channel->num_segs_in = 0; + } + } + else + { + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + } + + channel->boundary_known = TRUE; + } + + *segs_in = channel->segs_in; + *segs_out = channel->segs_out; + *num_segs_in = channel->num_segs_in; + *num_segs_out = channel->num_segs_out; + + return (! channel->empty); +} + +static gboolean +gimp_channel_real_is_empty (GimpChannel *channel) +{ + GeglBuffer *buffer; + + if (channel->bounds_known) + return channel->empty; + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + if (! gimp_gegl_mask_is_empty (buffer)) + return FALSE; + + /* The mask is empty, meaning we can set the bounds as known */ + g_clear_pointer (&channel->segs_in, g_free); + g_clear_pointer (&channel->segs_out, g_free); + + channel->empty = TRUE; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + channel->bounds_known = TRUE; + channel->boundary_known = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); + channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); + + return TRUE; +} + +static void +gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + gint x1, y1, x2, y2; + + if (radius_x <= 0.0 && radius_y <= 0.0) + return; + + if (! gimp_item_bounds (GIMP_ITEM (channel), &x1, &y1, &x2, &y2)) + return; + + x2 += x1; + y2 += y1; + + if (gimp_channel_is_empty (channel)) + return; + + x1 = MAX (0, x1 - ceil (radius_x)); + y1 = MAX (0, y1 - ceil (radius_y)); + + x2 = MIN (gimp_item_get_width (GIMP_ITEM (channel)), x2 + ceil (radius_x)); + y2 = MIN (gimp_item_get_height (GIMP_ITEM (channel)), y2 + ceil (radius_y)); + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->feather_desc); + + gimp_gegl_apply_feather (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, NULL, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), + radius_x, + radius_y, + edge_lock); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (channel); + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->sharpen_desc); + + gimp_gegl_apply_threshold (gimp_drawable_get_buffer (drawable), + NULL, NULL, + gimp_drawable_get_buffer (drawable), + 0.5); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + GeglBuffer *buffer; + GeglRectangle rect; + GeglRectangle aligned_rect; + + if (channel->bounds_known && channel->empty) + return; + + if (push_undo) + { + if (! undo_desc) + undo_desc = GIMP_CHANNEL_GET_CLASS (channel)->clear_desc; + + gimp_channel_push_undo (channel, undo_desc); + } + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + if (channel->bounds_known) + { + rect.x = channel->x1; + rect.y = channel->y1; + rect.width = channel->x2 - channel->x1; + rect.height = channel->y2 - channel->y1; + } + else + { + rect.x = 0; + rect.y = 0; + rect.width = gimp_item_get_width (GIMP_ITEM (channel)); + rect.height = gimp_item_get_height (GIMP_ITEM (channel)); + } + + gegl_rectangle_align_to_buffer (&aligned_rect, &rect, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + gegl_buffer_clear (buffer, &aligned_rect); + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); + channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); + + gimp_drawable_update (GIMP_DRAWABLE (channel), + rect.x, rect.y, rect.width, rect.height); +} + +static void +gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo) +{ + GeglColor *color; + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->all_desc); + + /* clear the channel */ + color = gegl_color_new ("#fff"); + gegl_buffer_set_color (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, color); + g_object_unref (color); + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = FALSE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); + channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (channel); + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->invert_desc); + + if (channel->bounds_known && channel->empty) + { + gimp_channel_all (channel, FALSE); + } + else + { + gimp_gegl_apply_invert_linear (gimp_drawable_get_buffer (drawable), + NULL, NULL, + gimp_drawable_get_buffer (drawable)); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); + } +} + +static void +gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo) +{ + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + { + /* The relevant GEGL operations require radius_x and radius_y to be > 0. + * When both are 0 (currently can only be achieved by the user through + * PDB), the effect should be to clear the channel. + */ + gimp_channel_clear (channel, + GIMP_CHANNEL_GET_CLASS (channel)->border_desc, + push_undo); + return; + } + else if (radius_x <= 0 || radius_y <= 0) + { + /* FIXME: Implement the case where only one of radius_x and radius_y is 0. + * Currently, should never happen. + */ + g_return_if_reached(); + } + + if (! gimp_item_bounds (GIMP_ITEM (channel), &x1, &y1, &x2, &y2)) + return; + + x2 += x1; + y2 += y1; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x < 0) + x1 = 0; + else + x1 -= radius_x; + + if (x2 + radius_x > gimp_item_get_width (GIMP_ITEM (channel))) + x2 = gimp_item_get_width (GIMP_ITEM (channel)); + else + x2 += radius_x; + + if (y1 - radius_y < 0) + y1 = 0; + else + y1 -= radius_y; + + if (y2 + radius_y > gimp_item_get_height (GIMP_ITEM (channel))) + y2 = gimp_item_get_height (GIMP_ITEM (channel)); + else + y2 += radius_y; + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->border_desc); + + gimp_gegl_apply_border (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, NULL, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), + radius_x, radius_y, style, edge_lock); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_shrink (channel, -radius_x, -radius_y, FALSE, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_item_bounds (GIMP_ITEM (channel), &x1, &y1, &x2, &y2)) + return; + + x2 += x1; + y2 += y1; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x > 0) + x1 = x1 - radius_x; + else + x1 = 0; + + if (y1 - radius_y > 0) + y1 = y1 - radius_y; + else + y1 = 0; + + if (x2 + radius_x < gimp_item_get_width (GIMP_ITEM (channel))) + x2 = x2 + radius_x; + else + x2 = gimp_item_get_width (GIMP_ITEM (channel)); + + if (y2 + radius_y < gimp_item_get_height (GIMP_ITEM (channel))) + y2 = y2 + radius_y; + else + y2 = gimp_item_get_height (GIMP_ITEM (channel)); + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->grow_desc); + + gimp_gegl_apply_grow (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, NULL, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), + radius_x, radius_y); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_grow (channel, -radius_x, -radius_y, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_item_bounds (GIMP_ITEM (channel), &x1, &y1, &x2, &y2)) + return; + + x2 += x1; + y2 += y1; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 > 0) + x1--; + if (y1 > 0) + y1--; + if (x2 < gimp_item_get_width (GIMP_ITEM (channel))) + x2++; + if (y2 < gimp_item_get_height (GIMP_ITEM (channel))) + y2++; + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->shrink_desc); + + gimp_gegl_apply_shrink (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, NULL, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), + radius_x, radius_y, edge_lock); + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); +} + +static void +gimp_channel_real_flood (GimpChannel *channel, + gboolean push_undo) +{ + gint x, y, width, height; + + if (! gimp_item_bounds (GIMP_ITEM (channel), &x, &y, &width, &height)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (push_undo) + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->flood_desc); + + gimp_gegl_apply_flood (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL, NULL, + gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + GEGL_RECTANGLE (x, y, width, height)); + + gimp_drawable_update (GIMP_DRAWABLE (channel), x, y, width, height); +} + +static void +gimp_channel_buffer_changed (GeglBuffer *buffer, + const GeglRectangle *rect, + GimpChannel *channel) +{ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (channel)); +} + + +/* public functions */ + +GimpChannel * +gimp_channel_new (GimpImage *image, + gint width, + gint height, + const gchar *name, + const GimpRGB *color) +{ + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + channel = + GIMP_CHANNEL (gimp_drawable_new (GIMP_TYPE_CHANNEL, + image, name, + 0, 0, width, height, + gimp_image_get_channel_format (image))); + + if (color) + channel->color = *color; + + channel->show_masked = TRUE; + + /* selection mask variables */ + channel->x2 = width; + channel->y2 = height; + + return channel; +} + +GimpChannel * +gimp_channel_new_from_buffer (GimpImage *image, + GeglBuffer *buffer, + const gchar *name, + const GimpRGB *color) +{ + GimpChannel *channel; + GeglBuffer *dest; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + channel = gimp_channel_new (image, + gegl_buffer_get_width (buffer), + gegl_buffer_get_height (buffer), + name, color); + + dest = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, dest, NULL); + + return channel; +} + +GimpChannel * +gimp_channel_new_from_alpha (GimpImage *image, + GimpDrawable *drawable, + const gchar *name, + const GimpRGB *color) +{ + GimpChannel *channel; + GeglBuffer *dest_buffer; + gint width; + gint height; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_drawable_has_alpha (drawable), NULL); + + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + + channel = gimp_channel_new (image, width, height, name, color); + + gimp_channel_clear (channel, NULL, FALSE); + + dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + gegl_buffer_set_format (dest_buffer, + gimp_drawable_get_component_format (drawable, + GIMP_CHANNEL_ALPHA)); + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL, + GEGL_ABYSS_NONE, + dest_buffer, NULL); + gegl_buffer_set_format (dest_buffer, NULL); + + return channel; +} + +GimpChannel * +gimp_channel_new_from_component (GimpImage *image, + GimpChannelType type, + const gchar *name, + const GimpRGB *color) +{ + GimpChannel *channel; + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + gint width; + gint height; + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + format = gimp_image_get_component_format (image, type); + + g_return_val_if_fail (format != NULL, NULL); + + gimp_pickable_flush (GIMP_PICKABLE (image)); + + src_buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image)); + width = gegl_buffer_get_width (src_buffer); + height = gegl_buffer_get_height (src_buffer); + + channel = gimp_channel_new (image, width, height, name, color); + + dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + gegl_buffer_set_format (dest_buffer, format); + gimp_gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, dest_buffer, NULL); + gegl_buffer_set_format (dest_buffer, NULL); + + return channel; +} + +GimpChannel * +gimp_channel_get_parent (GimpChannel *channel) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL); + + return GIMP_CHANNEL (gimp_viewable_get_parent (GIMP_VIEWABLE (channel))); +} + +void +gimp_channel_set_color (GimpChannel *channel, + const GimpRGB *color, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (color != NULL); + + if (gimp_rgba_distance (&channel->color, color) > RGBA_EPSILON) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (channel))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (channel)); + + gimp_image_undo_push_channel_color (image, C_("undo-type", "Set Channel Color"), + channel); + } + + channel->color = *color; + + if (gimp_filter_peek_node (GIMP_FILTER (channel))) + { + gimp_gegl_node_set_color (channel->color_node, + &channel->color); + } + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); + + g_signal_emit (channel, channel_signals[COLOR_CHANGED], 0); + } +} + +void +gimp_channel_get_color (GimpChannel *channel, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (color != NULL); + + *color = channel->color; +} + +gdouble +gimp_channel_get_opacity (GimpChannel *channel) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT); + + return channel->color.a; +} + +void +gimp_channel_set_opacity (GimpChannel *channel, + gdouble opacity, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE); + + if (channel->color.a != opacity) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (channel))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (channel)); + + gimp_image_undo_push_channel_color (image, C_("undo-type", "Set Channel Opacity"), + channel); + } + + channel->color.a = opacity; + + if (gimp_filter_peek_node (GIMP_FILTER (channel))) + { + gimp_gegl_node_set_color (channel->color_node, + &channel->color); + } + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); + + g_signal_emit (channel, channel_signals[COLOR_CHANGED], 0); + } +} + +gboolean +gimp_channel_get_show_masked (GimpChannel *channel) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); + + return channel->show_masked; +} + +void +gimp_channel_set_show_masked (GimpChannel *channel, + gboolean show_masked) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (show_masked != channel->show_masked) + { + channel->show_masked = show_masked ? TRUE : FALSE; + + if (channel->invert_node) + { + GeglNode *source; + + source = gimp_drawable_get_source_node (GIMP_DRAWABLE (channel)); + + if (channel->show_masked) + { + gegl_node_connect_to (source, "output", + channel->invert_node, "input"); + gegl_node_connect_to (channel->invert_node, "output", + channel->mask_node, "aux"); + } + else + { + gegl_node_disconnect (channel->invert_node, "input"); + + gegl_node_connect_to (source, "output", + channel->mask_node, "aux"); + } + } + + gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); + } +} + +void +gimp_channel_push_undo (GimpChannel *channel, + const gchar *undo_desc) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + + gimp_image_undo_push_mask (gimp_item_get_image (GIMP_ITEM (channel)), + undo_desc, channel); +} + + +/******************************/ +/* selection mask functions */ +/******************************/ + +GimpChannel * +gimp_channel_new_mask (GimpImage *image, + gint width, + gint height) +{ + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + channel = + GIMP_CHANNEL (gimp_drawable_new (GIMP_TYPE_CHANNEL, + image, _("Selection Mask"), + 0, 0, width, height, + gimp_image_get_mask_format (image))); + + channel->show_masked = TRUE; + channel->x2 = width; + channel->y2 = height; + + gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), + NULL); + + return channel; +} + +gboolean +gimp_channel_boundary (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); + g_return_val_if_fail (segs_in != NULL, FALSE); + g_return_val_if_fail (segs_out != NULL, FALSE); + g_return_val_if_fail (num_segs_in != NULL, FALSE); + g_return_val_if_fail (num_segs_out != NULL, FALSE); + + return GIMP_CHANNEL_GET_CLASS (channel)->boundary (channel, + segs_in, segs_out, + num_segs_in, num_segs_out, + x1, y1, + x2, y2); +} + +gboolean +gimp_channel_is_empty (GimpChannel *channel) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), TRUE); + + return GIMP_CHANNEL_GET_CLASS (channel)->is_empty (channel); +} + +void +gimp_channel_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->feather (channel, radius_x, radius_y, + edge_lock, push_undo); +} + +void +gimp_channel_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->sharpen (channel, push_undo); +} + +void +gimp_channel_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->clear (channel, undo_desc, push_undo); +} + +void +gimp_channel_all (GimpChannel *channel, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->all (channel, push_undo); +} + +void +gimp_channel_invert (GimpChannel *channel, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->invert (channel, push_undo); +} + +void +gimp_channel_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->border (channel, + radius_x, radius_y, style, edge_lock, + push_undo); +} + +void +gimp_channel_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->grow (channel, radius_x, radius_y, + push_undo); +} + +void +gimp_channel_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->shrink (channel, radius_x, radius_y, + edge_lock, push_undo); +} + +void +gimp_channel_flood (GimpChannel *channel, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + + if (! gimp_item_is_attached (GIMP_ITEM (channel))) + push_undo = FALSE; + + GIMP_CHANNEL_GET_CLASS (channel)->flood (channel, push_undo); +} diff --git a/app/core/gimpchannel.h b/app/core/gimpchannel.h new file mode 100644 index 0000000..348f4f6 --- /dev/null +++ b/app/core/gimpchannel.h @@ -0,0 +1,216 @@ +/* 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 . + */ + +#ifndef __GIMP_CHANNEL_H__ +#define __GIMP_CHANNEL_H__ + +#include "gimpdrawable.h" + + +#define GIMP_TYPE_CHANNEL (gimp_channel_get_type ()) +#define GIMP_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CHANNEL, GimpChannel)) +#define GIMP_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CHANNEL, GimpChannelClass)) +#define GIMP_IS_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CHANNEL)) +#define GIMP_IS_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CHANNEL)) +#define GIMP_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CHANNEL, GimpChannelClass)) + + +typedef struct _GimpChannelClass GimpChannelClass; + +struct _GimpChannel +{ + GimpDrawable parent_instance; + + GimpRGB color; /* Also stores the opacity */ + gboolean show_masked; /* Show masked areas--as */ + /* opposed to selected areas */ + + GeglNode *color_node; + GeglNode *invert_node; + GeglNode *mask_node; + + /* Selection mask variables */ + gboolean boundary_known; /* is the current boundary valid */ + GimpBoundSeg *segs_in; /* outline of selected region */ + GimpBoundSeg *segs_out; /* outline of selected region */ + gint num_segs_in; /* number of lines in boundary */ + gint num_segs_out; /* number of lines in boundary */ + gboolean empty; /* is the region empty? */ + gboolean bounds_known; /* recalculate the bounds? */ + gint x1, y1; /* coordinates for bounding box */ + gint x2, y2; /* lower right hand coordinate */ +}; + +struct _GimpChannelClass +{ + GimpDrawableClass parent_class; + + /* signals */ + void (* color_changed) (GimpChannel *channel); + + /* virtual functions */ + gboolean (* boundary) (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); + gboolean (* is_empty) (GimpChannel *channel); + + void (* feather) (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo); + void (* sharpen) (GimpChannel *channel, + gboolean push_undo); + void (* clear) (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); + void (* all) (GimpChannel *channel, + gboolean push_undo); + void (* invert) (GimpChannel *channel, + gboolean push_undo); + void (* border) (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo); + void (* grow) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); + void (* shrink) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); + void (* flood) (GimpChannel *channel, + gboolean push_undo); + + const gchar *feather_desc; + const gchar *sharpen_desc; + const gchar *clear_desc; + const gchar *all_desc; + const gchar *invert_desc; + const gchar *border_desc; + const gchar *grow_desc; + const gchar *shrink_desc; + const gchar *flood_desc; +}; + + +/* function declarations */ + +GType gimp_channel_get_type (void) G_GNUC_CONST; + +GimpChannel * gimp_channel_new (GimpImage *image, + gint width, + gint height, + const gchar *name, + const GimpRGB *color); +GimpChannel * gimp_channel_new_from_buffer (GimpImage *image, + GeglBuffer *buffer, + const gchar *name, + const GimpRGB *color); +GimpChannel * gimp_channel_new_from_alpha (GimpImage *image, + GimpDrawable *drawable, + const gchar *name, + const GimpRGB *color); +GimpChannel * gimp_channel_new_from_component (GimpImage *image, + GimpChannelType type, + const gchar *name, + const GimpRGB *color); + +GimpChannel * gimp_channel_get_parent (GimpChannel *channel); + +gdouble gimp_channel_get_opacity (GimpChannel *channel); +void gimp_channel_set_opacity (GimpChannel *channel, + gdouble opacity, + gboolean push_undo); + +void gimp_channel_get_color (GimpChannel *channel, + GimpRGB *color); +void gimp_channel_set_color (GimpChannel *channel, + const GimpRGB *color, + gboolean push_undo); + +gboolean gimp_channel_get_show_masked (GimpChannel *channel); +void gimp_channel_set_show_masked (GimpChannel *channel, + gboolean show_masked); + +void gimp_channel_push_undo (GimpChannel *mask, + const gchar *undo_desc); + + +/* selection mask functions */ + +GimpChannel * gimp_channel_new_mask (GimpImage *image, + gint width, + gint height); + +gboolean gimp_channel_boundary (GimpChannel *mask, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +gboolean gimp_channel_is_empty (GimpChannel *mask); + +void gimp_channel_feather (GimpChannel *mask, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo); +void gimp_channel_sharpen (GimpChannel *mask, + gboolean push_undo); + +void gimp_channel_clear (GimpChannel *mask, + const gchar *undo_desc, + gboolean push_undo); +void gimp_channel_all (GimpChannel *mask, + gboolean push_undo); +void gimp_channel_invert (GimpChannel *mask, + gboolean push_undo); + +void gimp_channel_border (GimpChannel *mask, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo); +void gimp_channel_grow (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean push_undo); +void gimp_channel_shrink (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); +void gimp_channel_flood (GimpChannel *mask, + gboolean push_undo); + + +#endif /* __GIMP_CHANNEL_H__ */ diff --git a/app/core/gimpchannelpropundo.c b/app/core/gimpchannelpropundo.c new file mode 100644 index 0000000..f140d28 --- /dev/null +++ b/app/core/gimpchannelpropundo.c @@ -0,0 +1,108 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpchannel.h" +#include "gimpchannelpropundo.h" + + +static void gimp_channel_prop_undo_constructed (GObject *object); + +static void gimp_channel_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpChannelPropUndo, gimp_channel_prop_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_channel_prop_undo_parent_class + + +static void +gimp_channel_prop_undo_class_init (GimpChannelPropUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_channel_prop_undo_constructed; + + undo_class->pop = gimp_channel_prop_undo_pop; +} + +static void +gimp_channel_prop_undo_init (GimpChannelPropUndo *undo) +{ +} + +static void +gimp_channel_prop_undo_constructed (GObject *object) +{ + GimpChannelPropUndo *channel_prop_undo = GIMP_CHANNEL_PROP_UNDO (object); + GimpChannel *channel; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_CHANNEL (GIMP_ITEM_UNDO (object)->item)); + + channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (object)->item); + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_CHANNEL_COLOR: + gimp_channel_get_color (channel, &channel_prop_undo->color); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_channel_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpChannelPropUndo *channel_prop_undo = GIMP_CHANNEL_PROP_UNDO (undo); + GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_CHANNEL_COLOR: + { + GimpRGB color; + + gimp_channel_get_color (channel, &color); + gimp_channel_set_color (channel, &channel_prop_undo->color, FALSE); + channel_prop_undo->color = color; + } + break; + + default: + g_return_if_reached (); + } +} diff --git a/app/core/gimpchannelpropundo.h b/app/core/gimpchannelpropundo.h new file mode 100644 index 0000000..2aa7ad5 --- /dev/null +++ b/app/core/gimpchannelpropundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_CHANNEL_PROP_UNDO_H__ +#define __GIMP_CHANNEL_PROP_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_CHANNEL_PROP_UNDO (gimp_channel_prop_undo_get_type ()) +#define GIMP_CHANNEL_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CHANNEL_PROP_UNDO, GimpChannelPropUndo)) +#define GIMP_CHANNEL_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CHANNEL_PROP_UNDO, GimpChannelPropUndoClass)) +#define GIMP_IS_CHANNEL_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CHANNEL_PROP_UNDO)) +#define GIMP_IS_CHANNEL_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CHANNEL_PROP_UNDO)) +#define GIMP_CHANNEL_PROP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CHANNEL_PROP_UNDO, GimpChannelPropUndoClass)) + + +typedef struct _GimpChannelPropUndo GimpChannelPropUndo; +typedef struct _GimpChannelPropUndoClass GimpChannelPropUndoClass; + +struct _GimpChannelPropUndo +{ + GimpItemUndo parent_instance; + + GimpRGB color; +}; + +struct _GimpChannelPropUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_channel_prop_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CHANNEL_PROP_UNDO_H__ */ diff --git a/app/core/gimpchannelundo.c b/app/core/gimpchannelundo.c new file mode 100644 index 0000000..1892ced --- /dev/null +++ b/app/core/gimpchannelundo.c @@ -0,0 +1,214 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpchannel.h" +#include "gimpchannelundo.h" + + +enum +{ + PROP_0, + PROP_PREV_PARENT, + PROP_PREV_POSITION, + PROP_PREV_CHANNEL +}; + + +static void gimp_channel_undo_constructed (GObject *object); +static void gimp_channel_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_channel_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_channel_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_channel_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpChannelUndo, gimp_channel_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_channel_undo_parent_class + + +static void +gimp_channel_undo_class_init (GimpChannelUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_channel_undo_constructed; + object_class->set_property = gimp_channel_undo_set_property; + object_class->get_property = gimp_channel_undo_get_property; + + gimp_object_class->get_memsize = gimp_channel_undo_get_memsize; + + undo_class->pop = gimp_channel_undo_pop; + + g_object_class_install_property (object_class, PROP_PREV_PARENT, + g_param_spec_object ("prev-parent", + NULL, NULL, + GIMP_TYPE_CHANNEL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PREV_POSITION, + g_param_spec_int ("prev-position", + NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PREV_CHANNEL, + g_param_spec_object ("prev-channel", + NULL, NULL, + GIMP_TYPE_CHANNEL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_channel_undo_init (GimpChannelUndo *undo) +{ +} + +static void +gimp_channel_undo_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_CHANNEL (GIMP_ITEM_UNDO (object)->item)); +} + +static void +gimp_channel_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpChannelUndo *channel_undo = GIMP_CHANNEL_UNDO (object); + + switch (property_id) + { + case PROP_PREV_PARENT: + channel_undo->prev_parent = g_value_get_object (value); + break; + case PROP_PREV_POSITION: + channel_undo->prev_position = g_value_get_int (value); + break; + case PROP_PREV_CHANNEL: + channel_undo->prev_channel = g_value_get_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_channel_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpChannelUndo *channel_undo = GIMP_CHANNEL_UNDO (object); + + switch (property_id) + { + case PROP_PREV_PARENT: + g_value_set_object (value, channel_undo->prev_parent); + break; + case PROP_PREV_POSITION: + g_value_set_int (value, channel_undo->prev_position); + break; + case PROP_PREV_CHANNEL: + g_value_set_object (value, channel_undo->prev_channel); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_channel_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object); + gint64 memsize = 0; + + if (! gimp_item_is_attached (item_undo->item)) + memsize += gimp_object_get_memsize (GIMP_OBJECT (item_undo->item), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_channel_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpChannelUndo *channel_undo = GIMP_CHANNEL_UNDO (undo); + GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_CHANNEL_ADD) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_CHANNEL_REMOVE)) + { + /* remove channel */ + + /* record the current parent and position */ + channel_undo->prev_parent = gimp_channel_get_parent (channel); + channel_undo->prev_position = gimp_item_get_index (GIMP_ITEM (channel)); + + gimp_image_remove_channel (undo->image, channel, FALSE, + channel_undo->prev_channel); + } + else + { + /* restore channel */ + + /* record the active channel */ + channel_undo->prev_channel = gimp_image_get_active_channel (undo->image); + + gimp_image_add_channel (undo->image, channel, + channel_undo->prev_parent, + channel_undo->prev_position, FALSE); + } +} diff --git a/app/core/gimpchannelundo.h b/app/core/gimpchannelundo.h new file mode 100644 index 0000000..7bfceb9 --- /dev/null +++ b/app/core/gimpchannelundo.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 . + */ + +#ifndef __GIMP_CHANNEL_UNDO_H__ +#define __GIMP_CHANNEL_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_CHANNEL_UNDO (gimp_channel_undo_get_type ()) +#define GIMP_CHANNEL_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CHANNEL_UNDO, GimpChannelUndo)) +#define GIMP_CHANNEL_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CHANNEL_UNDO, GimpChannelUndoClass)) +#define GIMP_IS_CHANNEL_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CHANNEL_UNDO)) +#define GIMP_IS_CHANNEL_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CHANNEL_UNDO)) +#define GIMP_CHANNEL_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CHANNEL_UNDO, GimpChannelUndoClass)) + + +typedef struct _GimpChannelUndo GimpChannelUndo; +typedef struct _GimpChannelUndoClass GimpChannelUndoClass; + +struct _GimpChannelUndo +{ + GimpItemUndo parent_instance; + + GimpChannel *prev_parent; + gint prev_position; /* former position in list */ + GimpChannel *prev_channel; /* previous active channel */ +}; + +struct _GimpChannelUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_channel_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CHANNEL_UNDO_H__ */ diff --git a/app/core/gimpchunkiterator.c b/app/core/gimpchunkiterator.c new file mode 100644 index 0000000..616bb46 --- /dev/null +++ b/app/core/gimpchunkiterator.c @@ -0,0 +1,555 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpchunkiterator.c + * Copyright (C) 2019 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 . + */ + + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpchunkiterator.h" + + +/* the maximal chunk size */ +#define MAX_CHUNK_WIDTH 4096 +#define MAX_CHUNK_HEIGHT 4096 + +/* the default iteration interval */ +#define DEFAULT_INTERVAL (1.0 / 15.0) /* seconds */ + +/* the minimal area to process per iteration */ +#define MIN_AREA_PER_ITERATION 4096 + +/* the maximal ratio between the actual processed area and the target area, + * above which the current chunk height is readjusted, even in the middle of a + * row, to better match the target area + */ +#define MAX_AREA_RATIO 2.0 + +/* the width of the target-area sliding window */ +#define TARGET_AREA_HISTORY_SIZE 3 + + +struct _GimpChunkIterator +{ + cairo_region_t *region; + cairo_region_t *priority_region; + + GeglRectangle tile_rect; + GeglRectangle priority_rect; + + gdouble interval; + + cairo_region_t *current_region; + GeglRectangle current_rect; + + gint current_x; + gint current_y; + gint current_height; + + gint64 iteration_time; + + gint64 last_time; + gint last_area; + + gdouble target_area; + gdouble target_area_min; + gdouble target_area_history[TARGET_AREA_HISTORY_SIZE]; + gint target_area_history_i; + gint target_area_history_n; +}; + + +/* local function prototypes */ + +static void gimp_chunk_iterator_set_current_rect (GimpChunkIterator *iter, + const GeglRectangle *rect); +static void gimp_chunk_iterator_merge_current_rect (GimpChunkIterator *iter); + +static void gimp_chunk_iterator_merge (GimpChunkIterator *iter); + +static gboolean gimp_chunk_iterator_prepare (GimpChunkIterator *iter); + +static void gimp_chunk_iterator_set_target_area (GimpChunkIterator *iter, + gdouble target_area); +static gdouble gimp_chunk_iterator_get_target_area (GimpChunkIterator *iter); +static void gimp_chunk_iterator_reset_target_area (GimpChunkIterator *iter); + +static void gimp_chunk_iterator_calc_rect (GimpChunkIterator *iter, + GeglRectangle *rect, + gboolean readjust_height); + + +/* private functions */ + +static void +gimp_chunk_iterator_set_current_rect (GimpChunkIterator *iter, + const GeglRectangle *rect) +{ + cairo_region_subtract_rectangle (iter->current_region, + (const cairo_rectangle_int_t *) rect); + + iter->current_rect = *rect; + + iter->current_x = rect->x; + iter->current_y = rect->y; + iter->current_height = 0; +} + +static void +gimp_chunk_iterator_merge_current_rect (GimpChunkIterator *iter) +{ + GeglRectangle rect; + + if (gegl_rectangle_is_empty (&iter->current_rect)) + return; + + /* merge the remainder of the current row */ + rect.x = iter->current_x; + rect.y = iter->current_y; + rect.width = iter->current_rect.x + iter->current_rect.width - + iter->current_x; + rect.height = iter->current_height; + + cairo_region_union_rectangle (iter->current_region, + (const cairo_rectangle_int_t *) &rect); + + /* merge the remainder of the current rect */ + rect.x = iter->current_rect.x; + rect.y = iter->current_y + iter->current_height; + rect.width = iter->current_rect.width; + rect.height = iter->current_rect.y + iter->current_rect.height - rect.y; + + cairo_region_union_rectangle (iter->current_region, + (const cairo_rectangle_int_t *) &rect); + + /* reset the current rect and coordinates */ + iter->current_rect.x = 0; + iter->current_rect.y = 0; + iter->current_rect.width = 0; + iter->current_rect.height = 0; + + iter->current_x = 0; + iter->current_y = 0; + iter->current_height = 0; +} + +static void +gimp_chunk_iterator_merge (GimpChunkIterator *iter) +{ + /* merge the current rect back to the current region */ + gimp_chunk_iterator_merge_current_rect (iter); + + /* merge the priority region back to the global region */ + if (iter->priority_region) + { + cairo_region_union (iter->region, iter->priority_region); + + g_clear_pointer (&iter->priority_region, cairo_region_destroy); + + iter->current_region = iter->region; + } +} + +static gboolean +gimp_chunk_iterator_prepare (GimpChunkIterator *iter) +{ + if (iter->current_x == iter->current_rect.x + iter->current_rect.width) + { + iter->current_x = iter->current_rect.x; + iter->current_y += iter->current_height; + iter->current_height = 0; + + if (iter->current_y == iter->current_rect.y + iter->current_rect.height) + { + GeglRectangle rect; + + if (! iter->priority_region && + ! gegl_rectangle_is_empty (&iter->priority_rect)) + { + iter->priority_region = cairo_region_copy (iter->region); + + cairo_region_intersect_rectangle ( + iter->priority_region, + (const cairo_rectangle_int_t *) &iter->priority_rect); + + cairo_region_subtract_rectangle ( + iter->region, + (const cairo_rectangle_int_t *) &iter->priority_rect); + } + + if (! iter->priority_region || + cairo_region_is_empty (iter->priority_region)) + { + iter->current_region = iter->region; + } + else + { + iter->current_region = iter->priority_region; + } + + if (cairo_region_is_empty (iter->current_region)) + { + iter->current_rect.x = 0; + iter->current_rect.y = 0; + iter->current_rect.width = 0; + iter->current_rect.height = 0; + + iter->current_x = 0; + iter->current_y = 0; + iter->current_height = 0; + + return FALSE; + } + + cairo_region_get_rectangle (iter->current_region, 0, + (cairo_rectangle_int_t *) &rect); + + gimp_chunk_iterator_set_current_rect (iter, &rect); + } + } + + return TRUE; +} + +static gint +compare_double (const gdouble *x, + const gdouble *y) +{ + return (*x > *y) - (*x < *y); +} + +static void +gimp_chunk_iterator_set_target_area (GimpChunkIterator *iter, + gdouble target_area) +{ + gdouble target_area_history[TARGET_AREA_HISTORY_SIZE]; + + iter->target_area_min = MIN (iter->target_area_min, target_area); + + iter->target_area_history[iter->target_area_history_i++] = target_area; + + iter->target_area_history_n = MAX (iter->target_area_history_n, + iter->target_area_history_i); + iter->target_area_history_i %= TARGET_AREA_HISTORY_SIZE; + + memcpy (target_area_history, iter->target_area_history, + iter->target_area_history_n * sizeof (gdouble)); + + qsort (target_area_history, iter->target_area_history_n, sizeof (gdouble), + (gpointer) compare_double); + + iter->target_area = target_area_history[iter->target_area_history_n / 2]; +} + +static gdouble +gimp_chunk_iterator_get_target_area (GimpChunkIterator *iter) +{ + if (iter->target_area) + return iter->target_area; + else + return iter->tile_rect.width * iter->tile_rect.height; +} + +static void +gimp_chunk_iterator_reset_target_area (GimpChunkIterator *iter) +{ + if (iter->target_area_history_n) + { + iter->target_area = iter->target_area_min; + iter->target_area_min = MAX_CHUNK_WIDTH * MAX_CHUNK_HEIGHT; + iter->target_area_history_i = 0; + iter->target_area_history_n = 0; + } +} + +static void +gimp_chunk_iterator_calc_rect (GimpChunkIterator *iter, + GeglRectangle *rect, + gboolean readjust_height) +{ + gdouble target_area; + gdouble aspect_ratio; + gint offset_x; + gint offset_y; + + if (readjust_height) + gimp_chunk_iterator_reset_target_area (iter); + + target_area = gimp_chunk_iterator_get_target_area (iter); + + aspect_ratio = (gdouble) iter->tile_rect.height / + (gdouble) iter->tile_rect.width; + + rect->x = iter->current_x; + rect->y = iter->current_y; + + offset_x = rect->x - iter->tile_rect.x; + offset_y = rect->y - iter->tile_rect.y; + + if (readjust_height) + { + rect->height = RINT ((offset_y + sqrt (target_area * aspect_ratio)) / + iter->tile_rect.height) * + iter->tile_rect.height - + offset_y; + + if (rect->height <= 0) + rect->height += iter->tile_rect.height; + + rect->height = MIN (rect->height, + iter->current_rect.y + iter->current_rect.height - + rect->y); + rect->height = MIN (rect->height, MAX_CHUNK_HEIGHT); + } + else + { + rect->height = iter->current_height; + } + + rect->width = RINT ((offset_x + (gdouble) target_area / + (gdouble) rect->height) / + iter->tile_rect.width) * + iter->tile_rect.width - + offset_x; + + if (rect->width <= 0) + rect->width += iter->tile_rect.width; + + rect->width = MIN (rect->width, + iter->current_rect.x + iter->current_rect.width - + rect->x); + rect->width = MIN (rect->width, MAX_CHUNK_WIDTH); +} + + +/* public functions */ + +GimpChunkIterator * +gimp_chunk_iterator_new (cairo_region_t *region) +{ + GimpChunkIterator *iter; + + g_return_val_if_fail (region != NULL, NULL); + + iter = g_slice_new0 (GimpChunkIterator); + + iter->region = region; + iter->current_region = region; + + g_object_get (gegl_config (), + "tile-width", &iter->tile_rect.width, + "tile-height", &iter->tile_rect.height, + NULL); + + iter->interval = DEFAULT_INTERVAL; + + return iter; +} + +void +gimp_chunk_iterator_set_tile_rect (GimpChunkIterator *iter, + const GeglRectangle *rect) +{ + g_return_if_fail (iter != NULL); + g_return_if_fail (rect != NULL); + g_return_if_fail (! gegl_rectangle_is_empty (rect)); + + iter->tile_rect = *rect; +} + +void +gimp_chunk_iterator_set_priority_rect (GimpChunkIterator *iter, + const GeglRectangle *rect) +{ + const GeglRectangle empty_rect = {}; + + g_return_if_fail (iter != NULL); + + if (! rect) + rect = &empty_rect; + + if (! gegl_rectangle_equal (rect, &iter->priority_rect)) + { + iter->priority_rect = *rect; + + gimp_chunk_iterator_merge (iter); + } +} + +void +gimp_chunk_iterator_set_interval (GimpChunkIterator *iter, + gdouble interval) +{ + g_return_if_fail (iter != NULL); + + interval = MAX (interval, 0.0); + + if (interval != iter->interval) + { + if (iter->interval) + { + gdouble ratio = interval / iter->interval; + gint i; + + iter->target_area *= ratio; + + for (i = 0; i < TARGET_AREA_HISTORY_SIZE; i++) + iter->target_area_history[i] *= ratio; + } + + iter->interval = interval; + } +} + +gboolean +gimp_chunk_iterator_next (GimpChunkIterator *iter) +{ + g_return_val_if_fail (iter != NULL, FALSE); + + if (! gimp_chunk_iterator_prepare (iter)) + { + gimp_chunk_iterator_stop (iter, TRUE); + + return FALSE; + } + + iter->iteration_time = g_get_monotonic_time (); + + iter->last_time = iter->iteration_time; + iter->last_area = 0; + + return TRUE; +} + +gboolean +gimp_chunk_iterator_get_rect (GimpChunkIterator *iter, + GeglRectangle *rect) +{ + gint64 time; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (rect != NULL, FALSE); + + if (! gimp_chunk_iterator_prepare (iter)) + return FALSE; + + time = g_get_monotonic_time (); + + if (iter->last_area >= MIN_AREA_PER_ITERATION) + { + gdouble interval; + + interval = (gdouble) (time - iter->last_time) / G_TIME_SPAN_SECOND; + + gimp_chunk_iterator_set_target_area ( + iter, + iter->last_area * iter->interval / interval); + + interval = (gdouble) (time - iter->iteration_time) / G_TIME_SPAN_SECOND; + + if (interval > iter->interval) + return FALSE; + } + + if (iter->current_x == iter->current_rect.x) + { + gimp_chunk_iterator_calc_rect (iter, rect, TRUE); + } + else + { + gimp_chunk_iterator_calc_rect (iter, rect, FALSE); + + if (rect->width * rect->height >= + MAX_AREA_RATIO * gimp_chunk_iterator_get_target_area (iter)) + { + GeglRectangle old_rect = *rect; + + gimp_chunk_iterator_calc_rect (iter, rect, TRUE); + + if (rect->height >= old_rect.height) + *rect = old_rect; + } + } + + if (rect->height != iter->current_height) + { + /* if the chunk height changed in the middle of a row, merge the + * remaining area back into the current region, and reset the current + * area to the remainder of the row, using the new chunk height + */ + if (rect->x != iter->current_rect.x) + { + GeglRectangle rem; + + rem.x = rect->x; + rem.y = rect->y; + rem.width = iter->current_rect.x + iter->current_rect.width - + rect->x; + rem.height = rect->height; + + gimp_chunk_iterator_merge_current_rect (iter); + + gimp_chunk_iterator_set_current_rect (iter, &rem); + } + + iter->current_height = rect->height; + } + + iter->current_x += rect->width; + + iter->last_time = time; + iter->last_area = rect->width * rect->height; + + return TRUE; +} + +cairo_region_t * +gimp_chunk_iterator_stop (GimpChunkIterator *iter, + gboolean free_region) +{ + cairo_region_t *result = NULL; + + g_return_val_if_fail (iter != NULL, NULL); + + if (free_region) + { + cairo_region_destroy (iter->region); + } + else + { + gimp_chunk_iterator_merge (iter); + + result = iter->region; + } + + g_clear_pointer (&iter->priority_region, cairo_region_destroy); + + g_slice_free (GimpChunkIterator, iter); + + return result; +} diff --git a/app/core/gimpchunkiterator.h b/app/core/gimpchunkiterator.h new file mode 100644 index 0000000..e1756f3 --- /dev/null +++ b/app/core/gimpchunkiterator.h @@ -0,0 +1,44 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpchunkiterator.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_CHUNK_ITEARTOR_H__ +#define __GIMP_CHUNK_ITEARTOR_H__ + + +GimpChunkIterator * gimp_chunk_iterator_new (cairo_region_t *region); + +void gimp_chunk_iterator_set_tile_rect (GimpChunkIterator *iter, + const GeglRectangle *rect); + +void gimp_chunk_iterator_set_priority_rect (GimpChunkIterator *iter, + const GeglRectangle *rect); + +void gimp_chunk_iterator_set_interval (GimpChunkIterator *iter, + gdouble interval); + +gboolean gimp_chunk_iterator_next (GimpChunkIterator *iter); +gboolean gimp_chunk_iterator_get_rect (GimpChunkIterator *iter, + GeglRectangle *rect); + +cairo_region_t * gimp_chunk_iterator_stop (GimpChunkIterator *iter, + gboolean free_region); + + +#endif /* __GIMP_CHUNK_ITEARTOR_H__ */ diff --git a/app/core/gimpcontainer-filter.c b/app/core/gimpcontainer-filter.c new file mode 100644 index 0000000..956a4a0 --- /dev/null +++ b/app/core/gimpcontainer-filter.c @@ -0,0 +1,174 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcontainer-filter.c + * Copyright (C) 2003 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 . + */ + +#include "config.h" + +#include + +#include + +#include "core-types.h" + +#include "gimpcontainer.h" +#include "gimpcontainer-filter.h" +#include "gimplist.h" + + +typedef struct +{ + GimpObjectFilterFunc filter; + GimpContainer *container; + gpointer user_data; +} GimpContainerFilterContext; + + +static void +gimp_container_filter_foreach_func (GimpObject *object, + GimpContainerFilterContext *context) +{ + if (context->filter (object, context->user_data)) + gimp_container_add (context->container, object); +} + +/** + * gimp_container_filter: + * @container: a #GimpContainer to filter + * @filter: a #GimpObjectFilterFunc + * @user_data: a pointer passed to @filter + * + * Calls the supplied @filter function on each object in @container. + * A return value of %TRUE is interpreted as a match. + * + * Returns: a weak #GimpContainer filled with matching objects. + **/ +GimpContainer * +gimp_container_filter (GimpContainer *container, + GimpObjectFilterFunc filter, + gpointer user_data) +{ + GimpContainer *result; + GimpContainerFilterContext context; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (filter != NULL, NULL); + + result = + g_object_new (G_TYPE_FROM_INSTANCE (container), + "children-type", gimp_container_get_children_type (container), + "policy", GIMP_CONTAINER_POLICY_WEAK, + NULL); + + context.filter = filter; + context.container = result; + context.user_data = user_data; + + gimp_container_foreach (container, + (GFunc) gimp_container_filter_foreach_func, + &context); + + /* This is somewhat ugly, but it keeps lists in the same order. */ + if (GIMP_IS_LIST (result)) + gimp_list_reverse (GIMP_LIST (result)); + + + return result; +} + + +static gboolean +gimp_object_filter_by_name (GimpObject *object, + const GRegex *regex) +{ + return g_regex_match (regex, gimp_object_get_name (object), 0, NULL); +} + +/** + * gimp_container_filter_by_name: + * @container: a #GimpContainer to filter + * @regexp: a regular expression (as a %NULL-terminated string) + * @error: error location to report errors or %NULL + * + * This function performs a case-insensitive regular expression search + * on the names of the GimpObjects in @container. + * + * Returns: a weak #GimpContainer filled with matching objects. + **/ +GimpContainer * +gimp_container_filter_by_name (GimpContainer *container, + const gchar *regexp, + GError **error) +{ + GimpContainer *result; + GRegex *regex; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (regexp != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + regex = g_regex_new (regexp, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, 0, + error); + + if (! regex) + return NULL; + + result = + gimp_container_filter (container, + (GimpObjectFilterFunc) gimp_object_filter_by_name, + regex); + + g_regex_unref (regex); + + return result; +} + + +gchar ** +gimp_container_get_filtered_name_array (GimpContainer *container, + const gchar *regexp, + gint *length) +{ + GimpContainer *weak; + GError *error = NULL; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (length != NULL, NULL); + + if (regexp == NULL || strlen (regexp) == 0) + return (gimp_container_get_name_array (container, length)); + + weak = gimp_container_filter_by_name (container, regexp, &error); + + if (weak) + { + gchar **retval = gimp_container_get_name_array (weak, length); + + g_object_unref (weak); + + return retval; + } + else + { + g_warning ("%s", error->message); + g_error_free (error); + + *length = 0; + return NULL; + } +} diff --git a/app/core/gimpcontainer-filter.h b/app/core/gimpcontainer-filter.h new file mode 100644 index 0000000..7c98b32 --- /dev/null +++ b/app/core/gimpcontainer-filter.h @@ -0,0 +1,38 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcontainer-filter.c + * Copyright (C) 2003 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 . + */ + +#ifndef __GIMP_CONTAINER_FILTER_H__ +#define __GIMP_CONTAINER_FILTER_H__ + + +GimpContainer * gimp_container_filter (GimpContainer *container, + GimpObjectFilterFunc filter, + gpointer user_data); +GimpContainer * gimp_container_filter_by_name (GimpContainer *container, + const gchar *regexp, + GError **error); + +gchar ** gimp_container_get_filtered_name_array + (GimpContainer *container, + const gchar *regexp, + gint *length); + + +#endif /* __GIMP_CONTAINER_FILTER_H__ */ diff --git a/app/core/gimpcontainer.c b/app/core/gimpcontainer.c new file mode 100644 index 0000000..322a3ec --- /dev/null +++ b/app/core/gimpcontainer.c @@ -0,0 +1,1167 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcontainer.c + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-memsize.h" +#include "gimpcontainer.h" +#include "gimpmarshal.h" + + +/* #define DEBUG_CONTAINER */ + +#ifdef DEBUG_CONTAINER +#define D(stmnt) stmnt +#else +#define D(stmnt) +#endif + + +enum +{ + ADD, + REMOVE, + REORDER, + FREEZE, + THAW, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_CHILDREN_TYPE, + PROP_POLICY +}; + + +typedef struct +{ + gchar *signame; + GCallback callback; + gpointer callback_data; + + GQuark quark; /* used to attach the signal id's of child signals */ +} GimpContainerHandler; + +struct _GimpContainerPrivate +{ + GType children_type; + GimpContainerPolicy policy; + gint n_children; + + GList *handlers; + gint freeze_count; +}; + + +/* local function prototypes */ + +static void gimp_container_config_iface_init (GimpConfigInterface *iface); + +static void gimp_container_dispose (GObject *object); + +static void gimp_container_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_container_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_container_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_container_real_add (GimpContainer *container, + GimpObject *object); +static void gimp_container_real_remove (GimpContainer *container, + GimpObject *object); + +static gboolean gimp_container_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_container_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); + +static void gimp_container_disconnect_callback (GimpObject *object, + gpointer data); + +static void gimp_container_free_handler (GimpContainer *container, + GimpContainerHandler *handler); + + +G_DEFINE_TYPE_WITH_CODE (GimpContainer, gimp_container, GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpContainer) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_container_config_iface_init)) + +#define parent_class gimp_container_parent_class + +static guint container_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_container_class_init (GimpContainerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + container_signals[ADD] = + g_signal_new ("add", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContainerClass, add), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_OBJECT); + + container_signals[REMOVE] = + g_signal_new ("remove", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContainerClass, remove), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_OBJECT); + + container_signals[REORDER] = + g_signal_new ("reorder", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContainerClass, reorder), + NULL, NULL, + gimp_marshal_VOID__OBJECT_INT, + G_TYPE_NONE, 2, + GIMP_TYPE_OBJECT, + G_TYPE_INT); + + container_signals[FREEZE] = + g_signal_new ("freeze", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpContainerClass, freeze), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + container_signals[THAW] = + g_signal_new ("thaw", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpContainerClass, thaw), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = gimp_container_dispose; + object_class->set_property = gimp_container_set_property; + object_class->get_property = gimp_container_get_property; + + gimp_object_class->get_memsize = gimp_container_get_memsize; + + klass->add = gimp_container_real_add; + klass->remove = gimp_container_real_remove; + klass->reorder = NULL; + klass->freeze = NULL; + klass->thaw = NULL; + + klass->clear = NULL; + klass->have = NULL; + klass->foreach = NULL; + klass->search = NULL; + klass->get_unique_names = NULL; + klass->get_child_by_name = NULL; + klass->get_child_by_index = NULL; + klass->get_child_index = NULL; + + g_object_class_install_property (object_class, PROP_CHILDREN_TYPE, + g_param_spec_gtype ("children-type", + NULL, NULL, + GIMP_TYPE_OBJECT, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_POLICY, + g_param_spec_enum ("policy", + NULL, NULL, + GIMP_TYPE_CONTAINER_POLICY, + GIMP_CONTAINER_POLICY_STRONG, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_container_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_container_serialize; + iface->deserialize = gimp_container_deserialize; +} + +static void +gimp_container_init (GimpContainer *container) +{ + container->priv = gimp_container_get_instance_private (container); + container->priv->handlers = NULL; + container->priv->freeze_count = 0; + + container->priv->children_type = G_TYPE_NONE; + container->priv->policy = GIMP_CONTAINER_POLICY_STRONG; + container->priv->n_children = 0; +} + +static void +gimp_container_dispose (GObject *object) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + gimp_container_clear (container); + + while (container->priv->handlers) + gimp_container_remove_handler (container, + ((GimpContainerHandler *) + container->priv->handlers->data)->quark); + + if (container->priv->children_type != G_TYPE_NONE) + { + g_type_class_unref (g_type_class_peek (container->priv->children_type)); + container->priv->children_type = G_TYPE_NONE; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_container_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + switch (property_id) + { + case PROP_CHILDREN_TYPE: + container->priv->children_type = g_value_get_gtype (value); + g_type_class_ref (container->priv->children_type); + break; + case PROP_POLICY: + container->priv->policy = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_container_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + switch (property_id) + { + case PROP_CHILDREN_TYPE: + g_value_set_gtype (value, container->priv->children_type); + break; + case PROP_POLICY: + g_value_set_enum (value, container->priv->policy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_container_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpContainer *container = GIMP_CONTAINER (object); + gint64 memsize = 0; + GList *list; + + for (list = container->priv->handlers; list; list = g_list_next (list)) + { + GimpContainerHandler *handler = list->data; + + memsize += (sizeof (GList) + + sizeof (GimpContainerHandler) + + gimp_string_get_memsize (handler->signame)); + } + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_container_real_add (GimpContainer *container, + GimpObject *object) +{ + container->priv->n_children++; +} + +static void +gimp_container_real_remove (GimpContainer *container, + GimpObject *object) +{ + container->priv->n_children--; +} + + +typedef struct +{ + GimpConfigWriter *writer; + gpointer data; + gboolean success; +} SerializeData; + +static void +gimp_container_serialize_foreach (GObject *object, + SerializeData *serialize_data) +{ + GimpConfigInterface *config_iface; + const gchar *name; + + config_iface = GIMP_CONFIG_GET_INTERFACE (object); + + if (! config_iface) + serialize_data->success = FALSE; + + if (! serialize_data->success) + return; + + gimp_config_writer_open (serialize_data->writer, + g_type_name (G_TYPE_FROM_INSTANCE (object))); + + name = gimp_object_get_name (object); + + if (name) + gimp_config_writer_string (serialize_data->writer, name); + else + gimp_config_writer_print (serialize_data->writer, "NULL", 4); + + serialize_data->success = config_iface->serialize (GIMP_CONFIG (object), + serialize_data->writer, + serialize_data->data); + gimp_config_writer_close (serialize_data->writer); +} + +static gboolean +gimp_container_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + GimpContainer *container = GIMP_CONTAINER (config); + SerializeData serialize_data; + + serialize_data.writer = writer; + serialize_data.data = data; + serialize_data.success = TRUE; + + gimp_container_foreach (container, + (GFunc) gimp_container_serialize_foreach, + &serialize_data); + + return serialize_data.success; +} + +static gboolean +gimp_container_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpContainer *container = GIMP_CONTAINER (config); + GTokenType token; + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_IDENTIFIER; + break; + + case G_TOKEN_IDENTIFIER: + { + GimpObject *child = NULL; + GType type; + gchar *name = NULL; + gboolean add_child = FALSE; + + type = g_type_from_name (scanner->value.v_identifier); + + if (! type) + { + g_scanner_error (scanner, + "unable to determine type of '%s'", + scanner->value.v_identifier); + return FALSE; + } + + if (! g_type_is_a (type, container->priv->children_type)) + { + g_scanner_error (scanner, + "'%s' is not a subclass of '%s'", + scanner->value.v_identifier, + g_type_name (container->priv->children_type)); + return FALSE; + } + + if (! g_type_is_a (type, GIMP_TYPE_CONFIG)) + { + g_scanner_error (scanner, + "'%s' does not implement GimpConfigInterface", + scanner->value.v_identifier); + return FALSE; + } + + if (! gimp_scanner_parse_string (scanner, &name)) + { + token = G_TOKEN_STRING; + break; + } + + if (! name) + name = g_strdup (""); + + if (gimp_container_get_unique_names (container)) + child = gimp_container_get_child_by_name (container, name); + + if (! child) + { + if (GIMP_IS_GIMP (data)) + child = g_object_new (type, "gimp", data, NULL); + else + child = g_object_new (type, NULL); + + add_child = TRUE; + } + + /* always use the deserialized name. while it normally + * doesn't make a difference there are obscure case like + * template migration. + */ + gimp_object_take_name (child, name); + + if (! GIMP_CONFIG_GET_INTERFACE (child)->deserialize (GIMP_CONFIG (child), + scanner, + nest_level + 1, + NULL)) + { + if (add_child) + g_object_unref (child); + + /* warning should be already set by child */ + return FALSE; + } + + if (add_child) + { + gimp_container_add (container, child); + + if (container->priv->policy == GIMP_CONTAINER_POLICY_STRONG) + g_object_unref (child); + } + } + token = G_TOKEN_RIGHT_PAREN; + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + return gimp_config_deserialize_return (scanner, token, nest_level); +} + +static void +gimp_container_disconnect_callback (GimpObject *object, + gpointer data) +{ + GimpContainer *container = GIMP_CONTAINER (data); + + gimp_container_remove (container, object); +} + +static void +gimp_container_free_handler_foreach_func (GimpObject *object, + GimpContainerHandler *handler) +{ + gulong handler_id; + + handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object), + handler->quark)); + + if (handler_id) + { + g_signal_handler_disconnect (object, handler_id); + + g_object_set_qdata (G_OBJECT (object), handler->quark, NULL); + } +} + +static void +gimp_container_free_handler (GimpContainer *container, + GimpContainerHandler *handler) +{ + D (g_print ("%s: id = %d\n", G_STRFUNC, handler->quark)); + + gimp_container_foreach (container, + (GFunc) gimp_container_free_handler_foreach_func, + handler); + + g_free (handler->signame); + g_slice_free (GimpContainerHandler, handler); +} + +GType +gimp_container_get_children_type (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), G_TYPE_NONE); + + return container->priv->children_type; +} + +GimpContainerPolicy +gimp_container_get_policy (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); + + return container->priv->policy; +} + +gint +gimp_container_get_n_children (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); + + return container->priv->n_children; +} + +gboolean +gimp_container_add (GimpContainer *container, + GimpObject *object) +{ + GList *list; + gint n_children; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, + container->priv->children_type), + FALSE); + + if (gimp_container_have (container, object)) + { + g_warning ("%s: container %p already contains object %p", + G_STRFUNC, container, object); + return FALSE; + } + + for (list = container->priv->handlers; list; list = g_list_next (list)) + { + GimpContainerHandler *handler = list->data; + gulong handler_id; + + handler_id = g_signal_connect (object, + handler->signame, + handler->callback, + handler->callback_data); + + g_object_set_qdata (G_OBJECT (object), handler->quark, + GUINT_TO_POINTER (handler_id)); + } + + switch (container->priv->policy) + { + case GIMP_CONTAINER_POLICY_STRONG: + g_object_ref (object); + break; + + case GIMP_CONTAINER_POLICY_WEAK: + g_signal_connect (object, "disconnect", + G_CALLBACK (gimp_container_disconnect_callback), + container); + break; + } + + n_children = container->priv->n_children; + + g_signal_emit (container, container_signals[ADD], 0, object); + + if (n_children == container->priv->n_children) + { + g_warning ("%s: GimpContainer::add() implementation did not " + "chain up. Please report this at https://www.gimp.org/bugs/", + G_STRFUNC); + + container->priv->n_children++; + } + + return TRUE; +} + +gboolean +gimp_container_remove (GimpContainer *container, + GimpObject *object) +{ + GList *list; + gint n_children; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, + container->priv->children_type), + FALSE); + + if (! gimp_container_have (container, object)) + { + g_warning ("%s: container %p does not contain object %p", + G_STRFUNC, container, object); + return FALSE; + } + + for (list = container->priv->handlers; list; list = g_list_next (list)) + { + GimpContainerHandler *handler = list->data; + gulong handler_id; + + handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object), + handler->quark)); + + if (handler_id) + { + g_signal_handler_disconnect (object, handler_id); + + g_object_set_qdata (G_OBJECT (object), handler->quark, NULL); + } + } + + n_children = container->priv->n_children; + + g_signal_emit (container, container_signals[REMOVE], 0, object); + + if (n_children == container->priv->n_children) + { + g_warning ("%s: GimpContainer::remove() implementation did not " + "chain up. Please report this at https://www.gimp.org/bugs/", + G_STRFUNC); + + container->priv->n_children--; + } + + switch (container->priv->policy) + { + case GIMP_CONTAINER_POLICY_STRONG: + g_object_unref (object); + break; + + case GIMP_CONTAINER_POLICY_WEAK: + g_signal_handlers_disconnect_by_func (object, + gimp_container_disconnect_callback, + container); + break; + } + + return TRUE; +} + +gboolean +gimp_container_insert (GimpContainer *container, + GimpObject *object, + gint index) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, + container->priv->children_type), + FALSE); + + g_return_val_if_fail (index >= -1 && + index <= container->priv->n_children, FALSE); + + if (gimp_container_have (container, object)) + { + g_warning ("%s: container %p already contains object %p", + G_STRFUNC, container, object); + return FALSE; + } + + if (gimp_container_add (container, object)) + { + return gimp_container_reorder (container, object, index); + } + + return FALSE; +} + +gboolean +gimp_container_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + gint index; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, + container->priv->children_type), + FALSE); + + g_return_val_if_fail (new_index >= -1 && + new_index < container->priv->n_children, FALSE); + + if (new_index == -1) + new_index = container->priv->n_children - 1; + + index = gimp_container_get_child_index (container, object); + + if (index == -1) + { + g_warning ("%s: container %p does not contain object %p", + G_STRFUNC, container, object); + return FALSE; + } + + if (index != new_index) + g_signal_emit (container, container_signals[REORDER], 0, + object, new_index); + + return TRUE; +} + +void +gimp_container_freeze (GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + container->priv->freeze_count++; + + if (container->priv->freeze_count == 1) + g_signal_emit (container, container_signals[FREEZE], 0); +} + +void +gimp_container_thaw (GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + if (container->priv->freeze_count > 0) + container->priv->freeze_count--; + + if (container->priv->freeze_count == 0) + g_signal_emit (container, container_signals[THAW], 0); +} + +gboolean +gimp_container_frozen (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + + return (container->priv->freeze_count > 0) ? TRUE : FALSE; +} + +gint +gimp_container_freeze_count (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); + + return container->priv->freeze_count; +} + +void +gimp_container_clear (GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + if (container->priv->n_children > 0) + { + gimp_container_freeze (container); + GIMP_CONTAINER_GET_CLASS (container)->clear (container); + gimp_container_thaw (container); + } +} + +gboolean +gimp_container_is_empty (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + + return (container->priv->n_children == 0); +} + +gboolean +gimp_container_have (GimpContainer *container, + GimpObject *object) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + + if (container->priv->n_children < 1) + return FALSE; + + return GIMP_CONTAINER_GET_CLASS (container)->have (container, object); +} + +void +gimp_container_foreach (GimpContainer *container, + GFunc func, + gpointer user_data) +{ + g_return_if_fail (GIMP_IS_CONTAINER (container)); + g_return_if_fail (func != NULL); + + if (container->priv->n_children > 0) + GIMP_CONTAINER_GET_CLASS (container)->foreach (container, func, user_data); +} + +GimpObject * +gimp_container_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (func != NULL, NULL); + + if (container->priv->n_children > 0) + { + return GIMP_CONTAINER_GET_CLASS (container)->search (container, + func, user_data); + } + + return NULL; +} + +gboolean +gimp_container_get_unique_names (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); + + if (GIMP_CONTAINER_GET_CLASS (container)->get_unique_names) + return GIMP_CONTAINER_GET_CLASS (container)->get_unique_names (container); + + return FALSE; +} + +GimpObject * +gimp_container_get_child_by_name (GimpContainer *container, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + + if (!name) + return NULL; + + return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_name (container, + name); +} + +GimpObject * +gimp_container_get_child_by_index (GimpContainer *container, + gint index) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + + if (index < 0 || index >= container->priv->n_children) + return NULL; + + return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, + index); +} + +/** + * gimp_container_get_first_child: + * @container: a #GimpContainer + * + * Return value: the first child object stored in @container or %NULL if the + * container is empty + */ +GimpObject * +gimp_container_get_first_child (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + + if (container->priv->n_children > 0) + return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, + 0); + + return NULL; +} + +/** + * gimp_container_get_last_child: + * @container: a #GimpContainer + * + * Return value: the last child object stored in @container or %NULL if the + * container is empty + */ +GimpObject * +gimp_container_get_last_child (GimpContainer *container) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + + if (container->priv->n_children > 0) + return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, + container->priv->n_children - 1); + + return NULL; +} + +gint +gimp_container_get_child_index (GimpContainer *container, + GimpObject *object) +{ + g_return_val_if_fail (GIMP_IS_CONTAINER (container), -1); + g_return_val_if_fail (object != NULL, -1); + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, + container->priv->children_type), + -1); + + return GIMP_CONTAINER_GET_CLASS (container)->get_child_index (container, + object); +} + +GimpObject * +gimp_container_get_neighbor_of (GimpContainer *container, + GimpObject *object) +{ + gint index; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (GIMP_IS_OBJECT (object), NULL); + + index = gimp_container_get_child_index (container, object); + + if (index != -1) + { + GimpObject *new; + + new = gimp_container_get_child_by_index (container, index + 1); + + if (! new && index > 0) + new = gimp_container_get_child_by_index (container, index - 1); + + return new; + } + + return NULL; +} + +static void +gimp_container_get_name_array_foreach_func (GimpObject *object, + gchar ***iter) +{ + gchar **array = *iter; + + *array = g_strdup (gimp_object_get_name (object)); + (*iter)++; +} + +gchar ** +gimp_container_get_name_array (GimpContainer *container, + gint *length) +{ + gchar **names; + gchar **iter; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (length != NULL, NULL); + + *length = gimp_container_get_n_children (container); + if (*length == 0) + return NULL; + + names = iter = g_new (gchar *, *length); + + gimp_container_foreach (container, + (GFunc) gimp_container_get_name_array_foreach_func, + &iter); + + return names; +} + +static void +gimp_container_add_handler_foreach_func (GimpObject *object, + GimpContainerHandler *handler) +{ + gulong handler_id; + + handler_id = g_signal_connect (object, + handler->signame, + handler->callback, + handler->callback_data); + + g_object_set_qdata (G_OBJECT (object), handler->quark, + GUINT_TO_POINTER (handler_id)); +} + +GQuark +gimp_container_add_handler (GimpContainer *container, + const gchar *signame, + GCallback callback, + gpointer callback_data) +{ + GimpContainerHandler *handler; + gchar *key; + + static gint handler_id = 0; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); + g_return_val_if_fail (signame != NULL, 0); + g_return_val_if_fail (callback != NULL, 0); + + if (! g_str_has_prefix (signame, "notify::")) + g_return_val_if_fail (g_signal_lookup (signame, + container->priv->children_type), 0); + + handler = g_slice_new0 (GimpContainerHandler); + + /* create a unique key for this handler */ + key = g_strdup_printf ("%s-%d", signame, handler_id++); + + handler->signame = g_strdup (signame); + handler->callback = callback; + handler->callback_data = callback_data; + handler->quark = g_quark_from_string (key); + + D (g_print ("%s: key = %s, id = %d\n", G_STRFUNC, key, handler->quark)); + + g_free (key); + + container->priv->handlers = g_list_prepend (container->priv->handlers, handler); + + gimp_container_foreach (container, + (GFunc) gimp_container_add_handler_foreach_func, + handler); + + return handler->quark; +} + +void +gimp_container_remove_handler (GimpContainer *container, + GQuark id) +{ + GimpContainerHandler *handler; + GList *list; + + g_return_if_fail (GIMP_IS_CONTAINER (container)); + g_return_if_fail (id != 0); + + for (list = container->priv->handlers; list; list = g_list_next (list)) + { + handler = (GimpContainerHandler *) list->data; + + if (handler->quark == id) + break; + } + + if (! list) + { + g_warning ("%s: tried to remove handler which unknown id %d", + G_STRFUNC, id); + return; + } + + gimp_container_free_handler (container, handler); + + container->priv->handlers = g_list_delete_link (container->priv->handlers, + list); +} + +void +gimp_container_remove_handlers_by_func (GimpContainer *container, + GCallback callback, + gpointer callback_data) +{ + GList *list; + + g_return_if_fail (GIMP_IS_CONTAINER (container)); + g_return_if_fail (callback != NULL); + + list = container->priv->handlers; + + while (list) + { + GimpContainerHandler *handler = list->data; + GList *next = g_list_next (list); + + if (handler->callback == callback && + handler->callback_data == callback_data) + { + gimp_container_free_handler (container, handler); + + container->priv->handlers = g_list_delete_link ( + container->priv->handlers, list); + } + + list = next; + } +} + +void +gimp_container_remove_handlers_by_data (GimpContainer *container, + gpointer callback_data) +{ + GList *list; + + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + list = container->priv->handlers; + + while (list) + { + GimpContainerHandler *handler = list->data; + GList *next = g_list_next (list); + + if (handler->callback_data == callback_data) + { + gimp_container_free_handler (container, handler); + + container->priv->handlers = g_list_delete_link ( + container->priv->handlers, list); + } + + list = next; + } +} diff --git a/app/core/gimpcontainer.h b/app/core/gimpcontainer.h new file mode 100644 index 0000000..88465b5 --- /dev/null +++ b/app/core/gimpcontainer.h @@ -0,0 +1,150 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcontainer.h + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CONTAINER_H__ +#define __GIMP_CONTAINER_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_CONTAINER (gimp_container_get_type ()) +#define GIMP_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONTAINER, GimpContainer)) +#define GIMP_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CONTAINER, GimpContainerClass)) +#define GIMP_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONTAINER)) +#define GIMP_IS_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CONTAINER)) +#define GIMP_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CONTAINER, GimpContainerClass)) + + +typedef gboolean (* GimpContainerSearchFunc) (GimpObject *object, + gpointer user_data); + + +typedef struct _GimpContainerClass GimpContainerClass; +typedef struct _GimpContainerPrivate GimpContainerPrivate; + +struct _GimpContainer +{ + GimpObject parent_instance; + + GimpContainerPrivate *priv; +}; + +struct _GimpContainerClass +{ + GimpObjectClass parent_class; + + /* signals */ + void (* add) (GimpContainer *container, + GimpObject *object); + void (* remove) (GimpContainer *container, + GimpObject *object); + void (* reorder) (GimpContainer *container, + GimpObject *object, + gint new_index); + void (* freeze) (GimpContainer *container); + void (* thaw) (GimpContainer *container); + + /* virtual functions */ + void (* clear) (GimpContainer *container); + gboolean (* have) (GimpContainer *container, + GimpObject *object); + void (* foreach) (GimpContainer *container, + GFunc func, + gpointer user_data); + GimpObject * (* search) (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data); + gboolean (* get_unique_names) (GimpContainer *container); + GimpObject * (* get_child_by_name) (GimpContainer *container, + const gchar *name); + GimpObject * (* get_child_by_index) (GimpContainer *container, + gint index); + gint (* get_child_index) (GimpContainer *container, + GimpObject *object); +}; + + +GType gimp_container_get_type (void) G_GNUC_CONST; + +GType gimp_container_get_children_type (GimpContainer *container); +GimpContainerPolicy gimp_container_get_policy (GimpContainer *container); +gint gimp_container_get_n_children (GimpContainer *container); + +gboolean gimp_container_add (GimpContainer *container, + GimpObject *object); +gboolean gimp_container_remove (GimpContainer *container, + GimpObject *object); +gboolean gimp_container_insert (GimpContainer *container, + GimpObject *object, + gint new_index); +gboolean gimp_container_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); + +void gimp_container_freeze (GimpContainer *container); +void gimp_container_thaw (GimpContainer *container); +gboolean gimp_container_frozen (GimpContainer *container); +gint gimp_container_freeze_count (GimpContainer *container); + +void gimp_container_clear (GimpContainer *container); +gboolean gimp_container_is_empty (GimpContainer *container); +gboolean gimp_container_have (GimpContainer *container, + GimpObject *object); +void gimp_container_foreach (GimpContainer *container, + GFunc func, + gpointer user_data); +GimpObject * gimp_container_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data); + +gboolean gimp_container_get_unique_names (GimpContainer *container); + +GimpObject * gimp_container_get_child_by_name (GimpContainer *container, + const gchar *name); +GimpObject * gimp_container_get_child_by_index (GimpContainer *container, + gint index); +GimpObject * gimp_container_get_first_child (GimpContainer *container); +GimpObject * gimp_container_get_last_child (GimpContainer *container); +gint gimp_container_get_child_index (GimpContainer *container, + GimpObject *object); + +GimpObject * gimp_container_get_neighbor_of (GimpContainer *container, + GimpObject *object); + +gchar ** gimp_container_get_name_array (GimpContainer *container, + gint *length); + +GQuark gimp_container_add_handler (GimpContainer *container, + const gchar *signame, + GCallback callback, + gpointer callback_data); +void gimp_container_remove_handler (GimpContainer *container, + GQuark id); +void gimp_container_remove_handlers_by_func + (GimpContainer *container, + GCallback callback, + gpointer callback_data); +void gimp_container_remove_handlers_by_data + (GimpContainer *container, + gpointer callback_data); + + +#endif /* __GIMP_CONTAINER_H__ */ diff --git a/app/core/gimpcontext.c b/app/core/gimpcontext.c new file mode 100644 index 0000000..3788583 --- /dev/null +++ b/app/core/gimpcontext.c @@ -0,0 +1,3828 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcontext.c + * Copyright (C) 1999-2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimp-memsize.h" +#include "gimpbrush.h" +#include "gimpbuffer.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpdatafactory.h" +#include "gimpdynamics.h" +#include "gimpimagefile.h" +#include "gimpgradient.h" +#include "gimpimage.h" +#include "gimpmarshal.h" +#include "gimpmybrush.h" +#include "gimppaintinfo.h" +#include "gimppalette.h" +#include "gimppattern.h" +#include "gimptemplate.h" +#include "gimptoolinfo.h" +#include "gimptoolpreset.h" + +#include "text/gimpfont.h" + +#include "gimp-intl.h" + + +#define RGBA_EPSILON 1e-10 + +typedef void (* GimpContextCopyPropFunc) (GimpContext *src, + GimpContext *dest); + +#define context_find_defined(context, prop) \ + while (!(((context)->defined_props) & (1 << (prop))) && (context)->parent) \ + (context) = (context)->parent + +#define COPY_NAME(src, dest, member) \ + g_free (dest->member); \ + dest->member = g_strdup (src->member) + + +/* local function prototypes */ + +static void gimp_context_config_iface_init (GimpConfigInterface *iface); + +static void gimp_context_constructed (GObject *object); +static void gimp_context_dispose (GObject *object); +static void gimp_context_finalize (GObject *object); +static void gimp_context_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_context_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static gint64 gimp_context_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_context_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_context_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_context_serialize_property (GimpConfig *config, + guint property_id, + const GValue *value, + GParamSpec *pspec, + GimpConfigWriter *writer); +static gboolean gimp_context_deserialize_property (GimpConfig *config, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected); +static GimpConfig * gimp_context_duplicate (GimpConfig *config); +static gboolean gimp_context_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + +/* image */ +static void gimp_context_image_removed (GimpContainer *container, + GimpImage *image, + GimpContext *context); +static void gimp_context_real_set_image (GimpContext *context, + GimpImage *image); + +/* display */ +static void gimp_context_display_removed (GimpContainer *container, + gpointer display, + GimpContext *context); +static void gimp_context_real_set_display (GimpContext *context, + gpointer display); + +/* tool */ +static void gimp_context_tool_dirty (GimpToolInfo *tool_info, + GimpContext *context); +static void gimp_context_tool_removed (GimpContainer *container, + GimpToolInfo *tool_info, + GimpContext *context); +static void gimp_context_tool_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_tool (GimpContext *context, + GimpToolInfo *tool_info); + +/* paint info */ +static void gimp_context_paint_info_dirty (GimpPaintInfo *paint_info, + GimpContext *context); +static void gimp_context_paint_info_removed (GimpContainer *container, + GimpPaintInfo *paint_info, + GimpContext *context); +static void gimp_context_paint_info_list_thaw(GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_paint_info (GimpContext *context, + GimpPaintInfo *paint_info); + +/* foreground */ +static void gimp_context_real_set_foreground (GimpContext *context, + const GimpRGB *color); + +/* background */ +static void gimp_context_real_set_background (GimpContext *context, + const GimpRGB *color); + +/* opacity */ +static void gimp_context_real_set_opacity (GimpContext *context, + gdouble opacity); + +/* paint mode */ +static void gimp_context_real_set_paint_mode (GimpContext *context, + GimpLayerMode paint_mode); + +/* brush */ +static void gimp_context_brush_dirty (GimpBrush *brush, + GimpContext *context); +static void gimp_context_brush_removed (GimpContainer *brush_list, + GimpBrush *brush, + GimpContext *context); +static void gimp_context_brush_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_brush (GimpContext *context, + GimpBrush *brush); + +/* dynamics */ + +static void gimp_context_dynamics_dirty (GimpDynamics *dynamics, + GimpContext *context); +static void gimp_context_dynamics_removed (GimpContainer *container, + GimpDynamics *dynamics, + GimpContext *context); +static void gimp_context_dynamics_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_dynamics (GimpContext *context, + GimpDynamics *dynamics); + +/* mybrush */ +static void gimp_context_mybrush_dirty (GimpMybrush *brush, + GimpContext *context); +static void gimp_context_mybrush_removed (GimpContainer *brush_list, + GimpMybrush *brush, + GimpContext *context); +static void gimp_context_mybrush_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_mybrush (GimpContext *context, + GimpMybrush *brush); + +/* pattern */ +static void gimp_context_pattern_dirty (GimpPattern *pattern, + GimpContext *context); +static void gimp_context_pattern_removed (GimpContainer *container, + GimpPattern *pattern, + GimpContext *context); +static void gimp_context_pattern_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_pattern (GimpContext *context, + GimpPattern *pattern); + +/* gradient */ +static void gimp_context_gradient_dirty (GimpGradient *gradient, + GimpContext *context); +static void gimp_context_gradient_removed (GimpContainer *container, + GimpGradient *gradient, + GimpContext *context); +static void gimp_context_gradient_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_gradient (GimpContext *context, + GimpGradient *gradient); + +/* palette */ +static void gimp_context_palette_dirty (GimpPalette *palette, + GimpContext *context); +static void gimp_context_palette_removed (GimpContainer *container, + GimpPalette *palette, + GimpContext *context); +static void gimp_context_palette_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_palette (GimpContext *context, + GimpPalette *palette); + +/* font */ +static void gimp_context_font_dirty (GimpFont *font, + GimpContext *context); +static void gimp_context_font_removed (GimpContainer *container, + GimpFont *font, + GimpContext *context); +static void gimp_context_font_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_font (GimpContext *context, + GimpFont *font); + +/* tool preset */ +static void gimp_context_tool_preset_dirty (GimpToolPreset *tool_preset, + GimpContext *context); +static void gimp_context_tool_preset_removed (GimpContainer *container, + GimpToolPreset *tool_preset, + GimpContext *context); +static void gimp_context_tool_preset_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_tool_preset (GimpContext *context, + GimpToolPreset *tool_preset); + +/* buffer */ +static void gimp_context_buffer_dirty (GimpBuffer *buffer, + GimpContext *context); +static void gimp_context_buffer_removed (GimpContainer *container, + GimpBuffer *buffer, + GimpContext *context); +static void gimp_context_buffer_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_buffer (GimpContext *context, + GimpBuffer *buffer); + +/* imagefile */ +static void gimp_context_imagefile_dirty (GimpImagefile *imagefile, + GimpContext *context); +static void gimp_context_imagefile_removed (GimpContainer *container, + GimpImagefile *imagefile, + GimpContext *context); +static void gimp_context_imagefile_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_imagefile (GimpContext *context, + GimpImagefile *imagefile); + +/* template */ +static void gimp_context_template_dirty (GimpTemplate *template, + GimpContext *context); +static void gimp_context_template_removed (GimpContainer *container, + GimpTemplate *template, + GimpContext *context); +static void gimp_context_template_list_thaw (GimpContainer *container, + GimpContext *context); +static void gimp_context_real_set_template (GimpContext *context, + GimpTemplate *template); + + +/* utilities */ +static gpointer gimp_context_find_object (GimpContext *context, + GimpContainer *container, + const gchar *object_name, + gpointer standard_object); + + +/* properties & signals */ + +enum +{ + GIMP_CONTEXT_PROP_0, + GIMP_CONTEXT_PROP_GIMP + + /* remaining values are in core-enums.h (GimpContextPropType) */ +}; + +enum +{ + DUMMY_0, + DUMMY_1, + IMAGE_CHANGED, + DISPLAY_CHANGED, + TOOL_CHANGED, + PAINT_INFO_CHANGED, + FOREGROUND_CHANGED, + BACKGROUND_CHANGED, + OPACITY_CHANGED, + PAINT_MODE_CHANGED, + BRUSH_CHANGED, + DYNAMICS_CHANGED, + MYBRUSH_CHANGED, + PATTERN_CHANGED, + GRADIENT_CHANGED, + PALETTE_CHANGED, + FONT_CHANGED, + TOOL_PRESET_CHANGED, + BUFFER_CHANGED, + IMAGEFILE_CHANGED, + TEMPLATE_CHANGED, + PROP_NAME_CHANGED, + LAST_SIGNAL +}; + +static const gchar * const gimp_context_prop_names[] = +{ + NULL, /* PROP_0 */ + "gimp", + "image", + "display", + "tool", + "paint-info", + "foreground", + "background", + "opacity", + "paint-mode", + "brush", + "dynamics", + "mybrush", + "pattern", + "gradient", + "palette", + "font", + "tool-preset", + "buffer", + "imagefile", + "template" +}; + +static GType gimp_context_prop_types[] = +{ + G_TYPE_NONE, /* PROP_0 */ + G_TYPE_NONE, /* PROP_GIMP */ + 0, + G_TYPE_NONE, + 0, + 0, + G_TYPE_NONE, + G_TYPE_NONE, + G_TYPE_NONE, + G_TYPE_NONE, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + + +G_DEFINE_TYPE_WITH_CODE (GimpContext, gimp_context, GIMP_TYPE_VIEWABLE, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_context_config_iface_init)) + +#define parent_class gimp_context_parent_class + +static GimpConfigInterface *parent_config_iface = NULL; + +static guint gimp_context_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_context_class_init (GimpContextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpRGB black; + GimpRGB white; + + gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); + gimp_rgba_set (&white, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + + gimp_context_signals[IMAGE_CHANGED] = + g_signal_new ("image-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, image_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_IMAGE); + + gimp_context_signals[DISPLAY_CHANGED] = + g_signal_new ("display-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, display_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_OBJECT); + + gimp_context_signals[TOOL_CHANGED] = + g_signal_new ("tool-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, tool_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_TOOL_INFO); + + gimp_context_signals[PAINT_INFO_CHANGED] = + g_signal_new ("paint-info-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, paint_info_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_PAINT_INFO); + + gimp_context_signals[FOREGROUND_CHANGED] = + g_signal_new ("foreground-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, foreground_changed), + NULL, NULL, + gimp_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GIMP_TYPE_RGB | G_SIGNAL_TYPE_STATIC_SCOPE); + + gimp_context_signals[BACKGROUND_CHANGED] = + g_signal_new ("background-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, background_changed), + NULL, NULL, + gimp_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GIMP_TYPE_RGB | G_SIGNAL_TYPE_STATIC_SCOPE); + + gimp_context_signals[OPACITY_CHANGED] = + g_signal_new ("opacity-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, opacity_changed), + NULL, NULL, + gimp_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, + G_TYPE_DOUBLE); + + gimp_context_signals[PAINT_MODE_CHANGED] = + g_signal_new ("paint-mode-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, paint_mode_changed), + NULL, NULL, + gimp_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GIMP_TYPE_LAYER_MODE); + + gimp_context_signals[BRUSH_CHANGED] = + g_signal_new ("brush-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, brush_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_BRUSH); + + gimp_context_signals[DYNAMICS_CHANGED] = + g_signal_new ("dynamics-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, dynamics_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_DYNAMICS); + + gimp_context_signals[MYBRUSH_CHANGED] = + g_signal_new ("mybrush-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, mybrush_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_MYBRUSH); + + gimp_context_signals[PATTERN_CHANGED] = + g_signal_new ("pattern-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, pattern_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_PATTERN); + + gimp_context_signals[GRADIENT_CHANGED] = + g_signal_new ("gradient-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, gradient_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_GRADIENT); + + gimp_context_signals[PALETTE_CHANGED] = + g_signal_new ("palette-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, palette_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_PALETTE); + + gimp_context_signals[FONT_CHANGED] = + g_signal_new ("font-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, font_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_FONT); + + gimp_context_signals[TOOL_PRESET_CHANGED] = + g_signal_new ("tool-preset-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, tool_preset_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_TOOL_PRESET); + + gimp_context_signals[BUFFER_CHANGED] = + g_signal_new ("buffer-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, buffer_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_BUFFER); + + gimp_context_signals[IMAGEFILE_CHANGED] = + g_signal_new ("imagefile-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, imagefile_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_IMAGEFILE); + + gimp_context_signals[TEMPLATE_CHANGED] = + g_signal_new ("template-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, template_changed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_TEMPLATE); + + gimp_context_signals[PROP_NAME_CHANGED] = + g_signal_new ("prop-name-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpContextClass, prop_name_changed), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + object_class->constructed = gimp_context_constructed; + object_class->set_property = gimp_context_set_property; + object_class->get_property = gimp_context_get_property; + object_class->dispose = gimp_context_dispose; + object_class->finalize = gimp_context_finalize; + + gimp_object_class->get_memsize = gimp_context_get_memsize; + + klass->image_changed = NULL; + klass->display_changed = NULL; + klass->tool_changed = NULL; + klass->paint_info_changed = NULL; + klass->foreground_changed = NULL; + klass->background_changed = NULL; + klass->opacity_changed = NULL; + klass->paint_mode_changed = NULL; + klass->brush_changed = NULL; + klass->dynamics_changed = NULL; + klass->mybrush_changed = NULL; + klass->pattern_changed = NULL; + klass->gradient_changed = NULL; + klass->palette_changed = NULL; + klass->font_changed = NULL; + klass->tool_preset_changed = NULL; + klass->buffer_changed = NULL; + klass->imagefile_changed = NULL; + klass->template_changed = NULL; + klass->prop_name_changed = NULL; + + gimp_context_prop_types[GIMP_CONTEXT_PROP_IMAGE] = GIMP_TYPE_IMAGE; + gimp_context_prop_types[GIMP_CONTEXT_PROP_TOOL] = GIMP_TYPE_TOOL_INFO; + gimp_context_prop_types[GIMP_CONTEXT_PROP_PAINT_INFO] = GIMP_TYPE_PAINT_INFO; + gimp_context_prop_types[GIMP_CONTEXT_PROP_BRUSH] = GIMP_TYPE_BRUSH; + gimp_context_prop_types[GIMP_CONTEXT_PROP_DYNAMICS] = GIMP_TYPE_DYNAMICS; + gimp_context_prop_types[GIMP_CONTEXT_PROP_MYBRUSH] = GIMP_TYPE_MYBRUSH; + gimp_context_prop_types[GIMP_CONTEXT_PROP_PATTERN] = GIMP_TYPE_PATTERN; + gimp_context_prop_types[GIMP_CONTEXT_PROP_GRADIENT] = GIMP_TYPE_GRADIENT; + gimp_context_prop_types[GIMP_CONTEXT_PROP_PALETTE] = GIMP_TYPE_PALETTE; + gimp_context_prop_types[GIMP_CONTEXT_PROP_FONT] = GIMP_TYPE_FONT; + gimp_context_prop_types[GIMP_CONTEXT_PROP_TOOL_PRESET] = GIMP_TYPE_TOOL_PRESET; + gimp_context_prop_types[GIMP_CONTEXT_PROP_BUFFER] = GIMP_TYPE_BUFFER; + gimp_context_prop_types[GIMP_CONTEXT_PROP_IMAGEFILE] = GIMP_TYPE_IMAGEFILE; + gimp_context_prop_types[GIMP_CONTEXT_PROP_TEMPLATE] = GIMP_TYPE_TEMPLATE; + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_GIMP, + g_param_spec_object ("gimp", + NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_IMAGE, + g_param_spec_object (gimp_context_prop_names[GIMP_CONTEXT_PROP_IMAGE], + NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_DISPLAY, + g_param_spec_object (gimp_context_prop_names[GIMP_CONTEXT_PROP_DISPLAY], + NULL, NULL, + GIMP_TYPE_OBJECT, + GIMP_PARAM_READWRITE)); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_TOOL, + gimp_context_prop_names[GIMP_CONTEXT_PROP_TOOL], + NULL, NULL, + GIMP_TYPE_TOOL_INFO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_PAINT_INFO, + gimp_context_prop_names[GIMP_CONTEXT_PROP_PAINT_INFO], + NULL, NULL, + GIMP_TYPE_PAINT_INFO, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, GIMP_CONTEXT_PROP_FOREGROUND, + gimp_context_prop_names[GIMP_CONTEXT_PROP_FOREGROUND], + _("Foreground"), + _("Foreground color"), + FALSE, &black, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, GIMP_CONTEXT_PROP_BACKGROUND, + gimp_context_prop_names[GIMP_CONTEXT_PROP_BACKGROUND], + _("Background"), + _("Background color"), + FALSE, &white, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, GIMP_CONTEXT_PROP_OPACITY, + gimp_context_prop_names[GIMP_CONTEXT_PROP_OPACITY], + _("Opacity"), + _("Opacity"), + GIMP_OPACITY_TRANSPARENT, + GIMP_OPACITY_OPAQUE, + GIMP_OPACITY_OPAQUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, GIMP_CONTEXT_PROP_PAINT_MODE, + gimp_context_prop_names[GIMP_CONTEXT_PROP_PAINT_MODE], + _("Paint Mode"), + _("Paint Mode"), + GIMP_TYPE_LAYER_MODE, + GIMP_LAYER_MODE_NORMAL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_BRUSH, + gimp_context_prop_names[GIMP_CONTEXT_PROP_BRUSH], + _("Brush"), + _("Brush"), + GIMP_TYPE_BRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_DYNAMICS, + gimp_context_prop_names[GIMP_CONTEXT_PROP_DYNAMICS], + _("Dynamics"), + _("Paint dynamics"), + GIMP_TYPE_DYNAMICS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_MYBRUSH, + gimp_context_prop_names[GIMP_CONTEXT_PROP_MYBRUSH], + _("MyPaint Brush"), + _("MyPaint Brush"), + GIMP_TYPE_MYBRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_PATTERN, + gimp_context_prop_names[GIMP_CONTEXT_PROP_PATTERN], + _("Pattern"), + _("Pattern"), + GIMP_TYPE_PATTERN, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_GRADIENT, + gimp_context_prop_names[GIMP_CONTEXT_PROP_GRADIENT], + _("Gradient"), + _("Gradient"), + GIMP_TYPE_GRADIENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_PALETTE, + gimp_context_prop_names[GIMP_CONTEXT_PROP_PALETTE], + _("Palette"), + _("Palette"), + GIMP_TYPE_PALETTE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_FONT, + gimp_context_prop_names[GIMP_CONTEXT_PROP_FONT], + _("Font"), + _("Font"), + GIMP_TYPE_FONT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, GIMP_CONTEXT_PROP_TOOL_PRESET, + gimp_context_prop_names[GIMP_CONTEXT_PROP_TOOL_PRESET], + _("Tool Preset"), + _("Tool Preset"), + GIMP_TYPE_TOOL_PRESET, + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_BUFFER, + g_param_spec_object (gimp_context_prop_names[GIMP_CONTEXT_PROP_BUFFER], + NULL, NULL, + GIMP_TYPE_BUFFER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_IMAGEFILE, + g_param_spec_object (gimp_context_prop_names[GIMP_CONTEXT_PROP_IMAGEFILE], + NULL, NULL, + GIMP_TYPE_IMAGEFILE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, GIMP_CONTEXT_PROP_TEMPLATE, + g_param_spec_object (gimp_context_prop_names[GIMP_CONTEXT_PROP_TEMPLATE], + NULL, NULL, + GIMP_TYPE_TEMPLATE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_context_init (GimpContext *context) +{ + context->gimp = NULL; + + context->parent = NULL; + + context->defined_props = GIMP_CONTEXT_PROP_MASK_ALL; + context->serialize_props = GIMP_CONTEXT_PROP_MASK_ALL; + + context->image = NULL; + context->display = NULL; + + context->tool_info = NULL; + context->tool_name = NULL; + + context->paint_info = NULL; + context->paint_name = NULL; + + context->brush = NULL; + context->brush_name = NULL; + + context->dynamics = NULL; + context->dynamics_name = NULL; + + context->mybrush = NULL; + context->mybrush_name = NULL; + + context->pattern = NULL; + context->pattern_name = NULL; + + context->gradient = NULL; + context->gradient_name = NULL; + + context->palette = NULL; + context->palette_name = NULL; + + context->font = NULL; + context->font_name = NULL; + + context->tool_preset = NULL; + context->tool_preset_name = NULL; + + context->buffer = NULL; + context->buffer_name = NULL; + + context->imagefile = NULL; + context->imagefile_name = NULL; + + context->template = NULL; + context->template_name = NULL; +} + +static void +gimp_context_config_iface_init (GimpConfigInterface *iface) +{ + parent_config_iface = g_type_interface_peek_parent (iface); + + if (! parent_config_iface) + parent_config_iface = g_type_default_interface_peek (GIMP_TYPE_CONFIG); + + iface->serialize = gimp_context_serialize; + iface->deserialize = gimp_context_deserialize; + iface->serialize_property = gimp_context_serialize_property; + iface->deserialize_property = gimp_context_deserialize_property; + iface->duplicate = gimp_context_duplicate; + iface->copy = gimp_context_copy; +} + +static void +gimp_context_constructed (GObject *object) +{ + Gimp *gimp; + GimpContainer *container; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp = GIMP_CONTEXT (object)->gimp; + + gimp_assert (GIMP_IS_GIMP (gimp)); + + gimp->context_list = g_list_prepend (gimp->context_list, object); + + g_signal_connect_object (gimp->images, "remove", + G_CALLBACK (gimp_context_image_removed), + object, 0); + g_signal_connect_object (gimp->displays, "remove", + G_CALLBACK (gimp_context_display_removed), + object, 0); + + g_signal_connect_object (gimp->tool_info_list, "remove", + G_CALLBACK (gimp_context_tool_removed), + object, 0); + g_signal_connect_object (gimp->tool_info_list, "thaw", + G_CALLBACK (gimp_context_tool_list_thaw), + object, 0); + + g_signal_connect_object (gimp->paint_info_list, "remove", + G_CALLBACK (gimp_context_paint_info_removed), + object, 0); + g_signal_connect_object (gimp->paint_info_list, "thaw", + G_CALLBACK (gimp_context_paint_info_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->brush_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_brush_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_brush_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->dynamics_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_dynamics_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_dynamics_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->mybrush_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_mybrush_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_mybrush_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->pattern_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_pattern_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_pattern_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->gradient_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_gradient_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_gradient_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->palette_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_palette_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_palette_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->font_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_font_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_font_list_thaw), + object, 0); + + container = gimp_data_factory_get_container (gimp->tool_preset_factory); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_context_tool_preset_removed), + object, 0); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_context_tool_preset_list_thaw), + object, 0); + + g_signal_connect_object (gimp->named_buffers, "remove", + G_CALLBACK (gimp_context_buffer_removed), + object, 0); + g_signal_connect_object (gimp->named_buffers, "thaw", + G_CALLBACK (gimp_context_buffer_list_thaw), + object, 0); + + g_signal_connect_object (gimp->documents, "remove", + G_CALLBACK (gimp_context_imagefile_removed), + object, 0); + g_signal_connect_object (gimp->documents, "thaw", + G_CALLBACK (gimp_context_imagefile_list_thaw), + object, 0); + + g_signal_connect_object (gimp->templates, "remove", + G_CALLBACK (gimp_context_template_removed), + object, 0); + g_signal_connect_object (gimp->templates, "thaw", + G_CALLBACK (gimp_context_template_list_thaw), + object, 0); + + gimp_context_set_paint_info (GIMP_CONTEXT (object), + gimp_paint_info_get_standard (gimp)); +} + +static void +gimp_context_dispose (GObject *object) +{ + GimpContext *context = GIMP_CONTEXT (object); + + gimp_context_set_parent (context, NULL); + + if (context->gimp) + { + context->gimp->context_list = g_list_remove (context->gimp->context_list, + context); + context->gimp = NULL; + } + + g_clear_object (&context->tool_info); + g_clear_object (&context->paint_info); + g_clear_object (&context->brush); + g_clear_object (&context->dynamics); + g_clear_object (&context->mybrush); + g_clear_object (&context->pattern); + g_clear_object (&context->gradient); + g_clear_object (&context->palette); + g_clear_object (&context->font); + g_clear_object (&context->tool_preset); + g_clear_object (&context->buffer); + g_clear_object (&context->imagefile); + g_clear_object (&context->template); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_context_finalize (GObject *object) +{ + GimpContext *context = GIMP_CONTEXT (object); + + context->parent = NULL; + context->image = NULL; + context->display = NULL; + + g_clear_pointer (&context->tool_name, g_free); + g_clear_pointer (&context->paint_name, g_free); + g_clear_pointer (&context->brush_name, g_free); + g_clear_pointer (&context->dynamics_name, g_free); + g_clear_pointer (&context->mybrush_name, g_free); + g_clear_pointer (&context->pattern_name, g_free); + g_clear_pointer (&context->gradient_name, g_free); + g_clear_pointer (&context->palette_name, g_free); + g_clear_pointer (&context->font_name, g_free); + g_clear_pointer (&context->tool_preset_name, g_free); + g_clear_pointer (&context->buffer_name, g_free); + g_clear_pointer (&context->imagefile_name, g_free); + g_clear_pointer (&context->template_name, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_context_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpContext *context = GIMP_CONTEXT (object); + + switch (property_id) + { + case GIMP_CONTEXT_PROP_GIMP: + context->gimp = g_value_get_object (value); + break; + case GIMP_CONTEXT_PROP_IMAGE: + gimp_context_set_image (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_DISPLAY: + gimp_context_set_display (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_TOOL: + gimp_context_set_tool (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_PAINT_INFO: + gimp_context_set_paint_info (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_FOREGROUND: + gimp_context_set_foreground (context, g_value_get_boxed (value)); + break; + case GIMP_CONTEXT_PROP_BACKGROUND: + gimp_context_set_background (context, g_value_get_boxed (value)); + break; + case GIMP_CONTEXT_PROP_OPACITY: + gimp_context_set_opacity (context, g_value_get_double (value)); + break; + case GIMP_CONTEXT_PROP_PAINT_MODE: + gimp_context_set_paint_mode (context, g_value_get_enum (value)); + break; + case GIMP_CONTEXT_PROP_BRUSH: + gimp_context_set_brush (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_DYNAMICS: + gimp_context_set_dynamics (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_MYBRUSH: + gimp_context_set_mybrush (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_PATTERN: + gimp_context_set_pattern (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_GRADIENT: + gimp_context_set_gradient (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_PALETTE: + gimp_context_set_palette (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_FONT: + gimp_context_set_font (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_TOOL_PRESET: + gimp_context_set_tool_preset (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_BUFFER: + gimp_context_set_buffer (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_IMAGEFILE: + gimp_context_set_imagefile (context, g_value_get_object (value)); + break; + case GIMP_CONTEXT_PROP_TEMPLATE: + gimp_context_set_template (context, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_context_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpContext *context = GIMP_CONTEXT (object); + + switch (property_id) + { + case GIMP_CONTEXT_PROP_GIMP: + g_value_set_object (value, context->gimp); + break; + case GIMP_CONTEXT_PROP_IMAGE: + g_value_set_object (value, gimp_context_get_image (context)); + break; + case GIMP_CONTEXT_PROP_DISPLAY: + g_value_set_object (value, gimp_context_get_display (context)); + break; + case GIMP_CONTEXT_PROP_TOOL: + g_value_set_object (value, gimp_context_get_tool (context)); + break; + case GIMP_CONTEXT_PROP_PAINT_INFO: + g_value_set_object (value, gimp_context_get_paint_info (context)); + break; + case GIMP_CONTEXT_PROP_FOREGROUND: + { + GimpRGB color; + + gimp_context_get_foreground (context, &color); + g_value_set_boxed (value, &color); + } + break; + case GIMP_CONTEXT_PROP_BACKGROUND: + { + GimpRGB color; + + gimp_context_get_background (context, &color); + g_value_set_boxed (value, &color); + } + break; + case GIMP_CONTEXT_PROP_OPACITY: + g_value_set_double (value, gimp_context_get_opacity (context)); + break; + case GIMP_CONTEXT_PROP_PAINT_MODE: + g_value_set_enum (value, gimp_context_get_paint_mode (context)); + break; + case GIMP_CONTEXT_PROP_BRUSH: + g_value_set_object (value, gimp_context_get_brush (context)); + break; + case GIMP_CONTEXT_PROP_DYNAMICS: + g_value_set_object (value, gimp_context_get_dynamics (context)); + break; + case GIMP_CONTEXT_PROP_MYBRUSH: + g_value_set_object (value, gimp_context_get_mybrush (context)); + break; + case GIMP_CONTEXT_PROP_PATTERN: + g_value_set_object (value, gimp_context_get_pattern (context)); + break; + case GIMP_CONTEXT_PROP_GRADIENT: + g_value_set_object (value, gimp_context_get_gradient (context)); + break; + case GIMP_CONTEXT_PROP_PALETTE: + g_value_set_object (value, gimp_context_get_palette (context)); + break; + case GIMP_CONTEXT_PROP_FONT: + g_value_set_object (value, gimp_context_get_font (context)); + break; + case GIMP_CONTEXT_PROP_TOOL_PRESET: + g_value_set_object (value, gimp_context_get_tool_preset (context)); + break; + case GIMP_CONTEXT_PROP_BUFFER: + g_value_set_object (value, gimp_context_get_buffer (context)); + break; + case GIMP_CONTEXT_PROP_IMAGEFILE: + g_value_set_object (value, gimp_context_get_imagefile (context)); + break; + case GIMP_CONTEXT_PROP_TEMPLATE: + g_value_set_object (value, gimp_context_get_template (context)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_context_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpContext *context = GIMP_CONTEXT (object); + gint64 memsize = 0; + + memsize += gimp_string_get_memsize (context->tool_name); + memsize += gimp_string_get_memsize (context->paint_name); + memsize += gimp_string_get_memsize (context->brush_name); + memsize += gimp_string_get_memsize (context->dynamics_name); + memsize += gimp_string_get_memsize (context->mybrush_name); + memsize += gimp_string_get_memsize (context->pattern_name); + memsize += gimp_string_get_memsize (context->palette_name); + memsize += gimp_string_get_memsize (context->font_name); + memsize += gimp_string_get_memsize (context->tool_preset_name); + memsize += gimp_string_get_memsize (context->buffer_name); + memsize += gimp_string_get_memsize (context->imagefile_name); + memsize += gimp_string_get_memsize (context->template_name); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_context_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + return gimp_config_serialize_changed_properties (config, writer); +} + +static gboolean +gimp_context_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpContext *context = GIMP_CONTEXT (config); + GimpLayerMode old_paint_mode = context->paint_mode; + gboolean success; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + if (context->paint_mode != old_paint_mode) + { + if (context->paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY) + g_object_set (context, + "paint-mode", GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, + NULL); + } + + return success; +} + +static gboolean +gimp_context_serialize_property (GimpConfig *config, + guint property_id, + const GValue *value, + GParamSpec *pspec, + GimpConfigWriter *writer) +{ + GimpContext *context = GIMP_CONTEXT (config); + GimpObject *serialize_obj; + + /* serialize nothing if the property is not in serialize_props */ + if (! ((1 << property_id) & context->serialize_props)) + return TRUE; + + switch (property_id) + { + case GIMP_CONTEXT_PROP_TOOL: + case GIMP_CONTEXT_PROP_PAINT_INFO: + case GIMP_CONTEXT_PROP_BRUSH: + case GIMP_CONTEXT_PROP_DYNAMICS: + case GIMP_CONTEXT_PROP_MYBRUSH: + case GIMP_CONTEXT_PROP_PATTERN: + case GIMP_CONTEXT_PROP_GRADIENT: + case GIMP_CONTEXT_PROP_PALETTE: + case GIMP_CONTEXT_PROP_FONT: + case GIMP_CONTEXT_PROP_TOOL_PRESET: + serialize_obj = g_value_get_object (value); + break; + + default: + return FALSE; + } + + gimp_config_writer_open (writer, pspec->name); + + if (serialize_obj) + gimp_config_writer_string (writer, gimp_object_get_name (serialize_obj)); + else + gimp_config_writer_print (writer, "NULL", 4); + + gimp_config_writer_close (writer); + + return TRUE; +} + +static gboolean +gimp_context_deserialize_property (GimpConfig *object, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected) +{ + GimpContext *context = GIMP_CONTEXT (object); + GimpContainer *container; + gpointer standard; + gchar **name_loc; + gchar *object_name; + + switch (property_id) + { + case GIMP_CONTEXT_PROP_TOOL: + container = context->gimp->tool_info_list; + standard = gimp_tool_info_get_standard (context->gimp); + name_loc = &context->tool_name; + break; + + case GIMP_CONTEXT_PROP_PAINT_INFO: + container = context->gimp->paint_info_list; + standard = gimp_paint_info_get_standard (context->gimp); + name_loc = &context->paint_name; + break; + + case GIMP_CONTEXT_PROP_BRUSH: + container = gimp_data_factory_get_container (context->gimp->brush_factory); + standard = gimp_brush_get_standard (context); + name_loc = &context->brush_name; + break; + + case GIMP_CONTEXT_PROP_DYNAMICS: + container = gimp_data_factory_get_container (context->gimp->dynamics_factory); + standard = gimp_dynamics_get_standard (context); + name_loc = &context->dynamics_name; + break; + + case GIMP_CONTEXT_PROP_MYBRUSH: + container = gimp_data_factory_get_container (context->gimp->mybrush_factory); + standard = gimp_mybrush_get_standard (context); + name_loc = &context->mybrush_name; + break; + + case GIMP_CONTEXT_PROP_PATTERN: + container = gimp_data_factory_get_container (context->gimp->pattern_factory); + standard = gimp_pattern_get_standard (context); + name_loc = &context->pattern_name; + break; + + case GIMP_CONTEXT_PROP_GRADIENT: + container = gimp_data_factory_get_container (context->gimp->gradient_factory); + standard = gimp_gradient_get_standard (context); + name_loc = &context->gradient_name; + break; + + case GIMP_CONTEXT_PROP_PALETTE: + container = gimp_data_factory_get_container (context->gimp->palette_factory); + standard = gimp_palette_get_standard (context); + name_loc = &context->palette_name; + break; + + case GIMP_CONTEXT_PROP_FONT: + container = gimp_data_factory_get_container (context->gimp->font_factory); + standard = gimp_font_get_standard (); + name_loc = &context->font_name; + break; + + case GIMP_CONTEXT_PROP_TOOL_PRESET: + container = gimp_data_factory_get_container (context->gimp->tool_preset_factory); + standard = NULL; + name_loc = &context->tool_preset_name; + break; + + default: + return FALSE; + } + + if (gimp_scanner_parse_identifier (scanner, "NULL")) + { + g_value_set_object (value, NULL); + } + else if (gimp_scanner_parse_string (scanner, &object_name)) + { + GimpObject *deserialize_obj; + + if (! object_name) + object_name = g_strdup (""); + + deserialize_obj = gimp_container_get_child_by_name (container, + object_name); + + if (! deserialize_obj) + { + g_value_set_object (value, standard); + + g_free (*name_loc); + *name_loc = g_strdup (object_name); + } + else + { + g_value_set_object (value, deserialize_obj); + } + + g_free (object_name); + } + else + { + *expected = G_TOKEN_STRING; + } + + return TRUE; +} + +static GimpConfig * +gimp_context_duplicate (GimpConfig *config) +{ + GimpContext *context = GIMP_CONTEXT (config); + GimpContext *new; + + new = GIMP_CONTEXT (parent_config_iface->duplicate (config)); + + COPY_NAME (context, new, tool_name); + COPY_NAME (context, new, paint_name); + COPY_NAME (context, new, brush_name); + COPY_NAME (context, new, dynamics_name); + COPY_NAME (context, new, mybrush_name); + COPY_NAME (context, new, pattern_name); + COPY_NAME (context, new, gradient_name); + COPY_NAME (context, new, palette_name); + COPY_NAME (context, new, font_name); + COPY_NAME (context, new, tool_preset_name); + COPY_NAME (context, new, buffer_name); + COPY_NAME (context, new, imagefile_name); + COPY_NAME (context, new, template_name); + + return GIMP_CONFIG (new); +} + +static gboolean +gimp_context_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpContext *src_context = GIMP_CONTEXT (src); + GimpContext *dest_context = GIMP_CONTEXT (dest); + gboolean success = parent_config_iface->copy (src, dest, flags); + + COPY_NAME (src_context, dest_context, tool_name); + COPY_NAME (src_context, dest_context, paint_name); + COPY_NAME (src_context, dest_context, brush_name); + COPY_NAME (src_context, dest_context, dynamics_name); + COPY_NAME (src_context, dest_context, mybrush_name); + COPY_NAME (src_context, dest_context, pattern_name); + COPY_NAME (src_context, dest_context, gradient_name); + COPY_NAME (src_context, dest_context, palette_name); + COPY_NAME (src_context, dest_context, font_name); + COPY_NAME (src_context, dest_context, tool_preset_name); + COPY_NAME (src_context, dest_context, buffer_name); + COPY_NAME (src_context, dest_context, imagefile_name); + COPY_NAME (src_context, dest_context, template_name); + + return success; +} + + +/*****************************************************************************/ +/* public functions ********************************************************/ + +GimpContext * +gimp_context_new (Gimp *gimp, + const gchar *name, + GimpContext *template) +{ + GimpContext *context; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (template == NULL || GIMP_IS_CONTEXT (template), NULL); + + context = g_object_new (GIMP_TYPE_CONTEXT, + "name", name, + "gimp", gimp, + NULL); + + if (template) + { + context->defined_props = template->defined_props; + + gimp_context_copy_properties (template, context, + GIMP_CONTEXT_PROP_MASK_ALL); + } + + return context; +} + +GimpContext * +gimp_context_get_parent (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->parent; +} + +static void +gimp_context_parent_notify (GimpContext *parent, + GParamSpec *pspec, + GimpContext *context) +{ + if (pspec->owner_type == GIMP_TYPE_CONTEXT) + { + GimpContextPropType prop = pspec->param_id; + + /* copy from parent if the changed property is undefined; + * ignore properties that are not context properties, for + * example notifications on the context's "gimp" property + */ + if ((prop >= GIMP_CONTEXT_PROP_FIRST) && + (prop <= GIMP_CONTEXT_PROP_LAST) && + ! ((1 << prop) & context->defined_props)) + { + gimp_context_copy_property (parent, context, prop); + } + } +} + +void +gimp_context_set_parent (GimpContext *context, + GimpContext *parent) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (parent == NULL || GIMP_IS_CONTEXT (parent)); + g_return_if_fail (parent == NULL || parent->parent != context); + g_return_if_fail (context != parent); + + if (context->parent == parent) + return; + + if (context->parent) + { + g_signal_handlers_disconnect_by_func (context->parent, + gimp_context_parent_notify, + context); + + g_object_remove_weak_pointer (G_OBJECT (context->parent), + (gpointer) &context->parent); + } + + context->parent = parent; + + if (parent) + { + g_object_add_weak_pointer (G_OBJECT (context->parent), + (gpointer) &context->parent); + + /* copy all undefined properties from the new parent */ + gimp_context_copy_properties (parent, context, + ~context->defined_props & + GIMP_CONTEXT_PROP_MASK_ALL); + + g_signal_connect_object (parent, "notify", + G_CALLBACK (gimp_context_parent_notify), + context, + 0); + } +} + + +/* define / undefinine context properties */ + +void +gimp_context_define_property (GimpContext *context, + GimpContextPropType prop, + gboolean defined) +{ + GimpContextPropMask mask; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail ((prop >= GIMP_CONTEXT_PROP_FIRST) && + (prop <= GIMP_CONTEXT_PROP_LAST)); + + mask = (1 << prop); + + if (defined) + { + if (! (context->defined_props & mask)) + { + context->defined_props |= mask; + } + } + else + { + if (context->defined_props & mask) + { + context->defined_props &= ~mask; + + if (context->parent) + gimp_context_copy_property (context->parent, context, prop); + } + } +} + +gboolean +gimp_context_property_defined (GimpContext *context, + GimpContextPropType prop) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + + return (context->defined_props & (1 << prop)) ? TRUE : FALSE; +} + +void +gimp_context_define_properties (GimpContext *context, + GimpContextPropMask prop_mask, + gboolean defined) +{ + GimpContextPropType prop; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + for (prop = GIMP_CONTEXT_PROP_FIRST; prop <= GIMP_CONTEXT_PROP_LAST; prop++) + if ((1 << prop) & prop_mask) + gimp_context_define_property (context, prop, defined); +} + + +/* specify which context properties will be serialized */ + +void +gimp_context_set_serialize_properties (GimpContext *context, + GimpContextPropMask props_mask) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + context->serialize_props = props_mask; +} + +GimpContextPropMask +gimp_context_get_serialize_properties (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), 0); + + return context->serialize_props; +} + + +/* copying context properties */ + +void +gimp_context_copy_property (GimpContext *src, + GimpContext *dest, + GimpContextPropType prop) +{ + g_return_if_fail (GIMP_IS_CONTEXT (src)); + g_return_if_fail (GIMP_IS_CONTEXT (dest)); + g_return_if_fail ((prop >= GIMP_CONTEXT_PROP_FIRST) && + (prop <= GIMP_CONTEXT_PROP_LAST)); + + switch (prop) + { + case GIMP_CONTEXT_PROP_IMAGE: + gimp_context_real_set_image (dest, src->image); + break; + + case GIMP_CONTEXT_PROP_DISPLAY: + gimp_context_real_set_display (dest, src->display); + break; + + case GIMP_CONTEXT_PROP_TOOL: + gimp_context_real_set_tool (dest, src->tool_info); + COPY_NAME (src, dest, tool_name); + break; + + case GIMP_CONTEXT_PROP_PAINT_INFO: + gimp_context_real_set_paint_info (dest, src->paint_info); + COPY_NAME (src, dest, paint_name); + break; + + case GIMP_CONTEXT_PROP_FOREGROUND: + gimp_context_real_set_foreground (dest, &src->foreground); + break; + + case GIMP_CONTEXT_PROP_BACKGROUND: + gimp_context_real_set_background (dest, &src->background); + break; + + case GIMP_CONTEXT_PROP_OPACITY: + gimp_context_real_set_opacity (dest, src->opacity); + break; + + case GIMP_CONTEXT_PROP_PAINT_MODE: + gimp_context_real_set_paint_mode (dest, src->paint_mode); + break; + + case GIMP_CONTEXT_PROP_BRUSH: + gimp_context_real_set_brush (dest, src->brush); + COPY_NAME (src, dest, brush_name); + break; + + case GIMP_CONTEXT_PROP_DYNAMICS: + gimp_context_real_set_dynamics (dest, src->dynamics); + COPY_NAME (src, dest, dynamics_name); + break; + + case GIMP_CONTEXT_PROP_MYBRUSH: + gimp_context_real_set_mybrush (dest, src->mybrush); + COPY_NAME (src, dest, mybrush_name); + break; + + case GIMP_CONTEXT_PROP_PATTERN: + gimp_context_real_set_pattern (dest, src->pattern); + COPY_NAME (src, dest, pattern_name); + break; + + case GIMP_CONTEXT_PROP_GRADIENT: + gimp_context_real_set_gradient (dest, src->gradient); + COPY_NAME (src, dest, gradient_name); + break; + + case GIMP_CONTEXT_PROP_PALETTE: + gimp_context_real_set_palette (dest, src->palette); + COPY_NAME (src, dest, palette_name); + break; + + case GIMP_CONTEXT_PROP_FONT: + gimp_context_real_set_font (dest, src->font); + COPY_NAME (src, dest, font_name); + break; + + case GIMP_CONTEXT_PROP_TOOL_PRESET: + gimp_context_real_set_tool_preset (dest, src->tool_preset); + COPY_NAME (src, dest, tool_preset_name); + break; + + case GIMP_CONTEXT_PROP_BUFFER: + gimp_context_real_set_buffer (dest, src->buffer); + COPY_NAME (src, dest, buffer_name); + break; + + case GIMP_CONTEXT_PROP_IMAGEFILE: + gimp_context_real_set_imagefile (dest, src->imagefile); + COPY_NAME (src, dest, imagefile_name); + break; + + case GIMP_CONTEXT_PROP_TEMPLATE: + gimp_context_real_set_template (dest, src->template); + COPY_NAME (src, dest, template_name); + break; + + default: + break; + } +} + +void +gimp_context_copy_properties (GimpContext *src, + GimpContext *dest, + GimpContextPropMask prop_mask) +{ + GimpContextPropType prop; + + g_return_if_fail (GIMP_IS_CONTEXT (src)); + g_return_if_fail (GIMP_IS_CONTEXT (dest)); + + for (prop = GIMP_CONTEXT_PROP_FIRST; prop <= GIMP_CONTEXT_PROP_LAST; prop++) + if ((1 << prop) & prop_mask) + gimp_context_copy_property (src, dest, prop); +} + + +/* attribute access functions */ + +/*****************************************************************************/ +/* manipulate by GType *****************************************************/ + +GimpContextPropType +gimp_context_type_to_property (GType type) +{ + GimpContextPropType prop; + + for (prop = GIMP_CONTEXT_PROP_FIRST; prop <= GIMP_CONTEXT_PROP_LAST; prop++) + { + if (g_type_is_a (type, gimp_context_prop_types[prop])) + return prop; + } + + return -1; +} + +const gchar * +gimp_context_type_to_prop_name (GType type) +{ + GimpContextPropType prop; + + for (prop = GIMP_CONTEXT_PROP_FIRST; prop <= GIMP_CONTEXT_PROP_LAST; prop++) + { + if (g_type_is_a (type, gimp_context_prop_types[prop])) + return gimp_context_prop_names[prop]; + } + + return NULL; +} + +const gchar * +gimp_context_type_to_signal_name (GType type) +{ + GimpContextPropType prop; + + for (prop = GIMP_CONTEXT_PROP_FIRST; prop <= GIMP_CONTEXT_PROP_LAST; prop++) + { + if (g_type_is_a (type, gimp_context_prop_types[prop])) + return g_signal_name (gimp_context_signals[prop]); + } + + return NULL; +} + +GimpObject * +gimp_context_get_by_type (GimpContext *context, + GType type) +{ + GimpContextPropType prop; + GimpObject *object = NULL; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + prop = gimp_context_type_to_property (type); + + g_return_val_if_fail (prop != -1, NULL); + + g_object_get (context, + gimp_context_prop_names[prop], &object, + NULL); + + /* g_object_get() refs the object, this function however is a getter, + * which usually doesn't ref it's return value + */ + if (object) + g_object_unref (object); + + return object; +} + +void +gimp_context_set_by_type (GimpContext *context, + GType type, + GimpObject *object) +{ + GimpContextPropType prop; + GParamSpec *pspec; + GValue value = G_VALUE_INIT; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (object == NULL || G_IS_OBJECT (object)); + + prop = gimp_context_type_to_property (type); + + g_return_if_fail (prop != -1); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (context), + gimp_context_prop_names[prop]); + + g_return_if_fail (pspec != NULL); + + g_value_init (&value, pspec->value_type); + g_value_set_object (&value, object); + + /* we use gimp_context_set_property() (which in turn only calls + * gimp_context_set_foo() functions) instead of the much more obvious + * g_object_set(); this avoids g_object_freeze_notify()/thaw_notify() + * around the g_object_set() and makes GimpContext callbacks being + * called in a much more predictable order. See bug #731279. + */ + gimp_context_set_property (G_OBJECT (context), + pspec->param_id, + (const GValue *) &value, + pspec); + + g_value_unset (&value); +} + +void +gimp_context_changed_by_type (GimpContext *context, + GType type) +{ + GimpContextPropType prop; + GimpObject *object; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + prop = gimp_context_type_to_property (type); + + g_return_if_fail (prop != -1); + + object = gimp_context_get_by_type (context, type); + + g_signal_emit (context, + gimp_context_signals[prop], 0, + object); +} + + +/*****************************************************************************/ +/* image *******************************************************************/ + +GimpImage * +gimp_context_get_image (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->image; +} + +void +gimp_context_set_image (GimpContext *context, + GimpImage *image) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + + context_find_defined (context, GIMP_CONTEXT_PROP_IMAGE); + + gimp_context_real_set_image (context, image); +} + +void +gimp_context_image_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[IMAGE_CHANGED], 0, + context->image); +} + +static void +gimp_context_image_removed (GimpContainer *container, + GimpImage *image, + GimpContext *context) +{ + if (context->image == image) + gimp_context_real_set_image (context, NULL); +} + +static void +gimp_context_real_set_image (GimpContext *context, + GimpImage *image) +{ + if (context->image == image) + return; + + context->image = image; + + g_object_notify (G_OBJECT (context), "image"); + gimp_context_image_changed (context); +} + + +/*****************************************************************************/ +/* display *****************************************************************/ + +gpointer +gimp_context_get_display (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->display; +} + +void +gimp_context_set_display (GimpContext *context, + gpointer display) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (display == NULL || GIMP_IS_OBJECT (display)); + + context_find_defined (context, GIMP_CONTEXT_PROP_DISPLAY); + + gimp_context_real_set_display (context, display); +} + +void +gimp_context_display_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[DISPLAY_CHANGED], 0, + context->display); +} + +static void +gimp_context_display_removed (GimpContainer *container, + gpointer display, + GimpContext *context) +{ + if (context->display == display) + gimp_context_real_set_display (context, NULL); +} + +static void +gimp_context_real_set_display (GimpContext *context, + gpointer display) +{ + GimpObject *old_display; + + if (context->display == display) + { + /* make sure that setting a display *always* sets the image + * to that display's image, even if the display already + * matches + */ + if (display) + { + GimpImage *image; + + g_object_get (display, "image", &image, NULL); + + gimp_context_real_set_image (context, image); + + if (image) + g_object_unref (image); + } + + return; + } + + old_display = context->display; + + context->display = display; + + if (context->display) + { + GimpImage *image; + + g_object_get (display, "image", &image, NULL); + + gimp_context_real_set_image (context, image); + + if (image) + g_object_unref (image); + } + else if (old_display) + { + gimp_context_real_set_image (context, NULL); + } + + g_object_notify (G_OBJECT (context), "display"); + gimp_context_display_changed (context); +} + + +/*****************************************************************************/ +/* tool ********************************************************************/ + +GimpToolInfo * +gimp_context_get_tool (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->tool_info; +} + +void +gimp_context_set_tool (GimpContext *context, + GimpToolInfo *tool_info) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (tool_info == NULL || GIMP_IS_TOOL_INFO (tool_info)); + + context_find_defined (context, GIMP_CONTEXT_PROP_TOOL); + + gimp_context_real_set_tool (context, tool_info); +} + +void +gimp_context_tool_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[TOOL_CHANGED], 0, + context->tool_info); +} + +static void +gimp_context_tool_dirty (GimpToolInfo *tool_info, + GimpContext *context) +{ + g_free (context->tool_name); + context->tool_name = g_strdup (gimp_object_get_name (tool_info)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_TOOL); +} + +static void +gimp_context_tool_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpToolInfo *tool_info; + + if (! context->tool_name) + context->tool_name = g_strdup ("gimp-paintbrush-tool"); + + tool_info = gimp_context_find_object (context, container, + context->tool_name, + gimp_tool_info_get_standard (context->gimp)); + + gimp_context_real_set_tool (context, tool_info); +} + +static void +gimp_context_tool_removed (GimpContainer *container, + GimpToolInfo *tool_info, + GimpContext *context) +{ + if (tool_info == context->tool_info) + { + g_signal_handlers_disconnect_by_func (context->tool_info, + gimp_context_tool_dirty, + context); + g_clear_object (&context->tool_info); + + if (! gimp_container_frozen (container)) + gimp_context_tool_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_tool (GimpContext *context, + GimpToolInfo *tool_info) +{ + if (context->tool_info == tool_info) + return; + + if (context->tool_name && + tool_info != gimp_tool_info_get_standard (context->gimp)) + { + g_clear_pointer (&context->tool_name, g_free); + } + + if (context->tool_info) + g_signal_handlers_disconnect_by_func (context->tool_info, + gimp_context_tool_dirty, + context); + + g_set_object (&context->tool_info, tool_info); + + if (tool_info) + { + g_signal_connect_object (tool_info, "name-changed", + G_CALLBACK (gimp_context_tool_dirty), + context, + 0); + + if (tool_info != gimp_tool_info_get_standard (context->gimp)) + context->tool_name = g_strdup (gimp_object_get_name (tool_info)); + + if (tool_info->paint_info) + gimp_context_real_set_paint_info (context, tool_info->paint_info); + } + + g_object_notify (G_OBJECT (context), "tool"); + gimp_context_tool_changed (context); +} + + +/*****************************************************************************/ +/* paint info **************************************************************/ + +GimpPaintInfo * +gimp_context_get_paint_info (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->paint_info; +} + +void +gimp_context_set_paint_info (GimpContext *context, + GimpPaintInfo *paint_info) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (paint_info == NULL || GIMP_IS_PAINT_INFO (paint_info)); + + context_find_defined (context, GIMP_CONTEXT_PROP_PAINT_INFO); + + gimp_context_real_set_paint_info (context, paint_info); +} + +void +gimp_context_paint_info_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[PAINT_INFO_CHANGED], 0, + context->paint_info); +} + +static void +gimp_context_paint_info_dirty (GimpPaintInfo *paint_info, + GimpContext *context) +{ + g_free (context->paint_name); + context->paint_name = g_strdup (gimp_object_get_name (paint_info)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_PAINT_INFO); +} + +/* the global paint info list is there again after refresh */ +static void +gimp_context_paint_info_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpPaintInfo *paint_info; + + if (! context->paint_name) + context->paint_name = g_strdup ("gimp-paintbrush"); + + paint_info = gimp_context_find_object (context, container, + context->paint_name, + gimp_paint_info_get_standard (context->gimp)); + + gimp_context_real_set_paint_info (context, paint_info); +} + +static void +gimp_context_paint_info_removed (GimpContainer *container, + GimpPaintInfo *paint_info, + GimpContext *context) +{ + if (paint_info == context->paint_info) + { + g_signal_handlers_disconnect_by_func (context->paint_info, + gimp_context_paint_info_dirty, + context); + g_clear_object (&context->paint_info); + + if (! gimp_container_frozen (container)) + gimp_context_paint_info_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_paint_info (GimpContext *context, + GimpPaintInfo *paint_info) +{ + if (context->paint_info == paint_info) + return; + + if (context->paint_name && + paint_info != gimp_paint_info_get_standard (context->gimp)) + { + g_clear_pointer (&context->paint_name, g_free); + } + + if (context->paint_info) + g_signal_handlers_disconnect_by_func (context->paint_info, + gimp_context_paint_info_dirty, + context); + + g_set_object (&context->paint_info, paint_info); + + if (paint_info) + { + g_signal_connect_object (paint_info, "name-changed", + G_CALLBACK (gimp_context_paint_info_dirty), + context, + 0); + + if (paint_info != gimp_paint_info_get_standard (context->gimp)) + context->paint_name = g_strdup (gimp_object_get_name (paint_info)); + } + + g_object_notify (G_OBJECT (context), "paint-info"); + gimp_context_paint_info_changed (context); +} + + +/*****************************************************************************/ +/* foreground color ********************************************************/ + +void +gimp_context_get_foreground (GimpContext *context, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (color != NULL); + + *color = context->foreground; +} + +void +gimp_context_set_foreground (GimpContext *context, + const GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (color != NULL); + + context_find_defined (context, GIMP_CONTEXT_PROP_FOREGROUND); + + gimp_context_real_set_foreground (context, color); +} + +void +gimp_context_foreground_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[FOREGROUND_CHANGED], 0, + &context->foreground); +} + +static void +gimp_context_real_set_foreground (GimpContext *context, + const GimpRGB *color) +{ + if (gimp_rgba_distance (&context->foreground, color) < RGBA_EPSILON) + return; + + context->foreground = *color; + gimp_rgb_set_alpha (&context->foreground, GIMP_OPACITY_OPAQUE); + + g_object_notify (G_OBJECT (context), "foreground"); + gimp_context_foreground_changed (context); +} + + +/*****************************************************************************/ +/* background color ********************************************************/ + +void +gimp_context_get_background (GimpContext *context, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_return_if_fail (color != NULL); + + *color = context->background; +} + +void +gimp_context_set_background (GimpContext *context, + const GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (color != NULL); + + context_find_defined (context, GIMP_CONTEXT_PROP_BACKGROUND); + + gimp_context_real_set_background (context, color); +} + +void +gimp_context_background_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[BACKGROUND_CHANGED], 0, + &context->background); +} + +static void +gimp_context_real_set_background (GimpContext *context, + const GimpRGB *color) +{ + if (gimp_rgba_distance (&context->background, color) < RGBA_EPSILON) + return; + + context->background = *color; + gimp_rgb_set_alpha (&context->background, GIMP_OPACITY_OPAQUE); + + g_object_notify (G_OBJECT (context), "background"); + gimp_context_background_changed (context); +} + + +/*****************************************************************************/ +/* color utility functions *************************************************/ + +void +gimp_context_set_default_colors (GimpContext *context) +{ + GimpContext *bg_context; + GimpRGB fg; + GimpRGB bg; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + bg_context = context; + + context_find_defined (context, GIMP_CONTEXT_PROP_FOREGROUND); + context_find_defined (bg_context, GIMP_CONTEXT_PROP_BACKGROUND); + + gimp_rgba_set (&fg, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); + gimp_rgba_set (&bg, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + + gimp_context_real_set_foreground (context, &fg); + gimp_context_real_set_background (bg_context, &bg); +} + +void +gimp_context_swap_colors (GimpContext *context) +{ + GimpContext *bg_context; + GimpRGB fg; + GimpRGB bg; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + bg_context = context; + + context_find_defined (context, GIMP_CONTEXT_PROP_FOREGROUND); + context_find_defined (bg_context, GIMP_CONTEXT_PROP_BACKGROUND); + + gimp_context_get_foreground (context, &fg); + gimp_context_get_background (bg_context, &bg); + + gimp_context_real_set_foreground (context, &bg); + gimp_context_real_set_background (bg_context, &fg); +} + + +/*****************************************************************************/ +/* opacity *****************************************************************/ + +gdouble +gimp_context_get_opacity (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), GIMP_OPACITY_OPAQUE); + + return context->opacity; +} + +void +gimp_context_set_opacity (GimpContext *context, + gdouble opacity) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + context_find_defined (context, GIMP_CONTEXT_PROP_OPACITY); + + gimp_context_real_set_opacity (context, opacity); +} + +void +gimp_context_opacity_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[OPACITY_CHANGED], 0, + context->opacity); +} + +static void +gimp_context_real_set_opacity (GimpContext *context, + gdouble opacity) +{ + if (context->opacity == opacity) + return; + + context->opacity = opacity; + + g_object_notify (G_OBJECT (context), "opacity"); + gimp_context_opacity_changed (context); +} + + +/*****************************************************************************/ +/* paint mode **************************************************************/ + +GimpLayerMode +gimp_context_get_paint_mode (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), GIMP_LAYER_MODE_NORMAL); + + return context->paint_mode; +} + +void +gimp_context_set_paint_mode (GimpContext *context, + GimpLayerMode paint_mode) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + context_find_defined (context, GIMP_CONTEXT_PROP_PAINT_MODE); + + gimp_context_real_set_paint_mode (context, paint_mode); +} + +void +gimp_context_paint_mode_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[PAINT_MODE_CHANGED], 0, + context->paint_mode); +} + +static void +gimp_context_real_set_paint_mode (GimpContext *context, + GimpLayerMode paint_mode) +{ + if (context->paint_mode == paint_mode) + return; + + context->paint_mode = paint_mode; + + g_object_notify (G_OBJECT (context), "paint-mode"); + gimp_context_paint_mode_changed (context); +} + + +/*****************************************************************************/ +/* brush *******************************************************************/ + +GimpBrush * +gimp_context_get_brush (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->brush; +} + +void +gimp_context_set_brush (GimpContext *context, + GimpBrush *brush) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush)); + + context_find_defined (context, GIMP_CONTEXT_PROP_BRUSH); + + gimp_context_real_set_brush (context, brush); +} + +void +gimp_context_brush_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[BRUSH_CHANGED], 0, + context->brush); +} + +static void +gimp_context_brush_dirty (GimpBrush *brush, + GimpContext *context) +{ + g_free (context->brush_name); + context->brush_name = g_strdup (gimp_object_get_name (brush)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_BRUSH); +} + +static void +gimp_context_brush_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpBrush *brush; + + if (! context->brush_name) + context->brush_name = g_strdup (context->gimp->config->default_brush); + + brush = gimp_context_find_object (context, container, + context->brush_name, + gimp_brush_get_standard (context)); + + gimp_context_real_set_brush (context, brush); +} + +/* the active brush disappeared */ +static void +gimp_context_brush_removed (GimpContainer *container, + GimpBrush *brush, + GimpContext *context) +{ + if (brush == context->brush) + { + g_signal_handlers_disconnect_by_func (context->brush, + gimp_context_brush_dirty, + context); + g_clear_object (&context->brush); + + if (! gimp_container_frozen (container)) + gimp_context_brush_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_brush (GimpContext *context, + GimpBrush *brush) +{ + if (context->brush == brush) + return; + + if (context->brush_name && + brush != GIMP_BRUSH (gimp_brush_get_standard (context))) + { + g_clear_pointer (&context->brush_name, g_free); + } + + if (context->brush) + g_signal_handlers_disconnect_by_func (context->brush, + gimp_context_brush_dirty, + context); + + g_set_object (&context->brush, brush); + + if (brush) + { + g_signal_connect_object (brush, "name-changed", + G_CALLBACK (gimp_context_brush_dirty), + context, + 0); + + if (brush != GIMP_BRUSH (gimp_brush_get_standard (context))) + context->brush_name = g_strdup (gimp_object_get_name (brush)); + } + + g_object_notify (G_OBJECT (context), "brush"); + gimp_context_brush_changed (context); +} + + +/*****************************************************************************/ +/* dynamics *****************************************************************/ + +GimpDynamics * +gimp_context_get_dynamics (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->dynamics; +} + +void +gimp_context_set_dynamics (GimpContext *context, + GimpDynamics *dynamics) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (dynamics == NULL || GIMP_IS_DYNAMICS (dynamics)); + + context_find_defined (context, GIMP_CONTEXT_PROP_DYNAMICS); + + gimp_context_real_set_dynamics (context, dynamics); +} + +void +gimp_context_dynamics_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[DYNAMICS_CHANGED], 0, + context->dynamics); +} + +static void +gimp_context_dynamics_dirty (GimpDynamics *dynamics, + GimpContext *context) +{ + g_free (context->dynamics_name); + context->dynamics_name = g_strdup (gimp_object_get_name (dynamics)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_DYNAMICS); +} + +static void +gimp_context_dynamics_removed (GimpContainer *container, + GimpDynamics *dynamics, + GimpContext *context) +{ + if (dynamics == context->dynamics) + { + g_signal_handlers_disconnect_by_func (context->dynamics, + gimp_context_dynamics_dirty, + context); + g_clear_object (&context->dynamics); + + if (! gimp_container_frozen (container)) + gimp_context_dynamics_list_thaw (container, context); + } +} + +static void +gimp_context_dynamics_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpDynamics *dynamics; + + if (! context->dynamics_name) + context->dynamics_name = g_strdup (context->gimp->config->default_dynamics); + + dynamics = gimp_context_find_object (context, container, + context->dynamics_name, + gimp_dynamics_get_standard (context)); + + gimp_context_real_set_dynamics (context, dynamics); +} + +static void +gimp_context_real_set_dynamics (GimpContext *context, + GimpDynamics *dynamics) +{ + if (context->dynamics == dynamics) + return; + + if (context->dynamics_name && + dynamics != GIMP_DYNAMICS (gimp_dynamics_get_standard (context))) + { + g_clear_pointer (&context->dynamics_name, g_free); + } + + if (context->dynamics) + g_signal_handlers_disconnect_by_func (context->dynamics, + gimp_context_dynamics_dirty, + context); + + g_set_object (&context->dynamics, dynamics); + + if (dynamics) + { + g_signal_connect_object (dynamics, "name-changed", + G_CALLBACK (gimp_context_dynamics_dirty), + context, + 0); + + if (dynamics != GIMP_DYNAMICS (gimp_dynamics_get_standard (context))) + context->dynamics_name = g_strdup (gimp_object_get_name (dynamics)); + } + + g_object_notify (G_OBJECT (context), "dynamics"); + gimp_context_dynamics_changed (context); +} + + +/*****************************************************************************/ +/* mybrush *****************************************************************/ + +GimpMybrush * +gimp_context_get_mybrush (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->mybrush; +} + +void +gimp_context_set_mybrush (GimpContext *context, + GimpMybrush *brush) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (brush == NULL || GIMP_IS_MYBRUSH (brush)); + + context_find_defined (context, GIMP_CONTEXT_PROP_MYBRUSH); + + gimp_context_real_set_mybrush (context, brush); +} + +void +gimp_context_mybrush_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[MYBRUSH_CHANGED], 0, + context->mybrush); +} + +static void +gimp_context_mybrush_dirty (GimpMybrush *brush, + GimpContext *context) +{ + g_free (context->mybrush_name); + context->mybrush_name = g_strdup (gimp_object_get_name (brush)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_MYBRUSH); +} + +static void +gimp_context_mybrush_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpMybrush *brush; + + if (! context->mybrush_name) + context->mybrush_name = g_strdup (context->gimp->config->default_mypaint_brush); + + brush = gimp_context_find_object (context, container, + context->mybrush_name, + gimp_mybrush_get_standard (context)); + + gimp_context_real_set_mybrush (context, brush); +} + +static void +gimp_context_mybrush_removed (GimpContainer *container, + GimpMybrush *brush, + GimpContext *context) +{ + if (brush == context->mybrush) + { + g_signal_handlers_disconnect_by_func (context->mybrush, + gimp_context_mybrush_dirty, + context); + g_clear_object (&context->mybrush); + + if (! gimp_container_frozen (container)) + gimp_context_mybrush_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_mybrush (GimpContext *context, + GimpMybrush *brush) +{ + if (context->mybrush == brush) + return; + + if (context->mybrush_name && + brush != GIMP_MYBRUSH (gimp_mybrush_get_standard (context))) + { + g_clear_pointer (&context->mybrush_name, g_free); + } + + if (context->mybrush) + g_signal_handlers_disconnect_by_func (context->mybrush, + gimp_context_mybrush_dirty, + context); + + g_set_object (&context->mybrush, brush); + + if (brush) + { + g_signal_connect_object (brush, "name-changed", + G_CALLBACK (gimp_context_mybrush_dirty), + context, + 0); + + if (brush != GIMP_MYBRUSH (gimp_mybrush_get_standard (context))) + context->mybrush_name = g_strdup (gimp_object_get_name (brush)); + } + + g_object_notify (G_OBJECT (context), "mybrush"); + gimp_context_mybrush_changed (context); +} + + +/*****************************************************************************/ +/* pattern *****************************************************************/ + +GimpPattern * +gimp_context_get_pattern (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->pattern; +} + +void +gimp_context_set_pattern (GimpContext *context, + GimpPattern *pattern) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (pattern == NULL || GIMP_IS_PATTERN (pattern)); + + context_find_defined (context, GIMP_CONTEXT_PROP_PATTERN); + + gimp_context_real_set_pattern (context, pattern); +} + +void +gimp_context_pattern_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[PATTERN_CHANGED], 0, + context->pattern); +} + +static void +gimp_context_pattern_dirty (GimpPattern *pattern, + GimpContext *context) +{ + g_free (context->pattern_name); + context->pattern_name = g_strdup (gimp_object_get_name (pattern)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_PATTERN); +} + +static void +gimp_context_pattern_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpPattern *pattern; + + if (! context->pattern_name) + context->pattern_name = g_strdup (context->gimp->config->default_pattern); + + pattern = gimp_context_find_object (context, container, + context->pattern_name, + gimp_pattern_get_standard (context)); + + gimp_context_real_set_pattern (context, pattern); +} + +static void +gimp_context_pattern_removed (GimpContainer *container, + GimpPattern *pattern, + GimpContext *context) +{ + if (pattern == context->pattern) + { + g_signal_handlers_disconnect_by_func (context->pattern, + gimp_context_pattern_dirty, + context); + g_clear_object (&context->pattern); + + if (! gimp_container_frozen (container)) + gimp_context_pattern_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_pattern (GimpContext *context, + GimpPattern *pattern) +{ + if (context->pattern == pattern) + return; + + if (context->pattern_name && + pattern != GIMP_PATTERN (gimp_pattern_get_standard (context))) + { + g_clear_pointer (&context->pattern_name, g_free); + } + + if (context->pattern) + g_signal_handlers_disconnect_by_func (context->pattern, + gimp_context_pattern_dirty, + context); + + g_set_object (&context->pattern, pattern); + + if (pattern) + { + g_signal_connect_object (pattern, "name-changed", + G_CALLBACK (gimp_context_pattern_dirty), + context, + 0); + + if (pattern != GIMP_PATTERN (gimp_pattern_get_standard (context))) + context->pattern_name = g_strdup (gimp_object_get_name (pattern)); + } + + g_object_notify (G_OBJECT (context), "pattern"); + gimp_context_pattern_changed (context); +} + + +/*****************************************************************************/ +/* gradient ****************************************************************/ + +GimpGradient * +gimp_context_get_gradient (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->gradient; +} + +void +gimp_context_set_gradient (GimpContext *context, + GimpGradient *gradient) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (gradient == NULL || GIMP_IS_GRADIENT (gradient)); + + context_find_defined (context, GIMP_CONTEXT_PROP_GRADIENT); + + gimp_context_real_set_gradient (context, gradient); +} + +void +gimp_context_gradient_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[GRADIENT_CHANGED], 0, + context->gradient); +} + +static void +gimp_context_gradient_dirty (GimpGradient *gradient, + GimpContext *context) +{ + g_free (context->gradient_name); + context->gradient_name = g_strdup (gimp_object_get_name (gradient)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_GRADIENT); +} + +static void +gimp_context_gradient_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpGradient *gradient; + + if (! context->gradient_name) + context->gradient_name = g_strdup (context->gimp->config->default_gradient); + + gradient = gimp_context_find_object (context, container, + context->gradient_name, + gimp_gradient_get_standard (context)); + + gimp_context_real_set_gradient (context, gradient); +} + +static void +gimp_context_gradient_removed (GimpContainer *container, + GimpGradient *gradient, + GimpContext *context) +{ + if (gradient == context->gradient) + { + g_signal_handlers_disconnect_by_func (context->gradient, + gimp_context_gradient_dirty, + context); + g_clear_object (&context->gradient); + + if (! gimp_container_frozen (container)) + gimp_context_gradient_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_gradient (GimpContext *context, + GimpGradient *gradient) +{ + if (context->gradient == gradient) + return; + + if (context->gradient_name && + gradient != GIMP_GRADIENT (gimp_gradient_get_standard (context))) + { + g_clear_pointer (&context->gradient_name, g_free); + } + + if (context->gradient) + g_signal_handlers_disconnect_by_func (context->gradient, + gimp_context_gradient_dirty, + context); + + g_set_object (&context->gradient, gradient); + + if (gradient) + { + g_signal_connect_object (gradient, "name-changed", + G_CALLBACK (gimp_context_gradient_dirty), + context, + 0); + + if (gradient != GIMP_GRADIENT (gimp_gradient_get_standard (context))) + context->gradient_name = g_strdup (gimp_object_get_name (gradient)); + } + + g_object_notify (G_OBJECT (context), "gradient"); + gimp_context_gradient_changed (context); +} + + +/*****************************************************************************/ +/* palette *****************************************************************/ + +GimpPalette * +gimp_context_get_palette (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->palette; +} + +void +gimp_context_set_palette (GimpContext *context, + GimpPalette *palette) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (palette == NULL || GIMP_IS_PALETTE (palette)); + + context_find_defined (context, GIMP_CONTEXT_PROP_PALETTE); + + gimp_context_real_set_palette (context, palette); +} + +void +gimp_context_palette_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[PALETTE_CHANGED], 0, + context->palette); +} + +static void +gimp_context_palette_dirty (GimpPalette *palette, + GimpContext *context) +{ + g_free (context->palette_name); + context->palette_name = g_strdup (gimp_object_get_name (palette)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_PALETTE); +} + +static void +gimp_context_palette_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpPalette *palette; + + if (! context->palette_name) + context->palette_name = g_strdup (context->gimp->config->default_palette); + + palette = gimp_context_find_object (context, container, + context->palette_name, + gimp_palette_get_standard (context)); + + gimp_context_real_set_palette (context, palette); +} + +static void +gimp_context_palette_removed (GimpContainer *container, + GimpPalette *palette, + GimpContext *context) +{ + if (palette == context->palette) + { + g_signal_handlers_disconnect_by_func (context->palette, + gimp_context_palette_dirty, + context); + g_clear_object (&context->palette); + + if (! gimp_container_frozen (container)) + gimp_context_palette_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_palette (GimpContext *context, + GimpPalette *palette) +{ + if (context->palette == palette) + return; + + if (context->palette_name && + palette != GIMP_PALETTE (gimp_palette_get_standard (context))) + { + g_clear_pointer (&context->palette_name, g_free); + } + + if (context->palette) + g_signal_handlers_disconnect_by_func (context->palette, + gimp_context_palette_dirty, + context); + + g_set_object (&context->palette, palette); + + if (palette) + { + g_signal_connect_object (palette, "name-changed", + G_CALLBACK (gimp_context_palette_dirty), + context, + 0); + + if (palette != GIMP_PALETTE (gimp_palette_get_standard (context))) + context->palette_name = g_strdup (gimp_object_get_name (palette)); + } + + g_object_notify (G_OBJECT (context), "palette"); + gimp_context_palette_changed (context); +} + + +/*****************************************************************************/ +/* font *****************************************************************/ + +GimpFont * +gimp_context_get_font (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->font; +} + +void +gimp_context_set_font (GimpContext *context, + GimpFont *font) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (font == NULL || GIMP_IS_FONT (font)); + + context_find_defined (context, GIMP_CONTEXT_PROP_FONT); + + gimp_context_real_set_font (context, font); +} + +const gchar * +gimp_context_get_font_name (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->font_name; +} + +void +gimp_context_set_font_name (GimpContext *context, + const gchar *name) +{ + GimpContainer *container; + GimpObject *font; + + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + container = gimp_data_factory_get_container (context->gimp->font_factory); + font = gimp_container_get_child_by_name (container, name); + + if (font) + { + gimp_context_set_font (context, GIMP_FONT (font)); + } + else + { + /* No font with this name exists, use the standard font, but + * keep the intended name around + */ + gimp_context_set_font (context, GIMP_FONT (gimp_font_get_standard ())); + + g_free (context->font_name); + context->font_name = g_strdup (name); + } +} + +void +gimp_context_font_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[FONT_CHANGED], 0, + context->font); +} + +static void +gimp_context_font_dirty (GimpFont *font, + GimpContext *context) +{ + g_free (context->font_name); + context->font_name = g_strdup (gimp_object_get_name (font)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_FONT); +} + +static void +gimp_context_font_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpFont *font; + + if (! context->font_name) + context->font_name = g_strdup (context->gimp->config->default_font); + + font = gimp_context_find_object (context, container, + context->font_name, + gimp_font_get_standard ()); + + gimp_context_real_set_font (context, font); +} + +static void +gimp_context_font_removed (GimpContainer *container, + GimpFont *font, + GimpContext *context) +{ + if (font == context->font) + { + g_signal_handlers_disconnect_by_func (context->font, + gimp_context_font_dirty, + context); + g_clear_object (&context->font); + + if (! gimp_container_frozen (container)) + gimp_context_font_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_font (GimpContext *context, + GimpFont *font) +{ + if (context->font == font) + return; + + if (context->font_name && + font != GIMP_FONT (gimp_font_get_standard ())) + { + g_clear_pointer (&context->font_name, g_free); + } + + if (context->font) + g_signal_handlers_disconnect_by_func (context->font, + gimp_context_font_dirty, + context); + + g_set_object (&context->font, font); + + if (font) + { + g_signal_connect_object (font, "name-changed", + G_CALLBACK (gimp_context_font_dirty), + context, + 0); + + if (font != GIMP_FONT (gimp_font_get_standard ())) + context->font_name = g_strdup (gimp_object_get_name (font)); + } + + g_object_notify (G_OBJECT (context), "font"); + gimp_context_font_changed (context); +} + + +/********************************************************************************/ +/* tool preset *****************************************************************/ + +GimpToolPreset * +gimp_context_get_tool_preset (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->tool_preset; +} + +void +gimp_context_set_tool_preset (GimpContext *context, + GimpToolPreset *tool_preset) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (tool_preset == NULL || GIMP_IS_TOOL_PRESET (tool_preset)); + + context_find_defined (context, GIMP_CONTEXT_PROP_TOOL_PRESET); + + gimp_context_real_set_tool_preset (context, tool_preset); +} + +void +gimp_context_tool_preset_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[TOOL_PRESET_CHANGED], 0, + context->tool_preset); +} + +static void +gimp_context_tool_preset_dirty (GimpToolPreset *tool_preset, + GimpContext *context) +{ + g_free (context->tool_preset_name); + context->tool_preset_name = g_strdup (gimp_object_get_name (tool_preset)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_TOOL_PRESET); +} + +static void +gimp_context_tool_preset_removed (GimpContainer *container, + GimpToolPreset *tool_preset, + GimpContext *context) +{ + if (tool_preset == context->tool_preset) + { + g_signal_handlers_disconnect_by_func (context->tool_preset, + gimp_context_tool_preset_dirty, + context); + g_clear_object (&context->tool_preset); + + if (! gimp_container_frozen (container)) + gimp_context_tool_preset_list_thaw (container, context); + } +} + +static void +gimp_context_tool_preset_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpToolPreset *tool_preset; + + tool_preset = gimp_context_find_object (context, container, + context->tool_preset_name, + NULL); + + gimp_context_real_set_tool_preset (context, tool_preset); +} + +static void +gimp_context_real_set_tool_preset (GimpContext *context, + GimpToolPreset *tool_preset) +{ + if (context->tool_preset == tool_preset) + return; + + if (context->tool_preset_name) + { + g_clear_pointer (&context->tool_preset_name, g_free); + } + + if (context->tool_preset) + g_signal_handlers_disconnect_by_func (context->tool_preset, + gimp_context_tool_preset_dirty, + context); + + g_set_object (&context->tool_preset, tool_preset); + + if (tool_preset) + { + g_signal_connect_object (tool_preset, "name-changed", + G_CALLBACK (gimp_context_tool_preset_dirty), + context, + 0); + + context->tool_preset_name = g_strdup (gimp_object_get_name (tool_preset)); + } + + g_object_notify (G_OBJECT (context), "tool-preset"); + gimp_context_tool_preset_changed (context); +} + + +/*****************************************************************************/ +/* buffer ******************************************************************/ + +GimpBuffer * +gimp_context_get_buffer (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->buffer; +} + +void +gimp_context_set_buffer (GimpContext *context, + GimpBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (buffer == NULL || GIMP_IS_BUFFER (buffer)); + + context_find_defined (context, GIMP_CONTEXT_PROP_BUFFER); + + gimp_context_real_set_buffer (context, buffer); +} + +void +gimp_context_buffer_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[BUFFER_CHANGED], 0, + context->buffer); +} + +static void +gimp_context_buffer_dirty (GimpBuffer *buffer, + GimpContext *context) +{ + g_free (context->buffer_name); + context->buffer_name = g_strdup (gimp_object_get_name (buffer)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_BUFFER); +} + +static void +gimp_context_buffer_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpBuffer *buffer; + + buffer = gimp_context_find_object (context, container, + context->buffer_name, + NULL); + + if (buffer) + { + gimp_context_real_set_buffer (context, buffer); + } + else + { + g_object_notify (G_OBJECT (context), "buffer"); + gimp_context_buffer_changed (context); + } +} + +static void +gimp_context_buffer_removed (GimpContainer *container, + GimpBuffer *buffer, + GimpContext *context) +{ + if (buffer == context->buffer) + { + g_signal_handlers_disconnect_by_func (context->buffer, + gimp_context_buffer_dirty, + context); + g_clear_object (&context->buffer); + + if (! gimp_container_frozen (container)) + gimp_context_buffer_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_buffer (GimpContext *context, + GimpBuffer *buffer) +{ + if (context->buffer == buffer) + return; + + if (context->buffer_name) + { + g_clear_pointer (&context->buffer_name, g_free); + } + + if (context->buffer) + g_signal_handlers_disconnect_by_func (context->buffer, + gimp_context_buffer_dirty, + context); + + g_set_object (&context->buffer, buffer); + + if (buffer) + { + g_signal_connect_object (buffer, "name-changed", + G_CALLBACK (gimp_context_buffer_dirty), + context, + 0); + + context->buffer_name = g_strdup (gimp_object_get_name (buffer)); + } + + g_object_notify (G_OBJECT (context), "buffer"); + gimp_context_buffer_changed (context); +} + + +/*****************************************************************************/ +/* imagefile ***************************************************************/ + +GimpImagefile * +gimp_context_get_imagefile (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->imagefile; +} + +void +gimp_context_set_imagefile (GimpContext *context, + GimpImagefile *imagefile) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (imagefile == NULL || GIMP_IS_IMAGEFILE (imagefile)); + + context_find_defined (context, GIMP_CONTEXT_PROP_IMAGEFILE); + + gimp_context_real_set_imagefile (context, imagefile); +} + +void +gimp_context_imagefile_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[IMAGEFILE_CHANGED], 0, + context->imagefile); +} + +static void +gimp_context_imagefile_dirty (GimpImagefile *imagefile, + GimpContext *context) +{ + g_free (context->imagefile_name); + context->imagefile_name = g_strdup (gimp_object_get_name (imagefile)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_IMAGEFILE); +} + +static void +gimp_context_imagefile_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpImagefile *imagefile; + + imagefile = gimp_context_find_object (context, container, + context->imagefile_name, + NULL); + + if (imagefile) + { + gimp_context_real_set_imagefile (context, imagefile); + } + else + { + g_object_notify (G_OBJECT (context), "imagefile"); + gimp_context_imagefile_changed (context); + } +} + +static void +gimp_context_imagefile_removed (GimpContainer *container, + GimpImagefile *imagefile, + GimpContext *context) +{ + if (imagefile == context->imagefile) + { + g_signal_handlers_disconnect_by_func (context->imagefile, + gimp_context_imagefile_dirty, + context); + g_clear_object (&context->imagefile); + + if (! gimp_container_frozen (container)) + gimp_context_imagefile_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_imagefile (GimpContext *context, + GimpImagefile *imagefile) +{ + if (context->imagefile == imagefile) + return; + + if (context->imagefile_name) + { + g_clear_pointer (&context->imagefile_name, g_free); + } + + if (context->imagefile) + g_signal_handlers_disconnect_by_func (context->imagefile, + gimp_context_imagefile_dirty, + context); + + g_set_object (&context->imagefile, imagefile); + + if (imagefile) + { + g_signal_connect_object (imagefile, "name-changed", + G_CALLBACK (gimp_context_imagefile_dirty), + context, + 0); + + context->imagefile_name = g_strdup (gimp_object_get_name (imagefile)); + } + + g_object_notify (G_OBJECT (context), "imagefile"); + gimp_context_imagefile_changed (context); +} + + +/*****************************************************************************/ +/* template ***************************************************************/ + +GimpTemplate * +gimp_context_get_template (GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + return context->template; +} + +void +gimp_context_set_template (GimpContext *context, + GimpTemplate *template) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (template == NULL || GIMP_IS_TEMPLATE (template)); + + context_find_defined (context, GIMP_CONTEXT_PROP_TEMPLATE); + + gimp_context_real_set_template (context, template); +} + +void +gimp_context_template_changed (GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + g_signal_emit (context, + gimp_context_signals[TEMPLATE_CHANGED], 0, + context->template); +} + +static void +gimp_context_template_dirty (GimpTemplate *template, + GimpContext *context) +{ + g_free (context->template_name); + context->template_name = g_strdup (gimp_object_get_name (template)); + + g_signal_emit (context, gimp_context_signals[PROP_NAME_CHANGED], 0, + GIMP_CONTEXT_PROP_TEMPLATE); +} + +static void +gimp_context_template_list_thaw (GimpContainer *container, + GimpContext *context) +{ + GimpTemplate *template; + + template = gimp_context_find_object (context, container, + context->template_name, + NULL); + + if (template) + { + gimp_context_real_set_template (context, template); + } + else + { + g_object_notify (G_OBJECT (context), "template"); + gimp_context_template_changed (context); + } +} + +static void +gimp_context_template_removed (GimpContainer *container, + GimpTemplate *template, + GimpContext *context) +{ + if (template == context->template) + { + g_signal_handlers_disconnect_by_func (context->template, + gimp_context_template_dirty, + context); + g_clear_object (&context->template); + + if (! gimp_container_frozen (container)) + gimp_context_template_list_thaw (container, context); + } +} + +static void +gimp_context_real_set_template (GimpContext *context, + GimpTemplate *template) +{ + if (context->template == template) + return; + + if (context->template_name) + { + g_clear_pointer (&context->template_name, g_free); + } + + if (context->template) + g_signal_handlers_disconnect_by_func (context->template, + gimp_context_template_dirty, + context); + + g_set_object (&context->template, template); + + if (template) + { + g_signal_connect_object (template, "name-changed", + G_CALLBACK (gimp_context_template_dirty), + context, + 0); + + context->template_name = g_strdup (gimp_object_get_name (template)); + } + + g_object_notify (G_OBJECT (context), "template"); + gimp_context_template_changed (context); +} + + +/*****************************************************************************/ +/* utility functions *******************************************************/ + +static gpointer +gimp_context_find_object (GimpContext *context, + GimpContainer *container, + const gchar *object_name, + gpointer standard_object) +{ + GimpObject *object = NULL; + + if (object_name) + object = gimp_container_get_child_by_name (container, object_name); + + if (! object && ! gimp_container_is_empty (container)) + object = gimp_container_get_child_by_index (container, 0); + + if (! object) + object = standard_object; + + return object; +} diff --git a/app/core/gimpcontext.h b/app/core/gimpcontext.h new file mode 100644 index 0000000..7f15a4a --- /dev/null +++ b/app/core/gimpcontext.h @@ -0,0 +1,360 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcontext.h + * Copyright (C) 1999-2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CONTEXT_H__ +#define __GIMP_CONTEXT_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_CONTEXT (gimp_context_get_type ()) +#define GIMP_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONTEXT, GimpContext)) +#define GIMP_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, GIMP_TYPE_CONTEXT, GimpContextClass)) +#define GIMP_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONTEXT)) +#define GIMP_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CONTEXT)) +#define GIMP_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, GIMP_TYPE_CONTEXT, GimpContextClass)) + + +typedef struct _GimpContextClass GimpContextClass; + +/** + * GimpContext: + * + * Holds state such as the active image, active display, active brush, + * active foreground and background color, and so on. There can many + * instances of contexts. The user context is what the user sees and + * interacts with but there can also be contexts for docks and for + * plug-ins. + */ +struct _GimpContext +{ + GimpViewable parent_instance; + + Gimp *gimp; + + GimpContext *parent; + + guint32 defined_props; + guint32 serialize_props; + + GimpImage *image; + gpointer display; + + GimpToolInfo *tool_info; + gchar *tool_name; + + GimpPaintInfo *paint_info; + gchar *paint_name; + + GimpRGB foreground; + GimpRGB background; + + gdouble opacity; + GimpLayerMode paint_mode; + + GimpBrush *brush; + gchar *brush_name; + + GimpDynamics *dynamics; + gchar *dynamics_name; + + GimpMybrush *mybrush; + gchar *mybrush_name; + + GimpPattern *pattern; + gchar *pattern_name; + + GimpGradient *gradient; + gchar *gradient_name; + + GimpPalette *palette; + gchar *palette_name; + + GimpFont *font; + gchar *font_name; + + GimpToolPreset *tool_preset; + gchar *tool_preset_name; + + GimpBuffer *buffer; + gchar *buffer_name; + + GimpImagefile *imagefile; + gchar *imagefile_name; + + GimpTemplate *template; + gchar *template_name; +}; + +struct _GimpContextClass +{ + GimpViewableClass parent_class; + + void (* image_changed) (GimpContext *context, + GimpImage *image); + void (* display_changed) (GimpContext *context, + gpointer display); + + void (* tool_changed) (GimpContext *context, + GimpToolInfo *tool_info); + void (* paint_info_changed) (GimpContext *context, + GimpPaintInfo *paint_info); + + void (* foreground_changed) (GimpContext *context, + GimpRGB *color); + void (* background_changed) (GimpContext *context, + GimpRGB *color); + void (* opacity_changed) (GimpContext *context, + gdouble opacity); + void (* paint_mode_changed) (GimpContext *context, + GimpLayerMode paint_mode); + void (* brush_changed) (GimpContext *context, + GimpBrush *brush); + void (* dynamics_changed) (GimpContext *context, + GimpDynamics *dynamics); + void (* mybrush_changed) (GimpContext *context, + GimpMybrush *brush); + void (* pattern_changed) (GimpContext *context, + GimpPattern *pattern); + void (* gradient_changed) (GimpContext *context, + GimpGradient *gradient); + void (* palette_changed) (GimpContext *context, + GimpPalette *palette); + void (* font_changed) (GimpContext *context, + GimpFont *font); + void (* tool_preset_changed)(GimpContext *context, + GimpToolPreset *tool_preset); + void (* buffer_changed) (GimpContext *context, + GimpBuffer *buffer); + void (* imagefile_changed) (GimpContext *context, + GimpImagefile *imagefile); + void (* template_changed) (GimpContext *context, + GimpTemplate *template); + + void (* prop_name_changed) (GimpContext *context, + GimpContextPropType prop); +}; + + +GType gimp_context_get_type (void) G_GNUC_CONST; + +GimpContext * gimp_context_new (Gimp *gimp, + const gchar *name, + GimpContext *template); + +GimpContext * gimp_context_get_parent (GimpContext *context); +void gimp_context_set_parent (GimpContext *context, + GimpContext *parent); + +/* define / undefinine context properties + * + * the value of an undefined property will be taken from the parent context. + */ +void gimp_context_define_property (GimpContext *context, + GimpContextPropType prop, + gboolean defined); + +gboolean gimp_context_property_defined (GimpContext *context, + GimpContextPropType prop); + +void gimp_context_define_properties (GimpContext *context, + GimpContextPropMask props_mask, + gboolean defined); + + +/* specify which context properties will be serialized + */ +void gimp_context_set_serialize_properties (GimpContext *context, + GimpContextPropMask props_mask); + +GimpContextPropMask + gimp_context_get_serialize_properties (GimpContext *context); + + +/* copying context properties + */ +void gimp_context_copy_property (GimpContext *src, + GimpContext *dest, + GimpContextPropType prop); + +void gimp_context_copy_properties (GimpContext *src, + GimpContext *dest, + GimpContextPropMask props_mask); + + +/* manipulate by GType */ +GimpContextPropType gimp_context_type_to_property (GType type); +const gchar * gimp_context_type_to_prop_name (GType type); +const gchar * gimp_context_type_to_signal_name (GType type); + +GimpObject * gimp_context_get_by_type (GimpContext *context, + GType type); +void gimp_context_set_by_type (GimpContext *context, + GType type, + GimpObject *object); +void gimp_context_changed_by_type (GimpContext *context, + GType type); + + +/* image */ +GimpImage * gimp_context_get_image (GimpContext *context); +void gimp_context_set_image (GimpContext *context, + GimpImage *image); +void gimp_context_image_changed (GimpContext *context); + + +/* display */ +gpointer gimp_context_get_display (GimpContext *context); +void gimp_context_set_display (GimpContext *context, + gpointer display); +void gimp_context_display_changed (GimpContext *context); + + +/* tool */ +GimpToolInfo * gimp_context_get_tool (GimpContext *context); +void gimp_context_set_tool (GimpContext *context, + GimpToolInfo *tool_info); +void gimp_context_tool_changed (GimpContext *context); + + +/* paint info */ +GimpPaintInfo * gimp_context_get_paint_info (GimpContext *context); +void gimp_context_set_paint_info (GimpContext *context, + GimpPaintInfo *paint_info); +void gimp_context_paint_info_changed (GimpContext *context); + + +/* foreground color */ +void gimp_context_get_foreground (GimpContext *context, + GimpRGB *color); +void gimp_context_set_foreground (GimpContext *context, + const GimpRGB *color); +void gimp_context_foreground_changed (GimpContext *context); + + +/* background color */ +void gimp_context_get_background (GimpContext *context, + GimpRGB *color); +void gimp_context_set_background (GimpContext *context, + const GimpRGB *color); +void gimp_context_background_changed (GimpContext *context); + + +/* color utility functions */ +void gimp_context_set_default_colors (GimpContext *context); +void gimp_context_swap_colors (GimpContext *context); + + +/* opacity */ +gdouble gimp_context_get_opacity (GimpContext *context); +void gimp_context_set_opacity (GimpContext *context, + gdouble opacity); +void gimp_context_opacity_changed (GimpContext *context); + + +/* paint mode */ +GimpLayerMode gimp_context_get_paint_mode (GimpContext *context); +void gimp_context_set_paint_mode (GimpContext *context, + GimpLayerMode paint_mode); +void gimp_context_paint_mode_changed (GimpContext *context); + + +/* brush */ +GimpBrush * gimp_context_get_brush (GimpContext *context); +void gimp_context_set_brush (GimpContext *context, + GimpBrush *brush); +void gimp_context_brush_changed (GimpContext *context); + + +/* dynamics */ +GimpDynamics * gimp_context_get_dynamics (GimpContext *context); +void gimp_context_set_dynamics (GimpContext *context, + GimpDynamics *dynamics); +void gimp_context_dynamics_changed (GimpContext *context); + + +/* mybrush */ +GimpMybrush * gimp_context_get_mybrush (GimpContext *context); +void gimp_context_set_mybrush (GimpContext *context, + GimpMybrush *brush); +void gimp_context_mybrush_changed (GimpContext *context); + + +/* pattern */ +GimpPattern * gimp_context_get_pattern (GimpContext *context); +void gimp_context_set_pattern (GimpContext *context, + GimpPattern *pattern); +void gimp_context_pattern_changed (GimpContext *context); + + +/* gradient */ +GimpGradient * gimp_context_get_gradient (GimpContext *context); +void gimp_context_set_gradient (GimpContext *context, + GimpGradient *gradient); +void gimp_context_gradient_changed (GimpContext *context); + + +/* palette */ +GimpPalette * gimp_context_get_palette (GimpContext *context); +void gimp_context_set_palette (GimpContext *context, + GimpPalette *palette); +void gimp_context_palette_changed (GimpContext *context); + + +/* font */ +GimpFont * gimp_context_get_font (GimpContext *context); +void gimp_context_set_font (GimpContext *context, + GimpFont *font); +const gchar * gimp_context_get_font_name (GimpContext *context); +void gimp_context_set_font_name (GimpContext *context, + const gchar *name); +void gimp_context_font_changed (GimpContext *context); + + +/* tool_preset */ +GimpToolPreset * gimp_context_get_tool_preset (GimpContext *context); +void gimp_context_set_tool_preset (GimpContext *context, + GimpToolPreset *tool_preset); +void gimp_context_tool_preset_changed (GimpContext *context); + + +/* buffer */ +GimpBuffer * gimp_context_get_buffer (GimpContext *context); +void gimp_context_set_buffer (GimpContext *context, + GimpBuffer *palette); +void gimp_context_buffer_changed (GimpContext *context); + + +/* imagefile */ +GimpImagefile * gimp_context_get_imagefile (GimpContext *context); +void gimp_context_set_imagefile (GimpContext *context, + GimpImagefile *imagefile); +void gimp_context_imagefile_changed (GimpContext *context); + + +/* template */ +GimpTemplate * gimp_context_get_template (GimpContext *context); +void gimp_context_set_template (GimpContext *context, + GimpTemplate *template); +void gimp_context_template_changed (GimpContext *context); + + +#endif /* __GIMP_CONTEXT_H__ */ diff --git a/app/core/gimpcoords-interpolate.c b/app/core/gimpcoords-interpolate.c new file mode 100644 index 0000000..f667e57 --- /dev/null +++ b/app/core/gimpcoords-interpolate.c @@ -0,0 +1,371 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcoords-interpolate.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 . + */ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpcoords.h" +#include "gimpcoords-interpolate.h" + + +/* Local helper functions declarations*/ +static void gimp_coords_interpolate_bezier_internal (const GimpCoords bezier_pt[4], + const gdouble start_t, + const gdouble end_t, + const gdouble precision, + GArray *ret_coords, + GArray *ret_params, + gint depth); +static gdouble gimp_coords_get_catmull_spline_point (const gdouble t, + const gdouble p0, + const gdouble p1, + const gdouble p2, + const gdouble p3); + +/* Functions for bezier subdivision */ + +void +gimp_coords_interpolate_bezier (const GimpCoords bezier_pt[4], + const gdouble precision, + GArray *ret_coords, + GArray *ret_params) +{ + g_return_if_fail (bezier_pt != NULL); + g_return_if_fail (precision > 0.0); + g_return_if_fail (ret_coords != NULL); + + gimp_coords_interpolate_bezier_internal (bezier_pt, + 0.0, 1.0, + precision, + ret_coords, ret_params, 10); +} + +/* Recursive subdivision helper function */ +static void +gimp_coords_interpolate_bezier_internal (const GimpCoords bezier_pt[4], + const gdouble start_t, + const gdouble end_t, + const gdouble precision, + GArray *ret_coords, + GArray *ret_params, + gint depth) +{ + /* + * bezier_pt has to contain four GimpCoords with the four control points + * of the bezier segment. We subdivide it at the parameter 0.5. + */ + + GimpCoords subdivided[8]; + gdouble middle_t = (start_t + end_t) / 2; + + subdivided[0] = bezier_pt[0]; + subdivided[6] = bezier_pt[3]; + + /* if (!depth) g_printerr ("Hit recursion depth limit!\n"); */ + + gimp_coords_average (&bezier_pt[0], &bezier_pt[1], &subdivided[1]); + gimp_coords_average (&bezier_pt[1], &bezier_pt[2], &subdivided[7]); + gimp_coords_average (&bezier_pt[2], &bezier_pt[3], &subdivided[5]); + + gimp_coords_average (&subdivided[1], &subdivided[7], &subdivided[2]); + gimp_coords_average (&subdivided[7], &subdivided[5], &subdivided[4]); + gimp_coords_average (&subdivided[2], &subdivided[4], &subdivided[3]); + + /* + * We now have the coordinates of the two bezier segments in + * subdivided [0-3] and subdivided [3-6] + */ + + /* + * Here we need to check, if we have sufficiently subdivided, i.e. + * if the stroke is sufficiently close to a straight line. + */ + + if (! depth || + gimp_coords_bezier_is_straight (subdivided, precision)) /* 1st half */ + { + g_array_append_vals (ret_coords, subdivided, 3); + + if (ret_params) + { + gdouble params[3]; + + params[0] = start_t; + params[1] = (2 * start_t + middle_t) / 3; + params[2] = (start_t + 2 * middle_t) / 3; + + g_array_append_vals (ret_params, params, 3); + } + } + else + { + gimp_coords_interpolate_bezier_internal (subdivided, + start_t, (start_t + end_t) / 2, + precision, + ret_coords, ret_params, + depth - 1); + } + + if (! depth || + gimp_coords_bezier_is_straight (subdivided + 3, precision)) /* 2nd half */ + { + g_array_append_vals (ret_coords, subdivided + 3, 3); + + if (ret_params) + { + gdouble params[3]; + + params[0] = middle_t; + params[1] = (2 * middle_t + end_t) / 3; + params[2] = (middle_t + 2 * end_t) / 3; + + g_array_append_vals (ret_params, params, 3); + } + } + else + { + gimp_coords_interpolate_bezier_internal (subdivided + 3, + (start_t + end_t) / 2, end_t, + precision, + ret_coords, ret_params, + depth - 1); + } +} + + +/* + * Returns the position and/or velocity of a Bezier curve at time 't'. + */ + +void +gimp_coords_interpolate_bezier_at (const GimpCoords bezier_pt[4], + gdouble t, + GimpCoords *position, + GimpCoords *velocity) +{ + gdouble u = 1.0 - t; + + g_return_if_fail (bezier_pt != NULL); + + if (position) + { + GimpCoords a; + GimpCoords b; + + gimp_coords_mix ( u * u * u, &bezier_pt[0], + 3.0 * u * u * t, &bezier_pt[1], + &a); + gimp_coords_mix (3.0 * u * t * t, &bezier_pt[2], + t * t * t, &bezier_pt[3], + &b); + + gimp_coords_add (&a, &b, position); + } + + if (velocity) + { + GimpCoords a; + GimpCoords b; + + gimp_coords_mix (-3.0 * u * u, &bezier_pt[0], + 3.0 * (u - 2.0 * t) * u, &bezier_pt[1], + &a); + gimp_coords_mix (-3.0 * (t - 2.0 * u) * t, &bezier_pt[2], + 3.0 * t * t, &bezier_pt[3], + &b); + + gimp_coords_add (&a, &b, velocity); + } +} + +/* + * a helper function that determines if a bezier segment is "straight + * enough" to be approximated by a line. + * + * To be more exact, it also checks for the control points to be distributed + * evenly along the line. This makes it easier to reconstruct parameters for + * a given point along the segment. + * + * Needs four GimpCoords in an array. + */ + +gboolean +gimp_coords_bezier_is_straight (const GimpCoords bezier_pt[4], + gdouble precision) +{ + GimpCoords pt1, pt2; + + g_return_val_if_fail (bezier_pt != NULL, FALSE); + g_return_val_if_fail (precision > 0.0, FALSE); + + /* calculate the "ideal" positions for the control points */ + + gimp_coords_mix (2.0 / 3.0, &bezier_pt[0], + 1.0 / 3.0, &bezier_pt[3], + &pt1); + gimp_coords_mix (1.0 / 3.0, &bezier_pt[0], + 2.0 / 3.0, &bezier_pt[3], + &pt2); + + /* calculate the deviation of the actual control points */ + + return (gimp_coords_manhattan_dist (&bezier_pt[1], &pt1) < precision && + gimp_coords_manhattan_dist (&bezier_pt[2], &pt2) < precision); +} + + +/* Functions for catmull-rom interpolation */ + +void +gimp_coords_interpolate_catmull (const GimpCoords catmull_pt[4], + gdouble precision, + GArray *ret_coords, + GArray *ret_params) +{ + gdouble delta_x, delta_y; + gdouble distance; + gdouble dir_step; + gdouble delta_dir; + gint num_points; + gint n; + + GimpCoords past_coords; + GimpCoords start_coords; + GimpCoords end_coords; + GimpCoords future_coords; + + g_return_if_fail (catmull_pt != NULL); + g_return_if_fail (precision > 0.0); + g_return_if_fail (ret_coords != NULL); + + delta_x = catmull_pt[2].x - catmull_pt[1].x; + delta_y = catmull_pt[2].y - catmull_pt[1].y; + + /* Catmull-Rom interpolation requires 4 points. + * Two endpoints plus one more at each end. + */ + + past_coords = catmull_pt[0]; + start_coords = catmull_pt[1]; + end_coords = catmull_pt[2]; + future_coords = catmull_pt[3]; + + distance = sqrt (SQR (delta_x) + SQR (delta_y)); + + num_points = distance / precision; + + delta_dir = end_coords.direction - start_coords.direction; + + if (delta_dir <= -0.5) + delta_dir += 1.0; + else if (delta_dir >= 0.5) + delta_dir -= 1.0; + + dir_step = delta_dir / num_points; + + for (n = 1; n <= num_points; n++) + { + GimpCoords coords = past_coords; /* Make sure we carry over things + * we do not interpolate */ + gdouble velocity; + gdouble pressure; + gdouble p = (gdouble) n / num_points; + + coords.x = + gimp_coords_get_catmull_spline_point (p, + past_coords.x, + start_coords.x, + end_coords.x, + future_coords.x); + + coords.y = + gimp_coords_get_catmull_spline_point (p, + past_coords.y, + start_coords.y, + end_coords.y, + future_coords.y); + + pressure = + gimp_coords_get_catmull_spline_point (p, + past_coords.pressure, + start_coords.pressure, + end_coords.pressure, + future_coords.pressure); + coords.pressure = CLAMP (pressure, 0.0, 1.0); + + coords.xtilt = + gimp_coords_get_catmull_spline_point (p, + past_coords.xtilt, + start_coords.xtilt, + end_coords.xtilt, + future_coords.xtilt); + coords.ytilt = + gimp_coords_get_catmull_spline_point (p, + past_coords.ytilt, + start_coords.ytilt, + end_coords.ytilt, + future_coords.ytilt); + + coords.wheel = + gimp_coords_get_catmull_spline_point (p, + past_coords.wheel, + start_coords.wheel, + end_coords.wheel, + future_coords.wheel); + + velocity = gimp_coords_get_catmull_spline_point (p, + past_coords.velocity, + start_coords.velocity, + end_coords.velocity, + future_coords.velocity); + coords.velocity = CLAMP (velocity, 0.0, 1.0); + + coords.direction = start_coords.direction + dir_step * n; + + coords.direction = coords.direction - floor (coords.direction); + + coords.xscale = end_coords.xscale; + coords.yscale = end_coords.yscale; + coords.angle = end_coords.angle; + coords.reflect = end_coords.reflect; + + g_array_append_val (ret_coords, coords); + + if (ret_params) + g_array_append_val (ret_params, p); + } +} + +static gdouble +gimp_coords_get_catmull_spline_point (const gdouble t, + const gdouble p0, + const gdouble p1, + const gdouble p2, + const gdouble p3) +{ + return ((((-t + 2.0) * t - 1.0) * t / 2.0) * p0 + + ((((3.0 * t - 5.0) * t) * t + 2.0) / 2.0) * p1 + + (((-3.0 * t + 4.0) * t + 1.0) * t / 2.0) * p2 + + (((t - 1) * t * t) / 2.0) * p3); +} diff --git a/app/core/gimpcoords-interpolate.h b/app/core/gimpcoords-interpolate.h new file mode 100644 index 0000000..d729cd4 --- /dev/null +++ b/app/core/gimpcoords-interpolate.h @@ -0,0 +1,41 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcoords-interpolate.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 . + */ + +#ifndef __GIMP_COORDS_INTERPOLATE_H__ +#define __GIMP_COORDS_INTERPOLATE_H__ + +void gimp_coords_interpolate_bezier (const GimpCoords bezier_pt[4], + gdouble precision, + GArray *ret_coords, + GArray *ret_params); + +void gimp_coords_interpolate_bezier_at (const GimpCoords bezier_pt[4], + gdouble t, + GimpCoords *position, + GimpCoords *velocity); + +gboolean gimp_coords_bezier_is_straight (const GimpCoords bezier_pt[4], + gdouble precision); + +void gimp_coords_interpolate_catmull (const GimpCoords catmull_pt[4], + gdouble precision, + GArray *ret_coords, + GArray *ret_params); + +#endif /* __GIMP_COORDS_INTERPOLATE_H__ */ diff --git a/app/core/gimpcoords.c b/app/core/gimpcoords.c new file mode 100644 index 0000000..306f7f2 --- /dev/null +++ b/app/core/gimpcoords.c @@ -0,0 +1,248 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcoords.c + * Copyright (C) 2002 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpcoords.h" + + +#define INPUT_RESOLUTION 256 + + +/* amul * a + bmul * b = ret_val */ + +void +gimp_coords_mix (const gdouble amul, + const GimpCoords *a, + const gdouble bmul, + const GimpCoords *b, + GimpCoords *ret_val) +{ + if (b) + { + ret_val->x = amul * a->x + bmul * b->x; + ret_val->y = amul * a->y + bmul * b->y; + ret_val->pressure = amul * a->pressure + bmul * b->pressure; + ret_val->xtilt = amul * a->xtilt + bmul * b->xtilt; + ret_val->ytilt = amul * a->ytilt + bmul * b->ytilt; + ret_val->wheel = amul * a->wheel + bmul * b->wheel; + ret_val->velocity = amul * a->velocity + bmul * b->velocity; + ret_val->direction = amul * a->direction + bmul * b->direction; + ret_val->extended = b->extended || a->extended; + } + else + { + ret_val->x = amul * a->x; + ret_val->y = amul * a->y; + ret_val->pressure = amul * a->pressure; + ret_val->xtilt = amul * a->xtilt; + ret_val->ytilt = amul * a->ytilt; + ret_val->wheel = amul * a->wheel; + ret_val->velocity = amul * a->velocity; + ret_val->direction = amul * a->direction; + ret_val->extended = a->extended; + } +} + + +/* (a+b)/2 = ret_average */ + +void +gimp_coords_average (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *ret_average) +{ + gimp_coords_mix (0.5, a, 0.5, b, ret_average); +} + + +/* a + b = ret_add */ + +void +gimp_coords_add (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *ret_add) +{ + gimp_coords_mix (1.0, a, 1.0, b, ret_add); +} + + +/* a - b = ret_difference */ + +void +gimp_coords_difference (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *ret_difference) +{ + gimp_coords_mix (1.0, a, -1.0, b, ret_difference); +} + + +/* a * f = ret_product */ + +void +gimp_coords_scale (const gdouble f, + const GimpCoords *a, + GimpCoords *ret_product) +{ + gimp_coords_mix (f, a, 0.0, NULL, ret_product); +} + + +/* local helper for measuring the scalarproduct of two gimpcoords. */ + +gdouble +gimp_coords_scalarprod (const GimpCoords *a, + const GimpCoords *b) +{ + return (a->x * b->x + + a->y * b->y + + a->pressure * b->pressure + + a->xtilt * b->xtilt + + a->ytilt * b->ytilt + + a->wheel * b->wheel + + a->velocity * b->velocity + + a->direction * b->direction); +} + + +/* + * The "length" of the gimpcoord. + * Applies a metric that increases the weight on the + * pressure/xtilt/ytilt/wheel to ensure proper interpolation + */ + +gdouble +gimp_coords_length_squared (const GimpCoords *a) +{ + GimpCoords upscaled_a; + + upscaled_a.x = a->x; + upscaled_a.y = a->y; + upscaled_a.pressure = a->pressure * INPUT_RESOLUTION; + upscaled_a.xtilt = a->xtilt * INPUT_RESOLUTION; + upscaled_a.ytilt = a->ytilt * INPUT_RESOLUTION; + upscaled_a.wheel = a->wheel * INPUT_RESOLUTION; + upscaled_a.velocity = a->velocity * INPUT_RESOLUTION; + upscaled_a.direction = a->direction * INPUT_RESOLUTION; + + return gimp_coords_scalarprod (&upscaled_a, &upscaled_a); +} + + +gdouble +gimp_coords_length (const GimpCoords *a) +{ + return sqrt (gimp_coords_length_squared (a)); +} + +/* + * Distance via manhattan metric, an upper bound for the eucledian metric. + * used for e.g. bezier approximation + */ + +gdouble +gimp_coords_manhattan_dist (const GimpCoords *a, + const GimpCoords *b) +{ + gdouble dist = 0; + + dist += ABS (a->pressure - b->pressure); + dist += ABS (a->xtilt - b->xtilt); + dist += ABS (a->ytilt - b->ytilt); + dist += ABS (a->wheel - b->wheel); + dist += ABS (a->velocity - b->velocity); + dist += ABS (a->direction - b->direction); + + dist *= INPUT_RESOLUTION; + + dist += ABS (a->x - b->x); + dist += ABS (a->y - b->y); + + return dist; +} + +gboolean +gimp_coords_equal (const GimpCoords *a, + const GimpCoords *b) +{ + return (a->x == b->x && + a->y == b->y && + a->pressure == b->pressure && + a->xtilt == b->xtilt && + a->ytilt == b->ytilt && + a->wheel == b->wheel && + a->velocity == b->velocity && + a->direction == b->direction); + + /* Extended attribute was omitted from this comparison deliberately, + * it describes the events origin, not its value + */ +} + +/* helper for calculating direction of two gimpcoords. */ + +gdouble +gimp_coords_direction (const GimpCoords *a, + const GimpCoords *b) +{ + gdouble direction; + gdouble delta_x, delta_y; + + delta_x = a->x - b->x; + delta_y = a->y - b->y; + + if ((delta_x == 0) && (delta_y == 0)) + { + direction = a->direction; + } + else if (delta_x == 0) + { + if (delta_y > 0) + direction = 0.25; + else + direction = 0.75; + } + else if (delta_y == 0) + { + if (delta_x < 0) + direction = 0.0; + else + direction = 0.5; + } + else + { + direction = atan ((- 1.0 * delta_y) / delta_x) / (2 * G_PI); + + if (delta_x > 0.0) + direction = direction + 0.5; + + if (direction < 0.0) + direction = direction + 1.0; + } + + return direction; +} diff --git a/app/core/gimpcoords.h b/app/core/gimpcoords.h new file mode 100644 index 0000000..38297f2 --- /dev/null +++ b/app/core/gimpcoords.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcoords.h + * Copyright (C) 2002 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_COORDS_H__ +#define __GIMP_COORDS_H__ + + +void gimp_coords_mix (const gdouble amul, + const GimpCoords *a, + const gdouble bmul, + const GimpCoords *b, + GimpCoords *ret_val); +void gimp_coords_average (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *ret_average); +void gimp_coords_add (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *ret_add); +void gimp_coords_difference (const GimpCoords *a, + const GimpCoords *b, + GimpCoords *difference); +void gimp_coords_scale (const gdouble f, + const GimpCoords *a, + GimpCoords *ret_product); + +gdouble gimp_coords_scalarprod (const GimpCoords *a, + const GimpCoords *b); +gdouble gimp_coords_length (const GimpCoords *a); +gdouble gimp_coords_length_squared (const GimpCoords *a); +gdouble gimp_coords_manhattan_dist (const GimpCoords *a, + const GimpCoords *b); + +gboolean gimp_coords_equal (const GimpCoords *a, + const GimpCoords *b); + +gdouble gimp_coords_direction (const GimpCoords *a, + const GimpCoords *b); + + +#endif /* __GIMP_COORDS_H__ */ diff --git a/app/core/gimpcurve-load.c b/app/core/gimpcurve-load.c new file mode 100644 index 0000000..9397d4a --- /dev/null +++ b/app/core/gimpcurve-load.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpcurve.h" +#include "gimpcurve-load.h" + + +GList * +gimp_curve_load (GFile *file, + GInputStream *input, + GError **error) +{ + GimpCurve *curve; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + curve = g_object_new (GIMP_TYPE_CURVE, NULL); + + if (gimp_config_deserialize_stream (GIMP_CONFIG (curve), + input, + NULL, error)) + { + return g_list_prepend (NULL, curve); + } + + g_object_unref (curve); + + return NULL; +} diff --git a/app/core/gimpcurve-load.h b/app/core/gimpcurve-load.h new file mode 100644 index 0000000..fc7ffa3 --- /dev/null +++ b/app/core/gimpcurve-load.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __GIMP_CURVE_LOAD_H__ +#define __GIMP_CURVE_LOAD_H__ + + +#define GIMP_CURVE_FILE_EXTENSION ".curve" + + +GList * gimp_curve_load (GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_CURVE_LOAD_H__ */ diff --git a/app/core/gimpcurve-map.c b/app/core/gimpcurve-map.c new file mode 100644 index 0000000..bd39b27 --- /dev/null +++ b/app/core/gimpcurve-map.c @@ -0,0 +1,253 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpcurve.h" +#include "gimpcurve-map.h" + + +#if defined (HAVE_ISFINITE) +#define FINITE(x) isfinite(x) +#elif defined (HAVE_FINITE) +#define FINITE(x) finite(x) +#elif defined (G_OS_WIN32) +#define FINITE(x) _finite(x) +#else +#error "no FINITE() implementation available?!" +#endif + + +enum +{ + CURVE_NONE = 0, + CURVE_COLORS = 1 << 0, + CURVE_RED = 1 << 1, + CURVE_GREEN = 1 << 2, + CURVE_BLUE = 1 << 3, + CURVE_ALPHA = 1 << 4 +}; + +static guint gimp_curve_get_apply_mask (GimpCurve *curve_colors, + GimpCurve *curve_red, + GimpCurve *curve_green, + GimpCurve *curve_blue, + GimpCurve *curve_alpha); +static inline gdouble gimp_curve_map_value_inline (GimpCurve *curve, + gdouble value); + + +gdouble +gimp_curve_map_value (GimpCurve *curve, + gdouble value) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), 0.0); + + return gimp_curve_map_value_inline (curve, value); +} + +void +gimp_curve_map_pixels (GimpCurve *curve_colors, + GimpCurve *curve_red, + GimpCurve *curve_green, + GimpCurve *curve_blue, + GimpCurve *curve_alpha, + gfloat *src, + gfloat *dest, + glong samples) +{ + g_return_if_fail (GIMP_IS_CURVE (curve_colors)); + g_return_if_fail (GIMP_IS_CURVE (curve_red)); + g_return_if_fail (GIMP_IS_CURVE (curve_green)); + g_return_if_fail (GIMP_IS_CURVE (curve_blue)); + g_return_if_fail (GIMP_IS_CURVE (curve_alpha)); + + switch (gimp_curve_get_apply_mask (curve_colors, + curve_red, + curve_green, + curve_blue, + curve_alpha)) + { + case CURVE_NONE: + memcpy (dest, src, samples * 4 * sizeof (gfloat)); + break; + + case CURVE_COLORS: + while (samples--) + { + dest[0] = gimp_curve_map_value_inline (curve_colors, src[0]); + dest[1] = gimp_curve_map_value_inline (curve_colors, src[1]); + dest[2] = gimp_curve_map_value_inline (curve_colors, src[2]); + /* don't apply the colors curve to the alpha channel */ + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case CURVE_RED: + while (samples--) + { + dest[0] = gimp_curve_map_value_inline (curve_red, src[0]); + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case CURVE_GREEN: + while (samples--) + { + dest[0] = src[0]; + dest[1] = gimp_curve_map_value_inline (curve_green, src[1]); + dest[2] = src[2]; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case CURVE_BLUE: + while (samples--) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = gimp_curve_map_value_inline (curve_blue, src[2]); + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case CURVE_ALPHA: + while (samples--) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = gimp_curve_map_value_inline (curve_alpha, src[3]); + + src += 4; + dest += 4; + } + break; + + case (CURVE_RED | CURVE_GREEN | CURVE_BLUE): + while (samples--) + { + dest[0] = gimp_curve_map_value_inline (curve_red, src[0]); + dest[1] = gimp_curve_map_value_inline (curve_green, src[1]); + dest[2] = gimp_curve_map_value_inline (curve_blue, src[2]); + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + default: + while (samples--) + { + dest[0] = gimp_curve_map_value_inline (curve_colors, + gimp_curve_map_value_inline (curve_red, + src[0])); + dest[1] = gimp_curve_map_value_inline (curve_colors, + gimp_curve_map_value_inline (curve_green, + src[1])); + dest[2] = gimp_curve_map_value_inline (curve_colors, + gimp_curve_map_value_inline (curve_blue, + src[2])); + /* don't apply the colors curve to the alpha channel */ + dest[3] = gimp_curve_map_value_inline (curve_alpha, src[3]); + + src += 4; + dest += 4; + } + break; + } +} + +static guint +gimp_curve_get_apply_mask (GimpCurve *curve_colors, + GimpCurve *curve_red, + GimpCurve *curve_green, + GimpCurve *curve_blue, + GimpCurve *curve_alpha) +{ + return ((gimp_curve_is_identity (curve_colors) ? 0 : CURVE_COLORS) | + (gimp_curve_is_identity (curve_red) ? 0 : CURVE_RED) | + (gimp_curve_is_identity (curve_green) ? 0 : CURVE_GREEN) | + (gimp_curve_is_identity (curve_blue) ? 0 : CURVE_BLUE) | + (gimp_curve_is_identity (curve_alpha) ? 0 : CURVE_ALPHA)); +} + +static inline gdouble +gimp_curve_map_value_inline (GimpCurve *curve, + gdouble value) +{ + if (curve->identity) + { + if (FINITE (value)) + return CLAMP (value, 0.0, 1.0); + + return 0.0; + } + + /* check for known values first, so broken values like NaN + * delivered by broken drivers don't run into the interpolation + * code + */ + if (value > 0.0 && value < 1.0) /* interpolate the curve */ + { + gdouble f; + gint index; + + /* map value to the sample space */ + value = value * (curve->n_samples - 1); + + /* determine the indices of the closest sample points */ + index = (gint) value; + + /* calculate the position between the sample points */ + f = value - index; + + return (1.0 - f) * curve->samples[index] + f * curve->samples[index + 1]; + } + else if (value >= 1.0) + { + return curve->samples[curve->n_samples - 1]; + } + else + { + return curve->samples[0]; + } +} diff --git a/app/core/gimpcurve-map.h b/app/core/gimpcurve-map.h new file mode 100644 index 0000000..185e2fc --- /dev/null +++ b/app/core/gimpcurve-map.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 . + */ + +#ifndef __GIMP_CURVE_MAP_H__ +#define __GIMP_CURVE_MAP_H__ + + +gdouble gimp_curve_map_value (GimpCurve *curve, + gdouble value); +void gimp_curve_map_pixels (GimpCurve *curve_colors, + GimpCurve *curve_red, + GimpCurve *curve_green, + GimpCurve *curve_blue, + GimpCurve *curve_alpha, + gfloat *src, + gfloat *dest, + glong samples); + + +#endif /* __GIMP_CURVE_MAP_H__ */ diff --git a/app/core/gimpcurve-save.c b/app/core/gimpcurve-save.c new file mode 100644 index 0000000..2791b13 --- /dev/null +++ b/app/core/gimpcurve-save.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpcurve.h" +#include "gimpcurve-save.h" + + +gboolean +gimp_curve_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_CURVE (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return gimp_config_serialize_to_stream (GIMP_CONFIG (data), + output, + "GIMP curve file", + "end of GIMP curve file", + NULL, error); +} diff --git a/app/core/gimpcurve-save.h b/app/core/gimpcurve-save.h new file mode 100644 index 0000000..98d504f --- /dev/null +++ b/app/core/gimpcurve-save.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 . + */ + +#ifndef __GIMP_CURVE_SAVE_H__ +#define __GIMP_CURVE_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_curve_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_CURVE_SAVE_H__ */ diff --git a/app/core/gimpcurve.c b/app/core/gimpcurve.c new file mode 100644 index 0000000..bde20b2 --- /dev/null +++ b/app/core/gimpcurve.c @@ -0,0 +1,1289 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include /* memcmp */ + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpcurve.h" +#include "gimpcurve-load.h" +#include "gimpcurve-save.h" +#include "gimpparamspecs.h" + +#include "gimp-intl.h" + + +#define EPSILON 1e-6 + + +enum +{ + PROP_0, + PROP_CURVE_TYPE, + PROP_N_POINTS, + PROP_POINTS, + PROP_POINT_TYPES, + PROP_N_SAMPLES, + PROP_SAMPLES +}; + + +/* local function prototypes */ + +static void gimp_curve_config_iface_init (GimpConfigInterface *iface); + +static void gimp_curve_finalize (GObject *object); +static void gimp_curve_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_curve_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_curve_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_curve_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_curve_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_curve_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_curve_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_curve_dirty (GimpData *data); +static const gchar * gimp_curve_get_extension (GimpData *data); +static void gimp_curve_data_copy (GimpData *data, + GimpData *src_data); + +static gboolean gimp_curve_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_curve_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_curve_equal (GimpConfig *a, + GimpConfig *b); +static void _gimp_curve_reset (GimpConfig *config); +static gboolean gimp_curve_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + +static void gimp_curve_calculate (GimpCurve *curve); +static void gimp_curve_plot (GimpCurve *curve, + gint p1, + gint p2, + gint p3, + gint p4); + + +G_DEFINE_TYPE_WITH_CODE (GimpCurve, gimp_curve, GIMP_TYPE_DATA, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_curve_config_iface_init)) + +#define parent_class gimp_curve_parent_class + + +static void +gimp_curve_class_init (GimpCurveClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + GParamSpec *array_spec; + + object_class->finalize = gimp_curve_finalize; + object_class->set_property = gimp_curve_set_property; + object_class->get_property = gimp_curve_get_property; + + gimp_object_class->get_memsize = gimp_curve_get_memsize; + + viewable_class->default_icon_name = "FIXME icon name"; + viewable_class->get_preview_size = gimp_curve_get_preview_size; + viewable_class->get_popup_size = gimp_curve_get_popup_size; + viewable_class->get_new_preview = gimp_curve_get_new_preview; + viewable_class->get_description = gimp_curve_get_description; + + data_class->dirty = gimp_curve_dirty; + data_class->save = gimp_curve_save; + data_class->get_extension = gimp_curve_get_extension; + data_class->copy = gimp_curve_data_copy; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CURVE_TYPE, + "curve-type", + "Curve Type", + "The curve type", + GIMP_TYPE_CURVE_TYPE, + GIMP_CURVE_SMOOTH, 0); + + GIMP_CONFIG_PROP_INT (object_class, PROP_N_POINTS, + "n-points", + "Number of Points", + "The number of points", + 0, G_MAXINT, 0, + /* for backward compatibility */ + GIMP_CONFIG_PARAM_IGNORE); + + array_spec = g_param_spec_double ("point", NULL, NULL, + -1.0, 1.0, 0.0, GIMP_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_POINTS, + gimp_param_spec_value_array ("points", + NULL, NULL, + array_spec, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_FLAGS)); + + array_spec = g_param_spec_enum ("point-type", NULL, NULL, + GIMP_TYPE_CURVE_POINT_TYPE, + GIMP_CURVE_POINT_SMOOTH, + GIMP_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_POINT_TYPES, + gimp_param_spec_value_array ("point-types", + NULL, NULL, + array_spec, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_FLAGS)); + + GIMP_CONFIG_PROP_INT (object_class, PROP_N_SAMPLES, + "n-samples", + "Number of Samples", + "The number of samples", + 256, 256, 256, 0); + + array_spec = g_param_spec_double ("sample", NULL, NULL, + 0.0, 1.0, 0.0, GIMP_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SAMPLES, + gimp_param_spec_value_array ("samples", + NULL, NULL, + array_spec, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_FLAGS)); +} + +static void +gimp_curve_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_curve_serialize; + iface->deserialize = gimp_curve_deserialize; + iface->equal = gimp_curve_equal; + iface->reset = _gimp_curve_reset; + iface->copy = gimp_curve_config_copy; +} + +static void +gimp_curve_init (GimpCurve *curve) +{ + curve->n_points = 0; + curve->points = NULL; + curve->n_samples = 0; + curve->samples = NULL; + curve->identity = FALSE; +} + +static void +gimp_curve_finalize (GObject *object) +{ + GimpCurve *curve = GIMP_CURVE (object); + + g_clear_pointer (&curve->points, g_free); + curve->n_points = 0; + + g_clear_pointer (&curve->samples, g_free); + curve->n_samples = 0; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_curve_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCurve *curve = GIMP_CURVE (object); + + switch (property_id) + { + case PROP_CURVE_TYPE: + gimp_curve_set_curve_type (curve, g_value_get_enum (value)); + break; + + case PROP_N_POINTS: + /* ignored */ + break; + + case PROP_POINTS: + { + GimpValueArray *array = g_value_get_boxed (value); + GimpCurvePoint *points; + gint length; + gint n_points; + gint i; + + if (! array) + { + gimp_curve_clear_points (curve); + + break; + } + + length = gimp_value_array_length (array) / 2; + + n_points = 0; + points = g_new0 (GimpCurvePoint, length); + + for (i = 0; i < length; i++) + { + GValue *x = gimp_value_array_index (array, i * 2); + GValue *y = gimp_value_array_index (array, i * 2 + 1); + + /* for backward compatibility */ + if (g_value_get_double (x) < 0.0) + continue; + + points[n_points].x = CLAMP (g_value_get_double (x), 0.0, 1.0); + points[n_points].y = CLAMP (g_value_get_double (y), 0.0, 1.0); + + if (n_points > 0) + { + points[n_points].x = MAX (points[n_points].x, + points[n_points - 1].x); + } + + if (n_points < curve->n_points) + points[n_points].type = curve->points[n_points].type; + else + points[n_points].type = GIMP_CURVE_POINT_SMOOTH; + + n_points++; + } + + g_free (curve->points); + + curve->n_points = n_points; + curve->points = points; + + g_object_notify (object, "n-points"); + g_object_notify (object, "point-types"); + } + break; + + case PROP_POINT_TYPES: + { + GimpValueArray *array = g_value_get_boxed (value); + GimpCurvePoint *points; + gint length; + gdouble x = 0.0; + gdouble y = 0.0; + gint i; + + if (! array) + { + gimp_curve_clear_points (curve); + + break; + } + + length = gimp_value_array_length (array); + + points = g_new0 (GimpCurvePoint, length); + + for (i = 0; i < length; i++) + { + GValue *type = gimp_value_array_index (array, i); + + points[i].type = g_value_get_enum (type); + + if (i < curve->n_points) + { + x = curve->points[i].x; + y = curve->points[i].y; + } + + points[i].x = x; + points[i].y = y; + } + + g_free (curve->points); + + curve->n_points = length; + curve->points = points; + + g_object_notify (object, "n-points"); + g_object_notify (object, "points"); + } + break; + + case PROP_N_SAMPLES: + gimp_curve_set_n_samples (curve, g_value_get_int (value)); + break; + + case PROP_SAMPLES: + { + GimpValueArray *array = g_value_get_boxed (value); + gint length; + gint i; + + if (! array) + break; + + length = gimp_value_array_length (array); + + for (i = 0; i < curve->n_samples && i < length; i++) + { + GValue *v = gimp_value_array_index (array, i); + + curve->samples[i] = CLAMP (g_value_get_double (v), 0.0, 1.0); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_curve_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCurve *curve = GIMP_CURVE (object); + + switch (property_id) + { + case PROP_CURVE_TYPE: + g_value_set_enum (value, curve->curve_type); + break; + + case PROP_N_POINTS: + g_value_set_int (value, curve->n_points); + break; + + case PROP_POINTS: + { + GimpValueArray *array = gimp_value_array_new (curve->n_points * 2); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, G_TYPE_DOUBLE); + + for (i = 0; i < curve->n_points; i++) + { + g_value_set_double (&v, curve->points[i].x); + gimp_value_array_append (array, &v); + + g_value_set_double (&v, curve->points[i].y); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + case PROP_POINT_TYPES: + { + GimpValueArray *array = gimp_value_array_new (curve->n_points); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, GIMP_TYPE_CURVE_POINT_TYPE); + + for (i = 0; i < curve->n_points; i++) + { + g_value_set_enum (&v, curve->points[i].type); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + case PROP_N_SAMPLES: + g_value_set_int (value, curve->n_samples); + break; + + case PROP_SAMPLES: + { + GimpValueArray *array = gimp_value_array_new (curve->n_samples); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, G_TYPE_DOUBLE); + + for (i = 0; i < curve->n_samples; i++) + { + g_value_set_double (&v, curve->samples[i]); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_curve_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpCurve *curve = GIMP_CURVE (object); + gint64 memsize = 0; + + memsize += curve->n_points * sizeof (GimpCurvePoint); + memsize += curve->n_samples * sizeof (gdouble); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_curve_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + *width = size; + *height = size; +} + +static gboolean +gimp_curve_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + *popup_width = width * 2; + *popup_height = height * 2; + + return TRUE; +} + +static GimpTempBuf * +gimp_curve_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + return NULL; +} + +static gchar * +gimp_curve_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpCurve *curve = GIMP_CURVE (viewable); + + return g_strdup_printf ("%s", gimp_object_get_name (curve)); +} + +static void +gimp_curve_dirty (GimpData *data) +{ + GimpCurve *curve = GIMP_CURVE (data); + + curve->identity = FALSE; + + gimp_curve_calculate (curve); + + GIMP_DATA_CLASS (parent_class)->dirty (data); +} + +static const gchar * +gimp_curve_get_extension (GimpData *data) +{ + return GIMP_CURVE_FILE_EXTENSION; +} + +static void +gimp_curve_data_copy (GimpData *data, + GimpData *src_data) +{ + gimp_data_freeze (data); + + gimp_config_copy (GIMP_CONFIG (src_data), + GIMP_CONFIG (data), 0); + + gimp_data_thaw (data); +} + +static gboolean +gimp_curve_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + return gimp_config_serialize_properties (config, writer); +} + +static gboolean +gimp_curve_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + gboolean success; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + GIMP_CURVE (config)->identity = FALSE; + + return success; +} + +static gboolean +gimp_curve_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpCurve *a_curve = GIMP_CURVE (a); + GimpCurve *b_curve = GIMP_CURVE (b); + + if (a_curve->curve_type != b_curve->curve_type) + return FALSE; + + if (a_curve->n_points != b_curve->n_points || + memcmp (a_curve->points, b_curve->points, + sizeof (GimpCurvePoint) * a_curve->n_points)) + { + return FALSE; + } + + if (a_curve->n_samples != b_curve->n_samples || + memcmp (a_curve->samples, b_curve->samples, + sizeof (gdouble) * a_curve->n_samples)) + { + return FALSE; + } + + return TRUE; +} + +static void +_gimp_curve_reset (GimpConfig *config) +{ + gimp_curve_reset (GIMP_CURVE (config), TRUE); +} + +static gboolean +gimp_curve_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpCurve *src_curve = GIMP_CURVE (src); + GimpCurve *dest_curve = GIMP_CURVE (dest); + + /* make sure the curve type is copied *before* the points, so that we don't + * overwrite the copied points when changing the type + */ + dest_curve->curve_type = src_curve->curve_type; + + gimp_config_sync (G_OBJECT (src), G_OBJECT (dest), flags); + + dest_curve->identity = src_curve->identity; + + gimp_data_dirty (GIMP_DATA (dest)); + + return TRUE; +} + + +/* public functions */ + +GimpData * +gimp_curve_new (const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + return g_object_new (GIMP_TYPE_CURVE, + "name", name, + NULL); +} + +GimpData * +gimp_curve_get_standard (void) +{ + static GimpData *standard_curve = NULL; + + if (! standard_curve) + { + standard_curve = gimp_curve_new ("Standard"); + + gimp_data_clean (standard_curve); + gimp_data_make_internal (standard_curve, + "gimp-curve-standard"); + + g_object_ref (standard_curve); + } + + return standard_curve; +} + +void +gimp_curve_reset (GimpCurve *curve, + gboolean reset_type) +{ + gint i; + + g_return_if_fail (GIMP_IS_CURVE (curve)); + + g_object_freeze_notify (G_OBJECT (curve)); + + for (i = 0; i < curve->n_samples; i++) + curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1); + + g_object_notify (G_OBJECT (curve), "samples"); + + g_free (curve->points); + + curve->n_points = 2; + curve->points = g_new0 (GimpCurvePoint, 2); + + curve->points[0].x = 0.0; + curve->points[0].y = 0.0; + curve->points[0].type = GIMP_CURVE_POINT_SMOOTH; + + curve->points[1].x = 1.0; + curve->points[1].y = 1.0; + curve->points[1].type = GIMP_CURVE_POINT_SMOOTH; + + g_object_notify (G_OBJECT (curve), "n-points"); + g_object_notify (G_OBJECT (curve), "points"); + g_object_notify (G_OBJECT (curve), "point-types"); + + if (reset_type) + { + curve->curve_type = GIMP_CURVE_SMOOTH; + g_object_notify (G_OBJECT (curve), "curve-type"); + } + + curve->identity = TRUE; + + g_object_thaw_notify (G_OBJECT (curve)); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +void +gimp_curve_set_curve_type (GimpCurve *curve, + GimpCurveType curve_type) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + + if (curve->curve_type != curve_type) + { + gimp_data_freeze (GIMP_DATA (curve)); + + g_object_freeze_notify (G_OBJECT (curve)); + + curve->curve_type = curve_type; + + if (curve_type == GIMP_CURVE_SMOOTH) + { + gint i; + + g_free (curve->points); + + /* pick some points from the curve and make them control + * points + */ + curve->n_points = 9; + curve->points = g_new0 (GimpCurvePoint, 9); + + for (i = 0; i < curve->n_points; i++) + { + gint sample = i * (curve->n_samples - 1) / (curve->n_points - 1); + + curve->points[i].x = (gdouble) sample / + (gdouble) (curve->n_samples - 1); + curve->points[i].y = curve->samples[sample]; + curve->points[i].type = GIMP_CURVE_POINT_SMOOTH; + } + + g_object_notify (G_OBJECT (curve), "n-points"); + g_object_notify (G_OBJECT (curve), "points"); + g_object_notify (G_OBJECT (curve), "point-types"); + } + else + { + gimp_curve_clear_points (curve); + } + + g_object_notify (G_OBJECT (curve), "curve-type"); + + g_object_thaw_notify (G_OBJECT (curve)); + + gimp_data_thaw (GIMP_DATA (curve)); + } +} + +GimpCurveType +gimp_curve_get_curve_type (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_SMOOTH); + + return curve->curve_type; +} + +gint +gimp_curve_get_n_points (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), 0); + + return curve->n_points; +} + +void +gimp_curve_set_n_samples (GimpCurve *curve, + gint n_samples) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (n_samples >= 256); + g_return_if_fail (n_samples <= 4096); + + if (n_samples != curve->n_samples) + { + gint i; + + g_object_freeze_notify (G_OBJECT (curve)); + + curve->n_samples = n_samples; + g_object_notify (G_OBJECT (curve), "n-samples"); + + curve->samples = g_renew (gdouble, curve->samples, curve->n_samples); + + for (i = 0; i < curve->n_samples; i++) + curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1); + + g_object_notify (G_OBJECT (curve), "samples"); + + if (curve->curve_type == GIMP_CURVE_FREE) + curve->identity = TRUE; + + g_object_thaw_notify (G_OBJECT (curve)); + } +} + +gint +gimp_curve_get_n_samples (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), 0); + + return curve->n_samples; +} + +gint +gimp_curve_get_point_at (GimpCurve *curve, + gdouble x) +{ + gint closest_point = -1; + gdouble distance = EPSILON; + gint i; + + g_return_val_if_fail (GIMP_IS_CURVE (curve), -1); + + for (i = 0; i < curve->n_points; i++) + { + gdouble point_distance; + + point_distance = fabs (x - curve->points[i].x); + + if (point_distance <= distance) + { + closest_point = i; + distance = point_distance; + } + } + + return closest_point; +} + +gint +gimp_curve_get_closest_point (GimpCurve *curve, + gdouble x, + gdouble y, + gdouble max_distance) +{ + gint closest_point = -1; + gdouble distance2 = G_MAXDOUBLE; + gint i; + + g_return_val_if_fail (GIMP_IS_CURVE (curve), -1); + + if (max_distance >= 0.0) + distance2 = SQR (max_distance); + + for (i = curve->n_points - 1; i >= 0; i--) + { + gdouble point_distance2; + + point_distance2 = SQR (x - curve->points[i].x) + + SQR (y - curve->points[i].y); + + if (point_distance2 <= distance2) + { + closest_point = i; + distance2 = point_distance2; + } + } + + return closest_point; +} + +gint +gimp_curve_add_point (GimpCurve *curve, + gdouble x, + gdouble y) +{ + GimpCurvePoint *points; + gint point; + + g_return_val_if_fail (GIMP_IS_CURVE (curve), -1); + + if (curve->curve_type == GIMP_CURVE_FREE) + return -1; + + x = CLAMP (x, 0.0, 1.0); + y = CLAMP (y, 0.0, 1.0); + + for (point = 0; point < curve->n_points; point++) + { + if (curve->points[point].x > x) + break; + } + + points = g_new0 (GimpCurvePoint, curve->n_points + 1); + + memcpy (points, curve->points, + point * sizeof (GimpCurvePoint)); + memcpy (points + point + 1, curve->points + point, + (curve->n_points - point) * sizeof (GimpCurvePoint)); + + points[point].x = x; + points[point].y = y; + points[point].type = GIMP_CURVE_POINT_SMOOTH; + + g_free (curve->points); + + curve->n_points++; + curve->points = points; + + g_object_notify (G_OBJECT (curve), "n-points"); + g_object_notify (G_OBJECT (curve), "points"); + g_object_notify (G_OBJECT (curve), "point-types"); + + gimp_data_dirty (GIMP_DATA (curve)); + + return point; +} + +void +gimp_curve_delete_point (GimpCurve *curve, + gint point) +{ + GimpCurvePoint *points; + + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + points = g_new0 (GimpCurvePoint, curve->n_points - 1); + + memcpy (points, curve->points, + point * sizeof (GimpCurvePoint)); + memcpy (points + point, curve->points + point + 1, + (curve->n_points - point - 1) * sizeof (GimpCurvePoint)); + + g_free (curve->points); + + curve->n_points--; + curve->points = points; + + g_object_notify (G_OBJECT (curve), "n-points"); + g_object_notify (G_OBJECT (curve), "points"); + g_object_notify (G_OBJECT (curve), "point-types"); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +void +gimp_curve_set_point (GimpCurve *curve, + gint point, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + curve->points[point].x = CLAMP (x, 0.0, 1.0); + curve->points[point].y = CLAMP (y, 0.0, 1.0); + + if (point > 0) + curve->points[point].x = MAX (x, curve->points[point - 1].x); + + if (point < curve->n_points - 1) + curve->points[point].x = MIN (x, curve->points[point + 1].x); + + g_object_notify (G_OBJECT (curve), "points"); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +void +gimp_curve_move_point (GimpCurve *curve, + gint point, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + curve->points[point].y = CLAMP (y, 0.0, 1.0); + + g_object_notify (G_OBJECT (curve), "points"); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +void +gimp_curve_get_point (GimpCurve *curve, + gint point, + gdouble *x, + gdouble *y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + if (x) *x = curve->points[point].x; + if (y) *y = curve->points[point].y; +} + +void +gimp_curve_set_point_type (GimpCurve *curve, + gint point, + GimpCurvePointType type) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + curve->points[point].type = type; + + g_object_notify (G_OBJECT (curve), "point-types"); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +GimpCurvePointType +gimp_curve_get_point_type (GimpCurve *curve, + gint point) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_POINT_SMOOTH); + g_return_val_if_fail (point >= 0 && point < curve->n_points, GIMP_CURVE_POINT_SMOOTH); + + return curve->points[point].type; +} + +void +gimp_curve_clear_points (GimpCurve *curve) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + + if (curve->points) + { + g_clear_pointer (&curve->points, g_free); + curve->n_points = 0; + + g_object_notify (G_OBJECT (curve), "n-points"); + g_object_notify (G_OBJECT (curve), "points"); + g_object_notify (G_OBJECT (curve), "point-types"); + + gimp_data_dirty (GIMP_DATA (curve)); + } +} + +void +gimp_curve_set_curve (GimpCurve *curve, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (x >= 0 && x <= 1.0); + g_return_if_fail (y >= 0 && y <= 1.0); + + if (curve->curve_type == GIMP_CURVE_SMOOTH) + return; + + curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y; + + g_object_notify (G_OBJECT (curve), "samples"); + + gimp_data_dirty (GIMP_DATA (curve)); +} + +/** + * gimp_curve_is_identity: + * @curve: a #GimpCurve object + * + * If this function returns %TRUE, then the curve maps each value to + * itself. If it returns %FALSE, then this assumption can not be made. + * + * Return value: %TRUE if the curve is an identity mapping, %FALSE otherwise. + **/ +gboolean +gimp_curve_is_identity (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), FALSE); + + return curve->identity; +} + +void +gimp_curve_get_uchar (GimpCurve *curve, + gint n_samples, + guchar *samples) +{ + gint i; + + g_return_if_fail (GIMP_IS_CURVE (curve)); + /* FIXME: support n_samples != curve->n_samples */ + g_return_if_fail (n_samples == curve->n_samples); + g_return_if_fail (samples != NULL); + + for (i = 0; i < curve->n_samples; i++) + samples[i] = curve->samples[i] * 255.999; +} + + +/* private functions */ + +static void +gimp_curve_calculate (GimpCurve *curve) +{ + gint i; + gint p1, p2, p3, p4; + + if (gimp_data_is_frozen (GIMP_DATA (curve))) + return; + + switch (curve->curve_type) + { + case GIMP_CURVE_SMOOTH: + /* Initialize boundary curve points */ + if (curve->n_points > 0) + { + GimpCurvePoint point; + gint boundary; + + point = curve->points[0]; + boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1)); + + for (i = 0; i < boundary; i++) + curve->samples[i] = point.y; + + point = curve->points[curve->n_points - 1]; + boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1)); + + for (i = boundary; i < curve->n_samples; i++) + curve->samples[i] = point.y; + } + + for (i = 0; i < curve->n_points - 1; i++) + { + p1 = MAX (i - 1, 0); + p2 = i; + p3 = i + 1; + p4 = MIN (i + 2, curve->n_points - 1); + + if (curve->points[p2].type == GIMP_CURVE_POINT_CORNER) + p1 = p2; + + if (curve->points[p3].type == GIMP_CURVE_POINT_CORNER) + p4 = p3; + + gimp_curve_plot (curve, p1, p2, p3, p4); + } + + /* ensure that the control points are used exactly */ + for (i = 0; i < curve->n_points; i++) + { + gdouble x = curve->points[i].x; + gdouble y = curve->points[i].y; + + curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y; + } + + g_object_notify (G_OBJECT (curve), "samples"); + break; + + case GIMP_CURVE_FREE: + break; + } +} + +/* + * This function calculates the curve values between the control points + * p2 and p3, taking the potentially existing neighbors p1 and p4 into + * account. + * + * This function uses a cubic bezier curve for the individual segments and + * calculates the necessary intermediate control points depending on the + * neighbor curve control points. + */ +static void +gimp_curve_plot (GimpCurve *curve, + gint p1, + gint p2, + gint p3, + gint p4) +{ + gint i; + gdouble x0, x3; + gdouble y0, y1, y2, y3; + gdouble dx, dy; + gdouble slope; + + /* the outer control points for the bezier curve. */ + x0 = curve->points[p2].x; + y0 = curve->points[p2].y; + x3 = curve->points[p3].x; + y3 = curve->points[p3].y; + + /* + * the x values of the inner control points are fixed at + * x1 = 2/3*x0 + 1/3*x3 and x2 = 1/3*x0 + 2/3*x3 + * this ensures that the x values increase linearly with the + * parameter t and enables us to skip the calculation of the x + * values altogether - just calculate y(t) evenly spaced. + */ + + dx = x3 - x0; + dy = y3 - y0; + + if (dx <= EPSILON) + { + gint index; + + index = ROUND (x0 * (gdouble) (curve->n_samples - 1)); + + curve->samples[index] = y3; + + return; + } + + if (p1 == p2 && p3 == p4) + { + /* No information about the neighbors, + * calculate y1 and y2 to get a straight line + */ + y1 = y0 + dy / 3.0; + y2 = y0 + dy * 2.0 / 3.0; + } + else if (p1 == p2 && p3 != p4) + { + /* only the right neighbor is available. Make the tangent at the + * right endpoint parallel to the line between the left endpoint + * and the right neighbor. Then point the tangent at the left towards + * the control handle of the right tangent, to ensure that the curve + * does not have an inflection point. + */ + slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0); + + y2 = y3 - slope * dx / 3.0; + y1 = y0 + (y2 - y0) / 2.0; + } + else if (p1 != p2 && p3 == p4) + { + /* see previous case */ + slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x); + + y1 = y0 + slope * dx / 3.0; + y2 = y3 + (y1 - y3) / 2.0; + } + else /* (p1 != p2 && p3 != p4) */ + { + /* Both neighbors are available. Make the tangents at the endpoints + * parallel to the line between the opposite endpoint and the adjacent + * neighbor. + */ + slope = (y3 - curve->points[p1].y) / (x3 - curve->points[p1].x); + + y1 = y0 + slope * dx / 3.0; + + slope = (curve->points[p4].y - y0) / (curve->points[p4].x - x0); + + y2 = y3 - slope * dx / 3.0; + } + + /* + * finally calculate the y(t) values for the given bezier values. We can + * use homogeneously distributed values for t, since x(t) increases linearly. + */ + for (i = 0; i <= ROUND (dx * (gdouble) (curve->n_samples - 1)); i++) + { + gdouble y, t; + gint index; + + t = i / dx / (gdouble) (curve->n_samples - 1); + y = y0 * (1-t) * (1-t) * (1-t) + + 3 * y1 * (1-t) * (1-t) * t + + 3 * y2 * (1-t) * t * t + + y3 * t * t * t; + + index = i + ROUND (x0 * (gdouble) (curve->n_samples - 1)); + + if (index < curve->n_samples) + curve->samples[index] = CLAMP (y, 0.0, 1.0); + } +} diff --git a/app/core/gimpcurve.h b/app/core/gimpcurve.h new file mode 100644 index 0000000..71654aa --- /dev/null +++ b/app/core/gimpcurve.h @@ -0,0 +1,124 @@ +/* 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 . + */ + +#ifndef __GIMP_CURVE_H__ +#define __GIMP_CURVE_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_CURVE (gimp_curve_get_type ()) +#define GIMP_CURVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CURVE, GimpCurve)) +#define GIMP_CURVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CURVE, GimpCurveClass)) +#define GIMP_IS_CURVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CURVE)) +#define GIMP_IS_CURVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CURVE)) +#define GIMP_CURVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CURVE, GimpCurveClass)) + + +typedef struct _GimpCurvePoint GimpCurvePoint; +typedef struct _GimpCurveClass GimpCurveClass; + +struct _GimpCurvePoint +{ + gdouble x; + gdouble y; + + GimpCurvePointType type; +}; + +struct _GimpCurve +{ + GimpData parent_instance; + + GimpCurveType curve_type; + + gint n_points; + GimpCurvePoint *points; + + gint n_samples; + gdouble *samples; + + gboolean identity; /* whether the curve is an identity mapping */ +}; + +struct _GimpCurveClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_curve_get_type (void) G_GNUC_CONST; + +GimpData * gimp_curve_new (const gchar *name); +GimpData * gimp_curve_get_standard (void); + +void gimp_curve_reset (GimpCurve *curve, + gboolean reset_type); + +void gimp_curve_set_curve_type (GimpCurve *curve, + GimpCurveType curve_type); +GimpCurveType gimp_curve_get_curve_type (GimpCurve *curve); + +gint gimp_curve_get_n_points (GimpCurve *curve); + +void gimp_curve_set_n_samples (GimpCurve *curve, + gint n_samples); +gint gimp_curve_get_n_samples (GimpCurve *curve); + +gint gimp_curve_get_point_at (GimpCurve *curve, + gdouble x); +gint gimp_curve_get_closest_point (GimpCurve *curve, + gdouble x, + gdouble y, + gdouble max_distance); + +gint gimp_curve_add_point (GimpCurve *curve, + gdouble x, + gdouble y); +void gimp_curve_delete_point (GimpCurve *curve, + gint point); +void gimp_curve_set_point (GimpCurve *curve, + gint point, + gdouble x, + gdouble y); +void gimp_curve_move_point (GimpCurve *curve, + gint point, + gdouble y); +void gimp_curve_get_point (GimpCurve *curve, + gint point, + gdouble *x, + gdouble *y); +void gimp_curve_set_point_type (GimpCurve *curve, + gint point, + GimpCurvePointType type); +GimpCurvePointType gimp_curve_get_point_type (GimpCurve *curve, + gint point); +void gimp_curve_clear_points (GimpCurve *curve); + +void gimp_curve_set_curve (GimpCurve *curve, + gdouble x, + gdouble y); + +gboolean gimp_curve_is_identity (GimpCurve *curve); + +void gimp_curve_get_uchar (GimpCurve *curve, + gint n_samples, + guchar *samples); + + +#endif /* __GIMP_CURVE_H__ */ diff --git a/app/core/gimpdashpattern.c b/app/core/gimpdashpattern.c new file mode 100644 index 0000000..e9fb9fc --- /dev/null +++ b/app/core/gimpdashpattern.c @@ -0,0 +1,355 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpdashpattern.c + * Copyright (C) 2003 Simon Budig + * Copyright (C) 2005 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 . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpdashpattern.h" + + +GType +gimp_dash_pattern_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpDashPattern", + (GBoxedCopyFunc) gimp_dash_pattern_copy, + (GBoxedFreeFunc) gimp_dash_pattern_free); + + return type; +} + +GArray * +gimp_dash_pattern_new_from_preset (GimpDashPreset preset) +{ + GArray *pattern; + gdouble dash; + gint i; + + pattern = g_array_new (FALSE, FALSE, sizeof (gdouble)); + + switch (preset) + { + case GIMP_DASH_CUSTOM: + g_warning ("GIMP_DASH_CUSTOM passed to gimp_dash_pattern_from_preset()"); + break; + + case GIMP_DASH_LINE: + break; + + case GIMP_DASH_LONG_DASH: + dash = 9.0; g_array_append_val (pattern, dash); + dash = 3.0; g_array_append_val (pattern, dash); + break; + + case GIMP_DASH_MEDIUM_DASH: + dash = 6.0; g_array_append_val (pattern, dash); + dash = 6.0; g_array_append_val (pattern, dash); + break; + + case GIMP_DASH_SHORT_DASH: + dash = 3.0; g_array_append_val (pattern, dash); + dash = 9.0; g_array_append_val (pattern, dash); + break; + + case GIMP_DASH_SPARSE_DOTS: + for (i = 0; i < 2; i++) + { + dash = 1.0; g_array_append_val (pattern, dash); + dash = 5.0; g_array_append_val (pattern, dash); + } + break; + + case GIMP_DASH_NORMAL_DOTS: + for (i = 0; i < 3; i++) + { + dash = 1.0; g_array_append_val (pattern, dash); + dash = 3.0; g_array_append_val (pattern, dash); + } + break; + + case GIMP_DASH_DENSE_DOTS: + for (i = 0; i < 12; i++) + { + dash = 1.0; g_array_append_val (pattern, dash); + } + break; + + case GIMP_DASH_STIPPLES: + for (i = 0; i < 24; i++) + { + dash = 0.5; g_array_append_val (pattern, dash); + } + break; + + case GIMP_DASH_DASH_DOT: + dash = 7.0; g_array_append_val (pattern, dash); + dash = 2.0; g_array_append_val (pattern, dash); + dash = 1.0; g_array_append_val (pattern, dash); + dash = 2.0; g_array_append_val (pattern, dash); + break; + + case GIMP_DASH_DASH_DOT_DOT: + dash = 7.0; g_array_append_val (pattern, dash); + for (i=0; i < 5; i++) + { + dash = 1.0; g_array_append_val (pattern, dash); + } + break; + } + + if (pattern->len < 2) + { + gimp_dash_pattern_free (pattern); + return NULL; + } + + return pattern; +} + +GArray * +gimp_dash_pattern_new_from_segments (const gboolean *segments, + gint n_segments, + gdouble dash_length) +{ + GArray *pattern; + gint i; + gint count; + gboolean state; + + g_return_val_if_fail (segments != NULL || n_segments == 0, NULL); + + pattern = g_array_new (FALSE, FALSE, sizeof (gdouble)); + + for (i = 0, count = 0, state = TRUE; i <= n_segments; i++) + { + if (i < n_segments && segments[i] == state) + { + count++; + } + else + { + gdouble l = (dash_length * count) / n_segments; + + g_array_append_val (pattern, l); + + count = 1; + state = ! state; + } + } + + if (pattern->len < 2) + { + gimp_dash_pattern_free (pattern); + return NULL; + } + + return pattern; +} + +void +gimp_dash_pattern_fill_segments (GArray *pattern, + gboolean *segments, + gint n_segments) +{ + gdouble factor; + gdouble sum; + gint i, j; + gboolean paint; + + g_return_if_fail (segments != NULL || n_segments == 0); + + if (pattern == NULL || pattern->len <= 1) + { + for (i = 0; i < n_segments; i++) + segments[i] = TRUE; + + return; + } + + for (i = 0, sum = 0; i < pattern->len ; i++) + { + sum += g_array_index (pattern, gdouble, i); + } + + factor = ((gdouble) n_segments) / sum; + + j = 0; + sum = g_array_index (pattern, gdouble, j) * factor; + paint = TRUE; + + for (i = 0; i < n_segments ; i++) + { + while (j < pattern->len && sum <= i) + { + paint = ! paint; + j++; + sum += g_array_index (pattern, gdouble, j) * factor; + } + + segments[i] = paint; + } +} + +GArray * +gimp_dash_pattern_from_value_array (GimpValueArray *value_array) +{ + if (value_array == NULL || gimp_value_array_length (value_array) == 0) + { + return NULL; + } + else + { + GArray *pattern; + gint length; + gint i; + + length = gimp_value_array_length (value_array); + + pattern = g_array_sized_new (FALSE, FALSE, sizeof (gdouble), length); + + for (i = 0; i < length; i++) + { + GValue *item = gimp_value_array_index (value_array, i); + gdouble val; + + g_return_val_if_fail (G_VALUE_HOLDS_DOUBLE (item), NULL); + + val = g_value_get_double (item); + + g_array_append_val (pattern, val); + } + + return pattern; + } +} + +GimpValueArray * +gimp_dash_pattern_to_value_array (GArray *pattern) +{ + if (pattern != NULL && pattern->len > 0) + { + GimpValueArray *value_array = gimp_value_array_new (pattern->len); + GValue item = G_VALUE_INIT; + gint i; + + g_value_init (&item, G_TYPE_DOUBLE); + + for (i = 0; i < pattern->len; i++) + { + g_value_set_double (&item, g_array_index (pattern, gdouble, i)); + gimp_value_array_append (value_array, &item); + } + + g_value_unset (&item); + + return value_array; + } + + return NULL; +} + +GArray * +gimp_dash_pattern_from_double_array (gint n_dashes, + const gdouble *dashes) +{ + if (n_dashes > 0 && dashes != NULL) + { + GArray *pattern; + gint i; + + pattern = g_array_new (FALSE, FALSE, sizeof (gdouble)); + + for (i = 0; i < n_dashes; i++) + { + if (dashes[i] >= 0.0) + { + g_array_append_val (pattern, dashes[i]); + } + else + { + g_array_free (pattern, TRUE); + return NULL; + } + } + + return pattern; + } + + return NULL; +} + +gdouble * +gimp_dash_pattern_to_double_array (GArray *pattern, + gint *n_dashes) +{ + if (pattern != NULL && pattern->len > 0) + { + gdouble *dashes; + gint i; + + dashes = g_new0 (gdouble, pattern->len); + + for (i = 0; i < pattern->len; i++) + { + dashes[i] = g_array_index (pattern, gdouble, i); + } + + if (n_dashes) + *n_dashes = pattern->len; + + return dashes; + } + + return NULL; +} + +GArray * +gimp_dash_pattern_copy (GArray *pattern) +{ + if (pattern) + { + GArray *copy; + gint i; + + copy = g_array_sized_new (FALSE, FALSE, sizeof (gdouble), pattern->len); + + for (i = 0; i < pattern->len; i++) + g_array_append_val (copy, g_array_index (pattern, gdouble, i)); + + return copy; + } + + return NULL; +} + +void +gimp_dash_pattern_free (GArray *pattern) +{ + if (pattern) + g_array_free (pattern, TRUE); +} diff --git a/app/core/gimpdashpattern.h b/app/core/gimpdashpattern.h new file mode 100644 index 0000000..005c516 --- /dev/null +++ b/app/core/gimpdashpattern.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpdashpattern.h + * Copyright (C) 2003 Simon Budig + * Copyright (C) 2005 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 . + */ + +#ifndef __GIMP_DASH_PATTERN_H__ +#define __GIMP_DASH_PATTERN_H__ + + +#define GIMP_TYPE_DASH_PATTERN (gimp_dash_pattern_get_type ()) +#define GIMP_VALUE_HOLDS_DASH_PATTERN(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_DASH_PATTERN)) + + +GType gimp_dash_pattern_get_type (void) G_GNUC_CONST; + +GArray * gimp_dash_pattern_new_from_preset (GimpDashPreset preset); +GArray * gimp_dash_pattern_new_from_segments (const gboolean *segments, + gint n_segments, + gdouble dash_length); + +void gimp_dash_pattern_fill_segments (GArray *pattern, + gboolean *segments, + gint n_segments); + +GArray * gimp_dash_pattern_from_value_array (GimpValueArray *value_array); +GimpValueArray * gimp_dash_pattern_to_value_array (GArray *pattern); + +GArray * gimp_dash_pattern_from_double_array (gint n_dashes, + const gdouble *dashes); +gdouble * gimp_dash_pattern_to_double_array (GArray *pattern, + gint *n_dashes); + +GArray * gimp_dash_pattern_copy (GArray *pattern); +void gimp_dash_pattern_free (GArray *pattern); + + +#endif /* __GIMP_DASH_PATTERN_H__ */ diff --git a/app/core/gimpdata.c b/app/core/gimpdata.c new file mode 100644 index 0000000..e4640f3 --- /dev/null +++ b/app/core/gimpdata.c @@ -0,0 +1,1245 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdata.c + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpdata.h" +#include "gimpmarshal.h" +#include "gimptag.h" +#include "gimptagged.h" + +#include "gimp-intl.h" + + +enum +{ + DIRTY, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_FILE, + PROP_WRITABLE, + PROP_DELETABLE, + PROP_MIME_TYPE +}; + + +struct _GimpDataPrivate +{ + GFile *file; + GQuark mime_type; + guint writable : 1; + guint deletable : 1; + guint dirty : 1; + guint internal : 1; + gint freeze_count; + gint64 mtime; + + /* Identifies the GimpData object across sessions. Used when there + * is not a filename associated with the object. + */ + gchar *identifier; + + GList *tags; +}; + +#define GIMP_DATA_GET_PRIVATE(obj) (((GimpData *) (obj))->priv) + + +static void gimp_data_tagged_iface_init (GimpTaggedInterface *iface); + +static void gimp_data_constructed (GObject *object); +static void gimp_data_finalize (GObject *object); +static void gimp_data_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_data_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_data_name_changed (GimpObject *object); +static gint64 gimp_data_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_data_is_name_editable (GimpViewable *viewable); + +static void gimp_data_real_dirty (GimpData *data); +static GimpData * gimp_data_real_duplicate (GimpData *data); +static gint gimp_data_real_compare (GimpData *data1, + GimpData *data2); + +static gboolean gimp_data_add_tag (GimpTagged *tagged, + GimpTag *tag); +static gboolean gimp_data_remove_tag (GimpTagged *tagged, + GimpTag *tag); +static GList * gimp_data_get_tags (GimpTagged *tagged); +static gchar * gimp_data_get_identifier (GimpTagged *tagged); +static gchar * gimp_data_get_checksum (GimpTagged *tagged); + + +G_DEFINE_TYPE_WITH_CODE (GimpData, gimp_data, GIMP_TYPE_VIEWABLE, + G_ADD_PRIVATE (GimpData) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_data_tagged_iface_init)) + +#define parent_class gimp_data_parent_class + +static guint data_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_data_class_init (GimpDataClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + data_signals[DIRTY] = + g_signal_new ("dirty", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDataClass, dirty), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->constructed = gimp_data_constructed; + object_class->finalize = gimp_data_finalize; + object_class->set_property = gimp_data_set_property; + object_class->get_property = gimp_data_get_property; + + gimp_object_class->name_changed = gimp_data_name_changed; + gimp_object_class->get_memsize = gimp_data_get_memsize; + + viewable_class->name_editable = TRUE; + viewable_class->is_name_editable = gimp_data_is_name_editable; + + klass->dirty = gimp_data_real_dirty; + klass->save = NULL; + klass->get_extension = NULL; + klass->copy = NULL; + klass->duplicate = gimp_data_real_duplicate; + klass->compare = gimp_data_real_compare; + + g_object_class_install_property (object_class, PROP_FILE, + g_param_spec_object ("file", NULL, NULL, + G_TYPE_FILE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WRITABLE, + g_param_spec_boolean ("writable", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_DELETABLE, + g_param_spec_boolean ("deletable", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_MIME_TYPE, + g_param_spec_string ("mime-type", NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_data_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->add_tag = gimp_data_add_tag; + iface->remove_tag = gimp_data_remove_tag; + iface->get_tags = gimp_data_get_tags; + iface->get_identifier = gimp_data_get_identifier; + iface->get_checksum = gimp_data_get_checksum; +} + +static void +gimp_data_init (GimpData *data) +{ + GimpDataPrivate *private = gimp_data_get_instance_private (data); + + data->priv = private; + + private->writable = TRUE; + private->deletable = TRUE; + private->dirty = TRUE; + + /* freeze the data object during construction */ + gimp_data_freeze (data); +} + +static void +gimp_data_constructed (GObject *object) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + if (! GIMP_DATA_GET_CLASS (object)->save) + private->writable = FALSE; + + gimp_data_thaw (GIMP_DATA (object)); +} + +static void +gimp_data_finalize (GObject *object) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (object); + + g_clear_object (&private->file); + + if (private->tags) + { + g_list_free_full (private->tags, (GDestroyNotify) g_object_unref); + private->tags = NULL; + } + + g_clear_pointer (&private->identifier, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_data_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpData *data = GIMP_DATA (object); + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (data); + + switch (property_id) + { + case PROP_FILE: + gimp_data_set_file (data, + g_value_get_object (value), + private->writable, + private->deletable); + break; + + case PROP_WRITABLE: + private->writable = g_value_get_boolean (value); + break; + + case PROP_DELETABLE: + private->deletable = g_value_get_boolean (value); + break; + + case PROP_MIME_TYPE: + if (g_value_get_string (value)) + private->mime_type = g_quark_from_string (g_value_get_string (value)); + else + private->mime_type = 0; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_data_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (object); + + switch (property_id) + { + case PROP_FILE: + g_value_set_object (value, private->file); + break; + + case PROP_WRITABLE: + g_value_set_boolean (value, private->writable); + break; + + case PROP_DELETABLE: + g_value_set_boolean (value, private->deletable); + break; + + case PROP_MIME_TYPE: + g_value_set_string (value, g_quark_to_string (private->mime_type)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_data_name_changed (GimpObject *object) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (object); + + private->dirty = TRUE; + + if (GIMP_OBJECT_CLASS (parent_class)->name_changed) + GIMP_OBJECT_CLASS (parent_class)->name_changed (object); +} + +static gint64 +gimp_data_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (object); + gint64 memsize = 0; + + memsize += gimp_g_object_get_memsize (G_OBJECT (private->file)); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_data_is_name_editable (GimpViewable *viewable) +{ + return gimp_data_is_writable (GIMP_DATA (viewable)) && + ! gimp_data_is_internal (GIMP_DATA (viewable)); +} + +static void +gimp_data_real_dirty (GimpData *data) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (data)); + + /* Emit the "name-changed" to signal general dirtiness, our name + * changed implementation will also set the "dirty" flag to TRUE. + */ + gimp_object_name_changed (GIMP_OBJECT (data)); +} + +static GimpData * +gimp_data_real_duplicate (GimpData *data) +{ + if (GIMP_DATA_GET_CLASS (data)->copy) + { + GimpData *new = g_object_new (G_OBJECT_TYPE (data), NULL); + + gimp_data_copy (new, data); + + return new; + } + + return NULL; +} + +static gint +gimp_data_real_compare (GimpData *data1, + GimpData *data2) +{ + GimpDataPrivate *private1 = GIMP_DATA_GET_PRIVATE (data1); + GimpDataPrivate *private2 = GIMP_DATA_GET_PRIVATE (data2); + + /* move the internal objects (like the FG -> BG) gradient) to the top */ + if (private1->internal != private2->internal) + return private1->internal ? -1 : 1; + + /* keep user-deletable objects above system resource files */ + if (private1->deletable != private2->deletable) + return private1->deletable ? -1 : 1; + + return gimp_object_name_collate ((GimpObject *) data1, + (GimpObject *) data2); +} + +static gboolean +gimp_data_add_tag (GimpTagged *tagged, + GimpTag *tag) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (tagged); + GList *list; + + for (list = private->tags; list; list = g_list_next (list)) + { + GimpTag *this = GIMP_TAG (list->data); + + if (gimp_tag_equals (tag, this)) + return FALSE; + } + + private->tags = g_list_prepend (private->tags, g_object_ref (tag)); + + return TRUE; +} + +static gboolean +gimp_data_remove_tag (GimpTagged *tagged, + GimpTag *tag) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (tagged); + GList *list; + + for (list = private->tags; list; list = g_list_next (list)) + { + GimpTag *this = GIMP_TAG (list->data); + + if (gimp_tag_equals (tag, this)) + { + private->tags = g_list_delete_link (private->tags, list); + g_object_unref (this); + return TRUE; + } + } + + return FALSE; +} + +static GList * +gimp_data_get_tags (GimpTagged *tagged) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (tagged); + + return private->tags; +} + +static gchar * +gimp_data_get_identifier (GimpTagged *tagged) +{ + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (tagged); + gchar *identifier = NULL; + + if (private->file) + { + const gchar *data_dir = gimp_data_directory (); + const gchar *gimp_dir = gimp_directory (); + gchar *path = g_file_get_path (private->file); + gchar *tmp; + + if (g_str_has_prefix (path, data_dir)) + { + tmp = g_strconcat ("${gimp_data_dir}", + path + strlen (data_dir), + NULL); + identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL); + g_free (tmp); + } + else if (g_str_has_prefix (path, gimp_dir)) + { + tmp = g_strconcat ("${gimp_dir}", + path + strlen (gimp_dir), + NULL); + identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL); + g_free (tmp); + } + else if (g_str_has_prefix (path, MYPAINT_BRUSHES_DIR)) + { + tmp = g_strconcat ("${mypaint_brushes_dir}", + path + strlen (MYPAINT_BRUSHES_DIR), + NULL); + identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL); + g_free (tmp); + } + else + { + identifier = g_filename_to_utf8 (path, -1, + NULL, NULL, NULL); + } + + if (! identifier) + { + g_printerr ("%s: failed to convert '%s' to utf8.\n", + G_STRFUNC, path); + identifier = g_strdup (path); + } + + g_free (path); + } + else if (private->internal) + { + identifier = g_strdup (private->identifier); + } + + return identifier; +} + +static gchar * +gimp_data_get_checksum (GimpTagged *tagged) +{ + return NULL; +} + +/** + * gimp_data_save: + * @data: object whose contents are to be saved. + * @error: return location for errors or %NULL + * + * Save the object. If the object is marked as "internal", nothing + * happens. Otherwise, it is saved to disk, using the file name set + * by gimp_data_set_file(). If the save is successful, the object is + * marked as not dirty. If not, an error message is returned using + * the @error argument. + * + * Returns: %TRUE if the object is internal or the save is successful. + **/ +gboolean +gimp_data_save (GimpData *data, + GError **error) +{ + GimpDataPrivate *private; + gboolean success = FALSE; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + g_return_val_if_fail (private->writable == TRUE, FALSE); + + if (private->internal) + { + private->dirty = FALSE; + return TRUE; + } + + g_return_val_if_fail (G_IS_FILE (private->file), FALSE); + + if (GIMP_DATA_GET_CLASS (data)->save) + { + GOutputStream *output; + + output = G_OUTPUT_STREAM (g_file_replace (private->file, + NULL, FALSE, G_FILE_CREATE_NONE, + NULL, error)); + + if (output) + { + success = GIMP_DATA_GET_CLASS (data)->save (data, output, error); + + if (success) + { + if (! g_output_stream_close (output, NULL, error)) + { + g_prefix_error (error, + _("Error saving '%s': "), + gimp_file_get_utf8_name (private->file)); + success = FALSE; + } + } + else + { + GCancellable *cancellable = g_cancellable_new (); + + g_cancellable_cancel (cancellable); + if (error && *error) + { + g_prefix_error (error, + _("Error saving '%s': "), + gimp_file_get_utf8_name (private->file)); + } + else + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_WRITE, + _("Error saving '%s'"), + gimp_file_get_utf8_name (private->file)); + } + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + } + + g_object_unref (output); + } + } + + if (success) + { + GFileInfo *info = g_file_query_info (private->file, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (info) + { + private->mtime = + g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + g_object_unref (info); + } + + private->dirty = FALSE; + } + + return success; +} + +/** + * gimp_data_dirty: + * @data: a #GimpData object. + * + * Marks @data as dirty. Unless the object is frozen, this causes + * its preview to be invalidated, and emits a "dirty" signal. If the + * object is frozen, the function has no effect. + **/ +void +gimp_data_dirty (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + if (private->freeze_count == 0) + g_signal_emit (data, data_signals[DIRTY], 0); +} + +void +gimp_data_clean (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + private->dirty = FALSE; +} + +gboolean +gimp_data_is_dirty (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->dirty; +} + +/** + * gimp_data_freeze: + * @data: a #GimpData object. + * + * Increments the freeze count for the object. A positive freeze count + * prevents the object from being treated as dirty. Any call to this + * function must be followed eventually by a call to gimp_data_thaw(). + **/ +void +gimp_data_freeze (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + private->freeze_count++; +} + +/** + * gimp_data_thaw: + * @data: a #GimpData object. + * + * Decrements the freeze count for the object. If the freeze count + * drops to zero, the object is marked as dirty, and the "dirty" + * signal is emitted. It is an error to call this function without + * having previously called gimp_data_freeze(). + **/ +void +gimp_data_thaw (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + g_return_if_fail (private->freeze_count > 0); + + private->freeze_count--; + + if (private->freeze_count == 0) + gimp_data_dirty (data); +} + +gboolean +gimp_data_is_frozen (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->freeze_count > 0; +} + +/** + * gimp_data_delete_from_disk: + * @data: a #GimpData object. + * @error: return location for errors or %NULL + * + * Deletes the object from disk. If the object is marked as "internal", + * nothing happens. Otherwise, if the file exists whose name has been + * set by gimp_data_set_file(), it is deleted. Obviously this is + * a potentially dangerous function, which should be used with care. + * + * Returns: %TRUE if the object is internal to Gimp, or the deletion is + * successful. + **/ +gboolean +gimp_data_delete_from_disk (GimpData *data, + GError **error) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + g_return_val_if_fail (private->file != NULL, FALSE); + g_return_val_if_fail (private->deletable == TRUE, FALSE); + + if (private->internal) + return TRUE; + + return g_file_delete (private->file, NULL, error); +} + +const gchar * +gimp_data_get_extension (GimpData *data) +{ + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + + if (GIMP_DATA_GET_CLASS (data)->get_extension) + return GIMP_DATA_GET_CLASS (data)->get_extension (data); + + return NULL; +} + +/** + * gimp_data_set_file: + * @data: A #GimpData object + * @file: File to assign to @data. + * @writable: %TRUE if we want to be able to write to this file. + * @deletable: %TRUE if we want to be able to delete this file. + * + * This function assigns a file to @data, and sets some flags + * according to the properties of the file. If @writable is %TRUE, + * and the user has permission to write or overwrite the requested + * file name, and a "save" method exists for @data's object type, then + * @data is marked as writable. + **/ +void +gimp_data_set_file (GimpData *data, + GFile *file, + gboolean writable, + gboolean deletable) +{ + GimpDataPrivate *private; + gchar *path; + + g_return_if_fail (GIMP_IS_DATA (data)); + g_return_if_fail (G_IS_FILE (file)); + + path = g_file_get_path (file); + + g_return_if_fail (path != NULL); + g_return_if_fail (g_path_is_absolute (path)); + + g_free (path); + + private = GIMP_DATA_GET_PRIVATE (data); + + if (private->internal) + return; + + g_set_object (&private->file, file); + + private->writable = FALSE; + private->deletable = FALSE; + + /* if the data is supposed to be writable or deletable, + * still check if it really is + */ + if (writable || deletable) + { + GFileInfo *info; + + if (g_file_query_exists (private->file, NULL)) /* check if it exists */ + { + info = g_file_query_info (private->file, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + /* and we can write it */ + if (info) + { + if (g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + { + private->writable = writable ? TRUE : FALSE; + private->deletable = deletable ? TRUE : FALSE; + } + + g_object_unref (info); + } + } + else /* OR it doesn't exist */ + { + GFile *parent = g_file_get_parent (private->file); + + info = g_file_query_info (parent, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + /* and we can write to its parent directory */ + if (info) + { + if (g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + { + private->writable = writable ? TRUE : FALSE; + private->deletable = deletable ? TRUE : FALSE; + } + + g_object_unref (info); + } + + g_object_unref (parent); + } + + /* if we can't save, we are not writable */ + if (! GIMP_DATA_GET_CLASS (data)->save) + private->writable = FALSE; + } +} + +GFile * +gimp_data_get_file (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->file; +} + +/** + * gimp_data_create_filename: + * @data: a #Gimpdata object. + * @dest_dir: directory in which to create a file name. + * + * This function creates a unique file name to be used for saving + * a representation of @data in the directory @dest_dir. If the + * user does not have write permission in @dest_dir, then @data + * is marked as "not writable", so you should check on this before + * assuming that @data can be saved. + **/ +void +gimp_data_create_filename (GimpData *data, + GFile *dest_dir) +{ + GimpDataPrivate *private; + gchar *safename; + gchar *basename; + GFile *file; + gint i; + gint unum = 1; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_DATA (data)); + g_return_if_fail (G_IS_FILE (dest_dir)); + + private = GIMP_DATA_GET_PRIVATE (data); + + if (private->internal) + return; + + safename = g_strstrip (g_strdup (gimp_object_get_name (data))); + + if (safename[0] == '.') + safename[0] = '-'; + + for (i = 0; safename[i]; i++) + if (strchr ("\\/*?\"`'<>{}|\n\t ;:$^&", safename[i])) + safename[i] = '-'; + + basename = g_strconcat (safename, gimp_data_get_extension (data), NULL); + + file = g_file_get_child_for_display_name (dest_dir, basename, &error); + g_free (basename); + + if (! file) + { + g_warning ("gimp_data_create_filename:\n" + "g_file_get_child_for_display_name() failed for '%s': %s", + gimp_object_get_name (data), error->message); + g_clear_error (&error); + g_free (safename); + return; + } + + while (g_file_query_exists (file, NULL)) + { + g_object_unref (file); + + basename = g_strdup_printf ("%s-%d%s", + safename, + unum++, + gimp_data_get_extension (data)); + + file = g_file_get_child_for_display_name (dest_dir, basename, NULL); + g_free (basename); + } + + g_free (safename); + + gimp_data_set_file (data, file, TRUE, TRUE); + + g_object_unref (file); +} + +static const gchar *tag_blacklist[] = { "brushes", + "dynamics", + "patterns", + "palettes", + "gradients", + "tool-presets" }; + +/** + * gimp_data_set_folder_tags: + * @data: a #Gimpdata object. + * @top_directory: the top directory of the currently processed data + * hierarchy. + * + * Sets tags based on all folder names below top_directory. So if the + * data's filename is e.g. + * /home/foo/.config/GIMP/X.Y/brushes/Flowers/Roses/rose.gbr, it will + * add "Flowers" and "Roses" tags. + * + * if the top directory (as passed, or as derived from the data's + * filename) does not end with one of the default data directory names + * (brushes, patterns etc), its name will be added as tag too. + **/ +void +gimp_data_set_folder_tags (GimpData *data, + GFile *top_directory) +{ + GimpDataPrivate *private; + gchar *tmp; + gchar *dirname; + gchar *top_path; + + g_return_if_fail (GIMP_IS_DATA (data)); + g_return_if_fail (G_IS_FILE (top_directory)); + + private = GIMP_DATA_GET_PRIVATE (data); + + if (private->internal) + return; + + g_return_if_fail (private->file != NULL); + + tmp = g_file_get_path (private->file); + dirname = g_path_get_dirname (tmp); + g_free (tmp); + + top_path = g_file_get_path (top_directory); + + g_return_if_fail (g_str_has_prefix (dirname, top_path)); + + /* walk up the hierarchy and set each folder on the way as tag, + * except the top_directory + */ + while (strcmp (dirname, top_path)) + { + gchar *basename = g_path_get_basename (dirname); + GimpTag *tag = gimp_tag_new (basename); + + gimp_tag_set_internal (tag, TRUE); + gimp_tagged_add_tag (GIMP_TAGGED (data), tag); + g_object_unref (tag); + g_free (basename); + + tmp = g_path_get_dirname (dirname); + g_free (dirname); + dirname = tmp; + } + + g_free (top_path); + + if (dirname) + { + gchar *basename = g_path_get_basename (dirname); + gint i; + + for (i = 0; i < G_N_ELEMENTS (tag_blacklist); i++) + { + if (! strcmp (basename, tag_blacklist[i])) + break; + } + + if (i == G_N_ELEMENTS (tag_blacklist)) + { + GimpTag *tag = gimp_tag_new (basename); + + gimp_tag_set_internal (tag, TRUE); + gimp_tagged_add_tag (GIMP_TAGGED (data), tag); + g_object_unref (tag); + } + + g_free (basename); + g_free (dirname); + } +} + +const gchar * +gimp_data_get_mime_type (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + + private = GIMP_DATA_GET_PRIVATE (data); + + return g_quark_to_string (private->mime_type); +} + +gboolean +gimp_data_is_writable (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->writable; +} + +gboolean +gimp_data_is_deletable (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->deletable; +} + +void +gimp_data_set_mtime (GimpData *data, + gint64 mtime) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + private->mtime = mtime; +} + +gint64 +gimp_data_get_mtime (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), 0); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->mtime; +} + +gboolean +gimp_data_is_copyable (GimpData *data) +{ + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + return GIMP_DATA_GET_CLASS (data)->copy != NULL; +} + +/** + * gimp_data_copy: + * @data: a #GimpData object + * @src_data: the #GimpData object to copy from + * + * Copies @src_data to @data. Only the object data is copied: the + * name, file name, preview, etc. are not copied. + **/ +void +gimp_data_copy (GimpData *data, + GimpData *src_data) +{ + g_return_if_fail (GIMP_IS_DATA (data)); + g_return_if_fail (GIMP_IS_DATA (src_data)); + g_return_if_fail (GIMP_DATA_GET_CLASS (data)->copy != NULL); + g_return_if_fail (GIMP_DATA_GET_CLASS (data)->copy == + GIMP_DATA_GET_CLASS (src_data)->copy); + + if (data != src_data) + GIMP_DATA_GET_CLASS (data)->copy (data, src_data); +} + +gboolean +gimp_data_is_duplicatable (GimpData *data) +{ + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + if (GIMP_DATA_GET_CLASS (data)->duplicate == gimp_data_real_duplicate) + return gimp_data_is_copyable (data); + else + return GIMP_DATA_GET_CLASS (data)->duplicate != NULL; +} + +/** + * gimp_data_duplicate: + * @data: a #GimpData object + * + * Creates a copy of @data, if possible. Only the object data is + * copied: the newly created object is not automatically given an + * object name, file name, preview, etc. + * + * Returns: the newly created copy, or %NULL if @data cannot be copied. + **/ +GimpData * +gimp_data_duplicate (GimpData *data) +{ + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + + if (gimp_data_is_duplicatable (data)) + { + GimpData *new = GIMP_DATA_GET_CLASS (data)->duplicate (data); + GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (new); + + g_object_set (new, + "name", NULL, + "writable", GIMP_DATA_GET_CLASS (new)->save != NULL, + "deletable", TRUE, + NULL); + + g_clear_object (&private->file); + + return new; + } + + return NULL; +} + +/** + * gimp_data_make_internal: + * @data: a #GimpData object. + * + * Mark @data as "internal" to Gimp, which means that it will not be + * saved to disk. Note that if you do this, later calls to + * gimp_data_save() and gimp_data_delete_from_disk() will + * automatically return successfully without giving any warning. + * + * The identifier name shall be an untranslated globally unique string + * that identifies the internal object across sessions. + **/ +void +gimp_data_make_internal (GimpData *data, + const gchar *identifier) +{ + GimpDataPrivate *private; + + g_return_if_fail (GIMP_IS_DATA (data)); + + private = GIMP_DATA_GET_PRIVATE (data); + + g_clear_object (&private->file); + + g_free (private->identifier); + private->identifier = g_strdup (identifier); + + private->writable = FALSE; + private->deletable = FALSE; + private->internal = TRUE; +} + +gboolean +gimp_data_is_internal (GimpData *data) +{ + GimpDataPrivate *private; + + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + + private = GIMP_DATA_GET_PRIVATE (data); + + return private->internal; +} + +/** + * gimp_data_compare: + * @data1: a #GimpData object. + * @data2: another #GimpData object. + * + * Compares two data objects for use in sorting. Objects marked as + * "internal" come first, then user-writable objects, then system data + * files. In these three groups, the objects are sorted alphabetically + * by name, using gimp_object_name_collate(). + * + * Return value: -1 if @data1 compares before @data2, + * 0 if they compare equal, + * 1 if @data1 compares after @data2. + **/ +gint +gimp_data_compare (GimpData *data1, + GimpData *data2) +{ + g_return_val_if_fail (GIMP_IS_DATA (data1), 0); + g_return_val_if_fail (GIMP_IS_DATA (data2), 0); + g_return_val_if_fail (GIMP_DATA_GET_CLASS (data1)->compare == + GIMP_DATA_GET_CLASS (data2)->compare, 0); + + return GIMP_DATA_GET_CLASS (data1)->compare (data1, data2); +} + +/** + * gimp_data_error_quark: + * + * This function is used to implement the GIMP_DATA_ERROR macro. It + * shouldn't be called directly. + * + * Return value: the #GQuark to identify error in the GimpData error domain. + **/ +GQuark +gimp_data_error_quark (void) +{ + return g_quark_from_static_string ("gimp-data-error-quark"); +} diff --git a/app/core/gimpdata.h b/app/core/gimpdata.h new file mode 100644 index 0000000..44cd26c --- /dev/null +++ b/app/core/gimpdata.h @@ -0,0 +1,133 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdata.h + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DATA_H__ +#define __GIMP_DATA_H__ + + +#include "gimpviewable.h" + + +typedef enum +{ + GIMP_DATA_ERROR_OPEN, /* opening data file failed */ + GIMP_DATA_ERROR_READ, /* reading data file failed */ + GIMP_DATA_ERROR_WRITE, /* writing data file failed */ + GIMP_DATA_ERROR_DELETE /* deleting data file failed */ +} GimpDataError; + + +#define GIMP_TYPE_DATA (gimp_data_get_type ()) +#define GIMP_DATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DATA, GimpData)) +#define GIMP_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DATA, GimpDataClass)) +#define GIMP_IS_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DATA)) +#define GIMP_IS_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DATA)) +#define GIMP_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DATA, GimpDataClass)) + + +typedef struct _GimpDataPrivate GimpDataPrivate; +typedef struct _GimpDataClass GimpDataClass; + +struct _GimpData +{ + GimpViewable parent_instance; + + GimpDataPrivate *priv; +}; + +struct _GimpDataClass +{ + GimpViewableClass parent_class; + + /* signals */ + void (* dirty) (GimpData *data); + + /* virtual functions */ + gboolean (* save) (GimpData *data, + GOutputStream *output, + GError **error); + const gchar * (* get_extension) (GimpData *data); + void (* copy) (GimpData *data, + GimpData *src_data); + GimpData * (* duplicate) (GimpData *data); + gint (* compare) (GimpData *data1, + GimpData *data2); +}; + + +GType gimp_data_get_type (void) G_GNUC_CONST; + +gboolean gimp_data_save (GimpData *data, + GError **error); + +void gimp_data_dirty (GimpData *data); +void gimp_data_clean (GimpData *data); +gboolean gimp_data_is_dirty (GimpData *data); + +void gimp_data_freeze (GimpData *data); +void gimp_data_thaw (GimpData *data); +gboolean gimp_data_is_frozen (GimpData *data); + +gboolean gimp_data_delete_from_disk (GimpData *data, + GError **error); + +const gchar * gimp_data_get_extension (GimpData *data); + +void gimp_data_set_file (GimpData *data, + GFile *file, + gboolean writable, + gboolean deletable); +GFile * gimp_data_get_file (GimpData *data); + +void gimp_data_create_filename (GimpData *data, + GFile *dest_dir); + +void gimp_data_set_folder_tags (GimpData *data, + GFile *top_directory); + +const gchar * gimp_data_get_mime_type (GimpData *data); + +gboolean gimp_data_is_writable (GimpData *data); +gboolean gimp_data_is_deletable (GimpData *data); + +void gimp_data_set_mtime (GimpData *data, + gint64 mtime); +gint64 gimp_data_get_mtime (GimpData *data); + +gboolean gimp_data_is_copyable (GimpData *data); +void gimp_data_copy (GimpData *data, + GimpData *src_data); + +gboolean gimp_data_is_duplicatable (GimpData *data); +GimpData * gimp_data_duplicate (GimpData *data); + +void gimp_data_make_internal (GimpData *data, + const gchar *identifier); +gboolean gimp_data_is_internal (GimpData *data); + +gint gimp_data_compare (GimpData *data1, + GimpData *data2); + +#define GIMP_DATA_ERROR (gimp_data_error_quark ()) + +GQuark gimp_data_error_quark (void) G_GNUC_CONST; + + +#endif /* __GIMP_DATA_H__ */ diff --git a/app/core/gimpdatafactory.c b/app/core/gimpdatafactory.c new file mode 100644 index 0000000..5ee7114 --- /dev/null +++ b/app/core/gimpdatafactory.c @@ -0,0 +1,945 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdatafactory.c + * Copyright (C) 2001-2018 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpasyncset.h" +#include "gimpcancelable.h" +#include "gimpcontext.h" +#include "gimpdata.h" +#include "gimpdatafactory.h" +#include "gimplist.h" +#include "gimpuncancelablewaitable.h" +#include "gimpwaitable.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_GIMP, + PROP_DATA_TYPE, + PROP_PATH_PROPERTY_NAME, + PROP_WRITABLE_PROPERTY_NAME, + PROP_NEW_FUNC, + PROP_GET_STANDARD_FUNC +}; + + +struct _GimpDataFactoryPrivate +{ + Gimp *gimp; + + GType data_type; + GimpContainer *container; + GimpContainer *container_obsolete; + + gchar *path_property_name; + gchar *writable_property_name; + + GimpDataNewFunc data_new_func; + GimpDataGetStandardFunc data_get_standard_func; + + GimpAsyncSet *async_set; +}; + +#define GET_PRIVATE(obj) (((GimpDataFactory *) (obj))->priv) + + +static void gimp_data_factory_constructed (GObject *object); +static void gimp_data_factory_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_data_factory_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_data_factory_finalize (GObject *object); + +static gint64 gimp_data_factory_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_data_factory_real_data_save (GimpDataFactory *factory); +static void gimp_data_factory_real_data_cancel (GimpDataFactory *factory); +static GimpData * gimp_data_factory_real_data_duplicate (GimpDataFactory *factory, + GimpData *data); +static gboolean gimp_data_factory_real_data_delete (GimpDataFactory *factory, + GimpData *data, + gboolean delete_from_disk, + GError **error); + +static void gimp_data_factory_path_notify (GObject *object, + const GParamSpec *pspec, + GimpDataFactory *factory); +static GFile * gimp_data_factory_get_save_dir (GimpDataFactory *factory, + GError **error); + + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpDataFactory, gimp_data_factory, + GIMP_TYPE_OBJECT) + +#define parent_class gimp_data_factory_parent_class + + +static void +gimp_data_factory_class_init (GimpDataFactoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + object_class->constructed = gimp_data_factory_constructed; + object_class->set_property = gimp_data_factory_set_property; + object_class->get_property = gimp_data_factory_get_property; + object_class->finalize = gimp_data_factory_finalize; + + gimp_object_class->get_memsize = gimp_data_factory_get_memsize; + + klass->data_init = NULL; + klass->data_refresh = NULL; + klass->data_save = gimp_data_factory_real_data_save; + klass->data_cancel = gimp_data_factory_real_data_cancel; + klass->data_duplicate = gimp_data_factory_real_data_duplicate; + klass->data_delete = gimp_data_factory_real_data_delete; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_DATA_TYPE, + g_param_spec_gtype ("data-type", NULL, NULL, + GIMP_TYPE_DATA, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PATH_PROPERTY_NAME, + g_param_spec_string ("path-property-name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_WRITABLE_PROPERTY_NAME, + g_param_spec_string ("writable-property-name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_NEW_FUNC, + g_param_spec_pointer ("new-func", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_GET_STANDARD_FUNC, + g_param_spec_pointer ("get-standard-func", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_data_factory_init (GimpDataFactory *factory) +{ + factory->priv = gimp_data_factory_get_instance_private (factory); + + factory->priv->async_set = gimp_async_set_new (); +} + +static void +gimp_data_factory_constructed (GObject *object) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (priv->gimp)); + gimp_assert (g_type_is_a (priv->data_type, GIMP_TYPE_DATA)); + gimp_assert (GIMP_DATA_FACTORY_GET_CLASS (object)->data_init != NULL); + gimp_assert (GIMP_DATA_FACTORY_GET_CLASS (object)->data_refresh != NULL); + + priv->container = gimp_list_new (priv->data_type, TRUE); + gimp_list_set_sort_func (GIMP_LIST (priv->container), + (GCompareFunc) gimp_data_compare); + + priv->container_obsolete = gimp_list_new (priv->data_type, TRUE); + gimp_list_set_sort_func (GIMP_LIST (priv->container_obsolete), + (GCompareFunc) gimp_data_compare); +} + +static void +gimp_data_factory_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GIMP: + priv->gimp = g_value_get_object (value); /* don't ref */ + break; + + case PROP_DATA_TYPE: + priv->data_type = g_value_get_gtype (value); + break; + + case PROP_PATH_PROPERTY_NAME: + priv->path_property_name = g_value_dup_string (value); + break; + + case PROP_WRITABLE_PROPERTY_NAME: + priv->writable_property_name = g_value_dup_string (value); + break; + + case PROP_NEW_FUNC: + priv->data_new_func = g_value_get_pointer (value); + break; + + case PROP_GET_STANDARD_FUNC: + priv->data_get_standard_func = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_data_factory_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, priv->gimp); + break; + + case PROP_DATA_TYPE: + g_value_set_gtype (value, priv->data_type); + break; + + case PROP_PATH_PROPERTY_NAME: + g_value_set_string (value, priv->path_property_name); + break; + + case PROP_WRITABLE_PROPERTY_NAME: + g_value_set_string (value, priv->writable_property_name); + break; + + case PROP_NEW_FUNC: + g_value_set_pointer (value, priv->data_new_func); + break; + + case PROP_GET_STANDARD_FUNC: + g_value_set_pointer (value, priv->data_get_standard_func); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_data_factory_finalize (GObject *object) +{ + GimpDataFactory *factory = GIMP_DATA_FACTORY (object); + GimpDataFactoryPrivate *priv = GET_PRIVATE (object); + + if (priv->async_set) + { + gimp_data_factory_data_cancel (factory); + + g_clear_object (&priv->async_set); + } + + g_clear_object (&priv->container); + g_clear_object (&priv->container_obsolete); + + g_clear_pointer (&priv->path_property_name, g_free); + g_clear_pointer (&priv->writable_property_name, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_data_factory_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (priv->container), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (priv->container_obsolete), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_data_factory_real_data_save (GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + GList *dirty = NULL; + GList *list; + GFile *writable_dir; + GError *error = NULL; + + for (list = GIMP_LIST (priv->container)->queue->head; + list; + list = g_list_next (list)) + { + GimpData *data = list->data; + + if (gimp_data_is_dirty (data) && + gimp_data_is_writable (data)) + { + dirty = g_list_prepend (dirty, data); + } + } + + if (! dirty) + return; + + writable_dir = gimp_data_factory_get_save_dir (factory, &error); + + if (! writable_dir) + { + gimp_message (priv->gimp, NULL, GIMP_MESSAGE_ERROR, + _("Failed to save data:\n\n%s"), + error->message); + g_clear_error (&error); + + g_list_free (dirty); + + return; + } + + for (list = dirty; list; list = g_list_next (list)) + { + GimpData *data = list->data; + GError *error = NULL; + + if (! gimp_data_get_file (data)) + gimp_data_create_filename (data, writable_dir); + + if (factory->priv->gimp->be_verbose) + { + GFile *file = gimp_data_get_file (data); + + if (file) + g_print ("Writing dirty data '%s'\n", + gimp_file_get_utf8_name (file)); + } + + if (! gimp_data_save (data, &error)) + { + /* check if there actually was an error (no error + * means the data class does not implement save) + */ + if (error) + { + gimp_message (priv->gimp, NULL, GIMP_MESSAGE_ERROR, + _("Failed to save data:\n\n%s"), + error->message); + g_clear_error (&error); + } + } + } + + g_object_unref (writable_dir); + + g_list_free (dirty); +} + +static void +gimp_data_factory_real_data_cancel (GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + + gimp_cancelable_cancel (GIMP_CANCELABLE (priv->async_set)); + gimp_waitable_wait (GIMP_WAITABLE (priv->async_set)); +} + +static GimpData * +gimp_data_factory_real_data_duplicate (GimpDataFactory *factory, + GimpData *data) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + GimpData *new_data; + + new_data = gimp_data_duplicate (data); + + if (new_data) + { + const gchar *name = gimp_object_get_name (data); + gchar *ext; + gint copy_len; + gint number; + gchar *new_name; + + ext = strrchr (name, '#'); + copy_len = strlen (_("copy")); + + if ((strlen (name) >= copy_len && + strcmp (&name[strlen (name) - copy_len], _("copy")) == 0) || + (ext && (number = atoi (ext + 1)) > 0 && + ((gint) (log10 (number) + 1)) == strlen (ext + 1))) + { + /* don't have redundant "copy"s */ + new_name = g_strdup (name); + } + else + { + new_name = g_strdup_printf (_("%s copy"), name); + } + + gimp_object_take_name (GIMP_OBJECT (new_data), new_name); + + gimp_container_add (priv->container, GIMP_OBJECT (new_data)); + g_object_unref (new_data); + } + + return new_data; +} + +static gboolean +gimp_data_factory_real_data_delete (GimpDataFactory *factory, + GimpData *data, + gboolean delete_from_disk, + GError **error) +{ + if (delete_from_disk && gimp_data_get_file (data)) + return gimp_data_delete_from_disk (data, error); + + return TRUE; +} + + +/* public functions */ + +void +gimp_data_factory_data_init (GimpDataFactory *factory, + GimpContext *context, + gboolean no_data) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + gchar *signal_name; + + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + /* Always freeze() and thaw() the container around initialization, + * even if no_data, the thaw() will implicitly make GimpContext + * create the standard data that serves as fallback. + */ + gimp_container_freeze (priv->container); + + if (! no_data) + { + if (priv->gimp->be_verbose) + { + const gchar *name = gimp_object_get_name (factory); + + g_print ("Loading '%s' data\n", name ? name : "???"); + } + + GIMP_DATA_FACTORY_GET_CLASS (factory)->data_init (factory, context); + } + + gimp_container_thaw (priv->container); + + signal_name = g_strdup_printf ("notify::%s", priv->path_property_name); + g_signal_connect_object (priv->gimp->config, signal_name, + G_CALLBACK (gimp_data_factory_path_notify), + factory, 0); + g_free (signal_name); +} + +static void +gimp_data_factory_clean_cb (GimpDataFactory *factory, + GimpData *data, + gpointer user_data) +{ + if (gimp_data_is_dirty (data)) + gimp_data_clean (data); +} + +void +gimp_data_factory_data_clean (GimpDataFactory *factory) +{ + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + + gimp_data_factory_data_foreach (factory, TRUE, + gimp_data_factory_clean_cb, NULL); +} + +void +gimp_data_factory_data_refresh (GimpDataFactory *factory, + GimpContext *context) +{ + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + GIMP_DATA_FACTORY_GET_CLASS (factory)->data_refresh (factory, context); +} + +void +gimp_data_factory_data_save (GimpDataFactory *factory) +{ + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + + if (! gimp_container_is_empty (factory->priv->container)) + GIMP_DATA_FACTORY_GET_CLASS (factory)->data_save (factory); +} + +static void +gimp_data_factory_data_free_foreach (GimpDataFactory *factory, + GimpData *data, + gpointer user_data) +{ + gimp_container_remove (factory->priv->container, GIMP_OBJECT (data)); +} + +void +gimp_data_factory_data_free (GimpDataFactory *factory) +{ + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + + gimp_data_factory_data_cancel (factory); + + if (! gimp_container_is_empty (factory->priv->container)) + { + gimp_container_freeze (factory->priv->container); + + gimp_data_factory_data_foreach (factory, TRUE, + gimp_data_factory_data_free_foreach, + NULL); + + gimp_container_thaw (factory->priv->container); + } +} + +GimpAsyncSet * +gimp_data_factory_get_async_set (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + return factory->priv->async_set; +} + +gboolean +gimp_data_factory_data_wait (GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv; + GimpWaitable *waitable; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE); + + priv = GET_PRIVATE (factory); + + /* don't allow cancellation for now */ + waitable = gimp_uncancelable_waitable_new (GIMP_WAITABLE (priv->async_set)); + + gimp_wait (priv->gimp, waitable, + _("Loading fonts (this may take a while...)")); + + g_object_unref (waitable); + + return TRUE; +} + +void +gimp_data_factory_data_cancel (GimpDataFactory *factory) +{ + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + + GIMP_DATA_FACTORY_GET_CLASS (factory)->data_cancel (factory); +} + +gboolean +gimp_data_factory_has_data_new_func (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE); + + return factory->priv->data_new_func != NULL; +} + +GimpData * +gimp_data_factory_data_new (GimpDataFactory *factory, + GimpContext *context, + const gchar *name) +{ + GimpDataFactoryPrivate *priv; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + priv = GET_PRIVATE (factory); + + if (priv->data_new_func) + { + GimpData *data = priv->data_new_func (context, name); + + if (data) + { + gimp_container_add (priv->container, GIMP_OBJECT (data)); + g_object_unref (data); + + return data; + } + + g_warning ("%s: GimpDataFactory::data_new_func() returned NULL", + G_STRFUNC); + } + + return NULL; +} + +GimpData * +gimp_data_factory_data_get_standard (GimpDataFactory *factory, + GimpContext *context) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + if (factory->priv->data_get_standard_func) + return factory->priv->data_get_standard_func (context); + + return NULL; +} + +GimpData * +gimp_data_factory_data_duplicate (GimpDataFactory *factory, + GimpData *data) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + + return GIMP_DATA_FACTORY_GET_CLASS (factory)->data_duplicate (factory, data); +} + +gboolean +gimp_data_factory_data_delete (GimpDataFactory *factory, + GimpData *data, + gboolean delete_from_disk, + GError **error) +{ + GimpDataFactoryPrivate *priv; + gboolean retval = TRUE; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE); + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + priv = GET_PRIVATE (factory); + + if (gimp_container_have (priv->container, GIMP_OBJECT (data))) + { + g_object_ref (data); + + gimp_container_remove (priv->container, GIMP_OBJECT (data)); + + retval = GIMP_DATA_FACTORY_GET_CLASS (factory)->data_delete (factory, data, + delete_from_disk, + error); + + g_object_unref (data); + } + + return retval; +} + +gboolean +gimp_data_factory_data_save_single (GimpDataFactory *factory, + GimpData *data, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE); + g_return_val_if_fail (GIMP_IS_DATA (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (! gimp_data_is_dirty (data)) + return TRUE; + + if (! gimp_data_get_file (data)) + { + GFile *writable_dir; + GError *my_error = NULL; + + writable_dir = gimp_data_factory_get_save_dir (factory, &my_error); + + if (! writable_dir) + { + g_set_error (error, GIMP_DATA_ERROR, 0, + _("Failed to save data:\n\n%s"), + my_error->message); + g_clear_error (&my_error); + + return FALSE; + } + + gimp_data_create_filename (data, writable_dir); + + g_object_unref (writable_dir); + } + + if (! gimp_data_is_writable (data)) + return FALSE; + + if (factory->priv->gimp->be_verbose) + { + GFile *file = gimp_data_get_file (data); + + if (file) + g_print ("Writing dirty data '%s'\n", + gimp_file_get_utf8_name (file)); + } + + if (! gimp_data_save (data, error)) + { + /* check if there actually was an error (no error + * means the data class does not implement save) + */ + if (! error) + g_set_error (error, GIMP_DATA_ERROR, 0, + _("Failed to save data:\n\n%s"), + "Data class does not implement saving"); + + return FALSE; + } + + return TRUE; +} + +void +gimp_data_factory_data_foreach (GimpDataFactory *factory, + gboolean skip_internal, + GimpDataForeachFunc callback, + gpointer user_data) +{ + GList *list; + + g_return_if_fail (GIMP_IS_DATA_FACTORY (factory)); + g_return_if_fail (callback != NULL); + + list = GIMP_LIST (factory->priv->container)->queue->head; + + while (list) + { + GList *next = g_list_next (list); + + if (! (skip_internal && gimp_data_is_internal (list->data))) + callback (factory, list->data, user_data); + + list = next; + } +} + +Gimp * +gimp_data_factory_get_gimp (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + return factory->priv->gimp; +} + +GType +gimp_data_factory_get_data_type (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), G_TYPE_NONE); + + return gimp_container_get_children_type (factory->priv->container); +} + +GimpContainer * +gimp_data_factory_get_container (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + return factory->priv->container; +} + +GimpContainer * +gimp_data_factory_get_container_obsolete (GimpDataFactory *factory) +{ + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + return factory->priv->container_obsolete; +} + +GList * +gimp_data_factory_get_data_path (GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + gchar *path = NULL; + GList *list = NULL; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + g_object_get (priv->gimp->config, + priv->path_property_name, &path, + NULL); + + if (path) + { + list = gimp_config_path_expand_to_files (path, NULL); + g_free (path); + } + + return list; +} + +GList * +gimp_data_factory_get_data_path_writable (GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + gchar *path = NULL; + GList *list = NULL; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + + g_object_get (priv->gimp->config, + priv->writable_property_name, &path, + NULL); + + if (path) + { + list = gimp_config_path_expand_to_files (path, NULL); + g_free (path); + } + + return list; +} + + +/* private functions */ + +static void +gimp_data_factory_path_notify (GObject *object, + const GParamSpec *pspec, + GimpDataFactory *factory) +{ + GimpDataFactoryPrivate *priv = GET_PRIVATE (factory); + + gimp_set_busy (priv->gimp); + + gimp_data_factory_data_refresh (factory, gimp_get_user_context (priv->gimp)); + + gimp_unset_busy (priv->gimp); +} + +static GFile * +gimp_data_factory_get_save_dir (GimpDataFactory *factory, + GError **error) +{ + GList *path; + GList *writable_path; + GFile *writable_dir = NULL; + + path = gimp_data_factory_get_data_path (factory); + writable_path = gimp_data_factory_get_data_path_writable (factory); + + if (writable_path) + { + GList *list; + gboolean found_any = FALSE; + + for (list = writable_path; list; list = g_list_next (list)) + { + GList *found = g_list_find_custom (path, list->data, + (GCompareFunc) gimp_file_compare); + if (found) + { + GFile *dir = found->data; + + found_any = TRUE; + + if (g_file_query_file_type (dir, G_FILE_QUERY_INFO_NONE, + NULL) != G_FILE_TYPE_DIRECTORY) + { + /* error out only if this is the last chance */ + if (! list->next) + { + g_set_error (error, GIMP_DATA_ERROR, 0, + _("You have a writable data folder " + "configured (%s), but this folder does " + "not exist. Please create the folder or " + "fix your configuration in the " + "Preferences dialog's 'Folders' section."), + gimp_file_get_utf8_name (dir)); + } + } + else + { + writable_dir = g_object_ref (dir); + break; + } + } + } + + if (! writable_dir && ! found_any) + { + g_set_error (error, GIMP_DATA_ERROR, 0, + _("You have a writable data folder configured, but this " + "folder is not part of your data search path. You " + "probably edited the gimprc file manually, " + "please fix it in the Preferences dialog's 'Folders' " + "section.")); + } + } + else + { + g_set_error (error, GIMP_DATA_ERROR, 0, + _("You don't have any writable data folder configured.")); + } + + g_list_free_full (path, (GDestroyNotify) g_object_unref); + g_list_free_full (writable_path, (GDestroyNotify) g_object_unref); + + return writable_dir; +} diff --git a/app/core/gimpdatafactory.h b/app/core/gimpdatafactory.h new file mode 100644 index 0000000..47c57ae --- /dev/null +++ b/app/core/gimpdatafactory.h @@ -0,0 +1,125 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdatafactory.h + * Copyright (C) 2001-2018 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DATA_FACTORY_H__ +#define __GIMP_DATA_FACTORY_H__ + + +#include "gimpobject.h" + + +typedef GimpData * (* GimpDataNewFunc) (GimpContext *context, + const gchar *name); +typedef GimpData * (* GimpDataGetStandardFunc) (GimpContext *context); + +typedef void (* GimpDataForeachFunc) (GimpDataFactory *factory, + GimpData *data, + gpointer user_data); + + +#define GIMP_TYPE_DATA_FACTORY (gimp_data_factory_get_type ()) +#define GIMP_DATA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DATA_FACTORY, GimpDataFactory)) +#define GIMP_DATA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DATA_FACTORY, GimpDataFactoryClass)) +#define GIMP_IS_DATA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DATA_FACTORY)) +#define GIMP_IS_DATA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DATA_FACTORY)) +#define GIMP_DATA_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DATA_FACTORY, GimpDataFactoryClass)) + + +typedef struct _GimpDataFactoryPrivate GimpDataFactoryPrivate; +typedef struct _GimpDataFactoryClass GimpDataFactoryClass; + +struct _GimpDataFactory +{ + GimpObject parent_instance; + + GimpDataFactoryPrivate *priv; +}; + +struct _GimpDataFactoryClass +{ + GimpObjectClass parent_class; + + void (* data_init) (GimpDataFactory *factory, + GimpContext *context); + void (* data_refresh) (GimpDataFactory *factory, + GimpContext *context); + void (* data_save) (GimpDataFactory *factory); + + void (* data_cancel) (GimpDataFactory *factory); + + GimpData * (* data_duplicate) (GimpDataFactory *factory, + GimpData *data); + gboolean (* data_delete) (GimpDataFactory *factory, + GimpData *data, + gboolean delete_from_disk, + GError **error); +}; + + +GType gimp_data_factory_get_type (void) G_GNUC_CONST; + +void gimp_data_factory_data_init (GimpDataFactory *factory, + GimpContext *context, + gboolean no_data); +void gimp_data_factory_data_clean (GimpDataFactory *factory); +void gimp_data_factory_data_refresh (GimpDataFactory *factory, + GimpContext *context); +void gimp_data_factory_data_save (GimpDataFactory *factory); +void gimp_data_factory_data_free (GimpDataFactory *factory); + +GimpAsyncSet * gimp_data_factory_get_async_set (GimpDataFactory *factory); +gboolean gimp_data_factory_data_wait (GimpDataFactory *factory); +void gimp_data_factory_data_cancel (GimpDataFactory *factory); + +gboolean gimp_data_factory_has_data_new_func (GimpDataFactory *factory); +GimpData * gimp_data_factory_data_new (GimpDataFactory *factory, + GimpContext *context, + const gchar *name); +GimpData * gimp_data_factory_data_get_standard (GimpDataFactory *factory, + GimpContext *context); + +GimpData * gimp_data_factory_data_duplicate (GimpDataFactory *factory, + GimpData *data); +gboolean gimp_data_factory_data_delete (GimpDataFactory *factory, + GimpData *data, + gboolean delete_from_disk, + GError **error); + +gboolean gimp_data_factory_data_save_single (GimpDataFactory *factory, + GimpData *data, + GError **error); + +void gimp_data_factory_data_foreach (GimpDataFactory *factory, + gboolean skip_internal, + GimpDataForeachFunc callback, + gpointer user_data); + +Gimp * gimp_data_factory_get_gimp (GimpDataFactory *factory); +GType gimp_data_factory_get_data_type (GimpDataFactory *factory); +GimpContainer * gimp_data_factory_get_container (GimpDataFactory *factory); +GimpContainer * gimp_data_factory_get_container_obsolete + (GimpDataFactory *factory); +GList * gimp_data_factory_get_data_path (GimpDataFactory *factory); +GList * gimp_data_factory_get_data_path_writable + (GimpDataFactory *factory); + + + +#endif /* __GIMP_DATA_FACTORY_H__ */ diff --git a/app/core/gimpdataloaderfactory.c b/app/core/gimpdataloaderfactory.c new file mode 100644 index 0000000..01c4496 --- /dev/null +++ b/app/core/gimpdataloaderfactory.c @@ -0,0 +1,562 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdataloaderfactory.c + * Copyright (C) 2001-2018 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpcontainer.h" +#include "gimpdata.h" +#include "gimpdataloaderfactory.h" + +#include "gimp-intl.h" + + +/* Data files that have this string in their path are considered + * obsolete and are only kept around for backwards compatibility + */ +#define GIMP_OBSOLETE_DATA_DIR_NAME "gimp-obsolete-files" + + +typedef struct _GimpDataLoader GimpDataLoader; + +struct _GimpDataLoader +{ + gchar *name; + GimpDataLoadFunc load_func; + gchar *extension; + gboolean writable; +}; + + +struct _GimpDataLoaderFactoryPrivate +{ + GList *loaders; + GimpDataLoader *fallback; +}; + +#define GET_PRIVATE(obj) (((GimpDataLoaderFactory *) (obj))->priv) + + +static void gimp_data_loader_factory_finalize (GObject *object); + +static void gimp_data_loader_factory_data_init (GimpDataFactory *factory, + GimpContext *context); +static void gimp_data_loader_factory_data_refresh (GimpDataFactory *factory, + GimpContext *context); + +static GimpDataLoader * + gimp_data_loader_factory_get_loader (GimpDataFactory *factory, + GFile *file); + +static void gimp_data_loader_factory_load (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache); +static void gimp_data_loader_factory_load_directory (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache, + gboolean dir_writable, + GFile *directory, + GFile *top_directory); +static void gimp_data_loader_factory_load_data (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache, + gboolean dir_writable, + GFile *file, + GFileInfo *info, + GFile *top_directory); + +static GimpDataLoader * gimp_data_loader_new (const gchar *name, + GimpDataLoadFunc load_func, + const gchar *extension, + gboolean writable); +static void gimp_data_loader_free (GimpDataLoader *loader); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpDataLoaderFactory, gimp_data_loader_factory, + GIMP_TYPE_DATA_FACTORY) + +#define parent_class gimp_data_loader_factory_parent_class + + +static void +gimp_data_loader_factory_class_init (GimpDataLoaderFactoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataFactoryClass *factory_class = GIMP_DATA_FACTORY_CLASS (klass); + + object_class->finalize = gimp_data_loader_factory_finalize; + + factory_class->data_init = gimp_data_loader_factory_data_init; + factory_class->data_refresh = gimp_data_loader_factory_data_refresh; +} + +static void +gimp_data_loader_factory_init (GimpDataLoaderFactory *factory) +{ + factory->priv = gimp_data_loader_factory_get_instance_private (factory); +} + +static void +gimp_data_loader_factory_finalize (GObject *object) +{ + GimpDataLoaderFactoryPrivate *priv = GET_PRIVATE (object); + + g_list_free_full (priv->loaders, (GDestroyNotify) gimp_data_loader_free); + priv->loaders = NULL; + + g_clear_pointer (&priv->fallback, gimp_data_loader_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_data_loader_factory_data_init (GimpDataFactory *factory, + GimpContext *context) +{ + gimp_data_loader_factory_load (factory, context, NULL); +} + +static void +gimp_data_loader_factory_refresh_cache_add (GimpDataFactory *factory, + GimpData *data, + gpointer user_data) +{ + GFile *file = gimp_data_get_file (data); + + if (file) + { + GimpContainer *container = gimp_data_factory_get_container (factory); + GHashTable *cache = user_data; + GList *list; + + g_object_ref (data); + + gimp_container_remove (container, GIMP_OBJECT (data)); + + list = g_hash_table_lookup (cache, file); + list = g_list_prepend (list, data); + + g_hash_table_insert (cache, file, list); + } +} + +static gboolean +gimp_data_loader_factory_refresh_cache_remove (gpointer key, + gpointer value, + gpointer user_data) +{ + GList *list; + + for (list = value; list; list = list->next) + g_object_unref (list->data); + + g_list_free (value); + + return TRUE; +} + +static void +gimp_data_loader_factory_data_refresh (GimpDataFactory *factory, + GimpContext *context) +{ + GimpContainer *container = gimp_data_factory_get_container (factory); + GHashTable *cache; + + gimp_container_freeze (container); + + /* First, save all dirty data objects */ + gimp_data_factory_data_save (factory); + + cache = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal); + + gimp_data_factory_data_foreach (factory, TRUE, + gimp_data_loader_factory_refresh_cache_add, + cache); + + /* Now the cache contains a GFile => list-of-objects mapping of + * the old objects. So we should now traverse the directory and for + * each file load it only if its mtime is newer. + * + * Once a file was added, it is removed from the cache, so the only + * objects remaining there will be those that are not present on + * the disk (that have to be destroyed) + */ + gimp_data_loader_factory_load (factory, context, cache); + + /* Now all the data is loaded. Free what remains in the cache */ + g_hash_table_foreach_remove (cache, + gimp_data_loader_factory_refresh_cache_remove, + NULL); + + g_hash_table_destroy (cache); + + gimp_container_thaw (container); +} + + +/* public functions */ + +GimpDataFactory * +gimp_data_loader_factory_new (Gimp *gimp, + GType data_type, + const gchar *path_property_name, + const gchar *writable_property_name, + GimpDataNewFunc new_func, + GimpDataGetStandardFunc get_standard_func) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (g_type_is_a (data_type, GIMP_TYPE_DATA), NULL); + g_return_val_if_fail (path_property_name != NULL, NULL); + g_return_val_if_fail (writable_property_name != NULL, NULL); + + return g_object_new (GIMP_TYPE_DATA_LOADER_FACTORY, + "gimp", gimp, + "data-type", data_type, + "path-property-name", path_property_name, + "writable-property-name", writable_property_name, + "new-func", new_func, + "get-standard-func", get_standard_func, + NULL); +} + +void +gimp_data_loader_factory_add_loader (GimpDataFactory *factory, + const gchar *name, + GimpDataLoadFunc load_func, + const gchar *extension, + gboolean writable) +{ + GimpDataLoaderFactoryPrivate *priv; + GimpDataLoader *loader; + + g_return_if_fail (GIMP_IS_DATA_LOADER_FACTORY (factory)); + g_return_if_fail (name != NULL); + g_return_if_fail (load_func != NULL); + g_return_if_fail (extension != NULL); + + priv = GET_PRIVATE (factory); + + loader = gimp_data_loader_new (name, load_func, extension, writable); + + priv->loaders = g_list_append (priv->loaders, loader); +} + +void +gimp_data_loader_factory_add_fallback (GimpDataFactory *factory, + const gchar *name, + GimpDataLoadFunc load_func) +{ + GimpDataLoaderFactoryPrivate *priv; + + g_return_if_fail (GIMP_IS_DATA_LOADER_FACTORY (factory)); + g_return_if_fail (name != NULL); + g_return_if_fail (load_func != NULL); + + priv = GET_PRIVATE (factory); + + g_clear_pointer (&priv->fallback, gimp_data_loader_free); + + priv->fallback = gimp_data_loader_new (name, load_func, NULL, FALSE); +} + + +/* private functions */ + +static GimpDataLoader * +gimp_data_loader_factory_get_loader (GimpDataFactory *factory, + GFile *file) +{ + GimpDataLoaderFactoryPrivate *priv = GET_PRIVATE (factory); + GList *list; + + for (list = priv->loaders; list; list = g_list_next (list)) + { + GimpDataLoader *loader = list->data; + + if (gimp_file_has_extension (file, loader->extension)) + return loader; + } + + return priv->fallback; +} + +static void +gimp_data_loader_factory_load (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache) +{ + GList *path; + GList *writable_path; + GList *list; + + path = gimp_data_factory_get_data_path (factory); + writable_path = gimp_data_factory_get_data_path_writable (factory); + + for (list = path; list; list = g_list_next (list)) + { + gboolean dir_writable = FALSE; + + if (g_list_find_custom (writable_path, list->data, + (GCompareFunc) gimp_file_compare)) + dir_writable = TRUE; + + gimp_data_loader_factory_load_directory (factory, context, cache, + dir_writable, + list->data, + list->data); + } + + g_list_free_full (path, (GDestroyNotify) g_object_unref); + g_list_free_full (writable_path, (GDestroyNotify) g_object_unref); +} + +static void +gimp_data_loader_factory_load_directory (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache, + gboolean dir_writable, + GFile *directory, + GFile *top_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_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (enumerator) + { + GFileInfo *info; + + while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) + { + GFileType file_type; + GFile *child; + + if (g_file_info_get_is_hidden (info)) + { + g_object_unref (info); + continue; + } + + file_type = g_file_info_get_file_type (info); + child = g_file_enumerator_get_child (enumerator, info); + + if (file_type == G_FILE_TYPE_DIRECTORY) + { + gimp_data_loader_factory_load_directory (factory, context, cache, + dir_writable, + child, + top_directory); + } + else if (file_type == G_FILE_TYPE_REGULAR) + { + gimp_data_loader_factory_load_data (factory, context, cache, + dir_writable, + child, info, + top_directory); + } + + g_object_unref (child); + g_object_unref (info); + } + + g_object_unref (enumerator); + } +} + +static void +gimp_data_loader_factory_load_data (GimpDataFactory *factory, + GimpContext *context, + GHashTable *cache, + gboolean dir_writable, + GFile *file, + GFileInfo *info, + GFile *top_directory) +{ + GimpDataLoader *loader; + GimpContainer *container; + GimpContainer *container_obsolete; + GList *data_list = NULL; + GInputStream *input; + guint64 mtime; + GError *error = NULL; + + loader = gimp_data_loader_factory_get_loader (factory, file); + + if (! loader) + return; + + container = gimp_data_factory_get_container (factory); + container_obsolete = gimp_data_factory_get_container_obsolete (factory); + + if (gimp_data_factory_get_gimp (factory)->be_verbose) + g_print (" Loading %s\n", gimp_file_get_utf8_name (file)); + + mtime = g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + + if (cache) + { + GList *cached_data = g_hash_table_lookup (cache, file); + + if (cached_data && + gimp_data_get_mtime (cached_data->data) != 0 && + gimp_data_get_mtime (cached_data->data) == mtime) + { + GList *list; + + for (list = cached_data; list; list = g_list_next (list)) + gimp_container_add (container, list->data); + + return; + } + } + + input = G_INPUT_STREAM (g_file_read (file, NULL, &error)); + + if (input) + { + GInputStream *buffered = g_buffered_input_stream_new (input); + + data_list = loader->load_func (context, file, buffered, &error); + + if (error) + { + g_prefix_error (&error, + _("Error loading '%s': "), + gimp_file_get_utf8_name (file)); + } + else if (! data_list) + { + g_set_error (&error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Error loading '%s'"), + gimp_file_get_utf8_name (file)); + } + + g_object_unref (buffered); + g_object_unref (input); + } + else + { + g_prefix_error (&error, + _("Could not open '%s' for reading: "), + gimp_file_get_utf8_name (file)); + } + + if (G_LIKELY (data_list)) + { + GList *list; + gchar *uri; + gboolean obsolete; + gboolean writable = FALSE; + gboolean deletable = FALSE; + + uri = g_file_get_uri (file); + + obsolete = (strstr (uri, GIMP_OBSOLETE_DATA_DIR_NAME) != 0); + + g_free (uri); + + /* obsolete files are immutable, don't check their writability */ + if (! obsolete) + { + deletable = (g_list_length (data_list) == 1 && dir_writable); + writable = (deletable && loader->writable); + } + + for (list = data_list; list; list = g_list_next (list)) + { + GimpData *data = list->data; + + gimp_data_set_file (data, file, writable, deletable); + gimp_data_set_mtime (data, mtime); + gimp_data_clean (data); + + if (obsolete) + { + gimp_container_add (container_obsolete, + GIMP_OBJECT (data)); + } + else + { + gimp_data_set_folder_tags (data, top_directory); + + gimp_container_add (container, + GIMP_OBJECT (data)); + } + + g_object_unref (data); + } + + g_list_free (data_list); + } + + /* not else { ... } because loader->load_func() can return a list + * of data objects *and* an error message if loading failed after + * something was already loaded + */ + if (G_UNLIKELY (error)) + { + gimp_message (gimp_data_factory_get_gimp (factory), NULL, + GIMP_MESSAGE_ERROR, + _("Failed to load data:\n\n%s"), error->message); + g_clear_error (&error); + } +} + +static GimpDataLoader * +gimp_data_loader_new (const gchar *name, + GimpDataLoadFunc load_func, + const gchar *extension, + gboolean writable) +{ + GimpDataLoader *loader = g_slice_new (GimpDataLoader); + + loader->name = g_strdup (name); + loader->load_func = load_func; + loader->extension = g_strdup (extension); + loader->writable = writable ? TRUE : FALSE; + + return loader; +} + +static void +gimp_data_loader_free (GimpDataLoader *loader) +{ + g_free (loader->name); + g_free (loader->extension); + + g_slice_free (GimpDataLoader, loader); +} diff --git a/app/core/gimpdataloaderfactory.h b/app/core/gimpdataloaderfactory.h new file mode 100644 index 0000000..cf0d742 --- /dev/null +++ b/app/core/gimpdataloaderfactory.h @@ -0,0 +1,77 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdataloaderfactory.h + * Copyright (C) 2001-2018 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DATA_LOADER_FACTORY_H__ +#define __GIMP_DATA_LOADER_FACTORY_H__ + + +#include "gimpdatafactory.h" + + +typedef GList * (* GimpDataLoadFunc) (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#define GIMP_TYPE_DATA_LOADER_FACTORY (gimp_data_loader_factory_get_type ()) +#define GIMP_DATA_LOADER_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactory)) +#define GIMP_DATA_LOADER_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactoryClass)) +#define GIMP_IS_DATA_LOADER_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DATA_LOADER_FACTORY)) +#define GIMP_IS_DATA_LOADER_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DATA_LOADER_FACTORY)) +#define GIMP_DATA_LOADER_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactoryClass)) + + +typedef struct _GimpDataLoaderFactoryPrivate GimpDataLoaderFactoryPrivate; +typedef struct _GimpDataLoaderFactoryClass GimpDataLoaderFactoryClass; + +struct _GimpDataLoaderFactory +{ + GimpDataFactory parent_instance; + + GimpDataLoaderFactoryPrivate *priv; +}; + +struct _GimpDataLoaderFactoryClass +{ + GimpDataFactoryClass parent_class; +}; + + +GType gimp_data_loader_factory_get_type (void) G_GNUC_CONST; + +GimpDataFactory * gimp_data_loader_factory_new (Gimp *gimp, + GType data_type, + const gchar *path_property_name, + const gchar *writable_property_name, + GimpDataNewFunc new_func, + GimpDataGetStandardFunc get_standard_func); + +void gimp_data_loader_factory_add_loader (GimpDataFactory *factory, + const gchar *name, + GimpDataLoadFunc load_func, + const gchar *extension, + gboolean writable); +void gimp_data_loader_factory_add_fallback (GimpDataFactory *factory, + const gchar *name, + GimpDataLoadFunc load_func); + + +#endif /* __GIMP_DATA_LOADER_FACTORY_H__ */ diff --git a/app/core/gimpdocumentlist.c b/app/core/gimpdocumentlist.c new file mode 100644 index 0000000..fe10f6f --- /dev/null +++ b/app/core/gimpdocumentlist.c @@ -0,0 +1,106 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimpdocumentlist.h" +#include "gimpimagefile.h" + + +G_DEFINE_TYPE (GimpDocumentList, gimp_document_list, GIMP_TYPE_LIST) + + +static void +gimp_document_list_class_init (GimpDocumentListClass *klass) +{ +} + +static void +gimp_document_list_init (GimpDocumentList *list) +{ +} + +GimpContainer * +gimp_document_list_new (Gimp *gimp) +{ + GimpDocumentList *document_list; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + document_list = g_object_new (GIMP_TYPE_DOCUMENT_LIST, + "name", "document-list", + "children-type", GIMP_TYPE_IMAGEFILE, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); + + document_list->gimp = gimp; + + return GIMP_CONTAINER (document_list); +} + +GimpImagefile * +gimp_document_list_add_file (GimpDocumentList *document_list, + GFile *file, + const gchar *mime_type) +{ + Gimp *gimp; + GimpImagefile *imagefile; + GimpContainer *container; + gchar *uri; + + g_return_val_if_fail (GIMP_IS_DOCUMENT_LIST (document_list), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + + container = GIMP_CONTAINER (document_list); + + gimp = document_list->gimp; + + uri = g_file_get_uri (file); + + imagefile = (GimpImagefile *) gimp_container_get_child_by_name (container, + uri); + + g_free (uri); + + if (imagefile) + { + gimp_container_reorder (container, GIMP_OBJECT (imagefile), 0); + } + else + { + imagefile = gimp_imagefile_new (gimp, file); + gimp_container_add (container, GIMP_OBJECT (imagefile)); + g_object_unref (imagefile); + } + + gimp_imagefile_set_mime_type (imagefile, mime_type); + + if (gimp->config->save_document_history) + gimp_recent_list_add_file (gimp, file, mime_type); + + return imagefile; +} diff --git a/app/core/gimpdocumentlist.h b/app/core/gimpdocumentlist.h new file mode 100644 index 0000000..68bca80 --- /dev/null +++ b/app/core/gimpdocumentlist.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_DOCUMENT_LIST_H__ +#define __GIMP_DOCUMENT_LIST_H__ + +#include "core/gimplist.h" + + +#define GIMP_TYPE_DOCUMENT_LIST (gimp_document_list_get_type ()) +#define GIMP_DOCUMENT_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DOCUMENT_LIST, GimpDocumentList)) +#define GIMP_DOCUMENT_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DOCUMENT_LIST, GimpDocumentListClass)) +#define GIMP_IS_DOCUMENT_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DOCUMENT_LIST)) +#define GIMP_IS_DOCUMENT_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DOCUMENT_LIST)) + + +typedef struct _GimpDocumentListClass GimpDocumentListClass; + +struct _GimpDocumentList +{ + GimpList parent_instance; + + Gimp *gimp; +}; + +struct _GimpDocumentListClass +{ + GimpListClass parent_class; +}; + + +GType gimp_document_list_get_type (void) G_GNUC_CONST; +GimpContainer * gimp_document_list_new (Gimp *gimp); + +GimpImagefile * gimp_document_list_add_file (GimpDocumentList *document_list, + GFile *file, + const gchar *mime_type); + + +#endif /* __GIMP_DOCUMENT_LIST_H__ */ diff --git a/app/core/gimpdrawable-bucket-fill.c b/app/core/gimpdrawable-bucket-fill.c new file mode 100644 index 0000000..1cef758 --- /dev/null +++ b/app/core/gimpdrawable-bucket-fill.c @@ -0,0 +1,499 @@ +/* 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 . + */ + +#include "config.h" + +#include +#define GEGL_ITERATOR2_API +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-mask.h" +#include "gegl/gimp-gegl-mask-combine.h" +#include "gegl/gimp-gegl-utils.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpdrawable.h" +#include "gimpdrawable-bucket-fill.h" +#include "gimpfilloptions.h" +#include "gimpimage.h" +#include "gimppickable.h" +#include "gimppickable-contiguous-region.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_drawable_bucket_fill (GimpDrawable *drawable, + GimpFillOptions *options, + gboolean fill_transparent, + GimpSelectCriterion fill_criterion, + gdouble threshold, + gboolean sample_merged, + gboolean diagonal_neighbors, + gdouble seed_x, + gdouble seed_y) +{ + GimpImage *image; + GeglBuffer *buffer; + gdouble mask_x; + gdouble mask_y; + gint width, height; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + gimp_set_busy (image->gimp); + + buffer = gimp_drawable_get_bucket_fill_buffer (drawable, options, + fill_transparent, fill_criterion, + threshold, FALSE, sample_merged, + diagonal_neighbors, + seed_x, seed_y, NULL, + &mask_x, &mask_y, &width, &height); + + if (buffer) + { + /* Apply it to the image */ + gimp_drawable_apply_buffer (drawable, buffer, + GEGL_RECTANGLE (0, 0, width, height), + TRUE, C_("undo-type", "Bucket Fill"), + gimp_context_get_opacity (GIMP_CONTEXT (options)), + gimp_context_get_paint_mode (GIMP_CONTEXT (options)), + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + gimp_layer_mode_get_paint_composite_mode + (gimp_context_get_paint_mode (GIMP_CONTEXT (options))), + NULL, (gint) mask_x, mask_y); + g_object_unref (buffer); + + gimp_drawable_update (drawable, mask_x, mask_y, width, height); + } + + gimp_unset_busy (image->gimp); +} + +/** + * gimp_drawable_get_bucket_fill_buffer: + * @drawable: the #GimpDrawable to edit. + * @options: + * @fill_transparent: + * @fill_criterion: + * @threshold: + * @show_all: + * @sample_merged: + * @diagonal_neighbors: + * @seed_x: X coordinate to start the fill. + * @seed_y: Y coordinate to start the fill. + * @mask_buffer: mask of the fill in-progress when in an interactive + * filling process. Set to NULL if you need a one-time + * fill. + * @mask_x: returned x bound of @mask_buffer. + * @mask_y: returned x bound of @mask_buffer. + * @mask_width: returned width bound of @mask_buffer. + * @mask_height: returned height bound of @mask_buffer. + * + * Creates the fill buffer for a bucket fill operation on @drawable, + * without actually applying it (if you want to apply it directly as a + * one-time operation, use gimp_drawable_bucket_fill() instead). If + * @mask_buffer is not NULL, the intermediate fill mask will also be + * returned. This fill mask can later be reused in successive calls to + * gimp_drawable_get_bucket_fill_buffer() for interactive filling. + * + * Returns: a fill buffer which can be directly applied to @drawable, or + * used in a drawable filter as preview. + */ +GeglBuffer * +gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable, + GimpFillOptions *options, + gboolean fill_transparent, + GimpSelectCriterion fill_criterion, + gdouble threshold, + gboolean show_all, + gboolean sample_merged, + gboolean diagonal_neighbors, + gdouble seed_x, + gdouble seed_y, + GeglBuffer **mask_buffer, + gdouble *mask_x, + gdouble *mask_y, + gint *mask_width, + gint *mask_height) +{ + GimpImage *image; + GimpPickable *pickable; + GeglBuffer *buffer; + GeglBuffer *new_mask; + gboolean antialias; + gint x, y, width, height; + gint mask_offset_x = 0; + gint mask_offset_y = 0; + gint sel_x, sel_y, sel_width, sel_height; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &sel_x, &sel_y, &sel_width, &sel_height)) + return NULL; + + if (mask_buffer && *mask_buffer && threshold == 0.0) + { + gfloat pixel; + + gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, + babl_format ("Y float"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + if (pixel != 0.0) + /* Already selected. This seed won't change the selection. */ + return NULL; + } + + gimp_set_busy (image->gimp); + + if (sample_merged) + { + if (! show_all) + pickable = GIMP_PICKABLE (image); + else + pickable = GIMP_PICKABLE (gimp_image_get_projection (image)); + } + else + { + pickable = GIMP_PICKABLE (drawable); + } + + antialias = gimp_fill_options_get_antialias (options); + + /* Do a seed bucket fill...To do this, calculate a new + * contiguous region. + */ + new_mask = gimp_pickable_contiguous_region_by_seed (pickable, + antialias, + threshold, + fill_transparent, + fill_criterion, + diagonal_neighbors, + (gint) seed_x, + (gint) seed_y); + if (mask_buffer && *mask_buffer) + { + gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, + GIMP_CHANNEL_OP_ADD, 0, 0); + g_object_unref (*mask_buffer); + } + + if (mask_buffer) + *mask_buffer = new_mask; + + gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); + width -= x; + height -= y; + + /* If there is a selection, intersect the region bounds + * with the selection bounds, to avoid processing areas + * that are going to be masked out anyway. The actual + * intersection of the fill region with the mask data + * happens when combining the fill buffer, in + * gimp_drawable_apply_buffer(). + */ + if (! gimp_channel_is_empty (gimp_image_get_mask (image))) + { + gint off_x = 0; + gint off_y = 0; + + if (sample_merged) + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + if (! gimp_rectangle_intersect (x, y, width, height, + + sel_x + off_x, sel_y + off_y, + sel_width, sel_height, + + &x, &y, &width, &height)) + { + /* The fill region and the selection are disjoint; bail. */ + + if (! mask_buffer) + g_object_unref (new_mask); + + gimp_unset_busy (image->gimp); + + return NULL; + } + } + + /* make sure we handle the mask correctly if it was sample-merged */ + if (sample_merged) + { + GimpItem *item = GIMP_ITEM (drawable); + gint off_x, off_y; + + /* Limit the channel bounds to the drawable's extents */ + gimp_item_get_offset (item, &off_x, &off_y); + + gimp_rectangle_intersect (x, y, width, height, + + off_x, off_y, + gimp_item_get_width (item), + gimp_item_get_height (item), + + &x, &y, &width, &height); + + mask_offset_x = x; + mask_offset_y = y; + + /* translate mask bounds to drawable coords */ + x -= off_x; + y -= off_y; + } + else + { + mask_offset_x = x; + mask_offset_y = y; + } + + buffer = gimp_fill_options_create_buffer (options, drawable, + GEGL_RECTANGLE (0, 0, + width, height), + -x, -y); + + gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, + -mask_offset_x, -mask_offset_y, 1.0); + + if (mask_x) + *mask_x = x; + if (mask_y) + *mask_y = y; + if (mask_width) + *mask_width = width; + if (mask_height) + *mask_height = height; + + if (! mask_buffer) + g_object_unref (new_mask); + + gimp_unset_busy (image->gimp); + + return buffer; +} + +/** + * gimp_drawable_get_line_art_fill_buffer: + * @drawable: the #GimpDrawable to edit. + * @line_art: the #GimpLineArt computed as fill source. + * @options: the #GimpFillOptions. + * @sample_merged: + * @seed_x: X coordinate to start the fill. + * @seed_y: Y coordinate to start the fill. + * @mask_buffer: mask of the fill in-progress when in an interactive + * filling process. Set to NULL if you need a one-time + * fill. + * @mask_x: returned x bound of @mask_buffer. + * @mask_y: returned x bound of @mask_buffer. + * @mask_width: returned width bound of @mask_buffer. + * @mask_height: returned height bound of @mask_buffer. + * + * Creates the fill buffer for a bucket fill operation on @drawable + * based on @line_art and @options, without actually applying it. + * If @mask_buffer is not NULL, the intermediate fill mask will also be + * returned. This fill mask can later be reused in successive calls to + * gimp_drawable_get_bucket_fill_buffer() for interactive filling. + * + * Returns: a fill buffer which can be directly applied to @drawable, or + * used in a drawable filter as preview. + */ +GeglBuffer * +gimp_drawable_get_line_art_fill_buffer (GimpDrawable *drawable, + GimpLineArt *line_art, + GimpFillOptions *options, + gboolean sample_merged, + gdouble seed_x, + gdouble seed_y, + GeglBuffer **mask_buffer, + gdouble *mask_x, + gdouble *mask_y, + gint *mask_width, + gint *mask_height) +{ + GimpImage *image; + GeglBuffer *buffer; + GeglBuffer *new_mask; + gint x, y, width, height; + gint mask_offset_x = 0; + gint mask_offset_y = 0; + gint sel_x, sel_y, sel_width, sel_height; + gdouble feather_radius; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &sel_x, &sel_y, &sel_width, &sel_height)) + return NULL; + + if (mask_buffer && *mask_buffer) + { + gfloat pixel; + + gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, + babl_format ("Y float"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + if (pixel != 0.0) + /* Already selected. This seed won't change the selection. */ + return NULL; + } + + gimp_set_busy (image->gimp); + + /* Do a seed bucket fill...To do this, calculate a new + * contiguous region. + */ + new_mask = gimp_pickable_contiguous_region_by_line_art (NULL, line_art, + (gint) seed_x, + (gint) seed_y); + if (mask_buffer && *mask_buffer) + { + gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, + GIMP_CHANNEL_OP_ADD, 0, 0); + g_object_unref (*mask_buffer); + } + if (mask_buffer) + *mask_buffer = new_mask; + + gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); + width -= x; + height -= y; + + /* If there is a selection, intersect the region bounds + * with the selection bounds, to avoid processing areas + * that are going to be masked out anyway. The actual + * intersection of the fill region with the mask data + * happens when combining the fill buffer, in + * gimp_drawable_apply_buffer(). + */ + if (! gimp_channel_is_empty (gimp_image_get_mask (image))) + { + gint off_x = 0; + gint off_y = 0; + + if (sample_merged) + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + if (! gimp_rectangle_intersect (x, y, width, height, + + sel_x + off_x, sel_y + off_y, + sel_width, sel_height, + + &x, &y, &width, &height)) + { + if (! mask_buffer) + g_object_unref (new_mask); + /* The fill region and the selection are disjoint; bail. */ + gimp_unset_busy (image->gimp); + + return NULL; + } + } + + /* make sure we handle the mask correctly if it was sample-merged */ + if (sample_merged) + { + GimpItem *item = GIMP_ITEM (drawable); + gint off_x, off_y; + + /* Limit the channel bounds to the drawable's extents */ + gimp_item_get_offset (item, &off_x, &off_y); + + gimp_rectangle_intersect (x, y, width, height, + + off_x, off_y, + gimp_item_get_width (item), + gimp_item_get_height (item), + + &x, &y, &width, &height); + + mask_offset_x = x; + mask_offset_y = y; + + /* translate mask bounds to drawable coords */ + x -= off_x; + y -= off_y; + } + else + { + mask_offset_x = x; + mask_offset_y = y; + } + + buffer = gimp_fill_options_create_buffer (options, drawable, + GEGL_RECTANGLE (0, 0, + width, height), + -x, -y); + + gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, + -mask_offset_x, -mask_offset_y, 1.0); + + if (gimp_fill_options_get_feather (options, &feather_radius)) + { + /* Feathering for the line art algorithm is not applied during + * mask creation because we just want to apply it on the borders + * of the mask at the end (since the mask can evolve, we don't + * want to actually touch it, but only the intermediate results). + */ + gimp_gegl_apply_feather (buffer, NULL, NULL, buffer, NULL, + feather_radius, feather_radius, TRUE); + } + + if (mask_x) + *mask_x = x; + if (mask_y) + *mask_y = y; + if (mask_width) + *mask_width = width; + if (mask_height) + *mask_height = height; + + if (! mask_buffer) + g_object_unref (new_mask); + + gimp_unset_busy (image->gimp); + + return buffer; +} diff --git a/app/core/gimpdrawable-bucket-fill.h b/app/core/gimpdrawable-bucket-fill.h new file mode 100644 index 0000000..148de97 --- /dev/null +++ b/app/core/gimpdrawable-bucket-fill.h @@ -0,0 +1,59 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_BUCKET_FILL_H__ +#define __GIMP_DRAWABLE_BUCKET_FILL_H__ + + +void gimp_drawable_bucket_fill (GimpDrawable *drawable, + GimpFillOptions *options, + gboolean fill_transparent, + GimpSelectCriterion fill_criterion, + gdouble threshold, + gboolean sample_merged, + gboolean diagonal_neighbors, + gdouble x, + gdouble y); +GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable, + GimpFillOptions *options, + gboolean fill_transparent, + GimpSelectCriterion fill_criterion, + gdouble threshold, + gboolean show_all, + gboolean sample_merged, + gboolean diagonal_neighbors, + gdouble seed_x, + gdouble seed_y, + GeglBuffer **mask_buffer, + gdouble *mask_x, + gdouble *mask_y, + gint *mask_width, + gint *mask_height); + +GeglBuffer * gimp_drawable_get_line_art_fill_buffer (GimpDrawable *drawable, + GimpLineArt *line_art, + GimpFillOptions *options, + gboolean sample_merged, + gdouble seed_x, + gdouble seed_y, + GeglBuffer **mask_buffer, + gdouble *mask_x, + gdouble *mask_y, + gint *mask_width, + gint *mask_height); + +#endif /* __GIMP_DRAWABLE_BUCKET_FILL_H__ */ diff --git a/app/core/gimpdrawable-combine.c b/app/core/gimpdrawable-combine.c new file mode 100644 index 0000000..e48d9dd --- /dev/null +++ b/app/core/gimpdrawable-combine.c @@ -0,0 +1,144 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimpapplicator.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpchunkiterator.h" +#include "gimpdrawable-combine.h" +#include "gimpimage.h" + + +void +gimp_drawable_real_apply_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GeglRectangle *buffer_region, + gboolean push_undo, + const gchar *undo_desc, + gdouble opacity, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + GeglBuffer *base_buffer, + gint base_x, + gint base_y) +{ + GimpItem *item = GIMP_ITEM (drawable); + GimpImage *image = gimp_item_get_image (item); + GimpChannel *mask = gimp_image_get_mask (image); + GimpApplicator *applicator; + GimpChunkIterator *iter; + gint x, y, width, height; + gint offset_x, offset_y; + + /* don't apply the mask to itself and don't apply an empty mask */ + if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask)) + mask = NULL; + + if (! base_buffer) + base_buffer = gimp_drawable_get_buffer (drawable); + + /* get the layer offsets */ + gimp_item_get_offset (item, &offset_x, &offset_y); + + /* make sure the image application coordinates are within drawable bounds */ + if (! gimp_rectangle_intersect (base_x, base_y, + buffer_region->width, buffer_region->height, + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item), + &x, &y, &width, &height)) + { + return; + } + + if (mask) + { + GimpItem *mask_item = GIMP_ITEM (mask); + + /* make sure coordinates are in mask bounds ... + * we need to add the layer offset to transform coords + * into the mask coordinate system + */ + if (! gimp_rectangle_intersect (x, y, width, height, + -offset_x, -offset_y, + gimp_item_get_width (mask_item), + gimp_item_get_height (mask_item), + &x, &y, &width, &height)) + { + return; + } + } + + if (push_undo) + { + gimp_drawable_push_undo (drawable, undo_desc, + NULL, x, y, width, height); + } + + applicator = gimp_applicator_new (NULL); + + if (mask) + { + GeglBuffer *mask_buffer; + + mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_applicator_set_mask_buffer (applicator, mask_buffer); + gimp_applicator_set_mask_offset (applicator, -offset_x, -offset_y); + } + + gimp_applicator_set_src_buffer (applicator, base_buffer); + gimp_applicator_set_dest_buffer (applicator, + gimp_drawable_get_buffer (drawable)); + + gimp_applicator_set_apply_buffer (applicator, buffer); + gimp_applicator_set_apply_offset (applicator, + base_x - buffer_region->x, + base_y - buffer_region->y); + + gimp_applicator_set_opacity (applicator, opacity); + gimp_applicator_set_mode (applicator, mode, + blend_space, composite_space, composite_mode); + gimp_applicator_set_affect (applicator, + gimp_drawable_get_active_mask (drawable)); + + iter = gimp_chunk_iterator_new (cairo_region_create_rectangle ( + &(cairo_rectangle_int_t) {x, y, width, height})); + + while (gimp_chunk_iterator_next (iter)) + { + GeglRectangle rect; + + while (gimp_chunk_iterator_get_rect (iter, &rect)) + gimp_applicator_blit (applicator, &rect); + } + + g_object_unref (applicator); +} diff --git a/app/core/gimpdrawable-combine.h b/app/core/gimpdrawable-combine.h new file mode 100644 index 0000000..d21d558 --- /dev/null +++ b/app/core/gimpdrawable-combine.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 . + */ + +#ifndef __GIMP_DRAWABLE_COMBINE_H__ +#define __GIMP_DRAWABLE_COMBINE_H__ + + +/* virtual functions of GimpDrawable, don't call directly */ + +void gimp_drawable_real_apply_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GeglRectangle *buffer_region, + gboolean push_undo, + const gchar *undo_desc, + gdouble opacity, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + GeglBuffer *base_buffer, + gint base_x, + gint base_y); + + +#endif /* __GIMP_DRAWABLE_COMBINE_H__ */ diff --git a/app/core/gimpdrawable-edit.c b/app/core/gimpdrawable-edit.c new file mode 100644 index 0000000..214b8fb --- /dev/null +++ b/app/core/gimpdrawable-edit.c @@ -0,0 +1,231 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "gimpchannel.h" +#include "gimpdrawable.h" +#include "gimpdrawable-edit.h" +#include "gimpdrawablefilter.h" +#include "gimpcontext.h" +#include "gimpfilloptions.h" +#include "gimpimage.h" +#include "gimppattern.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static gboolean gimp_drawable_edit_can_fill_direct (GimpDrawable *drawable, + GimpFillOptions *options); +static void gimp_drawable_edit_fill_direct (GimpDrawable *drawable, + GimpFillOptions *options, + const gchar *undo_desc); + + +/* private functions */ + +static gboolean +gimp_drawable_edit_can_fill_direct (GimpDrawable *drawable, + GimpFillOptions *options) +{ + GimpImage *image; + GimpContext *context; + gdouble opacity; + GimpComponentMask affect; + GimpLayerMode mode; + GimpLayerCompositeMode composite_mode; + GimpLayerCompositeRegion composite_region; + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + context = GIMP_CONTEXT (options); + opacity = gimp_context_get_opacity (context); + affect = gimp_drawable_get_active_mask (drawable); + mode = gimp_context_get_paint_mode (context); + composite_mode = gimp_layer_mode_get_paint_composite_mode (mode); + composite_region = gimp_layer_mode_get_included_region (mode, composite_mode); + + if (gimp_channel_is_empty (gimp_image_get_mask (image)) && + opacity == GIMP_OPACITY_OPAQUE && + affect == GIMP_COMPONENT_MASK_ALL && + gimp_layer_mode_is_trivial (mode) && + (! gimp_layer_mode_is_subtractive (mode) ^ + ! (composite_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE))) + { + switch (gimp_fill_options_get_style (options)) + { + case GIMP_FILL_STYLE_SOLID: + return TRUE; + + case GIMP_FILL_STYLE_PATTERN: + { + GimpPattern *pattern; + GimpTempBuf *mask; + const Babl *format; + + pattern = gimp_context_get_pattern (context); + mask = gimp_pattern_get_mask (pattern); + format = gimp_temp_buf_get_format (mask); + + return ! babl_format_has_alpha (format); + } + } + } + + return FALSE; +} + +static void +gimp_drawable_edit_fill_direct (GimpDrawable *drawable, + GimpFillOptions *options, + const gchar *undo_desc) +{ + GeglBuffer *buffer; + GimpContext *context; + GimpLayerMode mode; + gint width; + gint height; + + buffer = gimp_drawable_get_buffer (drawable); + context = GIMP_CONTEXT (options); + mode = gimp_context_get_paint_mode (context); + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + + gimp_drawable_push_undo (drawable, undo_desc, + NULL, 0, 0, width, height); + + if (! gimp_layer_mode_is_subtractive (mode)) + gimp_fill_options_fill_buffer (options, drawable, buffer, 0, 0); + else + gimp_gegl_clear (buffer, NULL); +} + + +/* public functions */ + +void +gimp_drawable_edit_clear (GimpDrawable *drawable, + GimpContext *context) +{ + GimpFillOptions *options; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + options = gimp_fill_options_new (context->gimp, NULL, FALSE); + + if (gimp_drawable_has_alpha (drawable)) + gimp_fill_options_set_by_fill_type (options, context, + GIMP_FILL_TRANSPARENT, NULL); + else + gimp_fill_options_set_by_fill_type (options, context, + GIMP_FILL_BACKGROUND, NULL); + + gimp_drawable_edit_fill (drawable, options, C_("undo-type", "Clear")); + + g_object_unref (options); +} + +void +gimp_drawable_edit_fill (GimpDrawable *drawable, + GimpFillOptions *options, + const gchar *undo_desc) +{ + GimpContext *context; + gint x, y, width, height; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &x, &y, &width, &height)) + { + return; /* nothing to do, but the fill succeeded */ + } + + context = GIMP_CONTEXT (options); + + if (gimp_layer_mode_is_alpha_only (gimp_context_get_paint_mode (context))) + { + if (! gimp_drawable_has_alpha (drawable) || + ! (gimp_drawable_get_active_mask (drawable) & + GIMP_COMPONENT_MASK_ALPHA)) + { + return; /* nothing to do, but the fill succeeded */ + } + } + + if (! undo_desc) + undo_desc = gimp_fill_options_get_undo_desc (options); + + /* check if we can fill the drawable's buffer directly */ + if (gimp_drawable_edit_can_fill_direct (drawable, options)) + { + gimp_drawable_edit_fill_direct (drawable, options, undo_desc); + + gimp_drawable_update (drawable, x, y, width, height); + } + else + { + GeglNode *operation; + GimpDrawableFilter *filter; + gdouble opacity; + GimpLayerMode mode; + GimpLayerMode composite_mode; + + opacity = gimp_context_get_opacity (context); + mode = gimp_context_get_paint_mode (context); + composite_mode = gimp_layer_mode_get_paint_composite_mode (mode); + + operation = gegl_node_new_child (NULL, + "operation", "gimp:fill-source", + "options", options, + "drawable", drawable, + "pattern-offset-x", -x, + "pattern-offset-y", -y, + NULL); + + filter = gimp_drawable_filter_new (drawable, undo_desc, operation, NULL); + + gimp_drawable_filter_set_opacity (filter, opacity); + gimp_drawable_filter_set_mode (filter, + mode, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + composite_mode); + + gimp_drawable_filter_apply (filter, NULL); + gimp_drawable_filter_commit (filter, NULL, FALSE); + + g_object_unref (filter); + g_object_unref (operation); + } +} diff --git a/app/core/gimpdrawable-edit.h b/app/core/gimpdrawable-edit.h new file mode 100644 index 0000000..a4ecb1c --- /dev/null +++ b/app/core/gimpdrawable-edit.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 . + */ + +#ifndef __GIMP_DRAWABLE_EDIT_H__ +#define __GIMP_DRAWABLE_EDIT_H__ + + +void gimp_drawable_edit_clear (GimpDrawable *drawable, + GimpContext *context); +void gimp_drawable_edit_fill (GimpDrawable *drawable, + GimpFillOptions *options, + const gchar *undo_desc); + + +#endif /* __GIMP_DRAWABLE_EDIT_H__ */ diff --git a/app/core/gimpdrawable-equalize.c b/app/core/gimpdrawable-equalize.c new file mode 100644 index 0000000..202dc7f --- /dev/null +++ b/app/core/gimpdrawable-equalize.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpdrawable.h" +#include "gimpdrawable-equalize.h" +#include "gimpdrawable-histogram.h" +#include "gimpdrawable-operation.h" +#include "gimphistogram.h" +#include "gimpimage.h" +#include "gimpselection.h" + +#include "gimp-intl.h" + + +void +gimp_drawable_equalize (GimpDrawable *drawable, + gboolean mask_only) +{ + GimpImage *image; + GimpChannel *selection; + GimpHistogram *histogram; + GeglNode *equalize; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + selection = gimp_image_get_mask (image); + + histogram = gimp_histogram_new (FALSE); + gimp_drawable_calculate_histogram (drawable, histogram, FALSE); + + equalize = gegl_node_new_child (NULL, + "operation", "gimp:equalize", + "histogram", histogram, + NULL); + + if (! mask_only) + gimp_selection_suspend (GIMP_SELECTION (selection)); + + gimp_drawable_apply_operation (drawable, NULL, + C_("undo-type", "Equalize"), + equalize); + + if (! mask_only) + gimp_selection_resume (GIMP_SELECTION (selection)); + + g_object_unref (equalize); + g_object_unref (histogram); +} diff --git a/app/core/gimpdrawable-equalize.h b/app/core/gimpdrawable-equalize.h new file mode 100644 index 0000000..065b83f --- /dev/null +++ b/app/core/gimpdrawable-equalize.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 . + */ + +#ifndef __GIMP_DRAWABLE_EQUALIZE_H__ +#define __GIMP_DRAWABLE_EQUALIZE_H__ + + +void gimp_drawable_equalize (GimpDrawable *drawable, + gboolean mask_only); + + +#endif /* __GIMP_DRAWABLE_EQUALIZE_H__ */ diff --git a/app/core/gimpdrawable-fill.c b/app/core/gimpdrawable-fill.c new file mode 100644 index 0000000..a596d47 --- /dev/null +++ b/app/core/gimpdrawable-fill.c @@ -0,0 +1,279 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gimp-utils.h" +#include "gimpbezierdesc.h" +#include "gimpchannel.h" +#include "gimpdrawable-fill.h" +#include "gimperror.h" +#include "gimpfilloptions.h" +#include "gimpimage.h" +#include "gimppattern.h" +#include "gimppickable.h" +#include "gimpscanconvert.h" + +#include "vectors/gimpvectors.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_drawable_fill (GimpDrawable *drawable, + GimpContext *context, + GimpFillType fill_type) +{ + GimpRGB color; + GimpPattern *pattern; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (fill_type == GIMP_FILL_TRANSPARENT && + ! gimp_drawable_has_alpha (drawable)) + { + fill_type = GIMP_FILL_BACKGROUND; + } + + if (! gimp_get_fill_params (context, fill_type, &color, &pattern, NULL)) + return; + + gimp_drawable_fill_buffer (drawable, + gimp_drawable_get_buffer (drawable), + &color, pattern, 0, 0); + + gimp_drawable_update (drawable, 0, 0, -1, -1); +} + +void +gimp_drawable_fill_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GimpRGB *color, + GimpPattern *pattern, + gint pattern_offset_x, + gint pattern_offset_y) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (color != NULL || pattern != NULL); + g_return_if_fail (pattern == NULL || GIMP_IS_PATTERN (pattern)); + + if (pattern) + { + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + GimpColorProfile *src_profile; + GimpColorProfile *dest_profile; + + src_buffer = gimp_pattern_create_buffer (pattern); + + src_profile = gimp_babl_format_get_color_profile ( + gegl_buffer_get_format (src_buffer)); + dest_profile = gimp_color_managed_get_color_profile ( + GIMP_COLOR_MANAGED (drawable)); + + if (gimp_color_transform_can_gegl_copy (src_profile, dest_profile)) + { + dest_buffer = g_object_ref (src_buffer); + } + else + { + dest_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), + gegl_buffer_get_format (buffer)); + + gimp_gegl_convert_color_profile ( + src_buffer, NULL, src_profile, + dest_buffer, NULL, dest_profile, + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + TRUE, + NULL); + } + + gegl_buffer_set_pattern (buffer, NULL, dest_buffer, + pattern_offset_x, pattern_offset_y); + + g_object_unref (src_buffer); + g_object_unref (dest_buffer); + } + else + { + GimpRGB image_color; + GeglColor *gegl_color; + + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + color, &image_color); + + if (! gimp_drawable_has_alpha (drawable)) + gimp_rgb_set_alpha (&image_color, 1.0); + + gegl_color = gimp_gegl_color_new (&image_color); + gegl_buffer_set_color (buffer, NULL, gegl_color); + g_object_unref (gegl_color); + } +} + +void +gimp_drawable_fill_boundary (GimpDrawable *drawable, + GimpFillOptions *options, + const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y, + gboolean push_undo) +{ + GimpScanConvert *scan_convert; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + g_return_if_fail (bound_segs == NULL || n_bound_segs != 0); + g_return_if_fail (gimp_fill_options_get_style (options) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL); + + scan_convert = gimp_scan_convert_new_from_boundary (bound_segs, n_bound_segs, + offset_x, offset_y); + + if (scan_convert) + { + gimp_drawable_fill_scan_convert (drawable, options, + scan_convert, push_undo); + gimp_scan_convert_free (scan_convert); + } +} + +gboolean +gimp_drawable_fill_vectors (GimpDrawable *drawable, + GimpFillOptions *options, + GimpVectors *vectors, + gboolean push_undo, + GError **error) +{ + const GimpBezierDesc *bezier; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (gimp_fill_options_get_style (options) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL, + FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + bezier = gimp_vectors_get_bezier (vectors); + + if (bezier && bezier->num_data > 4) + { + GimpScanConvert *scan_convert = gimp_scan_convert_new (); + + gimp_scan_convert_add_bezier (scan_convert, bezier); + gimp_drawable_fill_scan_convert (drawable, options, + scan_convert, push_undo); + + gimp_scan_convert_free (scan_convert); + + return TRUE; + } + + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Not enough points to fill")); + + return FALSE; +} + +void +gimp_drawable_fill_scan_convert (GimpDrawable *drawable, + GimpFillOptions *options, + GimpScanConvert *scan_convert, + gboolean push_undo) +{ + GimpContext *context; + GeglBuffer *buffer; + GeglBuffer *mask_buffer; + gint x, y, w, h; + gint off_x; + gint off_y; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + g_return_if_fail (scan_convert != NULL); + g_return_if_fail (gimp_fill_options_get_style (options) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL); + + context = GIMP_CONTEXT (options); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h)) + return; + + /* fill a 1-bpp GeglBuffer with black, this will describe the shape + * of the stroke. + */ + mask_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, w, h), + babl_format ("Y u8")); + + /* render the stroke into it */ + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + gimp_scan_convert_render (scan_convert, mask_buffer, + x + off_x, y + off_y, + gimp_fill_options_get_antialias (options)); + + buffer = gimp_fill_options_create_buffer (options, drawable, + GEGL_RECTANGLE (0, 0, w, h), + -x, -y); + + gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, + mask_buffer, 0, 0, 1.0); + g_object_unref (mask_buffer); + + /* Apply to drawable */ + gimp_drawable_apply_buffer (drawable, buffer, + GEGL_RECTANGLE (0, 0, w, h), + push_undo, C_("undo-type", "Render Stroke"), + gimp_context_get_opacity (context), + gimp_context_get_paint_mode (context), + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + gimp_layer_mode_get_paint_composite_mode ( + gimp_context_get_paint_mode (context)), + NULL, x, y); + + g_object_unref (buffer); + + gimp_drawable_update (drawable, x, y, w, h); +} diff --git a/app/core/gimpdrawable-fill.h b/app/core/gimpdrawable-fill.h new file mode 100644 index 0000000..d85d5bb --- /dev/null +++ b/app/core/gimpdrawable-fill.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 . + */ + +#ifndef __GIMP_DRAWABLE_FILL_H__ +#define __GIMP_DRAWABLE_FILL_H__ + + +/* Lowlevel API that is used for initializing entire drawables and + * buffers before they are used in images, they don't push an undo. + */ + +void gimp_drawable_fill (GimpDrawable *drawable, + GimpContext *context, + GimpFillType fill_type); +void gimp_drawable_fill_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GimpRGB *color, + GimpPattern *pattern, + gint pattern_offset_x, + gint pattern_offset_y); + + +/* Proper API that is used for actual editing (not just initializing) + */ + +void gimp_drawable_fill_boundary (GimpDrawable *drawable, + GimpFillOptions *options, + const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y, + gboolean push_undo); + +gboolean gimp_drawable_fill_vectors (GimpDrawable *drawable, + GimpFillOptions *options, + GimpVectors *vectors, + gboolean push_undo, + GError **error); + +void gimp_drawable_fill_scan_convert (GimpDrawable *drawable, + GimpFillOptions *options, + GimpScanConvert *scan_convert, + gboolean push_undo); + + +#endif /* __GIMP_DRAWABLE_FILL_H__ */ diff --git a/app/core/gimpdrawable-filters.c b/app/core/gimpdrawable-filters.c new file mode 100644 index 0000000..762a870 --- /dev/null +++ b/app/core/gimpdrawable-filters.c @@ -0,0 +1,343 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-filters.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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "core-types.h" + +#include "gegl/gimpapplicator.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpdrawable.h" +#include "gimpdrawable-filters.h" +#include "gimpdrawable-private.h" +#include "gimpfilter.h" +#include "gimpfilterstack.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimplayer.h" +#include "gimpprogress.h" +#include "gimpprojection.h" + + +GimpContainer * +gimp_drawable_get_filters (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + return drawable->private->filter_stack; +} + +gboolean +gimp_drawable_has_filters (GimpDrawable *drawable) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + for (list = GIMP_LIST (drawable->private->filter_stack)->queue->head; + list; + list = g_list_next (list)) + { + GimpFilter *filter = list->data; + + if (gimp_filter_get_active (filter)) + return TRUE; + } + + return FALSE; +} + +void +gimp_drawable_add_filter (GimpDrawable *drawable, + GimpFilter *filter) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_FILTER (filter)); + g_return_if_fail (gimp_drawable_has_filter (drawable, filter) == FALSE); + + gimp_container_add (drawable->private->filter_stack, + GIMP_OBJECT (filter)); +} + +void +gimp_drawable_remove_filter (GimpDrawable *drawable, + GimpFilter *filter) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_FILTER (filter)); + g_return_if_fail (gimp_drawable_has_filter (drawable, filter) == TRUE); + + gimp_container_remove (drawable->private->filter_stack, + GIMP_OBJECT (filter)); +} + +gboolean +gimp_drawable_has_filter (GimpDrawable *drawable, + GimpFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE); + + return gimp_container_have (drawable->private->filter_stack, + GIMP_OBJECT (filter)); +} + +gboolean +gimp_drawable_merge_filter (GimpDrawable *drawable, + GimpFilter *filter, + GimpProgress *progress, + const gchar *undo_desc, + const Babl *format, + gboolean clip, + gboolean cancellable, + gboolean update) +{ + GimpImage *image; + GimpApplicator *applicator; + gboolean applicator_cache = FALSE; + const Babl *applicator_output_format = NULL; + GeglBuffer *buffer = NULL; + GeglBuffer *dest_buffer; + GeglBuffer *undo_buffer = NULL; + GeglRectangle undo_rect; + GeglBuffer *cache = NULL; + GeglRectangle *rects = NULL; + gint n_rects = 0; + GeglRectangle rect; + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + applicator = gimp_filter_get_applicator (filter); + dest_buffer = gimp_drawable_get_buffer (drawable); + + if (! format) + format = gimp_drawable_get_format (drawable); + + rect = gegl_node_get_bounding_box (gimp_filter_get_node (filter)); + + if (! clip && gegl_rectangle_equal (&rect, + gegl_buffer_get_extent (dest_buffer))) + { + clip = TRUE; + } + + if (clip) + { + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &rect.x, &rect.y, + &rect.width, &rect.height)) + { + return TRUE; + } + + if (format != gimp_drawable_get_format (drawable)) + { + buffer = gegl_buffer_new (gegl_buffer_get_extent (dest_buffer), + format); + + dest_buffer = buffer; + } + } + else + { + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect.width, rect.height), + format); + + dest_buffer = g_object_new (GEGL_TYPE_BUFFER, + "source", buffer, + "shift-x", -rect.x, + "shift-y", -rect.y, + NULL); + } + + if (applicator) + { + const GeglRectangle *crop_rect; + + crop_rect = gimp_applicator_get_crop (applicator); + + if (crop_rect && ! gegl_rectangle_intersect (&rect, &rect, crop_rect)) + return TRUE; + + /* the cache and its valid rectangles are the region that + * has already been processed by this applicator. + */ + cache = gimp_applicator_get_cache_buffer (applicator, + &rects, &n_rects); + + /* skip the cache and output-format conversion while processing + * the remaining area, so that the result is written directly to + * the drawable's buffer. + */ + applicator_cache = gimp_applicator_get_cache (applicator); + applicator_output_format = gimp_applicator_get_output_format (applicator); + + gimp_applicator_set_cache (applicator, FALSE); + if (applicator_output_format == format) + gimp_applicator_set_output_format (applicator, NULL); + } + + if (! buffer) + { + gegl_rectangle_align_to_buffer ( + &undo_rect, + &rect, + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + undo_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + undo_rect.width, + undo_rect.height), + gimp_drawable_get_format (drawable)); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), + &undo_rect, + GEGL_ABYSS_NONE, + undo_buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + } + + gimp_projection_stop_rendering (gimp_image_get_projection (image)); + + /* make sure we have a source node - this connects the filter stack to the + * underlying source node + */ + (void) gimp_drawable_get_source_node (drawable); + + if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable), + progress, undo_desc, + gimp_filter_get_node (filter), FALSE, + dest_buffer, &rect, FALSE, + cache, rects, n_rects, + cancellable)) + { + /* finished successfully */ + + if (clip) + { + if (buffer) + { + gimp_drawable_set_buffer_full (drawable, + TRUE, undo_desc, + buffer, NULL, + FALSE); + } + else + { + gimp_drawable_push_undo (drawable, undo_desc, undo_buffer, + undo_rect.x, undo_rect.y, + undo_rect.width, undo_rect.height); + } + } + else + { + GimpLayerMask *mask = NULL; + gint offset_x; + gint offset_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); + + if (GIMP_IS_LAYER (drawable)) + mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); + + if (mask) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD, + undo_desc); + } + + gimp_drawable_set_buffer_full ( + drawable, TRUE, undo_desc, buffer, + GEGL_RECTANGLE (offset_x + rect.x, offset_y + rect.y, 0, 0), + FALSE); + + if (mask) + { + gimp_item_resize (GIMP_ITEM (mask), + gimp_get_default_context (image->gimp), + GIMP_FILL_TRANSPARENT, + rect.width, rect.height, + -rect.x, -rect.y); + + gimp_image_undo_group_end (image); + } + } + } + else + { + /* canceled by the user */ + + if (clip) + { + gimp_gegl_buffer_copy (undo_buffer, + GEGL_RECTANGLE (0, 0, + undo_rect.width, + undo_rect.height), + GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (drawable), + &undo_rect); + } + + success = FALSE; + } + + if (clip) + { + g_clear_object (&undo_buffer); + g_clear_object (&buffer); + } + else + { + g_object_unref (buffer); + g_object_unref (dest_buffer); + } + + if (cache) + { + g_object_unref (cache); + g_free (rects); + } + + if (applicator) + { + gimp_applicator_set_cache (applicator, applicator_cache); + gimp_applicator_set_output_format (applicator, applicator_output_format); + } + + if (update) + { + gimp_drawable_update (drawable, + rect.x, rect.y, + rect.width, rect.height); + } + + return success; +} diff --git a/app/core/gimpdrawable-filters.h b/app/core/gimpdrawable-filters.h new file mode 100644 index 0000000..504d402 --- /dev/null +++ b/app/core/gimpdrawable-filters.h @@ -0,0 +1,46 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-filters.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 . + */ + +#ifndef __GIMP_DRAWABLE_FILTERS_H__ +#define __GIMP_DRAWABLE_FILTERS_H__ + + +GimpContainer * gimp_drawable_get_filters (GimpDrawable *drawable); + +gboolean gimp_drawable_has_filters (GimpDrawable *drawable); + +void gimp_drawable_add_filter (GimpDrawable *drawable, + GimpFilter *filter); +void gimp_drawable_remove_filter (GimpDrawable *drawable, + GimpFilter *filter); + +gboolean gimp_drawable_has_filter (GimpDrawable *drawable, + GimpFilter *filter); + +gboolean gimp_drawable_merge_filter (GimpDrawable *drawable, + GimpFilter *filter, + GimpProgress *progress, + const gchar *undo_desc, + const Babl *format, + gboolean clip, + gboolean cancellable, + gboolean update); + + +#endif /* __GIMP_DRAWABLE_FILTERS_H__ */ diff --git a/app/core/gimpdrawable-floating-selection.c b/app/core/gimpdrawable-floating-selection.c new file mode 100644 index 0000000..71866b1 --- /dev/null +++ b/app/core/gimpdrawable-floating-selection.c @@ -0,0 +1,512 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimpapplicator.h" + +#include "gimpchannel.h" +#include "gimpdrawable-floating-selection.h" +#include "gimpdrawable-filters.h" +#include "gimpdrawable-private.h" +#include "gimpimage.h" +#include "gimplayer.h" + +#include "gimp-log.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_drawable_remove_fs_filter (GimpDrawable *drawable); +static void gimp_drawable_sync_fs_filter (GimpDrawable *drawable); + +static void gimp_drawable_fs_notify (GObject *object, + const GParamSpec *pspec, + GimpDrawable *drawable); +static void gimp_drawable_fs_lock_position_changed (GimpDrawable *signal_drawable, + GimpDrawable *drawable); +static void gimp_drawable_fs_format_changed (GimpDrawable *signal_drawable, + GimpDrawable *drawable); +static void gimp_drawable_fs_affect_changed (GimpImage *image, + GimpChannelType channel, + GimpDrawable *drawable); +static void gimp_drawable_fs_mask_changed (GimpImage *image, + GimpDrawable *drawable); +static void gimp_drawable_fs_visibility_changed (GimpLayer *fs, + GimpDrawable *drawable); +static void gimp_drawable_fs_excludes_backdrop_changed (GimpLayer *fs, + GimpDrawable *drawable); +static void gimp_drawable_fs_bounding_box_changed (GimpLayer *fs, + GimpDrawable *drawable); +static void gimp_drawable_fs_update (GimpLayer *fs, + gint x, + gint y, + gint width, + gint height, + GimpDrawable *drawable); + + +/* public functions */ + +GimpLayer * +gimp_drawable_get_floating_sel (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + return drawable->private->floating_selection; +} + +void +gimp_drawable_attach_floating_sel (GimpDrawable *drawable, + GimpLayer *fs) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (gimp_drawable_get_floating_sel (drawable) == NULL); + g_return_if_fail (GIMP_IS_LAYER (fs)); + + GIMP_LOG (FLOATING_SELECTION, "%s", G_STRFUNC); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + drawable->private->floating_selection = fs; + gimp_image_set_floating_selection (image, fs); + + /* clear the selection */ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fs)); + + gimp_item_bind_visible_to_active (GIMP_ITEM (fs), FALSE); + gimp_filter_set_active (GIMP_FILTER (fs), FALSE); + + _gimp_drawable_add_floating_sel_filter (drawable); + + g_signal_connect (fs, "visibility-changed", + G_CALLBACK (gimp_drawable_fs_visibility_changed), + drawable); + g_signal_connect (fs, "excludes-backdrop-changed", + G_CALLBACK (gimp_drawable_fs_excludes_backdrop_changed), + drawable); + g_signal_connect (fs, "bounding-box-changed", + G_CALLBACK (gimp_drawable_fs_bounding_box_changed), + drawable); + g_signal_connect (fs, "update", + G_CALLBACK (gimp_drawable_fs_update), + drawable); + + gimp_drawable_fs_update (fs, + 0, 0, + gimp_item_get_width (GIMP_ITEM (fs)), + gimp_item_get_height (GIMP_ITEM (fs)), + drawable); +} + +void +gimp_drawable_detach_floating_sel (GimpDrawable *drawable) +{ + GimpImage *image; + GimpLayer *fs; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_drawable_get_floating_sel (drawable) != NULL); + + GIMP_LOG (FLOATING_SELECTION, "%s", G_STRFUNC); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + fs = drawable->private->floating_selection; + + gimp_drawable_remove_fs_filter (drawable); + + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_visibility_changed, + drawable); + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_excludes_backdrop_changed, + drawable); + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_bounding_box_changed, + drawable); + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_update, + drawable); + + gimp_drawable_fs_update (fs, + 0, 0, + gimp_item_get_width (GIMP_ITEM (fs)), + gimp_item_get_height (GIMP_ITEM (fs)), + drawable); + + gimp_item_bind_visible_to_active (GIMP_ITEM (fs), TRUE); + + /* clear the selection */ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fs)); + + gimp_image_set_floating_selection (image, NULL); + drawable->private->floating_selection = NULL; +} + +GimpFilter * +gimp_drawable_get_floating_sel_filter (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_drawable_get_floating_sel (drawable) != NULL, NULL); + + /* Ensure that the graph is construced before the filter is used. + * Otherwise, we rely on the projection to cause the graph to be + * constructed, which fails for images that aren't displayed. + */ + gimp_filter_get_node (GIMP_FILTER (drawable)); + + return drawable->private->fs_filter; +} + + +/* private functions */ + +void +_gimp_drawable_add_floating_sel_filter (GimpDrawable *drawable) +{ + GimpDrawablePrivate *private = drawable->private; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + GeglNode *node; + GeglNode *fs_source; + + if (! private->source_node) + return; + + private->fs_filter = gimp_filter_new (_("Floating Selection")); + gimp_viewable_set_icon_name (GIMP_VIEWABLE (private->fs_filter), + "gimp-floating-selection"); + + node = gimp_filter_get_node (private->fs_filter); + + fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs)); + + /* rip the fs' source node out of its graph */ + if (fs->layer_offset_node) + { + gegl_node_disconnect (fs->layer_offset_node, "input"); + gegl_node_remove_child (gimp_filter_get_node (GIMP_FILTER (fs)), + fs_source); + } + + gegl_node_add_child (node, fs_source); + + private->fs_applicator = gimp_applicator_new (node); + + gimp_filter_set_applicator (private->fs_filter, private->fs_applicator); + + gimp_applicator_set_cache (private->fs_applicator, TRUE); + + private->fs_crop_node = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + + gegl_node_connect_to (fs_source, "output", + private->fs_crop_node, "input"); + gegl_node_connect_to (private->fs_crop_node, "output", + node, "aux"); + + gimp_drawable_add_filter (drawable, private->fs_filter); + + g_signal_connect (fs, "notify", + G_CALLBACK (gimp_drawable_fs_notify), + drawable); + g_signal_connect (drawable, "notify::offset-x", + G_CALLBACK (gimp_drawable_fs_notify), + drawable); + g_signal_connect (drawable, "notify::offset-y", + G_CALLBACK (gimp_drawable_fs_notify), + drawable); + g_signal_connect (drawable, "lock-position-changed", + G_CALLBACK (gimp_drawable_fs_lock_position_changed), + drawable); + g_signal_connect (drawable, "format-changed", + G_CALLBACK (gimp_drawable_fs_format_changed), + drawable); + g_signal_connect (image, "component-active-changed", + G_CALLBACK (gimp_drawable_fs_affect_changed), + drawable); + g_signal_connect (image, "mask-changed", + G_CALLBACK (gimp_drawable_fs_mask_changed), + drawable); + + gimp_drawable_sync_fs_filter (drawable); +} + + +/* private functions */ + +static void +gimp_drawable_remove_fs_filter (GimpDrawable *drawable) +{ + GimpDrawablePrivate *private = drawable->private; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + + if (private->fs_filter) + { + GeglNode *node; + GeglNode *fs_source; + + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_notify, + drawable); + g_signal_handlers_disconnect_by_func (drawable, + gimp_drawable_fs_notify, + drawable); + g_signal_handlers_disconnect_by_func (drawable, + gimp_drawable_fs_lock_position_changed, + drawable); + g_signal_handlers_disconnect_by_func (drawable, + gimp_drawable_fs_format_changed, + drawable); + g_signal_handlers_disconnect_by_func (image, + gimp_drawable_fs_affect_changed, + drawable); + g_signal_handlers_disconnect_by_func (image, + gimp_drawable_fs_mask_changed, + drawable); + + gimp_drawable_remove_filter (drawable, private->fs_filter); + + node = gimp_filter_get_node (private->fs_filter); + + fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs)); + + gegl_node_remove_child (node, fs_source); + + /* plug the fs' source node back into its graph */ + if (fs->layer_offset_node) + { + gegl_node_add_child (gimp_filter_get_node (GIMP_FILTER (fs)), + fs_source); + gegl_node_connect_to (fs_source, "output", + fs->layer_offset_node, "input"); + } + + g_clear_object (&private->fs_filter); + g_clear_object (&private->fs_applicator); + + private->fs_crop_node = NULL; + + gimp_drawable_update_bounding_box (drawable); + } +} + +static void +gimp_drawable_sync_fs_filter (GimpDrawable *drawable) +{ + GimpDrawablePrivate *private = drawable->private; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpChannel *mask = gimp_image_get_mask (image); + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + gint off_x, off_y; + gint fs_off_x, fs_off_y; + + gimp_filter_set_active (private->fs_filter, + gimp_item_get_visible (GIMP_ITEM (fs))); + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + gimp_item_get_offset (GIMP_ITEM (fs), &fs_off_x, &fs_off_y); + + if (gimp_item_get_clip (GIMP_ITEM (drawable), GIMP_TRANSFORM_RESIZE_ADJUST) == + GIMP_TRANSFORM_RESIZE_CLIP || + ! gimp_drawable_has_alpha (drawable)) + { + gegl_node_set ( + private->fs_crop_node, + "operation", "gegl:crop", + "x", (gdouble) (off_x - fs_off_x), + "y", (gdouble) (off_y - fs_off_y), + "width", (gdouble) gimp_item_get_width (GIMP_ITEM (drawable)), + "height", (gdouble) gimp_item_get_height (GIMP_ITEM (drawable)), + NULL); + } + else + { + gegl_node_set ( + private->fs_crop_node, + "operation", "gegl:nop", + NULL); + } + + gimp_applicator_set_apply_offset (private->fs_applicator, + fs_off_x - off_x, + fs_off_y - off_y); + + if (gimp_channel_is_empty (mask)) + { + gimp_applicator_set_mask_buffer (private->fs_applicator, NULL); + } + else + { + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_applicator_set_mask_buffer (private->fs_applicator, buffer); + gimp_applicator_set_mask_offset (private->fs_applicator, + -off_x, -off_y); + } + + gimp_applicator_set_opacity (private->fs_applicator, + gimp_layer_get_opacity (fs)); + gimp_applicator_set_mode (private->fs_applicator, + gimp_layer_get_mode (fs), + gimp_layer_get_blend_space (fs), + gimp_layer_get_composite_space (fs), + gimp_layer_get_composite_mode (fs)); + gimp_applicator_set_affect (private->fs_applicator, + gimp_drawable_get_active_mask (drawable)); + gimp_applicator_set_output_format (private->fs_applicator, + gimp_drawable_get_format (drawable)); + + gimp_drawable_update_bounding_box (drawable); +} + +static void +gimp_drawable_fs_notify (GObject *object, + const GParamSpec *pspec, + GimpDrawable *drawable) +{ + if (! strcmp (pspec->name, "offset-x") || + ! strcmp (pspec->name, "offset-y") || + ! strcmp (pspec->name, "visible") || + ! strcmp (pspec->name, "mode") || + ! strcmp (pspec->name, "blend-space") || + ! strcmp (pspec->name, "composite-space") || + ! strcmp (pspec->name, "composite-mode") || + ! strcmp (pspec->name, "opacity")) + { + gimp_drawable_sync_fs_filter (drawable); + } +} + +static void +gimp_drawable_fs_lock_position_changed (GimpDrawable *signal_drawable, + GimpDrawable *drawable) +{ + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + + gimp_drawable_sync_fs_filter (drawable); + + gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_format_changed (GimpDrawable *signal_drawable, + GimpDrawable *drawable) +{ + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + + gimp_drawable_sync_fs_filter (drawable); + + gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_affect_changed (GimpImage *image, + GimpChannelType channel, + GimpDrawable *drawable) +{ + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + + gimp_drawable_sync_fs_filter (drawable); + + gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_mask_changed (GimpImage *image, + GimpDrawable *drawable) +{ + GimpLayer *fs = gimp_drawable_get_floating_sel (drawable); + + gimp_drawable_sync_fs_filter (drawable); + + gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_visibility_changed (GimpLayer *fs, + GimpDrawable *drawable) +{ + if (gimp_layer_get_excludes_backdrop (fs)) + gimp_drawable_update (drawable, 0, 0, -1, -1); + else + gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_excludes_backdrop_changed (GimpLayer *fs, + GimpDrawable *drawable) +{ + if (gimp_item_get_visible (GIMP_ITEM (fs))) + gimp_drawable_update (drawable, 0, 0, -1, -1); +} + +static void +gimp_drawable_fs_bounding_box_changed (GimpLayer *fs, + GimpDrawable *drawable) +{ + gimp_drawable_update_bounding_box (drawable); +} + +static void +gimp_drawable_fs_update (GimpLayer *fs, + gint x, + gint y, + gint width, + gint height, + GimpDrawable *drawable) +{ + GeglRectangle bounding_box; + GeglRectangle rect; + gint fs_off_x, fs_off_y; + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (fs), &fs_off_x, &fs_off_y); + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + bounding_box = gimp_drawable_get_bounding_box (drawable); + + bounding_box.x += off_x; + bounding_box.y += off_y; + + rect.x = x + fs_off_x; + rect.y = y + fs_off_y; + rect.width = width; + rect.height = height; + + if (gegl_rectangle_intersect (&rect, &rect, &bounding_box)) + { + gimp_drawable_update (drawable, + rect.x - off_x, rect.y - off_y, + rect.width, rect.height); + } +} diff --git a/app/core/gimpdrawable-floating-selection.h b/app/core/gimpdrawable-floating-selection.h new file mode 100644 index 0000000..336a0cf --- /dev/null +++ b/app/core/gimpdrawable-floating-selection.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_FLOATING_SELECTION_H__ +#define __GIMP_DRAWABLE_FLOATING_SELECTION_H__ + + +GimpLayer * gimp_drawable_get_floating_sel (GimpDrawable *drawable); +void gimp_drawable_attach_floating_sel (GimpDrawable *drawable, + GimpLayer *floating_sel); +void gimp_drawable_detach_floating_sel (GimpDrawable *drawable); +GimpFilter * gimp_drawable_get_floating_sel_filter (GimpDrawable *drawable); + +void _gimp_drawable_add_floating_sel_filter (GimpDrawable *drawable); + + +#endif /* __GIMP_DRAWABLE_FLOATING_SELECTION_H__ */ diff --git a/app/core/gimpdrawable-foreground-extract.c b/app/core/gimpdrawable-foreground-extract.c new file mode 100644 index 0000000..5846363 --- /dev/null +++ b/app/core/gimpdrawable-foreground-extract.c @@ -0,0 +1,150 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "gimpchannel.h" +#include "gimpdrawable.h" +#include "gimpdrawable-foreground-extract.h" +#include "gimpimage.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GeglBuffer * +gimp_drawable_foreground_extract (GimpDrawable *drawable, + GimpMattingEngine engine, + gint global_iterations, + gint levin_levels, + gint levin_active_levels, + GeglBuffer *trimap, + GimpProgress *progress) +{ + GeglBuffer *drawable_buffer; + GeglNode *gegl; + GeglNode *input_node; + GeglNode *trimap_node; + GeglNode *matting_node; + GeglNode *output_node; + GeglBuffer *buffer; + GeglProcessor *processor; + gdouble value; + gint off_x, off_y; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (trimap), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + progress = gimp_progress_start (progress, FALSE, + _("Computing alpha of unknown pixels")); + + drawable_buffer = gimp_drawable_get_buffer (drawable); + + gegl = gegl_node_new (); + + trimap_node = gegl_node_new_child (gegl, + "operation", "gegl:buffer-source", + "buffer", trimap, + NULL); + input_node = gegl_node_new_child (gegl, + "operation", "gegl:buffer-source", + "buffer", drawable_buffer, + NULL); + output_node = gegl_node_new_child (gegl, + "operation", "gegl:buffer-sink", + "buffer", &buffer, + "format", NULL, + NULL); + + if (engine == GIMP_MATTING_ENGINE_GLOBAL) + { + matting_node = gegl_node_new_child (gegl, + "operation", "gegl:matting-global", + "iterations", global_iterations, + NULL); + } + else + { + matting_node = gegl_node_new_child (gegl, + "operation", "gegl:matting-levin", + "levels", levin_levels, + "active_levels", levin_active_levels, + NULL); + } + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + if (off_x || off_y) + { + GeglNode *pre; + GeglNode *post; + + pre = gegl_node_new_child (gegl, + "operation", "gegl:translate", + "x", -1.0 * off_x, + "y", -1.0 * off_y, + NULL); + post = gegl_node_new_child (gegl, + "operation", "gegl:translate", + "x", 1.0 * off_x, + "y", 1.0 * off_y, + NULL); + + gegl_node_connect_to (trimap_node, "output", pre, "input"); + gegl_node_connect_to (pre, "output", matting_node, "aux"); + gegl_node_link_many (input_node, matting_node, post, output_node, NULL); + } + else + { + gegl_node_connect_to (input_node, "output", + matting_node, "input"); + gegl_node_connect_to (trimap_node, "output", + matting_node, "aux"); + gegl_node_connect_to (matting_node, "output", + output_node, "input"); + } + + processor = gegl_node_new_processor (output_node, NULL); + + while (gegl_processor_work (processor, &value)) + { + if (progress) + gimp_progress_set_value (progress, value); + } + + if (progress) + gimp_progress_end (progress); + + g_object_unref (processor); + + g_object_unref (gegl); + + return buffer; +} diff --git a/app/core/gimpdrawable-foreground-extract.h b/app/core/gimpdrawable-foreground-extract.h new file mode 100644 index 0000000..19e2356 --- /dev/null +++ b/app/core/gimpdrawable-foreground-extract.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_FOREGROUND_EXTRACT_H__ +#define __GIMP_DRAWABLE_FOREGROUND_EXTRACT_H__ + + +GeglBuffer * gimp_drawable_foreground_extract (GimpDrawable *drawable, + GimpMattingEngine engine, + gint global_iterations, + gint levin_levels, + gint levin_active_levels, + GeglBuffer *trimap, + GimpProgress *progress); + + +#endif /* __GIMP_DRAWABLE_FOREGROUND_EXTRACT_H__ */ diff --git a/app/core/gimpdrawable-gradient.c b/app/core/gimpdrawable-gradient.c new file mode 100644 index 0000000..2d7cbe8 --- /dev/null +++ b/app/core/gimpdrawable-gradient.c @@ -0,0 +1,313 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpcontext.h" +#include "gimpdrawable-gradient.h" +#include "gimpgradient.h" +#include "gimpimage.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_drawable_gradient (GimpDrawable *drawable, + GimpContext *context, + GimpGradient *gradient, + GeglDistanceMetric metric, + GimpLayerMode paint_mode, + GimpGradientType gradient_type, + gdouble opacity, + gdouble offset, + GimpRepeatMode repeat, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + gboolean supersample, + gint max_depth, + gdouble threshold, + gboolean dither, + gdouble startx, + gdouble starty, + gdouble endx, + gdouble endy, + GimpProgress *progress) +{ + GimpImage *image; + GeglBuffer *buffer; + GeglBuffer *shapeburst = NULL; + GeglNode *render; + gint x, y, width, height; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) + return; + + gimp_set_busy (image->gimp); + + /* Always create an alpha temp buf (for generality) */ + buffer = gegl_buffer_new (GEGL_RECTANGLE (x, y, width, height), + gimp_drawable_get_format_with_alpha (drawable)); + + if (gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR && + gradient_type <= GIMP_GRADIENT_SHAPEBURST_DIMPLED) + { + shapeburst = + gimp_drawable_gradient_shapeburst_distmap (drawable, metric, + GEGL_RECTANGLE (x, y, width, height), + progress); + } + + gimp_drawable_gradient_adjust_coords (drawable, + gradient_type, + GEGL_RECTANGLE (x, y, width, height), + &startx, &starty, &endx, &endy); + + render = gegl_node_new_child (NULL, + "operation", "gimp:gradient", + "context", context, + "gradient", gradient, + "start-x", startx, + "start-y", starty, + "end-x", endx, + "end-y", endy, + "gradient-type", gradient_type, + "gradient-repeat", repeat, + "offset", offset, + "gradient-reverse", reverse, + "gradient-blend-color-space", blend_color_space, + "supersample", supersample, + "supersample-depth", max_depth, + "supersample-threshold", threshold, + "dither", dither, + NULL); + + gimp_gegl_apply_operation (shapeburst, progress, C_("undo-type", "Gradient"), + render, + buffer, GEGL_RECTANGLE (x, y, width, height), + FALSE); + + g_object_unref (render); + + if (shapeburst) + g_object_unref (shapeburst); + + gimp_drawable_apply_buffer (drawable, buffer, + GEGL_RECTANGLE (x, y, width, height), + TRUE, C_("undo-type", "Gradient"), + opacity, paint_mode, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + gimp_layer_mode_get_paint_composite_mode (paint_mode), + NULL, x, y); + + gimp_drawable_update (drawable, x, y, width, height); + + g_object_unref (buffer); + + gimp_unset_busy (image->gimp); +} + +GeglBuffer * +gimp_drawable_gradient_shapeburst_distmap (GimpDrawable *drawable, + GeglDistanceMetric metric, + const GeglRectangle *region, + GimpProgress *progress) +{ + GimpChannel *mask; + GimpImage *image; + GeglBuffer *dist_buffer; + GeglBuffer *temp_buffer; + GeglNode *shapeburst; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* allocate the distance map */ + dist_buffer = gegl_buffer_new (region, babl_format ("Y float")); + + /* allocate the selection mask copy */ + temp_buffer = gegl_buffer_new (region, babl_format ("Y float")); + + mask = gimp_image_get_mask (image); + + /* If the image mask is not empty, use it as the shape burst source */ + if (! gimp_channel_is_empty (mask)) + { + gint x, y, width, height; + gint off_x, off_y; + + gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height); + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + /* copy the mask to the temp mask */ + gimp_gegl_buffer_copy ( + gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), + GEGL_RECTANGLE (x + off_x, y + off_y, width, height), + GEGL_ABYSS_NONE, temp_buffer, region); + } + else + { + /* If the intended drawable has an alpha channel, use that */ + if (gimp_drawable_has_alpha (drawable)) + { + const Babl *component_format; + + component_format = babl_format ("A float"); + + /* extract the aplha into the temp mask */ + gegl_buffer_set_format (temp_buffer, component_format); + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), region, + GEGL_ABYSS_NONE, + temp_buffer, region); + gegl_buffer_set_format (temp_buffer, NULL); + } + else + { + GeglColor *white = gegl_color_new ("white"); + + /* Otherwise, just fill the shapeburst to white */ + gegl_buffer_set_color (temp_buffer, NULL, white); + g_object_unref (white); + } + } + + shapeburst = gegl_node_new_child (NULL, + "operation", "gegl:distance-transform", + "normalize", TRUE, + "metric", metric, + NULL); + + if (progress) + gimp_gegl_progress_connect (shapeburst, progress, + _("Calculating distance map")); + + gimp_gegl_apply_operation (temp_buffer, NULL, NULL, + shapeburst, + dist_buffer, region, FALSE); + + g_object_unref (shapeburst); + + g_object_unref (temp_buffer); + + return dist_buffer; +} + +void +gimp_drawable_gradient_adjust_coords (GimpDrawable *drawable, + GimpGradientType gradient_type, + const GeglRectangle *region, + gdouble *startx, + gdouble *starty, + gdouble *endx, + gdouble *endy) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (region != NULL); + g_return_if_fail (startx != NULL); + g_return_if_fail (starty != NULL); + g_return_if_fail (endx != NULL); + g_return_if_fail (endy != NULL); + + /* we potentially adjust the gradient coordinates according to the gradient + * type, so that in cases where the gradient span is not related to the + * segment length, the gradient cache (in GimpOperationGradient) is big + * enough not to produce banding. + */ + + switch (gradient_type) + { + /* for conical gradients, use a segment with the original origin and + * direction, whose length is the circumference of the largest circle + * centered at the origin, passing through one of the regions's vertices. + */ + case GIMP_GRADIENT_CONICAL_SYMMETRIC: + case GIMP_GRADIENT_CONICAL_ASYMMETRIC: + { + gdouble r = 0.0; + GimpVector2 v; + + r = MAX (r, hypot (region->x - *startx, + region->y - *starty)); + r = MAX (r, hypot (region->x + region->width - *startx, + region->y - *starty)); + r = MAX (r, hypot (region->x - *startx, + region->y + region->height - *starty)); + r = MAX (r, hypot (region->x + region->width - *startx, + region->y + region->height - *starty)); + + /* symmetric conical gradients only span half a revolution, and + * therefore require only half the cache size. + */ + if (gradient_type == GIMP_GRADIENT_CONICAL_SYMMETRIC) + r /= 2.0; + + gimp_vector2_set (&v, *endx - *startx, *endy - *starty); + gimp_vector2_normalize (&v); + gimp_vector2_mul (&v, 2.0 * G_PI * r); + + *endx = *startx + v.x; + *endy = *starty + v.y; + } + break; + + /* for shaped gradients, only the segment's length matters; use the + * regions's diagonal, which is the largest possible distance between two + * points in the region. + */ + case GIMP_GRADIENT_SHAPEBURST_ANGULAR: + case GIMP_GRADIENT_SHAPEBURST_SPHERICAL: + case GIMP_GRADIENT_SHAPEBURST_DIMPLED: + *startx = region->x; + *starty = region->y; + *endx = region->x + region->width; + *endy = region->y + region->height; + break; + + default: + break; + } +} diff --git a/app/core/gimpdrawable-gradient.h b/app/core/gimpdrawable-gradient.h new file mode 100644 index 0000000..4f38c8a --- /dev/null +++ b/app/core/gimpdrawable-gradient.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 . + */ + +#ifndef __GIMP_DRAWABLE_GRADIENT_H__ +#define __GIMP_DRAWABLE_GRADIENT_H__ + + +void gimp_drawable_gradient (GimpDrawable *drawable, + GimpContext *context, + GimpGradient *gradient, + GeglDistanceMetric metric, + GimpLayerMode paint_mode, + GimpGradientType gradient_type, + gdouble opacity, + gdouble offset, + GimpRepeatMode repeat, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + gboolean supersample, + gint max_depth, + gdouble threshold, + gboolean dither, + gdouble startx, + gdouble starty, + gdouble endx, + gdouble endy, + GimpProgress *progress); + +GeglBuffer * gimp_drawable_gradient_shapeburst_distmap (GimpDrawable *drawable, + GeglDistanceMetric metric, + const GeglRectangle *region, + GimpProgress *progress); + +void gimp_drawable_gradient_adjust_coords (GimpDrawable *drawable, + GimpGradientType gradient_type, + const GeglRectangle *region, + gdouble *startx, + gdouble *starty, + gdouble *endx, + gdouble *endy); + + +#endif /* __GIMP_DRAWABLE_GRADIENT_H__ */ diff --git a/app/core/gimpdrawable-histogram.c b/app/core/gimpdrawable-histogram.c new file mode 100644 index 0000000..b0be3f5 --- /dev/null +++ b/app/core/gimpdrawable-histogram.c @@ -0,0 +1,258 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphistogram module Copyright (C) 1999 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-gegl-nodes.h" +#include "gegl/gimptilehandlervalidate.h" + +#include "gimpasync.h" +#include "gimpchannel.h" +#include "gimpdrawable-filters.h" +#include "gimpdrawable-histogram.h" +#include "gimphistogram.h" +#include "gimpimage.h" +#include "gimpprojectable.h" + + +/* local function prototypes */ + +static GimpAsync * gimp_drawable_calculate_histogram_internal (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters, + gboolean run_async); + + +/* private functions */ + + +static GimpAsync * +gimp_drawable_calculate_histogram_internal (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters, + gboolean run_async) +{ + GimpAsync *async = NULL; + GimpImage *image; + GimpChannel *mask; + gint x, y, width, height; + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) + goto end; + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + mask = gimp_image_get_mask (image); + + if (FALSE) + { + GeglNode *node = gegl_node_new (); + GeglNode *source; + GeglNode *histogram_sink; + GeglProcessor *processor; + + if (with_filters) + { + source = gimp_drawable_get_source_node (drawable); + } + else + { + source = + gimp_gegl_add_buffer_source (node, + gimp_drawable_get_buffer (drawable), + 0, 0); + } + + histogram_sink = + gegl_node_new_child (node, + "operation", "gimp:histogram-sink", + "histogram", histogram, + NULL); + + gegl_node_connect_to (source, "output", + histogram_sink, "input"); + + if (! gimp_channel_is_empty (mask)) + { + GeglNode *mask_source; + gint off_x, off_y; + + g_printerr ("adding mask aux\n"); + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + mask_source = + gimp_gegl_add_buffer_source (node, + gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), + -off_x, -off_y); + + gegl_node_connect_to (mask_source, "output", + histogram_sink, "aux"); + } + + processor = gegl_node_new_processor (histogram_sink, + GEGL_RECTANGLE (x, y, width, height)); + + while (gegl_processor_work (processor, NULL)); + + g_object_unref (processor); + g_object_unref (node); + } + else + { + GeglBuffer *buffer = gimp_drawable_get_buffer (drawable); + GimpProjectable *projectable = NULL; + + if (with_filters && gimp_drawable_has_filters (drawable)) + { + GimpTileHandlerValidate *validate; + GeglNode *node; + + node = gimp_drawable_get_source_node (drawable); + + buffer = gegl_buffer_new (gegl_buffer_get_extent (buffer), + gegl_buffer_get_format (buffer)); + + validate = + GIMP_TILE_HANDLER_VALIDATE (gimp_tile_handler_validate_new (node)); + + gimp_tile_handler_validate_assign (validate, buffer); + + g_object_unref (validate); + + gimp_tile_handler_validate_invalidate (validate, + gegl_buffer_get_extent (buffer)); + +#if 0 + /* this would keep the buffer updated across drawable or + * filter changes, but the histogram is created in one go + * and doesn't need the signal connection + */ + g_signal_connect_object (node, "invalidated", + G_CALLBACK (gimp_tile_handler_validate_invalidate), + validate, G_CONNECT_SWAPPED); +#endif + + if (GIMP_IS_PROJECTABLE (drawable)) + projectable = GIMP_PROJECTABLE (drawable); + } + else + { + g_object_ref (buffer); + } + + if (projectable) + gimp_projectable_begin_render (projectable); + + if (! gimp_channel_is_empty (mask)) + { + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + if (run_async) + { + async = gimp_histogram_calculate_async ( + histogram, buffer, + GEGL_RECTANGLE (x, y, width, height), + gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), + GEGL_RECTANGLE (x + off_x, y + off_y, + width, height)); + } + else + { + gimp_histogram_calculate ( + histogram, buffer, + GEGL_RECTANGLE (x, y, width, height), + gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), + GEGL_RECTANGLE (x + off_x, y + off_y, + width, height)); + } + } + else + { + if (run_async) + { + async = gimp_histogram_calculate_async ( + histogram, buffer, + GEGL_RECTANGLE (x, y, width, height), + NULL, NULL); + } + else + { + gimp_histogram_calculate ( + histogram, buffer, + GEGL_RECTANGLE (x, y, width, height), + NULL, NULL); + } + } + + if (projectable) + gimp_projectable_end_render (projectable); + + g_object_unref (buffer); + } + +end: + if (run_async && ! async) + { + async = gimp_async_new (); + + gimp_async_finish (async, NULL); + } + + return async; +} + + +/* public functions */ + + +void +gimp_drawable_calculate_histogram (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (histogram != NULL); + + gimp_drawable_calculate_histogram_internal (drawable, + histogram, with_filters, + FALSE); +} + +GimpAsync * +gimp_drawable_calculate_histogram_async (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (histogram != NULL, NULL); + + return gimp_drawable_calculate_histogram_internal (drawable, + histogram, with_filters, + TRUE); +} diff --git a/app/core/gimpdrawable-histogram.h b/app/core/gimpdrawable-histogram.h new file mode 100644 index 0000000..947393a --- /dev/null +++ b/app/core/gimpdrawable-histogram.h @@ -0,0 +1,32 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphistogram module Copyright (C) 1999 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DRAWABLE_HISTOGRAM_H__ +#define __GIMP_DRAWABLE_HISTOGRAM_H__ + + +void gimp_drawable_calculate_histogram (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters); +GimpAsync * gimp_drawable_calculate_histogram_async (GimpDrawable *drawable, + GimpHistogram *histogram, + gboolean with_filters); + + +#endif /* __GIMP_HISTOGRAM_H__ */ diff --git a/app/core/gimpdrawable-levels.c b/app/core/gimpdrawable-levels.c new file mode 100644 index 0000000..301326b --- /dev/null +++ b/app/core/gimpdrawable-levels.c @@ -0,0 +1,77 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "operations/gimplevelsconfig.h" + +#include "gimpdrawable.h" +#include "gimpdrawable-histogram.h" +#include "gimpdrawable-levels.h" +#include "gimpdrawable-operation.h" +#include "gimphistogram.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_drawable_levels_stretch (GimpDrawable *drawable, + GimpProgress *progress) +{ + GimpLevelsConfig *config; + GimpHistogram *histogram; + GeglNode *levels; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), NULL, NULL, NULL, NULL)) + return; + + config = g_object_new (GIMP_TYPE_LEVELS_CONFIG, NULL); + + histogram = gimp_histogram_new (FALSE); + gimp_drawable_calculate_histogram (drawable, histogram, FALSE); + + gimp_levels_config_stretch (config, histogram, + gimp_drawable_is_rgb (drawable)); + + g_object_unref (histogram); + + levels = g_object_new (GEGL_TYPE_NODE, + "operation", "gimp:levels", + NULL); + + gegl_node_set (levels, + "config", config, + NULL); + + gimp_drawable_apply_operation (drawable, progress, _("Levels"), + levels); + + g_object_unref (levels); + g_object_unref (config); +} diff --git a/app/core/gimpdrawable-levels.h b/app/core/gimpdrawable-levels.h new file mode 100644 index 0000000..22011c1 --- /dev/null +++ b/app/core/gimpdrawable-levels.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 . + */ + +#ifndef __GIMP_DRAWABLE_LEVELS_H__ +#define __GIMP_DRAWABLE_LEVELS_H__ + + +void gimp_drawable_levels_stretch (GimpDrawable *drawable, + GimpProgress *progress); + + +#endif /* __GIMP_DRAWABLE_LEVELS_H__ */ diff --git a/app/core/gimpdrawable-offset.c b/app/core/gimpdrawable-offset.c new file mode 100644 index 0000000..b7f5eb2 --- /dev/null +++ b/app/core/gimpdrawable-offset.c @@ -0,0 +1,83 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimpdrawable-offset.h" +#include "gimpdrawable-operation.h" + +#include "gimp-intl.h" + + +void +gimp_drawable_offset (GimpDrawable *drawable, + GimpContext *context, + gboolean wrap_around, + GimpOffsetType fill_type, + gint offset_x, + gint offset_y) +{ + GeglNode *node; + gint width; + gint height; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + NULL, NULL, &width, &height)) + { + return; + } + + if (wrap_around) + fill_type = GIMP_OFFSET_WRAP_AROUND; + + if (fill_type == GIMP_OFFSET_WRAP_AROUND) + { + offset_x %= width; + offset_y %= height; + } + + if (offset_x == 0 && offset_y == 0) + return; + + node = gegl_node_new_child (NULL, + "operation", "gimp:offset", + "context", context, + "type", fill_type, + "x", offset_x, + "y", offset_y, + NULL); + + gimp_drawable_apply_operation (drawable, NULL, + C_("undo-type", "Offset Drawable"), + node); + + g_object_unref (node); +} diff --git a/app/core/gimpdrawable-offset.h b/app/core/gimpdrawable-offset.h new file mode 100644 index 0000000..6629150 --- /dev/null +++ b/app/core/gimpdrawable-offset.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_OFFSET_H__ +#define __GIMP_DRAWABLE_OFFSET_H__ + + +void gimp_drawable_offset (GimpDrawable *drawable, + GimpContext *context, + gboolean wrap_around, + GimpOffsetType fill_type, + gint offset_x, + gint offset_y); + + +#endif /* __GIMP_DRAWABLE_OFFSET_H__ */ diff --git a/app/core/gimpdrawable-operation.c b/app/core/gimpdrawable-operation.c new file mode 100644 index 0000000..fdda286 --- /dev/null +++ b/app/core/gimpdrawable-operation.c @@ -0,0 +1,128 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-operation.c + * Copyright (C) 2007 Øyvind Kolås + * Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "operations/gimp-operation-config.h" +#include "operations/gimpoperationsettings.h" + +#include "gimpdrawable.h" +#include "gimpdrawable-operation.h" +#include "gimpdrawablefilter.h" +#include "gimpprogress.h" +#include "gimpsettings.h" + + +/* public functions */ + +void +gimp_drawable_apply_operation (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation) +{ + gimp_drawable_apply_operation_with_config (drawable, + progress, undo_desc, + operation, NULL); +} + +void +gimp_drawable_apply_operation_with_config (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + GObject *config) +{ + GimpDrawableFilter *filter; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (GEGL_IS_NODE (operation)); + g_return_if_fail (config == NULL || GIMP_IS_OPERATION_SETTINGS (config)); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + NULL, NULL, NULL, NULL)) + { + return; + } + + filter = gimp_drawable_filter_new (drawable, undo_desc, operation, NULL); + + gimp_drawable_filter_set_add_alpha (filter, + gimp_gegl_node_has_key (operation, + "needs-alpha")); + + if (config) + { + gimp_operation_config_sync_node (config, operation); + + gimp_operation_settings_sync_drawable_filter ( + GIMP_OPERATION_SETTINGS (config), filter); + } + + gimp_drawable_filter_apply (filter, NULL); + gimp_drawable_filter_commit (filter, progress, TRUE); + + g_object_unref (filter); + + if (progress) + gimp_progress_end (progress); +} + +void +gimp_drawable_apply_operation_by_name (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + const gchar *operation_type, + GObject *config) +{ + GeglNode *node; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (undo_desc != NULL); + g_return_if_fail (operation_type != NULL); + g_return_if_fail (config == NULL || GIMP_IS_SETTINGS (config)); + + node = g_object_new (GEGL_TYPE_NODE, + "operation", operation_type, + NULL); + + if (config) + gegl_node_set (node, + "config", config, + NULL); + + gimp_drawable_apply_operation (drawable, progress, undo_desc, node); + + g_object_unref (node); +} diff --git a/app/core/gimpdrawable-operation.h b/app/core/gimpdrawable-operation.h new file mode 100644 index 0000000..6ca381f --- /dev/null +++ b/app/core/gimpdrawable-operation.h @@ -0,0 +1,43 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-operation.h + * Copyright (C) 2007 Øyvind Kolås + * Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DRAWABLE_OPERATION_H__ +#define __GIMP_DRAWABLE_OPERATION_H__ + + +void gimp_drawable_apply_operation (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation); +void gimp_drawable_apply_operation_with_config (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + GObject *config); +void gimp_drawable_apply_operation_by_name (GimpDrawable *drawable, + GimpProgress *progress, + const gchar *undo_desc, + const gchar *operation_type, + GObject *config); + + +#endif /* __GIMP_DRAWABLE_OPERATION_H__ */ diff --git a/app/core/gimpdrawable-preview.c b/app/core/gimpdrawable-preview.c new file mode 100644 index 0000000..28ae1e2 --- /dev/null +++ b/app/core/gimpdrawable-preview.c @@ -0,0 +1,492 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimptilehandlervalidate.h" + +#include "gimp.h" +#include "gimp-parallel.h" +#include "gimp-utils.h" +#include "gimpasync.h" +#include "gimpchannel.h" +#include "gimpchunkiterator.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpdrawable-preview.h" +#include "gimpdrawable-private.h" +#include "gimplayer.h" +#include "gimptempbuf.h" + +#include "gimp-priorities.h" + + +typedef struct +{ + const Babl *format; + GeglBuffer *buffer; + GeglRectangle rect; + gdouble scale; + + GimpChunkIterator *iter; +} SubPreviewData; + + +/* local function prototypes */ + +static SubPreviewData * sub_preview_data_new (const Babl *format, + GeglBuffer *buffer, + const GeglRectangle *rect, + gdouble scale); +static void sub_preview_data_free (SubPreviewData *data); + + + +/* private functions */ + + +static SubPreviewData * +sub_preview_data_new (const Babl *format, + GeglBuffer *buffer, + const GeglRectangle *rect, + gdouble scale) +{ + SubPreviewData *data = g_slice_new (SubPreviewData); + + data->format = format; + data->buffer = g_object_ref (buffer); + data->rect = *rect; + data->scale = scale; + + data->iter = NULL; + + return data; +} + +static void +sub_preview_data_free (SubPreviewData *data) +{ + g_object_unref (data->buffer); + + if (data->iter) + gimp_chunk_iterator_stop (data->iter, TRUE); + + g_slice_free (SubPreviewData, data); +} + + +/* public functions */ + + +GimpTempBuf * +gimp_drawable_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpItem *item = GIMP_ITEM (viewable); + GimpImage *image = gimp_item_get_image (item); + + if (! image->gimp->config->layer_previews) + return NULL; + + return gimp_drawable_get_sub_preview (GIMP_DRAWABLE (viewable), + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item), + width, + height); +} + +GdkPixbuf * +gimp_drawable_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpItem *item = GIMP_ITEM (viewable); + GimpImage *image = gimp_item_get_image (item); + + if (! image->gimp->config->layer_previews) + return NULL; + + return gimp_drawable_get_sub_pixbuf (GIMP_DRAWABLE (viewable), + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item), + width, + height); +} + +const Babl * +gimp_drawable_get_preview_format (GimpDrawable *drawable) +{ + gboolean alpha; + gboolean linear; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + alpha = gimp_drawable_has_alpha (drawable); + linear = gimp_drawable_get_linear (drawable); + + switch (gimp_drawable_get_base_type (drawable)) + { + case GIMP_GRAY: + return gimp_babl_format (GIMP_GRAY, + gimp_babl_precision (GIMP_COMPONENT_TYPE_U8, + linear), + alpha); + + case GIMP_RGB: + return gimp_babl_format (GIMP_RGB, + gimp_babl_precision (GIMP_COMPONENT_TYPE_U8, + linear), + alpha); + + case GIMP_INDEXED: + if (alpha) + return babl_format ("R'G'B'A u8"); + else + return babl_format ("R'G'B' u8"); + } + + g_return_val_if_reached (NULL); +} + +GimpTempBuf * +gimp_drawable_get_sub_preview (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height) +{ + GimpItem *item; + GimpImage *image; + GeglBuffer *buffer; + GimpTempBuf *preview; + gdouble scale; + gint scaled_x; + gint scaled_y; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (src_x >= 0, NULL); + g_return_val_if_fail (src_y >= 0, NULL); + g_return_val_if_fail (src_width > 0, NULL); + g_return_val_if_fail (src_height > 0, NULL); + g_return_val_if_fail (dest_width > 0, NULL); + g_return_val_if_fail (dest_height > 0, NULL); + + item = GIMP_ITEM (drawable); + + g_return_val_if_fail ((src_x + src_width) <= gimp_item_get_width (item), NULL); + g_return_val_if_fail ((src_y + src_height) <= gimp_item_get_height (item), NULL); + + image = gimp_item_get_image (item); + + if (! image->gimp->config->layer_previews) + return NULL; + + buffer = gimp_drawable_get_buffer (drawable); + + preview = gimp_temp_buf_new (dest_width, dest_height, + gimp_drawable_get_preview_format (drawable)); + + scale = MIN ((gdouble) dest_width / (gdouble) src_width, + (gdouble) dest_height / (gdouble) src_height); + + scaled_x = RINT ((gdouble) src_x * scale); + scaled_y = RINT ((gdouble) src_y * scale); + + gegl_buffer_get (buffer, + GEGL_RECTANGLE (scaled_x, scaled_y, dest_width, dest_height), + scale, + gimp_temp_buf_get_format (preview), + gimp_temp_buf_get_data (preview), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + return preview; +} + +GdkPixbuf * +gimp_drawable_get_sub_pixbuf (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height) +{ + GimpItem *item; + GimpImage *image; + GeglBuffer *buffer; + GdkPixbuf *pixbuf; + gdouble scale; + gint scaled_x; + gint scaled_y; + GimpColorTransform *transform; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (src_x >= 0, NULL); + g_return_val_if_fail (src_y >= 0, NULL); + g_return_val_if_fail (src_width > 0, NULL); + g_return_val_if_fail (src_height > 0, NULL); + g_return_val_if_fail (dest_width > 0, NULL); + g_return_val_if_fail (dest_height > 0, NULL); + + item = GIMP_ITEM (drawable); + + g_return_val_if_fail ((src_x + src_width) <= gimp_item_get_width (item), NULL); + g_return_val_if_fail ((src_y + src_height) <= gimp_item_get_height (item), NULL); + + image = gimp_item_get_image (item); + + if (! image->gimp->config->layer_previews) + return NULL; + + buffer = gimp_drawable_get_buffer (drawable); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + dest_width, dest_height); + + scale = MIN ((gdouble) dest_width / (gdouble) src_width, + (gdouble) dest_height / (gdouble) src_height); + + scaled_x = RINT ((gdouble) src_x * scale); + scaled_y = RINT ((gdouble) src_y * scale); + + transform = gimp_image_get_color_transform_to_srgb_u8 (image); + + if (transform) + { + GimpTempBuf *temp_buf; + GeglBuffer *src_buf; + GeglBuffer *dest_buf; + + temp_buf = gimp_temp_buf_new (dest_width, dest_height, + gimp_drawable_get_format (drawable)); + + gegl_buffer_get (buffer, + GEGL_RECTANGLE (scaled_x, scaled_y, + dest_width, dest_height), + scale, + gimp_temp_buf_get_format (temp_buf), + gimp_temp_buf_get_data (temp_buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + src_buf = gimp_temp_buf_create_buffer (temp_buf); + dest_buf = gimp_pixbuf_create_buffer (pixbuf); + + gimp_temp_buf_unref (temp_buf); + + gimp_color_transform_process_buffer (transform, + src_buf, + GEGL_RECTANGLE (0, 0, + dest_width, dest_height), + dest_buf, + GEGL_RECTANGLE (0, 0, 0, 0)); + + g_object_unref (src_buf); + g_object_unref (dest_buf); + } + else + { + gegl_buffer_get (buffer, + GEGL_RECTANGLE (scaled_x, scaled_y, + dest_width, dest_height), + scale, + gimp_pixbuf_get_format (pixbuf), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + GEGL_ABYSS_CLAMP); + } + + return pixbuf; +} + +static void +gimp_drawable_get_sub_preview_async_func (GimpAsync *async, + SubPreviewData *data) +{ + GimpTempBuf *preview; + GimpTileHandlerValidate *validate; + + preview = gimp_temp_buf_new (data->rect.width, data->rect.height, + data->format); + + validate = gimp_tile_handler_validate_get_assigned (data->buffer); + + if (validate) + { + if (! data->iter) + { + cairo_region_t *region; + cairo_rectangle_int_t rect; + + rect.x = floor (data->rect.x / data->scale); + rect.y = floor (data->rect.y / data->scale); + rect.width = ceil ((data->rect.x + data->rect.width) / + data->scale) - rect.x; + rect.height = ceil ((data->rect.x + data->rect.height) / + data->scale) - rect.y; + + region = cairo_region_copy (validate->dirty_region); + + cairo_region_intersect_rectangle (region, &rect); + + data->iter = gimp_chunk_iterator_new (region); + } + + if (gimp_chunk_iterator_next (data->iter)) + { + GeglRectangle rect; + + gimp_tile_handler_validate_begin_validate (validate); + + while (gimp_chunk_iterator_get_rect (data->iter, &rect)) + { + gimp_tile_handler_validate_validate (validate, + data->buffer, &rect, + FALSE, FALSE); + } + + gimp_tile_handler_validate_end_validate (validate); + + return; + } + + data->iter = NULL; + } + + gegl_buffer_get (data->buffer, &data->rect, data->scale, + gimp_temp_buf_get_format (preview), + gimp_temp_buf_get_data (preview), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + sub_preview_data_free (data); + + gimp_async_finish_full (async, + preview, + (GDestroyNotify) gimp_temp_buf_unref); +} + +GimpAsync * +gimp_drawable_get_sub_preview_async (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height) +{ + GimpItem *item; + GimpImage *image; + GeglBuffer *buffer; + SubPreviewData *data; + gdouble scale; + gint scaled_x; + gint scaled_y; + static gint no_async_drawable_previews = -1; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (src_x >= 0, NULL); + g_return_val_if_fail (src_y >= 0, NULL); + g_return_val_if_fail (src_width > 0, NULL); + g_return_val_if_fail (src_height > 0, NULL); + g_return_val_if_fail (dest_width > 0, NULL); + g_return_val_if_fail (dest_height > 0, NULL); + + item = GIMP_ITEM (drawable); + + g_return_val_if_fail ((src_x + src_width) <= gimp_item_get_width (item), NULL); + g_return_val_if_fail ((src_y + src_height) <= gimp_item_get_height (item), NULL); + + image = gimp_item_get_image (item); + + if (! image->gimp->config->layer_previews) + return NULL; + + buffer = gimp_drawable_get_buffer (drawable); + + if (no_async_drawable_previews < 0) + { + no_async_drawable_previews = + (g_getenv ("GIMP_NO_ASYNC_DRAWABLE_PREVIEWS") != NULL); + } + + if (no_async_drawable_previews) + { + GimpAsync *async = gimp_async_new (); + + gimp_async_finish_full (async, + gimp_drawable_get_sub_preview (drawable, + src_x, + src_y, + src_width, + src_height, + dest_width, + dest_height), + (GDestroyNotify) gimp_temp_buf_unref); + + return async; + } + + scale = MIN ((gdouble) dest_width / (gdouble) src_width, + (gdouble) dest_height / (gdouble) src_height); + + scaled_x = RINT ((gdouble) src_x * scale); + scaled_y = RINT ((gdouble) src_y * scale); + + data = sub_preview_data_new ( + gimp_drawable_get_preview_format (drawable), + buffer, + GEGL_RECTANGLE (scaled_x, scaled_y, dest_width, dest_height), + scale); + + if (gimp_tile_handler_validate_get_assigned (buffer)) + { + return gimp_idle_run_async_full ( + GIMP_PRIORITY_VIEWABLE_IDLE, + (GimpRunAsyncFunc) gimp_drawable_get_sub_preview_async_func, + data, + (GDestroyNotify) sub_preview_data_free); + } + else + { + return gimp_parallel_run_async_full ( + +1, + (GimpRunAsyncFunc) gimp_drawable_get_sub_preview_async_func, + data, + (GDestroyNotify) sub_preview_data_free); + } +} diff --git a/app/core/gimpdrawable-preview.h b/app/core/gimpdrawable-preview.h new file mode 100644 index 0000000..31f8ade --- /dev/null +++ b/app/core/gimpdrawable-preview.h @@ -0,0 +1,63 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE__PREVIEW_H__ +#define __GIMP_DRAWABLE__PREVIEW_H__ + + +/* + * virtual functions of GimpDrawable -- don't call directly + */ +GimpTempBuf * gimp_drawable_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +GdkPixbuf * gimp_drawable_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + +/* + * normal functions (no virtuals) + */ +const Babl * gimp_drawable_get_preview_format (GimpDrawable *drawable); + +GimpTempBuf * gimp_drawable_get_sub_preview (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height); +GdkPixbuf * gimp_drawable_get_sub_pixbuf (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height); + +GimpAsync * gimp_drawable_get_sub_preview_async (GimpDrawable *drawable, + gint src_x, + gint src_y, + gint src_width, + gint src_height, + gint dest_width, + gint dest_height); + + +#endif /* __GIMP_DRAWABLE__PREVIEW_H__ */ diff --git a/app/core/gimpdrawable-private.h b/app/core/gimpdrawable-private.h new file mode 100644 index 0000000..780d5bf --- /dev/null +++ b/app/core/gimpdrawable-private.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 . + */ + +#ifndef __GIMP_DRAWABLE_PRIVATE_H__ +#define __GIMP_DRAWABLE_PRIVATE_H__ + +struct _GimpDrawablePrivate +{ + GeglBuffer *buffer; /* buffer for drawable data */ + GeglBuffer *shadow; /* shadow buffer */ + + GeglNode *source_node; + GeglNode *buffer_source_node; + GimpContainer *filter_stack; + GeglRectangle bounding_box; + + GimpLayer *floating_selection; + GimpFilter *fs_filter; + GeglNode *fs_crop_node; + GimpApplicator *fs_applicator; + + GeglNode *mode_node; + + gint paint_count; + GeglBuffer *paint_buffer; + cairo_region_t *paint_copy_region; + cairo_region_t *paint_update_region; +}; + +#endif /* __GIMP_DRAWABLE_PRIVATE_H__ */ diff --git a/app/core/gimpdrawable-shadow.c b/app/core/gimpdrawable-shadow.c new file mode 100644 index 0000000..4c42655 --- /dev/null +++ b/app/core/gimpdrawable-shadow.c @@ -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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "gimpdrawable.h" +#include "gimpdrawable-private.h" +#include "gimpdrawable-shadow.h" + + +GeglBuffer * +gimp_drawable_get_shadow_buffer (GimpDrawable *drawable) +{ + GimpItem *item; + gint width; + gint height; + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + item = GIMP_ITEM (drawable); + + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + format = gimp_drawable_get_format (drawable); + + if (drawable->private->shadow) + { + if ((width != gegl_buffer_get_width (drawable->private->shadow)) || + (height != gegl_buffer_get_height (drawable->private->shadow)) || + (format != gegl_buffer_get_format (drawable->private->shadow))) + { + gimp_drawable_free_shadow_buffer (drawable); + } + else + { + return drawable->private->shadow; + } + } + + drawable->private->shadow = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + width, height), + format); + + return drawable->private->shadow; +} + +void +gimp_drawable_free_shadow_buffer (GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + g_clear_object (&drawable->private->shadow); +} + +void +gimp_drawable_merge_shadow_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc) +{ + gint x, y; + gint width, height; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GEGL_IS_BUFFER (drawable->private->shadow)); + + /* A useful optimization here is to limit the update to the + * extents of the selection mask, as it cannot extend beyond + * them. + */ + if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) + { + GeglBuffer *buffer = g_object_ref (drawable->private->shadow); + + gimp_drawable_apply_buffer (drawable, buffer, + GEGL_RECTANGLE (x, y, width, height), + push_undo, undo_desc, + GIMP_OPACITY_OPAQUE, + GIMP_LAYER_MODE_REPLACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COMPOSITE_AUTO, + NULL, x, y); + + g_object_unref (buffer); + } +} diff --git a/app/core/gimpdrawable-shadow.h b/app/core/gimpdrawable-shadow.h new file mode 100644 index 0000000..b3ab582 --- /dev/null +++ b/app/core/gimpdrawable-shadow.h @@ -0,0 +1,32 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-shadow.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 . + */ + +#ifndef __GIMP_DRAWABLE_SHADOW_H__ +#define __GIMP_DRAWABLE_SHADOW_H__ + + +GeglBuffer * gimp_drawable_get_shadow_buffer (GimpDrawable *drawable); +void gimp_drawable_free_shadow_buffer (GimpDrawable *drawable); + +void gimp_drawable_merge_shadow_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc); + + +#endif /* __GIMP_DRAWABLE_SHADOW_H__ */ diff --git a/app/core/gimpdrawable-stroke.c b/app/core/gimpdrawable-stroke.c new file mode 100644 index 0000000..5b35d51 --- /dev/null +++ b/app/core/gimpdrawable-stroke.c @@ -0,0 +1,161 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-stroke.c + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpchannel.h" +#include "gimpdrawable-fill.h" +#include "gimpdrawable-stroke.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpscanconvert.h" +#include "gimpstrokeoptions.h" + +#include "vectors/gimpvectors.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_drawable_stroke_boundary (GimpDrawable *drawable, + GimpStrokeOptions *options, + const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y, + gboolean push_undo) +{ + GimpScanConvert *scan_convert; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_STROKE_OPTIONS (options)); + g_return_if_fail (bound_segs == NULL || n_bound_segs != 0); + g_return_if_fail (gimp_fill_options_get_style (GIMP_FILL_OPTIONS (options)) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL); + + scan_convert = gimp_scan_convert_new_from_boundary (bound_segs, n_bound_segs, + offset_x, offset_y); + + if (scan_convert) + { + gimp_drawable_stroke_scan_convert (drawable, options, + scan_convert, push_undo); + gimp_scan_convert_free (scan_convert); + } +} + +gboolean +gimp_drawable_stroke_vectors (GimpDrawable *drawable, + GimpStrokeOptions *options, + GimpVectors *vectors, + gboolean push_undo, + GError **error) +{ + const GimpBezierDesc *bezier; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (gimp_fill_options_get_style (GIMP_FILL_OPTIONS (options)) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL, + FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + bezier = gimp_vectors_get_bezier (vectors); + + if (bezier && bezier->num_data >= 2) + { + GimpScanConvert *scan_convert = gimp_scan_convert_new (); + + gimp_scan_convert_add_bezier (scan_convert, bezier); + gimp_drawable_stroke_scan_convert (drawable, options, + scan_convert, push_undo); + + gimp_scan_convert_free (scan_convert); + + return TRUE; + } + + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Not enough points to stroke")); + + return FALSE; +} + +void +gimp_drawable_stroke_scan_convert (GimpDrawable *drawable, + GimpStrokeOptions *options, + GimpScanConvert *scan_convert, + gboolean push_undo) +{ + gdouble width; + GimpUnit unit; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GIMP_IS_STROKE_OPTIONS (options)); + g_return_if_fail (scan_convert != NULL); + g_return_if_fail (gimp_fill_options_get_style (GIMP_FILL_OPTIONS (options)) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), NULL, NULL, NULL, NULL)) + return; + + width = gimp_stroke_options_get_width (options); + unit = gimp_stroke_options_get_unit (options); + + if (unit != GIMP_UNIT_PIXEL) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_scan_convert_set_pixel_ratio (scan_convert, yres / xres); + + width = gimp_units_to_pixels (width, unit, yres); + } + + gimp_scan_convert_stroke (scan_convert, width, + gimp_stroke_options_get_join_style (options), + gimp_stroke_options_get_cap_style (options), + gimp_stroke_options_get_miter_limit (options), + gimp_stroke_options_get_dash_offset (options), + gimp_stroke_options_get_dash_info (options)); + + gimp_drawable_fill_scan_convert (drawable, GIMP_FILL_OPTIONS (options), + scan_convert, push_undo); +} diff --git a/app/core/gimpdrawable-stroke.h b/app/core/gimpdrawable-stroke.h new file mode 100644 index 0000000..5f16e5b --- /dev/null +++ b/app/core/gimpdrawable-stroke.h @@ -0,0 +1,45 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawable-stroke.h + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DRAWABLE_STROKE_H__ +#define __GIMP_DRAWABLE_STROKE_H__ + + +void gimp_drawable_stroke_boundary (GimpDrawable *drawable, + GimpStrokeOptions *options, + const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y, + gboolean push_undo); + +gboolean gimp_drawable_stroke_vectors (GimpDrawable *drawable, + GimpStrokeOptions *options, + GimpVectors *vectors, + gboolean push_undo, + GError **error); + +void gimp_drawable_stroke_scan_convert (GimpDrawable *drawable, + GimpStrokeOptions *options, + GimpScanConvert *scan_convert, + gboolean push_undo); + + +#endif /* __GIMP_DRAWABLE_STROKE_H__ */ diff --git a/app/core/gimpdrawable-transform.c b/app/core/gimpdrawable-transform.c new file mode 100644 index 0000000..9cbedfd --- /dev/null +++ b/app/core/gimpdrawable-transform.c @@ -0,0 +1,1070 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2003 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp.h" +#include "gimp-transform-resize.h" +#include "gimpchannel.h" +#include "gimpcontext.h" +#include "gimpdrawable-transform.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimplayer-floating-selection.h" +#include "gimplayer-new.h" +#include "gimppickable.h" +#include "gimpprogress.h" +#include "gimpselection.h" + +#include "gimp-intl.h" + + +#if defined (HAVE_FINITE) +#define FINITE(x) finite(x) +#elif defined (HAVE_ISFINITE) +#define FINITE(x) isfinite(x) +#elif defined (G_OS_WIN32) +#define FINITE(x) _finite(x) +#else +#error "no FINITE() implementation available?!" +#endif + + +/* public functions */ + +GeglBuffer * +gimp_drawable_transform_buffer_affine (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *orig_buffer, + gint orig_offset_x, + gint orig_offset_y, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y, + GimpProgress *progress) +{ + GeglBuffer *new_buffer; + GimpMatrix3 m; + gint u1, v1, u2, v2; /* source bounding box */ + gint x1, y1, x2, y2; /* target bounding box */ + GimpMatrix3 gegl_matrix; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); + g_return_val_if_fail (matrix != NULL, NULL); + g_return_val_if_fail (buffer_profile != NULL, NULL); + g_return_val_if_fail (new_offset_x != NULL, NULL); + g_return_val_if_fail (new_offset_y != NULL, NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + *buffer_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); + + m = *matrix; + + if (direction == GIMP_TRANSFORM_BACKWARD) + { + /* Find the inverse of the transformation matrix */ + gimp_matrix3_invert (&m); + } + + u1 = orig_offset_x; + v1 = orig_offset_y; + u2 = u1 + gegl_buffer_get_width (orig_buffer); + v2 = v1 + gegl_buffer_get_height (orig_buffer); + + /* Find the bounding coordinates of target */ + gimp_transform_resize_boundary (&m, clip_result, + u1, v1, u2, v2, + &x1, &y1, &x2, &y2); + + /* Get the new temporary buffer for the transformed result */ + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), + gegl_buffer_get_format (orig_buffer)); + + gimp_matrix3_identity (&gegl_matrix); + gimp_matrix3_translate (&gegl_matrix, u1, v1); + gimp_matrix3_mult (&m, &gegl_matrix); + gimp_matrix3_translate (&gegl_matrix, -x1, -y1); + + gimp_gegl_apply_transform (orig_buffer, progress, NULL, + new_buffer, + interpolation_type, + &gegl_matrix); + + *new_offset_x = x1; + *new_offset_y = y1; + + return new_buffer; +} + +GeglBuffer * +gimp_drawable_transform_buffer_flip (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *orig_buffer, + gint orig_offset_x, + gint orig_offset_y, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y) +{ + const Babl *format; + GeglBuffer *new_buffer; + GeglBufferIterator *iter; + GeglRectangle src_rect; + GeglRectangle dest_rect; + gint bpp; + gint orig_x, orig_y; + gint orig_width, orig_height; + gint new_x, new_y; + gint new_width, new_height; + gint x, y; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); + g_return_val_if_fail (buffer_profile != NULL, NULL); + g_return_val_if_fail (new_offset_x != NULL, NULL); + g_return_val_if_fail (new_offset_y != NULL, NULL); + + *buffer_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); + + orig_x = orig_offset_x; + orig_y = orig_offset_y; + orig_width = gegl_buffer_get_width (orig_buffer); + orig_height = gegl_buffer_get_height (orig_buffer); + + new_x = orig_x; + new_y = orig_y; + new_width = orig_width; + new_height = orig_height; + + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + new_x = RINT (-((gdouble) orig_x + + (gdouble) orig_width - axis) + axis); + break; + + case GIMP_ORIENTATION_VERTICAL: + new_y = RINT (-((gdouble) orig_y + + (gdouble) orig_height - axis) + axis); + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_val_if_reached (NULL); + break; + } + + format = gegl_buffer_get_format (orig_buffer); + bpp = babl_format_get_bytes_per_pixel (format); + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + new_width, new_height), + format); + + if (clip_result && (new_x != orig_x || new_y != orig_y)) + { + GimpRGB bg; + GeglColor *color; + gint clip_x, clip_y; + gint clip_width, clip_height; + + *new_offset_x = orig_x; + *new_offset_y = orig_y; + + /* Use transparency, rather than the bg color, as the "outside" color of + * channels, and drawables with an alpha channel. + */ + if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format)) + { + gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); + } + else + { + gimp_context_get_background (context, &bg); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + &bg, &bg); + } + + color = gimp_gegl_color_new (&bg); + gegl_buffer_set_color (new_buffer, NULL, color); + g_object_unref (color); + + if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, + new_x, new_y, new_width, new_height, + &clip_x, &clip_y, + &clip_width, &clip_height)) + { + orig_x = new_x = clip_x - orig_x; + orig_y = new_y = clip_y - orig_y; + } + + orig_width = new_width = clip_width; + orig_height = new_height = clip_height; + } + else + { + *new_offset_x = new_x; + *new_offset_y = new_y; + + orig_x = 0; + orig_y = 0; + new_x = 0; + new_y = 0; + } + + if (new_width == 0 && new_height == 0) + return new_buffer; + + dest_rect.x = new_x; + dest_rect.y = new_y; + dest_rect.width = new_width; + dest_rect.height = new_height; + + iter = gegl_buffer_iterator_new (new_buffer, &dest_rect, 0, NULL, + GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE, 1); + + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + while (gegl_buffer_iterator_next (iter)) + { + gint stride = iter->items[0].roi.width * bpp; + + src_rect = iter->items[0].roi; + + src_rect.x = (orig_x + orig_width) - + (iter->items[0].roi.x - dest_rect.x) - + iter->items[0].roi.width; + + gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data, + stride, GEGL_ABYSS_NONE); + + for (y = 0; y < iter->items[0].roi.height; y++) + { + guint8 *left = iter->items[0].data; + guint8 *right = iter->items[0].data; + + left += y * stride; + right += y * stride + (iter->items[0].roi.width - 1) * bpp; + + for (x = 0; x < iter->items[0].roi.width / 2; x++) + { + guint8 temp[bpp]; + + memcpy (temp, left, bpp); + memcpy (left, right, bpp); + memcpy (right, temp, bpp); + + left += bpp; + right -= bpp; + } + } + } + break; + + case GIMP_ORIENTATION_VERTICAL: + while (gegl_buffer_iterator_next (iter)) + { + gint stride = iter->items[0].roi.width * bpp; + + src_rect = iter->items[0].roi; + + src_rect.y = (orig_y + orig_height) - + (iter->items[0].roi.y - dest_rect.y) - + iter->items[0].roi.height; + + gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data, + stride, GEGL_ABYSS_NONE); + + for (x = 0; x < iter->items[0].roi.width; x++) + { + guint8 *top = iter->items[0].data; + guint8 *bottom = iter->items[0].data; + + top += x * bpp; + bottom += x * bpp + (iter->items[0].roi.height - 1) * stride; + + for (y = 0; y < iter->items[0].roi.height / 2; y++) + { + guint8 temp[bpp]; + + memcpy (temp, top, bpp); + memcpy (top, bottom, bpp); + memcpy (bottom, temp, bpp); + + top += stride; + bottom -= stride; + } + } + } + break; + + case GIMP_ORIENTATION_UNKNOWN: + gegl_buffer_iterator_stop (iter); + break; + } + + return new_buffer; +} + +static void +gimp_drawable_transform_rotate_point (gint x, + gint y, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gint *new_x, + gint *new_y) +{ + g_return_if_fail (new_x != NULL); + g_return_if_fail (new_y != NULL); + + switch (rotate_type) + { + case GIMP_ROTATE_90: + *new_x = RINT (center_x - (gdouble) y + center_y); + *new_y = RINT (center_y + (gdouble) x - center_x); + break; + + case GIMP_ROTATE_180: + *new_x = RINT (center_x - ((gdouble) x - center_x)); + *new_y = RINT (center_y - ((gdouble) y - center_y)); + break; + + case GIMP_ROTATE_270: + *new_x = RINT (center_x + (gdouble) y - center_y); + *new_y = RINT (center_y - (gdouble) x + center_x); + break; + + default: + *new_x = x; + *new_y = y; + g_return_if_reached (); + } +} + +GeglBuffer * +gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *orig_buffer, + gint orig_offset_x, + gint orig_offset_y, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y) +{ + const Babl *format; + GeglBuffer *new_buffer; + GeglRectangle src_rect; + GeglRectangle dest_rect; + gint orig_x, orig_y; + gint orig_width, orig_height; + gint orig_bpp; + gint new_x, new_y; + gint new_width, new_height; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); + g_return_val_if_fail (buffer_profile != NULL, NULL); + g_return_val_if_fail (new_offset_x != NULL, NULL); + g_return_val_if_fail (new_offset_y != NULL, NULL); + + *buffer_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); + + orig_x = orig_offset_x; + orig_y = orig_offset_y; + orig_width = gegl_buffer_get_width (orig_buffer); + orig_height = gegl_buffer_get_height (orig_buffer); + orig_bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (orig_buffer)); + + switch (rotate_type) + { + case GIMP_ROTATE_90: + gimp_drawable_transform_rotate_point (orig_x, + orig_y + orig_height, + rotate_type, center_x, center_y, + &new_x, &new_y); + new_width = orig_height; + new_height = orig_width; + break; + + case GIMP_ROTATE_180: + gimp_drawable_transform_rotate_point (orig_x + orig_width, + orig_y + orig_height, + rotate_type, center_x, center_y, + &new_x, &new_y); + new_width = orig_width; + new_height = orig_height; + break; + + case GIMP_ROTATE_270: + gimp_drawable_transform_rotate_point (orig_x + orig_width, + orig_y, + rotate_type, center_x, center_y, + &new_x, &new_y); + new_width = orig_height; + new_height = orig_width; + break; + + default: + g_return_val_if_reached (NULL); + break; + } + + format = gegl_buffer_get_format (orig_buffer); + + if (clip_result && (new_x != orig_x || new_y != orig_y || + new_width != orig_width || new_height != orig_height)) + + { + GimpRGB bg; + GeglColor *color; + gint clip_x, clip_y; + gint clip_width, clip_height; + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + orig_width, orig_height), + format); + + *new_offset_x = orig_x; + *new_offset_y = orig_y; + + /* Use transparency, rather than the bg color, as the "outside" color of + * channels, and drawables with an alpha channel. + */ + if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format)) + { + gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); + } + else + { + gimp_context_get_background (context, &bg); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + &bg, &bg); + } + + color = gimp_gegl_color_new (&bg); + gegl_buffer_set_color (new_buffer, NULL, color); + g_object_unref (color); + + if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, + new_x, new_y, new_width, new_height, + &clip_x, &clip_y, + &clip_width, &clip_height)) + { + gint saved_orig_x = orig_x; + gint saved_orig_y = orig_y; + + new_x = clip_x - orig_x; + new_y = clip_y - orig_y; + + switch (rotate_type) + { + case GIMP_ROTATE_90: + gimp_drawable_transform_rotate_point (clip_x + clip_width, + clip_y, + GIMP_ROTATE_270, + center_x, + center_y, + &orig_x, + &orig_y); + orig_x -= saved_orig_x; + orig_y -= saved_orig_y; + orig_width = clip_height; + orig_height = clip_width; + break; + + case GIMP_ROTATE_180: + orig_x = clip_x - orig_x; + orig_y = clip_y - orig_y; + orig_width = clip_width; + orig_height = clip_height; + break; + + case GIMP_ROTATE_270: + gimp_drawable_transform_rotate_point (clip_x, + clip_y + clip_height, + GIMP_ROTATE_90, + center_x, + center_y, + &orig_x, + &orig_y); + orig_x -= saved_orig_x; + orig_y -= saved_orig_y; + orig_width = clip_height; + orig_height = clip_width; + break; + } + + new_width = clip_width; + new_height = clip_height; + } + else + { + new_width = 0; + new_height = 0; + } + } + else + { + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + new_width, new_height), + format); + + *new_offset_x = new_x; + *new_offset_y = new_y; + + orig_x = 0; + orig_y = 0; + new_x = 0; + new_y = 0; + } + + if (new_width < 1 || new_height < 1) + return new_buffer; + + src_rect.x = orig_x; + src_rect.y = orig_y; + src_rect.width = orig_width; + src_rect.height = orig_height; + + dest_rect.x = new_x; + dest_rect.y = new_y; + dest_rect.width = new_width; + dest_rect.height = new_height; + + switch (rotate_type) + { + case GIMP_ROTATE_90: + { + guchar *buf = g_new (guchar, new_height * orig_bpp); + gint i; + + /* Not cool, we leak memory if we return, but anyway that is + * never supposed to happen. If we see this warning, a bug has + * to be fixed! + */ + g_return_val_if_fail (new_height == orig_width, NULL); + + src_rect.y = orig_y + orig_height - 1; + src_rect.height = 1; + + dest_rect.x = new_x; + dest_rect.width = 1; + + for (i = 0; i < orig_height; i++) + { + src_rect.y = orig_y + orig_height - 1 - i; + dest_rect.x = new_x + i; + + gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, + GEGL_AUTO_ROWSTRIDE); + } + + g_free (buf); + } + break; + + case GIMP_ROTATE_180: + { + guchar *buf = g_new (guchar, new_width * orig_bpp); + gint i, j, k; + + /* Not cool, we leak memory if we return, but anyway that is + * never supposed to happen. If we see this warning, a bug has + * to be fixed! + */ + g_return_val_if_fail (new_width == orig_width, NULL); + + src_rect.y = orig_y + orig_height - 1; + src_rect.height = 1; + + dest_rect.y = new_y; + dest_rect.height = 1; + + for (i = 0; i < orig_height; i++) + { + src_rect.y = orig_y + orig_height - 1 - i; + dest_rect.y = new_y + i; + + gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + for (j = 0; j < orig_width / 2; j++) + { + guchar *left = buf + j * orig_bpp; + guchar *right = buf + (orig_width - 1 - j) * orig_bpp; + + for (k = 0; k < orig_bpp; k++) + { + guchar tmp = left[k]; + left[k] = right[k]; + right[k] = tmp; + } + } + + gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, + GEGL_AUTO_ROWSTRIDE); + } + + g_free (buf); + } + break; + + case GIMP_ROTATE_270: + { + guchar *buf = g_new (guchar, new_width * orig_bpp); + gint i; + + /* Not cool, we leak memory if we return, but anyway that is + * never supposed to happen. If we see this warning, a bug has + * to be fixed! + */ + g_return_val_if_fail (new_width == orig_height, NULL); + + src_rect.x = orig_x + orig_width - 1; + src_rect.width = 1; + + dest_rect.y = new_y; + dest_rect.height = 1; + + for (i = 0; i < orig_width; i++) + { + src_rect.x = orig_x + orig_width - 1 - i; + dest_rect.y = new_y + i; + + gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, + GEGL_AUTO_ROWSTRIDE); + } + + g_free (buf); + } + break; + } + + return new_buffer; +} + +GimpDrawable * +gimp_drawable_transform_affine (GimpDrawable *drawable, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpImage *image; + GeglBuffer *orig_buffer; + gint orig_offset_x; + gint orig_offset_y; + gboolean new_layer; + GimpDrawable *result = NULL; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (matrix != NULL, NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* Start a transform undo group */ + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Transform")); + + /* Cut/Copy from the specified drawable */ + orig_buffer = gimp_drawable_transform_cut (drawable, context, + &orig_offset_x, &orig_offset_y, + &new_layer); + + if (orig_buffer) + { + GeglBuffer *new_buffer; + gint new_offset_x; + gint new_offset_y; + GimpColorProfile *profile; + + /* also transform the mask if we are transforming an entire layer */ + if (GIMP_IS_LAYER (drawable) && + gimp_layer_get_mask (GIMP_LAYER (drawable)) && + gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); + + gimp_item_transform (GIMP_ITEM (mask), context, + matrix, + direction, + interpolation_type, + clip_result, + progress); + } + + /* transform the buffer */ + new_buffer = gimp_drawable_transform_buffer_affine (drawable, context, + orig_buffer, + orig_offset_x, + orig_offset_y, + matrix, + direction, + interpolation_type, + clip_result, + &profile, + &new_offset_x, + &new_offset_y, + progress); + + /* Free the cut/copied buffer */ + g_object_unref (orig_buffer); + + if (new_buffer) + { + result = gimp_drawable_transform_paste (drawable, new_buffer, profile, + new_offset_x, new_offset_y, + new_layer); + g_object_unref (new_buffer); + } + } + + /* push the undo group end */ + gimp_image_undo_group_end (image); + + return result; +} + +GimpDrawable * +gimp_drawable_transform_flip (GimpDrawable *drawable, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpImage *image; + GeglBuffer *orig_buffer; + gint orig_offset_x; + gint orig_offset_y; + gboolean new_layer; + GimpDrawable *result = NULL; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* Start a transform undo group */ + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Flip")); + + /* Cut/Copy from the specified drawable */ + orig_buffer = gimp_drawable_transform_cut (drawable, context, + &orig_offset_x, &orig_offset_y, + &new_layer); + + if (orig_buffer) + { + GeglBuffer *new_buffer; + gint new_offset_x; + gint new_offset_y; + GimpColorProfile *profile; + + /* also transform the mask if we are transforming an entire layer */ + if (GIMP_IS_LAYER (drawable) && + gimp_layer_get_mask (GIMP_LAYER (drawable)) && + gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); + + gimp_item_flip (GIMP_ITEM (mask), context, + flip_type, + axis, + clip_result); + } + + /* transform the buffer */ + new_buffer = gimp_drawable_transform_buffer_flip (drawable, context, + orig_buffer, + orig_offset_x, + orig_offset_y, + flip_type, axis, + clip_result, + &profile, + &new_offset_x, + &new_offset_y); + + /* Free the cut/copied buffer */ + g_object_unref (orig_buffer); + + if (new_buffer) + { + result = gimp_drawable_transform_paste (drawable, new_buffer, profile, + new_offset_x, new_offset_y, + new_layer); + g_object_unref (new_buffer); + } + } + + /* push the undo group end */ + gimp_image_undo_group_end (image); + + return result; +} + +GimpDrawable * +gimp_drawable_transform_rotate (GimpDrawable *drawable, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpImage *image; + GeglBuffer *orig_buffer; + gint orig_offset_x; + gint orig_offset_y; + gboolean new_layer; + GimpDrawable *result = NULL; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* Start a transform undo group */ + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Rotate")); + + /* Cut/Copy from the specified drawable */ + orig_buffer = gimp_drawable_transform_cut (drawable, context, + &orig_offset_x, &orig_offset_y, + &new_layer); + + if (orig_buffer) + { + GeglBuffer *new_buffer; + gint new_offset_x; + gint new_offset_y; + GimpColorProfile *profile; + + /* also transform the mask if we are transforming an entire layer */ + if (GIMP_IS_LAYER (drawable) && + gimp_layer_get_mask (GIMP_LAYER (drawable)) && + gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); + + gimp_item_rotate (GIMP_ITEM (mask), context, + rotate_type, + center_x, + center_y, + clip_result); + } + + /* transform the buffer */ + new_buffer = gimp_drawable_transform_buffer_rotate (drawable, context, + orig_buffer, + orig_offset_x, + orig_offset_y, + rotate_type, + center_x, center_y, + clip_result, + &profile, + &new_offset_x, + &new_offset_y); + + /* Free the cut/copied buffer */ + g_object_unref (orig_buffer); + + if (new_buffer) + { + result = gimp_drawable_transform_paste (drawable, new_buffer, profile, + new_offset_x, new_offset_y, + new_layer); + g_object_unref (new_buffer); + } + } + + /* push the undo group end */ + gimp_image_undo_group_end (image); + + return result; +} + +GeglBuffer * +gimp_drawable_transform_cut (GimpDrawable *drawable, + GimpContext *context, + gint *offset_x, + gint *offset_y, + gboolean *new_layer) +{ + GimpImage *image; + GeglBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (offset_x != NULL, NULL); + g_return_val_if_fail (offset_y != NULL, NULL); + g_return_val_if_fail (new_layer != NULL, NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* extract the selected mask if there is a selection */ + if (! gimp_channel_is_empty (gimp_image_get_mask (image))) + { + gint x, y, w, h; + + /* set the keep_indexed flag to FALSE here, since we use + * gimp_layer_new_from_gegl_buffer() later which assumes that + * the buffer are either RGB or GRAY. Eeek!!! (Sven) + */ + if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h)) + { + buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)), + GIMP_PICKABLE (drawable), + context, + TRUE, FALSE, TRUE, + offset_x, offset_y, + NULL); + /* clear the selection */ + gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); + + *new_layer = TRUE; + } + else + { + buffer = NULL; + *new_layer = FALSE; + } + } + else /* otherwise, just copy the layer */ + { + buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)), + GIMP_PICKABLE (drawable), + context, + FALSE, TRUE, GIMP_IS_LAYER (drawable), + offset_x, offset_y, + NULL); + + *new_layer = FALSE; + } + + return buffer; +} + +GimpDrawable * +gimp_drawable_transform_paste (GimpDrawable *drawable, + GeglBuffer *buffer, + GimpColorProfile *buffer_profile, + gint offset_x, + gint offset_y, + gboolean new_layer) +{ + GimpImage *image; + GimpLayer *layer = NULL; + const gchar *undo_desc = NULL; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (buffer_profile), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + if (GIMP_IS_LAYER (drawable)) + undo_desc = C_("undo-type", "Transform Layer"); + else if (GIMP_IS_CHANNEL (drawable)) + undo_desc = C_("undo-type", "Transform Channel"); + else + return NULL; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, undo_desc); + + if (new_layer) + { + layer = + gimp_layer_new_from_gegl_buffer (buffer, image, + gimp_drawable_get_format_with_alpha (drawable), + _("Transformation"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image), + buffer_profile); + + gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y); + + floating_sel_attach (layer, drawable); + + drawable = GIMP_DRAWABLE (layer); + } + else + { + gimp_drawable_set_buffer_full (drawable, TRUE, NULL, + buffer, + GEGL_RECTANGLE (offset_x, offset_y, 0, 0), + TRUE); + } + + gimp_image_undo_group_end (image); + + return drawable; +} diff --git a/app/core/gimpdrawable-transform.h b/app/core/gimpdrawable-transform.h new file mode 100644 index 0000000..8cdd53f --- /dev/null +++ b/app/core/gimpdrawable-transform.h @@ -0,0 +1,94 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DRAWABLE_TRANSFORM_H__ +#define __GIMP_DRAWABLE_TRANSFORM_H__ + + +GeglBuffer * gimp_drawable_transform_buffer_affine (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *orig_buffer, + gint orig_offset_x, + gint orig_offset_y, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y, + GimpProgress *progress); +GeglBuffer * gimp_drawable_transform_buffer_flip (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *orig_buffer, + gint orig_offset_x, + gint orig_offset_y, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y); + +GeglBuffer * gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable, + GimpContext *context, + GeglBuffer *buffer, + gint orig_offset_x, + gint orig_offset_y, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result, + GimpColorProfile **buffer_profile, + gint *new_offset_x, + gint *new_offset_y); + +GimpDrawable * gimp_drawable_transform_affine (GimpDrawable *drawable, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + +GimpDrawable * gimp_drawable_transform_flip (GimpDrawable *drawable, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); + +GimpDrawable * gimp_drawable_transform_rotate (GimpDrawable *drawable, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); + +GeglBuffer * gimp_drawable_transform_cut (GimpDrawable *drawable, + GimpContext *context, + gint *offset_x, + gint *offset_y, + gboolean *new_layer); +GimpDrawable * gimp_drawable_transform_paste (GimpDrawable *drawable, + GeglBuffer *buffer, + GimpColorProfile *buffer_profile, + gint offset_x, + gint offset_y, + gboolean new_layer); + + +#endif /* __GIMP_DRAWABLE_TRANSFORM_H__ */ diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c new file mode 100644 index 0000000..9a85eee --- /dev/null +++ b/app/core/gimpdrawable.c @@ -0,0 +1,1916 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp-memsize.h" +#include "gimp-utils.h" +#include "gimpchannel.h" +#include "gimpcontext.h" +#include "gimpdrawable-combine.h" +#include "gimpdrawable-fill.h" +#include "gimpdrawable-floating-selection.h" +#include "gimpdrawable-preview.h" +#include "gimpdrawable-private.h" +#include "gimpdrawable-shadow.h" +#include "gimpdrawable-transform.h" +#include "gimpfilterstack.h" +#include "gimpimage.h" +#include "gimpimage-colormap.h" +#include "gimpimage-undo-push.h" +#include "gimpmarshal.h" +#include "gimppickable.h" +#include "gimpprogress.h" + +#include "gimp-log.h" + +#include "gimp-intl.h" + + +#define PAINT_UPDATE_CHUNK_WIDTH 32 +#define PAINT_UPDATE_CHUNK_HEIGHT 32 + + +enum +{ + UPDATE, + FORMAT_CHANGED, + ALPHA_CHANGED, + BOUNDING_BOX_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_BUFFER +}; + + +/* local function prototypes */ + +static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface); +static void gimp_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_drawable_dispose (GObject *object); +static void gimp_drawable_finalize (GObject *object); +static void gimp_drawable_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_drawable_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_drawable_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_drawable_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static void gimp_drawable_preview_freeze (GimpViewable *viewable); +static void gimp_drawable_preview_thaw (GimpViewable *viewable); + +static GeglNode * gimp_drawable_get_node (GimpFilter *filter); + +static void gimp_drawable_removed (GimpItem *item); +static GimpItem * gimp_drawable_duplicate (GimpItem *item, + GType new_type); +static void gimp_drawable_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_drawable_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static void gimp_drawable_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +static void gimp_drawable_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +static void gimp_drawable_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + +static const guint8 * + gimp_drawable_get_icc_profile (GimpColorManaged *managed, + gsize *len); +static GimpColorProfile * + gimp_drawable_get_color_profile (GimpColorManaged *managed); +static void gimp_drawable_profile_changed (GimpColorManaged *managed); + +static gboolean gimp_drawable_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +static void gimp_drawable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); + +static void gimp_drawable_real_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height); + +static gint64 gimp_drawable_real_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height); + +static void gimp_drawable_real_update_all (GimpDrawable *drawable); + +static GimpComponentMask + gimp_drawable_real_get_active_mask (GimpDrawable *drawable); + +static gboolean gimp_drawable_real_supports_alpha + (GimpDrawable *drawable); + +static void gimp_drawable_real_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); + +static GeglBuffer * gimp_drawable_real_get_buffer (GimpDrawable *drawable); +static void gimp_drawable_real_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds); + +static GeglRectangle gimp_drawable_real_get_bounding_box + (GimpDrawable *drawable); + +static void gimp_drawable_real_push_undo (GimpDrawable *drawable, + const gchar *undo_desc, + GeglBuffer *buffer, + gint x, + gint y, + gint width, + gint height); +static void gimp_drawable_real_swap_pixels (GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y); +static GeglNode * gimp_drawable_real_get_source_node (GimpDrawable *drawable); + +static void gimp_drawable_format_changed (GimpDrawable *drawable); +static void gimp_drawable_alpha_changed (GimpDrawable *drawable); + + +G_DEFINE_TYPE_WITH_CODE (GimpDrawable, gimp_drawable, GIMP_TYPE_ITEM, + G_ADD_PRIVATE (GimpDrawable) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_color_managed_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_pickable_iface_init)) + +#define parent_class gimp_drawable_parent_class + +static guint gimp_drawable_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_drawable_class_init (GimpDrawableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpFilterClass *filter_class = GIMP_FILTER_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + + gimp_drawable_signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableClass, update), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + gimp_drawable_signals[FORMAT_CHANGED] = + g_signal_new ("format-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableClass, format_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_drawable_signals[ALPHA_CHANGED] = + g_signal_new ("alpha-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableClass, alpha_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_drawable_signals[BOUNDING_BOX_CHANGED] = + g_signal_new ("bounding-box-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableClass, bounding_box_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = gimp_drawable_dispose; + object_class->finalize = gimp_drawable_finalize; + object_class->set_property = gimp_drawable_set_property; + object_class->get_property = gimp_drawable_get_property; + + gimp_object_class->get_memsize = gimp_drawable_get_memsize; + + viewable_class->get_size = gimp_drawable_get_size; + viewable_class->get_new_preview = gimp_drawable_get_new_preview; + viewable_class->get_new_pixbuf = gimp_drawable_get_new_pixbuf; + viewable_class->preview_freeze = gimp_drawable_preview_freeze; + viewable_class->preview_thaw = gimp_drawable_preview_thaw; + + filter_class->get_node = gimp_drawable_get_node; + + item_class->removed = gimp_drawable_removed; + item_class->duplicate = gimp_drawable_duplicate; + item_class->scale = gimp_drawable_scale; + item_class->resize = gimp_drawable_resize; + item_class->flip = gimp_drawable_flip; + item_class->rotate = gimp_drawable_rotate; + item_class->transform = gimp_drawable_transform; + + klass->update = gimp_drawable_real_update; + klass->format_changed = NULL; + klass->alpha_changed = NULL; + klass->bounding_box_changed = NULL; + klass->estimate_memsize = gimp_drawable_real_estimate_memsize; + klass->update_all = gimp_drawable_real_update_all; + klass->invalidate_boundary = NULL; + klass->get_active_components = NULL; + klass->get_active_mask = gimp_drawable_real_get_active_mask; + klass->supports_alpha = gimp_drawable_real_supports_alpha; + klass->convert_type = gimp_drawable_real_convert_type; + klass->apply_buffer = gimp_drawable_real_apply_buffer; + klass->get_buffer = gimp_drawable_real_get_buffer; + klass->set_buffer = gimp_drawable_real_set_buffer; + klass->get_bounding_box = gimp_drawable_real_get_bounding_box; + klass->push_undo = gimp_drawable_real_push_undo; + klass->swap_pixels = gimp_drawable_real_swap_pixels; + klass->get_source_node = gimp_drawable_real_get_source_node; + + g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); +} + +static void +gimp_drawable_init (GimpDrawable *drawable) +{ + drawable->private = gimp_drawable_get_instance_private (drawable); + + drawable->private->filter_stack = gimp_filter_stack_new (GIMP_TYPE_FILTER); +} + +/* sorry for the evil casts */ + +static void +gimp_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_icc_profile = gimp_drawable_get_icc_profile; + iface->get_color_profile = gimp_drawable_get_color_profile; + iface->profile_changed = gimp_drawable_profile_changed; +} + +static void +gimp_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->get_image = (GimpImage * (*) (GimpPickable *pickable)) gimp_item_get_image; + iface->get_format = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format; + iface->get_format_with_alpha = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format_with_alpha; + iface->get_buffer = (GeglBuffer * (*) (GimpPickable *pickable)) gimp_drawable_get_buffer; + iface->get_pixel_at = gimp_drawable_get_pixel_at; + iface->get_pixel_average = gimp_drawable_get_pixel_average; +} + +static void +gimp_drawable_dispose (GObject *object) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (object); + + if (gimp_drawable_get_floating_sel (drawable)) + gimp_drawable_detach_floating_sel (drawable); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_drawable_finalize (GObject *object) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (object); + + while (drawable->private->paint_count) + gimp_drawable_end_paint (drawable); + + g_clear_object (&drawable->private->buffer); + + gimp_drawable_free_shadow_buffer (drawable); + + g_clear_object (&drawable->private->source_node); + g_clear_object (&drawable->private->buffer_source_node); + g_clear_object (&drawable->private->filter_stack); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_drawable_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_BUFFER: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_drawable_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (object); + + switch (property_id) + { + case PROP_BUFFER: + g_value_set_object (value, drawable->private->buffer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_drawable_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (gimp_drawable_get_buffer (drawable)); + memsize += gimp_gegl_buffer_get_memsize (drawable->private->shadow); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_drawable_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpItem *item = GIMP_ITEM (viewable); + + *width = gimp_item_get_width (item); + *height = gimp_item_get_height (item); + + return TRUE; +} + +static void +gimp_drawable_preview_freeze (GimpViewable *viewable) +{ + GimpViewable *parent = gimp_viewable_get_parent (viewable); + + if (! parent && gimp_item_is_attached (GIMP_ITEM (viewable))) + parent = GIMP_VIEWABLE (gimp_item_get_image (GIMP_ITEM (viewable))); + + if (parent) + gimp_viewable_preview_freeze (parent); +} + +static void +gimp_drawable_preview_thaw (GimpViewable *viewable) +{ + GimpViewable *parent = gimp_viewable_get_parent (viewable); + + if (! parent && gimp_item_is_attached (GIMP_ITEM (viewable))) + parent = GIMP_VIEWABLE (gimp_item_get_image (GIMP_ITEM (viewable))); + + if (parent) + gimp_viewable_preview_thaw (parent); +} + +static GeglNode * +gimp_drawable_get_node (GimpFilter *filter) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (filter); + GeglNode *node; + GeglNode *input; + GeglNode *output; + + node = GIMP_FILTER_CLASS (parent_class)->get_node (filter); + + g_warn_if_fail (drawable->private->mode_node == NULL); + + drawable->private->mode_node = + gegl_node_new_child (node, + "operation", "gimp:normal", + NULL); + + input = gegl_node_get_input_proxy (node, "input"); + output = gegl_node_get_output_proxy (node, "output"); + + gegl_node_connect_to (input, "output", + drawable->private->mode_node, "input"); + gegl_node_connect_to (drawable->private->mode_node, "output", + output, "input"); + + return node; +} + +static void +gimp_drawable_removed (GimpItem *item) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + + gimp_drawable_free_shadow_buffer (drawable); + + if (GIMP_ITEM_CLASS (parent_class)->removed) + GIMP_ITEM_CLASS (parent_class)->removed (item); +} + +static GimpItem * +gimp_drawable_duplicate (GimpItem *item, + GType new_type) +{ + GimpItem *new_item; + + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL); + + new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type); + + if (GIMP_IS_DRAWABLE (new_item)) + { + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GimpDrawable *new_drawable = GIMP_DRAWABLE (new_item); + GeglBuffer *new_buffer; + + new_buffer = gimp_gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); + + gimp_drawable_set_buffer (new_drawable, FALSE, NULL, new_buffer); + g_object_unref (new_buffer); + } + + return new_item; +} + +static void +gimp_drawable_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *new_buffer; + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + new_width, new_height), + gimp_drawable_get_format (drawable)); + + gimp_gegl_apply_scale (gimp_drawable_get_buffer (drawable), + progress, C_("undo-type", "Scale"), + new_buffer, + interpolation_type, + ((gdouble) new_width / + gimp_item_get_width (item)), + ((gdouble) new_height / + gimp_item_get_height (item))); + + gimp_drawable_set_buffer_full (drawable, gimp_item_is_attached (item), NULL, + new_buffer, + GEGL_RECTANGLE (new_offset_x, new_offset_y, + 0, 0), + TRUE); + g_object_unref (new_buffer); +} + +static void +gimp_drawable_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *new_buffer; + gint new_offset_x; + gint new_offset_y; + gint copy_x, copy_y; + gint copy_width, copy_height; + gboolean intersect; + + /* if the size doesn't change, this is a nop */ + if (new_width == gimp_item_get_width (item) && + new_height == gimp_item_get_height (item) && + offset_x == 0 && + offset_y == 0) + return; + + new_offset_x = gimp_item_get_offset_x (item) - offset_x; + new_offset_y = gimp_item_get_offset_y (item) - offset_y; + + intersect = gimp_rectangle_intersect (gimp_item_get_offset_x (item), + gimp_item_get_offset_y (item), + gimp_item_get_width (item), + gimp_item_get_height (item), + new_offset_x, + new_offset_y, + new_width, + new_height, + ©_x, + ©_y, + ©_width, + ©_height); + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + new_width, new_height), + gimp_drawable_get_format (drawable)); + + if (! intersect || + copy_width != new_width || + copy_height != new_height) + { + /* Clear the new buffer if needed */ + + GimpRGB color; + GimpPattern *pattern; + + gimp_get_fill_params (context, fill_type, &color, &pattern, NULL); + gimp_drawable_fill_buffer (drawable, new_buffer, + &color, pattern, 0, 0); + } + + if (intersect && copy_width && copy_height) + { + /* Copy the pixels in the intersection */ + gimp_gegl_buffer_copy ( + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (copy_x - gimp_item_get_offset_x (item), + copy_y - gimp_item_get_offset_y (item), + copy_width, + copy_height), GEGL_ABYSS_NONE, + new_buffer, + GEGL_RECTANGLE (copy_x - new_offset_x, + copy_y - new_offset_y, 0, 0)); + } + + gimp_drawable_set_buffer_full (drawable, gimp_item_is_attached (item), NULL, + new_buffer, + GEGL_RECTANGLE (new_offset_x, new_offset_y, + 0, 0), + TRUE); + g_object_unref (new_buffer); +} + +static void +gimp_drawable_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *buffer; + GimpColorProfile *buffer_profile; + gint off_x, off_y; + gint new_off_x, new_off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + buffer = gimp_drawable_transform_buffer_flip (drawable, context, + gimp_drawable_get_buffer (drawable), + off_x, off_y, + flip_type, axis, + clip_result, + &buffer_profile, + &new_off_x, &new_off_y); + + if (buffer) + { + gimp_drawable_transform_paste (drawable, buffer, buffer_profile, + new_off_x, new_off_y, FALSE); + g_object_unref (buffer); + } +} + +static void +gimp_drawable_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *buffer; + GimpColorProfile *buffer_profile; + gint off_x, off_y; + gint new_off_x, new_off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + buffer = gimp_drawable_transform_buffer_rotate (drawable, context, + gimp_drawable_get_buffer (drawable), + off_x, off_y, + rotate_type, center_x, center_y, + clip_result, + &buffer_profile, + &new_off_x, &new_off_y); + + if (buffer) + { + gimp_drawable_transform_paste (drawable, buffer, buffer_profile, + new_off_x, new_off_y, FALSE); + g_object_unref (buffer); + } +} + +static void +gimp_drawable_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GeglBuffer *buffer; + GimpColorProfile *buffer_profile; + gint off_x, off_y; + gint new_off_x, new_off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + buffer = gimp_drawable_transform_buffer_affine (drawable, context, + gimp_drawable_get_buffer (drawable), + off_x, off_y, + matrix, direction, + interpolation_type, + clip_result, + &buffer_profile, + &new_off_x, &new_off_y, + progress); + + if (buffer) + { + gimp_drawable_transform_paste (drawable, buffer, buffer_profile, + new_off_x, new_off_y, FALSE); + g_object_unref (buffer); + } +} + +static const guint8 * +gimp_drawable_get_icc_profile (GimpColorManaged *managed, + gsize *len) +{ + GimpColorProfile *profile = gimp_color_managed_get_color_profile (managed); + + return gimp_color_profile_get_icc_profile (profile, len); +} + +static GimpColorProfile * +gimp_drawable_get_color_profile (GimpColorManaged *managed) +{ + const Babl *format = gimp_drawable_get_format (GIMP_DRAWABLE (managed)); + + return gimp_babl_format_get_color_profile (format); +} + +static void +gimp_drawable_profile_changed (GimpColorManaged *managed) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (managed)); +} + +static gboolean +gimp_drawable_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (pickable); + + /* do not make this a g_return_if_fail() */ + if (x < 0 || x >= gimp_item_get_width (GIMP_ITEM (drawable)) || + y < 0 || y >= gimp_item_get_height (GIMP_ITEM (drawable))) + return FALSE; + + gegl_buffer_sample (gimp_drawable_get_buffer (drawable), + x, y, NULL, pixel, format, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + return TRUE; +} + +static void +gimp_drawable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (pickable); + + return gimp_gegl_average_color (gimp_drawable_get_buffer (drawable), + rect, TRUE, GEGL_ABYSS_NONE, format, pixel); +} + +static void +gimp_drawable_real_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); +} + +static gint64 +gimp_drawable_real_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gboolean linear = gimp_drawable_get_linear (drawable); + const Babl *format; + + format = gimp_image_get_format (image, + gimp_drawable_get_base_type (drawable), + gimp_babl_precision (component_type, linear), + gimp_drawable_has_alpha (drawable)); + + return (gint64) babl_format_get_bytes_per_pixel (format) * width * height; +} + +static void +gimp_drawable_real_update_all (GimpDrawable *drawable) +{ + gimp_drawable_update (drawable, 0, 0, -1, -1); +} + +static GimpComponentMask +gimp_drawable_real_get_active_mask (GimpDrawable *drawable) +{ + /* Return all, because that skips the component mask op when painting */ + return GIMP_COMPONENT_MASK_ALL; +} + +static gboolean +gimp_drawable_real_supports_alpha (GimpDrawable *drawable) +{ + return FALSE; +} + +/* FIXME: this default impl is currently unused because no subclass + * chains up. the goal is to handle the almost identical subclass code + * here again. + */ +static void +gimp_drawable_real_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + GeglBuffer *dest_buffer; + + dest_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))), + new_format); + + gimp_gegl_buffer_copy ( + gimp_drawable_get_buffer (drawable), NULL, GEGL_ABYSS_NONE, + dest_buffer, NULL); + + gimp_drawable_set_buffer (drawable, push_undo, NULL, dest_buffer); + g_object_unref (dest_buffer); +} + +static GeglBuffer * +gimp_drawable_real_get_buffer (GimpDrawable *drawable) +{ + return drawable->private->buffer; +} + +static void +gimp_drawable_real_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds) +{ + GimpItem *item = GIMP_ITEM (drawable); + const Babl *old_format = NULL; + gint old_has_alpha = -1; + + g_object_freeze_notify (G_OBJECT (drawable)); + + gimp_drawable_invalidate_boundary (drawable); + + if (push_undo) + gimp_image_undo_push_drawable_mod (gimp_item_get_image (item), undo_desc, + drawable, FALSE); + + if (drawable->private->buffer) + { + old_format = gimp_drawable_get_format (drawable); + old_has_alpha = gimp_drawable_has_alpha (drawable); + } + + g_set_object (&drawable->private->buffer, buffer); + + if (drawable->private->buffer_source_node) + gegl_node_set (drawable->private->buffer_source_node, + "buffer", gimp_drawable_get_buffer (drawable), + NULL); + + gimp_item_set_offset (item, bounds->x, bounds->y); + gimp_item_set_size (item, + bounds->width ? bounds->width : + gegl_buffer_get_width (buffer), + bounds->height ? bounds->height : + gegl_buffer_get_height (buffer)); + + gimp_drawable_update_bounding_box (drawable); + + if (gimp_drawable_get_format (drawable) != old_format) + gimp_drawable_format_changed (drawable); + + if (gimp_drawable_has_alpha (drawable) != old_has_alpha) + gimp_drawable_alpha_changed (drawable); + + g_object_notify (G_OBJECT (drawable), "buffer"); + + g_object_thaw_notify (G_OBJECT (drawable)); +} + +static GeglRectangle +gimp_drawable_real_get_bounding_box (GimpDrawable *drawable) +{ + return gegl_node_get_bounding_box (gimp_drawable_get_source_node (drawable)); +} + +static void +gimp_drawable_real_push_undo (GimpDrawable *drawable, + const gchar *undo_desc, + GeglBuffer *buffer, + gint x, + gint y, + gint width, + gint height) +{ + GimpImage *image; + + if (! buffer) + { + GeglBuffer *drawable_buffer = gimp_drawable_get_buffer (drawable); + GeglRectangle drawable_rect; + + gegl_rectangle_align_to_buffer ( + &drawable_rect, + GEGL_RECTANGLE (x, y, width, height), + drawable_buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + x = drawable_rect.x; + y = drawable_rect.y; + width = drawable_rect.width; + height = drawable_rect.height; + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), + gimp_drawable_get_format (drawable)); + + gimp_gegl_buffer_copy ( + drawable_buffer, + &drawable_rect, GEGL_ABYSS_NONE, + buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + } + else + { + g_object_ref (buffer); + } + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + gimp_image_undo_push_drawable (image, + undo_desc, drawable, + buffer, x, y); + + g_object_unref (buffer); +} + +static void +gimp_drawable_real_swap_pixels (GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y) +{ + GeglBuffer *tmp; + gint width = gegl_buffer_get_width (buffer); + gint height = gegl_buffer_get_height (buffer); + + tmp = gimp_gegl_buffer_dup (buffer); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (x, y, width, height), GEGL_ABYSS_NONE, + buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + gimp_gegl_buffer_copy (tmp, + GEGL_RECTANGLE (0, 0, width, height), GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (x, y, 0, 0)); + + g_object_unref (tmp); + + gimp_drawable_update (drawable, x, y, width, height); +} + +static GeglNode * +gimp_drawable_real_get_source_node (GimpDrawable *drawable) +{ + g_warn_if_fail (drawable->private->buffer_source_node == NULL); + + drawable->private->buffer_source_node = + gegl_node_new_child (NULL, + "operation", "gimp:buffer-source-validate", + "buffer", gimp_drawable_get_buffer (drawable), + NULL); + + return g_object_ref (drawable->private->buffer_source_node); +} + +static void +gimp_drawable_format_changed (GimpDrawable *drawable) +{ + g_signal_emit (drawable, gimp_drawable_signals[FORMAT_CHANGED], 0); +} + +static void +gimp_drawable_alpha_changed (GimpDrawable *drawable) +{ + g_signal_emit (drawable, gimp_drawable_signals[ALPHA_CHANGED], 0); +} + + +/* public functions */ + +GimpDrawable * +gimp_drawable_new (GType type, + GimpImage *image, + const gchar *name, + gint offset_x, + gint offset_y, + gint width, + gint height, + const Babl *format) +{ + GimpDrawable *drawable; + GeglBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (g_type_is_a (type, GIMP_TYPE_DRAWABLE), NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + g_return_val_if_fail (format != NULL, NULL); + + drawable = GIMP_DRAWABLE (gimp_item_new (type, + image, name, + offset_x, offset_y, + width, height)); + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), format); + + gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); + g_object_unref (buffer); + + return drawable; +} + +gint64 +gimp_drawable_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), 0); + + return GIMP_DRAWABLE_GET_CLASS (drawable)->estimate_memsize (drawable, + component_type, + width, height); +} + +void +gimp_drawable_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + if (width < 0) + { + GeglRectangle bounding_box; + + bounding_box = gimp_drawable_get_bounding_box (drawable); + + x = bounding_box.x; + width = bounding_box.width; + } + + if (height < 0) + { + GeglRectangle bounding_box; + + bounding_box = gimp_drawable_get_bounding_box (drawable); + + y = bounding_box.y; + height = bounding_box.height; + } + + if (drawable->private->paint_count == 0) + { + g_signal_emit (drawable, gimp_drawable_signals[UPDATE], 0, + x, y, width, height); + } + else + { + GeglRectangle rect; + + if (gegl_rectangle_intersect ( + &rect, + GEGL_RECTANGLE (x, y, width, height), + GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))))) + { + GeglRectangle aligned_rect; + + gegl_rectangle_align_to_buffer (&aligned_rect, &rect, + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + if (drawable->private->paint_copy_region) + { + cairo_region_union_rectangle ( + drawable->private->paint_copy_region, + (const cairo_rectangle_int_t *) &aligned_rect); + } + else + { + drawable->private->paint_copy_region = + cairo_region_create_rectangle ( + (const cairo_rectangle_int_t *) &aligned_rect); + } + + gegl_rectangle_align (&aligned_rect, &rect, + GEGL_RECTANGLE (0, 0, + PAINT_UPDATE_CHUNK_WIDTH, + PAINT_UPDATE_CHUNK_HEIGHT), + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + if (drawable->private->paint_update_region) + { + cairo_region_union_rectangle ( + drawable->private->paint_update_region, + (const cairo_rectangle_int_t *) &aligned_rect); + } + else + { + drawable->private->paint_update_region = + cairo_region_create_rectangle ( + (const cairo_rectangle_int_t *) &aligned_rect); + } + } + } +} + +void +gimp_drawable_update_all (GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + GIMP_DRAWABLE_GET_CLASS (drawable)->update_all (drawable); +} + +void +gimp_drawable_invalidate_boundary (GimpDrawable *drawable) +{ + GimpDrawableClass *drawable_class; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + drawable_class = GIMP_DRAWABLE_GET_CLASS (drawable); + + if (drawable_class->invalidate_boundary) + drawable_class->invalidate_boundary (drawable); +} + +void +gimp_drawable_get_active_components (GimpDrawable *drawable, + gboolean *active) +{ + GimpDrawableClass *drawable_class; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (active != NULL); + + drawable_class = GIMP_DRAWABLE_GET_CLASS (drawable); + + if (drawable_class->get_active_components) + drawable_class->get_active_components (drawable, active); +} + +GimpComponentMask +gimp_drawable_get_active_mask (GimpDrawable *drawable) +{ + GimpComponentMask mask; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), 0); + + mask = GIMP_DRAWABLE_GET_CLASS (drawable)->get_active_mask (drawable); + + /* if the drawable doesn't have an alpha channel, the value of the mask's + * alpha-bit doesn't matter, however, we'd like to have a fully-clear or + * fully-set mask whenever possible, since it allows us to skip component + * masking altogether. we therefore set or clear the alpha bit, depending on + * the state of the other bits, so that it never gets in the way of a uniform + * mask. + */ + if (! gimp_drawable_has_alpha (drawable)) + { + if (mask & ~GIMP_COMPONENT_MASK_ALPHA) + mask |= GIMP_COMPONENT_MASK_ALPHA; + else + mask &= ~GIMP_COMPONENT_MASK_ALPHA; + } + + return mask; +} + +gboolean +gimp_drawable_supports_alpha (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + return GIMP_DRAWABLE_GET_CLASS (drawable)->supports_alpha (drawable); +} + +void +gimp_drawable_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + GimpImageBaseType new_base_type, + GimpPrecision new_precision, + gboolean new_has_alpha, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + const Babl *old_format; + const Babl *new_format; + gint old_bits; + gint new_bits; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_IMAGE (dest_image)); + g_return_if_fail (new_base_type != gimp_drawable_get_base_type (drawable) || + new_precision != gimp_drawable_get_precision (drawable) || + new_has_alpha != gimp_drawable_has_alpha (drawable) || + dest_profile); + g_return_if_fail (dest_profile == NULL || GIMP_IS_COLOR_PROFILE (dest_profile)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (! gimp_item_is_attached (GIMP_ITEM (drawable))) + push_undo = FALSE; + + old_format = gimp_drawable_get_format (drawable); + new_format = gimp_image_get_format (dest_image, + new_base_type, + new_precision, + new_has_alpha); + + old_bits = (babl_format_get_bytes_per_pixel (old_format) * 8 / + babl_format_get_n_components (old_format)); + new_bits = (babl_format_get_bytes_per_pixel (new_format) * 8 / + babl_format_get_n_components (new_format)); + + if (old_bits <= new_bits || new_bits > 16) + { + /* don't dither if we are converting to a higher bit depth, + * or to more than 16 bits (gegl:dither only does + * 16 bits). + */ + layer_dither_type = GEGL_DITHER_NONE; + mask_dither_type = GEGL_DITHER_NONE; + } + + GIMP_DRAWABLE_GET_CLASS (drawable)->convert_type (drawable, dest_image, + new_format, + dest_profile, + layer_dither_type, + mask_dither_type, + push_undo, + progress); + + if (progress) + gimp_progress_set_value (progress, 1.0); +} + +void +gimp_drawable_apply_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GeglRectangle *buffer_region, + gboolean push_undo, + const gchar *undo_desc, + gdouble opacity, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + GeglBuffer *base_buffer, + gint base_x, + gint base_y) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (buffer_region != NULL); + g_return_if_fail (base_buffer == NULL || GEGL_IS_BUFFER (base_buffer)); + + GIMP_DRAWABLE_GET_CLASS (drawable)->apply_buffer (drawable, buffer, + buffer_region, + push_undo, undo_desc, + opacity, mode, + blend_space, + composite_space, + composite_mode, + base_buffer, + base_x, base_y); +} + +GeglBuffer * +gimp_drawable_get_buffer (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + if (drawable->private->paint_count == 0) + return GIMP_DRAWABLE_GET_CLASS (drawable)->get_buffer (drawable); + else + return drawable->private->paint_buffer; +} + +void +gimp_drawable_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + if (! gimp_item_is_attached (GIMP_ITEM (drawable))) + push_undo = FALSE; + + gimp_drawable_set_buffer_full (drawable, push_undo, undo_desc, buffer, NULL, + TRUE); +} + +void +gimp_drawable_set_buffer_full (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds, + gboolean update) +{ + GimpItem *item; + GeglRectangle curr_bounds; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + item = GIMP_ITEM (drawable); + + if (! gimp_item_is_attached (GIMP_ITEM (drawable))) + push_undo = FALSE; + + if (! bounds) + { + gimp_item_get_offset (GIMP_ITEM (drawable), + &curr_bounds.x, &curr_bounds.y); + + curr_bounds.width = 0; + curr_bounds.height = 0; + + bounds = &curr_bounds; + } + + if (update && gimp_drawable_get_buffer (drawable)) + { + GeglBuffer *old_buffer = gimp_drawable_get_buffer (drawable); + GeglRectangle old_extent; + GeglRectangle new_extent; + + old_extent = *gegl_buffer_get_extent (old_buffer); + old_extent.x += gimp_item_get_offset_x (item); + old_extent.y += gimp_item_get_offset_x (item); + + new_extent = *gegl_buffer_get_extent (buffer); + new_extent.x += bounds->x; + new_extent.y += bounds->y; + + if (! gegl_rectangle_equal (&old_extent, &new_extent)) + gimp_drawable_update (drawable, 0, 0, -1, -1); + } + + g_object_freeze_notify (G_OBJECT (drawable)); + + GIMP_DRAWABLE_GET_CLASS (drawable)->set_buffer (drawable, + push_undo, undo_desc, + buffer, bounds); + + g_object_thaw_notify (G_OBJECT (drawable)); + + if (update) + gimp_drawable_update (drawable, 0, 0, -1, -1); +} + +void +gimp_drawable_steal_buffer (GimpDrawable *drawable, + GimpDrawable *src_drawable) +{ + GeglBuffer *buffer; + GeglBuffer *replacement_buffer; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_DRAWABLE (src_drawable)); + + buffer = gimp_drawable_get_buffer (src_drawable); + + g_return_if_fail (buffer != NULL); + + g_object_ref (buffer); + + replacement_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 1, 1), + gegl_buffer_get_format (buffer)); + + gimp_drawable_set_buffer (src_drawable, FALSE, NULL, replacement_buffer); + gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); + + g_object_unref (replacement_buffer); + g_object_unref (buffer); +} + +GeglNode * +gimp_drawable_get_source_node (GimpDrawable *drawable) +{ + GeglNode *input; + GeglNode *source; + GeglNode *filter; + GeglNode *output; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + if (drawable->private->source_node) + return drawable->private->source_node; + + drawable->private->source_node = gegl_node_new (); + + input = gegl_node_get_input_proxy (drawable->private->source_node, "input"); + + source = GIMP_DRAWABLE_GET_CLASS (drawable)->get_source_node (drawable); + + gegl_node_add_child (drawable->private->source_node, source); + + g_object_unref (source); + + if (gegl_node_has_pad (source, "input")) + { + gegl_node_connect_to (input, "output", + source, "input"); + } + + filter = gimp_filter_stack_get_graph (GIMP_FILTER_STACK (drawable->private->filter_stack)); + + gegl_node_add_child (drawable->private->source_node, filter); + + gegl_node_connect_to (source, "output", + filter, "input"); + + output = gegl_node_get_output_proxy (drawable->private->source_node, "output"); + + gegl_node_connect_to (filter, "output", + output, "input"); + + if (gimp_drawable_get_floating_sel (drawable)) + _gimp_drawable_add_floating_sel_filter (drawable); + + return drawable->private->source_node; +} + +GeglNode * +gimp_drawable_get_mode_node (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + if (! drawable->private->mode_node) + gimp_filter_get_node (GIMP_FILTER (drawable)); + + return drawable->private->mode_node; +} + +GeglRectangle +gimp_drawable_get_bounding_box (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), + *GEGL_RECTANGLE (0, 0, 0, 0)); + + if (gegl_rectangle_is_empty (&drawable->private->bounding_box)) + gimp_drawable_update_bounding_box (drawable); + + return drawable->private->bounding_box; +} + +gboolean +gimp_drawable_update_bounding_box (GimpDrawable *drawable) +{ + GeglRectangle bounding_box; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + bounding_box = + GIMP_DRAWABLE_GET_CLASS (drawable)->get_bounding_box (drawable); + + if (! gegl_rectangle_equal (&bounding_box, &drawable->private->bounding_box)) + { + GeglRectangle old_bounding_box = drawable->private->bounding_box; + GeglRectangle diff_rects[4]; + gint n_diff_rects; + gint i; + + n_diff_rects = gegl_rectangle_subtract (diff_rects, + &old_bounding_box, + &bounding_box); + + for (i = 0; i < n_diff_rects; i++) + { + gimp_drawable_update (drawable, + diff_rects[i].x, + diff_rects[i].y, + diff_rects[i].width, + diff_rects[i].height); + } + + drawable->private->bounding_box = bounding_box; + + g_signal_emit (drawable, gimp_drawable_signals[BOUNDING_BOX_CHANGED], 0); + + n_diff_rects = gegl_rectangle_subtract (diff_rects, + &bounding_box, + &old_bounding_box); + + for (i = 0; i < n_diff_rects; i++) + { + gimp_drawable_update (drawable, + diff_rects[i].x, + diff_rects[i].y, + diff_rects[i].width, + diff_rects[i].height); + } + + return TRUE; + } + + return FALSE; +} + +void +gimp_drawable_swap_pixels (GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + GIMP_DRAWABLE_GET_CLASS (drawable)->swap_pixels (drawable, buffer, x, y); +} + +void +gimp_drawable_push_undo (GimpDrawable *drawable, + const gchar *undo_desc, + GeglBuffer *buffer, + gint x, + gint y, + gint width, + gint height) +{ + GimpItem *item; + + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (buffer == NULL || GEGL_IS_BUFFER (buffer)); + + item = GIMP_ITEM (drawable); + + g_return_if_fail (gimp_item_is_attached (item)); + + if (! buffer && + ! gimp_rectangle_intersect (x, y, + width, height, + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item), + &x, &y, &width, &height)) + { + g_warning ("%s: tried to push empty region", G_STRFUNC); + return; + } + + GIMP_DRAWABLE_GET_CLASS (drawable)->push_undo (drawable, undo_desc, + buffer, + x, y, width, height); +} + +const Babl * +gimp_drawable_get_format (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + return gegl_buffer_get_format (drawable->private->buffer); +} + +const Babl * +gimp_drawable_get_format_with_alpha (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + return gimp_image_get_format (gimp_item_get_image (GIMP_ITEM (drawable)), + gimp_drawable_get_base_type (drawable), + gimp_drawable_get_precision (drawable), + TRUE); +} + +const Babl * +gimp_drawable_get_format_without_alpha (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + return gimp_image_get_format (gimp_item_get_image (GIMP_ITEM (drawable)), + gimp_drawable_get_base_type (drawable), + gimp_drawable_get_precision (drawable), + FALSE); +} + +gboolean +gimp_drawable_get_linear (GimpDrawable *drawable) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + format = gegl_buffer_get_format (drawable->private->buffer); + + return gimp_babl_format_get_linear (format); +} + +gboolean +gimp_drawable_has_alpha (GimpDrawable *drawable) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + format = gegl_buffer_get_format (drawable->private->buffer); + + return babl_format_has_alpha (format); +} + +GimpImageBaseType +gimp_drawable_get_base_type (GimpDrawable *drawable) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), -1); + + format = gegl_buffer_get_format (drawable->private->buffer); + + return gimp_babl_format_get_base_type (format); +} + +GimpComponentType +gimp_drawable_get_component_type (GimpDrawable *drawable) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), -1); + + format = gegl_buffer_get_format (drawable->private->buffer); + + return gimp_babl_format_get_component_type (format); +} + +GimpPrecision +gimp_drawable_get_precision (GimpDrawable *drawable) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), -1); + + format = gegl_buffer_get_format (drawable->private->buffer); + + return gimp_babl_format_get_precision (format); +} + +gboolean +gimp_drawable_is_rgb (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + return (gimp_drawable_get_base_type (drawable) == GIMP_RGB); +} + +gboolean +gimp_drawable_is_gray (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + return (gimp_drawable_get_base_type (drawable) == GIMP_GRAY); +} + +gboolean +gimp_drawable_is_indexed (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + return (gimp_drawable_get_base_type (drawable) == GIMP_INDEXED); +} + +const Babl * +gimp_drawable_get_component_format (GimpDrawable *drawable, + GimpChannelType channel) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + switch (channel) + { + case GIMP_CHANNEL_RED: + return gimp_babl_component_format (GIMP_RGB, + gimp_drawable_get_precision (drawable), + RED); + + case GIMP_CHANNEL_GREEN: + return gimp_babl_component_format (GIMP_RGB, + gimp_drawable_get_precision (drawable), + GREEN); + + case GIMP_CHANNEL_BLUE: + return gimp_babl_component_format (GIMP_RGB, + gimp_drawable_get_precision (drawable), + BLUE); + + case GIMP_CHANNEL_ALPHA: + return gimp_babl_component_format (GIMP_RGB, + gimp_drawable_get_precision (drawable), + ALPHA); + + case GIMP_CHANNEL_GRAY: + return gimp_babl_component_format (GIMP_GRAY, + gimp_drawable_get_precision (drawable), + GRAY); + + case GIMP_CHANNEL_INDEXED: + return babl_format ("Y u8"); /* will extract grayscale, the best + * we can do here */ + } + + return NULL; +} + +gint +gimp_drawable_get_component_index (GimpDrawable *drawable, + GimpChannelType channel) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), -1); + + switch (channel) + { + case GIMP_CHANNEL_RED: return RED; + case GIMP_CHANNEL_GREEN: return GREEN; + case GIMP_CHANNEL_BLUE: return BLUE; + case GIMP_CHANNEL_GRAY: return GRAY; + case GIMP_CHANNEL_INDEXED: return INDEXED; + case GIMP_CHANNEL_ALPHA: + switch (gimp_drawable_get_base_type (drawable)) + { + case GIMP_RGB: return ALPHA; + case GIMP_GRAY: return ALPHA_G; + case GIMP_INDEXED: return ALPHA_I; + } + } + + return -1; +} + +const guchar * +gimp_drawable_get_colormap (GimpDrawable *drawable) +{ + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + return image ? gimp_image_get_colormap (image) : NULL; +} + +void +gimp_drawable_start_paint (GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + + if (drawable->private->paint_count == 0) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (drawable); + + g_return_if_fail (buffer != NULL); + g_return_if_fail (drawable->private->paint_buffer == NULL); + g_return_if_fail (drawable->private->paint_copy_region == NULL); + g_return_if_fail (drawable->private->paint_update_region == NULL); + + drawable->private->paint_buffer = gimp_gegl_buffer_dup (buffer); + } + + drawable->private->paint_count++; +} + +gboolean +gimp_drawable_end_paint (GimpDrawable *drawable) +{ + gboolean result = FALSE; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (drawable->private->paint_count > 0, FALSE); + + if (drawable->private->paint_count == 1) + { + result = gimp_drawable_flush_paint (drawable); + + g_clear_object (&drawable->private->paint_buffer); + } + + drawable->private->paint_count--; + + return result; +} + +gboolean +gimp_drawable_flush_paint (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (drawable->private->paint_count > 0, FALSE); + + if (drawable->private->paint_copy_region) + { + GeglBuffer *buffer; + gint n_rects; + gint i; + + buffer = GIMP_DRAWABLE_GET_CLASS (drawable)->get_buffer (drawable); + + g_return_val_if_fail (buffer != NULL, FALSE); + g_return_val_if_fail (drawable->private->paint_buffer != NULL, FALSE); + + n_rects = cairo_region_num_rectangles ( + drawable->private->paint_copy_region); + + for (i = 0; i < n_rects; i++) + { + GeglRectangle rect; + + cairo_region_get_rectangle (drawable->private->paint_copy_region, + i, (cairo_rectangle_int_t *) &rect); + + gimp_gegl_buffer_copy ( + drawable->private->paint_buffer, &rect, GEGL_ABYSS_NONE, + buffer, NULL); + } + + g_clear_pointer (&drawable->private->paint_copy_region, + cairo_region_destroy); + + n_rects = cairo_region_num_rectangles ( + drawable->private->paint_update_region); + + for (i = 0; i < n_rects; i++) + { + GeglRectangle rect; + + cairo_region_get_rectangle (drawable->private->paint_update_region, + i, (cairo_rectangle_int_t *) &rect); + + g_signal_emit (drawable, gimp_drawable_signals[UPDATE], 0, + rect.x, rect.y, rect.width, rect.height); + } + + g_clear_pointer (&drawable->private->paint_update_region, + cairo_region_destroy); + + return TRUE; + } + + return FALSE; +} + +gboolean +gimp_drawable_is_painting (GimpDrawable *drawable) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + + return drawable->private->paint_count > 0; +} diff --git a/app/core/gimpdrawable.h b/app/core/gimpdrawable.h new file mode 100644 index 0000000..73c4283 --- /dev/null +++ b/app/core/gimpdrawable.h @@ -0,0 +1,227 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_H__ +#define __GIMP_DRAWABLE_H__ + + +#include "gimpitem.h" + + +#define GIMP_TYPE_DRAWABLE (gimp_drawable_get_type ()) +#define GIMP_DRAWABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE, GimpDrawable)) +#define GIMP_DRAWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE, GimpDrawableClass)) +#define GIMP_IS_DRAWABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE)) +#define GIMP_IS_DRAWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE)) +#define GIMP_DRAWABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE, GimpDrawableClass)) + + +typedef struct _GimpDrawablePrivate GimpDrawablePrivate; +typedef struct _GimpDrawableClass GimpDrawableClass; + +struct _GimpDrawable +{ + GimpItem parent_instance; + + GimpDrawablePrivate *private; +}; + +struct _GimpDrawableClass +{ + GimpItemClass parent_class; + + /* signals */ + void (* update) (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height); + void (* format_changed) (GimpDrawable *drawable); + void (* alpha_changed) (GimpDrawable *drawable); + void (* bounding_box_changed) (GimpDrawable *drawable); + + /* virtual functions */ + gint64 (* estimate_memsize) (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height); + void (* update_all) (GimpDrawable *drawable); + void (* invalidate_boundary) (GimpDrawable *drawable); + void (* get_active_components) (GimpDrawable *drawable, + gboolean *active); + GimpComponentMask (* get_active_mask) (GimpDrawable *drawable); + gboolean (* supports_alpha) (GimpDrawable *drawable); + void (* convert_type) (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); + void (* apply_buffer) (GimpDrawable *drawable, + GeglBuffer *buffer, + const GeglRectangle *buffer_region, + gboolean push_undo, + const gchar *undo_desc, + gdouble opacity, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + GeglBuffer *base_buffer, + gint base_x, + gint base_y); + GeglBuffer * (* get_buffer) (GimpDrawable *drawable); + void (* set_buffer) (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds); + GeglRectangle (* get_bounding_box) (GimpDrawable *drawable); + void (* push_undo) (GimpDrawable *drawable, + const gchar *undo_desc, + GeglBuffer *buffer, + gint x, + gint y, + gint width, + gint height); + void (* swap_pixels) (GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y); + GeglNode * (* get_source_node) (GimpDrawable *drawable); +}; + + +GType gimp_drawable_get_type (void) G_GNUC_CONST; + +GimpDrawable * gimp_drawable_new (GType type, + GimpImage *image, + const gchar *name, + gint offset_x, + gint offset_y, + gint width, + gint height, + const Babl *format); + +gint64 gimp_drawable_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height); + +void gimp_drawable_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height); +void gimp_drawable_update_all (GimpDrawable *drawable); + +void gimp_drawable_invalidate_boundary (GimpDrawable *drawable); +void gimp_drawable_get_active_components (GimpDrawable *drawable, + gboolean *active); +GimpComponentMask gimp_drawable_get_active_mask (GimpDrawable *drawable); + +gboolean gimp_drawable_supports_alpha (GimpDrawable *drawable); + +void gimp_drawable_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + GimpImageBaseType new_base_type, + GimpPrecision new_precision, + gboolean new_has_alpha, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); + +void gimp_drawable_apply_buffer (GimpDrawable *drawable, + GeglBuffer *buffer, + const GeglRectangle *buffer_rect, + gboolean push_undo, + const gchar *undo_desc, + gdouble opacity, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + GeglBuffer *base_buffer, + gint base_x, + gint base_y); + +GeglBuffer * gimp_drawable_get_buffer (GimpDrawable *drawable); +void gimp_drawable_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer); +void gimp_drawable_set_buffer_full (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds, + gboolean update); + +void gimp_drawable_steal_buffer (GimpDrawable *drawable, + GimpDrawable *src_drawable); + +GeglNode * gimp_drawable_get_source_node (GimpDrawable *drawable); +GeglNode * gimp_drawable_get_mode_node (GimpDrawable *drawable); + +GeglRectangle gimp_drawable_get_bounding_box (GimpDrawable *drawable); +gboolean gimp_drawable_update_bounding_box + (GimpDrawable *drawable); + +void gimp_drawable_swap_pixels (GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y); + +void gimp_drawable_push_undo (GimpDrawable *drawable, + const gchar *undo_desc, + GeglBuffer *buffer, + gint x, + gint y, + gint width, + gint height); + +const Babl * gimp_drawable_get_format (GimpDrawable *drawable); +const Babl * gimp_drawable_get_format_with_alpha(GimpDrawable *drawable); +const Babl * gimp_drawable_get_format_without_alpha + (GimpDrawable *drawable); +gboolean gimp_drawable_get_linear (GimpDrawable *drawable); +gboolean gimp_drawable_has_alpha (GimpDrawable *drawable); +GimpImageBaseType gimp_drawable_get_base_type (GimpDrawable *drawable); +GimpComponentType gimp_drawable_get_component_type (GimpDrawable *drawable); +GimpPrecision gimp_drawable_get_precision (GimpDrawable *drawable); +gboolean gimp_drawable_is_rgb (GimpDrawable *drawable); +gboolean gimp_drawable_is_gray (GimpDrawable *drawable); +gboolean gimp_drawable_is_indexed (GimpDrawable *drawable); + +const Babl * gimp_drawable_get_component_format (GimpDrawable *drawable, + GimpChannelType channel); +gint gimp_drawable_get_component_index (GimpDrawable *drawable, + GimpChannelType channel); + +const guchar * gimp_drawable_get_colormap (GimpDrawable *drawable); + +void gimp_drawable_start_paint (GimpDrawable *drawable); +gboolean gimp_drawable_end_paint (GimpDrawable *drawable); +gboolean gimp_drawable_flush_paint (GimpDrawable *drawable); +gboolean gimp_drawable_is_painting (GimpDrawable *drawable); + + +#endif /* __GIMP_DRAWABLE_H__ */ diff --git a/app/core/gimpdrawablefilter.c b/app/core/gimpdrawablefilter.c new file mode 100644 index 0000000..6cda4f1 --- /dev/null +++ b/app/core/gimpdrawablefilter.c @@ -0,0 +1,1364 @@ +/* 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 . + */ + +/* This file contains the code necessary for generating on canvas + * previews, by connecting a specified GEGL operation to do the + * processing. It uses drawable filters that allow for non-destructive + * manipulation of drawable data, with live preview on screen. + * + * To create a tool that uses this, see app/tools/gimpfiltertool.c for + * the interface and e.g. app/tools/gimpcolorbalancetool.c for an + * example of using that interface. + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimpapplicator.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimpchannel.h" +#include "gimpdrawable-filters.h" +#include "gimpdrawablefilter.h" +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimpmarshal.h" +#include "gimpprogress.h" + + +enum +{ + FLUSH, + LAST_SIGNAL +}; + + +struct _GimpDrawableFilter +{ + GimpFilter parent_instance; + + GimpDrawable *drawable; + GeglNode *operation; + + gboolean has_input; + + gboolean clip; + GimpFilterRegion region; + gboolean crop_enabled; + GeglRectangle crop_rect; + gboolean preview_enabled; + gboolean preview_split_enabled; + GimpAlignmentType preview_split_alignment; + gint preview_split_position; + gdouble opacity; + GimpLayerMode paint_mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + gboolean add_alpha; + gboolean color_managed; + gboolean gamma_hack; + + gboolean override_constraints; + + GeglRectangle filter_area; + gboolean filter_clip; + + GeglNode *translate; + GeglNode *crop_before; + GeglNode *cast_before; + GeglNode *transform_before; + GeglNode *transform_after; + GeglNode *cast_after; + GeglNode *crop_after; + GimpApplicator *applicator; +}; + + +static void gimp_drawable_filter_dispose (GObject *object); +static void gimp_drawable_filter_finalize (GObject *object); + +static void gimp_drawable_filter_sync_active (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_clip (GimpDrawableFilter *filter, + gboolean sync_region); +static void gimp_drawable_filter_sync_region (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter, + gboolean old_crop_enabled, + const GeglRectangle *old_crop_rect, + gboolean old_preview_split_enabled, + GimpAlignmentType old_preview_split_alignment, + gint old_preview_split_position, + gboolean update); +static void gimp_drawable_filter_sync_opacity (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_mode (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_affect (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_format (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_mask (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_transform (GimpDrawableFilter *filter); +static void gimp_drawable_filter_sync_gamma_hack (GimpDrawableFilter *filter); + +static gboolean gimp_drawable_filter_is_added (GimpDrawableFilter *filter); +static gboolean gimp_drawable_filter_is_active (GimpDrawableFilter *filter); +static gboolean gimp_drawable_filter_add_filter (GimpDrawableFilter *filter); +static gboolean gimp_drawable_filter_remove_filter (GimpDrawableFilter *filter); + +static void gimp_drawable_filter_update_drawable (GimpDrawableFilter *filter, + const GeglRectangle *area); + +static void gimp_drawable_filter_affect_changed (GimpImage *image, + GimpChannelType channel, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_mask_changed (GimpImage *image, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_profile_changed (GimpColorManaged *managed, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_lock_position_changed (GimpDrawable *drawable, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_format_changed (GimpDrawable *drawable, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_drawable_removed (GimpDrawable *drawable, + GimpDrawableFilter *filter); +static void gimp_drawable_filter_lock_alpha_changed (GimpLayer *layer, + GimpDrawableFilter *filter); + + +G_DEFINE_TYPE (GimpDrawableFilter, gimp_drawable_filter, GIMP_TYPE_FILTER) + +#define parent_class gimp_drawable_filter_parent_class + +static guint drawable_filter_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_drawable_filter_class_init (GimpDrawableFilterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + drawable_filter_signals[FLUSH] = + g_signal_new ("flush", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableFilterClass, flush), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = gimp_drawable_filter_dispose; + object_class->finalize = gimp_drawable_filter_finalize; +} + +static void +gimp_drawable_filter_init (GimpDrawableFilter *drawable_filter) +{ + drawable_filter->clip = TRUE; + drawable_filter->region = GIMP_FILTER_REGION_SELECTION; + drawable_filter->preview_enabled = TRUE; + drawable_filter->preview_split_enabled = FALSE; + drawable_filter->preview_split_alignment = GIMP_ALIGN_LEFT; + drawable_filter->preview_split_position = 0; + drawable_filter->opacity = GIMP_OPACITY_OPAQUE; + drawable_filter->paint_mode = GIMP_LAYER_MODE_REPLACE; + drawable_filter->blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + drawable_filter->composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + drawable_filter->composite_mode = GIMP_LAYER_COMPOSITE_AUTO; +} + +static void +gimp_drawable_filter_dispose (GObject *object) +{ + GimpDrawableFilter *drawable_filter = GIMP_DRAWABLE_FILTER (object); + + if (drawable_filter->drawable) + gimp_drawable_filter_remove_filter (drawable_filter); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_drawable_filter_finalize (GObject *object) +{ + GimpDrawableFilter *drawable_filter = GIMP_DRAWABLE_FILTER (object); + + g_clear_object (&drawable_filter->operation); + g_clear_object (&drawable_filter->applicator); + g_clear_object (&drawable_filter->drawable); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +GimpDrawableFilter * +gimp_drawable_filter_new (GimpDrawable *drawable, + const gchar *undo_desc, + GeglNode *operation, + const gchar *icon_name) +{ + GimpDrawableFilter *filter; + GeglNode *node; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GEGL_IS_NODE (operation), NULL); + g_return_val_if_fail (gegl_node_has_pad (operation, "output"), NULL); + + filter = g_object_new (GIMP_TYPE_DRAWABLE_FILTER, + "name", undo_desc, + "icon-name", icon_name, + NULL); + + filter->drawable = g_object_ref (drawable); + filter->operation = g_object_ref (operation); + + node = gimp_filter_get_node (GIMP_FILTER (filter)); + + gegl_node_add_child (node, operation); + gimp_gegl_node_set_underlying_operation (node, operation); + + filter->applicator = gimp_applicator_new (node); + + gimp_filter_set_applicator (GIMP_FILTER (filter), filter->applicator); + + gimp_applicator_set_cache (filter->applicator, TRUE); + + filter->has_input = gegl_node_has_pad (filter->operation, "input"); + + if (filter->has_input) + { + GeglNode *input; + + input = gegl_node_get_input_proxy (node, "input"); + + filter->translate = gegl_node_new_child (node, + "operation", "gegl:translate", + NULL); + + filter->crop_before = gegl_node_new_child (node, + "operation", "gegl:crop", + NULL); + + filter->cast_before = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + + filter->transform_before = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + + gegl_node_link_many (input, + filter->translate, + filter->crop_before, + filter->cast_before, + filter->transform_before, + filter->operation, + NULL); + } + + filter->transform_after = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + + filter->cast_after = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + + filter->crop_after = gegl_node_new_child (node, + "operation", "gegl:crop", + NULL); + + gegl_node_link_many (filter->operation, + filter->transform_after, + filter->cast_after, + filter->crop_after, + NULL); + + gegl_node_connect_to (filter->crop_after, "output", + node, "aux"); + + return filter; +} + +GimpDrawable * +gimp_drawable_filter_get_drawable (GimpDrawableFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL); + + return filter->drawable; +} + +GeglNode * +gimp_drawable_filter_get_operation (GimpDrawableFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL); + + return filter->operation; +} + +void +gimp_drawable_filter_set_clip (GimpDrawableFilter *filter, + gboolean clip) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (clip != filter->clip) + { + filter->clip = clip; + + gimp_drawable_filter_sync_clip (filter, TRUE); + } +} + +void +gimp_drawable_filter_set_region (GimpDrawableFilter *filter, + GimpFilterRegion region) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (region != filter->region) + { + filter->region = region; + + gimp_drawable_filter_sync_region (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_crop (GimpDrawableFilter *filter, + const GeglRectangle *rect, + gboolean update) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if ((rect != NULL) != filter->crop_enabled || + (rect && ! gegl_rectangle_equal (rect, &filter->crop_rect))) + { + gboolean old_enabled = filter->crop_enabled; + GeglRectangle old_rect = filter->crop_rect; + + if (rect) + { + filter->crop_enabled = TRUE; + filter->crop_rect = *rect; + } + else + { + filter->crop_enabled = FALSE; + } + + gimp_drawable_filter_sync_crop (filter, + old_enabled, + &old_rect, + filter->preview_split_enabled, + filter->preview_split_alignment, + filter->preview_split_position, + update); + } +} + +void +gimp_drawable_filter_set_preview (GimpDrawableFilter *filter, + gboolean enabled) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (enabled != filter->preview_enabled) + { + filter->preview_enabled = enabled; + + gimp_drawable_filter_sync_active (filter); + + if (gimp_drawable_filter_is_added (filter)) + { + gimp_drawable_update_bounding_box (filter->drawable); + + gimp_drawable_filter_update_drawable (filter, NULL); + } + } +} + +void +gimp_drawable_filter_set_preview_split (GimpDrawableFilter *filter, + gboolean enabled, + GimpAlignmentType alignment, + gint position) +{ + GimpItem *item; + + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + g_return_if_fail (alignment == GIMP_ALIGN_LEFT || + alignment == GIMP_ALIGN_RIGHT || + alignment == GIMP_ALIGN_TOP || + alignment == GIMP_ALIGN_BOTTOM); + + item = GIMP_ITEM (filter->drawable); + + switch (alignment) + { + case GIMP_ALIGN_LEFT: + case GIMP_ALIGN_RIGHT: + position = CLAMP (position, 0, gimp_item_get_width (item)); + break; + + case GIMP_ALIGN_TOP: + case GIMP_ALIGN_BOTTOM: + position = CLAMP (position, 0, gimp_item_get_height (item)); + break; + + default: + g_return_if_reached (); + } + + if (enabled != filter->preview_split_enabled || + alignment != filter->preview_split_alignment || + position != filter->preview_split_position) + { + gboolean old_enabled = filter->preview_split_enabled; + GimpAlignmentType old_alignment = filter->preview_split_alignment; + gint old_position = filter->preview_split_position; + + filter->preview_split_enabled = enabled; + filter->preview_split_alignment = alignment; + filter->preview_split_position = position; + + gimp_drawable_filter_sync_crop (filter, + filter->crop_enabled, + &filter->crop_rect, + old_enabled, + old_alignment, + old_position, + TRUE); + } +} + +void +gimp_drawable_filter_set_opacity (GimpDrawableFilter *filter, + gdouble opacity) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (opacity != filter->opacity) + { + filter->opacity = opacity; + + gimp_drawable_filter_sync_opacity (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_mode (GimpDrawableFilter *filter, + GimpLayerMode paint_mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (paint_mode != filter->paint_mode || + blend_space != filter->blend_space || + composite_space != filter->composite_space || + composite_mode != filter->composite_mode) + { + filter->paint_mode = paint_mode; + filter->blend_space = blend_space; + filter->composite_space = composite_space; + filter->composite_mode = composite_mode; + + gimp_drawable_filter_sync_mode (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_add_alpha (GimpDrawableFilter *filter, + gboolean add_alpha) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (add_alpha != filter->add_alpha) + { + filter->add_alpha = add_alpha; + + gimp_drawable_filter_sync_format (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_color_managed (GimpDrawableFilter *filter, + gboolean color_managed) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (color_managed != filter->color_managed) + { + filter->color_managed = color_managed; + + gimp_drawable_filter_sync_transform (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_gamma_hack (GimpDrawableFilter *filter, + gboolean gamma_hack) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (gamma_hack != filter->gamma_hack) + { + filter->gamma_hack = gamma_hack; + + gimp_drawable_filter_sync_gamma_hack (filter); + gimp_drawable_filter_sync_transform (filter); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +void +gimp_drawable_filter_set_override_constraints (GimpDrawableFilter *filter, + gboolean override_constraints) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (override_constraints != filter->override_constraints) + { + filter->override_constraints = override_constraints; + + gimp_drawable_filter_sync_affect (filter); + gimp_drawable_filter_sync_format (filter); + gimp_drawable_filter_sync_clip (filter, TRUE); + + if (gimp_drawable_filter_is_active (filter)) + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + +const Babl * +gimp_drawable_filter_get_format (GimpDrawableFilter *filter) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL); + + format = gimp_applicator_get_output_format (filter->applicator); + + if (! format) + format = gimp_drawable_get_format (filter->drawable); + + return format; +} + +void +gimp_drawable_filter_apply (GimpDrawableFilter *filter, + const GeglRectangle *area) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (filter->drawable))); + + gimp_drawable_filter_add_filter (filter); + + gimp_drawable_filter_sync_clip (filter, TRUE); + + if (gimp_drawable_filter_is_active (filter)) + { + gimp_drawable_update_bounding_box (filter->drawable); + + gimp_drawable_filter_update_drawable (filter, area); + } +} + +gboolean +gimp_drawable_filter_commit (GimpDrawableFilter *filter, + GimpProgress *progress, + gboolean cancellable) +{ + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), FALSE); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (filter->drawable)), + FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + + if (gimp_drawable_filter_is_added (filter)) + { + const Babl *format; + + format = gimp_drawable_filter_get_format (filter); + + gimp_drawable_filter_set_preview_split (filter, FALSE, + filter->preview_split_alignment, + filter->preview_split_position); + gimp_drawable_filter_set_preview (filter, TRUE); + + success = gimp_drawable_merge_filter (filter->drawable, + GIMP_FILTER (filter), + progress, + gimp_object_get_name (filter), + format, + filter->filter_clip, + cancellable, + FALSE); + + gimp_drawable_filter_remove_filter (filter); + + if (! success) + gimp_drawable_filter_update_drawable (filter, NULL); + + g_signal_emit (filter, drawable_filter_signals[FLUSH], 0); + } + + return success; +} + +void +gimp_drawable_filter_abort (GimpDrawableFilter *filter) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + if (gimp_drawable_filter_remove_filter (filter)) + { + gimp_drawable_filter_update_drawable (filter, NULL); + } +} + + +/* private functions */ + +static void +gimp_drawable_filter_sync_active (GimpDrawableFilter *filter) +{ + gimp_applicator_set_active (filter->applicator, filter->preview_enabled); +} + +static void +gimp_drawable_filter_sync_clip (GimpDrawableFilter *filter, + gboolean sync_region) +{ + gboolean clip; + + if (filter->override_constraints) + clip = filter->clip; + else + clip = gimp_item_get_clip (GIMP_ITEM (filter->drawable), filter->clip); + + if (! clip) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + GimpChannel *mask = gimp_image_get_mask (image); + + if (! gimp_channel_is_empty (mask)) + clip = TRUE; + } + + if (! clip) + { + GeglRectangle bounding_box; + + bounding_box = gegl_node_get_bounding_box (filter->operation); + + if (gegl_rectangle_is_infinite_plane (&bounding_box)) + clip = TRUE; + } + + if (clip != filter->filter_clip) + { + filter->filter_clip = clip; + + if (sync_region) + gimp_drawable_filter_sync_region (filter); + } +} + +static void +gimp_drawable_filter_sync_region (GimpDrawableFilter *filter) +{ + if (filter->region == GIMP_FILTER_REGION_SELECTION) + { + if (filter->has_input) + { + gegl_node_set (filter->translate, + "x", (gdouble) -filter->filter_area.x, + "y", (gdouble) -filter->filter_area.y, + NULL); + + gegl_node_set (filter->crop_before, + "width", (gdouble) filter->filter_area.width, + "height", (gdouble) filter->filter_area.height, + NULL); + } + + if (filter->filter_clip) + { + gegl_node_set (filter->crop_after, + "operation", "gegl:crop", + "x", 0.0, + "y", 0.0, + "width", (gdouble) filter->filter_area.width, + "height", (gdouble) filter->filter_area.height, + NULL); + } + else + { + gegl_node_set (filter->crop_after, + "operation", "gegl:nop", + NULL); + } + + gimp_applicator_set_apply_offset (filter->applicator, + filter->filter_area.x, + filter->filter_area.y); + } + else + { + GimpItem *item = GIMP_ITEM (filter->drawable); + gdouble width = gimp_item_get_width (item); + gdouble height = gimp_item_get_height (item); + + if (filter->has_input) + { + gegl_node_set (filter->translate, + "x", (gdouble) 0.0, + "y", (gdouble) 0.0, + NULL); + + gegl_node_set (filter->crop_before, + "width", width, + "height", height, + NULL); + } + + if (filter->filter_clip) + { + gegl_node_set (filter->crop_after, + "operation", "gegl:crop", + "x", (gdouble) filter->filter_area.x, + "y", (gdouble) filter->filter_area.y, + "width", (gdouble) filter->filter_area.width, + "height", (gdouble) filter->filter_area.height, + NULL); + } + else + { + gegl_node_set (filter->crop_after, + "operation", "gegl:nop", + NULL); + } + + gimp_applicator_set_apply_offset (filter->applicator, 0, 0); + } + + if (gimp_drawable_filter_is_active (filter)) + { + if (gimp_drawable_update_bounding_box (filter->drawable)) + g_signal_emit (filter, drawable_filter_signals[FLUSH], 0); + } +} + +static gboolean +gimp_drawable_filter_get_crop_rect (GimpDrawableFilter *filter, + gboolean crop_enabled, + const GeglRectangle *crop_rect, + gboolean preview_split_enabled, + GimpAlignmentType preview_split_alignment, + gint preview_split_position, + GeglRectangle *rect) +{ + GeglRectangle bounds; + gint x1, x2; + gint y1, y2; + + bounds = gegl_rectangle_infinite_plane (); + + x1 = bounds.x; + x2 = bounds.x + bounds.width; + + y1 = bounds.y; + y2 = bounds.y + bounds.height; + + if (preview_split_enabled) + { + switch (preview_split_alignment) + { + case GIMP_ALIGN_LEFT: + x2 = preview_split_position; + break; + + case GIMP_ALIGN_RIGHT: + x1 = preview_split_position; + break; + + case GIMP_ALIGN_TOP: + y2 = preview_split_position; + break; + + case GIMP_ALIGN_BOTTOM: + y1 = preview_split_position; + break; + + default: + g_return_val_if_reached (FALSE); + } + } + + gegl_rectangle_set (rect, x1, y1, x2 - x1, y2 - y1); + + if (crop_enabled) + gegl_rectangle_intersect (rect, rect, crop_rect); + + return ! gegl_rectangle_equal (rect, &bounds); +} + +static void +gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter, + gboolean old_crop_enabled, + const GeglRectangle *old_crop_rect, + gboolean old_preview_split_enabled, + GimpAlignmentType old_preview_split_alignment, + gint old_preview_split_position, + gboolean update) +{ + GeglRectangle old_rect; + GeglRectangle new_rect; + gboolean enabled; + + gimp_drawable_filter_get_crop_rect (filter, + old_crop_enabled, + old_crop_rect, + old_preview_split_enabled, + old_preview_split_alignment, + old_preview_split_position, + &old_rect); + + enabled = gimp_drawable_filter_get_crop_rect (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_split_enabled, + filter->preview_split_alignment, + filter->preview_split_position, + &new_rect); + + gimp_applicator_set_crop (filter->applicator, enabled ? &new_rect : NULL); + + if (update && + gimp_drawable_filter_is_active (filter) && + ! gegl_rectangle_equal (&old_rect, &new_rect)) + { + GeglRectangle diff_rects[4]; + gint n_diff_rects; + gint i; + + gimp_drawable_update_bounding_box (filter->drawable); + + n_diff_rects = gegl_rectangle_xor (diff_rects, &old_rect, &new_rect); + + for (i = 0; i < n_diff_rects; i++) + gimp_drawable_filter_update_drawable (filter, &diff_rects[i]); + } +} + +static void +gimp_drawable_filter_sync_opacity (GimpDrawableFilter *filter) +{ + gimp_applicator_set_opacity (filter->applicator, + filter->opacity); +} + +static void +gimp_drawable_filter_sync_mode (GimpDrawableFilter *filter) +{ + GimpLayerMode paint_mode = filter->paint_mode; + + if (! filter->has_input && paint_mode == GIMP_LAYER_MODE_REPLACE) + { + /* if the filter's op has no input, use NORMAL instead of REPLACE, so + * that we composite the op's output on top of the input, instead of + * completely replacing it. + */ + paint_mode = GIMP_LAYER_MODE_NORMAL; + } + + gimp_applicator_set_mode (filter->applicator, + paint_mode, + filter->blend_space, + filter->composite_space, + filter->composite_mode); +} + +static void +gimp_drawable_filter_sync_affect (GimpDrawableFilter *filter) +{ + gimp_applicator_set_affect ( + filter->applicator, + filter->override_constraints ? + + GIMP_COMPONENT_MASK_RED | + GIMP_COMPONENT_MASK_GREEN | + GIMP_COMPONENT_MASK_BLUE | + GIMP_COMPONENT_MASK_ALPHA : + + gimp_drawable_get_active_mask (filter->drawable)); +} + +static void +gimp_drawable_filter_sync_format (GimpDrawableFilter *filter) +{ + const Babl *format; + + if (filter->add_alpha && + (gimp_drawable_supports_alpha (filter->drawable) || + filter->override_constraints)) + { + format = gimp_drawable_get_format_with_alpha (filter->drawable); + } + else + { + format = gimp_drawable_get_format (filter->drawable); + } + + gimp_applicator_set_output_format (filter->applicator, format); +} + +static void +gimp_drawable_filter_sync_mask (GimpDrawableFilter *filter) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + GimpChannel *mask = gimp_image_get_mask (image); + + if (gimp_channel_is_empty (mask)) + { + gimp_applicator_set_mask_buffer (filter->applicator, NULL); + } + else + { + GeglBuffer *mask_buffer; + gint offset_x, offset_y; + + mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + gimp_item_get_offset (GIMP_ITEM (filter->drawable), + &offset_x, &offset_y); + + gimp_applicator_set_mask_buffer (filter->applicator, mask_buffer); + gimp_applicator_set_mask_offset (filter->applicator, + -offset_x, -offset_y); + } + + gimp_item_mask_intersect (GIMP_ITEM (filter->drawable), + &filter->filter_area.x, + &filter->filter_area.y, + &filter->filter_area.width, + &filter->filter_area.height); +} + +static void +gimp_drawable_filter_sync_transform (GimpDrawableFilter *filter) +{ + GimpColorManaged *managed = GIMP_COLOR_MANAGED (filter->drawable); + + if (filter->color_managed) + { + const Babl *drawable_format = NULL; + const Babl *input_format = NULL; + const Babl *output_format = NULL; + GimpColorProfile *drawable_profile = NULL; + GimpColorProfile *input_profile = NULL; + GimpColorProfile *output_profile = NULL; + guint32 dummy; + + drawable_format = gimp_drawable_get_format (filter->drawable); + if (filter->has_input) + input_format = gimp_gegl_node_get_format (filter->operation, "input"); + output_format = gimp_gegl_node_get_format (filter->operation, "output"); + + g_printerr ("drawable format: %s\n", babl_get_name (drawable_format)); + if (filter->has_input) + g_printerr ("filter input format: %s\n", babl_get_name (input_format)); + g_printerr ("filter output format: %s\n", babl_get_name (output_format)); + + /* convert the drawable format to float, so we get a precise + * color transform + */ + drawable_format = + gimp_babl_format (gimp_babl_format_get_base_type (drawable_format), + gimp_babl_precision (GIMP_COMPONENT_TYPE_FLOAT, + gimp_babl_format_get_linear (drawable_format)), + babl_format_has_alpha (drawable_format)); + + /* convert the filter input/output formats to something we have + * built-in color profiles for (see the get_color_profile() + * calls below) + */ + if (filter->has_input) + input_format = gimp_color_profile_get_lcms_format (input_format, &dummy); + output_format = gimp_color_profile_get_lcms_format (output_format, &dummy); + + g_printerr ("profile transform drawable format: %s\n", + babl_get_name (drawable_format)); + if (filter->has_input) + g_printerr ("profile transform input format: %s\n", + babl_get_name (input_format)); + g_printerr ("profile transform output format: %s\n", + babl_get_name (output_format)); + + drawable_profile = gimp_color_managed_get_color_profile (managed); + if (filter->has_input) + input_profile = gimp_babl_format_get_color_profile (input_format); + output_profile = gimp_babl_format_get_color_profile (output_format); + + if ((filter->has_input && + ! gimp_color_transform_can_gegl_copy (drawable_profile, + input_profile)) || + ! gimp_color_transform_can_gegl_copy (output_profile, + drawable_profile)) + { + g_printerr ("using gimp:profile-transform\n"); + + if (filter->has_input) + { + gegl_node_set (filter->transform_before, + "operation", "gimp:profile-transform", + "src-profile", drawable_profile, + "src-format", drawable_format, + "dest-profile", input_profile, + "dest-format", input_format, + NULL); + } + + gegl_node_set (filter->transform_after, + "operation", "gimp:profile-transform", + "src-profile", output_profile, + "src-format", output_format, + "dest-profile", drawable_profile, + "dest-format", drawable_format, + NULL); + + return; + } + } + + g_printerr ("using gegl copy\n"); + + if (filter->has_input) + { + gegl_node_set (filter->transform_before, + "operation", "gegl:nop", + NULL); + } + + gegl_node_set (filter->transform_after, + "operation", "gegl:nop", + NULL); +} + +static void +gimp_drawable_filter_sync_gamma_hack (GimpDrawableFilter *filter) +{ + if (filter->gamma_hack) + { + const Babl *drawable_format; + const Babl *cast_format; + + drawable_format = + gimp_drawable_get_format_with_alpha (filter->drawable); + + cast_format = + gimp_babl_format (gimp_babl_format_get_base_type (drawable_format), + gimp_babl_precision (gimp_babl_format_get_component_type (drawable_format), + ! gimp_babl_format_get_linear (drawable_format)), + TRUE); + + if (filter->has_input) + { + gegl_node_set (filter->cast_before, + "operation", "gegl:cast-format", + "input-format", drawable_format, + "output-format", cast_format, + NULL); + } + + gegl_node_set (filter->cast_after, + "operation", "gegl:cast-format", + "input-format", cast_format, + "output-format", drawable_format, + NULL); + } + else + { + if (filter->has_input) + { + gegl_node_set (filter->cast_before, + "operation", "gegl:nop", + NULL); + } + + gegl_node_set (filter->cast_after, + "operation", "gegl:nop", + NULL); + } +} + +static gboolean +gimp_drawable_filter_is_added (GimpDrawableFilter *filter) +{ + return gimp_drawable_has_filter (filter->drawable, + GIMP_FILTER (filter)); +} + +static gboolean +gimp_drawable_filter_is_active (GimpDrawableFilter *filter) +{ + return gimp_drawable_filter_is_added (filter) && + filter->preview_enabled; +} + +static gboolean +gimp_drawable_filter_add_filter (GimpDrawableFilter *filter) +{ + if (! gimp_drawable_filter_is_added (filter)) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + + gimp_viewable_preview_freeze (GIMP_VIEWABLE (filter->drawable)); + + gimp_drawable_filter_sync_active (filter); + gimp_drawable_filter_sync_mask (filter); + gimp_drawable_filter_sync_clip (filter, FALSE); + gimp_drawable_filter_sync_region (filter); + gimp_drawable_filter_sync_crop (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_split_enabled, + filter->preview_split_alignment, + filter->preview_split_position, + TRUE); + gimp_drawable_filter_sync_opacity (filter); + gimp_drawable_filter_sync_mode (filter); + gimp_drawable_filter_sync_affect (filter); + gimp_drawable_filter_sync_format (filter); + gimp_drawable_filter_sync_transform (filter); + gimp_drawable_filter_sync_gamma_hack (filter); + + gimp_drawable_add_filter (filter->drawable, + GIMP_FILTER (filter)); + + gimp_drawable_update_bounding_box (filter->drawable); + + g_signal_connect (image, "component-active-changed", + G_CALLBACK (gimp_drawable_filter_affect_changed), + filter); + g_signal_connect (image, "mask-changed", + G_CALLBACK (gimp_drawable_filter_mask_changed), + filter); + g_signal_connect (image, "profile-changed", + G_CALLBACK (gimp_drawable_filter_profile_changed), + filter); + g_signal_connect (filter->drawable, "lock-position-changed", + G_CALLBACK (gimp_drawable_filter_lock_position_changed), + filter); + g_signal_connect (filter->drawable, "format-changed", + G_CALLBACK (gimp_drawable_filter_format_changed), + filter); + g_signal_connect (filter->drawable, "removed", + G_CALLBACK (gimp_drawable_filter_drawable_removed), + filter); + + if (GIMP_IS_LAYER (filter->drawable)) + { + g_signal_connect (filter->drawable, "lock-alpha-changed", + G_CALLBACK (gimp_drawable_filter_lock_alpha_changed), + filter); + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +gimp_drawable_filter_remove_filter (GimpDrawableFilter *filter) +{ + if (gimp_drawable_filter_is_added (filter)) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + + if (GIMP_IS_LAYER (filter->drawable)) + { + g_signal_handlers_disconnect_by_func (filter->drawable, + gimp_drawable_filter_lock_alpha_changed, + filter); + } + + g_signal_handlers_disconnect_by_func (filter->drawable, + gimp_drawable_filter_drawable_removed, + filter); + g_signal_handlers_disconnect_by_func (filter->drawable, + gimp_drawable_filter_format_changed, + filter); + g_signal_handlers_disconnect_by_func (filter->drawable, + gimp_drawable_filter_lock_position_changed, + filter); + g_signal_handlers_disconnect_by_func (image, + gimp_drawable_filter_profile_changed, + filter); + g_signal_handlers_disconnect_by_func (image, + gimp_drawable_filter_mask_changed, + filter); + g_signal_handlers_disconnect_by_func (image, + gimp_drawable_filter_affect_changed, + filter); + + gimp_drawable_remove_filter (filter->drawable, + GIMP_FILTER (filter)); + + gimp_drawable_update_bounding_box (filter->drawable); + + gimp_viewable_preview_thaw (GIMP_VIEWABLE (filter->drawable)); + + return TRUE; + } + + return FALSE; +} + +static void +gimp_drawable_filter_update_drawable (GimpDrawableFilter *filter, + const GeglRectangle *area) +{ + GeglRectangle bounding_box; + GeglRectangle update_area; + + bounding_box = gimp_drawable_get_bounding_box (filter->drawable); + + if (area) + { + if (! gegl_rectangle_intersect (&update_area, + area, &bounding_box)) + { + return; + } + } + else + { + gimp_drawable_filter_get_crop_rect (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_split_enabled, + filter->preview_split_alignment, + filter->preview_split_position, + &update_area); + + if (! gegl_rectangle_intersect (&update_area, + &update_area, &bounding_box)) + { + return; + } + } + + if (update_area.width > 0 && + update_area.height > 0) + { + gimp_drawable_update (filter->drawable, + update_area.x, + update_area.y, + update_area.width, + update_area.height); + + g_signal_emit (filter, drawable_filter_signals[FLUSH], 0); + } +} + +static void +gimp_drawable_filter_affect_changed (GimpImage *image, + GimpChannelType channel, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_sync_affect (filter); + gimp_drawable_filter_update_drawable (filter, NULL); +} + +static void +gimp_drawable_filter_mask_changed (GimpImage *image, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_update_drawable (filter, NULL); + + gimp_drawable_filter_sync_mask (filter); + gimp_drawable_filter_sync_clip (filter, FALSE); + gimp_drawable_filter_sync_region (filter); + + gimp_drawable_filter_update_drawable (filter, NULL); +} + +static void +gimp_drawable_filter_profile_changed (GimpColorManaged *managed, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_sync_transform (filter); + gimp_drawable_filter_update_drawable (filter, NULL); +} + +static void +gimp_drawable_filter_lock_position_changed (GimpDrawable *drawable, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_sync_clip (filter, TRUE); + gimp_drawable_filter_update_drawable (filter, NULL); +} + +static void +gimp_drawable_filter_format_changed (GimpDrawable *drawable, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_sync_format (filter); + gimp_drawable_filter_update_drawable (filter, NULL); +} + +static void +gimp_drawable_filter_drawable_removed (GimpDrawable *drawable, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_remove_filter (filter); +} + +static void +gimp_drawable_filter_lock_alpha_changed (GimpLayer *layer, + GimpDrawableFilter *filter) +{ + gimp_drawable_filter_sync_affect (filter); + gimp_drawable_filter_update_drawable (filter, NULL); +} diff --git a/app/core/gimpdrawablefilter.h b/app/core/gimpdrawablefilter.h new file mode 100644 index 0000000..5aa8324 --- /dev/null +++ b/app/core/gimpdrawablefilter.h @@ -0,0 +1,108 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_FILTER_H__ +#define __GIMP_DRAWABLE_FILTER_H__ + + +#include "gimpfilter.h" + + +#define GIMP_TYPE_DRAWABLE_FILTER (gimp_drawable_filter_get_type ()) +#define GIMP_DRAWABLE_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_FILTER, GimpDrawableFilter)) +#define GIMP_DRAWABLE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_FILTER, GimpDrawableFilterClass)) +#define GIMP_IS_DRAWABLE_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_FILTER)) +#define GIMP_IS_DRAWABLE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_FILTER)) +#define GIMP_DRAWABLE_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_FILTER, GimpDrawableFilterClass)) + + +typedef struct _GimpDrawableFilterClass GimpDrawableFilterClass; + +struct _GimpDrawableFilterClass +{ + GimpFilterClass parent_class; + + void (* flush) (GimpDrawableFilter *filter); +}; + + +/* Drawable Filter functions */ + +/* Successive apply() functions can be called, but eventually MUST be + * followed with an commit() or an abort() call, both of which will + * remove the live filter from the drawable. + */ + +GType gimp_drawable_filter_get_type (void) G_GNUC_CONST; + +GimpDrawableFilter * + gimp_drawable_filter_new (GimpDrawable *drawable, + const gchar *undo_desc, + GeglNode *operation, + const gchar *icon_name); + +GimpDrawable * + gimp_drawable_filter_get_drawable (GimpDrawableFilter *filter); +GeglNode * gimp_drawable_filter_get_operation (GimpDrawableFilter *filter); + +void gimp_drawable_filter_set_clip (GimpDrawableFilter *filter, + gboolean clip); +void gimp_drawable_filter_set_region (GimpDrawableFilter *filter, + GimpFilterRegion region); +void gimp_drawable_filter_set_crop (GimpDrawableFilter *filter, + const GeglRectangle *rect, + gboolean update); +void gimp_drawable_filter_set_preview (GimpDrawableFilter *filter, + gboolean enabled); +void gimp_drawable_filter_set_preview_split + (GimpDrawableFilter *filter, + gboolean enabled, + GimpAlignmentType alignment, + gint split_position); +void gimp_drawable_filter_set_opacity (GimpDrawableFilter *filter, + gdouble opacity); +void gimp_drawable_filter_set_mode (GimpDrawableFilter *filter, + GimpLayerMode paint_mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode); +void gimp_drawable_filter_set_add_alpha (GimpDrawableFilter *filter, + gboolean add_alpha); + +void gimp_drawable_filter_set_color_managed + (GimpDrawableFilter *filter, + gboolean managed); +void gimp_drawable_filter_set_gamma_hack (GimpDrawableFilter *filter, + gboolean gamma_hack); + +void gimp_drawable_filter_set_override_constraints + (GimpDrawableFilter *filter, + gboolean override_constraints); + +const Babl * + gimp_drawable_filter_get_format (GimpDrawableFilter *filter); + +void gimp_drawable_filter_apply (GimpDrawableFilter *filter, + const GeglRectangle *area); + +gboolean gimp_drawable_filter_commit (GimpDrawableFilter *filter, + GimpProgress *progress, + gboolean cancellable); +void gimp_drawable_filter_abort (GimpDrawableFilter *filter); + + +#endif /* __GIMP_DRAWABLE_FILTER_H__ */ diff --git a/app/core/gimpdrawablemodundo.c b/app/core/gimpdrawablemodundo.c new file mode 100644 index 0000000..5c3e897 --- /dev/null +++ b/app/core/gimpdrawablemodundo.c @@ -0,0 +1,216 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "gimp-memsize.h" +#include "gimpimage.h" +#include "gimpdrawable.h" +#include "gimpdrawablemodundo.h" + + +enum +{ + PROP_0, + PROP_COPY_BUFFER +}; + + +static void gimp_drawable_mod_undo_constructed (GObject *object); +static void gimp_drawable_mod_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_drawable_mod_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_drawable_mod_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_drawable_mod_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_drawable_mod_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpDrawableModUndo, gimp_drawable_mod_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_drawable_mod_undo_parent_class + + +static void +gimp_drawable_mod_undo_class_init (GimpDrawableModUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_drawable_mod_undo_constructed; + object_class->set_property = gimp_drawable_mod_undo_set_property; + object_class->get_property = gimp_drawable_mod_undo_get_property; + + gimp_object_class->get_memsize = gimp_drawable_mod_undo_get_memsize; + + undo_class->pop = gimp_drawable_mod_undo_pop; + undo_class->free = gimp_drawable_mod_undo_free; + + g_object_class_install_property (object_class, PROP_COPY_BUFFER, + g_param_spec_boolean ("copy-buffer", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_drawable_mod_undo_init (GimpDrawableModUndo *undo) +{ +} + +static void +gimp_drawable_mod_undo_constructed (GObject *object) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (object); + GimpItem *item; + GimpDrawable *drawable; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_DRAWABLE (GIMP_ITEM_UNDO (object)->item)); + + item = GIMP_ITEM_UNDO (object)->item; + drawable = GIMP_DRAWABLE (item); + + if (drawable_mod_undo->copy_buffer) + { + drawable_mod_undo->buffer = + gimp_gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); + } + else + { + drawable_mod_undo->buffer = + g_object_ref (gimp_drawable_get_buffer (drawable)); + } + + gimp_item_get_offset (item, + &drawable_mod_undo->offset_x, + &drawable_mod_undo->offset_y); +} + +static void +gimp_drawable_mod_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (object); + + switch (property_id) + { + case PROP_COPY_BUFFER: + drawable_mod_undo->copy_buffer = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_drawable_mod_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (object); + + switch (property_id) + { + case PROP_COPY_BUFFER: + g_value_set_boolean (value, drawable_mod_undo->copy_buffer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_drawable_mod_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (drawable_mod_undo->buffer); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_drawable_mod_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (undo); + GimpDrawable *drawable = GIMP_DRAWABLE (GIMP_ITEM_UNDO (undo)->item); + GeglBuffer *buffer; + gint offset_x; + gint offset_y; + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + buffer = drawable_mod_undo->buffer; + offset_x = drawable_mod_undo->offset_x; + offset_y = drawable_mod_undo->offset_y; + + drawable_mod_undo->buffer = g_object_ref (gimp_drawable_get_buffer (drawable)); + + gimp_item_get_offset (GIMP_ITEM (drawable), + &drawable_mod_undo->offset_x, + &drawable_mod_undo->offset_y); + + gimp_drawable_set_buffer_full (drawable, FALSE, NULL, + buffer, + GEGL_RECTANGLE (offset_x, offset_y, 0, 0), + TRUE); + g_object_unref (buffer); +} + +static void +gimp_drawable_mod_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpDrawableModUndo *drawable_mod_undo = GIMP_DRAWABLE_MOD_UNDO (undo); + + g_clear_object (&drawable_mod_undo->buffer); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpdrawablemodundo.h b/app/core/gimpdrawablemodundo.h new file mode 100644 index 0000000..30bf5a0 --- /dev/null +++ b/app/core/gimpdrawablemodundo.h @@ -0,0 +1,55 @@ +/* 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 . + */ + +#ifndef __GIMP_DRAWABLE_MOD_UNDO_H__ +#define __GIMP_DRAWABLE_MOD_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_DRAWABLE_MOD_UNDO (gimp_drawable_mod_undo_get_type ()) +#define GIMP_DRAWABLE_MOD_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_MOD_UNDO, GimpDrawableModUndo)) +#define GIMP_DRAWABLE_MOD_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_MOD_UNDO, GimpDrawableModUndoClass)) +#define GIMP_IS_DRAWABLE_MOD_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_MOD_UNDO)) +#define GIMP_IS_DRAWABLE_MOD_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_MOD_UNDO)) +#define GIMP_DRAWABLE_MOD_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_MOD_UNDO, GimpDrawableModUndoClass)) + + +typedef struct _GimpDrawableModUndo GimpDrawableModUndo; +typedef struct _GimpDrawableModUndoClass GimpDrawableModUndoClass; + +struct _GimpDrawableModUndo +{ + GimpItemUndo parent_instance; + + GeglBuffer *buffer; + gboolean copy_buffer; + gint offset_x; + gint offset_y; +}; + +struct _GimpDrawableModUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_drawable_mod_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_DRAWABLE_MOD_UNDO_H__ */ diff --git a/app/core/gimpdrawablestack.c b/app/core/gimpdrawablestack.c new file mode 100644 index 0000000..c8e706a --- /dev/null +++ b/app/core/gimpdrawablestack.c @@ -0,0 +1,224 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpdrawablestack.c + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpdrawable.h" +#include "gimpdrawablestack.h" +#include "gimpmarshal.h" + + +enum +{ + UPDATE, + LAST_SIGNAL +}; + + +/* local function prototypes */ + +static void gimp_drawable_stack_constructed (GObject *object); + +static void gimp_drawable_stack_add (GimpContainer *container, + GimpObject *object); +static void gimp_drawable_stack_remove (GimpContainer *container, + GimpObject *object); +static void gimp_drawable_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); + +static void gimp_drawable_stack_drawable_update (GimpItem *item, + gint x, + gint y, + gint width, + gint height, + GimpDrawableStack *stack); +static void gimp_drawable_stack_drawable_active (GimpItem *item, + GimpDrawableStack *stack); + + +G_DEFINE_TYPE (GimpDrawableStack, gimp_drawable_stack, GIMP_TYPE_ITEM_STACK) + +#define parent_class gimp_drawable_stack_parent_class + +static guint stack_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_drawable_stack_class_init (GimpDrawableStackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + stack_signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableStackClass, update), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + object_class->constructed = gimp_drawable_stack_constructed; + + container_class->add = gimp_drawable_stack_add; + container_class->remove = gimp_drawable_stack_remove; + container_class->reorder = gimp_drawable_stack_reorder; +} + +static void +gimp_drawable_stack_init (GimpDrawableStack *stack) +{ +} + +static void +gimp_drawable_stack_constructed (GObject *object) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (g_type_is_a (gimp_container_get_children_type (container), + GIMP_TYPE_DRAWABLE)); + + gimp_container_add_handler (container, "update", + G_CALLBACK (gimp_drawable_stack_drawable_update), + container); + gimp_container_add_handler (container, "active-changed", + G_CALLBACK (gimp_drawable_stack_drawable_active), + container); +} + +static void +gimp_drawable_stack_add (GimpContainer *container, + GimpObject *object) +{ + GimpDrawableStack *stack = GIMP_DRAWABLE_STACK (container); + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); + + if (gimp_filter_get_active (GIMP_FILTER (object))) + gimp_drawable_stack_drawable_active (GIMP_ITEM (object), stack); +} + +static void +gimp_drawable_stack_remove (GimpContainer *container, + GimpObject *object) +{ + GimpDrawableStack *stack = GIMP_DRAWABLE_STACK (container); + + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); + + if (gimp_filter_get_active (GIMP_FILTER (object))) + gimp_drawable_stack_drawable_active (GIMP_ITEM (object), stack); +} + +static void +gimp_drawable_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + GimpDrawableStack *stack = GIMP_DRAWABLE_STACK (container); + + GIMP_CONTAINER_CLASS (parent_class)->reorder (container, object, new_index); + + if (gimp_filter_get_active (GIMP_FILTER (object))) + gimp_drawable_stack_drawable_active (GIMP_ITEM (object), stack); +} + + +/* public functions */ + +GimpContainer * +gimp_drawable_stack_new (GType drawable_type) +{ + g_return_val_if_fail (g_type_is_a (drawable_type, GIMP_TYPE_DRAWABLE), NULL); + + return g_object_new (GIMP_TYPE_DRAWABLE_STACK, + "name", g_type_name (drawable_type), + "children-type", drawable_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); +} + + +/* protected functions */ + +void +gimp_drawable_stack_update (GimpDrawableStack *stack, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_STACK (stack)); + + g_signal_emit (stack, stack_signals[UPDATE], 0, + x, y, width, height); +} + + +/* private functions */ + +static void +gimp_drawable_stack_drawable_update (GimpItem *item, + gint x, + gint y, + gint width, + gint height, + GimpDrawableStack *stack) +{ + if (gimp_filter_get_active (GIMP_FILTER (item))) + { + gint offset_x; + gint offset_y; + + gimp_item_get_offset (item, &offset_x, &offset_y); + + gimp_drawable_stack_update (stack, + x + offset_x, y + offset_y, + width, height); + } +} + +static void +gimp_drawable_stack_drawable_active (GimpItem *item, + GimpDrawableStack *stack) +{ + GeglRectangle bounding_box; + + bounding_box = gimp_drawable_get_bounding_box (GIMP_DRAWABLE (item)); + + bounding_box.x += gimp_item_get_offset_x (item); + bounding_box.y += gimp_item_get_offset_y (item); + + gimp_drawable_stack_update (stack, + bounding_box.x, bounding_box.y, + bounding_box.width, bounding_box.height); +} diff --git a/app/core/gimpdrawablestack.h b/app/core/gimpdrawablestack.h new file mode 100644 index 0000000..28ee7c4 --- /dev/null +++ b/app/core/gimpdrawablestack.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpdrawablestack.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DRAWABLE_STACK_H__ +#define __GIMP_DRAWABLE_STACK_H__ + +#include "gimpitemstack.h" + + +#define GIMP_TYPE_DRAWABLE_STACK (gimp_drawable_stack_get_type ()) +#define GIMP_DRAWABLE_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_STACK, GimpDrawableStack)) +#define GIMP_DRAWABLE_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_STACK, GimpDrawableStackClass)) +#define GIMP_IS_DRAWABLE_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_STACK)) +#define GIMP_IS_DRAWABLE_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_STACK)) + + +typedef struct _GimpDrawableStackClass GimpDrawableStackClass; + +struct _GimpDrawableStack +{ + GimpItemStack parent_instance; +}; + +struct _GimpDrawableStackClass +{ + GimpItemStackClass parent_class; + + void (* update) (GimpDrawableStack *stack, + gint x, + gint y, + gint width, + gint height); +}; + + +GType gimp_drawable_stack_get_type (void) G_GNUC_CONST; +GimpContainer * gimp_drawable_stack_new (GType drawable_type); + + +/* protected */ + +void gimp_drawable_stack_update (GimpDrawableStack *stack, + gint x, + gint y, + gint width, + gint height); + + +#endif /* __GIMP_DRAWABLE_STACK_H__ */ diff --git a/app/core/gimpdrawableundo.c b/app/core/gimpdrawableundo.c new file mode 100644 index 0000000..78b8f39 --- /dev/null +++ b/app/core/gimpdrawableundo.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpimage.h" +#include "gimpdrawable.h" +#include "gimpdrawableundo.h" + + +enum +{ + PROP_0, + PROP_BUFFER, + PROP_X, + PROP_Y +}; + + +static void gimp_drawable_undo_constructed (GObject *object); +static void gimp_drawable_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_drawable_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_drawable_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_drawable_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_drawable_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpDrawableUndo, gimp_drawable_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_drawable_undo_parent_class + + +static void +gimp_drawable_undo_class_init (GimpDrawableUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_drawable_undo_constructed; + object_class->set_property = gimp_drawable_undo_set_property; + object_class->get_property = gimp_drawable_undo_get_property; + + gimp_object_class->get_memsize = gimp_drawable_undo_get_memsize; + + undo_class->pop = gimp_drawable_undo_pop; + undo_class->free = gimp_drawable_undo_free; + + g_object_class_install_property (object_class, PROP_BUFFER, + g_param_spec_object ("buffer", NULL, NULL, + GEGL_TYPE_BUFFER, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_int ("x", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_int ("y", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_drawable_undo_init (GimpDrawableUndo *undo) +{ +} + +static void +gimp_drawable_undo_constructed (GObject *object) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_DRAWABLE (GIMP_ITEM_UNDO (object)->item)); + gimp_assert (GEGL_IS_BUFFER (drawable_undo->buffer)); +} + +static void +gimp_drawable_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (object); + + switch (property_id) + { + case PROP_BUFFER: + drawable_undo->buffer = g_value_dup_object (value); + break; + case PROP_X: + drawable_undo->x = g_value_get_int (value); + break; + case PROP_Y: + drawable_undo->y = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_drawable_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (object); + + switch (property_id) + { + case PROP_BUFFER: + g_value_set_object (value, drawable_undo->buffer); + break; + case PROP_X: + g_value_set_int (value, drawable_undo->x); + break; + case PROP_Y: + g_value_set_int (value, drawable_undo->y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_drawable_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (drawable_undo->buffer); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_drawable_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (undo); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + gimp_drawable_swap_pixels (GIMP_DRAWABLE (GIMP_ITEM_UNDO (undo)->item), + drawable_undo->buffer, + drawable_undo->x, + drawable_undo->y); +} + +static void +gimp_drawable_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (undo); + + g_clear_object (&drawable_undo->buffer); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpdrawableundo.h b/app/core/gimpdrawableundo.h new file mode 100644 index 0000000..5d7269e --- /dev/null +++ b/app/core/gimpdrawableundo.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 . + */ + +#ifndef __GIMP_DRAWABLE_UNDO_H__ +#define __GIMP_DRAWABLE_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_DRAWABLE_UNDO (gimp_drawable_undo_get_type ()) +#define GIMP_DRAWABLE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_UNDO, GimpDrawableUndo)) +#define GIMP_DRAWABLE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_UNDO, GimpDrawableUndoClass)) +#define GIMP_IS_DRAWABLE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_UNDO)) +#define GIMP_IS_DRAWABLE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_UNDO)) +#define GIMP_DRAWABLE_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_UNDO, GimpDrawableUndoClass)) + + +typedef struct _GimpDrawableUndo GimpDrawableUndo; +typedef struct _GimpDrawableUndoClass GimpDrawableUndoClass; + +struct _GimpDrawableUndo +{ + GimpItemUndo parent_instance; + + GeglBuffer *buffer; + gint x; + gint y; +}; + +struct _GimpDrawableUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_drawable_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_DRAWABLE_UNDO_H__ */ diff --git a/app/core/gimpdynamics-load.c b/app/core/gimpdynamics-load.c new file mode 100644 index 0000000..36c9f72 --- /dev/null +++ b/app/core/gimpdynamics-load.c @@ -0,0 +1,55 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpdynamics.h" +#include "gimpdynamics-load.h" + + +GList * +gimp_dynamics_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpDynamics *dynamics; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + dynamics = g_object_new (GIMP_TYPE_DYNAMICS, NULL); + + if (gimp_config_deserialize_stream (GIMP_CONFIG (dynamics), + input, + NULL, error)) + { + return g_list_prepend (NULL, dynamics); + } + + g_object_unref (dynamics); + + return NULL; +} diff --git a/app/core/gimpdynamics-load.h b/app/core/gimpdynamics-load.h new file mode 100644 index 0000000..374c6ed --- /dev/null +++ b/app/core/gimpdynamics-load.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GIMP_DYNAMICS_LOAD_H__ +#define __GIMP_DYNAMICS_LOAD_H__ + + +#define GIMP_DYNAMICS_FILE_EXTENSION ".gdyn" + + +GList * gimp_dynamics_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_DYNAMICS_LOAD_H__ */ diff --git a/app/core/gimpdynamics-save.c b/app/core/gimpdynamics-save.c new file mode 100644 index 0000000..d8bdf3d --- /dev/null +++ b/app/core/gimpdynamics-save.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpdynamics.h" +#include "gimpdynamics-save.h" + + +gboolean +gimp_dynamics_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_DYNAMICS (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return gimp_config_serialize_to_stream (GIMP_CONFIG (data), + output, + "GIMP dynamics file", + "end of GIMP dynamics file", + NULL, error); +} diff --git a/app/core/gimpdynamics-save.h b/app/core/gimpdynamics-save.h new file mode 100644 index 0000000..7fe2df3 --- /dev/null +++ b/app/core/gimpdynamics-save.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 . + */ + +#ifndef __GIMP_DYNAMICS_SAVE_H__ +#define __GIMP_DYNAMICS_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_dynamics_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_DYNAMICS_SAVE_H__ */ diff --git a/app/core/gimpdynamics.c b/app/core/gimpdynamics.c new file mode 100644 index 0000000..a1fb0ef --- /dev/null +++ b/app/core/gimpdynamics.c @@ -0,0 +1,653 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpcurve.h" +#include "gimpdynamics.h" +#include "gimpdynamics-load.h" +#include "gimpdynamics-save.h" +#include "gimpdynamicsoutput.h" + +#include "gimp-intl.h" + + +#define DEFAULT_NAME "Nameless dynamics" + +enum +{ + PROP_0, + + PROP_NAME, + + PROP_OPACITY_OUTPUT, + PROP_SIZE_OUTPUT, + PROP_ANGLE_OUTPUT, + PROP_COLOR_OUTPUT, + PROP_FORCE_OUTPUT, + PROP_HARDNESS_OUTPUT, + PROP_ASPECT_RATIO_OUTPUT, + PROP_SPACING_OUTPUT, + PROP_RATE_OUTPUT, + PROP_FLOW_OUTPUT, + PROP_JITTER_OUTPUT +}; + + +typedef struct _GimpDynamicsPrivate GimpDynamicsPrivate; + +struct _GimpDynamicsPrivate +{ + GimpDynamicsOutput *opacity_output; + GimpDynamicsOutput *hardness_output; + GimpDynamicsOutput *force_output; + GimpDynamicsOutput *rate_output; + GimpDynamicsOutput *flow_output; + GimpDynamicsOutput *size_output; + GimpDynamicsOutput *aspect_ratio_output; + GimpDynamicsOutput *color_output; + GimpDynamicsOutput *angle_output; + GimpDynamicsOutput *jitter_output; + GimpDynamicsOutput *spacing_output; +}; + +#define GET_PRIVATE(output) \ + ((GimpDynamicsPrivate *) gimp_dynamics_get_instance_private ((GimpDynamics *) (output))) + + +static void gimp_dynamics_finalize (GObject *object); +static void gimp_dynamics_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_dynamics_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void + gimp_dynamics_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs); + +static const gchar * gimp_dynamics_get_extension (GimpData *data); +static void gimp_dynamics_copy (GimpData *data, + GimpData *src_data); + +static GimpDynamicsOutput * + gimp_dynamics_create_output (GimpDynamics *dynamics, + const gchar *name, + GimpDynamicsOutputType type); +static void gimp_dynamics_output_notify (GObject *output, + const GParamSpec *pspec, + GimpDynamics *dynamics); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpDynamics, gimp_dynamics, GIMP_TYPE_DATA) + +#define parent_class gimp_dynamics_parent_class + + +static void +gimp_dynamics_class_init (GimpDynamicsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->finalize = gimp_dynamics_finalize; + object_class->set_property = gimp_dynamics_set_property; + object_class->get_property = gimp_dynamics_get_property; + object_class->dispatch_properties_changed = gimp_dynamics_dispatch_properties_changed; + + viewable_class->default_icon_name = "gimp-dynamics"; + + data_class->save = gimp_dynamics_save; + data_class->get_extension = gimp_dynamics_get_extension; + data_class->copy = gimp_dynamics_copy; + + GIMP_CONFIG_PROP_STRING (object_class, PROP_NAME, + "name", + NULL, NULL, + DEFAULT_NAME, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_OPACITY_OUTPUT, + "opacity-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_FORCE_OUTPUT, + "force-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_HARDNESS_OUTPUT, + "hardness-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_RATE_OUTPUT, + "rate-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_FLOW_OUTPUT, + "flow-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_SIZE_OUTPUT, + "size-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_ASPECT_RATIO_OUTPUT, + "aspect-ratio-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_COLOR_OUTPUT, + "color-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_ANGLE_OUTPUT, + "angle-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_JITTER_OUTPUT, + "jitter-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_SPACING_OUTPUT, + "spacing-output", + NULL, NULL, + GIMP_TYPE_DYNAMICS_OUTPUT, + GIMP_CONFIG_PARAM_AGGREGATE); +} + +static void +gimp_dynamics_init (GimpDynamics *dynamics) +{ + GimpDynamicsPrivate *private = GET_PRIVATE (dynamics); + + private->opacity_output = + gimp_dynamics_create_output (dynamics, + "opacity-output", + GIMP_DYNAMICS_OUTPUT_OPACITY); + + private->force_output = + gimp_dynamics_create_output (dynamics, + "force-output", + GIMP_DYNAMICS_OUTPUT_FORCE); + + private->hardness_output = + gimp_dynamics_create_output (dynamics, + "hardness-output", + GIMP_DYNAMICS_OUTPUT_HARDNESS); + + private->rate_output = + gimp_dynamics_create_output (dynamics, + "rate-output", + GIMP_DYNAMICS_OUTPUT_RATE); + + private->flow_output = + gimp_dynamics_create_output (dynamics, + "flow-output", + GIMP_DYNAMICS_OUTPUT_FLOW); + + private->size_output = + gimp_dynamics_create_output (dynamics, + "size-output", + GIMP_DYNAMICS_OUTPUT_SIZE); + + private->aspect_ratio_output = + gimp_dynamics_create_output (dynamics, + "aspect-ratio-output", + GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO); + + private->color_output = + gimp_dynamics_create_output (dynamics, + "color-output", + GIMP_DYNAMICS_OUTPUT_COLOR); + + private->angle_output = + gimp_dynamics_create_output (dynamics, + "angle-output", + GIMP_DYNAMICS_OUTPUT_ANGLE); + + private->jitter_output = + gimp_dynamics_create_output (dynamics, + "jitter-output", + GIMP_DYNAMICS_OUTPUT_JITTER); + + private->spacing_output = + gimp_dynamics_create_output (dynamics, + "spacing-output", + GIMP_DYNAMICS_OUTPUT_SPACING); +} + +static void +gimp_dynamics_finalize (GObject *object) +{ + GimpDynamicsPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->opacity_output); + g_clear_object (&private->force_output); + g_clear_object (&private->hardness_output); + g_clear_object (&private->rate_output); + g_clear_object (&private->flow_output); + g_clear_object (&private->size_output); + g_clear_object (&private->aspect_ratio_output); + g_clear_object (&private->color_output); + g_clear_object (&private->angle_output); + g_clear_object (&private->jitter_output); + g_clear_object (&private->spacing_output); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_dynamics_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDynamicsPrivate *private = GET_PRIVATE (object); + GimpDynamicsOutput *src_output = NULL; + GimpDynamicsOutput *dest_output = NULL; + + switch (property_id) + { + case PROP_NAME: + gimp_object_set_name (GIMP_OBJECT (object), g_value_get_string (value)); + break; + + case PROP_OPACITY_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->opacity_output; + break; + + case PROP_FORCE_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->force_output; + break; + + case PROP_HARDNESS_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->hardness_output; + break; + + case PROP_RATE_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->rate_output; + break; + + case PROP_FLOW_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->flow_output; + break; + + case PROP_SIZE_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->size_output; + break; + + case PROP_ASPECT_RATIO_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->aspect_ratio_output; + break; + + case PROP_COLOR_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->color_output; + break; + + case PROP_ANGLE_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->angle_output; + break; + + case PROP_JITTER_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->jitter_output; + break; + + case PROP_SPACING_OUTPUT: + src_output = g_value_get_object (value); + dest_output = private->spacing_output; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } + + if (src_output && dest_output) + { + gimp_config_copy (GIMP_CONFIG (src_output), + GIMP_CONFIG (dest_output), + GIMP_CONFIG_PARAM_SERIALIZE); + } +} + +static void +gimp_dynamics_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDynamicsPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_NAME: + g_value_set_string (value, gimp_object_get_name (object)); + break; + + case PROP_OPACITY_OUTPUT: + g_value_set_object (value, private->opacity_output); + break; + + case PROP_FORCE_OUTPUT: + g_value_set_object (value, private->force_output); + break; + + case PROP_HARDNESS_OUTPUT: + g_value_set_object (value, private->hardness_output); + break; + + case PROP_RATE_OUTPUT: + g_value_set_object (value, private->rate_output); + break; + + case PROP_FLOW_OUTPUT: + g_value_set_object (value, private->flow_output); + break; + + case PROP_SIZE_OUTPUT: + g_value_set_object (value, private->size_output); + break; + + case PROP_ASPECT_RATIO_OUTPUT: + g_value_set_object (value, private->aspect_ratio_output); + break; + + case PROP_COLOR_OUTPUT: + g_value_set_object (value, private->color_output); + break; + + case PROP_ANGLE_OUTPUT: + g_value_set_object (value, private->angle_output); + break; + + case PROP_JITTER_OUTPUT: + g_value_set_object (value, private->jitter_output); + break; + + case PROP_SPACING_OUTPUT: + g_value_set_object (value, private->spacing_output); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dynamics_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + gint i; + + G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, + n_pspecs, pspecs); + + for (i = 0; i < n_pspecs; i++) + { + if (pspecs[i]->flags & GIMP_CONFIG_PARAM_SERIALIZE) + { + gimp_data_dirty (GIMP_DATA (object)); + break; + } + } +} + +static const gchar * +gimp_dynamics_get_extension (GimpData *data) +{ + return GIMP_DYNAMICS_FILE_EXTENSION; +} + +static void +gimp_dynamics_copy (GimpData *data, + GimpData *src_data) +{ + gimp_data_freeze (data); + + gimp_config_copy (GIMP_CONFIG (src_data), + GIMP_CONFIG (data), 0); + + gimp_data_thaw (data); +} + + +/* public functions */ + +GimpData * +gimp_dynamics_new (GimpContext *context, + const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (name[0] != '\0', NULL); + + return g_object_new (GIMP_TYPE_DYNAMICS, + "name", name, + NULL); +} + +GimpData * +gimp_dynamics_get_standard (GimpContext *context) +{ + static GimpData *standard_dynamics = NULL; + + if (! standard_dynamics) + { + standard_dynamics = gimp_dynamics_new (context, "Standard dynamics"); + + gimp_data_clean (standard_dynamics); + gimp_data_make_internal (standard_dynamics, "gimp-dynamics-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_dynamics), + (gpointer *) &standard_dynamics); + } + + return standard_dynamics; +} + +GimpDynamicsOutput * +gimp_dynamics_get_output (GimpDynamics *dynamics, + GimpDynamicsOutputType type_id) +{ + GimpDynamicsPrivate *private; + + g_return_val_if_fail (GIMP_IS_DYNAMICS (dynamics), NULL); + + private = GET_PRIVATE (dynamics); + + switch (type_id) + { + case GIMP_DYNAMICS_OUTPUT_OPACITY: + return private->opacity_output; + break; + + case GIMP_DYNAMICS_OUTPUT_FORCE: + return private->force_output; + break; + + case GIMP_DYNAMICS_OUTPUT_HARDNESS: + return private->hardness_output; + break; + + case GIMP_DYNAMICS_OUTPUT_RATE: + return private->rate_output; + break; + + case GIMP_DYNAMICS_OUTPUT_FLOW: + return private->flow_output; + break; + + case GIMP_DYNAMICS_OUTPUT_SIZE: + return private->size_output; + break; + + case GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO: + return private->aspect_ratio_output; + break; + + case GIMP_DYNAMICS_OUTPUT_COLOR: + return private->color_output; + break; + + case GIMP_DYNAMICS_OUTPUT_ANGLE: + return private->angle_output; + break; + + case GIMP_DYNAMICS_OUTPUT_JITTER: + return private->jitter_output; + break; + + case GIMP_DYNAMICS_OUTPUT_SPACING: + return private->spacing_output; + break; + + default: + g_return_val_if_reached (NULL); + break; + } +} + +gboolean +gimp_dynamics_is_output_enabled (GimpDynamics *dynamics, + GimpDynamicsOutputType type) +{ + GimpDynamicsOutput *output; + + g_return_val_if_fail (GIMP_IS_DYNAMICS (dynamics), FALSE); + + output = gimp_dynamics_get_output (dynamics, type); + + return gimp_dynamics_output_is_enabled (output); +} + +gdouble +gimp_dynamics_get_linear_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutput *output; + + g_return_val_if_fail (GIMP_IS_DYNAMICS (dynamics), 0.0); + + output = gimp_dynamics_get_output (dynamics, type); + + return gimp_dynamics_output_get_linear_value (output, coords, + options, fade_point); +} + +gdouble +gimp_dynamics_get_angular_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutput *output; + + g_return_val_if_fail (GIMP_IS_DYNAMICS (dynamics), 0.0); + + output = gimp_dynamics_get_output (dynamics, type); + + return gimp_dynamics_output_get_angular_value (output, coords, + options, fade_point); +} + +gdouble +gimp_dynamics_get_aspect_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutput *output; + + g_return_val_if_fail (GIMP_IS_DYNAMICS (dynamics), 0.0); + + output = gimp_dynamics_get_output (dynamics, type); + + return gimp_dynamics_output_get_aspect_value (output, coords, + options, fade_point); +} + + +/* private functions */ + +static GimpDynamicsOutput * +gimp_dynamics_create_output (GimpDynamics *dynamics, + const gchar *name, + GimpDynamicsOutputType type) +{ + GimpDynamicsOutput *output = gimp_dynamics_output_new (name, type); + + g_signal_connect (output, "notify", + G_CALLBACK (gimp_dynamics_output_notify), + dynamics); + + return output; +} + +static void +gimp_dynamics_output_notify (GObject *output, + const GParamSpec *pspec, + GimpDynamics *dynamics) +{ + g_object_notify (G_OBJECT (dynamics), gimp_object_get_name (output)); +} diff --git a/app/core/gimpdynamics.h b/app/core/gimpdynamics.h new file mode 100644 index 0000000..e65fdf6 --- /dev/null +++ b/app/core/gimpdynamics.h @@ -0,0 +1,77 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_DYNAMICS_H__ +#define __GIMP_DYNAMICS_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_DYNAMICS (gimp_dynamics_get_type ()) +#define GIMP_DYNAMICS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DYNAMICS, GimpDynamics)) +#define GIMP_DYNAMICS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DYNAMICS, GimpDynamicsClass)) +#define GIMP_IS_DYNAMICS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DYNAMICS)) +#define GIMP_IS_DYNAMICS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DYNAMICS)) +#define GIMP_DYNAMICS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DYNAMICS, GimpDynamicsClass)) + + +typedef struct _GimpDynamicsClass GimpDynamicsClass; + +struct _GimpDynamics +{ + GimpData parent_instance; +}; + +struct _GimpDynamicsClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_dynamics_get_type (void) G_GNUC_CONST; + +GimpData * gimp_dynamics_new (GimpContext *context, + const gchar *name); +GimpData * gimp_dynamics_get_standard (GimpContext *context); + +GimpDynamicsOutput * gimp_dynamics_get_output (GimpDynamics *dynamics, + GimpDynamicsOutputType type); + +gboolean gimp_dynamics_is_output_enabled (GimpDynamics *dynamics, + GimpDynamicsOutputType type); + +gdouble gimp_dynamics_get_linear_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); + +gdouble gimp_dynamics_get_angular_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); + +gdouble gimp_dynamics_get_aspect_value (GimpDynamics *dynamics, + GimpDynamicsOutputType type, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); + + +#endif /* __GIMP_DYNAMICS_H__ */ diff --git a/app/core/gimpdynamicsoutput.c b/app/core/gimpdynamicsoutput.c new file mode 100644 index 0000000..7a4f309 --- /dev/null +++ b/app/core/gimpdynamicsoutput.c @@ -0,0 +1,767 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "paint/gimppaintoptions.h" + + +#include "gimpcurve.h" +#include "gimpcurve-map.h" + +#include "gimpdynamicsoutput.h" + +#include "gimp-intl.h" + + +#define DEFAULT_USE_PRESSURE FALSE +#define DEFAULT_USE_VELOCITY FALSE +#define DEFAULT_USE_DIRECTION FALSE +#define DEFAULT_USE_TILT FALSE +#define DEFAULT_USE_WHEEL FALSE +#define DEFAULT_USE_RANDOM FALSE +#define DEFAULT_USE_FADE FALSE + + +enum +{ + PROP_0, + + PROP_TYPE, + PROP_USE_PRESSURE, + PROP_USE_VELOCITY, + PROP_USE_DIRECTION, + PROP_USE_TILT, + PROP_USE_WHEEL, + PROP_USE_RANDOM, + PROP_USE_FADE, + PROP_PRESSURE_CURVE, + PROP_VELOCITY_CURVE, + PROP_DIRECTION_CURVE, + PROP_TILT_CURVE, + PROP_WHEEL_CURVE, + PROP_RANDOM_CURVE, + PROP_FADE_CURVE +}; + + +typedef struct _GimpDynamicsOutputPrivate GimpDynamicsOutputPrivate; + +struct _GimpDynamicsOutputPrivate +{ + GimpDynamicsOutputType type; + + gboolean use_pressure; + gboolean use_velocity; + gboolean use_direction; + gboolean use_tilt; + gboolean use_wheel; + gboolean use_random; + gboolean use_fade; + + GimpCurve *pressure_curve; + GimpCurve *velocity_curve; + GimpCurve *direction_curve; + GimpCurve *tilt_curve; + GimpCurve *wheel_curve; + GimpCurve *random_curve; + GimpCurve *fade_curve; +}; + +#define GET_PRIVATE(output) \ + ((GimpDynamicsOutputPrivate *) gimp_dynamics_output_get_instance_private ((GimpDynamicsOutput *) (output))) + + +static void gimp_dynamics_output_finalize (GObject *object); +static void gimp_dynamics_output_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_dynamics_output_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_dynamics_output_copy_curve (GimpCurve *src, + GimpCurve *dest); + +static GimpCurve * + gimp_dynamics_output_create_curve (GimpDynamicsOutput *output, + const gchar *name); +static void gimp_dynamics_output_curve_dirty (GimpCurve *curve, + GimpDynamicsOutput *output); + + +G_DEFINE_TYPE_WITH_CODE (GimpDynamicsOutput, gimp_dynamics_output, + GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpDynamicsOutput) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +#define parent_class gimp_dynamics_output_parent_class + + +static void +gimp_dynamics_output_class_init (GimpDynamicsOutputClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_dynamics_output_finalize; + object_class->set_property = gimp_dynamics_output_set_property; + object_class->get_property = gimp_dynamics_output_get_property; + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, + _("Output type"), + GIMP_TYPE_DYNAMICS_OUTPUT_TYPE, + GIMP_DYNAMICS_OUTPUT_OPACITY, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_PRESSURE, + "use-pressure", + NULL, NULL, + DEFAULT_USE_PRESSURE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_VELOCITY, + "use-velocity", + NULL, NULL, + DEFAULT_USE_VELOCITY, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_DIRECTION, + "use-direction", + NULL, NULL, + DEFAULT_USE_DIRECTION, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_TILT, + "use-tilt", + NULL, NULL, + DEFAULT_USE_TILT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_WHEEL, + "use-wheel", + NULL, NULL, + DEFAULT_USE_TILT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_RANDOM, + "use-random", + NULL, NULL, + DEFAULT_USE_RANDOM, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_FADE, + "use-fade", + NULL, NULL, + DEFAULT_USE_FADE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_PRESSURE_CURVE, + "pressure-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_VELOCITY_CURVE, + "velocity-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_DIRECTION_CURVE, + "direction-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_TILT_CURVE, + "tilt-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_WHEEL_CURVE, + "wheel-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_RANDOM_CURVE, + "random-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_FADE_CURVE, + "fade-curve", + NULL, NULL, + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); +} + +static void +gimp_dynamics_output_init (GimpDynamicsOutput *output) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (output); + + private->pressure_curve = gimp_dynamics_output_create_curve (output, + "pressure-curve"); + private->velocity_curve = gimp_dynamics_output_create_curve (output, + "velocity-curve"); + private->direction_curve = gimp_dynamics_output_create_curve (output, + "direction-curve"); + private->tilt_curve = gimp_dynamics_output_create_curve (output, + "tilt-curve"); + private->wheel_curve = gimp_dynamics_output_create_curve (output, + "wheel-curve"); + private->random_curve = gimp_dynamics_output_create_curve (output, + "random-curve"); + private->fade_curve = gimp_dynamics_output_create_curve (output, + "fade-curve"); +} + +static void +gimp_dynamics_output_finalize (GObject *object) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->pressure_curve); + g_clear_object (&private->velocity_curve); + g_clear_object (&private->direction_curve); + g_clear_object (&private->tilt_curve); + g_clear_object (&private->wheel_curve); + g_clear_object (&private->random_curve); + g_clear_object (&private->fade_curve); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_dynamics_output_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + private->type = g_value_get_enum (value); + break; + + case PROP_USE_PRESSURE: + private->use_pressure = g_value_get_boolean (value); + break; + + case PROP_USE_VELOCITY: + private->use_velocity = g_value_get_boolean (value); + break; + + case PROP_USE_DIRECTION: + private->use_direction = g_value_get_boolean (value); + break; + + case PROP_USE_TILT: + private->use_tilt = g_value_get_boolean (value); + break; + + case PROP_USE_WHEEL: + private->use_wheel = g_value_get_boolean (value); + break; + + case PROP_USE_RANDOM: + private->use_random = g_value_get_boolean (value); + break; + + case PROP_USE_FADE: + private->use_fade = g_value_get_boolean (value); + break; + + case PROP_PRESSURE_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->pressure_curve); + break; + + case PROP_VELOCITY_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->velocity_curve); + break; + + case PROP_DIRECTION_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->direction_curve); + break; + + case PROP_TILT_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->tilt_curve); + break; + + case PROP_WHEEL_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->wheel_curve); + break; + + case PROP_RANDOM_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->random_curve); + break; + + case PROP_FADE_CURVE: + gimp_dynamics_output_copy_curve (g_value_get_object (value), + private->fade_curve); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dynamics_output_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, private->type); + break; + + case PROP_USE_PRESSURE: + g_value_set_boolean (value, private->use_pressure); + break; + + case PROP_USE_VELOCITY: + g_value_set_boolean (value, private->use_velocity); + break; + + case PROP_USE_DIRECTION: + g_value_set_boolean (value, private->use_direction); + break; + + case PROP_USE_TILT: + g_value_set_boolean (value, private->use_tilt); + break; + + case PROP_USE_WHEEL: + g_value_set_boolean (value, private->use_wheel); + break; + + case PROP_USE_RANDOM: + g_value_set_boolean (value, private->use_random); + break; + + case PROP_USE_FADE: + g_value_set_boolean (value, private->use_fade); + break; + + case PROP_PRESSURE_CURVE: + g_value_set_object (value, private->pressure_curve); + break; + + case PROP_VELOCITY_CURVE: + g_value_set_object (value, private->velocity_curve); + break; + + case PROP_DIRECTION_CURVE: + g_value_set_object (value, private->direction_curve); + break; + + case PROP_TILT_CURVE: + g_value_set_object (value, private->tilt_curve); + break; + + case PROP_WHEEL_CURVE: + g_value_set_object (value, private->wheel_curve); + break; + + case PROP_RANDOM_CURVE: + g_value_set_object (value, private->random_curve); + break; + + case PROP_FADE_CURVE: + g_value_set_object (value, private->fade_curve); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +GimpDynamicsOutput * +gimp_dynamics_output_new (const gchar *name, + GimpDynamicsOutputType type) +{ + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (GIMP_TYPE_DYNAMICS_OUTPUT, + "name", name, + "type", type, + NULL); +} + +gboolean +gimp_dynamics_output_is_enabled (GimpDynamicsOutput *output) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (output); + + return (private->use_pressure || + private->use_velocity || + private->use_direction || + private->use_tilt || + private->use_wheel || + private->use_random || + private->use_fade); +} + +gdouble +gimp_dynamics_output_get_linear_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (output); + gdouble total = 0.0; + gdouble result = 1.0; + gint factors = 0; + + if (private->use_pressure) + { + total += gimp_curve_map_value (private->pressure_curve, + coords->pressure); + factors++; + } + + if (private->use_velocity) + { + total += gimp_curve_map_value (private->velocity_curve, + (1.0 - coords->velocity)); + factors++; + } + + if (private->use_direction) + { + total += gimp_curve_map_value (private->direction_curve, + fmod (coords->direction + 0.5, 1)); + factors++; + } + + if (private->use_tilt) + { + total += gimp_curve_map_value (private->tilt_curve, + (1.0 - sqrt (SQR (coords->xtilt) + + SQR (coords->ytilt)))); + factors++; + } + + if (private->use_wheel) + { + gdouble wheel; + + wheel = coords->wheel; + + total += gimp_curve_map_value (private->wheel_curve, wheel); + factors++; + } + + if (private->use_random) + { + total += gimp_curve_map_value (private->random_curve, + g_random_double_range (0.0, 1.0)); + factors++; + } + + if (private->use_fade) + { + total += gimp_curve_map_value (private->fade_curve, fade_point); + + factors++; + } + + if (factors > 0) + result = total / factors; + +#if 0 + g_printerr ("Dynamics queried(linear). Result: %f, factors: %d, total: %f\n", + result, factors, total); +#endif + + return result; +} + +gdouble +gimp_dynamics_output_get_angular_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (output); + gdouble total = 0.0; + gdouble result = 0.0; /* angles are additive, so we return zero for no change. */ + gint factors = 0; + + if (private->use_pressure) + { + total += gimp_curve_map_value (private->pressure_curve, + coords->pressure); + factors++; + } + + if (private->use_velocity) + { + total += gimp_curve_map_value (private->velocity_curve, + (1.0 - coords->velocity)); + factors++; + } + + if (private->use_direction) + { + gdouble angle = gimp_curve_map_value (private->direction_curve, + coords->direction); + + if (options->brush_lock_to_view) + { + if (coords->reflect) + angle = 0.5 - angle; + + angle -= coords->angle; + angle = fmod (fmod (angle, 1.0) + 1.0, 1.0); + } + + total += angle; + factors++; + } + + /* For tilt to make sense, it needs to be converted to an angle, not + * just a vector + */ + if (private->use_tilt) + { + gdouble tilt_x = coords->xtilt; + gdouble tilt_y = coords->ytilt; + gdouble tilt = 0.0; + + if (tilt_x == 0.0) + { + if (tilt_y > 0.0) + tilt = 0.25; + else if (tilt_y < 0.0) + tilt = 0.75; + else + tilt = 0.0; + } + else + { + tilt = atan ((- 1.0 * tilt_y) / + tilt_x) / (2 * G_PI); + + if (tilt_x > 0.0) + tilt = tilt + 0.5; + } + + tilt = tilt + 0.5; /* correct the angle, its wrong by 180 degrees */ + + while (tilt > 1.0) + tilt -= 1.0; + + while (tilt < 0.0) + tilt += 1.0; + + total += gimp_curve_map_value (private->tilt_curve, tilt); + factors++; + } + + if (private->use_wheel) + { + gdouble angle = 1.0 - fmod(0.5 + coords->wheel, 1); + + total += gimp_curve_map_value (private->wheel_curve, angle); + factors++; + } + + if (private->use_random) + { + total += gimp_curve_map_value (private->random_curve, + g_random_double_range (0.0, 1.0)); + factors++; + } + + if (private->use_fade) + { + total += gimp_curve_map_value (private->fade_curve, fade_point); + + factors++; + } + + if (factors > 0) + result = total / factors; + +#if 0 + g_printerr ("Dynamics queried(angle). Result: %f, factors: %d, total: %f\n", + result, factors, total); +#endif + + return result; +} + +gdouble +gimp_dynamics_output_get_aspect_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point) +{ + GimpDynamicsOutputPrivate *private = GET_PRIVATE (output); + gdouble total = 0.0; + gint factors = 0; + gdouble sign = 1.0; + gdouble result = 1.0; + + if (private->use_pressure) + { + total += gimp_curve_map_value (private->pressure_curve, + coords->pressure); + factors++; + } + + if (private->use_velocity) + { + total += gimp_curve_map_value (private->velocity_curve, + coords->velocity); + factors++; + } + + if (private->use_direction) + { + gdouble direction = gimp_curve_map_value (private->direction_curve, + coords->direction); + + if (((direction > 0.875) && (direction <= 1.0)) || + ((direction > 0.0) && (direction < 0.125)) || + ((direction > 0.375) && (direction < 0.625))) + sign = -1.0; + + total += 1.0; + factors++; + } + + if (private->use_tilt) + { + gdouble tilt_value = MAX (fabs (coords->xtilt), fabs (coords->ytilt)); + + tilt_value = gimp_curve_map_value (private->tilt_curve, + tilt_value); + + total += tilt_value; + + factors++; + } + + if (private->use_wheel) + { + gdouble wheel = gimp_curve_map_value (private->wheel_curve, + coords->wheel); + + if (((wheel > 0.875) && (wheel <= 1.0)) || + ((wheel > 0.0) && (wheel < 0.125)) || + ((wheel > 0.375) && (wheel < 0.625))) + sign = -1.0; + + total += 1.0; + factors++; + + } + + if (private->use_random) + { + gdouble random = gimp_curve_map_value (private->random_curve, + g_random_double_range (0.0, 1.0)); + + total += random; + factors++; + } + + if (private->use_fade) + { + total += gimp_curve_map_value (private->fade_curve, fade_point); + + factors++; + } + + if (factors > 0) + result = total / factors; + + +#if 0 + g_printerr ("Dynamics queried(aspect). Result: %f, factors: %d, total: %f sign: %f\n", + result, factors, total, sign); +#endif + result = CLAMP (result * sign, -1.0, 1.0); + + return result; +} + +static void +gimp_dynamics_output_copy_curve (GimpCurve *src, + GimpCurve *dest) +{ + if (src && dest) + { + gimp_config_copy (GIMP_CONFIG (src), + GIMP_CONFIG (dest), + GIMP_CONFIG_PARAM_SERIALIZE); + } +} + +static GimpCurve * +gimp_dynamics_output_create_curve (GimpDynamicsOutput *output, + const gchar *name) +{ + GimpCurve *curve = GIMP_CURVE (gimp_curve_new (name)); + + g_signal_connect_object (curve, "dirty", + G_CALLBACK (gimp_dynamics_output_curve_dirty), + output, 0); + + return curve; +} + +static void +gimp_dynamics_output_curve_dirty (GimpCurve *curve, + GimpDynamicsOutput *output) +{ + g_object_notify (G_OBJECT (output), gimp_object_get_name (curve)); +} diff --git a/app/core/gimpdynamicsoutput.h b/app/core/gimpdynamicsoutput.h new file mode 100644 index 0000000..855cc4a --- /dev/null +++ b/app/core/gimpdynamicsoutput.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_DYNAMICS_OUTPUT_H__ +#define __GIMP_DYNAMICS_OUTPUT_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_DYNAMICS_OUTPUT (gimp_dynamics_output_get_type ()) +#define GIMP_DYNAMICS_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DYNAMICS_OUTPUT, GimpDynamicsOutput)) +#define GIMP_DYNAMICS_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DYNAMICS_OUTPUT, GimpDynamicsOutputClass)) +#define GIMP_IS_DYNAMICS_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DYNAMICS_OUTPUT)) +#define GIMP_IS_DYNAMICS_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DYNAMICS_OUTPUT)) +#define GIMP_DYNAMICS_OUTPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DYNAMICS_OUTPUT, GimpDynamicsOutputClass)) + + +typedef struct _GimpDynamicsOutputClass GimpDynamicsOutputClass; + +struct _GimpDynamicsOutput +{ + GimpObject parent_instance; +}; + +struct _GimpDynamicsOutputClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_dynamics_output_get_type (void) G_GNUC_CONST; + +GimpDynamicsOutput * gimp_dynamics_output_new (const gchar *name, + GimpDynamicsOutputType type); + +gboolean gimp_dynamics_output_is_enabled (GimpDynamicsOutput *output); + +gdouble gimp_dynamics_output_get_linear_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); + +gdouble gimp_dynamics_output_get_angular_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); +gdouble gimp_dynamics_output_get_aspect_value (GimpDynamicsOutput *output, + const GimpCoords *coords, + GimpPaintOptions *options, + gdouble fade_point); + + +#endif /* __GIMP_DYNAMICS_OUTPUT_H__ */ diff --git a/app/core/gimperror.c b/app/core/gimperror.c new file mode 100644 index 0000000..29394cb --- /dev/null +++ b/app/core/gimperror.c @@ -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 . + */ + +#include "config.h" + +#include + +#include "gimperror.h" + + +/** + * gimp_error_quark: + * + * This function is never called directly. Use GIMP_ERROR() instead. + * + * Return value: the #GQuark that defines the general GIMP error domain. + **/ +GQuark +gimp_error_quark (void) +{ + return g_quark_from_static_string ("gimp-error-quark"); +} diff --git a/app/core/gimperror.h b/app/core/gimperror.h new file mode 100644 index 0000000..f8088ec --- /dev/null +++ b/app/core/gimperror.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 . + */ + +#ifndef __GIMP_ERROR_H__ +#define __GIMP_ERROR_H__ + + +typedef enum +{ + GIMP_FAILED, /* generic error condition */ +} GimpErrorCode; + + +#define GIMP_ERROR (gimp_error_quark ()) + +GQuark gimp_error_quark (void) G_GNUC_CONST; + + +#endif /* __GIMP_ERROR_H__ */ diff --git a/app/core/gimpfilloptions.c b/app/core/gimpfilloptions.c new file mode 100644 index 0000000..d10b6e4 --- /dev/null +++ b/app/core/gimpfilloptions.c @@ -0,0 +1,548 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpfilloptions.c + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gimp.h" +#include "gimp-palettes.h" +#include "gimpdrawable.h" +#include "gimpdrawable-fill.h" +#include "gimperror.h" +#include "gimpfilloptions.h" +#include "gimppattern.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_STYLE, + PROP_ANTIALIAS, + PROP_FEATHER, + PROP_FEATHER_RADIUS, + PROP_PATTERN_VIEW_TYPE, + PROP_PATTERN_VIEW_SIZE +}; + + +typedef struct _GimpFillOptionsPrivate GimpFillOptionsPrivate; + +struct _GimpFillOptionsPrivate +{ + GimpFillStyle style; + gboolean antialias; + gboolean feather; + gdouble feather_radius; + + GimpViewType pattern_view_type; + GimpViewSize pattern_view_size; + + const gchar *undo_desc; +}; + +#define GET_PRIVATE(options) \ + ((GimpFillOptionsPrivate *) gimp_fill_options_get_instance_private ((GimpFillOptions *) (options))) + + +static void gimp_fill_options_config_init (GimpConfigInterface *iface); + +static void gimp_fill_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_fill_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gboolean gimp_fill_options_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); + + +G_DEFINE_TYPE_WITH_CODE (GimpFillOptions, gimp_fill_options, GIMP_TYPE_CONTEXT, + G_ADD_PRIVATE (GimpFillOptions) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_fill_options_config_init)) + + +static void +gimp_fill_options_class_init (GimpFillOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_fill_options_set_property; + object_class->get_property = gimp_fill_options_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_STYLE, + "style", + _("Style"), + NULL, + GIMP_TYPE_FILL_STYLE, + GIMP_FILL_STYLE_SOLID, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS, + "antialias", + _("Antialiasing"), + NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FEATHER, + "feather", + _("Feather edges"), + _("Enable feathering of fill edges"), + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FEATHER_RADIUS, + "feather-radius", + _("Radius"), + _("Radius of feathering"), + 0.0, 100.0, 10.0, + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, PROP_PATTERN_VIEW_TYPE, + g_param_spec_enum ("pattern-view-type", + NULL, NULL, + GIMP_TYPE_VIEW_TYPE, + GIMP_VIEW_TYPE_GRID, + G_PARAM_CONSTRUCT | + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PATTERN_VIEW_SIZE, + g_param_spec_int ("pattern-view-size", + NULL, NULL, + GIMP_VIEW_SIZE_TINY, + GIMP_VIEWABLE_MAX_BUTTON_SIZE, + GIMP_VIEW_SIZE_SMALL, + G_PARAM_CONSTRUCT | + GIMP_PARAM_READWRITE)); +} + +static void +gimp_fill_options_config_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_fill_options_serialize; +} + +static void +gimp_fill_options_init (GimpFillOptions *options) +{ +} + +static void +gimp_fill_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpFillOptionsPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_STYLE: + private->style = g_value_get_enum (value); + private->undo_desc = NULL; + break; + case PROP_ANTIALIAS: + private->antialias = g_value_get_boolean (value); + break; + case PROP_FEATHER: + private->feather = g_value_get_boolean (value); + break; + case PROP_FEATHER_RADIUS: + private->feather_radius = g_value_get_double (value); + break; + + case PROP_PATTERN_VIEW_TYPE: + private->pattern_view_type = g_value_get_enum (value); + break; + case PROP_PATTERN_VIEW_SIZE: + private->pattern_view_size = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_fill_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpFillOptionsPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_STYLE: + g_value_set_enum (value, private->style); + break; + case PROP_ANTIALIAS: + g_value_set_boolean (value, private->antialias); + break; + case PROP_FEATHER: + g_value_set_boolean (value, private->feather); + break; + case PROP_FEATHER_RADIUS: + g_value_set_double (value, private->feather_radius); + break; + + case PROP_PATTERN_VIEW_TYPE: + g_value_set_enum (value, private->pattern_view_type); + break; + case PROP_PATTERN_VIEW_SIZE: + g_value_set_int (value, private->pattern_view_size); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_fill_options_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + return gimp_config_serialize_properties (config, writer); +} + + +/* public functions */ + +GimpFillOptions * +gimp_fill_options_new (Gimp *gimp, + GimpContext *context, + gboolean use_context_color) +{ + GimpFillOptions *options; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (use_context_color == FALSE || context != NULL, NULL); + + options = g_object_new (GIMP_TYPE_FILL_OPTIONS, + "gimp", gimp, + NULL); + + if (use_context_color) + { + gimp_context_define_properties (GIMP_CONTEXT (options), + GIMP_CONTEXT_PROP_MASK_FOREGROUND | + GIMP_CONTEXT_PROP_MASK_PATTERN, + FALSE); + + gimp_context_set_parent (GIMP_CONTEXT (options), context); + } + + return options; +} + +GimpFillStyle +gimp_fill_options_get_style (GimpFillOptions *options) +{ + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), GIMP_FILL_STYLE_SOLID); + + return GET_PRIVATE (options)->style; +} + +void +gimp_fill_options_set_style (GimpFillOptions *options, + GimpFillStyle style) +{ + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + + g_object_set (options, "style", style, NULL); +} + +gboolean +gimp_fill_options_get_antialias (GimpFillOptions *options) +{ + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), FALSE); + + return GET_PRIVATE (options)->antialias; +} + +void +gimp_fill_options_set_antialias (GimpFillOptions *options, + gboolean antialias) +{ + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + + g_object_set (options, "antialias", antialias, NULL); +} + +gboolean +gimp_fill_options_get_feather (GimpFillOptions *options, + gdouble *radius) +{ + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), FALSE); + + if (radius) + *radius = GET_PRIVATE (options)->feather_radius; + + return GET_PRIVATE (options)->feather; +} + +void +gimp_fill_options_set_feather (GimpFillOptions *options, + gboolean feather, + gdouble radius) +{ + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + + g_object_set (options, "feather", feather, NULL); + g_object_set (options, "feather-radius", radius, NULL); +} + +gboolean +gimp_fill_options_set_by_fill_type (GimpFillOptions *options, + GimpContext *context, + GimpFillType fill_type, + GError **error) +{ + GimpFillOptionsPrivate *private; + GimpRGB color; + const gchar *undo_desc; + + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), FALSE); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + private = GET_PRIVATE (options); + + private->undo_desc = NULL; + + switch (fill_type) + { + case GIMP_FILL_FOREGROUND: + gimp_context_get_foreground (context, &color); + undo_desc = C_("undo-type", "Fill with Foreground Color"); + break; + + case GIMP_FILL_BACKGROUND: + gimp_context_get_background (context, &color); + undo_desc = C_("undo-type", "Fill with Background Color"); + break; + + case GIMP_FILL_WHITE: + gimp_rgba_set (&color, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + undo_desc = C_("undo-type", "Fill with White"); + break; + + case GIMP_FILL_TRANSPARENT: + gimp_context_get_background (context, &color); + gimp_context_set_paint_mode (GIMP_CONTEXT (options), + GIMP_LAYER_MODE_ERASE); + undo_desc = C_("undo-type", "Fill with Transparency"); + break; + + case GIMP_FILL_PATTERN: + { + GimpPattern *pattern = gimp_context_get_pattern (context); + + if (! pattern) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No patterns available for this operation.")); + return FALSE; + } + + gimp_fill_options_set_style (options, GIMP_FILL_STYLE_PATTERN); + gimp_context_set_pattern (GIMP_CONTEXT (options), pattern); + private->undo_desc = C_("undo-type", "Fill with Pattern"); + + return TRUE; + } + break; + + default: + g_warning ("%s: invalid fill_type %d", G_STRFUNC, fill_type); + return FALSE; + } + + gimp_fill_options_set_style (options, GIMP_FILL_STYLE_SOLID); + gimp_context_set_foreground (GIMP_CONTEXT (options), &color); + private->undo_desc = undo_desc; + + return TRUE; +} + +gboolean +gimp_fill_options_set_by_fill_mode (GimpFillOptions *options, + GimpContext *context, + GimpBucketFillMode fill_mode, + GError **error) +{ + GimpFillType fill_type; + + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), FALSE); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + switch (fill_mode) + { + default: + case GIMP_BUCKET_FILL_FG: + fill_type = GIMP_FILL_FOREGROUND; + break; + + case GIMP_BUCKET_FILL_BG: + fill_type = GIMP_FILL_BACKGROUND; + break; + + case GIMP_BUCKET_FILL_PATTERN: + fill_type = GIMP_FILL_PATTERN; + break; + } + + return gimp_fill_options_set_by_fill_type (options, context, + fill_type, error); +} + +const gchar * +gimp_fill_options_get_undo_desc (GimpFillOptions *options) +{ + GimpFillOptionsPrivate *private; + + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + + private = GET_PRIVATE (options); + + if (private->undo_desc) + return private->undo_desc; + + switch (private->style) + { + case GIMP_FILL_STYLE_SOLID: + return C_("undo-type", "Fill with Solid Color"); + + case GIMP_FILL_STYLE_PATTERN: + return C_("undo-type", "Fill with Pattern"); + } + + g_return_val_if_reached (NULL); +} + +const Babl * +gimp_fill_options_get_format (GimpFillOptions *options, + GimpDrawable *drawable) +{ + GimpContext *context; + + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + context = GIMP_CONTEXT (options); + + return gimp_layer_mode_get_format (gimp_context_get_paint_mode (context), + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + gimp_layer_mode_get_paint_composite_mode ( + gimp_context_get_paint_mode (context)), + gimp_drawable_get_format (drawable)); +} + +GeglBuffer * +gimp_fill_options_create_buffer (GimpFillOptions *options, + GimpDrawable *drawable, + const GeglRectangle *rect, + gint pattern_offset_x, + gint pattern_offset_y) +{ + GeglBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + g_return_val_if_fail (gimp_fill_options_get_style (options) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL, + NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (rect != NULL, NULL); + + buffer = gegl_buffer_new (rect, + gimp_fill_options_get_format (options, drawable)); + + gimp_fill_options_fill_buffer (options, drawable, buffer, + pattern_offset_x, pattern_offset_y); + + return buffer; +} + +void +gimp_fill_options_fill_buffer (GimpFillOptions *options, + GimpDrawable *drawable, + GeglBuffer *buffer, + gint pattern_offset_x, + gint pattern_offset_y) +{ + g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); + g_return_if_fail (gimp_fill_options_get_style (options) != + GIMP_FILL_STYLE_PATTERN || + gimp_context_get_pattern (GIMP_CONTEXT (options)) != NULL); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + switch (gimp_fill_options_get_style (options)) + { + case GIMP_FILL_STYLE_SOLID: + { + GimpRGB color; + + gimp_context_get_foreground (GIMP_CONTEXT (options), &color); + gimp_palettes_add_color_history (GIMP_CONTEXT (options)->gimp, &color); + + gimp_drawable_fill_buffer (drawable, buffer, + &color, NULL, 0, 0); + } + break; + + case GIMP_FILL_STYLE_PATTERN: + { + GimpPattern *pattern; + + pattern = gimp_context_get_pattern (GIMP_CONTEXT (options)); + + gimp_drawable_fill_buffer (drawable, buffer, + NULL, pattern, + pattern_offset_x, + pattern_offset_y); + } + break; + } +} diff --git a/app/core/gimpfilloptions.h b/app/core/gimpfilloptions.h new file mode 100644 index 0000000..434530b --- /dev/null +++ b/app/core/gimpfilloptions.h @@ -0,0 +1,95 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpfilloptions.h + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_FILL_OPTIONS_H__ +#define __GIMP_FILL_OPTIONS_H__ + + +#include "gimpcontext.h" + + +#define GIMP_TYPE_FILL_OPTIONS (gimp_fill_options_get_type ()) +#define GIMP_FILL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FILL_OPTIONS, GimpFillOptions)) +#define GIMP_FILL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FILL_OPTIONS, GimpFillOptionsClass)) +#define GIMP_IS_FILL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FILL_OPTIONS)) +#define GIMP_IS_FILL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FILL_OPTIONS)) +#define GIMP_FILL_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FILL_OPTIONS, GimpFillOptionsClass)) + + +typedef struct _GimpFillOptionsClass GimpFillOptionsClass; + +struct _GimpFillOptions +{ + GimpContext parent_instance; +}; + +struct _GimpFillOptionsClass +{ + GimpContextClass parent_class; +}; + + +GType gimp_fill_options_get_type (void) G_GNUC_CONST; + +GimpFillOptions * gimp_fill_options_new (Gimp *gimp, + GimpContext *context, + gboolean use_context_color); + +GimpFillStyle gimp_fill_options_get_style (GimpFillOptions *options); +void gimp_fill_options_set_style (GimpFillOptions *options, + GimpFillStyle style); + +gboolean gimp_fill_options_get_antialias (GimpFillOptions *options); +void gimp_fill_options_set_antialias (GimpFillOptions *options, + gboolean antialias); + +gboolean gimp_fill_options_get_feather (GimpFillOptions *options, + gdouble *radius); +void gimp_fill_options_set_feather (GimpFillOptions *options, + gboolean feather, + gdouble radius); + +gboolean gimp_fill_options_set_by_fill_type (GimpFillOptions *options, + GimpContext *context, + GimpFillType fill_type, + GError **error); +gboolean gimp_fill_options_set_by_fill_mode (GimpFillOptions *options, + GimpContext *context, + GimpBucketFillMode fill_mode, + GError **error); + +const gchar * gimp_fill_options_get_undo_desc (GimpFillOptions *options); + +const Babl * gimp_fill_options_get_format (GimpFillOptions *options, + GimpDrawable *drawable); + +GeglBuffer * gimp_fill_options_create_buffer (GimpFillOptions *options, + GimpDrawable *drawable, + const GeglRectangle *rect, + gint pattern_offset_x, + gint pattern_offset_y); +void gimp_fill_options_fill_buffer (GimpFillOptions *options, + GimpDrawable *drawable, + GeglBuffer *buffer, + gint pattern_offset_x, + gint pattern_offset_y); + + +#endif /* __GIMP_FILL_OPTIONS_H__ */ diff --git a/app/core/gimpfilter.c b/app/core/gimpfilter.c new file mode 100644 index 0000000..f565d49 --- /dev/null +++ b/app/core/gimpfilter.c @@ -0,0 +1,315 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpfilter.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-memsize.h" +#include "gimpfilter.h" +#include "gimpmarshal.h" + + +enum +{ + ACTIVE_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_ACTIVE, + PROP_IS_LAST_NODE +}; + + +typedef struct _GimpFilterPrivate GimpFilterPrivate; + +struct _GimpFilterPrivate +{ + GeglNode *node; + + guint active : 1; + guint is_last_node : 1; + + GimpApplicator *applicator; +}; + +#define GET_PRIVATE(filter) ((GimpFilterPrivate *) gimp_filter_get_instance_private ((GimpFilter *) (filter))) + + +/* local function prototypes */ + +static void gimp_filter_finalize (GObject *object); +static void gimp_filter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_filter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_filter_get_memsize (GimpObject *object, + gint64 *gui_size); + +static GeglNode * gimp_filter_real_get_node (GimpFilter *filter); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpFilter, gimp_filter, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_filter_parent_class + +static guint gimp_filter_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_filter_class_init (GimpFilterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + gimp_filter_signals[ACTIVE_CHANGED] = + g_signal_new ("active-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpFilterClass, active_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_filter_finalize; + object_class->set_property = gimp_filter_set_property; + object_class->get_property = gimp_filter_get_property; + + gimp_object_class->get_memsize = gimp_filter_get_memsize; + + klass->active_changed = NULL; + klass->get_node = gimp_filter_real_get_node; + + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", NULL, NULL, + TRUE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_IS_LAST_NODE, + g_param_spec_boolean ("is-last-node", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_filter_init (GimpFilter *filter) +{ + GimpFilterPrivate *private = GET_PRIVATE (filter); + + private->active = TRUE; +} + +static void +gimp_filter_finalize (GObject *object) +{ + GimpFilterPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->node); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_filter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpFilter *filter = GIMP_FILTER (object); + + switch (property_id) + { + case PROP_ACTIVE: + gimp_filter_set_active (filter, g_value_get_boolean (value)); + break; + case PROP_IS_LAST_NODE: + gimp_filter_set_is_last_node (filter, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_filter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpFilterPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, private->active); + break; + case PROP_IS_LAST_NODE: + g_value_set_boolean (value, private->is_last_node); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_filter_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpFilterPrivate *private = GET_PRIVATE (object); + gint64 memsize = 0; + + memsize += gimp_g_object_get_memsize (G_OBJECT (private->node)); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static GeglNode * +gimp_filter_real_get_node (GimpFilter *filter) +{ + GimpFilterPrivate *private = GET_PRIVATE (filter); + + private->node = gegl_node_new (); + + return private->node; +} + + +/* public functions */ + +GimpFilter * +gimp_filter_new (const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (GIMP_TYPE_FILTER, + "name", name, + NULL); +} + +GeglNode * +gimp_filter_get_node (GimpFilter *filter) +{ + GimpFilterPrivate *private; + + g_return_val_if_fail (GIMP_IS_FILTER (filter), NULL); + + private = GET_PRIVATE (filter); + + if (private->node) + return private->node; + + return GIMP_FILTER_GET_CLASS (filter)->get_node (filter); +} + +GeglNode * +gimp_filter_peek_node (GimpFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_FILTER (filter), NULL); + + return GET_PRIVATE (filter)->node; +} + +void +gimp_filter_set_active (GimpFilter *filter, + gboolean active) +{ + g_return_if_fail (GIMP_IS_FILTER (filter)); + + active = active ? TRUE : FALSE; + + if (active != gimp_filter_get_active (filter)) + { + GET_PRIVATE (filter)->active = active; + + g_signal_emit (filter, gimp_filter_signals[ACTIVE_CHANGED], 0); + + g_object_notify (G_OBJECT (filter), "active"); + } +} + +gboolean +gimp_filter_get_active (GimpFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE); + + return GET_PRIVATE (filter)->active; +} + +void +gimp_filter_set_is_last_node (GimpFilter *filter, + gboolean is_last_node) +{ + g_return_if_fail (GIMP_IS_FILTER (filter)); + + is_last_node = is_last_node ? TRUE : FALSE; + + if (is_last_node != gimp_filter_get_is_last_node (filter)) + { + GET_PRIVATE (filter)->is_last_node = is_last_node; + + g_object_notify (G_OBJECT (filter), "is-last-node"); + } +} + +gboolean +gimp_filter_get_is_last_node (GimpFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE); + + return GET_PRIVATE (filter)->is_last_node; +} + +void +gimp_filter_set_applicator (GimpFilter *filter, + GimpApplicator *applicator) +{ + GimpFilterPrivate *private; + + g_return_if_fail (GIMP_IS_FILTER (filter)); + + private = GET_PRIVATE (filter); + + private->applicator = applicator; +} + +GimpApplicator * +gimp_filter_get_applicator (GimpFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_FILTER (filter), NULL); + + return GET_PRIVATE (filter)->applicator; +} diff --git a/app/core/gimpfilter.h b/app/core/gimpfilter.h new file mode 100644 index 0000000..b1dc922 --- /dev/null +++ b/app/core/gimpfilter.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpfilter.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 . + */ + +#ifndef __GIMP_FILTER_H__ +#define __GIMP_FILTER_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_FILTER (gimp_filter_get_type ()) +#define GIMP_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FILTER, GimpFilter)) +#define GIMP_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FILTER, GimpFilterClass)) +#define GIMP_IS_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FILTER)) +#define GIMP_IS_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FILTER)) +#define GIMP_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FILTER, GimpFilterClass)) + + +typedef struct _GimpFilterClass GimpFilterClass; + +struct _GimpFilter +{ + GimpViewable parent_instance; +}; + +struct _GimpFilterClass +{ + GimpViewableClass parent_class; + + /* signals */ + void (* active_changed) (GimpFilter *filter); + + /* virtual functions */ + GeglNode * (* get_node) (GimpFilter *filter); +}; + + +GType gimp_filter_get_type (void) G_GNUC_CONST; +GimpFilter * gimp_filter_new (const gchar *name); + +GeglNode * gimp_filter_get_node (GimpFilter *filter); +GeglNode * gimp_filter_peek_node (GimpFilter *filter); + +void gimp_filter_set_active (GimpFilter *filter, + gboolean active); +gboolean gimp_filter_get_active (GimpFilter *filter); + +void gimp_filter_set_is_last_node (GimpFilter *filter, + gboolean is_last_node); +gboolean gimp_filter_get_is_last_node (GimpFilter *filter); + +void gimp_filter_set_applicator (GimpFilter *filter, + GimpApplicator *applicator); +GimpApplicator * gimp_filter_get_applicator (GimpFilter *filter); + + +#endif /* __GIMP_FILTER_H__ */ diff --git a/app/core/gimpfilteredcontainer.c b/app/core/gimpfilteredcontainer.c new file mode 100644 index 0000000..ec56593 --- /dev/null +++ b/app/core/gimpfilteredcontainer.c @@ -0,0 +1,373 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpfilteredcontainer.c + * Copyright (C) 2008 Aurimas Juška + * 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpfilteredcontainer.h" + + +enum +{ + PROP_0, + PROP_SRC_CONTAINER, + PROP_FILTER_FUNC, + PROP_FILTER_DATA +}; + + +static void gimp_filtered_container_constructed (GObject *object); +static void gimp_filtered_container_dispose (GObject *object); +static void gimp_filtered_container_finalize (GObject *object); +static void gimp_filtered_container_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_filtered_container_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_filtered_container_real_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_filtered_container_real_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_filtered_container_real_src_freeze (GimpFilteredContainer *filtered_container); +static void gimp_filtered_container_real_src_thaw (GimpFilteredContainer *filtered_container); + +static gboolean gimp_filtered_container_object_matches (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_filtered_container_src_add (GimpContainer *src_container, + GimpObject *obj, + GimpFilteredContainer *filtered_container); +static void gimp_filtered_container_src_remove (GimpContainer *src_container, + GimpObject *obj, + GimpFilteredContainer *filtered_container); +static void gimp_filtered_container_src_freeze (GimpContainer *src_container, + GimpFilteredContainer *filtered_container); +static void gimp_filtered_container_src_thaw (GimpContainer *src_container, + GimpFilteredContainer *filtered_container); + + +G_DEFINE_TYPE (GimpFilteredContainer, gimp_filtered_container, GIMP_TYPE_LIST) + +#define parent_class gimp_filtered_container_parent_class + + +static void +gimp_filtered_container_class_init (GimpFilteredContainerClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + GimpFilteredContainerClass *filtered_class = GIMP_FILTERED_CONTAINER_CLASS (klass); + + g_object_class->constructed = gimp_filtered_container_constructed; + g_object_class->dispose = gimp_filtered_container_dispose; + g_object_class->finalize = gimp_filtered_container_finalize; + g_object_class->set_property = gimp_filtered_container_set_property; + g_object_class->get_property = gimp_filtered_container_get_property; + + filtered_class->src_add = gimp_filtered_container_real_src_add; + filtered_class->src_remove = gimp_filtered_container_real_src_remove; + filtered_class->src_freeze = gimp_filtered_container_real_src_freeze; + filtered_class->src_thaw = gimp_filtered_container_real_src_thaw; + + g_object_class_install_property (g_object_class, PROP_SRC_CONTAINER, + g_param_spec_object ("src-container", + NULL, NULL, + GIMP_TYPE_CONTAINER, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (g_object_class, PROP_FILTER_FUNC, + g_param_spec_pointer ("filter-func", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (g_object_class, PROP_FILTER_DATA, + g_param_spec_pointer ("filter-data", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_filtered_container_init (GimpFilteredContainer *filtered_container) +{ +} + +static void +gimp_filtered_container_constructed (GObject *object) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_CONTAINER (filtered_container->src_container)); + + if (! gimp_container_frozen (filtered_container->src_container)) + { + /* a freeze/thaw can't hurt on a newly created container because + * we can't have any views yet. This way we get away without + * having a virtual function for initializing the container. + */ + gimp_filtered_container_src_freeze (filtered_container->src_container, + filtered_container); + gimp_filtered_container_src_thaw (filtered_container->src_container, + filtered_container); + } +} + +static void +gimp_filtered_container_dispose (GObject *object) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (object); + + if (filtered_container->src_container) + { + g_signal_handlers_disconnect_by_func (filtered_container->src_container, + gimp_filtered_container_src_add, + filtered_container); + g_signal_handlers_disconnect_by_func (filtered_container->src_container, + gimp_filtered_container_src_remove, + filtered_container); + g_signal_handlers_disconnect_by_func (filtered_container->src_container, + gimp_filtered_container_src_freeze, + filtered_container); + g_signal_handlers_disconnect_by_func (filtered_container->src_container, + gimp_filtered_container_src_thaw, + filtered_container); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_filtered_container_finalize (GObject *object) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (object); + + g_clear_object (&filtered_container->src_container); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_filtered_container_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (object); + + switch (property_id) + { + case PROP_SRC_CONTAINER: + filtered_container->src_container = g_value_dup_object (value); + + g_signal_connect (filtered_container->src_container, "add", + G_CALLBACK (gimp_filtered_container_src_add), + filtered_container); + g_signal_connect (filtered_container->src_container, "remove", + G_CALLBACK (gimp_filtered_container_src_remove), + filtered_container); + g_signal_connect (filtered_container->src_container, "freeze", + G_CALLBACK (gimp_filtered_container_src_freeze), + filtered_container); + g_signal_connect (filtered_container->src_container, "thaw", + G_CALLBACK (gimp_filtered_container_src_thaw), + filtered_container); + break; + + case PROP_FILTER_FUNC: + filtered_container->filter_func = g_value_get_pointer (value); + break; + + case PROP_FILTER_DATA: + filtered_container->filter_data = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_filtered_container_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (object); + + switch (property_id) + { + case PROP_SRC_CONTAINER: + g_value_set_object (value, filtered_container->src_container); + break; + + case PROP_FILTER_FUNC: + g_value_set_pointer (value, filtered_container->filter_func); + break; + + case PROP_FILTER_DATA: + g_value_set_pointer (value, filtered_container->filter_data); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_filtered_container_real_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + if (gimp_filtered_container_object_matches (filtered_container, object)) + { + gimp_container_add (GIMP_CONTAINER (filtered_container), object); + } +} + +static void +gimp_filtered_container_real_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + if (gimp_filtered_container_object_matches (filtered_container, object)) + { + gimp_container_remove (GIMP_CONTAINER (filtered_container), object); + } +} + +static void +gimp_filtered_container_real_src_freeze (GimpFilteredContainer *filtered_container) +{ + gimp_container_clear (GIMP_CONTAINER (filtered_container)); +} + +static void +gimp_filtered_container_real_src_thaw (GimpFilteredContainer *filtered_container) +{ + GList *list; + + for (list = GIMP_LIST (filtered_container->src_container)->queue->head; + list; + list = g_list_next (list)) + { + GimpObject *object = list->data; + + if (gimp_filtered_container_object_matches (filtered_container, object)) + { + gimp_container_add (GIMP_CONTAINER (filtered_container), object); + } + } +} + +/** + * gimp_filtered_container_new: + * @src_container: container to be filtered. + * + * Creates a new #GimpFilteredContainer object which creates filtered + * data view of #GimpTagged objects. It filters @src_container for objects + * containing all of the filtering tags. Synchronization with @src_container + * data is performed automatically. + * + * Return value: a new #GimpFilteredContainer object. + **/ +GimpContainer * +gimp_filtered_container_new (GimpContainer *src_container, + GimpObjectFilterFunc filter_func, + gpointer filter_data) +{ + GType children_type; + GCompareFunc sort_func; + + g_return_val_if_fail (GIMP_IS_LIST (src_container), NULL); + + children_type = gimp_container_get_children_type (src_container); + sort_func = GIMP_LIST (src_container)->sort_func; + + return g_object_new (GIMP_TYPE_FILTERED_CONTAINER, + "sort-func", sort_func, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "unique-names", FALSE, + "src-container", src_container, + "filter-func", filter_func, + "filter-data", filter_data, + NULL); +} + +static gboolean +gimp_filtered_container_object_matches (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + return (! filtered_container->filter_func || + filtered_container->filter_func (object, + filtered_container->filter_data)); +} + +static void +gimp_filtered_container_src_add (GimpContainer *src_container, + GimpObject *object, + GimpFilteredContainer *filtered_container) +{ + if (! gimp_container_frozen (filtered_container->src_container)) + { + GIMP_FILTERED_CONTAINER_GET_CLASS (filtered_container)->src_add (filtered_container, + object); + } +} + +static void +gimp_filtered_container_src_remove (GimpContainer *src_container, + GimpObject *object, + GimpFilteredContainer *filtered_container) +{ + if (! gimp_container_frozen (filtered_container->src_container)) + { + GIMP_FILTERED_CONTAINER_GET_CLASS (filtered_container)->src_remove (filtered_container, + object); + } +} + +static void +gimp_filtered_container_src_freeze (GimpContainer *src_container, + GimpFilteredContainer *filtered_container) +{ + gimp_container_freeze (GIMP_CONTAINER (filtered_container)); + + GIMP_FILTERED_CONTAINER_GET_CLASS (filtered_container)->src_freeze (filtered_container); +} + +static void +gimp_filtered_container_src_thaw (GimpContainer *src_container, + GimpFilteredContainer *filtered_container) +{ + GIMP_FILTERED_CONTAINER_GET_CLASS (filtered_container)->src_thaw (filtered_container); + + gimp_container_thaw (GIMP_CONTAINER (filtered_container)); +} diff --git a/app/core/gimpfilteredcontainer.h b/app/core/gimpfilteredcontainer.h new file mode 100644 index 0000000..28803c8 --- /dev/null +++ b/app/core/gimpfilteredcontainer.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpfilteredcontainer.h + * Copyright (C) 2008 Aurimas Juška + * 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_FILTERED_CONTAINER_H__ +#define __GIMP_FILTERED_CONTAINER_H__ + + +#include "gimplist.h" + + +#define GIMP_TYPE_FILTERED_CONTAINER (gimp_filtered_container_get_type ()) +#define GIMP_FILTERED_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FILTERED_CONTAINER, GimpFilteredContainer)) +#define GIMP_FILTERED_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FILTERED_CONTAINER, GimpFilteredContainerClass)) +#define GIMP_IS_FILTERED_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FILTERED_CONTAINER)) +#define GIMP_IS_FILTERED_CONTAINER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), GIMP_TYPE_FILTERED_CONTAINER)) +#define GIMP_FILTERED_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FILTERED_CONTAINER, GimpFilteredContainerClass)) + + +typedef struct _GimpFilteredContainerClass GimpFilteredContainerClass; + +struct _GimpFilteredContainer +{ + GimpList parent_instance; + + GimpContainer *src_container; + GimpObjectFilterFunc filter_func; + gpointer filter_data; +}; + +struct _GimpFilteredContainerClass +{ + GimpContainerClass parent_class; + + void (* src_add) (GimpFilteredContainer *filtered_container, + GimpObject *object); + void (* src_remove) (GimpFilteredContainer *filtered_container, + GimpObject *object); + void (* src_freeze) (GimpFilteredContainer *filtered_container); + void (* src_thaw) (GimpFilteredContainer *filtered_container); +}; + + +GType gimp_filtered_container_get_type (void) G_GNUC_CONST; + +GimpContainer * gimp_filtered_container_new (GimpContainer *src_container, + GimpObjectFilterFunc filter_func, + gpointer filter_data); + + +#endif /* __GIMP_FILTERED_CONTAINER_H__ */ diff --git a/app/core/gimpfilterstack.c b/app/core/gimpfilterstack.c new file mode 100644 index 0000000..09d9cfd --- /dev/null +++ b/app/core/gimpfilterstack.c @@ -0,0 +1,350 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpfilterstack.c + * Copyright (C) 2008-2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpfilter.h" +#include "gimpfilterstack.h" + + +/* local function prototypes */ + +static void gimp_filter_stack_constructed (GObject *object); +static void gimp_filter_stack_finalize (GObject *object); + +static void gimp_filter_stack_add (GimpContainer *container, + GimpObject *object); +static void gimp_filter_stack_remove (GimpContainer *container, + GimpObject *object); +static void gimp_filter_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); + +static void gimp_filter_stack_add_node (GimpFilterStack *stack, + GimpFilter *filter); +static void gimp_filter_stack_remove_node (GimpFilterStack *stack, + GimpFilter *filter); +static void gimp_filter_stack_update_last_node (GimpFilterStack *stack); + +static void gimp_filter_stack_filter_active (GimpFilter *filter, + GimpFilterStack *stack); + + +G_DEFINE_TYPE (GimpFilterStack, gimp_filter_stack, GIMP_TYPE_LIST); + +#define parent_class gimp_filter_stack_parent_class + + +static void +gimp_filter_stack_class_init (GimpFilterStackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + object_class->constructed = gimp_filter_stack_constructed; + object_class->finalize = gimp_filter_stack_finalize; + + container_class->add = gimp_filter_stack_add; + container_class->remove = gimp_filter_stack_remove; + container_class->reorder = gimp_filter_stack_reorder; +} + +static void +gimp_filter_stack_init (GimpFilterStack *stack) +{ +} + +static void +gimp_filter_stack_constructed (GObject *object) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (g_type_is_a (gimp_container_get_children_type (container), + GIMP_TYPE_FILTER)); + + gimp_container_add_handler (container, "active-changed", + G_CALLBACK (gimp_filter_stack_filter_active), + container); +} + +static void +gimp_filter_stack_finalize (GObject *object) +{ + GimpFilterStack *stack = GIMP_FILTER_STACK (object); + + g_clear_object (&stack->graph); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_filter_stack_add (GimpContainer *container, + GimpObject *object) +{ + GimpFilterStack *stack = GIMP_FILTER_STACK (container); + GimpFilter *filter = GIMP_FILTER (object); + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); + + if (gimp_filter_get_active (filter)) + { + if (stack->graph) + { + gegl_node_add_child (stack->graph, gimp_filter_get_node (filter)); + gimp_filter_stack_add_node (stack, filter); + } + + gimp_filter_stack_update_last_node (stack); + } +} + +static void +gimp_filter_stack_remove (GimpContainer *container, + GimpObject *object) +{ + GimpFilterStack *stack = GIMP_FILTER_STACK (container); + GimpFilter *filter = GIMP_FILTER (object); + + if (stack->graph && gimp_filter_get_active (filter)) + { + gimp_filter_stack_remove_node (stack, filter); + gegl_node_remove_child (stack->graph, gimp_filter_get_node (filter)); + } + + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); + + if (gimp_filter_get_active (filter)) + { + gimp_filter_set_is_last_node (filter, FALSE); + gimp_filter_stack_update_last_node (stack); + } +} + +static void +gimp_filter_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + GimpFilterStack *stack = GIMP_FILTER_STACK (container); + GimpFilter *filter = GIMP_FILTER (object); + + if (stack->graph && gimp_filter_get_active (filter)) + gimp_filter_stack_remove_node (stack, filter); + + GIMP_CONTAINER_CLASS (parent_class)->reorder (container, object, new_index); + + if (gimp_filter_get_active (filter)) + { + gimp_filter_stack_update_last_node (stack); + + if (stack->graph) + gimp_filter_stack_add_node (stack, filter); + } +} + + +/* public functions */ + +GimpContainer * +gimp_filter_stack_new (GType filter_type) +{ + g_return_val_if_fail (g_type_is_a (filter_type, GIMP_TYPE_FILTER), NULL); + + return g_object_new (GIMP_TYPE_FILTER_STACK, + "name", g_type_name (filter_type), + "children-type", filter_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); +} + +GeglNode * +gimp_filter_stack_get_graph (GimpFilterStack *stack) +{ + GList *list; + GeglNode *previous; + GeglNode *output; + + g_return_val_if_fail (GIMP_IS_FILTER_STACK (stack), NULL); + + if (stack->graph) + return stack->graph; + + stack->graph = gegl_node_new (); + + previous = gegl_node_get_input_proxy (stack->graph, "input"); + + for (list = GIMP_LIST (stack)->queue->tail; + list; + list = g_list_previous (list)) + { + GimpFilter *filter = list->data; + GeglNode *node; + + if (! gimp_filter_get_active (filter)) + continue; + + node = gimp_filter_get_node (filter); + + gegl_node_add_child (stack->graph, node); + + gegl_node_connect_to (previous, "output", + node, "input"); + + previous = node; + } + + output = gegl_node_get_output_proxy (stack->graph, "output"); + + gegl_node_connect_to (previous, "output", + output, "input"); + + return stack->graph; +} + + +/* private functions */ + +static void +gimp_filter_stack_add_node (GimpFilterStack *stack, + GimpFilter *filter) +{ + GeglNode *node; + GeglNode *node_above = NULL; + GeglNode *node_below = NULL; + GList *iter; + + node = gimp_filter_get_node (filter); + + + iter = g_list_find (GIMP_LIST (stack)->queue->head, filter); + + while ((iter = g_list_previous (iter))) + { + GimpFilter *filter_above = iter->data; + + if (gimp_filter_get_active (filter_above)) + { + node_above = gimp_filter_get_node (filter_above); + + break; + } + } + + if (! node_above) + node_above = gegl_node_get_output_proxy (stack->graph, "output"); + + node_below = gegl_node_get_producer (node_above, "input", NULL); + + gegl_node_connect_to (node_below, "output", + node, "input"); + gegl_node_connect_to (node, "output", + node_above, "input"); +} + +static void +gimp_filter_stack_remove_node (GimpFilterStack *stack, + GimpFilter *filter) +{ + GeglNode *node; + GeglNode *node_above = NULL; + GeglNode *node_below = NULL; + GList *iter; + + node = gimp_filter_get_node (filter); + + iter = g_list_find (GIMP_LIST (stack)->queue->head, filter); + + while ((iter = g_list_previous (iter))) + { + GimpFilter *filter_above = iter->data; + + if (gimp_filter_get_active (filter_above)) + { + node_above = gimp_filter_get_node (filter_above); + + break; + } + } + + if (! node_above) + node_above = gegl_node_get_output_proxy (stack->graph, "output"); + + node_below = gegl_node_get_producer (node, "input", NULL); + + gegl_node_disconnect (node, "input"); + + gegl_node_connect_to (node_below, "output", + node_above, "input"); +} + +static void +gimp_filter_stack_update_last_node (GimpFilterStack *stack) +{ + GList *list; + gboolean found_last = FALSE; + + for (list = GIMP_LIST (stack)->queue->tail; + list; + list = g_list_previous (list)) + { + GimpFilter *filter = list->data; + + if (! found_last && gimp_filter_get_active (filter)) + { + gimp_filter_set_is_last_node (filter, TRUE); + found_last = TRUE; + } + else + { + gimp_filter_set_is_last_node (filter, FALSE); + } + } +} + +static void +gimp_filter_stack_filter_active (GimpFilter *filter, + GimpFilterStack *stack) +{ + if (stack->graph) + { + if (gimp_filter_get_active (filter)) + { + gegl_node_add_child (stack->graph, gimp_filter_get_node (filter)); + gimp_filter_stack_add_node (stack, filter); + } + else + { + gimp_filter_stack_remove_node (stack, filter); + gegl_node_remove_child (stack->graph, gimp_filter_get_node (filter)); + } + } + + gimp_filter_stack_update_last_node (stack); + + if (! gimp_filter_get_active (filter)) + gimp_filter_set_is_last_node (filter, FALSE); +} diff --git a/app/core/gimpfilterstack.h b/app/core/gimpfilterstack.h new file mode 100644 index 0000000..3567f06 --- /dev/null +++ b/app/core/gimpfilterstack.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpfilterstack.h + * Copyright (C) 2008-2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_FILTER_STACK_H__ +#define __GIMP_FILTER_STACK_H__ + +#include "gimplist.h" + + +#define GIMP_TYPE_FILTER_STACK (gimp_filter_stack_get_type ()) +#define GIMP_FILTER_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FILTER_STACK, GimpFilterStack)) +#define GIMP_FILTER_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FILTER_STACK, GimpFilterStackClass)) +#define GIMP_IS_FILTER_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FILTER_STACK)) +#define GIMP_IS_FILTER_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FILTER_STACK)) + + +typedef struct _GimpFilterStackClass GimpFilterStackClass; + +struct _GimpFilterStack +{ + GimpList parent_instance; + + GeglNode *graph; +}; + +struct _GimpFilterStackClass +{ + GimpListClass parent_class; +}; + + +GType gimp_filter_stack_get_type (void) G_GNUC_CONST; +GimpContainer * gimp_filter_stack_new (GType filter_type); + +GeglNode * gimp_filter_stack_get_graph (GimpFilterStack *stack); + + +#endif /* __GIMP_FILTER_STACK_H__ */ diff --git a/app/core/gimpfloatingselectionundo.c b/app/core/gimpfloatingselectionundo.c new file mode 100644 index 0000000..6ca28ab --- /dev/null +++ b/app/core/gimpfloatingselectionundo.c @@ -0,0 +1,135 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpdrawable-floating-selection.h" +#include "gimpfloatingselectionundo.h" +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayer-floating-selection.h" + + +static void gimp_floating_selection_undo_constructed (GObject *object); + +static void gimp_floating_selection_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpFloatingSelectionUndo, gimp_floating_selection_undo, + GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_floating_selection_undo_parent_class + + +static void +gimp_floating_selection_undo_class_init (GimpFloatingSelectionUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_floating_selection_undo_constructed; + + undo_class->pop = gimp_floating_selection_undo_pop; +} + +static void +gimp_floating_selection_undo_init (GimpFloatingSelectionUndo *undo) +{ +} + +static void +gimp_floating_selection_undo_constructed (GObject *object) +{ + GimpFloatingSelectionUndo *floating_sel_undo; + GimpLayer *layer; + + floating_sel_undo = GIMP_FLOATING_SELECTION_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_LAYER (GIMP_ITEM_UNDO (object)->item)); + + layer = GIMP_LAYER (GIMP_ITEM_UNDO (object)->item); + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_FS_TO_LAYER: + floating_sel_undo->drawable = gimp_layer_get_floating_sel_drawable (layer); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_floating_selection_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpFloatingSelectionUndo *floating_sel_undo; + GimpLayer *floating_layer; + + floating_sel_undo = GIMP_FLOATING_SELECTION_UNDO (undo); + floating_layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_FS_TO_LAYER: + if (undo_mode == GIMP_UNDO_MODE_UNDO) + { + /* Update the preview for the floating selection */ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (floating_layer)); + + gimp_layer_set_floating_sel_drawable (floating_layer, + floating_sel_undo->drawable); + gimp_image_set_active_layer (undo->image, floating_layer); + + gimp_drawable_attach_floating_sel (gimp_layer_get_floating_sel_drawable (floating_layer), + floating_layer); + } + else + { + gimp_drawable_detach_floating_sel (gimp_layer_get_floating_sel_drawable (floating_layer)); + gimp_layer_set_floating_sel_drawable (floating_layer, NULL); + } + + /* When the floating selection is converted to/from a normal + * layer it does something resembling a name change, so emit the + * "name-changed" signal + */ + gimp_object_name_changed (GIMP_OBJECT (floating_layer)); + + gimp_drawable_update (GIMP_DRAWABLE (floating_layer), + 0, 0, + gimp_item_get_width (GIMP_ITEM (floating_layer)), + gimp_item_get_height (GIMP_ITEM (floating_layer))); + break; + + default: + g_return_if_reached (); + } +} diff --git a/app/core/gimpfloatingselectionundo.h b/app/core/gimpfloatingselectionundo.h new file mode 100644 index 0000000..4d9a626 --- /dev/null +++ b/app/core/gimpfloatingselectionundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_FLOATING_SELECTION_UNDO_H__ +#define __GIMP_FLOATING_SELECTION_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_FLOATING_SELECTION_UNDO (gimp_floating_selection_undo_get_type ()) +#define GIMP_FLOATING_SELECTION_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FLOATING_SELECTION_UNDO, GimpFloatingSelectionUndo)) +#define GIMP_FLOATING_SELECTION_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FLOATING_SELECTION_UNDO, GimpFloatingSelectionUndoClass)) +#define GIMP_IS_FLOATING_SELECTION_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FLOATING_SELECTION_UNDO)) +#define GIMP_IS_FLOATING_SELECTION_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FLOATING_SELECTION_UNDO)) +#define GIMP_FLOATING_SELECTION_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FLOATING_SELECTION_UNDO, GimpFloatingSelectionUndoClass)) + + +typedef struct _GimpFloatingSelectionUndo GimpFloatingSelectionUndo; +typedef struct _GimpFloatingSelectionUndoClass GimpFloatingSelectionUndoClass; + +struct _GimpFloatingSelectionUndo +{ + GimpItemUndo parent_instance; + + GimpDrawable *drawable; +}; + +struct _GimpFloatingSelectionUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_floating_selection_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_FLOATING_SELECTION_UNDO_H__ */ diff --git a/app/core/gimpgradient-load.c b/app/core/gimpgradient-load.c new file mode 100644 index 0000000..1abe494 --- /dev/null +++ b/app/core/gimpgradient-load.c @@ -0,0 +1,575 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "config/gimpxmlparser.h" + +#include "gimp-utils.h" +#include "gimpgradient.h" +#include "gimpgradient-load.h" + +#include "gimp-intl.h" + + +GList * +gimp_gradient_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpGradient *gradient = NULL; + GimpGradientSegment *prev; + gint num_segments; + gint i; + GDataInputStream *data_input; + gchar *line; + gsize line_len; + gint linenum; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data_input = g_data_input_stream_new (input); + + linenum = 1; + line_len = 1024; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + goto failed; + + if (! g_str_has_prefix (line, "GIMP Gradient")) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Not a GIMP gradient file.")); + g_free (line); + goto failed; + } + + g_free (line); + + gradient = g_object_new (GIMP_TYPE_GRADIENT, + "mime-type", "application/x-gimp-gradient", + NULL); + + linenum++; + line_len = 1024; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + goto failed; + + if (g_str_has_prefix (line, "Name: ")) + { + gchar *utf8; + + utf8 = gimp_any_to_utf8 (g_strstrip (line + strlen ("Name: ")), -1, + _("Invalid UTF-8 string in gradient file '%s'."), + gimp_file_get_utf8_name (file)); + gimp_object_take_name (GIMP_OBJECT (gradient), utf8); + + g_free (line); + + linenum++; + line_len = 1024; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + goto failed; + } + else /* old gradient format */ + { + gimp_object_take_name (GIMP_OBJECT (gradient), + g_path_get_basename (gimp_file_get_utf8_name (file))); + } + + num_segments = atoi (line); + + g_free (line); + + if (num_segments < 1) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("File is corrupt.")); + goto failed; + } + + prev = NULL; + + for (i = 0; i < num_segments; i++) + { + GimpGradientSegment *seg; + gchar *end; + gint color; + gint type; + gint left_color_type; + gint right_color_type; + + seg = gimp_gradient_segment_new (); + + seg->prev = prev; + + if (prev) + prev->next = seg; + else + gradient->segments = seg; + + linenum++; + line_len = 1024; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + goto failed; + + if (! gimp_ascii_strtod (line, &end, &seg->left) || + ! gimp_ascii_strtod (end, &end, &seg->middle) || + ! gimp_ascii_strtod (end, &end, &seg->right) || + + ! gimp_ascii_strtod (end, &end, &seg->left_color.r) || + ! gimp_ascii_strtod (end, &end, &seg->left_color.g) || + ! gimp_ascii_strtod (end, &end, &seg->left_color.b) || + ! gimp_ascii_strtod (end, &end, &seg->left_color.a) || + + ! gimp_ascii_strtod (end, &end, &seg->right_color.r) || + ! gimp_ascii_strtod (end, &end, &seg->right_color.g) || + ! gimp_ascii_strtod (end, &end, &seg->right_color.b) || + ! gimp_ascii_strtod (end, &end, &seg->right_color.a)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + + switch (sscanf (end, "%d %d %d %d", + &type, &color, + &left_color_type, &right_color_type)) + { + case 4: + seg->left_color_type = (GimpGradientColor) left_color_type; + if (seg->left_color_type < GIMP_GRADIENT_COLOR_FIXED || + seg->left_color_type > GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + + seg->right_color_type = (GimpGradientColor) right_color_type; + if (seg->right_color_type < GIMP_GRADIENT_COLOR_FIXED || + seg->right_color_type > GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + /* fall thru */ + + case 2: + seg->type = (GimpGradientSegmentType) type; + if (seg->type < GIMP_GRADIENT_SEGMENT_LINEAR || + seg->type > GIMP_GRADIENT_SEGMENT_STEP) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + + seg->color = (GimpGradientSegmentColor) color; + if (seg->color < GIMP_GRADIENT_SEGMENT_RGB || + seg->color > GIMP_GRADIENT_SEGMENT_HSV_CW) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + break; + + default: + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Corrupt segment %d."), i); + g_free (line); + goto failed; + } + + g_free (line); + + if (seg->left > seg->middle || + seg->middle > seg->right || + ( prev && (prev->right != seg->left)) || + (! prev && (0.0 != seg->left))) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Segments do not span the range 0-1.")); + goto failed; + } + + prev = seg; + } + + if (prev->right != 1.0) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Segments do not span the range 0-1.")); + goto failed; + } + + g_object_unref (data_input); + + return g_list_prepend (NULL, gradient); + + failed: + + g_object_unref (data_input); + + if (gradient) + g_object_unref (gradient); + + g_prefix_error (error, _("In line %d of gradient file: "), linenum); + + return NULL; +} + + +/* SVG gradient parser */ + +typedef struct +{ + GimpGradient *gradient; /* current gradient */ + GList *gradients; /* finished gradients */ + GList *stops; +} SvgParser; + +typedef struct +{ + gdouble offset; + GimpRGB color; +} SvgStop; + + +static void svg_parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void svg_parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); + +static GimpGradientSegment * + svg_parser_gradient_segments (GList *stops); + +static SvgStop * svg_parse_gradient_stop (const gchar **names, + const gchar **values); + + +static const GMarkupParser markup_parser = +{ + svg_parser_start_element, + svg_parser_end_element, + NULL, /* characters */ + NULL, /* passthrough */ + NULL /* error */ +}; + + +GList * +gimp_gradient_load_svg (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpXmlParser *xml_parser; + SvgParser parser = { NULL, }; + gboolean success; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* FIXME input */ + g_input_stream_close (input, NULL, NULL); + + xml_parser = gimp_xml_parser_new (&markup_parser, &parser); + + success = gimp_xml_parser_parse_gfile (xml_parser, file, error); + + gimp_xml_parser_free (xml_parser); + + if (success && ! parser.gradients) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("No linear gradients found.")); + } + + if (parser.gradient) + g_object_unref (parser.gradient); + + if (parser.stops) + { + GList *list; + + for (list = parser.stops; list; list = list->next) + g_slice_free (SvgStop, list->data); + + g_list_free (parser.stops); + } + + return g_list_reverse (parser.gradients); +} + +static void +svg_parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + SvgParser *parser = user_data; + + if (! parser->gradient && strcmp (element_name, "linearGradient") == 0) + { + const gchar *name = NULL; + + while (*attribute_names && *attribute_values) + { + if (strcmp (*attribute_names, "id") == 0 && *attribute_values) + name = *attribute_values; + + attribute_names++; + attribute_values++; + } + + parser->gradient = g_object_new (GIMP_TYPE_GRADIENT, + "name", name, + "mime-type", "image/svg+xml", + NULL); + } + else if (parser->gradient && strcmp (element_name, "stop") == 0) + { + SvgStop *stop = svg_parse_gradient_stop (attribute_names, + attribute_values); + + /* The spec clearly states that each gradient stop's offset + * value is required to be equal to or greater than the + * previous gradient stop's offset value. + */ + if (parser->stops) + stop->offset = MAX (stop->offset, + ((SvgStop *) parser->stops->data)->offset); + + parser->stops = g_list_prepend (parser->stops, stop); + } +} + +static void +svg_parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SvgParser *parser = user_data; + + if (parser->gradient && + strcmp (element_name, "linearGradient") == 0) + { + GList *list; + + parser->gradient->segments = svg_parser_gradient_segments (parser->stops); + + for (list = parser->stops; list; list = list->next) + g_slice_free (SvgStop, list->data); + + g_list_free (parser->stops); + parser->stops = NULL; + + if (parser->gradient->segments) + parser->gradients = g_list_prepend (parser->gradients, + parser->gradient); + else + g_object_unref (parser->gradient); + + parser->gradient = NULL; + } +} + +static GimpGradientSegment * +svg_parser_gradient_segments (GList *stops) +{ + GimpGradientSegment *segment; + SvgStop *stop; + GList *list; + + if (! stops) + return NULL; + + stop = stops->data; + + segment = gimp_gradient_segment_new (); + + segment->left_color = stop->color; + segment->right_color = stop->color; + + /* the list of offsets is sorted from largest to smallest */ + for (list = g_list_next (stops); list; list = g_list_next (list)) + { + GimpGradientSegment *next = segment; + + segment->left = stop->offset; + segment->middle = (segment->left + segment->right) / 2.0; + + segment = gimp_gradient_segment_new (); + + segment->next = next; + next->prev = segment; + + segment->right = stop->offset; + segment->right_color = stop->color; + + stop = list->data; + + segment->left_color = stop->color; + } + + segment->middle = (segment->left + segment->right) / 2.0; + + if (stop->offset > 0.0) + segment->right_color = stop->color; + + /* FIXME: remove empty segments here or add a GimpGradient API to do that + */ + + return segment; +} + +static void +svg_parse_gradient_stop_style_prop (SvgStop *stop, + const gchar *name, + const gchar *value) +{ + if (strcmp (name, "stop-color") == 0) + { + gimp_rgb_parse_css (&stop->color, value, -1); + } + else if (strcmp (name, "stop-opacity") == 0) + { + gdouble opacity = g_ascii_strtod (value, NULL); + + if (errno != ERANGE) + gimp_rgb_set_alpha (&stop->color, CLAMP (opacity, 0.0, 1.0)); + } +} + +/* very simplistic CSS style parser */ +static void +svg_parse_gradient_stop_style (SvgStop *stop, + const gchar *style) +{ + const gchar *end; + const gchar *sep; + + while (*style) + { + while (g_ascii_isspace (*style)) + style++; + + for (end = style; *end && *end != ';'; end++) + /* do nothing */; + + for (sep = style; sep < end && *sep != ':'; sep++) + /* do nothing */; + + if (end > sep && sep > style) + { + gchar *name; + gchar *value; + + name = g_strndup (style, sep - style); + sep++; + value = g_strndup (sep, end - sep - (*end == ';' ? 1 : 0)); + + svg_parse_gradient_stop_style_prop (stop, name, value); + + g_free (value); + g_free (name); + } + + style = end; + + if (*style == ';') + style++; + } +} + +static SvgStop * +svg_parse_gradient_stop (const gchar **names, + const gchar **values) +{ + SvgStop *stop = g_slice_new0 (SvgStop); + + gimp_rgb_set_alpha (&stop->color, 1.0); + + while (*names && *values) + { + if (strcmp (*names, "offset") == 0) + { + gchar *end; + + stop->offset = g_ascii_strtod (*values, &end); + + if (end && *end == '%') + stop->offset /= 100.0; + + stop->offset = CLAMP (stop->offset, 0.0, 1.0); + } + else if (strcmp (*names, "style") == 0) + { + svg_parse_gradient_stop_style (stop, *values); + } + else + { + svg_parse_gradient_stop_style_prop (stop, *names, *values); + } + + names++; + values++; + } + + return stop; +} diff --git a/app/core/gimpgradient-load.h b/app/core/gimpgradient-load.h new file mode 100644 index 0000000..fcc648c --- /dev/null +++ b/app/core/gimpgradient-load.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 . + */ + +#ifndef __GIMP_GRADIENT_LOAD_H__ +#define __GIMP_GRADIENT_LOAD_H__ + + +#define GIMP_GRADIENT_FILE_EXTENSION ".ggr" +#define GIMP_GRADIENT_SVG_FILE_EXTENSION ".svg" + + +GList * gimp_gradient_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_gradient_load_svg (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_GRADIENT_LOAD_H__ */ diff --git a/app/core/gimpgradient-save.c b/app/core/gimpgradient-save.c new file mode 100644 index 0000000..deca2d2 --- /dev/null +++ b/app/core/gimpgradient-save.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpgradient.h" +#include "gimpgradient-save.h" + +#include "gimp-intl.h" + + +gboolean +gimp_gradient_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpGradient *gradient = GIMP_GRADIENT (data); + GString *string; + GimpGradientSegment *seg; + gint num_segments; + + /* File format is: + * + * GIMP Gradient + * Name: name + * number_of_segments + * left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring left_color_type + * left middle right r0 g0 b0 a0 r1 g1 b1 a1 type coloring right_color_type + * ... + */ + + string = g_string_new ("GIMP Gradient\n"); + + g_string_append_printf (string, "Name: %s\n", + gimp_object_get_name (gradient)); + + /* Count number of segments */ + num_segments = 0; + seg = gradient->segments; + + while (seg) + { + num_segments++; + seg = seg->next; + } + + /* Write rest of file */ + g_string_append_printf (string, "%d\n", num_segments); + + for (seg = gradient->segments; seg; seg = seg->next) + { + gchar buf[11][G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr (buf[0], G_ASCII_DTOSTR_BUF_SIZE, seg->left); + g_ascii_dtostr (buf[1], G_ASCII_DTOSTR_BUF_SIZE, seg->middle); + g_ascii_dtostr (buf[2], G_ASCII_DTOSTR_BUF_SIZE, seg->right); + g_ascii_dtostr (buf[3], G_ASCII_DTOSTR_BUF_SIZE, seg->left_color.r); + g_ascii_dtostr (buf[4], G_ASCII_DTOSTR_BUF_SIZE, seg->left_color.g); + g_ascii_dtostr (buf[5], G_ASCII_DTOSTR_BUF_SIZE, seg->left_color.b); + g_ascii_dtostr (buf[6], G_ASCII_DTOSTR_BUF_SIZE, seg->left_color.a); + g_ascii_dtostr (buf[7], G_ASCII_DTOSTR_BUF_SIZE, seg->right_color.r); + g_ascii_dtostr (buf[8], G_ASCII_DTOSTR_BUF_SIZE, seg->right_color.g); + g_ascii_dtostr (buf[9], G_ASCII_DTOSTR_BUF_SIZE, seg->right_color.b); + g_ascii_dtostr (buf[10], G_ASCII_DTOSTR_BUF_SIZE, seg->right_color.a); + + g_string_append_printf (string, + "%s %s %s %s %s %s %s %s %s %s %s %d %d %d %d\n", + buf[0], buf[1], buf[2], /* left, middle, right */ + buf[3], buf[4], buf[5], buf[6], /* left color */ + buf[7], buf[8], buf[9], buf[10], /* right color */ + (gint) seg->type, + (gint) seg->color, + (gint) seg->left_color_type, + (gint) seg->right_color_type); + } + + if (! 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; +} + +gboolean +gimp_gradient_save_pov (GimpGradient *gradient, + GFile *file, + GError **error) +{ + GOutputStream *output; + GString *string; + GimpGradientSegment *seg; + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + gchar color_buf[4][G_ASCII_DTOSTR_BUF_SIZE]; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + output = G_OUTPUT_STREAM (g_file_replace (file, + NULL, FALSE, G_FILE_CREATE_NONE, + NULL, error)); + if (! output) + return FALSE; + + string = g_string_new ("/* color_map file created by GIMP */\n" + "/* https://www.gimp.org/ */\n" + "color_map {\n"); + + for (seg = gradient->segments; seg; seg = seg->next) + { + /* Left */ + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + seg->left); + g_ascii_dtostr (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, + seg->left_color.r); + g_ascii_dtostr (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, + seg->left_color.g); + g_ascii_dtostr (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, + seg->left_color.b); + g_ascii_dtostr (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, + 1.0 - seg->left_color.a); + + g_string_append_printf (string, + "\t[%s color rgbt <%s, %s, %s, %s>]\n", + buf, + color_buf[0], color_buf[1], + color_buf[2], color_buf[3]); + + /* Middle */ + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + seg->middle); + g_ascii_dtostr (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, + (seg->left_color.r + seg->right_color.r) / 2.0); + g_ascii_dtostr (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, + (seg->left_color.g + seg->right_color.g) / 2.0); + g_ascii_dtostr (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, + (seg->left_color.b + seg->right_color.b) / 2.0); + g_ascii_dtostr (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, + 1.0 - (seg->left_color.a + seg->right_color.a) / 2.0); + + g_string_append_printf (string, + "\t[%s color rgbt <%s, %s, %s, %s>]\n", + buf, + color_buf[0], color_buf[1], + color_buf[2], color_buf[3]); + + /* Right */ + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + seg->right); + g_ascii_dtostr (color_buf[0], G_ASCII_DTOSTR_BUF_SIZE, + seg->right_color.r); + g_ascii_dtostr (color_buf[1], G_ASCII_DTOSTR_BUF_SIZE, + seg->right_color.g); + g_ascii_dtostr (color_buf[2], G_ASCII_DTOSTR_BUF_SIZE, + seg->right_color.b); + g_ascii_dtostr (color_buf[3], G_ASCII_DTOSTR_BUF_SIZE, + 1.0 - seg->right_color.a); + + g_string_append_printf (string, + "\t[%s color rgbt <%s, %s, %s, %s>]\n", + buf, + color_buf[0], color_buf[1], + color_buf[2], color_buf[3]); + } + + g_string_append_printf (string, "} /* color_map */\n"); + + if (! g_output_stream_write_all (output, string->str, string->len, + NULL, NULL, &my_error)) + { + GCancellable *cancellable = g_cancellable_new (); + + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_WRITE, + _("Writing POV file '%s' failed: %s"), + gimp_file_get_utf8_name (file), + my_error->message); + g_clear_error (&my_error); + g_string_free (string, TRUE); + + /* Cancel the overwrite initiated by g_file_replace(). */ + g_cancellable_cancel (cancellable); + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + g_object_unref (output); + + return FALSE; + } + + g_string_free (string, TRUE); + g_object_unref (output); + + return TRUE; +} diff --git a/app/core/gimpgradient-save.h b/app/core/gimpgradient-save.h new file mode 100644 index 0000000..073bdd8 --- /dev/null +++ b/app/core/gimpgradient-save.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 . + */ + +#ifndef __GIMP_GRADIENT_SAVE_H__ +#define __GIMP_GRADIENT_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_gradient_save (GimpData *data, + GOutputStream *output, + GError **error); + +gboolean gimp_gradient_save_pov (GimpGradient *gradient, + GFile *file, + GError **error); + + +#endif /* __GIMP_GRADIENT_SAVE_H__ */ diff --git a/app/core/gimpgradient.c b/app/core/gimpgradient.c new file mode 100644 index 0000000..8265225 --- /dev/null +++ b/app/core/gimpgradient.c @@ -0,0 +1,2297 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpcontext.h" +#include "gimpgradient.h" +#include "gimpgradient-load.h" +#include "gimpgradient-save.h" +#include "gimptagged.h" +#include "gimptempbuf.h" + + +#define EPSILON 1e-10 + + +static void gimp_gradient_tagged_iface_init (GimpTaggedInterface *iface); +static void gimp_gradient_finalize (GObject *object); + +static gint64 gimp_gradient_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_gradient_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_gradient_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_gradient_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + +static const gchar * gimp_gradient_get_extension (GimpData *data); +static void gimp_gradient_copy (GimpData *data, + GimpData *src_data); +static gint gimp_gradient_compare (GimpData *data1, + GimpData *data2); + +static gchar * gimp_gradient_get_checksum (GimpTagged *tagged); + +static inline GimpGradientSegment * + gimp_gradient_get_segment_at_internal (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos); +static void gimp_gradient_get_flat_color (GimpContext *context, + const GimpRGB *color, + GimpGradientColor color_type, + GimpRGB *flat_color); + + +static inline gdouble gimp_gradient_calc_linear_factor (gdouble middle, + gdouble pos); +static inline gdouble gimp_gradient_calc_curved_factor (gdouble middle, + gdouble pos); +static inline gdouble gimp_gradient_calc_sine_factor (gdouble middle, + gdouble pos); +static inline gdouble gimp_gradient_calc_sphere_increasing_factor (gdouble middle, + gdouble pos); +static inline gdouble gimp_gradient_calc_sphere_decreasing_factor (gdouble middle, + gdouble pos); +static inline gdouble gimp_gradient_calc_step_factor (gdouble middle, + gdouble pos); + + +G_DEFINE_TYPE_WITH_CODE (GimpGradient, gimp_gradient, GIMP_TYPE_DATA, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_gradient_tagged_iface_init)) + +#define parent_class gimp_gradient_parent_class + +static const Babl *fish_srgb_to_linear_rgb = NULL; +static const Babl *fish_linear_rgb_to_srgb = NULL; +static const Babl *fish_srgb_to_cie_lab = NULL; +static const Babl *fish_cie_lab_to_srgb = NULL; + + +static void +gimp_gradient_class_init (GimpGradientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->finalize = gimp_gradient_finalize; + + gimp_object_class->get_memsize = gimp_gradient_get_memsize; + + viewable_class->default_icon_name = "gimp-tool-gradient"; + viewable_class->get_preview_size = gimp_gradient_get_preview_size; + viewable_class->get_popup_size = gimp_gradient_get_popup_size; + viewable_class->get_new_preview = gimp_gradient_get_new_preview; + + data_class->save = gimp_gradient_save; + data_class->get_extension = gimp_gradient_get_extension; + data_class->copy = gimp_gradient_copy; + data_class->compare = gimp_gradient_compare; + + fish_srgb_to_linear_rgb = babl_fish (babl_format ("R'G'B' double"), + babl_format ("RGB double")); + fish_linear_rgb_to_srgb = babl_fish (babl_format ("RGB double"), + babl_format ("R'G'B' double")); + fish_srgb_to_cie_lab = babl_fish (babl_format ("R'G'B' double"), + babl_format ("CIE Lab double")); + fish_cie_lab_to_srgb = babl_fish (babl_format ("CIE Lab double"), + babl_format ("R'G'B' double")); +} + +static void +gimp_gradient_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->get_checksum = gimp_gradient_get_checksum; +} + +static void +gimp_gradient_init (GimpGradient *gradient) +{ + gradient->segments = NULL; +} + +static void +gimp_gradient_finalize (GObject *object) +{ + GimpGradient *gradient = GIMP_GRADIENT (object); + + if (gradient->segments) + { + gimp_gradient_segments_free (gradient->segments); + gradient->segments = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_gradient_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpGradient *gradient = GIMP_GRADIENT (object); + GimpGradientSegment *segment; + gint64 memsize = 0; + + for (segment = gradient->segments; segment; segment = segment->next) + memsize += sizeof (GimpGradientSegment); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_gradient_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + *width = size; + *height = 1 + size / 2; +} + +static gboolean +gimp_gradient_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + if (width < 128 || height < 32) + { + *popup_width = 128; + *popup_height = 32; + + return TRUE; + } + + return FALSE; +} + +static GimpTempBuf * +gimp_gradient_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpGradient *gradient = GIMP_GRADIENT (viewable); + GimpGradientSegment *seg = NULL; + GimpTempBuf *temp_buf; + guchar *buf; + guchar *p; + guchar *row; + gint x, y; + gdouble dx, cur_x; + GimpRGB color; + + dx = 1.0 / (width - 1); + cur_x = 0.0; + p = row = g_malloc (width * 4); + + /* Create lines to fill the image */ + + for (x = 0; x < width; x++) + { + seg = gimp_gradient_get_color_at (gradient, context, seg, cur_x, + FALSE, + GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, + &color); + + *p++ = ROUND (color.r * 255.0); + *p++ = ROUND (color.g * 255.0); + *p++ = ROUND (color.b * 255.0); + *p++ = ROUND (color.a * 255.0); + + cur_x += dx; + } + + temp_buf = gimp_temp_buf_new (width, height, babl_format ("R'G'B'A u8")); + + buf = gimp_temp_buf_get_data (temp_buf); + + for (y = 0; y < height; y++) + memcpy (buf + (width * y * 4), row, width * 4); + + g_free (row); + + return temp_buf; +} + +static void +gimp_gradient_copy (GimpData *data, + GimpData *src_data) +{ + GimpGradient *gradient = GIMP_GRADIENT (data); + GimpGradient *src_gradient = GIMP_GRADIENT (src_data); + GimpGradientSegment *head, *prev, *cur, *orig; + + if (gradient->segments) + { + gimp_gradient_segments_free (gradient->segments); + gradient->segments = NULL; + } + + prev = NULL; + orig = src_gradient->segments; + head = NULL; + + while (orig) + { + cur = gimp_gradient_segment_new (); + + *cur = *orig; /* Copy everything */ + + cur->prev = prev; + cur->next = NULL; + + if (prev) + prev->next = cur; + else + head = cur; /* Remember head */ + + prev = cur; + orig = orig->next; + } + + gradient->segments = head; + + gimp_data_dirty (GIMP_DATA (gradient)); +} + +static gint +gimp_gradient_compare (GimpData *data1, + GimpData *data2) +{ + gboolean is_custom1; + gboolean is_custom2; + + /* check whether data1 and data2 are the custom gradient, which is the only + * writable internal gradient. + */ + is_custom1 = gimp_data_is_internal (data1) && gimp_data_is_writable (data1); + is_custom2 = gimp_data_is_internal (data2) && gimp_data_is_writable (data2); + + /* order the custom gradient before all the other gradients; use the default + * ordering for the rest. + */ + if (is_custom1) + { + if (is_custom2) + return 0; + else + return -1; + } + else if (is_custom2) + { + return +1; + } + else + return GIMP_DATA_CLASS (parent_class)->compare (data1, data2); +} + +static gchar * +gimp_gradient_get_checksum (GimpTagged *tagged) +{ + GimpGradient *gradient = GIMP_GRADIENT (tagged); + gchar *checksum_string = NULL; + + if (gradient->segments) + { + GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5); + GimpGradientSegment *segment = gradient->segments; + + while (segment) + { + g_checksum_update (checksum, + (const guchar *) &segment->left, + sizeof (segment->left)); + g_checksum_update (checksum, + (const guchar *) &segment->middle, + sizeof (segment->middle)); + g_checksum_update (checksum, + (const guchar *) &segment->right, + sizeof (segment->right)); + g_checksum_update (checksum, + (const guchar *) &segment->left_color_type, + sizeof (segment->left_color_type)); + g_checksum_update (checksum, + (const guchar *) &segment->left_color, + sizeof (segment->left_color)); + g_checksum_update (checksum, + (const guchar *) &segment->right_color_type, + sizeof (segment->right_color_type)); + g_checksum_update (checksum, + (const guchar *) &segment->right_color, + sizeof (segment->right_color)); + g_checksum_update (checksum, + (const guchar *) &segment->type, + sizeof (segment->type)); + g_checksum_update (checksum, + (const guchar *) &segment->color, + sizeof (segment->color)); + + segment = segment->next; + } + + checksum_string = g_strdup (g_checksum_get_string (checksum)); + + g_checksum_free (checksum); + } + + return checksum_string; +} + + +/* public functions */ + +GimpData * +gimp_gradient_new (GimpContext *context, + const gchar *name) +{ + GimpGradient *gradient; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + gradient = g_object_new (GIMP_TYPE_GRADIENT, + "name", name, + NULL); + + gradient->segments = gimp_gradient_segment_new (); + + return GIMP_DATA (gradient); +} + +GimpData * +gimp_gradient_get_standard (GimpContext *context) +{ + static GimpData *standard_gradient = NULL; + + if (! standard_gradient) + { + standard_gradient = gimp_gradient_new (context, "Standard"); + + gimp_data_clean (standard_gradient); + gimp_data_make_internal (standard_gradient, "gimp-gradient-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_gradient), + (gpointer *) &standard_gradient); + } + + return standard_gradient; +} + +static const gchar * +gimp_gradient_get_extension (GimpData *data) +{ + return GIMP_GRADIENT_FILE_EXTENSION; +} + +/** + * gimp_gradient_get_color_at: + * @gradient: a gradient + * @context: a context + * @seg: a segment to seed the search with (or %NULL) + * @pos: position in the gradient (between 0.0 and 1.0) + * @reverse: when %TRUE, use the reversed gradient + * @blend_color_space: color space to use for blending RGB segments + * @color: returns the color + * + * If you are iterating over an gradient, you should pass the the + * return value from the last call for @seg. + * + * Return value: the gradient segment the color is from + **/ +GimpGradientSegment * +gimp_gradient_get_color_at (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + gdouble pos, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + GimpRGB *color) +{ + gdouble factor = 0.0; + gdouble seg_len; + gdouble middle; + GimpRGB left_color; + GimpRGB right_color; + GimpRGB rgb; + + /* type-check disabled to improve speed */ + /* g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); */ + g_return_val_if_fail (color != NULL, NULL); + + pos = CLAMP (pos, 0.0, 1.0); + + if (reverse) + pos = 1.0 - pos; + + seg = gimp_gradient_get_segment_at_internal (gradient, seg, pos); + + seg_len = seg->right - seg->left; + + if (seg_len < EPSILON) + { + middle = 0.5; + pos = 0.5; + } + else + { + middle = (seg->middle - seg->left) / seg_len; + pos = (pos - seg->left) / seg_len; + } + + switch (seg->type) + { + case GIMP_GRADIENT_SEGMENT_LINEAR: + factor = gimp_gradient_calc_linear_factor (middle, pos); + break; + + case GIMP_GRADIENT_SEGMENT_CURVED: + factor = gimp_gradient_calc_curved_factor (middle, pos); + break; + + case GIMP_GRADIENT_SEGMENT_SINE: + factor = gimp_gradient_calc_sine_factor (middle, pos); + break; + + case GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING: + factor = gimp_gradient_calc_sphere_increasing_factor (middle, pos); + break; + + case GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING: + factor = gimp_gradient_calc_sphere_decreasing_factor (middle, pos); + break; + + case GIMP_GRADIENT_SEGMENT_STEP: + factor = gimp_gradient_calc_step_factor (middle, pos); + break; + + default: + g_warning ("%s: Unknown gradient type %d.", G_STRFUNC, seg->type); + break; + } + + /* Get left/right colors */ + + if (context) + { + gimp_gradient_segment_get_left_flat_color (gradient, + context, seg, &left_color); + + gimp_gradient_segment_get_right_flat_color (gradient, + context, seg, &right_color); + } + else + { + left_color = seg->left_color; + right_color = seg->right_color; + } + + /* Calculate color components */ + + if (seg->color == GIMP_GRADIENT_SEGMENT_RGB) + { + switch (blend_color_space) + { + case GIMP_GRADIENT_BLEND_CIE_LAB: + babl_process (fish_srgb_to_cie_lab, + &left_color, &left_color, 1); + babl_process (fish_srgb_to_cie_lab, + &right_color, &right_color, 1); + break; + + case GIMP_GRADIENT_BLEND_RGB_LINEAR: + babl_process (fish_srgb_to_linear_rgb, + &left_color, &left_color, 1); + babl_process (fish_srgb_to_linear_rgb, + &right_color, &right_color, 1); + + case GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL: + break; + } + + rgb.r = left_color.r + (right_color.r - left_color.r) * factor; + rgb.g = left_color.g + (right_color.g - left_color.g) * factor; + rgb.b = left_color.b + (right_color.b - left_color.b) * factor; + + switch (blend_color_space) + { + case GIMP_GRADIENT_BLEND_CIE_LAB: + babl_process (fish_cie_lab_to_srgb, + &rgb, &rgb, 1); + break; + + case GIMP_GRADIENT_BLEND_RGB_LINEAR: + babl_process (fish_linear_rgb_to_srgb, + &rgb, &rgb, 1); + + case GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL: + break; + } + } + else + { + GimpHSV left_hsv; + GimpHSV right_hsv; + + gimp_rgb_to_hsv (&left_color, &left_hsv); + gimp_rgb_to_hsv (&right_color, &right_hsv); + + left_hsv.s = left_hsv.s + (right_hsv.s - left_hsv.s) * factor; + left_hsv.v = left_hsv.v + (right_hsv.v - left_hsv.v) * factor; + + switch (seg->color) + { + case GIMP_GRADIENT_SEGMENT_HSV_CCW: + if (left_hsv.h < right_hsv.h) + { + left_hsv.h += (right_hsv.h - left_hsv.h) * factor; + } + else + { + left_hsv.h += (1.0 - (left_hsv.h - right_hsv.h)) * factor; + + if (left_hsv.h > 1.0) + left_hsv.h -= 1.0; + } + break; + + case GIMP_GRADIENT_SEGMENT_HSV_CW: + if (right_hsv.h < left_hsv.h) + { + left_hsv.h -= (left_hsv.h - right_hsv.h) * factor; + } + else + { + left_hsv.h -= (1.0 - (right_hsv.h - left_hsv.h)) * factor; + + if (left_hsv.h < 0.0) + left_hsv.h += 1.0; + } + break; + + default: + g_warning ("%s: Unknown coloring mode %d", + G_STRFUNC, (gint) seg->color); + break; + } + + gimp_hsv_to_rgb (&left_hsv, &rgb); + } + + /* Calculate alpha */ + + rgb.a = left_color.a + (right_color.a - left_color.a) * factor; + + *color = rgb; + + return seg; +} + +GimpGradientSegment * +gimp_gradient_get_segment_at (GimpGradient *gradient, + gdouble pos) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); + + return gimp_gradient_get_segment_at_internal (gradient, NULL, pos); +} + +gboolean +gimp_gradient_has_fg_bg_segments (GimpGradient *gradient) +{ + GimpGradientSegment *segment; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), FALSE); + + for (segment = gradient->segments; segment; segment = segment->next) + if (segment->left_color_type != GIMP_GRADIENT_COLOR_FIXED || + segment->right_color_type != GIMP_GRADIENT_COLOR_FIXED) + return TRUE; + + return FALSE; +} + +void +gimp_gradient_split_at (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + gdouble pos, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr) +{ + GimpRGB color; + GimpGradientSegment *newseg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + pos = CLAMP (pos, 0.0, 1.0); + seg = gimp_gradient_get_segment_at_internal (gradient, seg, pos); + + /* Get color at pos */ + gimp_gradient_get_color_at (gradient, context, seg, pos, + FALSE, blend_color_space, &color); + + /* Create a new segment and insert it in the list */ + + newseg = gimp_gradient_segment_new (); + + newseg->prev = seg; + newseg->next = seg->next; + + seg->next = newseg; + + if (newseg->next) + newseg->next->prev = newseg; + + /* Set coordinates of new segment */ + + newseg->left = pos; + newseg->right = seg->right; + newseg->middle = (newseg->left + newseg->right) / 2.0; + + /* Set coordinates of original segment */ + + seg->right = newseg->left; + seg->middle = (seg->left + seg->right) / 2.0; + + /* Set colors of both segments */ + + newseg->right_color_type = seg->right_color_type; + newseg->right_color = seg->right_color; + + seg->right_color_type = newseg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + seg->right_color = newseg->left_color = color; + + /* Set parameters of new segment */ + + newseg->type = seg->type; + newseg->color = seg->color; + + /* Done */ + + if (newl) *newl = seg; + if (newr) *newr = newseg; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +GimpGradient * +gimp_gradient_flatten (GimpGradient *gradient, + GimpContext *context) +{ + GimpGradient *flat; + GimpGradientSegment *seg; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + flat = GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient))); + + for (seg = flat->segments; seg; seg = seg->next) + { + gimp_gradient_segment_get_left_flat_color (gradient, + context, seg, + &seg->left_color); + + seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + + gimp_gradient_segment_get_right_flat_color (gradient, + context, seg, + &seg->right_color); + + seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; + } + + return flat; +} + + +/* gradient segment functions */ + +GimpGradientSegment * +gimp_gradient_segment_new (void) +{ + GimpGradientSegment *seg; + + seg = g_slice_new0 (GimpGradientSegment); + + seg->left = 0.0; + seg->middle = 0.5; + seg->right = 1.0; + + seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + gimp_rgba_set (&seg->left_color, 0.0, 0.0, 0.0, 1.0); + + seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; + gimp_rgba_set (&seg->right_color, 1.0, 1.0, 1.0, 1.0); + + seg->type = GIMP_GRADIENT_SEGMENT_LINEAR; + seg->color = GIMP_GRADIENT_SEGMENT_RGB; + + seg->prev = seg->next = NULL; + + return seg; +} + + +void +gimp_gradient_segment_free (GimpGradientSegment *seg) +{ + g_return_if_fail (seg != NULL); + + g_slice_free (GimpGradientSegment, seg); +} + +void +gimp_gradient_segments_free (GimpGradientSegment *seg) +{ + g_return_if_fail (seg != NULL); + + g_slice_free_chain (GimpGradientSegment, seg, next); +} + +GimpGradientSegment * +gimp_gradient_segment_get_last (GimpGradientSegment *seg) +{ + if (! seg) + return NULL; + + while (seg->next) + seg = seg->next; + + return seg; +} + +GimpGradientSegment * +gimp_gradient_segment_get_first (GimpGradientSegment *seg) +{ + if (! seg) + return NULL; + + while (seg->prev) + seg = seg->prev; + + return seg; +} + +GimpGradientSegment * +gimp_gradient_segment_get_nth (GimpGradientSegment *seg, + gint index) +{ + gint i = 0; + + g_return_val_if_fail (index >= 0, NULL); + + if (! seg) + return NULL; + + while (seg && (i < index)) + { + seg = seg->next; + i++; + } + + if (i == index) + return seg; + + return NULL; +} + +void +gimp_gradient_segment_split_midpoint (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *lseg, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (lseg != NULL); + g_return_if_fail (newl != NULL); + g_return_if_fail (newr != NULL); + + gimp_gradient_split_at (gradient, context, lseg, lseg->middle, + blend_color_space, newl, newr); +} + +void +gimp_gradient_segment_split_uniform (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *lseg, + gint parts, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr) +{ + GimpGradientSegment *seg, *prev, *tmp; + gdouble seg_len; + gint i; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (lseg != NULL); + g_return_if_fail (newl != NULL); + g_return_if_fail (newr != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg_len = (lseg->right - lseg->left) / parts; /* Length of divisions */ + + seg = NULL; + prev = NULL; + tmp = NULL; + + for (i = 0; i < parts; i++) + { + seg = gimp_gradient_segment_new (); + + if (i == 0) + tmp = seg; /* Remember first segment */ + + seg->left = lseg->left + i * seg_len; + seg->right = lseg->left + (i + 1) * seg_len; + seg->middle = (seg->left + seg->right) / 2.0; + + seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; + + gimp_gradient_get_color_at (gradient, context, lseg, + seg->left, FALSE, blend_color_space, + &seg->left_color); + gimp_gradient_get_color_at (gradient, context, lseg, + seg->right, FALSE, blend_color_space, + &seg->right_color); + + seg->type = lseg->type; + seg->color = lseg->color; + + seg->prev = prev; + seg->next = NULL; + + if (prev) + prev->next = seg; + + prev = seg; + } + + /* Fix edges */ + + tmp->left_color_type = lseg->left_color_type; + tmp->left_color = lseg->left_color; + + seg->right_color_type = lseg->right_color_type; + seg->right_color = lseg->right_color; + + tmp->left = lseg->left; + seg->right = lseg->right; /* To squish accumulative error */ + + /* Link in list */ + + tmp->prev = lseg->prev; + seg->next = lseg->next; + + if (lseg->prev) + lseg->prev->next = tmp; + else + gradient->segments = tmp; /* We are on leftmost segment */ + + if (lseg->next) + lseg->next->prev = seg; + + /* Done */ + *newl = tmp; + *newr = seg; + + /* Delete old segment */ + gimp_gradient_segment_free (lseg); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_get_left_color (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + *color = seg->left_color; +} + +void +gimp_gradient_segment_set_left_color (GimpGradient *gradient, + GimpGradientSegment *seg, + const GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_range_blend (gradient, seg, seg, + color, &seg->right_color, + TRUE, TRUE); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_get_right_color (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + *color = seg->right_color; +} + +void +gimp_gradient_segment_set_right_color (GimpGradient *gradient, + GimpGradientSegment *seg, + const GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + gimp_gradient_segment_range_blend (gradient, seg, seg, + &seg->left_color, color, + TRUE, TRUE); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +GimpGradientColor +gimp_gradient_segment_get_left_color_type (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0); + g_return_val_if_fail (seg != NULL, 0); + + return seg->left_color_type; +} + +void +gimp_gradient_segment_set_left_color_type (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpGradientColor color_type) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg->left_color_type = color_type; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +GimpGradientColor +gimp_gradient_segment_get_right_color_type (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0); + g_return_val_if_fail (seg != NULL, 0); + + return seg->right_color_type; +} + +void +gimp_gradient_segment_set_right_color_type (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpGradientColor color_type) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg->right_color_type = color_type; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_get_left_flat_color (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + gimp_gradient_get_flat_color (context, + &seg->left_color, seg->left_color_type, + color); +} + +void +gimp_gradient_segment_get_right_flat_color (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (seg != NULL); + g_return_if_fail (color != NULL); + + gimp_gradient_get_flat_color (context, + &seg->right_color, seg->right_color_type, + color); +} + +gdouble +gimp_gradient_segment_get_left_pos (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + return seg->left; +} + +gdouble +gimp_gradient_segment_set_left_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos) +{ + gdouble final_pos; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + if (seg->prev == NULL) + { + final_pos = 0; + } + else + { + gimp_data_freeze (GIMP_DATA (gradient)); + + final_pos = seg->prev->right = seg->left = + CLAMP (pos, + seg->prev->middle + EPSILON, + seg->middle - EPSILON); + + gimp_data_thaw (GIMP_DATA (gradient)); + } + + return final_pos; +} + +gdouble +gimp_gradient_segment_get_right_pos (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + return seg->right; +} + +gdouble +gimp_gradient_segment_set_right_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos) +{ + gdouble final_pos; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + if (seg->next == NULL) + { + final_pos = 1.0; + } + else + { + gimp_data_freeze (GIMP_DATA (gradient)); + + final_pos = seg->next->left = seg->right = + CLAMP (pos, + seg->middle + EPSILON, + seg->next->middle - EPSILON); + + gimp_data_thaw (GIMP_DATA (gradient)); + } + + return final_pos; +} + +gdouble +gimp_gradient_segment_get_middle_pos (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + return seg->middle; +} + +gdouble +gimp_gradient_segment_set_middle_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos) +{ + gdouble final_pos; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + g_return_val_if_fail (seg != NULL, 0.0); + + gimp_data_freeze (GIMP_DATA (gradient)); + + final_pos = seg->middle = + CLAMP (pos, + seg->left + EPSILON, + seg->right - EPSILON); + + gimp_data_thaw (GIMP_DATA (gradient)); + + return final_pos; +} + +GimpGradientSegmentType +gimp_gradient_segment_get_blending_function (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0); + + return seg->type; +} + +GimpGradientSegmentColor +gimp_gradient_segment_get_coloring_type (GimpGradient *gradient, + GimpGradientSegment *seg) +{ + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0); + + return seg->color; +} + +gint +gimp_gradient_segment_range_get_n_segments (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r) +{ + gint n_segments = 0; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0); + g_return_val_if_fail (range_l != NULL, 0); + + for (; range_l != range_r; range_l = range_l->next) + n_segments++; + + if (range_r != NULL) + n_segments++; + + return n_segments; +} + +void +gimp_gradient_segment_range_compress (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r, + gdouble new_l, + gdouble new_r) +{ + gdouble orig_l, orig_r; + GimpGradientSegment *seg, *aseg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (range_l != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! range_r) + range_r = gimp_gradient_segment_get_last (range_l); + + orig_l = range_l->left; + orig_r = range_r->right; + + if (orig_r - orig_l > EPSILON) + { + gdouble scale; + + scale = (new_r - new_l) / (orig_r - orig_l); + + seg = range_l; + + do + { + if (seg->prev) + seg->left = new_l + (seg->left - orig_l) * scale; + seg->middle = new_l + (seg->middle - orig_l) * scale; + if (seg->next) + seg->right = new_l + (seg->right - orig_l) * scale; + + /* Next */ + + aseg = seg; + seg = seg->next; + } + while (aseg != range_r); + } + else + { + gint n; + gint i; + + n = gimp_gradient_segment_range_get_n_segments (gradient, + range_l, range_r); + + for (i = 0, seg = range_l; i < n; i++, seg = seg->next) + { + if (seg->prev) + seg->left = new_l + (new_r - new_l) * (i + 0.0) / n; + seg->middle = new_l + (new_r - new_l) * (i + 0.5) / n;; + if (seg->next) + seg->right = new_l + (new_r - new_l) * (i + 1.0) / n; + } + } + + /* Make sure that the left and right endpoints of the range are *exactly* + * equal to new_l and new_r; the above computations can introduce + * numerical inaccuracies. + */ + + range_l->left = new_l; + range_l->right = new_r; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_blend (GimpGradient *gradient, + GimpGradientSegment *lseg, + GimpGradientSegment *rseg, + const GimpRGB *rgb1, + const GimpRGB *rgb2, + gboolean blend_colors, + gboolean blend_opacity) +{ + GimpRGB d; + gdouble left, len; + GimpGradientSegment *seg; + GimpGradientSegment *aseg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (lseg != NULL); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! rseg) + rseg = gimp_gradient_segment_get_last (lseg); + + d.r = rgb2->r - rgb1->r; + d.g = rgb2->g - rgb1->g; + d.b = rgb2->b - rgb1->b; + d.a = rgb2->a - rgb1->a; + + left = lseg->left; + len = rseg->right - left; + + seg = lseg; + + do + { + if (blend_colors) + { + seg->left_color.r = rgb1->r + (seg->left - left) / len * d.r; + seg->left_color.g = rgb1->g + (seg->left - left) / len * d.g; + seg->left_color.b = rgb1->b + (seg->left - left) / len * d.b; + + seg->right_color.r = rgb1->r + (seg->right - left) / len * d.r; + seg->right_color.g = rgb1->g + (seg->right - left) / len * d.g; + seg->right_color.b = rgb1->b + (seg->right - left) / len * d.b; + } + + if (blend_opacity) + { + seg->left_color.a = rgb1->a + (seg->left - left) / len * d.a; + seg->right_color.a = rgb1->a + (seg->right - left) / len * d.a; + } + + aseg = seg; + seg = seg->next; + } + while (aseg != rseg); + gimp_data_thaw (GIMP_DATA (gradient)); + +} + +void +gimp_gradient_segment_range_set_blending_function (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegmentType new_type) +{ + GimpGradientSegment *seg; + gboolean reached_last_segment = FALSE; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg = start_seg; + while (seg && ! reached_last_segment) + { + if (seg == end_seg) + reached_last_segment = TRUE; + + seg->type = new_type; + seg = seg->next; + } + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_set_coloring_type (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegmentColor new_color) +{ + GimpGradientSegment *seg; + gboolean reached_last_segment = FALSE; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg = start_seg; + while (seg && ! reached_last_segment) + { + if (seg == end_seg) + reached_last_segment = TRUE; + + seg->color = new_color; + seg = seg->next; + } + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_flip (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + GimpGradientSegment *oseg, *oaseg; + GimpGradientSegment *seg, *prev, *tmp; + GimpGradientSegment *lseg, *rseg; + gdouble left, right; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + left = start_seg->left; + right = end_seg->right; + + /* Build flipped segments */ + + prev = NULL; + oseg = end_seg; + tmp = NULL; + + do + { + seg = gimp_gradient_segment_new (); + + if (prev == NULL) + { + seg->left = left; + tmp = seg; /* Remember first segment */ + } + else + seg->left = left + right - oseg->right; + + seg->middle = left + right - oseg->middle; + seg->right = left + right - oseg->left; + + seg->left_color_type = oseg->right_color_type; + seg->left_color = oseg->right_color; + + seg->right_color_type = oseg->left_color_type; + seg->right_color = oseg->left_color; + + switch (oseg->type) + { + case GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING: + seg->type = GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING; + break; + + case GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING: + seg->type = GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING; + break; + + default: + seg->type = oseg->type; + } + + switch (oseg->color) + { + case GIMP_GRADIENT_SEGMENT_HSV_CCW: + seg->color = GIMP_GRADIENT_SEGMENT_HSV_CW; + break; + + case GIMP_GRADIENT_SEGMENT_HSV_CW: + seg->color = GIMP_GRADIENT_SEGMENT_HSV_CCW; + break; + + default: + seg->color = oseg->color; + } + + seg->prev = prev; + seg->next = NULL; + + if (prev) + prev->next = seg; + + prev = seg; + + oaseg = oseg; + oseg = oseg->prev; /* Move backwards! */ + } + while (oaseg != start_seg); + + seg->right = right; /* Squish accumulative error */ + + /* Free old segments */ + + lseg = start_seg->prev; + rseg = end_seg->next; + + oseg = start_seg; + + do + { + oaseg = oseg->next; + gimp_gradient_segment_free (oseg); + oseg = oaseg; + } + while (oaseg != rseg); + + /* Link in new segments */ + + if (lseg) + lseg->next = tmp; + else + gradient->segments = tmp; + + tmp->prev = lseg; + + seg->next = rseg; + + if (rseg) + rseg->prev = seg; + + /* Reset selection */ + + if (final_start_seg) + *final_start_seg = tmp; + + if (final_end_seg) + *final_end_seg = seg; + + /* Done */ + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_replicate (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + gint replicate_times, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + gdouble sel_left, sel_right, sel_len; + gdouble new_left; + gdouble factor; + GimpGradientSegment *prev, *seg, *tmp; + GimpGradientSegment *oseg, *oaseg; + GimpGradientSegment *lseg, *rseg; + gint i; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + if (replicate_times < 2) + { + *final_start_seg = start_seg; + *final_end_seg = end_seg; + return; + } + + gimp_data_freeze (GIMP_DATA (gradient)); + + /* Remember original parameters */ + sel_left = start_seg->left; + sel_right = end_seg->right; + sel_len = sel_right - sel_left; + + factor = 1.0 / replicate_times; + + /* Build replicated segments */ + + prev = NULL; + seg = NULL; + tmp = NULL; + + for (i = 0; i < replicate_times; i++) + { + /* Build one cycle */ + + new_left = sel_left + i * factor * sel_len; + + oseg = start_seg; + + do + { + seg = gimp_gradient_segment_new (); + + if (prev == NULL) + { + seg->left = sel_left; + tmp = seg; /* Remember first segment */ + } + else + { + seg->left = new_left + factor * (oseg->left - sel_left); + } + + seg->middle = new_left + factor * (oseg->middle - sel_left); + seg->right = new_left + factor * (oseg->right - sel_left); + + seg->left_color_type = oseg->left_color_type; + seg->left_color = oseg->left_color; + + seg->right_color_type = oseg->right_color_type; + seg->right_color = oseg->right_color; + + seg->type = oseg->type; + seg->color = oseg->color; + + seg->prev = prev; + seg->next = NULL; + + if (prev) + prev->next = seg; + + prev = seg; + + oaseg = oseg; + oseg = oseg->next; + } + while (oaseg != end_seg); + } + + seg->right = sel_right; /* Squish accumulative error */ + + /* Free old segments */ + + lseg = start_seg->prev; + rseg = end_seg->next; + + oseg = start_seg; + + do + { + oaseg = oseg->next; + gimp_gradient_segment_free (oseg); + oseg = oaseg; + } + while (oaseg != rseg); + + /* Link in new segments */ + + if (lseg) + lseg->next = tmp; + else + gradient->segments = tmp; + + tmp->prev = lseg; + + seg->next = rseg; + + if (rseg) + rseg->prev = seg; + + /* Reset selection */ + + if (final_start_seg) + *final_start_seg = tmp; + + if (final_end_seg) + *final_end_seg = seg; + + /* Done */ + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_split_midpoint (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + GimpGradientSegment *seg, *lseg, *rseg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + seg = start_seg; + + do + { + gimp_gradient_segment_split_midpoint (gradient, context, + seg, blend_color_space, + &lseg, &rseg); + seg = rseg->next; + } + while (lseg != end_seg); + + if (final_start_seg) + *final_start_seg = start_seg; + + if (final_end_seg) + *final_end_seg = rseg; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_split_uniform (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + gint parts, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + GimpGradientSegment *seg, *aseg, *lseg, *rseg, *lsel; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + if (parts < 2) + { + *final_start_seg = start_seg; + *final_end_seg = end_seg; + return; + } + + gimp_data_freeze (GIMP_DATA (gradient)); + + seg = start_seg; + lsel = NULL; + + do + { + aseg = seg; + + gimp_gradient_segment_split_uniform (gradient, context, seg, + parts, blend_color_space, + &lseg, &rseg); + + if (seg == start_seg) + lsel = lseg; + + seg = rseg->next; + } + while (aseg != end_seg); + + if (final_start_seg) + *final_start_seg = lsel; + + if (final_end_seg) + *final_end_seg = rseg; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_delete (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + GimpGradientSegment *lseg, *rseg, *seg, *aseg, *next; + gdouble join; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + /* Remember segments to the left and to the right of the selection */ + + lseg = start_seg->prev; + rseg = end_seg->next; + + /* Cannot delete all the segments in the gradient */ + + if ((lseg == NULL) && (rseg == NULL)) + goto premature_return; + + gimp_data_freeze (GIMP_DATA (gradient)); + + /* Calculate join point */ + + join = (start_seg->left + + end_seg->right) / 2.0; + + if (lseg == NULL) + join = 0.0; + else if (rseg == NULL) + join = 1.0; + + /* Move segments */ + + if (lseg != NULL) + gimp_gradient_segment_range_compress (gradient, lseg, lseg, + lseg->left, join); + + if (rseg != NULL) + gimp_gradient_segment_range_compress (gradient, rseg, rseg, + join, rseg->right); + + /* Link */ + + if (lseg) + lseg->next = rseg; + + if (rseg) + rseg->prev = lseg; + + /* Delete old segments */ + + seg = start_seg; + + do + { + next = seg->next; + aseg = seg; + + gimp_gradient_segment_free (seg); + + seg = next; + } + while (aseg != end_seg); + + /* Change selection */ + + if (rseg) + { + if (final_start_seg) + *final_start_seg = rseg; + + if (final_end_seg) + *final_end_seg = rseg; + } + else + { + if (final_start_seg) + *final_start_seg = lseg; + + if (final_end_seg) + *final_end_seg = lseg; + } + + if (lseg == NULL) + gradient->segments = rseg; + + gimp_data_thaw (GIMP_DATA (gradient)); + + return; + + premature_return: + if (final_start_seg) + *final_start_seg = start_seg; + if (final_end_seg) + *final_end_seg = end_seg; +} + +void +gimp_gradient_segment_range_merge (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg) +{ + GimpGradientSegment *seg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + gimp_data_freeze (GIMP_DATA (gradient)); + + /* Copy the end segment's right position and color to the start segment */ + + start_seg->right = end_seg->right; + start_seg->right_color_type = end_seg->right_color_type; + start_seg->right_color = end_seg->right_color; + + /* Center the start segment's midpoint */ + + start_seg->middle = (start_seg->left + start_seg->right) / 2.0; + + /* Remove range segments past the start segment from the segment list */ + + start_seg->next = end_seg->next; + + if (start_seg->next) + start_seg->next->prev = start_seg; + + /* Merge the range's blend function and coloring type, and free the rest of + * the segments. + */ + + seg = end_seg; + + while (seg != start_seg) + { + GimpGradientSegment *prev = seg->prev; + + /* If the blend function and/or coloring type aren't uniform, reset them. */ + + if (seg->type != start_seg->type) + start_seg->type = GIMP_GRADIENT_SEGMENT_LINEAR; + + if (seg->color != start_seg->color) + start_seg->color = GIMP_GRADIENT_SEGMENT_RGB; + + gimp_gradient_segment_free (seg); + + seg = prev; + } + + if (final_start_seg) + *final_start_seg = start_seg; + if (final_end_seg) + *final_end_seg = start_seg; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_recenter_handles (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg) +{ + GimpGradientSegment *seg, *aseg; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + seg = start_seg; + + do + { + seg->middle = (seg->left + seg->right) / 2.0; + + aseg = seg; + seg = seg->next; + } + while (aseg != end_seg); + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +void +gimp_gradient_segment_range_redistribute_handles (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg) +{ + GimpGradientSegment *seg, *aseg; + gdouble left, right, seg_len; + gint num_segs; + gint i; + + g_return_if_fail (GIMP_IS_GRADIENT (gradient)); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! end_seg) + end_seg = gimp_gradient_segment_get_last (start_seg); + + /* Count number of segments in selection */ + + num_segs = 0; + seg = start_seg; + + do + { + num_segs++; + aseg = seg; + seg = seg->next; + } + while (aseg != end_seg); + + /* Calculate new segment length */ + + left = start_seg->left; + right = end_seg->right; + seg_len = (right - left) / num_segs; + + /* Redistribute */ + + seg = start_seg; + + for (i = 0; i < num_segs; i++) + { + seg->left = left + i * seg_len; + seg->right = left + (i + 1) * seg_len; + seg->middle = (seg->left + seg->right) / 2.0; + + seg = seg->next; + } + + /* Fix endpoints to squish accumulative error */ + + start_seg->left = left; + end_seg->right = right; + + gimp_data_thaw (GIMP_DATA (gradient)); +} + +gdouble +gimp_gradient_segment_range_move (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r, + gdouble delta, + gboolean control_compress) +{ + gdouble lbound, rbound; + gint is_first, is_last; + GimpGradientSegment *seg, *aseg; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), 0.0); + + gimp_data_freeze (GIMP_DATA (gradient)); + + if (! range_r) + range_r = gimp_gradient_segment_get_last (range_l); + + /* First or last segments in gradient? */ + + is_first = (range_l->prev == NULL); + is_last = (range_r->next == NULL); + + /* Calculate drag bounds */ + + if (! control_compress) + { + if (!is_first) + lbound = range_l->prev->middle + EPSILON; + else + lbound = range_l->left + EPSILON; + + if (!is_last) + rbound = range_r->next->middle - EPSILON; + else + rbound = range_r->right - EPSILON; + } + else + { + if (!is_first) + lbound = range_l->prev->left + 2.0 * EPSILON; + else + lbound = range_l->left + EPSILON; + + if (!is_last) + rbound = range_r->next->right - 2.0 * EPSILON; + else + rbound = range_r->right - EPSILON; + } + + /* Fix the delta if necessary */ + + if (delta < 0.0) + { + if (!is_first) + { + if (range_l->left + delta < lbound) + delta = lbound - range_l->left; + } + else + if (range_l->middle + delta < lbound) + delta = lbound - range_l->middle; + } + else + { + if (!is_last) + { + if (range_r->right + delta > rbound) + delta = rbound - range_r->right; + } + else + if (range_r->middle + delta > rbound) + delta = rbound - range_r->middle; + } + + /* Move all the segments inside the range */ + + seg = range_l; + + do + { + if (!((seg == range_l) && is_first)) + seg->left += delta; + + seg->middle += delta; + + if (!((seg == range_r) && is_last)) + seg->right += delta; + + /* Next */ + + aseg = seg; + seg = seg->next; + } + while (aseg != range_r); + + /* Fix the segments that surround the range */ + + if (!is_first) + { + if (! control_compress) + range_l->prev->right = range_l->left; + else + gimp_gradient_segment_range_compress (gradient, + range_l->prev, range_l->prev, + range_l->prev->left, range_l->left); + } + + if (!is_last) + { + if (! control_compress) + range_r->next->left = range_r->right; + else + gimp_gradient_segment_range_compress (gradient, + range_r->next, range_r->next, + range_r->right, range_r->next->right); + } + + gimp_data_thaw (GIMP_DATA (gradient)); + + return delta; +} + + +/* private functions */ + +static inline GimpGradientSegment * +gimp_gradient_get_segment_at_internal (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos) +{ + /* handle FP imprecision at the edges of the gradient */ + pos = CLAMP (pos, 0.0, 1.0); + + if (! seg) + seg = gradient->segments; + + if (pos >= seg->left) + { + while (seg->next && pos >= seg->right) + seg = seg->next; + } + else + { + do + seg = seg->prev; + while (pos < seg->left); + } + + return seg; +} + +static void +gimp_gradient_get_flat_color (GimpContext *context, + const GimpRGB *color, + GimpGradientColor color_type, + GimpRGB *flat_color) +{ + switch (color_type) + { + case GIMP_GRADIENT_COLOR_FIXED: + *flat_color = *color; + break; + + case GIMP_GRADIENT_COLOR_FOREGROUND: + case GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT: + gimp_context_get_foreground (context, flat_color); + + if (color_type == GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT) + gimp_rgb_set_alpha (flat_color, 0.0); + break; + + case GIMP_GRADIENT_COLOR_BACKGROUND: + case GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT: + gimp_context_get_background (context, flat_color); + + if (color_type == GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT) + gimp_rgb_set_alpha (flat_color, 0.0); + break; + } +} + +static inline gdouble +gimp_gradient_calc_linear_factor (gdouble middle, + gdouble pos) +{ + if (pos <= middle) + { + if (middle < EPSILON) + return 0.0; + else + return 0.5 * pos / middle; + } + else + { + pos -= middle; + middle = 1.0 - middle; + + if (middle < EPSILON) + return 1.0; + else + return 0.5 + 0.5 * pos / middle; + } +} + +static inline gdouble +gimp_gradient_calc_curved_factor (gdouble middle, + gdouble pos) +{ + if (middle < EPSILON) + return 1.0; + else if (1.0 - middle < EPSILON) + return 0.0; + + return exp (-G_LN2 * log (pos) / log (middle)); +} + +static inline gdouble +gimp_gradient_calc_sine_factor (gdouble middle, + gdouble pos) +{ + pos = gimp_gradient_calc_linear_factor (middle, pos); + + return (sin ((-G_PI / 2.0) + G_PI * pos) + 1.0) / 2.0; +} + +static inline gdouble +gimp_gradient_calc_sphere_increasing_factor (gdouble middle, + gdouble pos) +{ + pos = gimp_gradient_calc_linear_factor (middle, pos) - 1.0; + + /* Works for convex increasing and concave decreasing */ + return sqrt (1.0 - pos * pos); +} + +static inline gdouble +gimp_gradient_calc_sphere_decreasing_factor (gdouble middle, + gdouble pos) +{ + pos = gimp_gradient_calc_linear_factor (middle, pos); + + /* Works for convex decreasing and concave increasing */ + return 1.0 - sqrt(1.0 - pos * pos); +} + +static inline gdouble +gimp_gradient_calc_step_factor (gdouble middle, + gdouble pos) +{ + return pos >= middle; +} diff --git a/app/core/gimpgradient.h b/app/core/gimpgradient.h new file mode 100644 index 0000000..8aa817d --- /dev/null +++ b/app/core/gimpgradient.h @@ -0,0 +1,296 @@ +/* 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 . + */ + +#ifndef __GIMP_GRADIENT_H__ +#define __GIMP_GRADIENT_H__ + + +#include "gimpdata.h" + + +#define GIMP_GRADIENT_DEFAULT_SAMPLE_SIZE 40 + + +struct _GimpGradientSegment +{ + gdouble left, middle, right; + + GimpGradientColor left_color_type; + GimpRGB left_color; + GimpGradientColor right_color_type; + GimpRGB right_color; + + GimpGradientSegmentType type; /* Segment's blending function */ + GimpGradientSegmentColor color; /* Segment's coloring type */ + + GimpGradientSegment *prev; + GimpGradientSegment *next; +}; + + +#define GIMP_TYPE_GRADIENT (gimp_gradient_get_type ()) +#define GIMP_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GRADIENT, GimpGradient)) +#define GIMP_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GRADIENT, GimpGradientClass)) +#define GIMP_IS_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GRADIENT)) +#define GIMP_IS_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GRADIENT)) +#define GIMP_GRADIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GRADIENT, GimpGradientClass)) + + +typedef struct _GimpGradientClass GimpGradientClass; + +struct _GimpGradient +{ + GimpData parent_instance; + + GimpGradientSegment *segments; +}; + +struct _GimpGradientClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_gradient_get_type (void) G_GNUC_CONST; + +GimpData * gimp_gradient_new (GimpContext *context, + const gchar *name); +GimpData * gimp_gradient_get_standard (GimpContext *context); + +GimpGradientSegment * gimp_gradient_get_color_at (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + gdouble pos, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + GimpRGB *color); +GimpGradientSegment * gimp_gradient_get_segment_at (GimpGradient *grad, + gdouble pos); +void gimp_gradient_split_at (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + gdouble pos, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr); + +gboolean gimp_gradient_has_fg_bg_segments (GimpGradient *gradient); +GimpGradient * gimp_gradient_flatten (GimpGradient *gradient, + GimpContext *context); + + +/* gradient segment functions */ + +GimpGradientSegment * gimp_gradient_segment_new (void); +GimpGradientSegment * gimp_gradient_segment_get_last (GimpGradientSegment *seg); +GimpGradientSegment * gimp_gradient_segment_get_first (GimpGradientSegment *seg); +GimpGradientSegment * gimp_gradient_segment_get_nth (GimpGradientSegment *seg, + gint index); + +void gimp_gradient_segment_free (GimpGradientSegment *seg); +void gimp_gradient_segments_free (GimpGradientSegment *seg); + +void gimp_gradient_segment_split_midpoint (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *lseg, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr); +void gimp_gradient_segment_split_uniform (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *lseg, + gint parts, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **newl, + GimpGradientSegment **newr); + +/* Colors Setting/Getting Routines */ +void gimp_gradient_segment_get_left_color (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpRGB *color); + +void gimp_gradient_segment_set_left_color (GimpGradient *gradient, + GimpGradientSegment *seg, + const GimpRGB *color); + + +void gimp_gradient_segment_get_right_color (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpRGB *color); + +void gimp_gradient_segment_set_right_color (GimpGradient *gradient, + GimpGradientSegment *seg, + const GimpRGB *color); + + +GimpGradientColor +gimp_gradient_segment_get_left_color_type (GimpGradient *gradient, + GimpGradientSegment *seg); + +void +gimp_gradient_segment_set_left_color_type (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpGradientColor color_type); + + +GimpGradientColor +gimp_gradient_segment_get_right_color_type (GimpGradient *gradient, + GimpGradientSegment *seg); + +void +gimp_gradient_segment_set_right_color_type (GimpGradient *gradient, + GimpGradientSegment *seg, + GimpGradientColor color_type); + + +void +gimp_gradient_segment_get_left_flat_color (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + GimpRGB *color); + + +void +gimp_gradient_segment_get_right_flat_color (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *seg, + GimpRGB *color); + + +/* Position Setting/Getting Routines */ +/* (Setters return the position after it was set) */ +gdouble gimp_gradient_segment_get_left_pos (GimpGradient *gradient, + GimpGradientSegment *seg); +gdouble gimp_gradient_segment_set_left_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos); + +gdouble gimp_gradient_segment_get_right_pos (GimpGradient *gradient, + GimpGradientSegment *seg); +gdouble gimp_gradient_segment_set_right_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos); + +gdouble gimp_gradient_segment_get_middle_pos (GimpGradient *gradient, + GimpGradientSegment *seg); +gdouble gimp_gradient_segment_set_middle_pos (GimpGradient *gradient, + GimpGradientSegment *seg, + gdouble pos); + +/* Getting/Setting the Blending Function/Coloring Type */ +GimpGradientSegmentType +gimp_gradient_segment_get_blending_function (GimpGradient *gradient, + GimpGradientSegment *seg); +GimpGradientSegmentColor +gimp_gradient_segment_get_coloring_type (GimpGradient *gradient, + GimpGradientSegment *seg); + +/* + * If the second segment is NULL, these functions will process + * until the end of the string. + * */ +gint gimp_gradient_segment_range_get_n_segments + (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r); + +void gimp_gradient_segment_range_compress (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r, + gdouble new_l, + gdouble new_r); +void gimp_gradient_segment_range_blend (GimpGradient *gradient, + GimpGradientSegment *lseg, + GimpGradientSegment *rseg, + const GimpRGB *rgb1, + const GimpRGB *rgb2, + gboolean blend_colors, + gboolean blend_opacity); + +void gimp_gradient_segment_range_set_blending_function + (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegmentType new_type); + +void gimp_gradient_segment_range_set_coloring_type + (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegmentColor new_color); + +void gimp_gradient_segment_range_flip (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_replicate (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + gint replicate_times, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_split_midpoint + (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_split_uniform + (GimpGradient *gradient, + GimpContext *context, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + gint parts, + GimpGradientBlendColorSpace blend_color_space, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_delete (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_merge (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg, + GimpGradientSegment **final_start_seg, + GimpGradientSegment **final_end_seg); + +void gimp_gradient_segment_range_recenter_handles + (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg); +void gimp_gradient_segment_range_redistribute_handles + (GimpGradient *gradient, + GimpGradientSegment *start_seg, + GimpGradientSegment *end_seg); + +gdouble gimp_gradient_segment_range_move (GimpGradient *gradient, + GimpGradientSegment *range_l, + GimpGradientSegment *range_r, + gdouble delta, + gboolean control_compress); + + +#endif /* __GIMP_GRADIENT_H__ */ diff --git a/app/core/gimpgrid.c b/app/core/gimpgrid.c new file mode 100644 index 0000000..d500113 --- /dev/null +++ b/app/core/gimpgrid.c @@ -0,0 +1,359 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGrid + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimpgrid.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_STYLE, + PROP_FGCOLOR, + PROP_BGCOLOR, + PROP_XSPACING, + PROP_YSPACING, + PROP_SPACING_UNIT, + PROP_XOFFSET, + PROP_YOFFSET, + PROP_OFFSET_UNIT +}; + + +static void gimp_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_CODE (GimpGrid, gimp_grid, GIMP_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + + +static void +gimp_grid_class_init (GimpGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpRGB black; + GimpRGB white; + + object_class->get_property = gimp_grid_get_property; + object_class->set_property = gimp_grid_set_property; + + gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); + gimp_rgba_set (&white, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_STYLE, + "style", + _("Line style"), + _("Line style used for the grid."), + GIMP_TYPE_GRID_STYLE, + GIMP_GRID_SOLID, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_FGCOLOR, + "fgcolor", + _("Foreground color"), + _("The foreground color of the grid."), + TRUE, &black, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RGB (object_class, PROP_BGCOLOR, + "bgcolor", + _("Background color"), + _("The background color of the grid; " + "only used in double dashed line style."), + TRUE, &white, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_XSPACING, + "xspacing", + _("Spacing X"), + _("Horizontal spacing of grid lines."), + 0.0, GIMP_MAX_IMAGE_SIZE, 10.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YSPACING, + "yspacing", + _("Spacing Y"), + _("Vertical spacing of grid lines."), + 0.0, GIMP_MAX_IMAGE_SIZE, 10.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_UNIT (object_class, PROP_SPACING_UNIT, + "spacing-unit", + _("Spacing unit"), + NULL, + FALSE, FALSE, GIMP_UNIT_INCH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_XOFFSET, + "xoffset", + _("Offset X"), + _("Horizontal offset of the first grid " + "line; this may be a negative number."), + - GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YOFFSET, + "yoffset", + _("Offset Y"), + _("Vertical offset of the first grid " + "line; this may be a negative number."), + - GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_UNIT (object_class, PROP_OFFSET_UNIT, + "offset-unit", + _("Offset unit"), + NULL, + FALSE, FALSE, GIMP_UNIT_INCH, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_grid_init (GimpGrid *grid) +{ +} + +static void +gimp_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpGrid *grid = GIMP_GRID (object); + + switch (property_id) + { + case PROP_STYLE: + g_value_set_enum (value, grid->style); + break; + case PROP_FGCOLOR: + g_value_set_boxed (value, &grid->fgcolor); + break; + case PROP_BGCOLOR: + g_value_set_boxed (value, &grid->bgcolor); + break; + case PROP_XSPACING: + g_value_set_double (value, grid->xspacing); + break; + case PROP_YSPACING: + g_value_set_double (value, grid->yspacing); + break; + case PROP_SPACING_UNIT: + g_value_set_int (value, grid->spacing_unit); + break; + case PROP_XOFFSET: + g_value_set_double (value, grid->xoffset); + break; + case PROP_YOFFSET: + g_value_set_double (value, grid->yoffset); + break; + case PROP_OFFSET_UNIT: + g_value_set_int (value, grid->offset_unit); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpGrid *grid = GIMP_GRID (object); + GimpRGB *color; + + switch (property_id) + { + case PROP_STYLE: + grid->style = g_value_get_enum (value); + break; + case PROP_FGCOLOR: + color = g_value_get_boxed (value); + grid->fgcolor = *color; + break; + case PROP_BGCOLOR: + color = g_value_get_boxed (value); + grid->bgcolor = *color; + break; + case PROP_XSPACING: + grid->xspacing = g_value_get_double (value); + break; + case PROP_YSPACING: + grid->yspacing = g_value_get_double (value); + break; + case PROP_SPACING_UNIT: + grid->spacing_unit = g_value_get_int (value); + break; + case PROP_XOFFSET: + grid->xoffset = g_value_get_double (value); + break; + case PROP_YOFFSET: + grid->yoffset = g_value_get_double (value); + break; + case PROP_OFFSET_UNIT: + grid->offset_unit = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +GimpGridStyle +gimp_grid_get_style (GimpGrid *grid) +{ + g_return_val_if_fail (GIMP_IS_GRID (grid), GIMP_GRID_SOLID); + + return grid->style; +} + +void +gimp_grid_get_fgcolor (GimpGrid *grid, + GimpRGB *fgcolor) +{ + g_return_if_fail (GIMP_IS_GRID (grid)); + g_return_if_fail (fgcolor != NULL); + + *fgcolor = grid->fgcolor; +} + +void +gimp_grid_get_bgcolor (GimpGrid *grid, + GimpRGB *bgcolor) +{ + g_return_if_fail (GIMP_IS_GRID (grid)); + g_return_if_fail (bgcolor != NULL); + + *bgcolor = grid->bgcolor; +} + +void +gimp_grid_get_spacing (GimpGrid *grid, + gdouble *xspacing, + gdouble *yspacing) +{ + g_return_if_fail (GIMP_IS_GRID (grid)); + + if (xspacing) *xspacing = grid->xspacing; + if (yspacing) *yspacing = grid->yspacing; +} + +void +gimp_grid_get_offset (GimpGrid *grid, + gdouble *xoffset, + gdouble *yoffset) +{ + g_return_if_fail (GIMP_IS_GRID (grid)); + + if (xoffset) *xoffset = grid->xoffset; + if (yoffset) *yoffset = grid->yoffset; +} + +const gchar * +gimp_grid_parasite_name (void) +{ + return "gimp-image-grid"; +} + +GimpParasite * +gimp_grid_to_parasite (GimpGrid *grid) +{ + GimpParasite *parasite; + gchar *str; + + g_return_val_if_fail (GIMP_IS_GRID (grid), NULL); + + str = gimp_config_serialize_to_string (GIMP_CONFIG (grid), NULL); + g_return_val_if_fail (str != NULL, NULL); + + parasite = gimp_parasite_new (gimp_grid_parasite_name (), + GIMP_PARASITE_PERSISTENT, + strlen (str) + 1, str); + g_free (str); + + return parasite; +} + +GimpGrid * +gimp_grid_from_parasite (const GimpParasite *parasite) +{ + GimpGrid *grid; + const gchar *str; + GError *error = NULL; + + g_return_val_if_fail (parasite != NULL, NULL); + g_return_val_if_fail (strcmp (gimp_parasite_name (parasite), + gimp_grid_parasite_name ()) == 0, NULL); + + str = gimp_parasite_data (parasite); + + if (! str) + { + g_warning ("Empty grid parasite"); + + return NULL; + } + + grid = g_object_new (GIMP_TYPE_GRID, NULL); + + if (! gimp_config_deserialize_string (GIMP_CONFIG (grid), + str, + gimp_parasite_data_size (parasite), + NULL, + &error)) + { + g_warning ("Failed to deserialize grid parasite: %s", error->message); + g_error_free (error); + } + + return grid; +} diff --git a/app/core/gimpgrid.h b/app/core/gimpgrid.h new file mode 100644 index 0000000..b7d45b2 --- /dev/null +++ b/app/core/gimpgrid.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGrid + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GRID_H__ +#define __GIMP_GRID_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_GRID (gimp_grid_get_type ()) +#define GIMP_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GRID, GimpGrid)) +#define GIMP_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GRID, GimpGridClass)) +#define GIMP_IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GRID)) +#define GIMP_IS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GRID)) +#define GIMP_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GRID, GimpGridClass)) + + +typedef struct _GimpGridClass GimpGridClass; + +struct _GimpGrid +{ + GimpObject parent_instance; + + GimpGridStyle style; + GimpRGB fgcolor; + GimpRGB bgcolor; + gdouble xspacing; + gdouble yspacing; + GimpUnit spacing_unit; + gdouble xoffset; + gdouble yoffset; + GimpUnit offset_unit; +}; + + +struct _GimpGridClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_grid_get_type (void) G_GNUC_CONST; + +GimpGridStyle gimp_grid_get_style (GimpGrid *grid); + +void gimp_grid_get_fgcolor (GimpGrid *grid, + GimpRGB *fgcolor); +void gimp_grid_get_bgcolor (GimpGrid *grid, + GimpRGB *bgcolor); + +void gimp_grid_get_spacing (GimpGrid *grid, + gdouble *xspacing, + gdouble *yspacing); +void gimp_grid_get_offset (GimpGrid *grid, + gdouble *xoffset, + gdouble *yoffset); + +const gchar * gimp_grid_parasite_name (void) G_GNUC_CONST; +GimpParasite * gimp_grid_to_parasite (GimpGrid *grid); +GimpGrid * gimp_grid_from_parasite (const GimpParasite *parasite); + + +#endif /* __GIMP_GRID_H__ */ diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c new file mode 100644 index 0000000..b0de646 --- /dev/null +++ b/app/core/gimpgrouplayer.c @@ -0,0 +1,2297 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGroupLayer + * Copyright (C) 2009 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimpdrawable-filters.h" +#include "gimpgrouplayer.h" +#include "gimpgrouplayerundo.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayerstack.h" +#include "gimpobjectqueue.h" +#include "gimppickable.h" +#include "gimpprogress.h" +#include "gimpprojectable.h" +#include "gimpprojection.h" + +#include "gimp-intl.h" + + +typedef struct _GimpGroupLayerPrivate GimpGroupLayerPrivate; + +struct _GimpGroupLayerPrivate +{ + GimpContainer *children; + GimpProjection *projection; + GeglNode *source_node; + GeglNode *parent_source_node; + GeglNode *graph; + GeglNode *offset_node; + GeglRectangle bounding_box; + gint suspend_resize; + gint suspend_mask; + GeglBuffer *suspended_mask_buffer; + GeglRectangle suspended_mask_bounds; + gint direct_update; + gint transforming; + gboolean expanded; + gboolean pass_through; + + /* hackish temp states to make the projection/tiles stuff work */ + const Babl *convert_format; + gboolean reallocate_projection; +}; + +#define GET_PRIVATE(item) ((GimpGroupLayerPrivate *) gimp_group_layer_get_instance_private ((GimpGroupLayer *) (item))) + + +static void gimp_projectable_iface_init (GimpProjectableInterface *iface); +static void gimp_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_group_layer_finalize (GObject *object); +static void gimp_group_layer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_group_layer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_group_layer_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_group_layer_ancestry_changed (GimpViewable *viewable); +static gboolean gimp_group_layer_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static GimpContainer * gimp_group_layer_get_children (GimpViewable *viewable); +static gboolean gimp_group_layer_get_expanded (GimpViewable *viewable); +static void gimp_group_layer_set_expanded (GimpViewable *viewable, + gboolean expanded); + +static gboolean gimp_group_layer_is_position_locked (GimpItem *item); +static GimpItem * gimp_group_layer_duplicate (GimpItem *item, + GType new_type); +static void gimp_group_layer_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type); +static void gimp_group_layer_start_transform (GimpItem *item, + gboolean push_undo); +static void gimp_group_layer_end_transform (GimpItem *item, + gboolean push_undo); +static void gimp_group_layer_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static GimpTransformResize + gimp_group_layer_get_clip (GimpItem *item, + GimpTransformResize clip_result); + +static gint64 gimp_group_layer_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height); +static void gimp_group_layer_update_all (GimpDrawable *drawable); + +static void gimp_group_layer_translate (GimpLayer *layer, + gint offset_x, + gint offset_y); +static void gimp_group_layer_scale (GimpLayer *layer, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_group_layer_flip (GimpLayer *layer, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +static void gimp_group_layer_rotate (GimpLayer *layer, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +static void gimp_group_layer_transform (GimpLayer *layer, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); +static void gimp_group_layer_convert_type (GimpLayer *layer, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); +static GeglNode * gimp_group_layer_get_source_node (GimpDrawable *drawable); + +static void gimp_group_layer_opacity_changed (GimpLayer *layer); +static void gimp_group_layer_effective_mode_changed (GimpLayer *layer); +static void + gimp_group_layer_excludes_backdrop_changed (GimpLayer *layer); +static GeglRectangle + gimp_group_layer_get_bounding_box (GimpLayer *layer); +static void gimp_group_layer_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode); +static gboolean + gimp_group_layer_get_excludes_backdrop (GimpLayer *layer); + +static const Babl * gimp_group_layer_get_format (GimpProjectable *projectable); +static GeglRectangle + gimp_group_layer_projectable_get_bounding_box (GimpProjectable *projectable); +static GeglNode * gimp_group_layer_get_graph (GimpProjectable *projectable); +static void gimp_group_layer_begin_render (GimpProjectable *projectable); +static void gimp_group_layer_end_render (GimpProjectable *projectable); + +static void gimp_group_layer_pickable_flush (GimpPickable *pickable); +static gdouble gimp_group_layer_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); + + +static void gimp_group_layer_child_add (GimpContainer *container, + GimpLayer *child, + GimpGroupLayer *group); +static void gimp_group_layer_child_remove (GimpContainer *container, + GimpLayer *child, + GimpGroupLayer *group); +static void gimp_group_layer_child_move (GimpLayer *child, + GParamSpec *pspec, + GimpGroupLayer *group); +static void gimp_group_layer_child_resize (GimpLayer *child, + GimpGroupLayer *group); +static void gimp_group_layer_child_active_changed (GimpLayer *child, + GimpGroupLayer *group); +static void + gimp_group_layer_child_effective_mode_changed (GimpLayer *child, + GimpGroupLayer *group); +static void + gimp_group_layer_child_excludes_backdrop_changed (GimpLayer *child, + GimpGroupLayer *group); + +static void gimp_group_layer_flush (GimpGroupLayer *group); +static void gimp_group_layer_update (GimpGroupLayer *group); +static void gimp_group_layer_update_size (GimpGroupLayer *group); +static void gimp_group_layer_update_mask_size (GimpGroupLayer *group); +static void gimp_group_layer_update_source_node (GimpGroupLayer *group); +static void gimp_group_layer_update_mode_node (GimpGroupLayer *group); + +static void gimp_group_layer_stack_update (GimpDrawableStack *stack, + gint x, + gint y, + gint width, + gint height, + GimpGroupLayer *group); +static void gimp_group_layer_proj_update (GimpProjection *proj, + gboolean now, + gint x, + gint y, + gint width, + gint height, + GimpGroupLayer *group); + + +G_DEFINE_TYPE_WITH_CODE (GimpGroupLayer, gimp_group_layer, GIMP_TYPE_LAYER, + G_ADD_PRIVATE (GimpGroupLayer) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROJECTABLE, + gimp_projectable_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_pickable_iface_init)) + + +#define parent_class gimp_group_layer_parent_class + + +/* disable pass-through groups strength-reduction to normal groups. + * see gimp_group_layer_get_effective_mode(). + */ +static gboolean no_pass_through_strength_reduction = FALSE; + + +static void +gimp_group_layer_class_init (GimpGroupLayerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + GimpLayerClass *layer_class = GIMP_LAYER_CLASS (klass); + + object_class->set_property = gimp_group_layer_set_property; + object_class->get_property = gimp_group_layer_get_property; + object_class->finalize = gimp_group_layer_finalize; + + gimp_object_class->get_memsize = gimp_group_layer_get_memsize; + + viewable_class->default_icon_name = "gimp-group-layer"; + viewable_class->ancestry_changed = gimp_group_layer_ancestry_changed; + viewable_class->get_size = gimp_group_layer_get_size; + viewable_class->get_children = gimp_group_layer_get_children; + viewable_class->set_expanded = gimp_group_layer_set_expanded; + viewable_class->get_expanded = gimp_group_layer_get_expanded; + + item_class->is_position_locked = gimp_group_layer_is_position_locked; + item_class->duplicate = gimp_group_layer_duplicate; + item_class->convert = gimp_group_layer_convert; + item_class->start_transform = gimp_group_layer_start_transform; + item_class->end_transform = gimp_group_layer_end_transform; + item_class->resize = gimp_group_layer_resize; + item_class->get_clip = gimp_group_layer_get_clip; + + item_class->default_name = _("Layer Group"); + item_class->rename_desc = C_("undo-type", "Rename Layer Group"); + item_class->translate_desc = C_("undo-type", "Move Layer Group"); + item_class->scale_desc = C_("undo-type", "Scale Layer Group"); + item_class->resize_desc = C_("undo-type", "Resize Layer Group"); + item_class->flip_desc = C_("undo-type", "Flip Layer Group"); + item_class->rotate_desc = C_("undo-type", "Rotate Layer Group"); + item_class->transform_desc = C_("undo-type", "Transform Layer Group"); + + drawable_class->estimate_memsize = gimp_group_layer_estimate_memsize; + drawable_class->update_all = gimp_group_layer_update_all; + drawable_class->get_source_node = gimp_group_layer_get_source_node; + + layer_class->opacity_changed = gimp_group_layer_opacity_changed; + layer_class->effective_mode_changed = gimp_group_layer_effective_mode_changed; + layer_class->excludes_backdrop_changed = gimp_group_layer_excludes_backdrop_changed; + layer_class->translate = gimp_group_layer_translate; + layer_class->scale = gimp_group_layer_scale; + layer_class->flip = gimp_group_layer_flip; + layer_class->rotate = gimp_group_layer_rotate; + layer_class->transform = gimp_group_layer_transform; + layer_class->convert_type = gimp_group_layer_convert_type; + layer_class->get_bounding_box = gimp_group_layer_get_bounding_box; + layer_class->get_effective_mode = gimp_group_layer_get_effective_mode; + layer_class->get_excludes_backdrop = gimp_group_layer_get_excludes_backdrop; + + if (g_getenv ("GIMP_NO_PASS_THROUGH_STRENGTH_REDUCTION")) + no_pass_through_strength_reduction = TRUE; +} + +static void +gimp_projectable_iface_init (GimpProjectableInterface *iface) +{ + iface->get_image = (GimpImage * (*) (GimpProjectable *)) gimp_item_get_image; + iface->get_format = gimp_group_layer_get_format; + iface->get_offset = (void (*) (GimpProjectable*, gint*, gint*)) gimp_item_get_offset; + iface->get_bounding_box = gimp_group_layer_projectable_get_bounding_box; + iface->get_graph = gimp_group_layer_get_graph; + iface->begin_render = gimp_group_layer_begin_render; + iface->end_render = gimp_group_layer_end_render; + iface->invalidate_preview = (void (*) (GimpProjectable*)) gimp_viewable_invalidate_preview; +} + +static void +gimp_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->flush = gimp_group_layer_pickable_flush; + iface->get_opacity_at = gimp_group_layer_get_opacity_at; +} + +static void +gimp_group_layer_init (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + + private->children = gimp_layer_stack_new (GIMP_TYPE_LAYER); + private->expanded = TRUE; + + g_signal_connect (private->children, "add", + G_CALLBACK (gimp_group_layer_child_add), + group); + g_signal_connect (private->children, "remove", + G_CALLBACK (gimp_group_layer_child_remove), + group); + + gimp_container_add_handler (private->children, "notify::offset-x", + G_CALLBACK (gimp_group_layer_child_move), + group); + gimp_container_add_handler (private->children, "notify::offset-y", + G_CALLBACK (gimp_group_layer_child_move), + group); + gimp_container_add_handler (private->children, "size-changed", + G_CALLBACK (gimp_group_layer_child_resize), + group); + gimp_container_add_handler (private->children, "bounding-box-changed", + G_CALLBACK (gimp_group_layer_child_resize), + group); + gimp_container_add_handler (private->children, "active-changed", + G_CALLBACK (gimp_group_layer_child_active_changed), + group); + gimp_container_add_handler (private->children, "effective-mode-changed", + G_CALLBACK (gimp_group_layer_child_effective_mode_changed), + group); + gimp_container_add_handler (private->children, "excludes-backdrop-changed", + G_CALLBACK (gimp_group_layer_child_excludes_backdrop_changed), + group); + + g_signal_connect (private->children, "update", + G_CALLBACK (gimp_group_layer_stack_update), + group); + + private->projection = gimp_projection_new (GIMP_PROJECTABLE (group)); + gimp_projection_set_priority (private->projection, 1); + + g_signal_connect (private->projection, "update", + G_CALLBACK (gimp_group_layer_proj_update), + group); +} + +static void +gimp_group_layer_finalize (GObject *object) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (object); + + if (private->children) + { + g_signal_handlers_disconnect_by_func (private->children, + gimp_group_layer_child_add, + object); + g_signal_handlers_disconnect_by_func (private->children, + gimp_group_layer_child_remove, + object); + g_signal_handlers_disconnect_by_func (private->children, + gimp_group_layer_stack_update, + object); + + /* this is particularly important to avoid reallocating the projection + * in response to a "bounding-box-changed" signal, which can be emitted + * during layer destruction. see issue #4584. + */ + gimp_container_remove_handlers_by_data (private->children, object); + + g_clear_object (&private->children); + } + + g_clear_object (&private->projection); + g_clear_object (&private->source_node); + g_clear_object (&private->graph); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_group_layer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_group_layer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_group_layer_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->children), gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->projection), gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_group_layer_ancestry_changed (GimpViewable *viewable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (viewable); + + gimp_projection_set_priority (private->projection, + gimp_viewable_get_depth (viewable) + 1); + + GIMP_VIEWABLE_CLASS (parent_class)->ancestry_changed (viewable); +} + +static gboolean +gimp_group_layer_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (viewable); + + /* return the size only if there are children ... */ + if (! gimp_container_is_empty (private->children)) + { + return GIMP_VIEWABLE_CLASS (parent_class)->get_size (viewable, + width, height); + } + + /* ... otherwise, return "no content" */ + return FALSE; +} + +static GimpContainer * +gimp_group_layer_get_children (GimpViewable *viewable) +{ + return GET_PRIVATE (viewable)->children; +} + +static gboolean +gimp_group_layer_get_expanded (GimpViewable *viewable) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (viewable); + + return GET_PRIVATE (group)->expanded; +} + +static void +gimp_group_layer_set_expanded (GimpViewable *viewable, + gboolean expanded) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (viewable); + + if (private->expanded != expanded) + { + private->expanded = expanded; + + gimp_viewable_expanded_changed (viewable); + } +} + +static gboolean +gimp_group_layer_is_position_locked (GimpItem *item) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (item); + GList *list; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + if (gimp_item_is_position_locked (child)) + return TRUE; + } + + return GIMP_ITEM_CLASS (parent_class)->is_position_locked (item); +} + +static GimpItem * +gimp_group_layer_duplicate (GimpItem *item, + GType new_type) +{ + GimpItem *new_item; + + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL); + + new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type); + + if (GIMP_IS_GROUP_LAYER (new_item)) + { + GimpGroupLayerPrivate *private = GET_PRIVATE (item); + GimpGroupLayer *new_group = GIMP_GROUP_LAYER (new_item); + GimpGroupLayerPrivate *new_private = GET_PRIVATE (new_item); + gint position = 0; + GList *list; + + gimp_group_layer_suspend_resize (new_group, FALSE); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + GimpItem *new_child; + GimpLayerMask *mask; + + new_child = gimp_item_duplicate (child, G_TYPE_FROM_INSTANCE (child)); + + gimp_object_set_name (GIMP_OBJECT (new_child), + gimp_object_get_name (child)); + + mask = gimp_layer_get_mask (GIMP_LAYER (child)); + + if (mask) + { + GimpLayerMask *new_mask; + + new_mask = gimp_layer_get_mask (GIMP_LAYER (new_child)); + + gimp_object_set_name (GIMP_OBJECT (new_mask), + gimp_object_get_name (mask)); + } + + gimp_viewable_set_parent (GIMP_VIEWABLE (new_child), + GIMP_VIEWABLE (new_group)); + + gimp_container_insert (new_private->children, + GIMP_OBJECT (new_child), + position++); + } + + /* force the projection to reallocate itself */ + GET_PRIVATE (new_group)->reallocate_projection = TRUE; + + gimp_group_layer_resume_resize (new_group, FALSE); + } + + return new_item; +} + +static void +gimp_group_layer_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (item); + GList *list; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + GIMP_ITEM_GET_CLASS (child)->convert (child, dest_image, + G_TYPE_FROM_INSTANCE (child)); + } + + GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type); +} + +static void +gimp_group_layer_start_transform (GimpItem *item, + gboolean push_undo) +{ + _gimp_group_layer_start_transform (GIMP_GROUP_LAYER (item), push_undo); + + if (GIMP_ITEM_CLASS (parent_class)->start_transform) + GIMP_ITEM_CLASS (parent_class)->start_transform (item, push_undo); +} + +static void +gimp_group_layer_end_transform (GimpItem *item, + gboolean push_undo) +{ + if (GIMP_ITEM_CLASS (parent_class)->end_transform) + GIMP_ITEM_CLASS (parent_class)->end_transform (item, push_undo); + + _gimp_group_layer_end_transform (GIMP_GROUP_LAYER (item), push_undo); +} + +static void +gimp_group_layer_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (item); + GimpGroupLayerPrivate *private = GET_PRIVATE (item); + GList *list; + gint x, y; + + /* we implement GimpItem::resize(), instead of GimpLayer::resize(), so that + * GimpLayer doesn't resize the mask. note that gimp_item_resize() calls + * gimp_item_{start,end}_move(), and not gimp_item_{start,end}_transform(), + * so that mask resizing is handled by gimp_group_layer_update_size(). + */ + + x = gimp_item_get_offset_x (item) - offset_x; + y = gimp_item_get_offset_y (item) - offset_y; + + gimp_group_layer_suspend_resize (group, TRUE); + + list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + + while (list) + { + GimpItem *child = list->data; + gint child_width; + gint child_height; + gint child_x; + gint child_y; + + list = g_list_next (list); + + if (gimp_rectangle_intersect (x, + y, + new_width, + new_height, + gimp_item_get_offset_x (child), + gimp_item_get_offset_y (child), + gimp_item_get_width (child), + gimp_item_get_height (child), + &child_x, + &child_y, + &child_width, + &child_height)) + { + gint child_offset_x = gimp_item_get_offset_x (child) - child_x; + gint child_offset_y = gimp_item_get_offset_y (child) - child_y; + + gimp_item_resize (child, context, fill_type, + child_width, child_height, + child_offset_x, child_offset_y); + } + else if (gimp_item_is_attached (item)) + { + gimp_image_remove_layer (gimp_item_get_image (item), + GIMP_LAYER (child), + TRUE, NULL); + } + else + { + gimp_container_remove (private->children, GIMP_OBJECT (child)); + } + } + + gimp_group_layer_resume_resize (group, TRUE); +} + +static GimpTransformResize +gimp_group_layer_get_clip (GimpItem *item, + GimpTransformResize clip_result) +{ + /* TODO: add clipping support, by clipping all sublayers as a unit, instead + * of individually. + */ + return GIMP_TRANSFORM_RESIZE_ADJUST; +} + +static gint64 +gimp_group_layer_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (drawable); + GList *list; + GimpImageBaseType base_type; + gint64 memsize = 0; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpDrawable *child = list->data; + gint child_width; + gint child_height; + + child_width = (gimp_item_get_width (GIMP_ITEM (child)) * + width / + gimp_item_get_width (GIMP_ITEM (drawable))); + child_height = (gimp_item_get_height (GIMP_ITEM (child)) * + height / + gimp_item_get_height (GIMP_ITEM (drawable))); + + memsize += gimp_drawable_estimate_memsize (child, + component_type, + child_width, + child_height); + } + + base_type = gimp_drawable_get_base_type (drawable); + + memsize += gimp_projection_estimate_memsize (base_type, component_type, + width, height); + + return memsize + + GIMP_DRAWABLE_CLASS (parent_class)->estimate_memsize (drawable, + component_type, + width, height); +} + +static void +gimp_group_layer_update_all (GimpDrawable *drawable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (drawable); + GList *list; + + /* redirect stack updates to the drawable, rather than to the projection */ + private->direct_update++; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpFilter *child = list->data; + + if (gimp_filter_get_active (child)) + gimp_drawable_update_all (GIMP_DRAWABLE (child)); + } + + /* redirect stack updates back to the projection */ + private->direct_update--; +} + +static void +gimp_group_layer_translate (GimpLayer *layer, + gint offset_x, + gint offset_y) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + gint x, y; + GList *list; + + /* don't use gimp_group_layer_suspend_resize(), but rather increment + * private->suspend_resize directly, since we're translating the group layer + * here, rather than relying on gimp_group_layer_update_size() to do it. + */ + private->suspend_resize++; + + /* redirect stack updates to the drawable, rather than to the projection */ + private->direct_update++; + + /* translate the child layers *before* updating the group's offset, so that, + * if this is a nested group, the parent's bounds still reflect the original + * layer positions. This prevents the original area of the child layers, + * which is updated as part of their translation, from being clipped to the + * post-translation parent bounds (see issue #3484). The new area of the + * child layers, which is likewise updated as part of translation, *may* get + * clipped to the old parent bounds, but the corresponding region will be + * updated anyway when the parent is resized, once we update the group's + * offset. + */ + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + /* don't push an undo here because undo will call us again */ + gimp_item_translate (child, offset_x, offset_y, FALSE); + } + + gimp_item_get_offset (GIMP_ITEM (group), &x, &y); + + x += offset_x; + y += offset_y; + + /* update the offset node */ + if (private->offset_node) + gegl_node_set (private->offset_node, + "x", (gdouble) -x, + "y", (gdouble) -y, + NULL); + + /* invalidate the selection boundary because of a layer modification */ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); + + /* update the group layer offset */ + gimp_item_set_offset (GIMP_ITEM (group), x, y); + + /* redirect stack updates back to the projection */ + private->direct_update--; + + /* don't use gimp_group_layer_resume_resize(), but rather decrement + * private->suspend_resize directly, so that gimp_group_layer_update_size() + * isn't called. + */ + private->suspend_resize--; +} + +static void +gimp_group_layer_scale (GimpLayer *layer, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GimpItem *item = GIMP_ITEM (layer); + GimpObjectQueue *queue = NULL; + GList *list; + gdouble width_factor; + gdouble height_factor; + gint old_offset_x; + gint old_offset_y; + + width_factor = (gdouble) new_width / (gdouble) gimp_item_get_width (item); + height_factor = (gdouble) new_height / (gdouble) gimp_item_get_height (item); + + old_offset_x = gimp_item_get_offset_x (item); + old_offset_y = gimp_item_get_offset_y (item); + + if (progress) + { + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, private->children); + } + + gimp_group_layer_suspend_resize (group, TRUE); + + list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + + while (list) + { + GimpItem *child = list->data; + + list = g_list_next (list); + + if (queue) + gimp_object_queue_pop (queue); + + if (! gimp_item_scale_by_factors_with_origin (child, + width_factor, height_factor, + old_offset_x, old_offset_y, + new_offset_x, new_offset_y, + interpolation_type, + progress)) + { + /* new width or height are 0; remove item */ + if (gimp_item_is_attached (item)) + { + gimp_image_remove_layer (gimp_item_get_image (item), + GIMP_LAYER (child), + TRUE, NULL); + } + else + { + gimp_container_remove (private->children, GIMP_OBJECT (child)); + } + } + } + + gimp_group_layer_resume_resize (group, TRUE); + + g_clear_object (&queue); +} + +static void +gimp_group_layer_flip (GimpLayer *layer, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GList *list; + + gimp_group_layer_suspend_resize (group, TRUE); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + gimp_item_flip (child, context, + flip_type, axis, clip_result); + } + + gimp_group_layer_resume_resize (group, TRUE); +} + +static void +gimp_group_layer_rotate (GimpLayer *layer, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GList *list; + + gimp_group_layer_suspend_resize (group, TRUE); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + gimp_item_rotate (child, context, + rotate_type, center_x, center_y, clip_result); + } + + gimp_group_layer_resume_resize (group, TRUE); +} + +static void +gimp_group_layer_transform (GimpLayer *layer, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GimpObjectQueue *queue = NULL; + GList *list; + + if (progress) + { + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, private->children); + } + + gimp_group_layer_suspend_resize (group, TRUE); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + + if (queue) + gimp_object_queue_pop (queue); + + gimp_item_transform (child, context, + matrix, direction, + interpolation_type, + clip_result, progress); + } + + gimp_group_layer_resume_resize (group, TRUE); + + g_clear_object (&queue); +} + +static const Babl * +get_projection_format (GimpProjectable *projectable, + GimpImageBaseType base_type, + GimpPrecision precision) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (projectable)); + + switch (base_type) + { + case GIMP_RGB: + case GIMP_INDEXED: + return gimp_image_get_format (image, GIMP_RGB, precision, TRUE); + + case GIMP_GRAY: + return gimp_image_get_format (image, GIMP_GRAY, precision, TRUE); + } + + g_return_val_if_reached (NULL); +} + +static void +gimp_group_layer_convert_type (GimpLayer *layer, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GeglBuffer *buffer; + + if (push_undo) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (group)); + + gimp_image_undo_push_group_layer_convert (image, NULL, group); + } + + /* Need to temporarily set the projectable's format to the new + * values so the projection will create its tiles with the right + * depth + */ + private->convert_format = + get_projection_format (GIMP_PROJECTABLE (group), + gimp_babl_format_get_base_type (new_format), + gimp_babl_format_get_precision (new_format)); + gimp_projectable_structure_changed (GIMP_PROJECTABLE (group)); + gimp_group_layer_flush (group); + + buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (private->projection)); + + gimp_drawable_set_buffer_full (GIMP_DRAWABLE (group), + FALSE, NULL, + buffer, NULL, + TRUE); + + /* reset, the actual format is right now */ + private->convert_format = NULL; +} + +static GeglNode * +gimp_group_layer_get_source_node (GimpDrawable *drawable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (drawable); + GeglNode *input; + + g_warn_if_fail (private->source_node == NULL); + + private->source_node = gegl_node_new (); + + input = gegl_node_get_input_proxy (private->source_node, "input"); + + private->parent_source_node = + GIMP_DRAWABLE_CLASS (parent_class)->get_source_node (drawable); + + gegl_node_add_child (private->source_node, private->parent_source_node); + + g_object_unref (private->parent_source_node); + + if (gegl_node_has_pad (private->parent_source_node, "input")) + { + gegl_node_connect_to (input, "output", + private->parent_source_node, "input"); + } + + /* make sure we have a graph */ + (void) gimp_group_layer_get_graph (GIMP_PROJECTABLE (drawable)); + + gegl_node_add_child (private->source_node, private->graph); + + gimp_group_layer_update_source_node (GIMP_GROUP_LAYER (drawable)); + + return g_object_ref (private->source_node); +} + +static void +gimp_group_layer_opacity_changed (GimpLayer *layer) +{ + gimp_layer_update_effective_mode (layer); + + if (GIMP_LAYER_CLASS (parent_class)->opacity_changed) + GIMP_LAYER_CLASS (parent_class)->opacity_changed (layer); +} + +static void +gimp_group_layer_effective_mode_changed (GimpLayer *layer) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + GimpLayerMode mode; + gboolean pass_through; + gboolean update_bounding_box = FALSE; + + gimp_layer_get_effective_mode (layer, &mode, NULL, NULL, NULL); + + pass_through = (mode == GIMP_LAYER_MODE_PASS_THROUGH); + + if (pass_through != private->pass_through) + { + if (private->pass_through && ! pass_through) + { + /* when switching from pass-through mode to a non-pass-through mode, + * flush the pickable in order to make sure the projection's buffer + * gets properly invalidated synchronously, so that it can be used + * as a source for the rest of the composition. + */ + gimp_pickable_flush (GIMP_PICKABLE (private->projection)); + } + + private->pass_through = pass_through; + + update_bounding_box = TRUE; + } + + gimp_group_layer_update_source_node (group); + gimp_group_layer_update_mode_node (group); + + if (update_bounding_box) + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (group)); + + if (GIMP_LAYER_CLASS (parent_class)->effective_mode_changed) + GIMP_LAYER_CLASS (parent_class)->effective_mode_changed (layer); +} + +static void +gimp_group_layer_excludes_backdrop_changed (GimpLayer *layer) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (layer); + + gimp_group_layer_update_source_node (group); + gimp_group_layer_update_mode_node (group); + + if (GIMP_LAYER_CLASS (parent_class)->excludes_backdrop_changed) + GIMP_LAYER_CLASS (parent_class)->excludes_backdrop_changed (layer); +} + +static GeglRectangle +gimp_group_layer_get_bounding_box (GimpLayer *layer) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + + /* for pass-through groups, use the group's calculated bounding box, instead + * of the source-node's bounding box, since we don't update the bounding box + * on all events that may affect the latter, and since it includes the + * bounding box of the backdrop. this means we can't attach filters that may + * affect the bounding box to a pass-through group (since their effect weon't + * be reflected by the group's bounding box), but attaching filters to pass- + * through groups makes little sense anyway. + */ + if (private->pass_through) + return private->bounding_box; + else + return GIMP_LAYER_CLASS (parent_class)->get_bounding_box (layer); +} + +static void +gimp_group_layer_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + + /* try to strength-reduce pass-through groups to normal groups, which are + * cheaper. + */ + if (gimp_layer_get_mode (layer) == GIMP_LAYER_MODE_PASS_THROUGH && + ! no_pass_through_strength_reduction) + { + /* we perform the strength-reduction if: + * + * - the group has no active children; + * + * or, + * + * - the group has a single active child; or, + * + * - the effective mode of all the active children is normal, their + * effective composite mode is UNION, and their effective blend and + * composite spaces are equal; + * + * - and, + * + * - the group's opacity is 100%, and it has no mask (or the mask + * isn't applied); or, + * + * - the group's composite space equals the active children's + * composite space. + */ + + GList *list; + gboolean reduce = TRUE; + gboolean first = TRUE; + + *mode = GIMP_LAYER_MODE_NORMAL; + *blend_space = gimp_layer_get_real_blend_space (layer); + *composite_space = gimp_layer_get_real_composite_space (layer); + *composite_mode = gimp_layer_get_real_composite_mode (layer); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpLayer *child = list->data; + + if (! gimp_filter_get_active (GIMP_FILTER (child))) + continue; + + if (first) + { + gimp_layer_get_effective_mode (child, + mode, + blend_space, + composite_space, + composite_mode); + + if (*mode == GIMP_LAYER_MODE_NORMAL_LEGACY) + *mode = GIMP_LAYER_MODE_NORMAL; + + first = FALSE; + } + else + { + GimpLayerMode other_mode; + GimpLayerColorSpace other_blend_space; + GimpLayerColorSpace other_composite_space; + GimpLayerCompositeMode other_composite_mode; + + if (*mode != GIMP_LAYER_MODE_NORMAL || + *composite_mode != GIMP_LAYER_COMPOSITE_UNION) + { + reduce = FALSE; + + break; + } + + gimp_layer_get_effective_mode (child, + &other_mode, + &other_blend_space, + &other_composite_space, + &other_composite_mode); + + if (other_mode == GIMP_LAYER_MODE_NORMAL_LEGACY) + other_mode = GIMP_LAYER_MODE_NORMAL; + + if (other_mode != *mode || + other_blend_space != *blend_space || + other_composite_space != *composite_space || + other_composite_mode != *composite_mode) + { + reduce = FALSE; + + break; + } + } + } + + if (reduce) + { + gboolean has_mask; + + has_mask = gimp_layer_get_mask (layer) && + gimp_layer_get_apply_mask (layer); + + if (first || + (gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE && + ! has_mask) || + *composite_space == gimp_layer_get_real_composite_space (layer)) + { + /* strength reduction succeeded! */ + return; + } + } + } + + /* strength-reduction failed. chain up. */ + GIMP_LAYER_CLASS (parent_class)->get_effective_mode (layer, + mode, + blend_space, + composite_space, + composite_mode); +} + +static gboolean +gimp_group_layer_get_excludes_backdrop (GimpLayer *layer) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (layer); + + if (private->pass_through) + { + GList *list; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpFilter *child = list->data; + + if (gimp_filter_get_active (child) && + gimp_layer_get_excludes_backdrop (GIMP_LAYER (child))) + return TRUE; + } + + return FALSE; + } + else + return GIMP_LAYER_CLASS (parent_class)->get_excludes_backdrop (layer); +} + +static const Babl * +gimp_group_layer_get_format (GimpProjectable *projectable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (projectable); + GimpImageBaseType base_type; + GimpPrecision precision; + + if (private->convert_format) + return private->convert_format; + + base_type = gimp_drawable_get_base_type (GIMP_DRAWABLE (projectable)); + precision = gimp_drawable_get_precision (GIMP_DRAWABLE (projectable)); + + return get_projection_format (projectable, base_type, precision); +} + +static GeglRectangle +gimp_group_layer_projectable_get_bounding_box (GimpProjectable *projectable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (projectable); + + return private->bounding_box; +} + +static GeglNode * +gimp_group_layer_get_graph (GimpProjectable *projectable) +{ + GimpGroupLayer *group = GIMP_GROUP_LAYER (projectable); + GimpGroupLayerPrivate *private = GET_PRIVATE (projectable); + GeglNode *input; + GeglNode *layers_node; + GeglNode *output; + gint off_x; + gint off_y; + + if (private->graph) + return private->graph; + + private->graph = gegl_node_new (); + + input = gegl_node_get_input_proxy (private->graph, "input"); + + layers_node = + gimp_filter_stack_get_graph (GIMP_FILTER_STACK (private->children)); + + gegl_node_add_child (private->graph, layers_node); + + gegl_node_connect_to (input, "output", + layers_node, "input"); + + gimp_item_get_offset (GIMP_ITEM (group), &off_x, &off_y); + + private->offset_node = gegl_node_new_child (private->graph, + "operation", "gegl:translate", + "x", (gdouble) -off_x, + "y", (gdouble) -off_y, + NULL); + + gegl_node_connect_to (layers_node, "output", + private->offset_node, "input"); + + output = gegl_node_get_output_proxy (private->graph, "output"); + + gegl_node_connect_to (private->offset_node, "output", + output, "input"); + + return private->graph; +} + +static void +gimp_group_layer_begin_render (GimpProjectable *projectable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (projectable); + + if (private->source_node == NULL) + return; + + if (private->pass_through) + gegl_node_disconnect (private->graph, "input"); +} + +static void +gimp_group_layer_end_render (GimpProjectable *projectable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (projectable); + + if (private->source_node == NULL) + return; + + if (private->pass_through) + { + GeglNode *input; + + input = gegl_node_get_input_proxy (private->source_node, "input"); + + gegl_node_connect_to (input, "output", + private->graph, "input"); + } +} + +static void +gimp_group_layer_pickable_flush (GimpPickable *pickable) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (pickable); + + gimp_pickable_flush (GIMP_PICKABLE (private->projection)); +} + +static gdouble +gimp_group_layer_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + /* Only consider child layers as having content */ + + return GIMP_OPACITY_TRANSPARENT; +} + + +/* public functions */ + +GimpLayer * +gimp_group_layer_new (GimpImage *image) +{ + GimpGroupLayer *group; + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + format = gimp_image_get_layer_format (image, TRUE); + + group = GIMP_GROUP_LAYER (gimp_drawable_new (GIMP_TYPE_GROUP_LAYER, + image, NULL, + 0, 0, 1, 1, + format)); + + gimp_layer_set_mode (GIMP_LAYER (group), + gimp_image_get_default_new_layer_mode (image), + FALSE); + + return GIMP_LAYER (group); +} + +GimpProjection * +gimp_group_layer_get_projection (GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + + return GET_PRIVATE (group)->projection; +} + +void +gimp_group_layer_suspend_resize (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpItem *item; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + item = GIMP_ITEM (group); + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_group_layer_suspend_resize (gimp_item_get_image (item), + NULL, group); + + GET_PRIVATE (group)->suspend_resize++; +} + +void +gimp_group_layer_resume_resize (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpGroupLayerPrivate *private; + GimpItem *item; + GimpItem *mask = NULL; + GeglBuffer *mask_buffer; + GeglRectangle mask_bounds; + GimpUndo *undo; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + private = GET_PRIVATE (group); + + g_return_if_fail (private->suspend_resize > 0); + + item = GIMP_ITEM (group); + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + { + undo = + gimp_image_undo_push_group_layer_resume_resize (gimp_item_get_image (item), + NULL, group); + + /* if there were any {suspend,resume}_mask() calls during the time the + * group's size was suspended, the resume_mask() calls will not have seen + * any changes to the mask, and will therefore won't restore the mask + * during undo. if the group's bounding box did change while resize was + * suspended, and if there are no other {suspend,resume}_mask() blocks + * that will see the resized mask, we have to restore the mask during the + * resume_resize() undo. + * + * we ref the mask buffer here, and compare it to the mask buffer after + * updating the size. + */ + if (private->suspend_resize == 1 && private->suspend_mask == 0) + { + mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group))); + + if (mask) + { + mask_buffer = + g_object_ref (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask))); + + mask_bounds.x = gimp_item_get_offset_x (mask); + mask_bounds.y = gimp_item_get_offset_y (mask); + mask_bounds.width = gimp_item_get_width (mask); + mask_bounds.height = gimp_item_get_height (mask); + } + } + } + + private->suspend_resize--; + + if (private->suspend_resize == 0) + { + gimp_group_layer_update_size (group); + + if (mask) + { + /* if the mask changed, make sure it's restored during undo, as per + * the comment above. + */ + if (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)) != mask_buffer) + { + g_return_if_fail (undo != NULL); + + GIMP_GROUP_LAYER_UNDO (undo)->mask_buffer = mask_buffer; + GIMP_GROUP_LAYER_UNDO (undo)->mask_bounds = mask_bounds; + } + else + { + g_object_unref (mask_buffer); + } + } + } +} + +void +gimp_group_layer_suspend_mask (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpGroupLayerPrivate *private; + GimpItem *item; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + private = GET_PRIVATE (group); + item = GIMP_ITEM (group); + + /* avoid pushing an undo step if this is a nested suspend_mask() call, since + * the value of 'push_undo' in nested calls should be the same as that passed + * to the outermost call, and only pushing an undo step for the outermost + * call in this case is enough. we can't support cases where the values of + * 'push_undo' in nested calls are different in a meaningful way, and + * avoiding undo steps for nested calls prevents us from storing multiple + * references to the suspend mask buffer on the undo stack. while storing + * multiple references to the buffer doesn't waste any memory (since all the + * references are to the same buffer), it does cause the undo stack memory- + * usage estimation to overshoot, potentially resulting in undo steps being + * dropped unnecessarily. + */ + if (! gimp_item_is_attached (item) || private->suspend_mask > 0) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_group_layer_suspend_mask (gimp_item_get_image (item), + NULL, group); + + if (private->suspend_mask == 0) + { + if (gimp_layer_get_mask (GIMP_LAYER (group))) + { + GimpItem *mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group))); + + private->suspended_mask_buffer = + g_object_ref (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask))); + + private->suspended_mask_bounds.x = gimp_item_get_offset_x (mask); + private->suspended_mask_bounds.y = gimp_item_get_offset_y (mask); + private->suspended_mask_bounds.width = gimp_item_get_width (mask); + private->suspended_mask_bounds.height = gimp_item_get_height (mask); + } + else + { + private->suspended_mask_buffer = NULL; + } + } + + private->suspend_mask++; +} + +void +gimp_group_layer_resume_mask (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpGroupLayerPrivate *private; + GimpItem *item; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + private = GET_PRIVATE (group); + + g_return_if_fail (private->suspend_mask > 0); + + item = GIMP_ITEM (group); + + /* avoid pushing an undo step if this is a nested resume_mask() call. see + * the comment in gimp_group_layer_suspend_mask(). + */ + if (! gimp_item_is_attached (item) || private->suspend_mask > 1) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_group_layer_resume_mask (gimp_item_get_image (item), + NULL, group); + + private->suspend_mask--; + + if (private->suspend_mask == 0) + g_clear_object (&private->suspended_mask_buffer); +} + + +/* protected functions */ + +void +_gimp_group_layer_set_suspended_mask (GimpGroupLayer *group, + GeglBuffer *buffer, + const GeglRectangle *bounds) +{ + GimpGroupLayerPrivate *private; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + g_return_if_fail (buffer != NULL); + g_return_if_fail (bounds != NULL); + + private = GET_PRIVATE (group); + + g_return_if_fail (private->suspend_mask > 0); + + g_object_ref (buffer); + + g_clear_object (&private->suspended_mask_buffer); + + private->suspended_mask_buffer = buffer; + private->suspended_mask_bounds = *bounds; +} + +GeglBuffer * +_gimp_group_layer_get_suspended_mask (GimpGroupLayer *group, + GeglRectangle *bounds) +{ + GimpGroupLayerPrivate *private; + GimpLayerMask *mask; + + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (bounds != NULL, NULL); + + private = GET_PRIVATE (group); + mask = gimp_layer_get_mask (GIMP_LAYER (group)); + + g_return_val_if_fail (private->suspend_mask > 0, NULL); + + if (mask && + gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)) != + private->suspended_mask_buffer) + { + *bounds = private->suspended_mask_bounds; + + return private->suspended_mask_buffer; + } + + return NULL; +} + +void +_gimp_group_layer_start_transform (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpGroupLayerPrivate *private; + GimpItem *item; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + private = GET_PRIVATE (group); + item = GIMP_ITEM (group); + + g_return_if_fail (private->suspend_mask == 0); + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_group_layer_start_transform (gimp_item_get_image (item), + NULL, group); + + private->transforming++; +} + +void +_gimp_group_layer_end_transform (GimpGroupLayer *group, + gboolean push_undo) +{ + GimpGroupLayerPrivate *private; + GimpItem *item; + + g_return_if_fail (GIMP_IS_GROUP_LAYER (group)); + + private = GET_PRIVATE (group); + item = GIMP_ITEM (group); + + g_return_if_fail (private->suspend_mask == 0); + g_return_if_fail (private->transforming > 0); + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_group_layer_end_transform (gimp_item_get_image (item), + NULL, group); + + private->transforming--; + + if (private->transforming == 0) + gimp_group_layer_update_mask_size (GIMP_GROUP_LAYER (item)); +} + + +/* private functions */ + +static void +gimp_group_layer_child_add (GimpContainer *container, + GimpLayer *child, + GimpGroupLayer *group) +{ + gimp_group_layer_update (group); + + if (gimp_filter_get_active (GIMP_FILTER (child))) + { + gimp_layer_update_effective_mode (GIMP_LAYER (group)); + + if (gimp_layer_get_excludes_backdrop (child)) + gimp_layer_update_excludes_backdrop (GIMP_LAYER (group)); + } +} + +static void +gimp_group_layer_child_remove (GimpContainer *container, + GimpLayer *child, + GimpGroupLayer *group) +{ + gimp_group_layer_update (group); + + if (gimp_filter_get_active (GIMP_FILTER (child))) + { + gimp_layer_update_effective_mode (GIMP_LAYER (group)); + + if (gimp_layer_get_excludes_backdrop (child)) + gimp_layer_update_excludes_backdrop (GIMP_LAYER (group)); + } +} + +static void +gimp_group_layer_child_move (GimpLayer *child, + GParamSpec *pspec, + GimpGroupLayer *group) +{ + gimp_group_layer_update (group); +} + +static void +gimp_group_layer_child_resize (GimpLayer *child, + GimpGroupLayer *group) +{ + gimp_group_layer_update (group); +} + +static void +gimp_group_layer_child_active_changed (GimpLayer *child, + GimpGroupLayer *group) +{ + gimp_layer_update_effective_mode (GIMP_LAYER (group)); + + if (gimp_layer_get_excludes_backdrop (child)) + gimp_layer_update_excludes_backdrop (GIMP_LAYER (group)); +} + +static void +gimp_group_layer_child_effective_mode_changed (GimpLayer *child, + GimpGroupLayer *group) +{ + if (gimp_filter_get_active (GIMP_FILTER (child))) + gimp_layer_update_effective_mode (GIMP_LAYER (group)); +} + +static void +gimp_group_layer_child_excludes_backdrop_changed (GimpLayer *child, + GimpGroupLayer *group) +{ + if (gimp_filter_get_active (GIMP_FILTER (child))) + gimp_layer_update_excludes_backdrop (GIMP_LAYER (group)); +} + +static void +gimp_group_layer_flush (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + + if (private->pass_through) + { + /* flush the projectable, not the pickable, because the source + * node of pass-through groups doesn't use the projection's + * buffer, hence there's no need to invalidate it synchronously. + */ + gimp_projectable_flush (GIMP_PROJECTABLE (group), TRUE); + } + else + { + /* make sure we have a buffer, and stop any idle rendering, which is + * initiated when a new buffer is allocated. the call to + * gimp_pickable_flush() below causes any pending idle rendering to + * finish synchronously, so this needs to happen before. + */ + gimp_pickable_get_buffer (GIMP_PICKABLE (private->projection)); + gimp_projection_stop_rendering (private->projection); + + /* flush the pickable not the projectable because flushing the + * pickable will finish all invalidation on the projection so it + * can be used as source (note that it will still be constructed + * when the actual read happens, so this it not a performance + * problem) + */ + gimp_pickable_flush (GIMP_PICKABLE (private->projection)); + } +} + +static void +gimp_group_layer_update (GimpGroupLayer *group) +{ + if (GET_PRIVATE (group)->suspend_resize == 0) + { + gimp_group_layer_update_size (group); + } +} + +static void +gimp_group_layer_update_size (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + GimpItem *item = GIMP_ITEM (group); + GimpLayer *layer = GIMP_LAYER (group); + GimpItem *mask = GIMP_ITEM (gimp_layer_get_mask (layer)); + GeglRectangle old_bounds; + GeglRectangle bounds; + GeglRectangle old_bounding_box; + GeglRectangle bounding_box; + gboolean first = TRUE; + gboolean size_changed; + gboolean resize_mask; + GList *list; + + old_bounds.x = gimp_item_get_offset_x (item); + old_bounds.y = gimp_item_get_offset_y (item); + old_bounds.width = gimp_item_get_width (item); + old_bounds.height = gimp_item_get_height (item); + + bounds.x = 0; + bounds.y = 0; + bounds.width = 1; + bounds.height = 1; + + old_bounding_box = private->bounding_box; + bounding_box = bounds; + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children)); + list; + list = g_list_next (list)) + { + GimpItem *child = list->data; + GeglRectangle child_bounds; + GeglRectangle child_bounding_box; + + if (! gimp_viewable_get_size (GIMP_VIEWABLE (child), + &child_bounds.width, &child_bounds.height)) + { + /* ignore children without content (empty group layers); + * see bug 777017 + */ + continue; + } + + gimp_item_get_offset (child, &child_bounds.x, &child_bounds.y); + + child_bounding_box = + gimp_drawable_get_bounding_box (GIMP_DRAWABLE (child)); + + child_bounding_box.x += child_bounds.x; + child_bounding_box.y += child_bounds.y; + + if (first) + { + bounds = child_bounds; + bounding_box = child_bounding_box; + + first = FALSE; + } + else + { + gegl_rectangle_bounding_box (&bounds, + &bounds, &child_bounds); + gegl_rectangle_bounding_box (&bounding_box, + &bounding_box, &child_bounding_box); + } + } + + bounding_box.x -= bounds.x; + bounding_box.y -= bounds.y; + + size_changed = ! (gegl_rectangle_equal (&bounds, &old_bounds) && + gegl_rectangle_equal (&bounding_box, &old_bounding_box)); + + resize_mask = mask && ! gegl_rectangle_equal (&bounds, &old_bounds); + + /* if we show the mask, invalidate the old mask area */ + if (resize_mask && gimp_layer_get_show_mask (layer)) + { + gimp_drawable_update (GIMP_DRAWABLE (group), + gimp_item_get_offset_x (mask) - old_bounds.x, + gimp_item_get_offset_y (mask) - old_bounds.y, + gimp_item_get_width (mask), + gimp_item_get_height (mask)); + } + + if (private->reallocate_projection || size_changed) + { + GeglBuffer *buffer; + + /* if the graph is already constructed, set the offset node's + * coordinates first, so the graph is in the right state when + * the projection is reallocated, see bug #730550. + */ + if (private->offset_node) + gegl_node_set (private->offset_node, + "x", (gdouble) -bounds.x, + "y", (gdouble) -bounds.y, + NULL); + + /* update our offset *before* calling gimp_pickable_get_buffer(), so + * that if our graph isn't constructed yet, the offset node picks + * up the right coordinates in gimp_group_layer_get_graph(). + */ + gimp_item_set_offset (item, bounds.x, bounds.y); + + /* update the bounding box before updating the projection, so that it + * picks up the right size. + */ + private->bounding_box = bounding_box; + + if (private->reallocate_projection) + { + private->reallocate_projection = FALSE; + + gimp_projectable_structure_changed (GIMP_PROJECTABLE (group)); + } + else + { + /* when there's no need to reallocate the projection, we call + * gimp_projectable_bounds_changed(), rather than structure_chaned(), + * so that the projection simply copies the old content over to the + * new buffer with an offset, rather than re-renders the graph. + */ + gimp_projectable_bounds_changed (GIMP_PROJECTABLE (group), + old_bounds.x, old_bounds.y); + } + + buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (private->projection)); + + gimp_drawable_set_buffer_full (GIMP_DRAWABLE (group), + FALSE, NULL, + buffer, &bounds, + FALSE /* don't update the drawable, the + * flush() below will take care of + * that. + */); + + gimp_group_layer_flush (group); + } + + /* resize the mask if not transforming (in which case, GimpLayer takes care + * of the mask) + */ + if (resize_mask && ! private->transforming) + gimp_group_layer_update_mask_size (group); + + /* if we show the mask, invalidate the new mask area */ + if (resize_mask && gimp_layer_get_show_mask (layer)) + { + gimp_drawable_update (GIMP_DRAWABLE (group), + gimp_item_get_offset_x (mask) - bounds.x, + gimp_item_get_offset_y (mask) - bounds.y, + gimp_item_get_width (mask), + gimp_item_get_height (mask)); + } +} + +static void +gimp_group_layer_update_mask_size (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + GimpItem *item = GIMP_ITEM (group); + GimpItem *mask; + GeglBuffer *buffer; + GeglBuffer *mask_buffer; + GeglRectangle bounds; + GeglRectangle mask_bounds; + GeglRectangle copy_bounds; + gboolean intersect; + + mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group))); + + if (! mask) + return; + + bounds.x = gimp_item_get_offset_x (item); + bounds.y = gimp_item_get_offset_y (item); + bounds.width = gimp_item_get_width (item); + bounds.height = gimp_item_get_height (item); + + mask_bounds.x = gimp_item_get_offset_x (mask); + mask_bounds.y = gimp_item_get_offset_y (mask); + mask_bounds.width = gimp_item_get_width (mask); + mask_bounds.height = gimp_item_get_height (mask); + + if (gegl_rectangle_equal (&bounds, &mask_bounds)) + return; + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, bounds.width, bounds.height), + gimp_drawable_get_format (GIMP_DRAWABLE (mask))); + + if (private->suspended_mask_buffer) + { + /* copy the suspended mask into the new mask */ + mask_buffer = private->suspended_mask_buffer; + mask_bounds = private->suspended_mask_bounds; + } + else + { + /* copy the old mask into the new mask */ + mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + } + + intersect = gimp_rectangle_intersect (bounds.x, + bounds.y, + bounds.width, + bounds.height, + mask_bounds.x, + mask_bounds.y, + mask_bounds.width, + mask_bounds.height, + ©_bounds.x, + ©_bounds.y, + ©_bounds.width, + ©_bounds.height); + + if (intersect) + { + gimp_gegl_buffer_copy (mask_buffer, + GEGL_RECTANGLE (copy_bounds.x - mask_bounds.x, + copy_bounds.y - mask_bounds.y, + copy_bounds.width, + copy_bounds.height), + GEGL_ABYSS_NONE, + buffer, + GEGL_RECTANGLE (copy_bounds.x - bounds.x, + copy_bounds.y - bounds.y, + copy_bounds.width, + copy_bounds.height)); + } + + gimp_drawable_set_buffer_full (GIMP_DRAWABLE (mask), + FALSE, NULL, + buffer, &bounds, + TRUE); + + g_object_unref (buffer); +} + +static void +gimp_group_layer_update_source_node (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + GeglNode *input; + GeglNode *output; + + if (private->source_node == NULL) + return; + + input = gegl_node_get_input_proxy (private->source_node, "input"); + output = gegl_node_get_output_proxy (private->source_node, "output"); + + if (private->pass_through) + { + gegl_node_connect_to (input, "output", + private->graph, "input"); + gegl_node_connect_to (private->graph, "output", + output, "input"); + } + else + { + gegl_node_disconnect (private->graph, "input"); + + gegl_node_connect_to (private->parent_source_node, "output", + output, "input"); + } +} + +static void +gimp_group_layer_update_mode_node (GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + GeglNode *node; + GeglNode *input; + GeglNode *mode_node; + + node = gimp_filter_get_node (GIMP_FILTER (group)); + input = gegl_node_get_input_proxy (node, "input"); + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (group)); + + if (private->pass_through && + gimp_layer_get_excludes_backdrop (GIMP_LAYER (group))) + { + gegl_node_disconnect (mode_node, "input"); + } + else + { + gegl_node_connect_to (input, "output", + mode_node, "input"); + } +} + +static void +gimp_group_layer_stack_update (GimpDrawableStack *stack, + gint x, + gint y, + gint width, + gint height, + GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + +#if 0 + g_printerr ("%s (%s) %d, %d (%d, %d)\n", + G_STRFUNC, gimp_object_get_name (group), + x, y, width, height); +#endif + + if (! private->direct_update) + { + /* the layer stack's update signal speaks in image coordinates, + * pass to the projection as-is. + */ + gimp_projectable_invalidate (GIMP_PROJECTABLE (group), + x, y, width, height); + + gimp_group_layer_flush (group); + } + + if (private->direct_update || private->pass_through) + { + /* the layer stack's update signal speaks in image coordinates, + * transform to layer coordinates when emitting our own update signal. + */ + gimp_drawable_update (GIMP_DRAWABLE (group), + x - gimp_item_get_offset_x (GIMP_ITEM (group)), + y - gimp_item_get_offset_y (GIMP_ITEM (group)), + width, height); + } +} + +static void +gimp_group_layer_proj_update (GimpProjection *proj, + gboolean now, + gint x, + gint y, + gint width, + gint height, + GimpGroupLayer *group) +{ + GimpGroupLayerPrivate *private = GET_PRIVATE (group); + +#if 0 + g_printerr ("%s (%s) %d, %d (%d, %d)\n", + G_STRFUNC, gimp_object_get_name (group), + x, y, width, height); +#endif + + if (! private->pass_through) + { + /* TODO: groups can currently have a gegl:transform op attached as a filter + * when using a transform tool, in which case the updated region needs + * undergo the same transformation. more generally, when a drawable has + * filters they may influence the area affected by drawable updates. + * + * this needs to be addressed much more generally at some point, but for now + * we just resort to updating the entire group when it has a filter (i.e., + * when it's being used with a transform tool). we restrict this to groups, + * and don't do this more generally in gimp_drawable_update(), because this + * negatively impacts the performance of the warp tool, which does perform + * accurate drawable updates while using a filter. + */ + if (gimp_drawable_has_filters (GIMP_DRAWABLE (group))) + { + width = -1; + height = -1; + } + + /* the projection speaks in image coordinates, transform to layer + * coordinates when emitting our own update signal. + */ + gimp_drawable_update (GIMP_DRAWABLE (group), + x - gimp_item_get_offset_x (GIMP_ITEM (group)), + y - gimp_item_get_offset_y (GIMP_ITEM (group)), + width, height); + } +} diff --git a/app/core/gimpgrouplayer.h b/app/core/gimpgrouplayer.h new file mode 100644 index 0000000..5fef1e9 --- /dev/null +++ b/app/core/gimpgrouplayer.h @@ -0,0 +1,78 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGroupLayer + * Copyright (C) 2009 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GROUP_LAYER_H__ +#define __GIMP_GROUP_LAYER_H__ + + +#include "core/gimplayer.h" + + +#define GIMP_TYPE_GROUP_LAYER (gimp_group_layer_get_type ()) +#define GIMP_GROUP_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GROUP_LAYER, GimpGroupLayer)) +#define GIMP_GROUP_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GROUP_LAYER, GimpGroupLayerClass)) +#define GIMP_IS_GROUP_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GROUP_LAYER)) +#define GIMP_IS_GROUP_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GROUP_LAYER)) +#define GIMP_GROUP_LAYER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GROUP_LAYER, GimpGroupLayerClass)) + + +typedef struct _GimpGroupLayerClass GimpGroupLayerClass; + +struct _GimpGroupLayer +{ + GimpLayer parent_instance; +}; + +struct _GimpGroupLayerClass +{ + GimpLayerClass parent_class; +}; + + +GType gimp_group_layer_get_type (void) G_GNUC_CONST; + +GimpLayer * gimp_group_layer_new (GimpImage *image); + +GimpProjection * gimp_group_layer_get_projection (GimpGroupLayer *group); + +void gimp_group_layer_suspend_resize (GimpGroupLayer *group, + gboolean push_undo); +void gimp_group_layer_resume_resize (GimpGroupLayer *group, + gboolean push_undo); + +void gimp_group_layer_suspend_mask (GimpGroupLayer *group, + gboolean push_undo); +void gimp_group_layer_resume_mask (GimpGroupLayer *group, + gboolean push_undo); + + +void _gimp_group_layer_set_suspended_mask (GimpGroupLayer *group, + GeglBuffer *buffer, + const GeglRectangle *bounds); +GeglBuffer * _gimp_group_layer_get_suspended_mask (GimpGroupLayer *group, + GeglRectangle *bounds); + +void _gimp_group_layer_start_transform (GimpGroupLayer *group, + gboolean push_undo); +void _gimp_group_layer_end_transform (GimpGroupLayer *group, + gboolean push_undo); + + +#endif /* __GIMP_GROUP_LAYER_H__ */ diff --git a/app/core/gimpgrouplayerundo.c b/app/core/gimpgrouplayerundo.c new file mode 100644 index 0000000..7ea70f4 --- /dev/null +++ b/app/core/gimpgrouplayerundo.c @@ -0,0 +1,253 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpimage.h" +#include "gimpgrouplayer.h" +#include "gimpgrouplayerundo.h" + + +static void gimp_group_layer_undo_constructed (GObject *object); + +static gint64 gimp_group_layer_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_group_layer_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_group_layer_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpGroupLayerUndo, gimp_group_layer_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_group_layer_undo_parent_class + + +static void +gimp_group_layer_undo_class_init (GimpGroupLayerUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_group_layer_undo_constructed; + + gimp_object_class->get_memsize = gimp_group_layer_undo_get_memsize; + + undo_class->pop = gimp_group_layer_undo_pop; + undo_class->free = gimp_group_layer_undo_free; +} + +static void +gimp_group_layer_undo_init (GimpGroupLayerUndo *undo) +{ +} + +static void +gimp_group_layer_undo_constructed (GObject *object) +{ + GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (object); + GimpGroupLayer *group; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + g_return_if_fail (GIMP_IS_GROUP_LAYER (GIMP_ITEM_UNDO (object)->item)); + + group = GIMP_GROUP_LAYER (GIMP_ITEM_UNDO (object)->item); + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE: + case GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE: + case GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK: + case GIMP_UNDO_GROUP_LAYER_START_TRANSFORM: + case GIMP_UNDO_GROUP_LAYER_END_TRANSFORM: + break; + + case GIMP_UNDO_GROUP_LAYER_RESUME_MASK: + group_layer_undo->mask_buffer = + _gimp_group_layer_get_suspended_mask(group, + &group_layer_undo->mask_bounds); + + if (group_layer_undo->mask_buffer) + g_object_ref (group_layer_undo->mask_buffer); + break; + + case GIMP_UNDO_GROUP_LAYER_CONVERT: + group_layer_undo->prev_type = gimp_drawable_get_base_type (GIMP_DRAWABLE (group)); + group_layer_undo->prev_precision = gimp_drawable_get_precision (GIMP_DRAWABLE (group)); + group_layer_undo->prev_has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (group)); + break; + + default: + g_return_if_reached (); + } +} + +static gint64 +gimp_group_layer_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (group_layer_undo->mask_buffer); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_group_layer_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (undo); + GimpGroupLayer *group; + + group = GIMP_GROUP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE: + case GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE: + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE)) + { + /* resume group layer auto-resizing */ + + gimp_group_layer_resume_resize (group, FALSE); + } + else + { + /* suspend group layer auto-resizing */ + + gimp_group_layer_suspend_resize (group, FALSE); + + if (undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE && + group_layer_undo->mask_buffer) + { + GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (group)); + + gimp_drawable_set_buffer_full (GIMP_DRAWABLE (mask), + FALSE, NULL, + group_layer_undo->mask_buffer, + &group_layer_undo->mask_bounds, + TRUE); + } + } + break; + + case GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK: + case GIMP_UNDO_GROUP_LAYER_RESUME_MASK: + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_MASK)) + { + /* resume group layer mask auto-resizing */ + + gimp_group_layer_resume_mask (group, FALSE); + } + else + { + /* suspend group layer mask auto-resizing */ + + gimp_group_layer_suspend_mask (group, FALSE); + + if (undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_MASK && + group_layer_undo->mask_buffer) + { + _gimp_group_layer_set_suspended_mask ( + group, + group_layer_undo->mask_buffer, + &group_layer_undo->mask_bounds); + } + } + break; + + case GIMP_UNDO_GROUP_LAYER_START_TRANSFORM: + case GIMP_UNDO_GROUP_LAYER_END_TRANSFORM: + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_START_TRANSFORM) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_GROUP_LAYER_END_TRANSFORM)) + { + /* end group layer transform operation */ + + _gimp_group_layer_end_transform (group, FALSE); + } + else + { + /* start group layer transform operation */ + + _gimp_group_layer_start_transform (group, FALSE); + } + break; + + case GIMP_UNDO_GROUP_LAYER_CONVERT: + { + GimpImageBaseType type; + GimpPrecision precision; + gboolean has_alpha; + + type = gimp_drawable_get_base_type (GIMP_DRAWABLE (group)); + precision = gimp_drawable_get_precision (GIMP_DRAWABLE (group)); + has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (group)); + + gimp_drawable_convert_type (GIMP_DRAWABLE (group), + gimp_item_get_image (GIMP_ITEM (group)), + group_layer_undo->prev_type, + group_layer_undo->prev_precision, + group_layer_undo->prev_has_alpha, + NULL, + 0, 0, + FALSE, NULL); + + group_layer_undo->prev_type = type; + group_layer_undo->prev_precision = precision; + group_layer_undo->prev_has_alpha = has_alpha; + } + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_group_layer_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (undo); + + g_clear_object (&group_layer_undo->mask_buffer); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpgrouplayerundo.h b/app/core/gimpgrouplayerundo.h new file mode 100644 index 0000000..14c7043 --- /dev/null +++ b/app/core/gimpgrouplayerundo.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 . + */ + +#ifndef __GIMP_GROUP_LAYER_UNDO_H__ +#define __GIMP_GROUP_LAYER_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_GROUP_LAYER_UNDO (gimp_group_layer_undo_get_type ()) +#define GIMP_GROUP_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GROUP_LAYER_UNDO, GimpGroupLayerUndo)) +#define GIMP_GROUP_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GROUP_LAYER_UNDO, GimpGroupLayerUndoClass)) +#define GIMP_IS_GROUP_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GROUP_LAYER_UNDO)) +#define GIMP_IS_GROUP_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GROUP_LAYER_UNDO)) +#define GIMP_GROUP_LAYER_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GROUP_LAYER_UNDO, GimpGroupLayerUndoClass)) + + +typedef struct _GimpGroupLayerUndo GimpGroupLayerUndo; +typedef struct _GimpGroupLayerUndoClass GimpGroupLayerUndoClass; + +struct _GimpGroupLayerUndo +{ + GimpItemUndo parent_instance; + + GeglBuffer *mask_buffer; + GeglRectangle mask_bounds; + + GimpImageBaseType prev_type; + GimpPrecision prev_precision; + gboolean prev_has_alpha; +}; + +struct _GimpGroupLayerUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_group_layer_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_GROUP_LAYER_UNDO_H__ */ diff --git a/app/core/gimpguide.c b/app/core/gimpguide.c new file mode 100644 index 0000000..1438958 --- /dev/null +++ b/app/core/gimpguide.c @@ -0,0 +1,242 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGuide + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpguide.h" + + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_POSITION, + PROP_STYLE +}; + + +struct _GimpGuidePrivate +{ + GimpOrientationType orientation; + gint position; + + GimpGuideStyle style; +}; + + +static void gimp_guide_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_guide_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpGuide, gimp_guide, GIMP_TYPE_AUX_ITEM) + + +static void +gimp_guide_class_init (GimpGuideClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gimp_guide_get_property; + object_class->set_property = gimp_guide_set_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_ORIENTATION, + "orientation", + NULL, NULL, + GIMP_TYPE_ORIENTATION_TYPE, + GIMP_ORIENTATION_UNKNOWN, + 0); + + GIMP_CONFIG_PROP_INT (object_class, PROP_POSITION, + "position", + NULL, NULL, + GIMP_GUIDE_POSITION_UNDEFINED, + GIMP_MAX_IMAGE_SIZE, + GIMP_GUIDE_POSITION_UNDEFINED, + 0); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_STYLE, + "style", + NULL, NULL, + GIMP_TYPE_GUIDE_STYLE, + GIMP_GUIDE_STYLE_NONE, + 0); +} + +static void +gimp_guide_init (GimpGuide *guide) +{ + guide->priv = gimp_guide_get_instance_private (guide); +} + +static void +gimp_guide_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpGuide *guide = GIMP_GUIDE (object); + + switch (property_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, guide->priv->orientation); + break; + case PROP_POSITION: + g_value_set_int (value, guide->priv->position); + break; + case PROP_STYLE: + g_value_set_enum (value, guide->priv->style); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_guide_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpGuide *guide = GIMP_GUIDE (object); + + switch (property_id) + { + case PROP_ORIENTATION: + guide->priv->orientation = g_value_get_enum (value); + break; + case PROP_POSITION: + guide->priv->position = g_value_get_int (value); + break; + case PROP_STYLE: + guide->priv->style = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +GimpGuide * +gimp_guide_new (GimpOrientationType orientation, + guint32 guide_ID) +{ + return g_object_new (GIMP_TYPE_GUIDE, + "id", guide_ID, + "orientation", orientation, + "style", GIMP_GUIDE_STYLE_NORMAL, + NULL); +} + +/** + * gimp_guide_custom_new: + * @orientation: the #GimpOrientationType + * @guide_ID: the unique guide ID + * @guide_style: the #GimpGuideStyle + * + * This function returns a new guide and will flag it as "custom". + * Custom guides are used for purpose "other" than the basic guides + * a user can create oneself, for instance as symmetry guides, to + * drive GEGL ops, etc. + * They are not saved in the XCF file. If an op, a symmetry or a plugin + * wishes to save its state, it has to do it internally. + * Moreover they don't follow guide snapping settings and never snap. + * + * Returns: the custom #GimpGuide. + **/ +GimpGuide * +gimp_guide_custom_new (GimpOrientationType orientation, + guint32 guide_ID, + GimpGuideStyle guide_style) +{ + return g_object_new (GIMP_TYPE_GUIDE, + "id", guide_ID, + "orientation", orientation, + "style", guide_style, + NULL); +} + +GimpOrientationType +gimp_guide_get_orientation (GimpGuide *guide) +{ + g_return_val_if_fail (GIMP_IS_GUIDE (guide), GIMP_ORIENTATION_UNKNOWN); + + return guide->priv->orientation; +} + +void +gimp_guide_set_orientation (GimpGuide *guide, + GimpOrientationType orientation) +{ + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + guide->priv->orientation = orientation; + + g_object_notify (G_OBJECT (guide), "orientation"); +} + +gint +gimp_guide_get_position (GimpGuide *guide) +{ + g_return_val_if_fail (GIMP_IS_GUIDE (guide), GIMP_GUIDE_POSITION_UNDEFINED); + + return guide->priv->position; +} + +void +gimp_guide_set_position (GimpGuide *guide, + gint position) +{ + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + guide->priv->position = position; + + g_object_notify (G_OBJECT (guide), "position"); +} + +GimpGuideStyle +gimp_guide_get_style (GimpGuide *guide) +{ + g_return_val_if_fail (GIMP_IS_GUIDE (guide), GIMP_GUIDE_STYLE_NONE); + + return guide->priv->style; +} + +gboolean +gimp_guide_is_custom (GimpGuide *guide) +{ + g_return_val_if_fail (GIMP_IS_GUIDE (guide), FALSE); + + return guide->priv->style != GIMP_GUIDE_STYLE_NORMAL; +} diff --git a/app/core/gimpguide.h b/app/core/gimpguide.h new file mode 100644 index 0000000..285e8eb --- /dev/null +++ b/app/core/gimpguide.h @@ -0,0 +1,75 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGuide + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GUIDE_H__ +#define __GIMP_GUIDE_H__ + + +#include "gimpauxitem.h" + + +#define GIMP_GUIDE_POSITION_UNDEFINED G_MININT + + +#define GIMP_TYPE_GUIDE (gimp_guide_get_type ()) +#define GIMP_GUIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GUIDE, GimpGuide)) +#define GIMP_GUIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GUIDE, GimpGuideClass)) +#define GIMP_IS_GUIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GUIDE)) +#define GIMP_IS_GUIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GUIDE)) +#define GIMP_GUIDE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GUIDE, GimpGuideClass)) + + +typedef struct _GimpGuidePrivate GimpGuidePrivate; +typedef struct _GimpGuideClass GimpGuideClass; + +struct _GimpGuide +{ + GimpAuxItem parent_instance; + + GimpGuidePrivate *priv; +}; + +struct _GimpGuideClass +{ + GimpAuxItemClass parent_class; +}; + + +GType gimp_guide_get_type (void) G_GNUC_CONST; + +GimpGuide * gimp_guide_new (GimpOrientationType orientation, + guint32 guide_ID); +GimpGuide * gimp_guide_custom_new (GimpOrientationType orientation, + guint32 guide_ID, + GimpGuideStyle guide_style); + +GimpOrientationType gimp_guide_get_orientation (GimpGuide *guide); +void gimp_guide_set_orientation (GimpGuide *guide, + GimpOrientationType orientation); + +gint gimp_guide_get_position (GimpGuide *guide); +void gimp_guide_set_position (GimpGuide *guide, + gint position); + +GimpGuideStyle gimp_guide_get_style (GimpGuide *guide); +gboolean gimp_guide_is_custom (GimpGuide *guide); + + +#endif /* __GIMP_GUIDE_H__ */ diff --git a/app/core/gimpguideundo.c b/app/core/gimpguideundo.c new file mode 100644 index 0000000..c3e3de4 --- /dev/null +++ b/app/core/gimpguideundo.c @@ -0,0 +1,116 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpguide.h" +#include "gimpguideundo.h" + + +static void gimp_guide_undo_constructed (GObject *object); + +static void gimp_guide_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpGuideUndo, gimp_guide_undo, + GIMP_TYPE_AUX_ITEM_UNDO) + +#define parent_class gimp_guide_undo_parent_class + + +static void +gimp_guide_undo_class_init (GimpGuideUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_guide_undo_constructed; + + undo_class->pop = gimp_guide_undo_pop; +} + +static void +gimp_guide_undo_init (GimpGuideUndo *undo) +{ +} + +static void +gimp_guide_undo_constructed (GObject *object) +{ + GimpGuideUndo *guide_undo = GIMP_GUIDE_UNDO (object); + GimpGuide *guide; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + guide = GIMP_GUIDE (GIMP_AUX_ITEM_UNDO (object)->aux_item); + + gimp_assert (GIMP_IS_GUIDE (guide)); + + guide_undo->orientation = gimp_guide_get_orientation (guide); + guide_undo->position = gimp_guide_get_position (guide); +} + +static void +gimp_guide_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpGuideUndo *guide_undo = GIMP_GUIDE_UNDO (undo); + GimpGuide *guide; + GimpOrientationType orientation; + gint position; + gboolean moved = FALSE; + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + guide = GIMP_GUIDE (GIMP_AUX_ITEM_UNDO (undo)->aux_item); + + orientation = gimp_guide_get_orientation (guide); + position = gimp_guide_get_position (guide); + + if (position == GIMP_GUIDE_POSITION_UNDEFINED) + { + gimp_image_add_guide (undo->image, guide, guide_undo->position); + } + else if (guide_undo->position == GIMP_GUIDE_POSITION_UNDEFINED) + { + gimp_image_remove_guide (undo->image, guide, FALSE); + } + else + { + gimp_guide_set_position (guide, guide_undo->position); + + moved = TRUE; + } + + gimp_guide_set_orientation (guide, guide_undo->orientation); + + if (moved || guide_undo->orientation != orientation) + gimp_image_guide_moved (undo->image, guide); + + guide_undo->position = position; + guide_undo->orientation = orientation; +} diff --git a/app/core/gimpguideundo.h b/app/core/gimpguideundo.h new file mode 100644 index 0000000..da77ff3 --- /dev/null +++ b/app/core/gimpguideundo.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __GIMP_GUIDE_UNDO_H__ +#define __GIMP_GUIDE_UNDO_H__ + + +#include "gimpauxitemundo.h" + + +#define GIMP_TYPE_GUIDE_UNDO (gimp_guide_undo_get_type ()) +#define GIMP_GUIDE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GUIDE_UNDO, GimpGuideUndo)) +#define GIMP_GUIDE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GUIDE_UNDO, GimpGuideUndoClass)) +#define GIMP_IS_GUIDE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GUIDE_UNDO)) +#define GIMP_IS_GUIDE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GUIDE_UNDO)) +#define GIMP_GUIDE_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GUIDE_UNDO, GimpGuideUndoClass)) + + +typedef struct _GimpGuideUndo GimpGuideUndo; +typedef struct _GimpGuideUndoClass GimpGuideUndoClass; + +struct _GimpGuideUndo +{ + GimpAuxItemUndo parent_instance; + + GimpOrientationType orientation; + gint position; +}; + +struct _GimpGuideUndoClass +{ + GimpAuxItemUndoClass parent_class; +}; + + +GType gimp_guide_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_GUIDE_UNDO_H__ */ diff --git a/app/core/gimphistogram.c b/app/core/gimphistogram.c new file mode 100644 index 0000000..571679b --- /dev/null +++ b/app/core/gimphistogram.c @@ -0,0 +1,1261 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphistogram module Copyright (C) 1999 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp-atomic.h" +#include "gimp-parallel.h" +#include "gimpasync.h" +#include "gimphistogram.h" +#include "gimpwaitable.h" + + +#define MAX_N_COMPONENTS 4 +#define N_DERIVED_CHANNELS 2 + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + + +enum +{ + PROP_0, + PROP_N_COMPONENTS, + PROP_N_BINS, + PROP_VALUES +}; + +struct _GimpHistogramPrivate +{ + gboolean linear; + gint n_channels; + gint n_bins; + gdouble *values; + GimpAsync *calculate_async; +}; + +typedef struct +{ + /* input */ + GimpHistogram *histogram; + GeglBuffer *buffer; + GeglRectangle buffer_rect; + GeglBuffer *mask; + GeglRectangle mask_rect; + + /* output */ + gint n_components; + gint n_bins; + gdouble *values; +} CalculateContext; + +typedef struct +{ + GimpAsync *async; + CalculateContext *context; + + const Babl *format; + GSList *values_list; +} CalculateData; + + +/* local function prototypes */ + +static void gimp_histogram_finalize (GObject *object); +static void gimp_histogram_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_histogram_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_histogram_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_histogram_map_channel (GimpHistogram *histogram, + GimpHistogramChannel *channel); + +static void gimp_histogram_set_values (GimpHistogram *histogram, + gint n_components, + gint n_bins, + gdouble *values); + +static void gimp_histogram_calculate_internal (GimpAsync *async, + CalculateContext *context); +static void gimp_histogram_calculate_area (const GeglRectangle *area, + CalculateData *data); +static void gimp_histogram_calculate_async_callback (GimpAsync *async, + CalculateContext *context); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT) + +#define parent_class gimp_histogram_parent_class + + +static void +gimp_histogram_class_init (GimpHistogramClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + object_class->finalize = gimp_histogram_finalize; + object_class->set_property = gimp_histogram_set_property; + object_class->get_property = gimp_histogram_get_property; + + gimp_object_class->get_memsize = gimp_histogram_get_memsize; + + g_object_class_install_property (object_class, PROP_N_COMPONENTS, + g_param_spec_int ("n-components", NULL, NULL, + 0, MAX_N_COMPONENTS, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_N_BINS, + g_param_spec_int ("n-bins", NULL, NULL, + 256, 1024, 1024, + GIMP_PARAM_READABLE)); + + /* this is just for notifications */ + g_object_class_install_property (object_class, PROP_VALUES, + g_param_spec_boolean ("values", NULL, NULL, + FALSE, + G_PARAM_READABLE)); +} + +static void +gimp_histogram_init (GimpHistogram *histogram) +{ + histogram->priv = gimp_histogram_get_instance_private (histogram); +} + +static void +gimp_histogram_finalize (GObject *object) +{ + GimpHistogram *histogram = GIMP_HISTOGRAM (object); + + gimp_histogram_clear_values (histogram, 0); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_histogram_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_histogram_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpHistogram *histogram = GIMP_HISTOGRAM (object); + + switch (property_id) + { + case PROP_N_COMPONENTS: + g_value_set_int (value, gimp_histogram_n_components (histogram)); + break; + + case PROP_N_BINS: + g_value_set_int (value, histogram->priv->n_bins); + break; + + case PROP_VALUES: + /* return a silly boolean */ + g_value_set_boolean (value, histogram->priv->values != NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_histogram_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpHistogram *histogram = GIMP_HISTOGRAM (object); + gint64 memsize = 0; + + if (histogram->priv->values) + memsize += (histogram->priv->n_channels * + histogram->priv->n_bins * sizeof (gdouble)); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +/* public functions */ + +GimpHistogram * +gimp_histogram_new (gboolean linear) +{ + GimpHistogram *histogram = g_object_new (GIMP_TYPE_HISTOGRAM, NULL); + + histogram->priv->linear = linear; + + return histogram; +} + +/** + * gimp_histogram_duplicate: + * @histogram: a %GimpHistogram + * + * Creates a duplicate of @histogram. The duplicate has a reference + * count of 1 and contains the values from @histogram. + * + * Return value: a newly allocated %GimpHistogram + **/ +GimpHistogram * +gimp_histogram_duplicate (GimpHistogram *histogram) +{ + GimpHistogram *dup; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL); + + if (histogram->priv->calculate_async) + gimp_waitable_wait (GIMP_WAITABLE (histogram->priv->calculate_async)); + + dup = gimp_histogram_new (histogram->priv->linear); + + dup->priv->n_channels = histogram->priv->n_channels; + dup->priv->n_bins = histogram->priv->n_bins; + dup->priv->values = g_memdup (histogram->priv->values, + sizeof (gdouble) * + dup->priv->n_channels * + dup->priv->n_bins); + + return dup; +} + +void +gimp_histogram_calculate (GimpHistogram *histogram, + GeglBuffer *buffer, + const GeglRectangle *buffer_rect, + GeglBuffer *mask, + const GeglRectangle *mask_rect) +{ + CalculateContext context = {}; + + g_return_if_fail (GIMP_IS_HISTOGRAM (histogram)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (buffer_rect != NULL); + + if (histogram->priv->calculate_async) + gimp_async_cancel_and_wait (histogram->priv->calculate_async); + + context.histogram = histogram; + context.buffer = buffer; + context.buffer_rect = *buffer_rect; + + if (mask) + { + context.mask = mask; + + if (mask_rect) + context.mask_rect = *mask_rect; + else + context.mask_rect = *gegl_buffer_get_extent (mask); + } + + gimp_histogram_calculate_internal (NULL, &context); + + gimp_histogram_set_values (histogram, + context.n_components, context.n_bins, + context.values); +} + +GimpAsync * +gimp_histogram_calculate_async (GimpHistogram *histogram, + GeglBuffer *buffer, + const GeglRectangle *buffer_rect, + GeglBuffer *mask, + const GeglRectangle *mask_rect) +{ + CalculateContext *context; + GeglRectangle rect; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (buffer_rect != NULL, NULL); + + if (histogram->priv->calculate_async) + gimp_async_cancel_and_wait (histogram->priv->calculate_async); + + gegl_rectangle_align_to_buffer (&rect, buffer_rect, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + context = g_slice_new0 (CalculateContext); + + context->histogram = histogram; + context->buffer = gegl_buffer_new (&rect, + gegl_buffer_get_format (buffer)); + context->buffer_rect = *buffer_rect; + + gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE, + context->buffer, NULL); + + if (mask) + { + if (mask_rect) + context->mask_rect = *mask_rect; + else + context->mask_rect = *gegl_buffer_get_extent (mask); + + gegl_rectangle_align_to_buffer (&rect, &context->mask_rect, mask, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + context->mask = gegl_buffer_new (&rect, gegl_buffer_get_format (mask)); + + gimp_gegl_buffer_copy (mask, &rect, GEGL_ABYSS_NONE, + context->mask, NULL); + } + + histogram->priv->calculate_async = gimp_parallel_run_async ( + (GimpRunAsyncFunc) gimp_histogram_calculate_internal, + context); + + gimp_async_add_callback ( + histogram->priv->calculate_async, + (GimpAsyncCallback) gimp_histogram_calculate_async_callback, + context); + + return histogram->priv->calculate_async; +} + +void +gimp_histogram_clear_values (GimpHistogram *histogram, + gint n_components) +{ + g_return_if_fail (GIMP_IS_HISTOGRAM (histogram)); + + if (histogram->priv->calculate_async) + gimp_async_cancel_and_wait (histogram->priv->calculate_async); + + gimp_histogram_set_values (histogram, n_components, 0, NULL); +} + + +#define HISTOGRAM_VALUE(c,i) (priv->values[(c) * priv->n_bins + (i)]) + + +gdouble +gimp_histogram_get_maximum (GimpHistogram *histogram, + GimpHistogramChannel channel) +{ + GimpHistogramPrivate *priv; + gdouble max = 0.0; + gint x; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + priv = histogram->priv; + + if (! priv->values || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + if (channel == GIMP_HISTOGRAM_RGB) + { + for (x = 0; x < priv->n_bins; x++) + { + max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, x)); + max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, x)); + max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, x)); + } + } + else + { + for (x = 0; x < priv->n_bins; x++) + { + max = MAX (max, HISTOGRAM_VALUE (channel, x)); + } + } + + return max; +} + +gdouble +gimp_histogram_get_value (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint bin) +{ + GimpHistogramPrivate *priv; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + priv = histogram->priv; + + if (! priv->values || + (bin < 0 || bin >= priv->n_bins) || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + if (channel == GIMP_HISTOGRAM_RGB) + { + gdouble min = HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, bin); + + min = MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, bin)); + + return MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, bin)); + } + else + { + return HISTOGRAM_VALUE (channel, bin); + } +} + +gdouble +gimp_histogram_get_component (GimpHistogram *histogram, + gint component, + gint bin) +{ + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + if (gimp_histogram_n_components (histogram) > 2) + component++; + + return gimp_histogram_get_value (histogram, component, bin); +} + +gint +gimp_histogram_n_components (GimpHistogram *histogram) +{ + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0); + + if (histogram->priv->n_channels > 0) + return histogram->priv->n_channels - N_DERIVED_CHANNELS; + else + return 0; +} + +gint +gimp_histogram_n_bins (GimpHistogram *histogram) +{ + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0); + + return histogram->priv->n_bins; +} + +gboolean +gimp_histogram_has_channel (GimpHistogram *histogram, + GimpHistogramChannel channel) +{ + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), FALSE); + + switch (channel) + { + case GIMP_HISTOGRAM_VALUE: + return TRUE; + + case GIMP_HISTOGRAM_RED: + case GIMP_HISTOGRAM_GREEN: + case GIMP_HISTOGRAM_BLUE: + case GIMP_HISTOGRAM_LUMINANCE: + case GIMP_HISTOGRAM_RGB: + return gimp_histogram_n_components (histogram) >= 3; + + case GIMP_HISTOGRAM_ALPHA: + return gimp_histogram_n_components (histogram) == 2 || + gimp_histogram_n_components (histogram) == 4; + } + + g_return_val_if_reached (FALSE); +} + +gdouble +gimp_histogram_get_count (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end) +{ + GimpHistogramPrivate *priv; + gint i; + gdouble count = 0.0; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + priv = histogram->priv; + + if (! priv->values || + start > end || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + if (channel == GIMP_HISTOGRAM_RGB) + return (gimp_histogram_get_count (histogram, + GIMP_HISTOGRAM_RED, start, end) + + gimp_histogram_get_count (histogram, + GIMP_HISTOGRAM_GREEN, start, end) + + gimp_histogram_get_count (histogram, + GIMP_HISTOGRAM_BLUE, start, end)); + + start = CLAMP (start, 0, priv->n_bins - 1); + end = CLAMP (end, 0, priv->n_bins - 1); + + for (i = start; i <= end; i++) + count += HISTOGRAM_VALUE (channel, i); + + return count; +} + +gdouble +gimp_histogram_get_mean (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end) +{ + GimpHistogramPrivate *priv; + gint i; + gdouble mean = 0.0; + gdouble count; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + priv = histogram->priv; + + if (! priv->values || + start > end || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + start = CLAMP (start, 0, priv->n_bins - 1); + end = CLAMP (end, 0, priv->n_bins - 1); + + if (channel == GIMP_HISTOGRAM_RGB) + { + for (i = start; i <= end; i++) + { + gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1); + + mean += (factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) + + factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) + + factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i)); + } + } + else + { + for (i = start; i <= end; i++) + { + gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1); + + mean += factor * HISTOGRAM_VALUE (channel, i); + } + } + + count = gimp_histogram_get_count (histogram, channel, start, end); + + if (count > 0.0) + return mean / count; + + return mean; +} + +gdouble +gimp_histogram_get_median (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end) +{ + GimpHistogramPrivate *priv; + gint i; + gdouble sum = 0.0; + gdouble count; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1.0); + + priv = histogram->priv; + + if (! priv->values || + start > end || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + start = CLAMP (start, 0, priv->n_bins - 1); + end = CLAMP (end, 0, priv->n_bins - 1); + + count = gimp_histogram_get_count (histogram, channel, start, end); + + if (channel == GIMP_HISTOGRAM_RGB) + { + for (i = start; i <= end; i++) + { + sum += (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i)); + + if (sum * 2 > count) + return ((gdouble) i / (gdouble) (priv->n_bins - 1)); + } + } + else + { + for (i = start; i <= end; i++) + { + sum += HISTOGRAM_VALUE (channel, i); + + if (sum * 2 > count) + return ((gdouble) i / (gdouble) (priv->n_bins - 1)); + } + } + + return -1.0; +} + +/* + * adapted from GNU ocrad 0.14 : page_image_io.cc : otsu_th + * + * N. Otsu, "A threshold selection method from gray-level histograms," + * IEEE Trans. Systems, Man, and Cybernetics, vol. 9, no. 1, pp. 62-66, 1979. + */ +gdouble +gimp_histogram_get_threshold (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end) +{ + GimpHistogramPrivate *priv; + gint i; + gint maxval; + gdouble *hist = NULL; + gdouble *chist = NULL; + gdouble *cmom = NULL; + gdouble hist_max = 0.0; + gdouble chist_max = 0.0; + gdouble cmom_max = 0.0; + gdouble bvar_max = 0.0; + gint threshold = 127; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1); + + priv = histogram->priv; + + if (! priv->values || + start > end || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0; + } + + start = CLAMP (start, 0, priv->n_bins - 1); + end = CLAMP (end, 0, priv->n_bins - 1); + + maxval = end - start; + + hist = g_newa (gdouble, maxval + 1); + chist = g_newa (gdouble, maxval + 1); + cmom = g_newa (gdouble, maxval + 1); + + if (channel == GIMP_HISTOGRAM_RGB) + { + for (i = start; i <= end; i++) + hist[i - start] = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i)); + } + else + { + for (i = start; i <= end; i++) + hist[i - start] = HISTOGRAM_VALUE (channel, i); + } + + hist_max = hist[0]; + chist[0] = hist[0]; + cmom[0] = 0; + + for (i = 1; i <= maxval; i++) + { + if (hist[i] > hist_max) + hist_max = hist[i]; + + chist[i] = chist[i-1] + hist[i]; + cmom[i] = cmom[i-1] + i * hist[i]; + } + + chist_max = chist[maxval]; + cmom_max = cmom[maxval]; + bvar_max = 0; + + for (i = 0; i < maxval; ++i) + { + if (chist[i] > 0 && chist[i] < chist_max) + { + gdouble bvar; + + bvar = (gdouble) cmom[i] / chist[i]; + bvar -= (cmom_max - cmom[i]) / (chist_max - chist[i]); + bvar *= bvar; + bvar *= chist[i]; + bvar *= chist_max - chist[i]; + + if (bvar > bvar_max) + { + bvar_max = bvar; + threshold = start + i; + } + } + } + + return threshold; +} + +gdouble +gimp_histogram_get_std_dev (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end) +{ + GimpHistogramPrivate *priv; + gint i; + gdouble dev = 0.0; + gdouble count; + gdouble mean; + + g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0); + + priv = histogram->priv; + + if (! priv->values || + start > end || + ! gimp_histogram_map_channel (histogram, &channel)) + { + return 0.0; + } + + mean = gimp_histogram_get_mean (histogram, channel, start, end); + count = gimp_histogram_get_count (histogram, channel, start, end); + + if (count == 0.0) + count = 1.0; + + for (i = start; i <= end; i++) + { + gdouble value; + + if (channel == GIMP_HISTOGRAM_RGB) + { + value = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) + + HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i)); + } + else + { + value = gimp_histogram_get_value (histogram, channel, i); + } + + dev += value * SQR (((gdouble) i / (gdouble) (priv->n_bins - 1)) - mean); + } + + return sqrt (dev / count); +} + + +/* private functions */ + +static gboolean +gimp_histogram_map_channel (GimpHistogram *histogram, + GimpHistogramChannel *channel) +{ + GimpHistogramPrivate *priv = histogram->priv; + + if (*channel == GIMP_HISTOGRAM_RGB) + return gimp_histogram_n_components (histogram) >= 3; + + switch (*channel) + { + case GIMP_HISTOGRAM_ALPHA: + if (gimp_histogram_n_components (histogram) == 2) + *channel = 1; + break; + + case GIMP_HISTOGRAM_LUMINANCE: + *channel = gimp_histogram_n_components (histogram) + 1; + break; + + default: + break; + } + + return *channel < priv->n_channels; +} + +static void +gimp_histogram_set_values (GimpHistogram *histogram, + gint n_components, + gint n_bins, + gdouble *values) +{ + GimpHistogramPrivate *priv = histogram->priv; + gint n_channels = n_components; + gboolean notify_n_components = FALSE; + gboolean notify_n_bins = FALSE; + + if (n_channels > 0) + n_channels += N_DERIVED_CHANNELS; + + if (n_channels != priv->n_channels) + { + priv->n_channels = n_channels; + + notify_n_components = TRUE; + } + + if (n_bins != priv->n_bins) + { + priv->n_bins = n_bins; + + notify_n_bins = TRUE; + } + + if (values != priv->values) + { + if (priv->values) + g_free (priv->values); + + priv->values = values; + } + + if (notify_n_components) + g_object_notify (G_OBJECT (histogram), "n-components"); + + if (notify_n_bins) + g_object_notify (G_OBJECT (histogram), "n-bins"); + + g_object_notify (G_OBJECT (histogram), "values"); +} + +static void +gimp_histogram_calculate_internal (GimpAsync *async, + CalculateContext *context) +{ + CalculateData data; + GimpHistogramPrivate *priv; + const Babl *format; + + priv = context->histogram->priv; + + format = gegl_buffer_get_format (context->buffer); + + if (babl_format_get_type (format, 0) == babl_type ("u8")) + context->n_bins = 256; + else + context->n_bins = 1024; + + if (babl_format_is_palette (format)) + { + if (babl_format_has_alpha (format)) + { + if (priv->linear) + format = babl_format ("RGB float"); + else + format = babl_format ("R'G'B' float"); + } + else + { + if (priv->linear) + format = babl_format ("RGBA float"); + else + format = babl_format ("R'G'B'A float"); + } + } + else + { + const Babl *model = babl_format_get_model (format); + + if (model == babl_model ("Y") || + model == babl_model ("Y'")) + { + if (priv->linear) + format = babl_format ("Y float"); + else + format = babl_format ("Y' float"); + } + else if (model == babl_model ("YA") || + model == babl_model ("Y'A")) + { + if (priv->linear) + format = babl_format ("YA float"); + else + format = babl_format ("Y'A float"); + } + else if (model == babl_model ("RGB") || + model == babl_model ("R'G'B'")) + { + if (priv->linear) + format = babl_format ("RGB float"); + else + format = babl_format ("R'G'B' float"); + } + else if (model == babl_model ("RGBA") || + model == babl_model ("R'G'B'A")) + { + if (priv->linear) + format = babl_format ("RGBA float"); + else + format = babl_format ("R'G'B'A float"); + } + else + { + if (async) + gimp_async_abort (async); + + g_return_if_reached (); + } + } + + context->n_components = babl_format_get_n_components (format); + + data.async = async; + data.context = context; + data.format = format; + data.values_list = NULL; + + gegl_parallel_distribute_area ( + &context->buffer_rect, PIXELS_PER_THREAD, GEGL_SPLIT_STRATEGY_AUTO, + (GeglParallelDistributeAreaFunc) gimp_histogram_calculate_area, + &data); + + if (! async || ! gimp_async_is_canceled (async)) + { + gdouble *total_values = NULL; + gint n_values = (context->n_components + N_DERIVED_CHANNELS) * + context->n_bins; + GSList *iter; + + for (iter = data.values_list; iter; iter = g_slist_next (iter)) + { + gdouble *values = iter->data; + + if (! total_values) + { + total_values = values; + } + else + { + gint i; + + for (i = 0; i < n_values; i++) + total_values[i] += values[i]; + + g_free (values); + } + } + + g_slist_free (data.values_list); + + context->values = total_values; + + if (async) + gimp_async_finish (async, NULL); + } + else + { + g_slist_free_full (data.values_list, g_free); + + if (async) + gimp_async_abort (async); + } +} + +static void +gimp_histogram_calculate_area (const GeglRectangle *area, + CalculateData *data) +{ + GimpAsync *async; + CalculateContext *context; + GeglBufferIterator *iter; + gdouble *values; + gint n_components; + gint n_bins; + gfloat n_bins_1f; + gfloat temp; + + async = data->async; + context = data->context; + + n_bins = context->n_bins; + n_components = context->n_components; + + values = g_new0 (gdouble, (n_components + N_DERIVED_CHANNELS) * n_bins); + gimp_atomic_slist_push_head (&data->values_list, values); + + iter = gegl_buffer_iterator_new (context->buffer, area, 0, + data->format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + if (context->mask) + { + GeglRectangle mask_area = *area; + + mask_area.x += context->mask_rect.x - context->buffer_rect.x; + mask_area.y += context->mask_rect.y - context->buffer_rect.y; + + gegl_buffer_iterator_add (iter, context->mask, &mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + } + + n_bins_1f = n_bins - 1; + +#define VALUE(c,i) (*(temp = (i) * n_bins_1f, \ + &values[(c) * n_bins + \ + SIGNED_ROUND (SAFE_CLAMP (temp, \ + 0.0f, \ + n_bins_1f))])) + +#define CHECK_CANCELED(length) \ + G_STMT_START \ + { \ + if ((length) % 128 == 0 && async && gimp_async_is_canceled (async)) \ + { \ + gegl_buffer_iterator_stop (iter); \ + \ + return; \ + } \ + } \ + G_STMT_END + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *data = iter->items[0].data; + gint length = iter->length; + gfloat max; + gfloat luminance; + + CHECK_CANCELED (0); + + if (context->mask) + { + const gfloat *mask_data = iter->items[1].data; + + switch (n_components) + { + case 1: + while (length--) + { + const gdouble masked = *mask_data; + + VALUE (0, data[0]) += masked; + + data += n_components; + mask_data += 1; + + CHECK_CANCELED (length); + } + break; + + case 2: + while (length--) + { + const gdouble masked = *mask_data; + const gdouble weight = data[1]; + + VALUE (0, data[0]) += weight * masked; + VALUE (1, data[1]) += masked; + + data += n_components; + mask_data += 1; + + CHECK_CANCELED (length); + } + break; + + case 3: /* calculate separate value values */ + while (length--) + { + const gdouble masked = *mask_data; + + VALUE (1, data[0]) += masked; + VALUE (2, data[1]) += masked; + VALUE (3, data[2]) += masked; + + max = MAX (data[0], data[1]); + max = MAX (data[2], max); + VALUE (0, max) += masked; + + luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]); + VALUE (4, luminance) += masked; + + data += n_components; + mask_data += 1; + + CHECK_CANCELED (length); + } + break; + + case 4: /* calculate separate value values */ + while (length--) + { + const gdouble masked = *mask_data; + const gdouble weight = data[3]; + + VALUE (1, data[0]) += weight * masked; + VALUE (2, data[1]) += weight * masked; + VALUE (3, data[2]) += weight * masked; + VALUE (4, data[3]) += masked; + + max = MAX (data[0], data[1]); + max = MAX (data[2], max); + VALUE (0, max) += weight * masked; + + luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]); + VALUE (5, luminance) += weight * masked; + + data += n_components; + mask_data += 1; + + CHECK_CANCELED (length); + } + break; + } + } + else /* no mask */ + { + switch (n_components) + { + case 1: + while (length--) + { + VALUE (0, data[0]) += 1.0; + + data += n_components; + + CHECK_CANCELED (length); + } + break; + + case 2: + while (length--) + { + const gdouble weight = data[1]; + + VALUE (0, data[0]) += weight; + VALUE (1, data[1]) += 1.0; + + data += n_components; + + CHECK_CANCELED (length); + } + break; + + case 3: /* calculate separate value values */ + while (length--) + { + VALUE (1, data[0]) += 1.0; + VALUE (2, data[1]) += 1.0; + VALUE (3, data[2]) += 1.0; + + max = MAX (data[0], data[1]); + max = MAX (data[2], max); + VALUE (0, max) += 1.0; + + luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]); + VALUE (4, luminance) += 1.0; + + data += n_components; + + CHECK_CANCELED (length); + } + break; + + case 4: /* calculate separate value values */ + while (length--) + { + const gdouble weight = data[3]; + + VALUE (1, data[0]) += weight; + VALUE (2, data[1]) += weight; + VALUE (3, data[2]) += weight; + VALUE (4, data[3]) += 1.0; + + max = MAX (data[0], data[1]); + max = MAX (data[2], max); + VALUE (0, max) += weight; + + luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]); + VALUE (5, luminance) += weight; + + data += n_components; + + CHECK_CANCELED (length); + } + break; + } + } + } + +#undef VALUE +#undef CHECK_CANCELED +} + +static void +gimp_histogram_calculate_async_callback (GimpAsync *async, + CalculateContext *context) +{ + context->histogram->priv->calculate_async = NULL; + + if (gimp_async_is_finished (async)) + { + gimp_histogram_set_values (context->histogram, + context->n_components, context->n_bins, + context->values); + } + + g_object_unref (context->buffer); + if (context->mask) + g_object_unref (context->mask); + + g_slice_free (CalculateContext, context); +} diff --git a/app/core/gimphistogram.h b/app/core/gimphistogram.h new file mode 100644 index 0000000..ba1e0ef --- /dev/null +++ b/app/core/gimphistogram.h @@ -0,0 +1,105 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphistogram module Copyright (C) 1999 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_HISTOGRAM_H__ +#define __GIMP_HISTOGRAM_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_HISTOGRAM (gimp_histogram_get_type ()) +#define GIMP_HISTOGRAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HISTOGRAM, GimpHistogram)) +#define GIMP_HISTOGRAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HISTOGRAM, GimpHistogramClass)) +#define GIMP_IS_HISTOGRAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HISTOGRAM)) +#define GIMP_IS_HISTOGRAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HISTOGRAM)) +#define GIMP_HISTOGRAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HISTOGRAM, GimpHistogramClass)) + + +typedef struct _GimpHistogramPrivate GimpHistogramPrivate; +typedef struct _GimpHistogramClass GimpHistogramClass; + +struct _GimpHistogram +{ + GimpObject parent_instance; + + GimpHistogramPrivate *priv; +}; + +struct _GimpHistogramClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_histogram_get_type (void) G_GNUC_CONST; + +GimpHistogram * gimp_histogram_new (gboolean linear); + +GimpHistogram * gimp_histogram_duplicate (GimpHistogram *histogram); + +void gimp_histogram_calculate (GimpHistogram *histogram, + GeglBuffer *buffer, + const GeglRectangle *buffer_rect, + GeglBuffer *mask, + const GeglRectangle *mask_rect); +GimpAsync * gimp_histogram_calculate_async (GimpHistogram *histogram, + GeglBuffer *buffer, + const GeglRectangle *buffer_rect, + GeglBuffer *mask, + const GeglRectangle *mask_rect); + +void gimp_histogram_clear_values (GimpHistogram *histogram, + gint n_components); + +gdouble gimp_histogram_get_maximum (GimpHistogram *histogram, + GimpHistogramChannel channel); +gdouble gimp_histogram_get_count (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end); +gdouble gimp_histogram_get_mean (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end); +gdouble gimp_histogram_get_median (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end); +gdouble gimp_histogram_get_std_dev (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end); +gdouble gimp_histogram_get_threshold (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint start, + gint end); +gdouble gimp_histogram_get_value (GimpHistogram *histogram, + GimpHistogramChannel channel, + gint bin); +gdouble gimp_histogram_get_component (GimpHistogram *histogram, + gint component, + gint bin); +gint gimp_histogram_n_components (GimpHistogram *histogram); +gint gimp_histogram_n_bins (GimpHistogram *histogram); +gboolean gimp_histogram_has_channel (GimpHistogram *histogram, + GimpHistogramChannel channel); + + +#endif /* __GIMP_HISTOGRAM_H__ */ diff --git a/app/core/gimpidtable.c b/app/core/gimpidtable.c new file mode 100644 index 0000000..b69a13d --- /dev/null +++ b/app/core/gimpidtable.c @@ -0,0 +1,227 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpidtable.c + * Copyright (C) 2011 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpidtable.h" + + +#define GIMP_ID_TABLE_START_ID 1 +#define GIMP_ID_TABLE_END_ID G_MAXINT + + +struct _GimpIdTablePrivate +{ + GHashTable *id_table; + gint next_id; +}; + + +static void gimp_id_table_finalize (GObject *object); +static gint64 gimp_id_table_get_memsize (GimpObject *object, + gint64 *gui_size); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpIdTable, gimp_id_table, GIMP_TYPE_OBJECT) + +#define parent_class gimp_id_table_parent_class + + +static void +gimp_id_table_class_init (GimpIdTableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + object_class->finalize = gimp_id_table_finalize; + + gimp_object_class->get_memsize = gimp_id_table_get_memsize; +} + +static void +gimp_id_table_init (GimpIdTable *id_table) +{ + id_table->priv = gimp_id_table_get_instance_private (id_table); + + id_table->priv->id_table = g_hash_table_new (g_direct_hash, NULL); + id_table->priv->next_id = GIMP_ID_TABLE_START_ID; +} + +static void +gimp_id_table_finalize (GObject *object) +{ + GimpIdTable *id_table = GIMP_ID_TABLE (object); + + g_clear_pointer (&id_table->priv->id_table, g_hash_table_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_id_table_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpIdTable *id_table = GIMP_ID_TABLE (object); + gint64 memsize = 0; + + memsize += gimp_g_hash_table_get_memsize (id_table->priv->id_table, 0); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +/** + * gimp_id_table_new: + * + * Returns: A new #GimpIdTable. + **/ +GimpIdTable * +gimp_id_table_new (void) +{ + return g_object_new (GIMP_TYPE_ID_TABLE, NULL); +} + +/** + * gimp_id_table_insert: + * @id_table: A #GimpIdTable + * @data: Data to insert and assign an id to + * + * Insert data in the id table. The data will get an, in this table, + * unused ID assigned to it that can be used to later lookup the data. + * + * Returns: The assigned ID. + **/ +gint +gimp_id_table_insert (GimpIdTable *id_table, gpointer data) +{ + gint new_id; + gint start_id; + + g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), 0); + + start_id = id_table->priv->next_id; + + do + { + new_id = id_table->priv->next_id++; + + if (id_table->priv->next_id == GIMP_ID_TABLE_END_ID) + id_table->priv->next_id = GIMP_ID_TABLE_START_ID; + + if (start_id == id_table->priv->next_id) + { + /* We looped once over all used ids. Very unlikely to happen. + And if it does, there is probably not much to be done. + It is just good design not to allow a theoretical infinite loop. */ + g_error ("%s: out of ids!", G_STRFUNC); + break; + } + } + while (gimp_id_table_lookup (id_table, new_id)); + + return gimp_id_table_insert_with_id (id_table, new_id, data); +} + +/** + * gimp_id_table_insert_with_id: + * @id_table: An #GimpIdTable + * @id: The ID to use. Must be greater than 0. + * @data: The data to associate with the id + * + * Insert data in the id table with a specific ID. If data already + * exsts with the given ID, this function fails. + * + * Returns: The used ID if successful, -1 if it was already in use. + **/ +gint +gimp_id_table_insert_with_id (GimpIdTable *id_table, gint id, gpointer data) +{ + g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), 0); + g_return_val_if_fail (id > 0, 0); + + if (gimp_id_table_lookup (id_table, id)) + return -1; + + g_hash_table_insert (id_table->priv->id_table, GINT_TO_POINTER (id), data); + + return id; +} + +/** + * gimp_id_table_replace: + * @id_table: An #GimpIdTable + * @id: The ID to use. Must be greater than 0. + * @data: The data to insert/replace + * + * Replaces (if an item with the given ID exists) or inserts a new + * entry in the id table. + **/ +void +gimp_id_table_replace (GimpIdTable *id_table, gint id, gpointer data) +{ + g_return_if_fail (GIMP_IS_ID_TABLE (id_table)); + g_return_if_fail (id > 0); + + g_hash_table_replace (id_table->priv->id_table, GINT_TO_POINTER (id), data); +} + +/** + * gimp_id_table_lookup: + * @id_table: An #GimpIdTable + * @id: The ID of the data to lookup + * + * Lookup data based on ID. + * + * Returns: The data, or NULL if no data with the given ID was found. + **/ +gpointer +gimp_id_table_lookup (GimpIdTable *id_table, gint id) +{ + g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), NULL); + + return g_hash_table_lookup (id_table->priv->id_table, GINT_TO_POINTER (id)); +} + + +/** + * gimp_id_table_remove: + * @id_table: An #GimpIdTable + * @id: The ID of the data to remove. + * + * Remove the data from the table with the given ID. + * + * Returns: %TRUE if data with the ID existed and was successfully + * removed, %FALSE otherwise. + **/ +gboolean +gimp_id_table_remove (GimpIdTable *id_table, gint id) +{ + g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), FALSE); + + g_return_val_if_fail (id_table != NULL, FALSE); + + return g_hash_table_remove (id_table->priv->id_table, GINT_TO_POINTER (id)); +} diff --git a/app/core/gimpidtable.h b/app/core/gimpidtable.h new file mode 100644 index 0000000..88097fd --- /dev/null +++ b/app/core/gimpidtable.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpidtable.h + * Copyright (C) 2011 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 . + */ + +#ifndef __GIMP_ID_TABLE_H__ +#define __GIMP_ID_TABLE_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_ID_TABLE (gimp_id_table_get_type ()) +#define GIMP_ID_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ID_TABLE, GimpIdTable)) +#define GIMP_ID_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ID_TABLE, GimpIdTableClass)) +#define GIMP_IS_ID_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ID_TABLE)) +#define GIMP_IS_ID_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ID_TABLE)) +#define GIMP_ID_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ID_TABLE, GimpIdTableClass)) + + +typedef struct _GimpIdTableClass GimpIdTableClass; +typedef struct _GimpIdTablePrivate GimpIdTablePrivate; + +struct _GimpIdTable +{ + GimpObject parent_instance; + + GimpIdTablePrivate *priv; +}; + +struct _GimpIdTableClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_id_table_get_type (void) G_GNUC_CONST; +GimpIdTable * gimp_id_table_new (void); +gint gimp_id_table_insert (GimpIdTable *id_table, + gpointer data); +gint gimp_id_table_insert_with_id (GimpIdTable *id_table, + gint id, + gpointer data); +void gimp_id_table_replace (GimpIdTable *id_table, + gint id, + gpointer data); +gpointer gimp_id_table_lookup (GimpIdTable *id_table, + gint id); +gboolean gimp_id_table_remove (GimpIdTable *id_table, + gint id); + + +#endif /* __GIMP_ID_TABLE_H__ */ diff --git a/app/core/gimpimage-arrange.c b/app/core/gimpimage-arrange.c new file mode 100644 index 0000000..6b6b05e --- /dev/null +++ b/app/core/gimpimage-arrange.c @@ -0,0 +1,388 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpimage-arrange.h" +#include "gimpimage-guides.h" +#include "gimpimage-undo.h" +#include "gimpitem.h" +#include "gimpguide.h" + +#include "gimp-intl.h" + + +static GList * sort_by_offset (GList *list); +static void compute_offsets (GList *list, + GimpAlignmentType alignment); +static void compute_offset (GObject *object, + GimpAlignmentType alignment); +static gint offset_compare (gconstpointer a, + gconstpointer b); + + +/** + * gimp_image_arrange_objects: + * @image: The #GimpImage to which the objects belong. + * @list: A #GList of objects to be aligned. + * @alignment: The point on each target object to bring into alignment. + * @reference: The #GObject to align the targets with, or #NULL. + * @reference_alignment: The point on the reference object to align the target item with.. + * @offset: How much to shift the target from perfect alignment.. + * + * This function shifts the positions of a set of target objects, + * which can be "items" or guides, to bring them into a specified type + * of alignment with a reference object, which can be an item, guide, + * or image. If the requested alignment does not make sense (i.e., + * trying to align a vertical guide vertically), nothing happens and + * no error message is generated. + * + * The objects in the list are sorted into increasing order before + * being arranged, where the order is defined by the type of alignment + * being requested. If the @reference argument is #NULL, then the + * first object in the sorted list is used as reference. + * + * When there are multiple target objects, they are arranged so that + * the spacing between consecutive ones is given by the argument + * @offset but for HFILL and VFILL - in this case, @offset works as an + * internal margin for the distribution (and it can be negative). + */ +void +gimp_image_arrange_objects (GimpImage *image, + GList *list, + GimpAlignmentType alignment, + GObject *reference, + GimpAlignmentType reference_alignment, + gint offset) +{ + gboolean do_x = FALSE; + gboolean do_y = FALSE; + gint z0 = 0; + GList *object_list; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (G_IS_OBJECT (reference) || reference == NULL); + + /* get offsets used for sorting */ + switch (alignment) + { + /* order vertically for horizontal alignment */ + case GIMP_ALIGN_LEFT: + case GIMP_ALIGN_HCENTER: + case GIMP_ALIGN_RIGHT: + do_x = TRUE; + compute_offsets (list, GIMP_ALIGN_TOP); + break; + + /* order horizontally for horizontal arrangement */ + case GIMP_ARRANGE_LEFT: + case GIMP_ARRANGE_HCENTER: + case GIMP_ARRANGE_RIGHT: + case GIMP_ARRANGE_HFILL: + do_x = TRUE; + compute_offsets (list, alignment); + break; + + /* order horizontally for vertical alignment */ + case GIMP_ALIGN_TOP: + case GIMP_ALIGN_VCENTER: + case GIMP_ALIGN_BOTTOM: + do_y = TRUE; + compute_offsets (list, GIMP_ALIGN_LEFT); + break; + + /* order vertically for vertical arrangement */ + case GIMP_ARRANGE_TOP: + case GIMP_ARRANGE_VCENTER: + case GIMP_ARRANGE_BOTTOM: + case GIMP_ARRANGE_VFILL: + do_y = TRUE; + compute_offsets (list, alignment); + break; + + default: + g_return_if_reached (); + } + + object_list = sort_by_offset (list); + + /* now get offsets used for aligning */ + compute_offsets (list, alignment); + + if (reference == NULL) + { + reference = G_OBJECT (object_list->data); + object_list = g_list_next (object_list); + reference_alignment = alignment; + } + else + { + compute_offset (reference, reference_alignment); + } + + z0 = GPOINTER_TO_INT (g_object_get_data (reference, "align-offset")); + + if (object_list) + { + GList *list; + gint n; + gint distr_width = 0; + gint distr_height = 0; + gdouble fill_offset = 0; + + if (reference_alignment == GIMP_ARRANGE_HFILL) + { + distr_width = GPOINTER_TO_INT (g_object_get_data + (reference, "align-width")); + /* The offset parameter works as an internal margin */ + fill_offset = (distr_width - 2 * offset) / + (gint) g_list_length (object_list); + } + if (reference_alignment == GIMP_ARRANGE_VFILL) + { + distr_height = GPOINTER_TO_INT (g_object_get_data + (reference, "align-height")); + fill_offset = (distr_height - 2 * offset) / + (gint) g_list_length (object_list); + } + + /* FIXME: undo group type is wrong */ + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, + C_("undo-type", "Arrange Objects")); + + for (list = object_list, n = 1; + list; + list = g_list_next (list), n++) + { + GObject *target = list->data; + gint xtranslate = 0; + gint ytranslate = 0; + gint z1; + + z1 = GPOINTER_TO_INT (g_object_get_data (target, "align-offset")); + + if (reference_alignment == GIMP_ARRANGE_HFILL) + { + gint width = GPOINTER_TO_INT (g_object_get_data (target, + "align-width")); + xtranslate = ROUND (z0 - z1 + (n - 0.5) * fill_offset - + width / 2.0 + offset); + } + else if (reference_alignment == GIMP_ARRANGE_VFILL) + { + gint height = GPOINTER_TO_INT (g_object_get_data (target, + "align-height")); + ytranslate = ROUND (z0 - z1 + (n - 0.5) * fill_offset - + height / 2.0 + offset); + } + else /* the normal computing, when we don't depend on the + * width or height of the reference object + */ + { + if (do_x) + xtranslate = z0 - z1 + n * offset; + + if (do_y) + ytranslate = z0 - z1 + n * offset; + } + + /* now actually align the target object */ + if (GIMP_IS_ITEM (target)) + { + gimp_item_translate (GIMP_ITEM (target), + xtranslate, ytranslate, TRUE); + } + else if (GIMP_IS_GUIDE (target)) + { + GimpGuide *guide = GIMP_GUIDE (target); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_VERTICAL: + gimp_image_move_guide (image, guide, z1 + xtranslate, TRUE); + break; + + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_move_guide (image, guide, z1 + ytranslate, TRUE); + break; + + default: + break; + } + } + } + + gimp_image_undo_group_end (image); + } + + g_list_free (object_list); +} + +static GList * +sort_by_offset (GList *list) +{ + return g_list_sort (g_list_copy (list), + offset_compare); + +} + +static gint +offset_compare (gconstpointer a, + gconstpointer b) +{ + gint offset1 = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (a), + "align-offset")); + gint offset2 = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (b), + "align-offset")); + + return offset1 - offset2; +} + +/* this function computes the position of the alignment point + * for each object in the list, and attaches it to the + * object as object data. + */ +static void +compute_offsets (GList *list, + GimpAlignmentType alignment) +{ + GList *l; + + for (l = list; l; l = g_list_next (l)) + compute_offset (l->data, alignment); +} + +static void +compute_offset (GObject *object, + GimpAlignmentType alignment) +{ + gint object_offset_x = 0; + gint object_offset_y = 0; + gint object_height = 0; + gint object_width = 0; + gint offset = 0; + + if (GIMP_IS_IMAGE (object)) + { + GimpImage *image = GIMP_IMAGE (object); + + object_offset_x = 0; + object_offset_y = 0; + object_height = gimp_image_get_height (image); + object_width = gimp_image_get_width (image); + } + else if (GIMP_IS_ITEM (object)) + { + GimpItem *item = GIMP_ITEM (object); + gint off_x, off_y; + + gimp_item_bounds (item, + &object_offset_x, + &object_offset_y, + &object_width, + &object_height); + + gimp_item_get_offset (item, &off_x, &off_y); + object_offset_x += off_x; + object_offset_y += off_y; + } + else if (GIMP_IS_GUIDE (object)) + { + GimpGuide *guide = GIMP_GUIDE (object); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_VERTICAL: + object_offset_x = gimp_guide_get_position (guide); + object_width = 0; + break; + + case GIMP_ORIENTATION_HORIZONTAL: + object_offset_y = gimp_guide_get_position (guide); + object_height = 0; + break; + + default: + break; + } + } + else + { + g_printerr ("Alignment object is not an image, item or guide.\n"); + } + + switch (alignment) + { + case GIMP_ALIGN_LEFT: + case GIMP_ARRANGE_LEFT: + case GIMP_ARRANGE_HFILL: + offset = object_offset_x; + break; + + case GIMP_ALIGN_HCENTER: + case GIMP_ARRANGE_HCENTER: + offset = object_offset_x + object_width / 2; + break; + + case GIMP_ALIGN_RIGHT: + case GIMP_ARRANGE_RIGHT: + offset = object_offset_x + object_width; + break; + + case GIMP_ALIGN_TOP: + case GIMP_ARRANGE_TOP: + case GIMP_ARRANGE_VFILL: + offset = object_offset_y; + break; + + case GIMP_ALIGN_VCENTER: + case GIMP_ARRANGE_VCENTER: + offset = object_offset_y + object_height / 2; + break; + + case GIMP_ALIGN_BOTTOM: + case GIMP_ARRANGE_BOTTOM: + offset = object_offset_y + object_height; + break; + + default: + g_return_if_reached (); + } + + g_object_set_data (object, "align-offset", + GINT_TO_POINTER (offset)); + + /* These are only used for HFILL and VFILL, but since the call to + * gimp_image_arrange_objects allows for two different alignments + * (object and reference_alignment) we better be on the safe side in + * case they differ. (the current implementation of the align tool + * always pass the same value to both parameters) + */ + g_object_set_data (object, "align-width", + GINT_TO_POINTER (object_width)); + + g_object_set_data (object, "align-height", + GINT_TO_POINTER (object_height)); +} diff --git a/app/core/gimpimage-arrange.h b/app/core/gimpimage-arrange.h new file mode 100644 index 0000000..1109a5d --- /dev/null +++ b/app/core/gimpimage-arrange.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 . + */ + +#ifndef __GIMP_IMAGE_ARRANGE_H__ +#define __GIMP_IMAGE_ARRANGE_H__ + + +void gimp_image_arrange_objects (GimpImage *image, + GList *list, + GimpAlignmentType alignment, + GObject *reference, + GimpAlignmentType reference_alignment, + gint offset); + +#endif /* __GIMP_IMAGE_ARRANGE_H__ */ diff --git a/app/core/gimpimage-color-profile.c b/app/core/gimpimage-color-profile.c new file mode 100644 index 0000000..a7030f3 --- /dev/null +++ b/app/core/gimpimage-color-profile.c @@ -0,0 +1,822 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-color-profile.c + * Copyright (C) 2015 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "config/gimpdialogconfig.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-private.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_image_convert_profile_layers (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress); +static void gimp_image_convert_profile_colormap (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress); + +static void gimp_image_create_color_transforms (GimpImage *image); + + +/* public functions */ + +gboolean +gimp_image_get_is_color_managed (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->is_color_managed; +} + +void +gimp_image_set_is_color_managed (GimpImage *image, + gboolean is_color_managed, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + is_color_managed = is_color_managed ? TRUE : FALSE; + + if (is_color_managed != private->is_color_managed) + { + if (push_undo) + gimp_image_undo_push_image_color_managed (image, NULL); + + private->is_color_managed = is_color_managed; + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); + } +} + +gboolean +gimp_image_validate_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite, + gboolean *is_builtin, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (icc_parasite != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strcmp (gimp_parasite_name (icc_parasite), + GIMP_ICC_PROFILE_PARASITE_NAME) != 0) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Parasite's name is not 'icc-profile'")); + return FALSE; + } + + if (gimp_parasite_flags (icc_parasite) != (GIMP_PARASITE_PERSISTENT | + GIMP_PARASITE_UNDOABLE)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Parasite's flags are not (PERSISTENT | UNDOABLE)")); + return FALSE; + } + + return gimp_image_validate_icc_profile (image, + gimp_parasite_data (icc_parasite), + gimp_parasite_data_size (icc_parasite), + is_builtin, + error); +} + +const GimpParasite * +gimp_image_get_icc_parasite (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_parasite_find (image, GIMP_ICC_PROFILE_PARASITE_NAME); +} + +void +gimp_image_set_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + if (icc_parasite) + { + g_return_if_fail (gimp_image_validate_icc_parasite (image, icc_parasite, + NULL, NULL) == TRUE); + + gimp_image_parasite_attach (image, icc_parasite, TRUE); + } + else + { + gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME, TRUE); + } +} + +gboolean +gimp_image_validate_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + gboolean *is_builtin, + GError **error) +{ + GimpColorProfile *profile; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (data != NULL || length == 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + profile = gimp_color_profile_new_from_icc_profile (data, length, error); + + if (! profile) + { + g_prefix_error (error, _("ICC profile validation failed: ")); + return FALSE; + } + + if (! gimp_image_validate_color_profile (image, profile, is_builtin, error)) + { + g_object_unref (profile); + return FALSE; + } + + g_object_unref (profile); + + return TRUE; +} + +const guint8 * +gimp_image_get_icc_profile (GimpImage *image, + gsize *length) +{ + const GimpParasite *parasite; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + parasite = gimp_image_parasite_find (image, GIMP_ICC_PROFILE_PARASITE_NAME); + + if (parasite) + { + if (length) + *length = gimp_parasite_data_size (parasite); + + return gimp_parasite_data (parasite); + } + + if (length) + *length = 0; + + return NULL; +} + +gboolean +gimp_image_set_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + GError **error) +{ + GimpParasite *parasite = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (data == NULL || length != 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (data) + { + gboolean is_builtin; + + parasite = gimp_parasite_new (GIMP_ICC_PROFILE_PARASITE_NAME, + GIMP_PARASITE_PERSISTENT | + GIMP_PARASITE_UNDOABLE, + length, data); + + if (! gimp_image_validate_icc_parasite (image, parasite, &is_builtin, + error)) + { + gimp_parasite_free (parasite); + return FALSE; + } + + /* don't tag the image with the built-in profile */ + if (is_builtin) + { + gimp_parasite_free (parasite); + parasite = NULL; + } + } + + gimp_image_set_icc_parasite (image, parasite); + + if (parasite) + gimp_parasite_free (parasite); + + return TRUE; +} + +gboolean +gimp_image_validate_color_profile (GimpImage *image, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + format = gimp_image_get_layer_format (image, TRUE); + + return gimp_image_validate_color_profile_by_format (format, + profile, is_builtin, + error); +} + +GimpColorProfile * +gimp_image_get_color_profile (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->color_profile; +} + +gboolean +gimp_image_set_color_profile (GimpImage *image, + GimpColorProfile *profile, + GError **error) +{ + const guint8 *data = NULL; + gsize length = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile), + FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (profile) + data = gimp_color_profile_get_icc_profile (profile, &length); + + return gimp_image_set_icc_profile (image, data, length, error); +} + +gboolean +gimp_image_validate_color_profile_by_format (const Babl *format, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error) +{ + g_return_val_if_fail (format != NULL, FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (gimp_babl_format_get_base_type (format) == GIMP_GRAY) + { + if (! gimp_color_profile_is_gray (profile)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Color profile is not for grayscale color space")); + return FALSE; + } + } + else + { + if (! gimp_color_profile_is_rgb (profile)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Color profile is not for RGB color space")); + return FALSE; + } + } + + if (is_builtin) + { + GimpColorProfile *builtin; + + builtin = gimp_babl_format_get_color_profile (format); + + *is_builtin = gimp_color_profile_is_equal (profile, builtin); + } + + return TRUE; +} + +GimpColorProfile * +gimp_image_get_builtin_color_profile (GimpImage *image) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + format = gimp_image_get_layer_format (image, FALSE); + + return gimp_babl_format_get_color_profile (format); +} + +gboolean +gimp_image_convert_color_profile (GimpImage *image, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress, + GError **error) +{ + GimpColorProfile *src_profile; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (! gimp_image_validate_color_profile (image, dest_profile, NULL, error)) + return FALSE; + + src_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + if (! src_profile || gimp_color_profile_is_equal (src_profile, dest_profile)) + return TRUE; + + if (progress) + gimp_progress_start (progress, FALSE, + _("Converting from '%s' to '%s'"), + gimp_color_profile_get_label (src_profile), + gimp_color_profile_get_label (dest_profile)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, + _("Color profile conversion")); + + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: + case GIMP_GRAY: + gimp_image_convert_profile_layers (image, + src_profile, dest_profile, + intent, bpc, + progress); + break; + + case GIMP_INDEXED: + gimp_image_convert_profile_colormap (image, + src_profile, dest_profile, + intent, bpc, + progress); + break; + } + + gimp_image_set_is_color_managed (image, TRUE, TRUE); + gimp_image_set_color_profile (image, dest_profile, NULL); + /* omg... */ + gimp_image_parasite_detach (image, "icc-profile-name", TRUE); + + gimp_image_undo_group_end (image); + + if (progress) + gimp_progress_end (progress); + + return TRUE; +} + +void +gimp_image_import_color_profile (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + gboolean interactive) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (gimp_image_get_is_color_managed (image) && + gimp_image_get_color_profile (image)) + { + GimpColorProfilePolicy policy; + GimpColorProfile *dest_profile = NULL; + GimpColorRenderingIntent intent; + gboolean bpc; + + policy = GIMP_DIALOG_CONFIG (image->gimp->config)->color_profile_policy; + intent = GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC; + bpc = TRUE; + + if (policy == GIMP_COLOR_PROFILE_POLICY_ASK) + { + if (interactive) + { + gboolean dont_ask = FALSE; + + policy = gimp_query_profile_policy (image->gimp, image, context, + &dest_profile, + &intent, &bpc, + &dont_ask); + + if (dont_ask) + { + g_object_set (G_OBJECT (image->gimp->config), + "color-profile-policy", policy, + NULL); + } + } + else + { + policy = GIMP_COLOR_PROFILE_POLICY_KEEP; + } + } + + if (policy == GIMP_COLOR_PROFILE_POLICY_CONVERT) + { + if (! dest_profile) + { + dest_profile = gimp_image_get_builtin_color_profile (image); + g_object_ref (dest_profile); + } + + gimp_image_convert_color_profile (image, dest_profile, + intent, bpc, + progress, NULL); + + g_object_unref (dest_profile); + } + } +} + +GimpColorTransform * +gimp_image_get_color_transform_to_srgb_u8 (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_to_srgb_u8; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_from_srgb_u8 (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_from_srgb_u8; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_to_srgb_double (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_to_srgb_double; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_from_srgb_double (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_from_srgb_double; + + return NULL; +} + +void +gimp_image_color_profile_pixel_to_srgb (GimpImage *image, + const Babl *pixel_format, + gpointer pixel, + GimpRGB *color) +{ + GimpColorTransform *transform; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + transform = gimp_image_get_color_transform_to_srgb_double (image); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + pixel_format, + pixel, + babl_format ("R'G'B'A double"), + color, + 1); + } + else + { + gimp_rgba_set_pixel (color, pixel_format, pixel); + } +} + +void +gimp_image_color_profile_srgb_to_pixel (GimpImage *image, + const GimpRGB *color, + const Babl *pixel_format, + gpointer pixel) +{ + GimpColorTransform *transform; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + transform = gimp_image_get_color_transform_from_srgb_double (image); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + babl_format ("R'G'B'A double"), + color, + pixel_format, + pixel, + 1); + } + else + { + gimp_rgba_get_pixel (color, pixel_format, pixel); + } +} + + +/* internal API */ + +void +_gimp_image_free_color_profile (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + g_clear_object (&private->color_profile); + + _gimp_image_free_color_transforms (image); +} + +void +_gimp_image_free_color_transforms (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + g_clear_object (&private->transform_to_srgb_u8); + g_clear_object (&private->transform_from_srgb_u8); + g_clear_object (&private->transform_to_srgb_double); + g_clear_object (&private->transform_from_srgb_double); + + private->color_transforms_created = FALSE; +} + +void +_gimp_image_update_color_profile (GimpImage *image, + const GimpParasite *icc_parasite) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + _gimp_image_free_color_profile (image); + + if (icc_parasite) + { + private->color_profile = + gimp_color_profile_new_from_icc_profile (gimp_parasite_data (icc_parasite), + gimp_parasite_data_size (icc_parasite), + NULL); + } + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); +} + + +/* private functions */ + +static void +gimp_image_convert_profile_layers (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GList *layers; + GList *list; + GimpDrawable *drawable; + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + layers = gimp_image_get_layer_list (image); + + for (list = layers; list; list = g_list_next (list)) + { + if (! gimp_viewable_get_children (list->data)) + gimp_object_queue_push (queue, list->data); + } + + g_list_free (layers); + + while ((drawable = gimp_object_queue_pop (queue))) + { + GimpItem *item = GIMP_ITEM (drawable); + GeglBuffer *buffer; + gboolean alpha; + + alpha = gimp_drawable_has_alpha (drawable); + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + gimp_image_get_layer_format (image, alpha)); + + gimp_drawable_push_undo (drawable, NULL, NULL, + 0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))); + + gimp_gegl_convert_color_profile (gimp_drawable_get_buffer (drawable), + NULL, + src_profile, + buffer, + NULL, + dest_profile, + intent, bpc, + progress); + + gimp_drawable_set_buffer (drawable, TRUE, NULL, buffer); + g_object_unref (buffer); + } + + g_object_unref (queue); +} + +static void +gimp_image_convert_profile_colormap (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpColorTransform *transform; + GimpColorTransformFlags flags = 0; + guchar *cmap; + gint n_colors; + + n_colors = gimp_image_get_colormap_size (image); + cmap = g_memdup (gimp_image_get_colormap (image), n_colors * 3); + + if (bpc) + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + transform = gimp_color_transform_new (src_profile, + babl_format ("R'G'B' u8"), + dest_profile, + babl_format ("R'G'B' u8"), + intent, flags); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + babl_format ("R'G'B' u8"), cmap, + babl_format ("R'G'B' u8"), cmap, + n_colors); + g_object_unref (transform); + + gimp_image_set_colormap (image, cmap, n_colors, TRUE); + } + else + { + g_warning ("cmsCreateTransform() failed!"); + } + + g_free (cmap); +} + +static void +gimp_image_create_color_transforms (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->color_profile && + ! private->color_transforms_created) + { + GimpColorProfile *srgb_profile; + GimpColorTransformFlags flags = 0; + + srgb_profile = gimp_color_profile_new_rgb_srgb (); + + flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + private->transform_to_srgb_u8 = + gimp_color_transform_new (private->color_profile, + gimp_image_get_layer_format (image, TRUE), + srgb_profile, + babl_format ("R'G'B'A u8"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_from_srgb_u8 = + gimp_color_transform_new (srgb_profile, + babl_format ("R'G'B'A u8"), + private->color_profile, + gimp_image_get_layer_format (image, TRUE), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_to_srgb_double = + gimp_color_transform_new (private->color_profile, + gimp_image_get_layer_format (image, TRUE), + srgb_profile, + babl_format ("R'G'B'A double"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_from_srgb_double = + gimp_color_transform_new (srgb_profile, + babl_format ("R'G'B'A double"), + private->color_profile, + gimp_image_get_layer_format (image, TRUE), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + g_object_unref (srgb_profile); + + private->color_transforms_created = TRUE; + } +} diff --git a/app/core/gimpimage-color-profile.h b/app/core/gimpimage-color-profile.h new file mode 100644 index 0000000..ae89038 --- /dev/null +++ b/app/core/gimpimage-color-profile.h @@ -0,0 +1,113 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-color-profile.h + * Copyright (C) 2015 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_COLOR_PROFILE_H__ +#define __GIMP_IMAGE_COLOR_PROFILE_H__ + + +#define GIMP_ICC_PROFILE_PARASITE_NAME "icc-profile" + + +gboolean gimp_image_get_is_color_managed (GimpImage *image); +void gimp_image_set_is_color_managed (GimpImage *image, + gboolean is_color_managed, + gboolean push_undo); + +gboolean gimp_image_validate_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite, + gboolean *is_builtin, + GError **error); +const GimpParasite * gimp_image_get_icc_parasite (GimpImage *image); +void gimp_image_set_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite); + +gboolean gimp_image_validate_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + gboolean *is_builtin, + GError **error); +const guint8 * gimp_image_get_icc_profile (GimpImage *image, + gsize *length); +gboolean gimp_image_set_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + GError **error); + +gboolean gimp_image_validate_color_profile (GimpImage *image, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error); +GimpColorProfile * gimp_image_get_color_profile (GimpImage *image); +gboolean gimp_image_set_color_profile (GimpImage *image, + GimpColorProfile *profile, + GError **error); + +gboolean gimp_image_validate_color_profile_by_format + (const Babl *format, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error); + +GimpColorProfile * gimp_image_get_builtin_color_profile + (GimpImage *image); + +gboolean gimp_image_convert_color_profile (GimpImage *image, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress, + GError **error); + +void gimp_image_import_color_profile (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + gboolean interactive); + +GimpColorTransform * gimp_image_get_color_transform_to_srgb_u8 + (GimpImage *image); +GimpColorTransform * gimp_image_get_color_transform_from_srgb_u8 + (GimpImage *image); + +GimpColorTransform * gimp_image_get_color_transform_to_srgb_double + (GimpImage *image); +GimpColorTransform * gimp_image_get_color_transform_from_srgb_double + (GimpImage *image); + +void gimp_image_color_profile_pixel_to_srgb + (GimpImage *image, + const Babl *pixel_format, + gpointer pixel, + GimpRGB *color); +void gimp_image_color_profile_srgb_to_pixel + (GimpImage *image, + const GimpRGB *color, + const Babl *pixel_format, + gpointer pixel); + + +/* internal API, to be called only from gimpimage.c */ + +void _gimp_image_free_color_profile (GimpImage *image); +void _gimp_image_free_color_transforms (GimpImage *image); +void _gimp_image_update_color_profile (GimpImage *image, + const GimpParasite *icc_parasite); + + +#endif /* __GIMP_IMAGE_COLOR_PROFILE_H__ */ diff --git a/app/core/gimpimage-colormap.c b/app/core/gimpimage-colormap.c new file mode 100644 index 0000000..8bf40a9 --- /dev/null +++ b/app/core/gimpimage-colormap.c @@ -0,0 +1,362 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpdatafactory.h" +#include "gimpimage.h" +#include "gimpimage-colormap.h" +#include "gimpimage-private.h" +#include "gimpimage-undo-push.h" +#include "gimppalette.h" + +#include "gimp-intl.h" + + +/* local function prototype */ + +static void gimp_image_colormap_set_palette_entry (GimpImage *image, + const GimpRGB *color, + gint index); + + +/* public functions */ + +void +gimp_image_colormap_init (GimpImage *image) +{ + GimpImagePrivate *private; + GimpContainer *palettes; + gchar *palette_name; + gchar *palette_id; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap == NULL); + g_return_if_fail (private->palette == NULL); + + palette_name = g_strdup_printf (_("Colormap of Image #%d (%s)"), + gimp_image_get_ID (image), + gimp_image_get_display_name (image)); + palette_id = g_strdup_printf ("gimp-indexed-image-palette-%d", + gimp_image_get_ID (image)); + + private->n_colors = 0; + private->colormap = g_new0 (guchar, GIMP_IMAGE_COLORMAP_SIZE); + private->palette = GIMP_PALETTE (gimp_palette_new (NULL, palette_name)); + + if (! private->babl_palette_rgb) + { + gchar *format_name = g_strdup_printf ("-gimp-indexed-format-%d", + gimp_image_get_ID (image)); + + babl_new_palette (format_name, + &private->babl_palette_rgb, + &private->babl_palette_rgba); + + g_free (format_name); + } + + gimp_palette_set_columns (private->palette, 16); + + gimp_data_make_internal (GIMP_DATA (private->palette), palette_id); + + palettes = gimp_data_factory_get_container (image->gimp->palette_factory); + + gimp_container_add (palettes, GIMP_OBJECT (private->palette)); + + g_free (palette_name); + g_free (palette_id); +} + +void +gimp_image_colormap_dispose (GimpImage *image) +{ + GimpImagePrivate *private; + GimpContainer *palettes; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap != NULL); + g_return_if_fail (GIMP_IS_PALETTE (private->palette)); + + palettes = gimp_data_factory_get_container (image->gimp->palette_factory); + + gimp_container_remove (palettes, GIMP_OBJECT (private->palette)); +} + +void +gimp_image_colormap_free (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap != NULL); + g_return_if_fail (GIMP_IS_PALETTE (private->palette)); + + g_clear_pointer (&private->colormap, g_free); + g_clear_object (&private->palette); + + /* don't touch the image's babl_palettes because we might still have + * buffers with that palette on the undo stack, and on undoing the + * image back to indexed, we must have exactly these palettes around + */ +} + +const Babl * +gimp_image_colormap_get_rgb_format (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->babl_palette_rgb; +} + +const Babl * +gimp_image_colormap_get_rgba_format (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->babl_palette_rgba; +} + +GimpPalette * +gimp_image_get_colormap_palette (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->palette; +} + +const guchar * +gimp_image_get_colormap (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->colormap; +} + +gint +gimp_image_get_colormap_size (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->n_colors; +} + +void +gimp_image_set_colormap (GimpImage *image, + const guchar *colormap, + gint n_colors, + gboolean push_undo) +{ + GimpImagePrivate *private; + GimpPaletteEntry *entry; + gint i; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (colormap != NULL || n_colors == 0); + g_return_if_fail (n_colors >= 0 && n_colors <= 256); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (push_undo) + gimp_image_undo_push_image_colormap (image, C_("undo-type", "Set Colormap")); + + if (private->colormap) + memset (private->colormap, 0, GIMP_IMAGE_COLORMAP_SIZE); + else + gimp_image_colormap_init (image); + + if (colormap) + memcpy (private->colormap, colormap, n_colors * 3); + + /* make sure the image's colormap always has at least one color. when + * n_colors == 0, use black. + */ + private->n_colors = MAX (n_colors, 1); + + gimp_data_freeze (GIMP_DATA (private->palette)); + + while ((entry = gimp_palette_get_entry (private->palette, 0))) + gimp_palette_delete_entry (private->palette, entry); + + for (i = 0; i < private->n_colors; i++) + gimp_image_colormap_set_palette_entry (image, NULL, i); + + gimp_data_thaw (GIMP_DATA (private->palette)); + + gimp_image_colormap_changed (image, -1); +} + +void +gimp_image_unset_colormap (GimpImage *image, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (push_undo) + gimp_image_undo_push_image_colormap (image, + C_("undo-type", "Unset Colormap")); + + if (private->colormap) + { + gimp_image_colormap_dispose (image); + gimp_image_colormap_free (image); + } + + private->n_colors = 0; + + gimp_image_colormap_changed (image, -1); +} + +void +gimp_image_get_colormap_entry (GimpImage *image, + gint color_index, + GimpRGB *color) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap != NULL); + g_return_if_fail (color_index >= 0 && color_index < private->n_colors); + g_return_if_fail (color != NULL); + + gimp_rgba_set_uchar (color, + private->colormap[color_index * 3], + private->colormap[color_index * 3 + 1], + private->colormap[color_index * 3 + 2], + 255); +} + +void +gimp_image_set_colormap_entry (GimpImage *image, + gint color_index, + const GimpRGB *color, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap != NULL); + g_return_if_fail (color_index >= 0 && color_index < private->n_colors); + g_return_if_fail (color != NULL); + + if (push_undo) + gimp_image_undo_push_image_colormap (image, + C_("undo-type", "Change Colormap entry")); + + gimp_rgb_get_uchar (color, + &private->colormap[color_index * 3], + &private->colormap[color_index * 3 + 1], + &private->colormap[color_index * 3 + 2]); + + if (private->palette) + gimp_image_colormap_set_palette_entry (image, color, color_index); + + gimp_image_colormap_changed (image, color_index); +} + +void +gimp_image_add_colormap_entry (GimpImage *image, + const GimpRGB *color) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_if_fail (private->colormap != NULL); + g_return_if_fail (private->n_colors < 256); + g_return_if_fail (color != NULL); + + gimp_image_undo_push_image_colormap (image, + C_("undo-type", "Add Color to Colormap")); + + gimp_rgb_get_uchar (color, + &private->colormap[private->n_colors * 3], + &private->colormap[private->n_colors * 3 + 1], + &private->colormap[private->n_colors * 3 + 2]); + + private->n_colors++; + + if (private->palette) + gimp_image_colormap_set_palette_entry (image, color, private->n_colors - 1); + + gimp_image_colormap_changed (image, -1); +} + + +/* private functions */ + +static void +gimp_image_colormap_set_palette_entry (GimpImage *image, + const GimpRGB *c, + gint index) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpRGB color; + gchar name[64]; + + /* Avoid converting to char then back to double if we have the + * original GimpRGB color. + */ + if (c) + color = *c; + else + gimp_rgba_set_uchar (&color, + private->colormap[3 * index + 0], + private->colormap[3 * index + 1], + private->colormap[3 * index + 2], + 255); + + g_snprintf (name, sizeof (name), "#%d", index); + + if (gimp_palette_get_n_colors (private->palette) < private->n_colors) + gimp_palette_add_entry (private->palette, index, name, &color); + else + gimp_palette_set_entry (private->palette, index, name, &color); +} diff --git a/app/core/gimpimage-colormap.h b/app/core/gimpimage-colormap.h new file mode 100644 index 0000000..703f5a2 --- /dev/null +++ b/app/core/gimpimage-colormap.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_COLORMAP_H__ +#define __GIMP_IMAGE_COLORMAP_H__ + + +#define GIMP_IMAGE_COLORMAP_SIZE 768 + + +void gimp_image_colormap_init (GimpImage *image); +void gimp_image_colormap_dispose (GimpImage *image); +void gimp_image_colormap_free (GimpImage *image); + +const Babl * gimp_image_colormap_get_rgb_format (GimpImage *image); +const Babl * gimp_image_colormap_get_rgba_format (GimpImage *image); + +GimpPalette * gimp_image_get_colormap_palette (GimpImage *image); + +const guchar * gimp_image_get_colormap (GimpImage *image); +gint gimp_image_get_colormap_size (GimpImage *image); +void gimp_image_set_colormap (GimpImage *image, + const guchar *colormap, + gint n_colors, + gboolean push_undo); +void gimp_image_unset_colormap (GimpImage *image, + gboolean push_undo); + +void gimp_image_get_colormap_entry (GimpImage *image, + gint color_index, + GimpRGB *color); +void gimp_image_set_colormap_entry (GimpImage *image, + gint color_index, + const GimpRGB *color, + gboolean push_undo); + +void gimp_image_add_colormap_entry (GimpImage *image, + const GimpRGB *color); + + +#endif /* __GIMP_IMAGE_COLORMAP_H__ */ diff --git a/app/core/gimpimage-convert-data.h b/app/core/gimpimage-convert-data.h new file mode 100644 index 0000000..dd0dd11 --- /dev/null +++ b/app/core/gimpimage-convert-data.h @@ -0,0 +1,143 @@ +/* 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 . + */ + +/* Misc data definitions used by the convert.c code module. Moved + out here simply to unclutter convert.c, mostly. */ + +#ifndef __GIMP_IMAGE_CONVERT_DATA_H__ +#define __GIMP_IMAGE_CONVERT_DATA_H__ + +#include + +/* 'web safe' palette. */ +static const guchar webpal[] = +{ + 255,255,255,255,255,204,255,255,153,255,255,102,255,255,51,255,255,0,255, + 204,255,255,204,204,255,204,153,255,204,102,255,204,51,255,204,0,255,153, + 255,255,153,204,255,153,153,255,153,102,255,153,51,255,153,0,255,102,255, + 255,102,204,255,102,153,255,102,102,255,102,51,255,102,0,255,51,255,255, + 51,204,255,51,153,255,51,102,255,51,51,255,51,0,255,0,255,255,0, + 204,255,0,153,255,0,102,255,0,51,255,0,0,204,255,255,204,255,204, + 204,255,153,204,255,102,204,255,51,204,255,0,204,204,255,204,204,204,204, + 204,153,204,204,102,204,204,51,204,204,0,204,153,255,204,153,204,204,153, + 153,204,153,102,204,153,51,204,153,0,204,102,255,204,102,204,204,102,153, + 204,102,102,204,102,51,204,102,0,204,51,255,204,51,204,204,51,153,204, + 51,102,204,51,51,204,51,0,204,0,255,204,0,204,204,0,153,204,0, + 102,204,0,51,204,0,0,153,255,255,153,255,204,153,255,153,153,255,102, + 153,255,51,153,255,0,153,204,255,153,204,204,153,204,153,153,204,102,153, + 204,51,153,204,0,153,153,255,153,153,204,153,153,153,153,153,102,153,153, + 51,153,153,0,153,102,255,153,102,204,153,102,153,153,102,102,153,102,51, + 153,102,0,153,51,255,153,51,204,153,51,153,153,51,102,153,51,51,153, + 51,0,153,0,255,153,0,204,153,0,153,153,0,102,153,0,51,153,0, + 0,102,255,255,102,255,204,102,255,153,102,255,102,102,255,51,102,255,0, + 102,204,255,102,204,204,102,204,153,102,204,102,102,204,51,102,204,0,102, + 153,255,102,153,204,102,153,153,102,153,102,102,153,51,102,153,0,102,102, + 255,102,102,204,102,102,153,102,102,102,102,102,51,102,102,0,102,51,255, + 102,51,204,102,51,153,102,51,102,102,51,51,102,51,0,102,0,255,102, + 0,204,102,0,153,102,0,102,102,0,51,102,0,0,51,255,255,51,255, + 204,51,255,153,51,255,102,51,255,51,51,255,0,51,204,255,51,204,204, + 51,204,153,51,204,102,51,204,51,51,204,0,51,153,255,51,153,204,51, + 153,153,51,153,102,51,153,51,51,153,0,51,102,255,51,102,204,51,102, + 153,51,102,102,51,102,51,51,102,0,51,51,255,51,51,204,51,51,153, + 51,51,102,51,51,51,51,51,0,51,0,255,51,0,204,51,0,153,51, + 0,102,51,0,51,51,0,0,0,255,255,0,255,204,0,255,153,0,255, + 102,0,255,51,0,255,0,0,204,255,0,204,204,0,204,153,0,204,102, + 0,204,51,0,204,0,0,153,255,0,153,204,0,153,153,0,153,102,0, + 153,51,0,153,0,0,102,255,0,102,204,0,102,153,0,102,102,0,102, + 51,0,102,0,0,51,255,0,51,204,0,51,153,0,51,102,0,51,51, + 0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0 +}; + + +/* the dither matrix, used for alpha dither and 'positional' dither */ +#define DM_WIDTH 32 +#define DM_WIDTHMASK ((DM_WIDTH)-1) +#define DM_HEIGHT 32 +#define DM_HEIGHTMASK ((DM_HEIGHT)-1) +/* matrix values should be scaled/biased to 1..255 range */ +/* this array is not const because it may be overwritten. */ +static guchar DM[32][32] = { + { 1,191, 48,239, 12,203, 60,251, 3,194, 51,242, 15,206, 63,254, 1,192, 49,240, 13,204, 61,252, 4,195, 52,243, 16,207, 64,255}, + {128, 64,175,112,140, 76,187,124,131, 67,178,115,143, 79,190,127,128, 65,176,112,140, 77,188,124,131, 68,179,115,143, 80,191,127}, + { 32,223, 16,207, 44,235, 28,219, 35,226, 19,210, 47,238, 31,222, 33,224, 17,208, 45,236, 29,220, 36,227, 20,211, 48,239, 32,223}, + {159, 96,144, 80,171,108,155, 92,162, 99,146, 83,174,111,158, 95,160, 97,144, 81,172,109,156, 93,163,100,147, 84,175,111,159, 96}, + { 8,199, 56,247, 4,195, 52,243, 11,202, 59,250, 7,198, 55,246, 9,200, 57,248, 5,196, 53,244, 12,203, 60,251, 8,199, 56,247}, + {136, 72,183,120,132, 68,179,116,139, 75,186,123,135, 71,182,119,136, 73,184,120,132, 69,180,116,139, 76,187,123,135, 72,183,119}, + { 40,231, 24,215, 36,227, 20,211, 43,234, 27,218, 39,230, 23,214, 41,232, 25,216, 37,228, 21,212, 44,235, 28,219, 40,231, 24,215}, + {167,104,151, 88,163,100,147, 84,170,107,154, 91,166,103,150, 87,168,105,152, 89,164,101,148, 85,171,108,155, 92,167,104,151, 88}, + { 2,193, 50,241, 14,205, 62,253, 1,192, 49,240, 13,204, 61,252, 3,194, 51,242, 15,206, 63,254, 2,193, 50,241, 14,205, 62,253}, + {130, 66,177,114,142, 78,189,126,129, 65,176,113,141, 77,188,125,130, 67,178,114,142, 79,190,126,129, 66,177,113,141, 78,189,125}, + { 34,225, 18,209, 46,237, 30,221, 33,224, 17,208, 45,236, 29,220, 35,226, 19,210, 47,238, 31,222, 34,225, 18,209, 46,237, 30,221}, + {161, 98,146, 82,173,110,157, 94,160, 97,145, 81,172,109,156, 93,162, 99,146, 83,174,110,158, 95,161, 98,145, 82,173,109,157, 94}, + { 10,201, 58,249, 6,197, 54,245, 9,200, 57,248, 5,196, 53,244, 11,202, 59,250, 7,198, 55,246, 10,201, 58,249, 6,197, 54,245}, + {138, 74,185,122,134, 70,181,118,137, 73,184,121,133, 69,180,117,138, 75,186,122,134, 71,182,118,137, 74,185,121,133, 70,181,117}, + { 42,233, 26,217, 38,229, 22,213, 41,232, 25,216, 37,228, 21,212, 43,234, 27,218, 39,230, 23,214, 42,233, 26,217, 38,229, 22,213}, + {169,106,153, 90,165,102,149, 86,168,105,152, 89,164,101,148, 85,170,107,154, 91,166,103,150, 87,169,106,153, 90,165,102,149, 86}, + { 1,192, 49,239, 13,204, 61,251, 4,195, 52,242, 16,207, 64,254, 1,191, 48,239, 13,203, 60,251, 4,194, 51,242, 16,206, 63,254}, + {128, 65,176,112,140, 76,188,124,131, 68,179,115,143, 79,191,127,128, 64,176,112,140, 76,187,124,131, 67,179,115,143, 79,190,127}, + { 33,223, 17,208, 45,235, 29,219, 36,226, 20,211, 48,238, 32,222, 33,223, 17,207, 44,235, 29,219, 36,226, 20,210, 47,238, 32,222}, + {160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95,160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95}, + { 9,200, 57,247, 5,196, 53,243, 12,203, 60,250, 8,199, 56,246, 9,199, 56,247, 5,195, 52,243, 12,202, 59,250, 8,198, 55,246}, + {136, 73,184,120,132, 69,180,116,139, 75,187,123,135, 72,183,119,136, 72,183,120,132, 68,180,116,139, 75,186,123,135, 71,182,119}, + { 41,231, 25,216, 37,227, 21,212, 44,234, 28,218, 40,230, 24,215, 40,231, 25,215, 37,227, 21,211, 43,234, 28,218, 39,230, 24,214}, + {168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87}, + { 3,194, 51,241, 15,206, 63,253, 2,193, 50,240, 14,205, 62,252, 3,193, 50,241, 15,205, 62,253, 2,192, 49,240, 14,204, 61,252}, + {130, 67,178,114,142, 78,190,126,129, 66,177,113,141, 77,189,125,130, 66,178,114,142, 78,189,126,129, 65,177,113,141, 77,188,125}, + { 35,225, 19,210, 47,237, 31,221, 34,224, 18,209, 46,236, 30,220, 35,225, 19,209, 46,237, 31,221, 34,224, 18,208, 45,236, 30,220}, + {162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93,162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93}, + { 11,202, 59,249, 7,198, 55,245, 10,201, 58,248, 6,197, 54,244, 11,201, 58,249, 7,197, 54,245, 10,200, 57,248, 6,196, 53,244}, + {138, 74,186,122,134, 71,182,118,137, 73,185,121,133, 70,181,117,138, 74,185,122,134, 70,182,118,137, 73,184,121,133, 69,181,117}, + { 43,233, 27,218, 39,229, 23,214, 42,232, 26,217, 38,228, 22,213, 42,233, 27,217, 38,229, 23,213, 41,232, 26,216, 37,228, 22,212}, + {170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85} +}; + +static const guchar DM_ORIGINAL[32][32] = { + { 1,191, 48,239, 12,203, 60,251, 3,194, 51,242, 15,206, 63,254, 1,192, 49,240, 13,204, 61,252, 4,195, 52,243, 16,207, 64,255}, + {128, 64,175,112,140, 76,187,124,131, 67,178,115,143, 79,190,127,128, 65,176,112,140, 77,188,124,131, 68,179,115,143, 80,191,127}, + { 32,223, 16,207, 44,235, 28,219, 35,226, 19,210, 47,238, 31,222, 33,224, 17,208, 45,236, 29,220, 36,227, 20,211, 48,239, 32,223}, + {159, 96,144, 80,171,108,155, 92,162, 99,146, 83,174,111,158, 95,160, 97,144, 81,172,109,156, 93,163,100,147, 84,175,111,159, 96}, + { 8,199, 56,247, 4,195, 52,243, 11,202, 59,250, 7,198, 55,246, 9,200, 57,248, 5,196, 53,244, 12,203, 60,251, 8,199, 56,247}, + {136, 72,183,120,132, 68,179,116,139, 75,186,123,135, 71,182,119,136, 73,184,120,132, 69,180,116,139, 76,187,123,135, 72,183,119}, + { 40,231, 24,215, 36,227, 20,211, 43,234, 27,218, 39,230, 23,214, 41,232, 25,216, 37,228, 21,212, 44,235, 28,219, 40,231, 24,215}, + {167,104,151, 88,163,100,147, 84,170,107,154, 91,166,103,150, 87,168,105,152, 89,164,101,148, 85,171,108,155, 92,167,104,151, 88}, + { 2,193, 50,241, 14,205, 62,253, 1,192, 49,240, 13,204, 61,252, 3,194, 51,242, 15,206, 63,254, 2,193, 50,241, 14,205, 62,253}, + {130, 66,177,114,142, 78,189,126,129, 65,176,113,141, 77,188,125,130, 67,178,114,142, 79,190,126,129, 66,177,113,141, 78,189,125}, + { 34,225, 18,209, 46,237, 30,221, 33,224, 17,208, 45,236, 29,220, 35,226, 19,210, 47,238, 31,222, 34,225, 18,209, 46,237, 30,221}, + {161, 98,146, 82,173,110,157, 94,160, 97,145, 81,172,109,156, 93,162, 99,146, 83,174,110,158, 95,161, 98,145, 82,173,109,157, 94}, + { 10,201, 58,249, 6,197, 54,245, 9,200, 57,248, 5,196, 53,244, 11,202, 59,250, 7,198, 55,246, 10,201, 58,249, 6,197, 54,245}, + {138, 74,185,122,134, 70,181,118,137, 73,184,121,133, 69,180,117,138, 75,186,122,134, 71,182,118,137, 74,185,121,133, 70,181,117}, + { 42,233, 26,217, 38,229, 22,213, 41,232, 25,216, 37,228, 21,212, 43,234, 27,218, 39,230, 23,214, 42,233, 26,217, 38,229, 22,213}, + {169,106,153, 90,165,102,149, 86,168,105,152, 89,164,101,148, 85,170,107,154, 91,166,103,150, 87,169,106,153, 90,165,102,149, 86}, + { 1,192, 49,239, 13,204, 61,251, 4,195, 52,242, 16,207, 64,254, 1,191, 48,239, 13,203, 60,251, 4,194, 51,242, 16,206, 63,254}, + {128, 65,176,112,140, 76,188,124,131, 68,179,115,143, 79,191,127,128, 64,176,112,140, 76,187,124,131, 67,179,115,143, 79,190,127}, + { 33,223, 17,208, 45,235, 29,219, 36,226, 20,211, 48,238, 32,222, 33,223, 17,207, 44,235, 29,219, 36,226, 20,210, 47,238, 32,222}, + {160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95,160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95}, + { 9,200, 57,247, 5,196, 53,243, 12,203, 60,250, 8,199, 56,246, 9,199, 56,247, 5,195, 52,243, 12,202, 59,250, 8,198, 55,246}, + {136, 73,184,120,132, 69,180,116,139, 75,187,123,135, 72,183,119,136, 72,183,120,132, 68,180,116,139, 75,186,123,135, 71,182,119}, + { 41,231, 25,216, 37,227, 21,212, 44,234, 28,218, 40,230, 24,215, 40,231, 25,215, 37,227, 21,211, 43,234, 28,218, 39,230, 24,214}, + {168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87}, + { 3,194, 51,241, 15,206, 63,253, 2,193, 50,240, 14,205, 62,252, 3,193, 50,241, 15,205, 62,253, 2,192, 49,240, 14,204, 61,252}, + {130, 67,178,114,142, 78,190,126,129, 66,177,113,141, 77,189,125,130, 66,178,114,142, 78,189,126,129, 65,177,113,141, 77,188,125}, + { 35,225, 19,210, 47,237, 31,221, 34,224, 18,209, 46,236, 30,220, 35,225, 19,209, 46,237, 31,221, 34,224, 18,208, 45,236, 30,220}, + {162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93,162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93}, + { 11,202, 59,249, 7,198, 55,245, 10,201, 58,248, 6,197, 54,244, 11,201, 58,249, 7,197, 54,245, 10,200, 57,248, 6,196, 53,244}, + {138, 74,186,122,134, 71,182,118,137, 73,185,121,133, 70,181,117,138, 74,185,122,134, 70,182,118,137, 73,184,121,133, 69,181,117}, + { 43,233, 27,218, 39,229, 23,214, 42,232, 26,217, 38,228, 22,213, 42,233, 27,217, 38,229, 23,213, 41,232, 26,216, 37,228, 22,212}, + {170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85} +}; + +#endif /* __GIMP_IMAGE_CONVERT_DATA_H__ */ diff --git a/app/core/gimpimage-convert-fsdither.h b/app/core/gimpimage-convert-fsdither.h new file mode 100644 index 0000000..ebd9800 --- /dev/null +++ b/app/core/gimpimage-convert-fsdither.h @@ -0,0 +1,559 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_CONVERT_FSDITHER_H__ +#define __GIMP_IMAGE_CONVERT_FSDITHER_H__ + +/* The following 5 arrays are used in performing floyd-steinberg + * error diffusion dithering. The range array allows the quick + * bounds checking of pixel values. The 4 error arrays contain + * the error computations for the east, south-east, south and + * south-west pixels surrounding the current pixel respectively. + */ + + +static const guchar range_array[] = { + 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, 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, 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, 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, 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, + 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, 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, 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, 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, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, +}; + +static const gshort floyd_steinberg_error1[] = { + -223, -223, -222, -222, -221, -221, -220, -220, -220, -219, + -219, -218, -218, -217, -217, -217, -216, -216, -215, -215, + -214, -214, -213, -213, -213, -212, -212, -211, -211, -210, + -210, -210, -209, -209, -208, -208, -207, -207, -206, -206, + -206, -205, -205, -204, -204, -203, -203, -203, -202, -202, + -201, -201, -200, -200, -199, -199, -199, -198, -198, -197, + -197, -196, -196, -196, -195, -195, -194, -194, -193, -193, + -192, -192, -192, -191, -191, -190, -190, -189, -189, -189, + -188, -188, -187, -187, -186, -186, -185, -185, -185, -184, + -184, -183, -183, -182, -182, -182, -181, -181, -180, -180, + -179, -179, -178, -178, -178, -177, -177, -176, -176, -175, + -175, -175, -174, -174, -173, -173, -172, -172, -171, -171, + -171, -170, -170, -169, -169, -168, -168, -168, -167, -167, + -166, -166, -165, -165, -164, -164, -164, -163, -163, -162, + -162, -161, -161, -161, -160, -160, -159, -159, -158, -158, + -157, -157, -157, -156, -156, -155, -155, -154, -154, -154, + -153, -153, -152, -152, -151, -151, -150, -150, -150, -149, + -149, -148, -148, -147, -147, -147, -146, -146, -145, -145, + -144, -144, -143, -143, -143, -142, -142, -141, -141, -140, + -140, -140, -139, -139, -138, -138, -137, -137, -136, -136, + -136, -135, -135, -134, -134, -133, -133, -133, -132, -132, + -131, -131, -130, -130, -129, -129, -129, -128, -128, -127, + -127, -126, -126, -126, -125, -125, -124, -124, -123, -123, + -122, -122, -122, -121, -121, -120, -120, -119, -119, -119, + -118, -118, -117, -117, -116, -116, -115, -115, -115, -114, + -114, -113, -113, -112, -112, -112, -111, -111, -110, -110, + -109, -109, -108, -108, -108, -107, -107, -106, -106, -105, + -105, -105, -104, -104, -103, -103, -102, -102, -101, -101, + -101, -100, -100, -99, -99, -98, -98, -98, -97, -97, + -96, -96, -95, -95, -94, -94, -94, -93, -93, -92, + -92, -91, -91, -91, -90, -90, -89, -89, -88, -88, + -87, -87, -87, -86, -86, -85, -85, -84, -84, -84, + -83, -83, -82, -82, -81, -81, -80, -80, -80, -79, + -79, -78, -78, -77, -77, -77, -76, -76, -75, -75, + -74, -74, -73, -73, -73, -72, -72, -71, -71, -70, + -70, -70, -69, -69, -68, -68, -67, -67, -66, -66, + -66, -65, -65, -64, -64, -63, -63, -63, -62, -62, + -61, -61, -60, -60, -59, -59, -59, -58, -58, -57, + -57, -56, -56, -56, -55, -55, -54, -54, -53, -53, + -52, -52, -52, -51, -51, -50, -50, -49, -49, -49, + -48, -48, -47, -47, -46, -46, -45, -45, -45, -44, + -44, -43, -43, -42, -42, -42, -41, -41, -40, -40, + -39, -39, -38, -38, -38, -37, -37, -36, -36, -35, + -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, + -31, -30, -30, -29, -29, -28, -28, -28, -27, -27, + -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, + -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, + -17, -17, -17, -16, -16, -15, -15, -14, -14, -14, + -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, + -9, -8, -8, -7, -7, -7, -6, -6, -5, -5, + -4, -4, -3, -3, -3, -2, -2, -1, -1, 0, + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, + 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, + 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, + 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, + 21, 21, 22, 22, 23, 23, 24, 24, 24, 25, + 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, + 30, 30, 31, 31, 31, 32, 32, 33, 33, 34, + 34, 35, 35, 35, 36, 36, 37, 37, 38, 38, + 38, 39, 39, 40, 40, 41, 41, 42, 42, 42, + 43, 43, 44, 44, 45, 45, 45, 46, 46, 47, + 47, 48, 48, 49, 49, 49, 50, 50, 51, 51, + 52, 52, 52, 53, 53, 54, 54, 55, 55, 56, + 56, 56, 57, 57, 58, 58, 59, 59, 59, 60, + 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, + 65, 65, 66, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 70, 71, 71, 72, 72, 73, 73, + 73, 74, 74, 75, 75, 76, 76, 77, 77, 77, + 78, 78, 79, 79, 80, 80, 80, 81, 81, 82, + 82, 83, 83, 84, 84, 84, 85, 85, 86, 86, + 87, 87, 87, 88, 88, 89, 89, 90, 90, 91, + 91, 91, 92, 92, 93, 93, 94, 94, 94, 95, + 95, 96, 96, 97, 97, 98, 98, 98, 99, 99, + 100, 100, 101, 101, 101, 102, 102, 103, 103, 104, + 104, 105, 105, 105, 106, 106, 107, 107, 108, 108, + 108, 109, 109, 110, 110, 111, 111, 112, 112, 112, + 113, 113, 114, 114, 115, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 119, 120, 120, 121, 121, + 122, 122, 122, 123, 123, 124, 124, 125, 125, 126, + 126, 126, 127, 127, 128, 128, 129, 129, 129, 130, + 130, 131, 131, 132, 132, 133, 133, 133, 134, 134, + 135, 135, 136, 136, 136, 137, 137, 138, 138, 139, + 139, 140, 140, 140, 141, 141, 142, 142, 143, 143, + 143, 144, 144, 145, 145, 146, 146, 147, 147, 147, + 148, 148, 149, 149, 150, 150, 150, 151, 151, 152, + 152, 153, 153, 154, 154, 154, 155, 155, 156, 156, + 157, 157, 157, 158, 158, 159, 159, 160, 160, 161, + 161, 161, 162, 162, 163, 163, 164, 164, 164, 165, + 165, 166, 166, 167, 167, 168, 168, 168, 169, 169, + 170, 170, 171, 171, 171, 172, 172, 173, 173, 174, + 174, 175, 175, 175, 176, 176, 177, 177, 178, 178, + 178, 179, 179, 180, 180, 181, 181, 182, 182, 182, + 183, 183, 184, 184, 185, 185, 185, 186, 186, 187, + 187, 188, 188, 189, 189, 189, 190, 190, 191, 191, + 192, 192, 192, 193, 193, 194, 194, 195, 195, 196, + 196, 196, 197, 197, 198, 198, 199, 199, 199, 200, + 200, 201, 201, 202, 202, 203, 203, 203, 204, 204, + 205, 205, 206, 206, 206, 207, 207, 208, 208, 209, + 209, 210, 210, 210, 211, 211, 212, 212, 213, 213, + 213, 214, 214, 215, 215, 216, 216, 217, 217, 217, + 218, 218, 219, 219, 220, 220, 220, 221, 221, 222, + 222, 223, 223, 224, 224, +}; + +static const gshort floyd_steinberg_error2[] = { + -95, -95, -95, -95, -95, -94, -94, -94, -94, -94, + -93, -93, -93, -93, -93, -93, -92, -92, -92, -92, + -92, -91, -91, -91, -91, -91, -90, -90, -90, -90, + -90, -90, -89, -89, -89, -89, -89, -88, -88, -88, + -88, -88, -87, -87, -87, -87, -87, -87, -86, -86, + -86, -86, -86, -85, -85, -85, -85, -85, -84, -84, + -84, -84, -84, -84, -83, -83, -83, -83, -83, -82, + -82, -82, -82, -82, -81, -81, -81, -81, -81, -81, + -80, -80, -80, -80, -80, -79, -79, -79, -79, -79, + -78, -78, -78, -78, -78, -78, -77, -77, -77, -77, + -77, -76, -76, -76, -76, -76, -75, -75, -75, -75, + -75, -75, -74, -74, -74, -74, -74, -73, -73, -73, + -73, -73, -72, -72, -72, -72, -72, -72, -71, -71, + -71, -71, -71, -70, -70, -70, -70, -70, -69, -69, + -69, -69, -69, -69, -68, -68, -68, -68, -68, -67, + -67, -67, -67, -67, -66, -66, -66, -66, -66, -66, + -65, -65, -65, -65, -65, -64, -64, -64, -64, -64, + -63, -63, -63, -63, -63, -63, -62, -62, -62, -62, + -62, -61, -61, -61, -61, -61, -60, -60, -60, -60, + -60, -60, -59, -59, -59, -59, -59, -58, -58, -58, + -58, -58, -57, -57, -57, -57, -57, -57, -56, -56, + -56, -56, -56, -55, -55, -55, -55, -55, -54, -54, + -54, -54, -54, -54, -53, -53, -53, -53, -53, -52, + -52, -52, -52, -52, -51, -51, -51, -51, -51, -51, + -50, -50, -50, -50, -50, -49, -49, -49, -49, -49, + -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, + -47, -46, -46, -46, -46, -46, -45, -45, -45, -45, + -45, -45, -44, -44, -44, -44, -44, -43, -43, -43, + -43, -43, -42, -42, -42, -42, -42, -42, -41, -41, + -41, -41, -41, -40, -40, -40, -40, -40, -39, -39, + -39, -39, -39, -39, -38, -38, -38, -38, -38, -37, + -37, -37, -37, -37, -36, -36, -36, -36, -36, -36, + -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, + -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, + -32, -31, -31, -31, -31, -31, -30, -30, -30, -30, + -30, -30, -29, -29, -29, -29, -29, -28, -28, -28, + -28, -28, -27, -27, -27, -27, -27, -27, -26, -26, + -26, -26, -26, -25, -25, -25, -25, -25, -24, -24, + -24, -24, -24, -24, -23, -23, -23, -23, -23, -22, + -22, -22, -22, -22, -21, -21, -21, -21, -21, -21, + -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, + -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, + -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, + -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, + -13, -13, -12, -12, -12, -12, -12, -12, -11, -11, + -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, + -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, + -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, + -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, + -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, + 14, 15, 15, 15, 15, 15, 15, 16, 16, 16, + 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, + 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, + 20, 20, 20, 21, 21, 21, 21, 21, 21, 22, + 22, 22, 22, 22, 23, 23, 23, 23, 23, 24, + 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, + 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, + 29, 30, 30, 30, 30, 30, 30, 31, 31, 31, + 31, 31, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, + 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, + 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, + 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, + 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, + 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, + 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, + 48, 48, 48, 49, 49, 49, 49, 49, 50, 50, + 50, 50, 50, 51, 51, 51, 51, 51, 51, 52, + 52, 52, 52, 52, 53, 53, 53, 53, 53, 54, + 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, + 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, + 57, 58, 58, 58, 58, 58, 59, 59, 59, 59, + 59, 60, 60, 60, 60, 60, 60, 61, 61, 61, + 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, + 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, + 65, 65, 65, 66, 66, 66, 66, 66, 66, 67, + 67, 67, 67, 67, 68, 68, 68, 68, 68, 69, + 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, + 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, + 72, 73, 73, 73, 73, 73, 74, 74, 74, 74, + 74, 75, 75, 75, 75, 75, 75, 76, 76, 76, + 76, 76, 77, 77, 77, 77, 77, 78, 78, 78, + 78, 78, 78, 79, 79, 79, 79, 79, 80, 80, + 80, 80, 80, 81, 81, 81, 81, 81, 81, 82, + 82, 82, 82, 82, 83, 83, 83, 83, 83, 84, + 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, + 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, + 87, 88, 88, 88, 88, 88, 89, 89, 89, 89, + 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, + 91, 91, 92, 92, 92, 92, 92, 93, 93, 93, + 93, 93, 93, 94, 94, 94, 94, 94, 95, 95, + 95, 95, 95, 96, 96, +}; + +static const gshort floyd_steinberg_error3[] = { + -159, -159, -159, -158, -158, -158, -157, -157, -157, -156, + -156, -156, -155, -155, -155, -155, -154, -154, -154, -153, + -153, -153, -152, -152, -152, -151, -151, -151, -150, -150, + -150, -150, -149, -149, -149, -148, -148, -148, -147, -147, + -147, -146, -146, -146, -145, -145, -145, -145, -144, -144, + -144, -143, -143, -143, -142, -142, -142, -141, -141, -141, + -140, -140, -140, -140, -139, -139, -139, -138, -138, -138, + -137, -137, -137, -136, -136, -136, -135, -135, -135, -135, + -134, -134, -134, -133, -133, -133, -132, -132, -132, -131, + -131, -131, -130, -130, -130, -130, -129, -129, -129, -128, + -128, -128, -127, -127, -127, -126, -126, -126, -125, -125, + -125, -125, -124, -124, -124, -123, -123, -123, -122, -122, + -122, -121, -121, -121, -120, -120, -120, -120, -119, -119, + -119, -118, -118, -118, -117, -117, -117, -116, -116, -116, + -115, -115, -115, -115, -114, -114, -114, -113, -113, -113, + -112, -112, -112, -111, -111, -111, -110, -110, -110, -110, + -109, -109, -109, -108, -108, -108, -107, -107, -107, -106, + -106, -106, -105, -105, -105, -105, -104, -104, -104, -103, + -103, -103, -102, -102, -102, -101, -101, -101, -100, -100, + -100, -100, -99, -99, -99, -98, -98, -98, -97, -97, + -97, -96, -96, -96, -95, -95, -95, -95, -94, -94, + -94, -93, -93, -93, -92, -92, -92, -91, -91, -91, + -90, -90, -90, -90, -89, -89, -89, -88, -88, -88, + -87, -87, -87, -86, -86, -86, -85, -85, -85, -85, + -84, -84, -84, -83, -83, -83, -82, -82, -82, -81, + -81, -81, -80, -80, -80, -80, -79, -79, -79, -78, + -78, -78, -77, -77, -77, -76, -76, -76, -75, -75, + -75, -75, -74, -74, -74, -73, -73, -73, -72, -72, + -72, -71, -71, -71, -70, -70, -70, -70, -69, -69, + -69, -68, -68, -68, -67, -67, -67, -66, -66, -66, + -65, -65, -65, -65, -64, -64, -64, -63, -63, -63, + -62, -62, -62, -61, -61, -61, -60, -60, -60, -60, + -59, -59, -59, -58, -58, -58, -57, -57, -57, -56, + -56, -56, -55, -55, -55, -55, -54, -54, -54, -53, + -53, -53, -52, -52, -52, -51, -51, -51, -50, -50, + -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, + -47, -46, -46, -46, -45, -45, -45, -45, -44, -44, + -44, -43, -43, -43, -42, -42, -42, -41, -41, -41, + -40, -40, -40, -40, -39, -39, -39, -38, -38, -38, + -37, -37, -37, -36, -36, -36, -35, -35, -35, -35, + -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, + -31, -31, -30, -30, -30, -30, -29, -29, -29, -28, + -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, + -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, + -22, -21, -21, -21, -20, -20, -20, -20, -19, -19, + -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, + -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, + -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, + -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, + -6, -6, -5, -5, -5, -5, -4, -4, -4, -3, + -3, -3, -2, -2, -2, -1, -1, -1, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, + 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, + 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, + 18, 18, 19, 19, 19, 20, 20, 20, 20, 21, + 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, + 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, + 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, + 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, + 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, + 43, 43, 44, 44, 44, 45, 45, 45, 45, 46, + 46, 46, 47, 47, 47, 48, 48, 48, 49, 49, + 49, 50, 50, 50, 50, 51, 51, 51, 52, 52, + 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, + 55, 56, 56, 56, 57, 57, 57, 58, 58, 58, + 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, + 62, 62, 62, 63, 63, 63, 64, 64, 64, 65, + 65, 65, 65, 66, 66, 66, 67, 67, 67, 68, + 68, 68, 69, 69, 69, 70, 70, 70, 70, 71, + 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, + 74, 75, 75, 75, 75, 76, 76, 76, 77, 77, + 77, 78, 78, 78, 79, 79, 79, 80, 80, 80, + 80, 81, 81, 81, 82, 82, 82, 83, 83, 83, + 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, + 87, 87, 87, 88, 88, 88, 89, 89, 89, 90, + 90, 90, 90, 91, 91, 91, 92, 92, 92, 93, + 93, 93, 94, 94, 94, 95, 95, 95, 95, 96, + 96, 96, 97, 97, 97, 98, 98, 98, 99, 99, + 99, 100, 100, 100, 100, 101, 101, 101, 102, 102, + 102, 103, 103, 103, 104, 104, 104, 105, 105, 105, + 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, + 109, 109, 109, 110, 110, 110, 110, 111, 111, 111, + 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, + 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, + 118, 118, 119, 119, 119, 120, 120, 120, 120, 121, + 121, 121, 122, 122, 122, 123, 123, 123, 124, 124, + 124, 125, 125, 125, 125, 126, 126, 126, 127, 127, + 127, 128, 128, 128, 129, 129, 129, 130, 130, 130, + 130, 131, 131, 131, 132, 132, 132, 133, 133, 133, + 134, 134, 134, 135, 135, 135, 135, 136, 136, 136, + 137, 137, 137, 138, 138, 138, 139, 139, 139, 140, + 140, 140, 140, 141, 141, 141, 142, 142, 142, 143, + 143, 143, 144, 144, 144, 145, 145, 145, 145, 146, + 146, 146, 147, 147, 147, 148, 148, 148, 149, 149, + 149, 150, 150, 150, 150, 151, 151, 151, 152, 152, + 152, 153, 153, 153, 154, 154, 154, 155, 155, 155, + 155, 156, 156, 156, 157, 157, 157, 158, 158, 158, + 159, 159, 159, 160, 160, +}; + +static const gshort floyd_steinberg_error4[] = { + -34, -33, -33, -33, -33, -33, -34, -33, -32, -33, + -33, -33, -33, -33, -32, -31, -33, -32, -32, -32, + -32, -32, -33, -32, -31, -32, -32, -32, -32, -32, + -31, -30, -32, -31, -31, -31, -31, -31, -32, -31, + -30, -31, -31, -31, -31, -31, -30, -29, -31, -30, + -30, -30, -30, -30, -31, -30, -29, -30, -30, -30, + -30, -30, -29, -28, -30, -29, -29, -29, -29, -29, + -30, -29, -28, -29, -29, -29, -29, -29, -28, -27, + -29, -28, -28, -28, -28, -28, -29, -28, -27, -28, + -28, -28, -28, -28, -27, -26, -28, -27, -27, -27, + -27, -27, -28, -27, -26, -27, -27, -27, -27, -27, + -26, -25, -27, -26, -26, -26, -26, -26, -27, -26, + -25, -26, -26, -26, -26, -26, -25, -24, -26, -25, + -25, -25, -25, -25, -26, -25, -24, -25, -25, -25, + -25, -25, -24, -23, -25, -24, -24, -24, -24, -24, + -25, -24, -23, -24, -24, -24, -24, -24, -23, -22, + -24, -23, -23, -23, -23, -23, -24, -23, -22, -23, + -23, -23, -23, -23, -22, -21, -23, -22, -22, -22, + -22, -22, -23, -22, -21, -22, -22, -22, -22, -22, + -21, -20, -22, -21, -21, -21, -21, -21, -22, -21, + -20, -21, -21, -21, -21, -21, -20, -19, -21, -20, + -20, -20, -20, -20, -21, -20, -19, -20, -20, -20, + -20, -20, -19, -18, -20, -19, -19, -19, -19, -19, + -20, -19, -18, -19, -19, -19, -19, -19, -18, -17, + -19, -18, -18, -18, -18, -18, -19, -18, -17, -18, + -18, -18, -18, -18, -17, -16, -18, -17, -17, -17, + -17, -17, -18, -17, -16, -17, -17, -17, -17, -17, + -16, -15, -17, -16, -16, -16, -16, -16, -17, -16, + -15, -16, -16, -16, -16, -16, -15, -14, -16, -15, + -15, -15, -15, -15, -16, -15, -14, -15, -15, -15, + -15, -15, -14, -13, -15, -14, -14, -14, -14, -14, + -15, -14, -13, -14, -14, -14, -14, -14, -13, -12, + -14, -13, -13, -13, -13, -13, -14, -13, -12, -13, + -13, -13, -13, -13, -12, -11, -13, -12, -12, -12, + -12, -12, -13, -12, -11, -12, -12, -12, -12, -12, + -11, -10, -12, -11, -11, -11, -11, -11, -12, -11, + -10, -11, -11, -11, -11, -11, -10, -9, -11, -10, + -10, -10, -10, -10, -11, -10, -9, -10, -10, -10, + -10, -10, -9, -8, -10, -9, -9, -9, -9, -9, + -10, -9, -8, -9, -9, -9, -9, -9, -8, -7, + -9, -8, -8, -8, -8, -8, -9, -8, -7, -8, + -8, -8, -8, -8, -7, -6, -8, -7, -7, -7, + -7, -7, -8, -7, -6, -7, -7, -7, -7, -7, + -6, -5, -7, -6, -6, -6, -6, -6, -7, -6, + -5, -6, -6, -6, -6, -6, -5, -4, -6, -5, + -5, -5, -5, -5, -6, -5, -4, -5, -5, -5, + -5, -5, -4, -3, -5, -4, -4, -4, -4, -4, + -5, -4, -3, -4, -4, -4, -4, -4, -3, -2, + -4, -3, -3, -3, -3, -3, -4, -3, -2, -3, + -3, -3, -3, -3, -2, -1, -3, -2, -2, -2, + -2, -2, -3, -2, -1, -2, -2, -2, -2, -2, + -1, 0, 1, 2, 2, 2, 2, 2, 1, 2, + 3, 2, 2, 2, 2, 2, 3, 1, 2, 3, + 3, 3, 3, 3, 2, 3, 4, 3, 3, 3, + 3, 3, 4, 2, 3, 4, 4, 4, 4, 4, + 3, 4, 5, 4, 4, 4, 4, 4, 5, 3, + 4, 5, 5, 5, 5, 5, 4, 5, 6, 5, + 5, 5, 5, 5, 6, 4, 5, 6, 6, 6, + 6, 6, 5, 6, 7, 6, 6, 6, 6, 6, + 7, 5, 6, 7, 7, 7, 7, 7, 6, 7, + 8, 7, 7, 7, 7, 7, 8, 6, 7, 8, + 8, 8, 8, 8, 7, 8, 9, 8, 8, 8, + 8, 8, 9, 7, 8, 9, 9, 9, 9, 9, + 8, 9, 10, 9, 9, 9, 9, 9, 10, 8, + 9, 10, 10, 10, 10, 10, 9, 10, 11, 10, + 10, 10, 10, 10, 11, 9, 10, 11, 11, 11, + 11, 11, 10, 11, 12, 11, 11, 11, 11, 11, + 12, 10, 11, 12, 12, 12, 12, 12, 11, 12, + 13, 12, 12, 12, 12, 12, 13, 11, 12, 13, + 13, 13, 13, 13, 12, 13, 14, 13, 13, 13, + 13, 13, 14, 12, 13, 14, 14, 14, 14, 14, + 13, 14, 15, 14, 14, 14, 14, 14, 15, 13, + 14, 15, 15, 15, 15, 15, 14, 15, 16, 15, + 15, 15, 15, 15, 16, 14, 15, 16, 16, 16, + 16, 16, 15, 16, 17, 16, 16, 16, 16, 16, + 17, 15, 16, 17, 17, 17, 17, 17, 16, 17, + 18, 17, 17, 17, 17, 17, 18, 16, 17, 18, + 18, 18, 18, 18, 17, 18, 19, 18, 18, 18, + 18, 18, 19, 17, 18, 19, 19, 19, 19, 19, + 18, 19, 20, 19, 19, 19, 19, 19, 20, 18, + 19, 20, 20, 20, 20, 20, 19, 20, 21, 20, + 20, 20, 20, 20, 21, 19, 20, 21, 21, 21, + 21, 21, 20, 21, 22, 21, 21, 21, 21, 21, + 22, 20, 21, 22, 22, 22, 22, 22, 21, 22, + 23, 22, 22, 22, 22, 22, 23, 21, 22, 23, + 23, 23, 23, 23, 22, 23, 24, 23, 23, 23, + 23, 23, 24, 22, 23, 24, 24, 24, 24, 24, + 23, 24, 25, 24, 24, 24, 24, 24, 25, 23, + 24, 25, 25, 25, 25, 25, 24, 25, 26, 25, + 25, 25, 25, 25, 26, 24, 25, 26, 26, 26, + 26, 26, 25, 26, 27, 26, 26, 26, 26, 26, + 27, 25, 26, 27, 27, 27, 27, 27, 26, 27, + 28, 27, 27, 27, 27, 27, 28, 26, 27, 28, + 28, 28, 28, 28, 27, 28, 29, 28, 28, 28, + 28, 28, 29, 27, 28, 29, 29, 29, 29, 29, + 28, 29, 30, 29, 29, 29, 29, 29, 30, 28, + 29, 30, 30, 30, 30, 30, 29, 30, 31, 30, + 30, 30, 30, 30, 31, 29, 30, 31, 31, 31, + 31, 31, 30, 31, 32, 31, 31, 31, 31, 31, + 32, 30, 31, 32, 32, 32, 32, 32, 31, 32, + 33, 32, 32, 32, 32, 32, 33, 31, 32, 33, + 33, 33, 33, 33, 32, 33, 34, 33, 33, 33, + 33, 33, 34, 32, 33, +}; + +#endif /* __GIMP_IMAGE_CONVERT_FSDITHER_H__ */ diff --git a/app/core/gimpimage-convert-indexed.c b/app/core/gimpimage-convert-indexed.c new file mode 100644 index 0000000..6a3eae3 --- /dev/null +++ b/app/core/gimpimage-convert-indexed.c @@ -0,0 +1,4567 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-convert-indexed.c + * Copyright (C) 1997-2004 Adam D. Moss + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * 2005-09-04 - Switch 'positional' dither matrix to a 32x32 Bayer, + * which generates results that compress somewhat better (and may look + * worse or better depending on what you enjoy...). [adam@gimp.org] + * + * 2004-12-12 - Use a slower but much nicer technique for finding the + * two best colors to dither between when using fixed/positional + * dither methods. Makes positional dither much less lame. [adam@gimp.org] + * + * 2002-02-10 - Quantizer version 3.0 (the rest of the commit started + * a year ago -- whoops). Divide colors within CIE L*a*b* space using + * CPercep module (cpercep.[ch]), color-match and dither likewise, + * change the underlying box selection criteria and division point + * logic, bump luminance precision upwards, etc.etc. Generally + * chooses a much richer color set, especially for low numbers of + * colors. n.b.: Less luminance-sloppy in straight remapping which is + * good for color but a bit worse for high-frequency detail (that's + * partly what fs-dithering is for -- use it). [adam@gimp.org] + * + * 2001-03-25 - Define accessor function/macro for histogram reads and + * writes. This slows us down a little because we avoid some of the + * dirty tricks we used when we knew that the histogram was a straight + * 3d array, so I've recovered some of the speed loss by implementing + * a 5d accessor function with good locality of reference. This change + * is the first step towards quantizing in a more interesting colorspace + * than frumpy old RGB. [Adam] + * + * 2000/01/30 - Use palette_selector instead of option_menu for custom + * palette. Use libgimp callback functions. [Sven] + * + * 99/09/01 - Created a low-bleed FS-dither option. [Adam] + * + * 99/08/29 - Deterministic color dithering to arbitrary palettes. + * Ideal for animations that are going to be delta-optimized or simply + * don't want to look 'busy' in static areas. Also a bunch of bugfixes + * and tweaks. [Adam] + * + * 99/08/28 - Deterministic alpha dithering over layers, reduced bleeding + * of transparent values into opaque values, added optional stage to + * remove duplicate or unused color entries from final colormap. [Adam] + * + * 99/02/24 - Many revisions to the box-cut quantizer used in RGB->INDEXED + * conversion. Box to be cut is chosen on the basis of possessing an axis + * with the largest sum of weighted perceptible error, rather than based on + * volume or population. The box is split along this axis rather than its + * longest axis, at the point of error mean rather than simply at its centre. + * Error-limiting in the F-S dither has been disabled - it may become optional + * again later. If you're convinced that you have an image where the old + * dither looks better, let me know. [Adam] + * + * 99/01/10 - Hourglass... [Adam] + * + * 98/07/25 - Convert-to-indexed now remembers the last invocation's + * settings. Also, GRAY->INDEXED is more flexible. [Adam] + * + * 98/07/05 - Sucked the warning about quantizing to too many colors into + * a text widget embedded in the dialog, improved intelligence of dialog + * to default 'custom palette' selection to 'Web' if available, and + * in this case not bother to present the native WWW-palette radio + * button. [Adam] + * + * 98/04/13 - avoid a division by zero when converting an empty gray-scale + * image (who would like to do such a thing anyway??) [Sven ] + * + * 98/03/23 - fixed a longstanding fencepost - hopefully the *right* + * way, *again*. [Adam] + * + * 97/11/14 - added a proper pdb interface and support for dithering + * to custom palettes (based on a patch by Eric Hernes) [Yosh] + * + * 97/11/04 - fixed the accidental use of the color-counting case + * when palette_type is WEB or MONO. [Adam] + * + * 97/10/25 - color-counting implemented (could use some hashing, but + * performance actually seems okay) - now RGB->INDEXED conversion isn't + * destructive if it doesn't have to be. [Adam] + * + * 97/10/14 - fixed divide-by-zero when converting a completely transparent + * RGB image to indexed. [Adam] + * + * 97/07/01 - started todo/revision log. Put code back in to + * eliminate full-alpha pixels from RGB histogram. + * [Adam D. Moss - adam@gimp.org] + */ + + /* TODO for Convert: + * + * . Tweak, tweak, tweak. Old RGB code was tuned muchly. + * + * . Re-enable Heckbert locality for matching, benchmark it + * + * . Try faster fixed-point sRGB<->L*a*b* pixel conversion (see cpercep.c) + * + * . Use palette of another open INDEXED image? + * + * . Do error-splitting trick for GREY->INDEXED (hardly worth it) + */ + + /* CODE READABILITY BUGS: + * + * . Most uses of variants of the R,G,B variable naming convention + * are referring to L*a*b* co-ordinates, not RGB co-ordinates! + * + * . Each said variable is usually one of the following, but it is + * rarely clear which one: + * - (assumed sRGB) raw non-linear 8-bit RGB co-ordinates + * - 'full'-precision (unshifted) 8-bit L*a*b* co-ordinates + * - box-space (reduced-precision shifted L*a*b*) co-ordinates + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpdrawable.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimpobjectqueue.h" +#include "gimppalette.h" +#include "gimpprogress.h" + +#include "text/gimptextlayer.h" + +#include "gimpimage-convert-fsdither.h" +#include "gimpimage-convert-data.h" +#include "gimpimage-convert-indexed.h" + +#include "gimp-intl.h" + + +/* basic memory/quality tradeoff */ +#define PRECISION_R 8 +#define PRECISION_G 6 +#define PRECISION_B 6 + +#define HIST_R_ELEMS (1< %0.3f,%0.3f,%0.3f ", r, g, b, sL, sa, sb);*/ + + or = RINT(lab[0] * LRAT); + og = RINT((lab[1] - LOWA) * ARAT); + ob = RINT((lab[2] - LOWB) * BRAT); + + *hr = CLAMP(or, 0, 255); + *hg = CLAMP(og, 0, 255); + *hb = CLAMP(ob, 0, 255); + + /* fprintf(stderr, " %d:%d:%d ", *hr, *hg, *hb); */ +} + + +static inline void +rgb_to_lin (const guchar r, + const guchar g, + const guchar b, + gint *hr, + gint *hg, + gint *hb) +{ + gint or, og, ob; + + /* + double sL, sa, sb; + { + double low_l = 999.0, low_a = 999.9, low_b = 999.0; + double high_l = -999.0, high_a = -999.0, high_b = -999.0; + + int r,g,b; + + for (r=0; r<256; r++) + for (g=0; g<256; g++) + for (b=0; b<256; b++) + { + cpercep_rgb_to_space(r,g,b, &sL, &sa, &sb); + + if (sL > high_l) + high_l = sL; + if (sL < low_l) + low_l = sL; + if (sa > high_a) + high_a = sa; + if (sa < low_a) + low_a = sa; + if (sb > high_b) + high_b = sb; + if (sb < low_b) + low_b = sb; + } + + fprintf(stderr, " [L: %0.3f -> %0.3f / a: %0.3f -> %0.3f / b: %0.3f -> %0.3f]\t", low_l, high_l, low_a, high_a, low_b, high_b); + + exit(-1); + } + */ + + rgb_to_unshifted_lin (r, g, b, &or, &og, &ob); + +#if 0 +#define RSDF(r) ((r) >= ((HIST_R_ELEMS-1) << R_SHIFT) ? HIST_R_ELEMS-1 : \ + ((r) + ((1<>1) ) >> R_SHIFT) +#define GSDF(g) ((g) >= ((HIST_G_ELEMS-1) << G_SHIFT) ? HIST_G_ELEMS-1 : \ + ((g) + ((1<>1) ) >> G_SHIFT) +#define BSDF(b) ((b) >= ((HIST_B_ELEMS-1) << B_SHIFT) ? HIST_B_ELEMS-1 : \ + ((b) + ((1<>1) ) >> B_SHIFT) +#else +#define RSDF(r) ((r) >> R_SHIFT) +#define GSDF(g) ((g) >> G_SHIFT) +#define BSDF(b) ((b) >> B_SHIFT) +#endif + + or = RSDF (or); + og = GSDF (og); + ob = BSDF (ob); + + *hr = or; + *hg = og; + *hb = ob; +} + + +static inline ColorFreq * +HIST_RGB (ColorFreq *hist_ptr, + const gint r, + const gint g, + const gint b) +{ + gint hr, hg, hb; + + rgb_to_lin (r, g, b, &hr, &hg, &hb); + + return HIST_LIN (hist_ptr, hr, hg, hb); +} + + +static inline void +lin_to_rgb (const gdouble hr, + const gdouble hg, + const gdouble hb, + guchar *r, + guchar *g, + guchar *b) +{ + gfloat rgb[3]; + gfloat lab[3]; + gdouble ir, ig, ib; + + ir = ((gdouble) (hr)) * 255.0F / (gdouble) (HIST_R_ELEMS - 1); + ig = ((gdouble)( hg)) * 255.0F / (gdouble) (HIST_G_ELEMS - 1); + ib = ((gdouble)( hb)) * 255.0F / (gdouble) (HIST_B_ELEMS - 1); + + ir = ir / LRAT; + ig = (ig / ARAT) + LOWA; + ib = (ib / BRAT) + LOWB; + + lab[0] = ir; + lab[1] = ig; + lab[2] = ib; + + babl_process (lab_to_rgb_fish, lab, rgb, 1); + + *r = RINT (CLAMP (rgb[0] * 255, 0.0F, 255.0F)); + *g = RINT (CLAMP (rgb[1] * 255, 0.0F, 255.0F)); + *b = RINT (CLAMP (rgb[2] * 255, 0.0F, 255.0F)); +} + + + +struct _Color +{ + gint red; + gint green; + gint blue; +}; + +struct _QuantizeObj +{ + Pass1Func first_pass; /* first pass over image data creates colormap */ + Pass2InitFunc second_pass_init; /* Initialize data which persists over invocations */ + Pass2Func second_pass; /* second pass maps from image data to colormap */ + CleanupFunc delete_func; /* function to clean up data associated with private */ + + GimpPalette *custom_palette; /* The custom palette, if any */ + + gint desired_number_of_colors; /* Number of colors we will allow */ + gint actual_number_of_colors; /* Number of colors actually needed */ + Color cmap[256]; /* colormap created by quantization */ + Color clin[256]; /* .. converted back to linear space */ + guint64 index_used_count[256]; /* how many times an index was used */ + CFHistogram histogram; /* holds the histogram */ + + gboolean want_dither_alpha; + gint error_freedom; /* 0=much bleed, 1=controlled bleed */ + + GimpProgress *progress; +}; + +typedef struct +{ + /* The bounds of the box (inclusive); expressed as histogram indexes */ + gint Rmin, Rmax; + gint Rhalferror; + gint Gmin, Gmax; + gint Ghalferror; + gint Bmin, Bmax; + gint Bhalferror; + + /* The volume (actually 2-norm) of the box */ + gint volume; + + /* The number of nonzero histogram cells within this box */ + gint64 colorcount; + + /* The sum of the weighted error within this box */ + guint64 error; + /* The sum of the unweighted error within this box */ + guint64 rerror; + guint64 gerror; + guint64 berror; + +} box, *boxptr; + + +static void zero_histogram_gray (CFHistogram histogram); +static void zero_histogram_rgb (CFHistogram histogram); +static void generate_histogram_gray (CFHistogram hostogram, + GimpLayer *layer, + gboolean dither_alpha); +static void generate_histogram_rgb (CFHistogram histogram, + GimpLayer *layer, + gint col_limit, + gboolean dither_alpha, + GimpProgress *progress); + +static QuantizeObj * initialize_median_cut (GimpImageBaseType old_type, + gint max_colors, + GimpConvertDitherType dither_type, + GimpConvertPaletteType palette_type, + GimpPalette *custom_palette, + gboolean dither_alpha, + GimpProgress *progress); + +static void compute_color_lin8 (QuantizeObj *quantobj, + CFHistogram histogram, + boxptr boxp, + const int icolor); + + +static guchar found_cols[MAXNUMCOLORS][3]; +static gint num_found_cols; +static gboolean needs_quantize; +static gboolean had_white; +static gboolean had_black; + + +/**********************************************************/ +typedef struct +{ + gint64 used_count; + guchar initial_index; +} PalEntry; + +static int +mapping_compare (const void *a, + const void *b) +{ + PalEntry *m1 = (PalEntry *) a; + PalEntry *m2 = (PalEntry *) b; + + return (m2->used_count - m1->used_count); +} + +/* FWIW, the make_remap_table() and mapping_compare() function source + * and PalEntry may be re-used under the XFree86-style license. + * + */ +static void +make_remap_table (const guchar old_palette[], + guchar new_palette[], + const guint64 index_used_count[], + guchar remap_table[], + gint *num_entries) +{ + gint i, j, k; + guchar temppal[256 * 3]; + guint64 tempuse[256]; + guint64 transmap[256]; + PalEntry *palentries; + gint used = 0; + + memset (temppal, 0, 256 * 3); + memset (tempuse, 0, 256 * sizeof (guint64)); + memset (transmap, 255, 256 * sizeof (guint64)); + + /* First pass - only collect entries which are marked as being used + * at all in index_used_count. + */ + for (i = 0; i < *num_entries; i++) + { + if (index_used_count[i]) + { + temppal[used*3 + 0] = old_palette[i*3 + 0]; + temppal[used*3 + 1] = old_palette[i*3 + 1]; + temppal[used*3 + 2] = old_palette[i*3 + 2]; + + tempuse[used] = index_used_count[i]; + transmap[i] = used; + + used++; + } + } + + /* Second pass - remove duplicates. (O(n^3), could do better!) */ + for (i = 0; i < used; i++) + { + for (j = 0; j < i; j++) + { + if ((temppal[i*3 + 1] == temppal[j*3 + 1]) && + (temppal[i*3 + 0] == temppal[j*3 + 0]) && + (temppal[i*3 + 2] == temppal[j*3 + 2]) && + tempuse[j] && + tempuse[i]) + { + /* Move the 'used' tally from one to the other. */ + tempuse[i] += tempuse[j]; + /* zero one of them, deactivating its entry. */ + tempuse[j] = 0; + + /* change all mappings from this dead index to the live + * one. + */ + for (k = 0; k < *num_entries; k++) + { + if (index_used_count[k] && (transmap[k] == j)) + transmap[k] = i; + } + } + } + } + + /* Third pass - rank all used indices to the beginning of the + * palette. + */ + palentries = g_new (PalEntry, used); + + for (i = 0; i < used; i++) + { + palentries[i].initial_index = i; + palentries[i].used_count = tempuse[i]; + } + + qsort (palentries, used, sizeof (PalEntry), &mapping_compare); + + for (i = 0; i < *num_entries; i++) + { + if (index_used_count[i]) + { + for (j = 0; j < used; j++) + { + if ((transmap[i] == palentries[j].initial_index) + && (palentries[j].used_count)) + { + remap_table[i] = j; + break; + } + } + } + } + for (i = 0; i < *num_entries; i++) + { + if (index_used_count[i]) + { + new_palette[remap_table[i] * 3 + 0] = old_palette[i * 3 + 0]; + new_palette[remap_table[i] * 3 + 1] = old_palette[i * 3 + 1]; + new_palette[remap_table[i] * 3 + 2] = old_palette[i * 3 + 2]; + } + } + + *num_entries = 0; + + for (j = 0; j < used; j++) + { + if (palentries[j].used_count) + { + (*num_entries)++; + } + } + + g_free (palentries); +} + +static void +remap_indexed_layer (GimpLayer *layer, + const guchar *remap_table, + gint num_entries) +{ + GeglBufferIterator *iter; + const Babl *format; + gint bpp; + gboolean has_alpha; + + format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + + bpp = babl_format_get_bytes_per_pixel (format); + has_alpha = babl_format_has_alpha (format); + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (iter)) + { + guchar *data = iter->items[0].data; + gint length = iter->length; + + if (has_alpha) + { + while (length--) + { + if (data[ALPHA_I]) + data[INDEXED] = remap_table[data[INDEXED]]; + else + data[INDEXED] = 0; + + data += bpp; + } + } + else + { + while (length--) + { + data[INDEXED] = remap_table[data[INDEXED]]; + + data += bpp; + } + } + } +} + +static gint +color_quicksort (const void *c1, + const void *c2) +{ + Color *color1 = (Color *) c1; + Color *color2 = (Color *) c2; + + gdouble v1 = GIMP_RGB_LUMINANCE (color1->red, color1->green, color1->blue); + gdouble v2 = GIMP_RGB_LUMINANCE (color2->red, color2->green, color2->blue); + + if (v1 < v2) + return -1; + else if (v1 > v2) + return 1; + else + return 0; +} + +gboolean +gimp_image_convert_indexed (GimpImage *image, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + GimpProgress *progress, + GError **error) +{ + QuantizeObj *quantobj = NULL; + GimpObjectQueue *queue = NULL; + GimpProgress *sub_progress = NULL; + GimpImageBaseType old_type; + GList *all_layers; + GList *list; + GimpColorProfile *dest_profile = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (gimp_image_get_base_type (image) != GIMP_INDEXED, FALSE); + g_return_val_if_fail (gimp_babl_is_valid (GIMP_INDEXED, + gimp_image_get_precision (image)), + FALSE); + g_return_val_if_fail (custom_palette == NULL || + GIMP_IS_PALETTE (custom_palette), FALSE); + g_return_val_if_fail (custom_palette == NULL || + gimp_palette_get_n_colors (custom_palette) <= 256, + FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (palette_type == GIMP_CONVERT_PALETTE_CUSTOM) + { + if (! custom_palette) + palette_type = GIMP_CONVERT_PALETTE_MONO; + + if (gimp_palette_get_n_colors (custom_palette) == 0) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot convert image: palette is empty.")); + return FALSE; + } + } + + gimp_set_busy (image->gimp); + + all_layers = gimp_image_get_layer_list (image); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, + C_("undo-type", "Convert Image to Indexed")); + + /* Push the image type to the stack */ + gimp_image_undo_push_image_type (image, NULL); + + /* Set the new base type */ + old_type = gimp_image_get_base_type (image); + + g_object_set (image, "base-type", GIMP_INDEXED, NULL); + + /* when converting from GRAY, convert to the new type's builtin + * profile. + */ + if (old_type == GIMP_GRAY) + { + if (gimp_image_get_color_profile (image)) + dest_profile = gimp_image_get_builtin_color_profile (image); + } + + /* Build histogram if necessary. */ + rgb_to_lab_fish = babl_fish (babl_format ("R'G'B' float"), + babl_format ("CIE Lab float")); + lab_to_rgb_fish = babl_fish (babl_format ("CIE Lab float"), + babl_format ("R'G'B' float")); + + /* don't dither if the input is grayscale and we are simply mapping + * every color + */ + if (old_type == GIMP_GRAY && + max_colors == 256 && + palette_type == GIMP_CONVERT_PALETTE_GENERATE) + { + dither_type = GIMP_CONVERT_DITHER_NONE; + } + + if (progress) + { + queue = gimp_object_queue_new (progress); + sub_progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_list (queue, all_layers); + } + + quantobj = initialize_median_cut (old_type, max_colors, dither_type, + palette_type, custom_palette, + dither_alpha, + sub_progress); + + if (palette_type == GIMP_CONVERT_PALETTE_GENERATE) + { + if (old_type == GIMP_GRAY) + zero_histogram_gray (quantobj->histogram); + else + zero_histogram_rgb (quantobj->histogram); + + /* To begin, assume that there are fewer colors in the image + * than the user actually asked for. In that case, we don't + * need to quantize or color-dither. + */ + needs_quantize = FALSE; + had_black = FALSE; + had_white = FALSE; + num_found_cols = 0; + + /* Build the histogram */ + for (list = all_layers; + list; + list = g_list_next (list)) + { + GimpLayer *layer = list->data; + + if (queue) + gimp_object_queue_pop (queue); + + if (old_type == GIMP_GRAY) + { + generate_histogram_gray (quantobj->histogram, + layer, dither_alpha); + } + else + { + /* Note: generate_histogram_rgb may set needs_quantize + * if the image contains more colors than the limit + * specified by the user. + */ + generate_histogram_rgb (quantobj->histogram, + layer, max_colors, dither_alpha, + sub_progress); + } + } + } + + if (progress) + gimp_progress_set_text_literal (progress, + _("Converting to indexed colors (stage 2)")); + + if (old_type == GIMP_RGB && + ! needs_quantize && + palette_type == GIMP_CONVERT_PALETTE_GENERATE) + { + gint i; + + /* If this is an RGB image, and the user wanted a custom-built + * generated palette, and this image has no more colors than + * the user asked for, we don't need the first pass + * (quantization). + * + * There's also no point in dithering, since there's no error + * to spread. So we destroy the old quantobj and make a new + * one with the remapping function set to a special LUT-based + * no-dither remapper. + */ + + quantobj->delete_func (quantobj); + quantobj = initialize_median_cut (old_type, max_colors, + GIMP_CONVERT_DITHER_NODESTRUCT, + palette_type, + custom_palette, + dither_alpha, + sub_progress); + /* We can skip the first pass (palette creation) */ + + quantobj->actual_number_of_colors = num_found_cols; + for (i = 0; i < num_found_cols; i++) + { + quantobj->cmap[i].red = found_cols[i][0]; + quantobj->cmap[i].green = found_cols[i][1]; + quantobj->cmap[i].blue = found_cols[i][2]; + } + } + else + { + quantobj->first_pass (quantobj); + } + + if (palette_type == GIMP_CONVERT_PALETTE_GENERATE) + qsort (quantobj->cmap, + quantobj->actual_number_of_colors, sizeof (Color), + color_quicksort); + + if (progress) + { + gimp_progress_set_text_literal (progress, + _("Converting to indexed colors (stage 3)")); + + gimp_object_queue_clear (queue); + gimp_object_queue_push_list (queue, all_layers); + } + + /* Initialise data which must persist across indexed layer iterations */ + if (quantobj->second_pass_init) + quantobj->second_pass_init (quantobj); + + /* Set the generated palette on the image, we need it to + * convert the layers. We optionally remove duplicate entries + * after the layer conversion. + */ + { + guchar colormap[GIMP_IMAGE_COLORMAP_SIZE]; + gint i, j; + + for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++) + { + colormap[j++] = quantobj->cmap[i].red; + colormap[j++] = quantobj->cmap[i].green; + colormap[j++] = quantobj->cmap[i].blue; + } + + gimp_image_set_colormap (image, colormap, + quantobj->actual_number_of_colors, TRUE); + } + + /* Convert all layers */ + for (list = all_layers; + list; + list = g_list_next (list)) + { + GimpLayer *layer = list->data; + gboolean quantize; + + if (queue) + gimp_object_queue_pop (queue); + + if (gimp_item_is_text_layer (GIMP_ITEM (layer))) + quantize = dither_text_layers; + else + quantize = TRUE; + + if (quantize) + { + GeglBuffer *new_buffer; + gboolean has_alpha; + + has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)); + + new_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (layer)), + gimp_item_get_height (GIMP_ITEM (layer))), + gimp_image_get_layer_format (image, + has_alpha)); + + quantobj->second_pass (quantobj, layer, new_buffer); + + gimp_drawable_set_buffer (GIMP_DRAWABLE (layer), TRUE, NULL, + new_buffer); + g_object_unref (new_buffer); + } + else + { + gimp_drawable_convert_type (GIMP_DRAWABLE (layer), image, + GIMP_INDEXED, + gimp_drawable_get_precision (GIMP_DRAWABLE (layer)), + gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)), + dest_profile, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + TRUE, sub_progress); + } + } + + /* Set the final palette on the image */ + if (remove_duplicates && + (palette_type != GIMP_CONVERT_PALETTE_GENERATE) && + (palette_type != GIMP_CONVERT_PALETTE_MONO)) + { + guchar colormap[GIMP_IMAGE_COLORMAP_SIZE]; + gint i, j; + guchar old_palette[256 * 3]; + guchar new_palette[256 * 3]; + guchar remap_table[256]; + gint num_entries; + + for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++) + { + old_palette[j++] = quantobj->cmap[i].red; + old_palette[j++] = quantobj->cmap[i].green; + old_palette[j++] = quantobj->cmap[i].blue; + } + + num_entries = quantobj->actual_number_of_colors; + + /* Generate a remapping table */ + make_remap_table (old_palette, new_palette, + quantobj->index_used_count, + remap_table, &num_entries); + + /* Convert all layers */ + for (list = all_layers; list; list = g_list_next (list)) + { + remap_indexed_layer (list->data, remap_table, num_entries); + } + + for (i = 0, j = 0; i < num_entries; i++) + { + colormap[j] = new_palette[j]; j++; + colormap[j] = new_palette[j]; j++; + colormap[j] = new_palette[j]; j++; + } + + gimp_image_set_colormap (image, colormap, num_entries, TRUE); + } + + /* When converting from GRAY, set the new profile. + */ + if (old_type == GIMP_GRAY) + { + if (gimp_image_get_color_profile (image)) + gimp_image_set_color_profile (image, dest_profile, NULL); + else + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); + } + + /* Delete the quantizer object, if there is one */ + if (quantobj) + quantobj->delete_func (quantobj); + + gimp_image_undo_group_end (image); + + gimp_image_mode_changed (image); + g_object_thaw_notify (G_OBJECT (image)); + + g_clear_object (&queue); + + g_list_free (all_layers); + + gimp_unset_busy (image->gimp); + + return TRUE; +} + +/* + * Indexed color conversion machinery + */ + +static void +zero_histogram_gray (CFHistogram histogram) +{ + gint i; + + for (i = 0; i < 256; i++) + histogram[i] = 0; +} + + +static void +zero_histogram_rgb (CFHistogram histogram) +{ + memset (histogram, 0, + HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS * sizeof (ColorFreq)); +} + + +static void +generate_histogram_gray (CFHistogram histogram, + GimpLayer *layer, + gboolean dither_alpha) +{ + GeglBufferIterator *iter; + const Babl *format; + gint bpp; + gboolean has_alpha; + + format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + + g_return_if_fail (format == babl_format_with_space ("Y' u8", format) || + format == babl_format_with_space ("Y'A u8", format)); + + bpp = babl_format_get_bytes_per_pixel (format); + has_alpha = babl_format_has_alpha (format); + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *data = iter->items[0].data; + gint length = iter->length; + + if (has_alpha) + { + while (length--) + { + if (data[ALPHA_G] > 127) + histogram[*data]++; + + data += bpp; + } + } + else + { + while (length--) + { + histogram[*data]++; + + data += bpp; + } + } + } +} + +static void +check_white_or_black (const guchar *data) +{ + if (data[RED] == 255 && + data[GREEN] == 255 && + data[BLUE] == 255) + had_white = TRUE; + if (data[RED] ==0 && + data[GREEN]==0 && + data[BLUE] ==0) + had_black = TRUE; +} + +static void +generate_histogram_rgb (CFHistogram histogram, + GimpLayer *layer, + gint col_limit, + gboolean dither_alpha, + GimpProgress *progress) +{ + GeglBufferIterator *iter; + const Babl *format; + GeglRectangle *roi; + ColorFreq *colfreq; + gint nfc_iter; + gint row, col, coledge; + gint offsetx, offsety; + gint64 layer_size; + gint64 total_size = 0; + gint count = 0; + gint bpp; + gboolean has_alpha; + + format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + + g_return_if_fail (format == babl_format ("R'G'B' u8") || + format == babl_format ("R'G'B'A u8")); + + bpp = babl_format_get_bytes_per_pixel (format); + has_alpha = babl_format_has_alpha (format); + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) * + gimp_item_get_height (GIMP_ITEM (layer))); + + /* g_printerr ("col_limit = %d, nfc = %d\n", col_limit, num_found_cols); */ + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + roi = &iter->items[0].roi; + + if (progress) + gimp_progress_set_value (progress, 0.0); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *data = iter->items[0].data; + gint length = iter->length; + + total_size += length; + + /* g_printerr (" [%d,%d - %d,%d]", srcPR.x, src_roi->y, offsetx, offsety); */ + + if (needs_quantize) + { + if (dither_alpha) + { + /* if alpha-dithering, + we need to be deterministic w.r.t. offsets */ + + col = roi->x + offsetx; + coledge = col + roi->width; + row = roi->y + offsety; + + while (length--) + { + gboolean transparent = FALSE; + + if (has_alpha && + data[ALPHA] < + DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK]) + transparent = TRUE; + + if (! transparent) + { + colfreq = HIST_RGB (histogram, + data[RED], + data[GREEN], + data[BLUE]); + check_white_or_black (data); + (*colfreq)++; + } + + col++; + if (col == coledge) + { + col = roi->x + offsetx; + row++; + } + + data += bpp; + } + } + else + { + while (length--) + { + if ((has_alpha && ((data[ALPHA] > 127))) + || (!has_alpha)) + { + colfreq = HIST_RGB (histogram, + data[RED], + data[GREEN], + data[BLUE]); + check_white_or_black (data); + (*colfreq)++; + } + + data += bpp; + } + } + } + else + { + /* if alpha-dithering, we need to be deterministic w.r.t. offsets */ + col = roi->x + offsetx; + coledge = col + roi->width; + row = roi->y + offsety; + + while (length--) + { + gboolean transparent = FALSE; + + if (has_alpha) + { + if (dither_alpha) + { + if (data[ALPHA] < + DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK]) + transparent = TRUE; + } + else + { + if (data[ALPHA] <= 127) + transparent = TRUE; + } + } + + if (! transparent) + { + colfreq = HIST_RGB (histogram, + data[RED], + data[GREEN], + data[BLUE]); + (*colfreq)++; + + if (!needs_quantize) + { + for (nfc_iter = 0; + nfc_iter < num_found_cols; + nfc_iter++) + { + if ((data[RED] == found_cols[nfc_iter][0]) && + (data[GREEN] == found_cols[nfc_iter][1]) && + (data[BLUE] == found_cols[nfc_iter][2])) + goto already_found; + } + + /* Color was not in the table of + * existing colors + */ + + num_found_cols++; + + if (num_found_cols > col_limit) + { + /* There are more colors in the image than + * were allowed. We switch to plain + * histogram calculation with a view to + * quantizing at a later stage. + */ + needs_quantize = TRUE; + /* g_print ("\nmax colors exceeded - needs quantize.\n");*/ + goto already_found; + } + else + { + /* Remember the new color we just found. + */ + found_cols[num_found_cols-1][0] = data[RED]; + found_cols[num_found_cols-1][1] = data[GREEN]; + found_cols[num_found_cols-1][2] = data[BLUE]; + + check_white_or_black (data); + } + } + } + already_found: + + col++; + if (col == coledge) + { + col = roi->x + offsetx; + row++; + } + + data += bpp; + } + } + + if (progress && (count % 16 == 0)) + gimp_progress_set_value (progress, + (gdouble) total_size / (gdouble) layer_size); + } + +/* g_print ("O: col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/ +} + + + +static boxptr +find_split_candidate (const boxptr boxlist, + const gint numboxes, + AxisType *which_axis, + const gint desired_colors) +{ + boxptr boxp; + gint i; + etype maxc = 0; + boxptr which = NULL; + gdouble Lbias; + + *which_axis = AXIS_UNDEF; + + /* we only perform the initial L-split bias /at all/ if the final + number of desired colors is quite low, otherwise it all comes + out in the wash anyway and this initial bias generally only hurts + us in the long run. */ + if (desired_colors <= 16) + { +#define BIAS_FACTOR 2.66F +#define BIAS_NUMBER 2 /* 0 */ + + /* we bias towards splitting across L* for first few colors */ + Lbias = (numboxes > BIAS_NUMBER) ? 1.0F : ((gdouble) (BIAS_NUMBER + 1) - + ((gdouble) numboxes)) / + ((gdouble) BIAS_NUMBER / BIAS_FACTOR); + /*Lbias = 1.0; + fprintf(stderr, " [[%d]] ", numboxes); + fprintf(stderr, "Using ramped L-split bias.\n"); + fprintf(stderr, "R\n"); + */ + } + else + Lbias = 1.0F; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) + { + if (boxp->volume > 0) + { +#ifndef _MSC_VER + etype rpe = (double)((boxp->rerror) * R_SCALE * R_SCALE); + etype gpe = (double)((boxp->gerror) * G_SCALE * G_SCALE); + etype bpe = (double)((boxp->berror) * B_SCALE * B_SCALE); +#else + /* + * Sorry about the mess, otherwise would get : + * error C2520: conversion from unsigned __int64 to double + * not implemented, use signed __int64 + */ + etype rpe = (double)(((__int64)boxp->rerror) * R_SCALE * R_SCALE); + etype gpe = (double)(((__int64)boxp->gerror) * G_SCALE * G_SCALE); + etype bpe = (double)(((__int64)boxp->berror) * B_SCALE * B_SCALE); +#endif + + if (Lbias * rpe > maxc && + boxp->Rmin < boxp->Rmax) + { + which = boxp; + maxc = Lbias * rpe; + *which_axis = AXIS_RED; + } + + if (gpe > maxc && + boxp->Gmin < boxp->Gmax) + { + which = boxp; + maxc = gpe; + *which_axis = AXIS_GREEN; + } + + if (bpe > maxc && + boxp->Bmin < boxp->Bmax) + { + which = boxp; + maxc = bpe; + *which_axis = AXIS_BLUE; + } + } + } + + /* fprintf(stderr, " %f,%p ", maxc, which); */ + /* fprintf(stderr, " %llu ", maxc); */ + + return which; +} + + +/* Find the splittable box with the largest (scaled) volume Returns + * NULL if no splittable boxes remain + */ +static boxptr +find_biggest_volume (const boxptr boxlist, + const gint numboxes) +{ + boxptr boxp; + gint i; + gint maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) + { + if (boxp->volume > maxv) + { + which = boxp; + maxv = boxp->volume; + } + } + + return which; +} + + +/* Shrink the min/max bounds of a box to enclose only nonzero + * elements, and recompute its volume and population + */ +static void +update_box_gray (const CFHistogram histogram, + boxptr boxp) +{ + gint i, min, max, dist; + ColorFreq ccount; + + min = boxp->Rmin; + max = boxp->Rmax; + + if (max > min) + for (i = min; i <= max; i++) + { + if (histogram[i] != 0) + { + boxp->Rmin = min = i; + break; + } + } + + if (max > min) + for (i = max; i >= min; i--) + { + if (histogram[i] != 0) + { + boxp->Rmax = max = i; + break; + } + } + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist = max - min; + boxp->volume = dist * dist; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (i = min; i <= max; i++) + if (histogram[i] != 0) + ccount++; + + boxp->colorcount = ccount; +} + + +/* Shrink the min/max bounds of a box to enclose only nonzero + * elements, and recompute its volume, population and error + */ +static void +update_box_rgb (const CFHistogram histogram, + boxptr boxp, + const gint cells_remaining) +{ + gint R, G, B; + gint Rmin, Rmax, Gmin, Gmax, Bmin, Bmax; + gint dist0, dist1, dist2; + ColorFreq ccount; + /* + guint64 tempRerror; + guint64 tempGerror; + guint64 tempBerror; + */ + QuantizeObj dummyqo; + box dummybox; + + /* fprintf(stderr, "U"); */ + + Rmin = boxp->Rmin; Rmax = boxp->Rmax; + Gmin = boxp->Gmin; Gmax = boxp->Gmax; + Bmin = boxp->Bmin; Bmax = boxp->Bmax; + + if (Rmax > Rmin) + for (R = Rmin; R <= Rmax; R++) + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Rmin = Rmin = R; + goto have_Rmin; + } + } + } + have_Rmin: + if (Rmax > Rmin) + for (R = Rmax; R >= Rmin; R--) + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Rmax = Rmax = R; + goto have_Rmax; + } + } + } + have_Rmax: + if (Gmax > Gmin) + for (G = Gmin; G <= Gmax; G++) + for (R = Rmin; R <= Rmax; R++) + { + for (B = Bmin; B <= Bmax; B++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Gmin = Gmin = G; + goto have_Gmin; + } + } + } + have_Gmin: + if (Gmax > Gmin) + for (G = Gmax; G >= Gmin; G--) + for (R = Rmin; R <= Rmax; R++) + { + for (B = Bmin; B <= Bmax; B++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Gmax = Gmax = G; + goto have_Gmax; + } + } + } + have_Gmax: + if (Bmax > Bmin) + for (B = Bmin; B <= Bmax; B++) + for (R = Rmin; R <= Rmax; R++) + { + for (G = Gmin; G <= Gmax; G++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Bmin = Bmin = B; + goto have_Bmin; + } + } + } + have_Bmin: + if (Bmax > Bmin) + for (B = Bmax; B >= Bmin; B--) + for (R = Rmin; R <= Rmax; R++) + { + for (G = Gmin; G <= Gmax; G++) + { + if (*HIST_LIN (histogram, R, G, B) != 0) + { + boxp->Bmax = Bmax = B; + goto have_Bmax; + } + } + } + have_Bmax: + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. (ADM: note: this isn't true.) + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((1 + Rmax - Rmin) << R_SHIFT) * R_SCALE; + dist1 = ((1 + Gmax - Gmin) << G_SHIFT) * G_SCALE; + dist2 = ((1 + Bmax - Bmin) << B_SHIFT) * B_SCALE; + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + /* boxp->volume = dist0 * dist1 * dist2; */ + + compute_color_lin8(&dummyqo, histogram, boxp, 0); + + /*printf("(%d %d %d)\n", dummyqo.cmap[0].red,dummyqo.cmap[0].green,dummyqo.cmap[0].blue); + fflush(stdout);*/ + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + boxp->error = 0; + boxp->rerror = 0; + boxp->gerror = 0; + boxp->berror = 0; + for (R = Rmin; R <= Rmax; R++) + { + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + ColorFreq freq_here; + + freq_here = *HIST_LIN (histogram, R, G, B); + + if (freq_here != 0) + { + int ge, be, re; + + dummybox.Rmin = dummybox.Rmax = R; + dummybox.Gmin = dummybox.Gmax = G; + dummybox.Bmin = dummybox.Bmax = B; + compute_color_lin8(&dummyqo, histogram, &dummybox, 1); + + re = dummyqo.cmap[0].red - dummyqo.cmap[1].red; + ge = dummyqo.cmap[0].green - dummyqo.cmap[1].green; + be = dummyqo.cmap[0].blue - dummyqo.cmap[1].blue; + + boxp->rerror += freq_here * (re) * (re); + boxp->gerror += freq_here * (ge) * (ge); + boxp->berror += freq_here * (be) * (be); + + ccount += freq_here; + } + } + } + } + +#if 0 + fg d;flg fd;kg fld;gflkfld + /* Scan again, taking note of halfway error point for red axis */ + tempRerror = 0; + boxp->Rhalferror = Rmin; +#warning r<=? + for (R = Rmin; R <= Rmax; R++) + { + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + ColorFreq freq_here; + freq_here = *HIST_LIN(histogram, R, G, B); + if (freq_here != 0) + { + int re; + int idist; + double dist; + + dummybox.Rmin = dummybox.Rmax = R; + dummybox.Gmin = dummybox.Gmax = G; + dummybox.Bmin = dummybox.Bmax = B; + compute_color_lin8(&dummyqo, histogram, &dummybox, 1); + + re = dummyqo.cmap[0].red - dummyqo.cmap[1].red; + + tempRerror += freq_here * (re) * (re); + + if (tempRerror*2 >= boxp->rerror) + goto green_axisscan; + else + boxp->Rhalferror = R; + } + } + } + } + fprintf(stderr, " D:"); + green_axisscan: + + fprintf(stderr, "<%d: %llu/%llu> ", R, tempRerror, boxp->rerror); + /* Scan again, taking note of halfway error point for green axis */ + tempGerror = 0; + boxp->Ghalferror = Gmin; +#warning G<=? + for (G = Gmin; G <= Gmax; G++) + { + for (R = Rmin; R <= Rmax; R++) + { + for (B = Bmin; B <= Bmax; B++) + { + ColorFreq freq_here; + freq_here = *HIST_LIN(histogram, R, G, B); + if (freq_here != 0) + { + int ge; + dummybox.Rmin = dummybox.Rmax = R; + dummybox.Gmin = dummybox.Gmax = G; + dummybox.Bmin = dummybox.Bmax = B; + compute_color_lin8(&dummyqo, histogram, &dummybox, 1); + + ge = dummyqo.cmap[0].green - dummyqo.cmap[1].green; + + tempGerror += freq_here * (ge) * (ge); + + if (tempGerror*2 >= boxp->gerror) + goto blue_axisscan; + else + boxp->Ghalferror = G; + } + } + } + } + + blue_axisscan: + /* Scan again, taking note of halfway error point for blue axis */ + tempBerror = 0; + boxp->Bhalferror = Bmin; +#warning B<=? + for (B = Bmin; B <= Bmax; B++) + { + for (R = Rmin; R <= Rmax; R++) + { + for (G = Gmin; G <= Gmax; G++) + { + ColorFreq freq_here; + freq_here = *HIST_LIN(histogram, R, G, B); + if (freq_here != 0) + { + int be; + dummybox.Rmin = dummybox.Rmax = R; + dummybox.Gmin = dummybox.Gmax = G; + dummybox.Bmin = dummybox.Bmax = B; + compute_color_lin8(&dummyqo, histogram, &dummybox, 1); + + be = dummyqo.cmap[0].blue - dummyqo.cmap[1].blue; + + tempBerror += freq_here * (be) * (be); + + if (tempBerror*2 >= boxp->berror) + goto finished_axesscan; + else + boxp->Bhalferror = B; + } + } + } + } + finished_axesscan: +#else + + boxp->Rhalferror = Rmin + (Rmax - Rmin + 1) / 2; + boxp->Ghalferror = Gmin + (Gmax - Gmin + 1) / 2; + boxp->Bhalferror = Bmin + (Bmax - Bmin + 1) / 2; + + if (dist0 && dist1 && dist2) + { + AxisType longest_ax = AXIS_UNDEF; + gint longest_length = 0; + gint longest_length2 = 0; + gint ratio; + + /* + fprintf(stderr, "[%d,%d,%d=%d,%d,%d] ", + (Rmax - Rmin), (Gmax - Gmin), (Bmax - Bmin), + dist0, dist1, dist2); + */ + + if (dist0 >= longest_length) + { + longest_length2 = longest_length; + longest_length = dist0; + longest_ax = AXIS_RED; + } + else if (dist0 >= longest_length2) + { + longest_length2 = dist0; + } + + if (dist1 >= longest_length) + { + longest_length2 = longest_length; + longest_length = dist1; + longest_ax = AXIS_GREEN; + } + else if (dist1 >= longest_length2) + { + longest_length2 = dist1; + } + + if (dist2 >= longest_length) + { + longest_length2 = longest_length; + longest_length = dist2; + longest_ax = AXIS_BLUE; + } + else if (dist2 >= longest_length2) + { + longest_length2 = dist2; + } + + if (longest_length2 == 0) + longest_length2 = 1; + + ratio = (longest_length + longest_length2/2) / longest_length2; + /* fprintf(stderr, " ratio:(%d/%d)=%d ", longest_length, longest_length2, ratio); + fprintf(stderr, "C%d ", cells_remaining); */ + + if (ratio > cells_remaining + 1) + ratio = cells_remaining + 1; + + if (ratio > 2) + { + switch (longest_ax) + { + case AXIS_RED: + if (Rmin + (Rmax - Rmin + ratio / 2) / ratio < Rmax) + { + /* fprintf(stderr, "FR%d \007\n",ratio);*/ + boxp->Rhalferror = Rmin + (Rmax - Rmin + ratio / 2) / ratio; + } + break; + case AXIS_GREEN: + if (Gmin + (Gmax - Gmin + ratio / 2) / ratio < Gmax) + { + /* fprintf(stderr, "FG%d \007\n",ratio);*/ + boxp->Ghalferror = Gmin + (Gmax - Gmin + ratio / 2) / ratio; + } + break; + case AXIS_BLUE: + if (Bmin + (Bmax - Bmin + ratio / 2) / ratio < Bmax) + { + /* fprintf(stderr, "FB%d \007\n",ratio);*/ + boxp->Bhalferror = Bmin + (Bmax - Bmin + ratio / 2) / ratio; + } + break; + default: + g_warning ("GRR, UNDEF LONGEST AXIS\007\n"); + } + } + } + + if (boxp->Rhalferror == Rmax) + boxp->Rhalferror = Rmin; + if (boxp->Ghalferror == Gmax) + boxp->Ghalferror = Gmin; + if (boxp->Bhalferror == Bmax) + boxp->Bhalferror = Bmin; + + /* + boxp->Rhalferror = RSDF(dummyqo.cmap[0].red); + boxp->Ghalferror = GSDF(dummyqo.cmap[0].green); + boxp->Bhalferror = BSDF(dummyqo.cmap[0].blue); + */ + + /* + boxp->Rhalferror = (RSDF(dummyqo.cmap[0].red) + (Rmin+Rmax)/2)/2; + boxp->Ghalferror = (GSDF(dummyqo.cmap[0].green) + (Gmin+Gmax)/2)/2; + boxp->Bhalferror = (BSDF(dummyqo.cmap[0].blue) + (Bmin+Bmax)/2)/2; + */ + + +#endif + /* + fprintf(stderr, " %d,%d", dummyqo.cmap[0].blue, boxp->Bmax); + + gimp_assert (boxp->Rhalferror >= boxp->Rmin); + gimp_assert (boxp->Rhalferror < boxp->Rmax); + gimp_assert (boxp->Ghalferror >= boxp->Gmin); + gimp_assert (boxp->Ghalferror < boxp->Gmax); + gimp_assert (boxp->Bhalferror >= boxp->Bmin); + gimp_assert (boxp->Bhalferror < boxp->Bmax);*/ + + /*boxp->error = (sqrt((double)(boxp->error/ccount)));*/ + /* boxp->rerror = (sqrt((double)((boxp->rerror)/ccount))); + boxp->gerror = (sqrt((double)((boxp->gerror)/ccount))); + boxp->berror = (sqrt((double)((boxp->berror)/ccount)));*/ + /*printf(":%lld / %ld: ", boxp->error, ccount); + printf("(%d-%d-%d)(%d-%d-%d)(%d-%d-%d)\n", + Rmin, boxp->Rhalferror, Rmax, + Gmin, boxp->Ghalferror, Gmax, + Bmin, boxp->Bhalferror, Bmax + ); + fflush(stdout);*/ + + boxp->colorcount = ccount; +} + + +/* Repeatedly select and split the largest box until we have enough + * boxes + */ +static gint +median_cut_gray (CFHistogram histogram, + boxptr boxlist, + gint numboxes, + gint desired_colors) +{ + gint lb; + boxptr b1, b2; + + while (numboxes < desired_colors) + { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + + b1 = find_biggest_volume (boxlist, numboxes); + + if (b1 == NULL) /* no splittable boxes left! */ + break; + + b2 = boxlist + numboxes; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->Rmax = b1->Rmax; + b2->Rmin = b1->Rmin; + + /* Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + lb = (b1->Rmax + b1->Rmin) / 2; + b1->Rmax = lb; + b2->Rmin = lb + 1; + + /* Update stats for boxes */ + update_box_gray (histogram, b1); + update_box_gray (histogram, b2); + numboxes++; + } + + return numboxes; +} + +/* Repeatedly select and split the largest box until we have enough + * boxes + */ +static gint +median_cut_rgb (CFHistogram histogram, + boxptr boxlist, + gint numboxes, + gint desired_colors, + GimpProgress *progress) +{ + gint lb; + boxptr b1, b2; + AxisType which_axis; + + while (numboxes < desired_colors) + { + b1 = find_split_candidate (boxlist, numboxes, &which_axis, desired_colors); + + if (b1 == NULL) /* no splittable boxes left! */ + break; + + b2 = boxlist + numboxes; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->Rmax = b1->Rmax; b2->Gmax = b1->Gmax; b2->Bmax = b1->Bmax; + b2->Rmin = b1->Rmin; b2->Gmin = b1->Gmin; b2->Bmin = b1->Bmin; + + + /* Choose split point along selected axis, and update box bounds. + * Note that lb value is max for lower box, so must be < old max. + */ + switch (which_axis) + { + case AXIS_RED: + lb = b1->Rhalferror;/* *0 + (b1->Rmax + b1->Rmin) / 2; */ + b1->Rmax = lb; + b2->Rmin = lb+1; + g_return_val_if_fail (b1->Rmax >= b1->Rmin, numboxes); + g_return_val_if_fail (b2->Rmax >= b2->Rmin, numboxes); + break; + case AXIS_GREEN: + lb = b1->Ghalferror;/* *0 + (b1->Gmax + b1->Gmin) / 2; */ + b1->Gmax = lb; + b2->Gmin = lb+1; + g_return_val_if_fail (b1->Gmax >= b1->Gmin, numboxes); + g_return_val_if_fail (b2->Gmax >= b2->Gmin, numboxes); + break; + case AXIS_BLUE: + lb = b1->Bhalferror;/* *0 + (b1->Bmax + b1->Bmin) / 2; */ + b1->Bmax = lb; + b2->Bmin = lb+1; + g_return_val_if_fail (b1->Bmax >= b1->Bmin, numboxes); + g_return_val_if_fail (b2->Bmax >= b2->Bmin, numboxes); + break; + default: + g_error ("Uh-oh."); + } + /* Update stats for boxes */ + numboxes++; + + if (progress && (numboxes % 16 == 0)) + gimp_progress_set_value (progress, (gdouble) numboxes / desired_colors); + + update_box_rgb (histogram, b1, desired_colors - numboxes); + update_box_rgb (histogram, b2, desired_colors - numboxes); + } + + return numboxes; +} + + +/* Compute representative color for a box, put it in colormap[icolor] + */ +static void +compute_color_gray (QuantizeObj *quantobj, + CFHistogram histogram, + boxptr boxp, + int icolor) +{ + gint i, min, max; + guint64 count; + guint64 total; + guint64 gtotal; + + min = boxp->Rmin; + max = boxp->Rmax; + + total = 0; + gtotal = 0; + + for (i = min; i <= max; i++) + { + count = histogram[i]; + if (count != 0) + { + total += count; + gtotal += i * count; + } + } + + if (total != 0) + { + quantobj->cmap[icolor].red = + quantobj->cmap[icolor].green = + quantobj->cmap[icolor].blue = (gtotal + (total >> 1)) / total; + } + else + { + /* The only situation where total==0 is if the image was null or + * all-transparent. In that case we just put a dummy value in + * the colormap. + */ + quantobj->cmap[icolor].red = + quantobj->cmap[icolor].green = + quantobj->cmap[icolor].blue = 0; + } +} + + +/* Compute representative color for a box, put it in colormap[icolor] + */ +static void +compute_color_rgb (QuantizeObj *quantobj, + CFHistogram histogram, + boxptr boxp, + int icolor) +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + gint R, G, B; + gint Rmin, Rmax; + gint Gmin, Gmax; + gint Bmin, Bmax; + ColorFreq total = 0; + ColorFreq Rtotal = 0; + ColorFreq Gtotal = 0; + ColorFreq Btotal = 0; + + Rmin = boxp->Rmin; Rmax = boxp->Rmax; + Gmin = boxp->Gmin; Gmax = boxp->Gmax; + Bmin = boxp->Bmin; Bmax = boxp->Bmax; + + for (R = Rmin; R <= Rmax; R++) + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + ColorFreq this_freq = *HIST_LIN (histogram, R, G, B); + + if (this_freq != 0) + { + total += this_freq; + Rtotal += R * this_freq; + Gtotal += G * this_freq; + Btotal += B * this_freq; + } + } + } + + if (total > 0) + { + guchar red, green, blue; + + lin_to_rgb (/*(Rtotal + (total>>1)) / total, + (Gtotal + (total>>1)) / total, + (Btotal + (total>>1)) / total,*/ + (double)Rtotal / (double)total, + (double)Gtotal / (double)total, + (double)Btotal / (double)total, + &red, &green, &blue); + + quantobj->cmap[icolor].red = red; + quantobj->cmap[icolor].green = green; + quantobj->cmap[icolor].blue = blue; + } + else + { + /* The only situation where total==0 is if the image was null or + * all-transparent. In that case we just put a dummy value in + * the colormap. + */ + quantobj->cmap[icolor].red = 0; + quantobj->cmap[icolor].green = 0; + quantobj->cmap[icolor].blue = 0; + } +} + + +/* Compute representative color for a box, put it in colormap[icolor] + */ +static void +compute_color_lin8 (QuantizeObj *quantobj, + CFHistogram histogram, + boxptr boxp, + const gint icolor) +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + gint R, G, B; + gint Rmin, Rmax; + gint Gmin, Gmax; + gint Bmin, Bmax; + ColorFreq total = 0; + ColorFreq Rtotal = 0; + ColorFreq Gtotal = 0; + ColorFreq Btotal = 0; + + Rmin = boxp->Rmin; Rmax = boxp->Rmax; + Gmin = boxp->Gmin; Gmax = boxp->Gmax; + Bmin = boxp->Bmin; Bmax = boxp->Bmax; + + for (R = Rmin; R <= Rmax; R++) + for (G = Gmin; G <= Gmax; G++) + { + for (B = Bmin; B <= Bmax; B++) + { + ColorFreq this_freq = *HIST_LIN (histogram, R, G, B); + + if (this_freq != 0) + { + Rtotal += R * this_freq; + Gtotal += G * this_freq; + Btotal += B * this_freq; + total += this_freq; + } + } + } + + if (total != 0) + { + quantobj->cmap[icolor].red = ((Rtotal << R_SHIFT) + (total>>1)) / total; + quantobj->cmap[icolor].green = ((Gtotal << G_SHIFT) + (total>>1)) / total; + quantobj->cmap[icolor].blue = ((Btotal << B_SHIFT) + (total>>1)) / total; + } + else + { + /* The only situation where total==0 is if the image was null or + * all-transparent. In that case we just put a dummy value in + * the colormap. + */ + g_warning ("eep."); + quantobj->cmap[icolor].red = 0; + quantobj->cmap[icolor].green = 128; + quantobj->cmap[icolor].blue = 128; + } +} + + +/* Master routine for color selection + */ +static void +select_colors_gray (QuantizeObj *quantobj, + CFHistogram histogram) +{ + boxptr boxlist; + gint numboxes; + gint desired = quantobj->desired_number_of_colors; + gint i; + + /* Allocate workspace for box list */ + boxlist = g_new (box, desired); + + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].Rmin = 0; + boxlist[0].Rmax = 255; + /* Shrink it to actually-used volume and set its statistics */ + update_box_gray (histogram, boxlist); + /* Perform median-cut to produce final box list */ + numboxes = median_cut_gray (histogram, boxlist, numboxes, desired); + + quantobj->actual_number_of_colors = numboxes; + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color_gray (quantobj, histogram, boxlist + i, i); +} + + +/* Master routine for color selection + */ +static void +select_colors_rgb (QuantizeObj *quantobj, + CFHistogram histogram) +{ + boxptr boxlist; + gint numboxes; + gint desired = quantobj->desired_number_of_colors; + gint i; + + /* Allocate workspace for box list */ + boxlist = g_new (box, desired); + + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].Rmin = 0; + boxlist[0].Rmax = HIST_R_ELEMS - 1; + boxlist[0].Gmin = 0; + boxlist[0].Gmax = HIST_G_ELEMS - 1; + boxlist[0].Bmin = 0; + boxlist[0].Bmax = HIST_B_ELEMS - 1; + /* Shrink it to actually-used volume and set its statistics */ + update_box_rgb (histogram, &boxlist[0], quantobj->desired_number_of_colors); + /* Perform median-cut to produce final box list */ + numboxes = median_cut_rgb (histogram, boxlist, numboxes, desired, + quantobj->progress); + + quantobj->actual_number_of_colors = numboxes; + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + { + compute_color_rgb (quantobj, histogram, &boxlist[i], i); + } + + g_free (boxlist); +} + + +/* + * These routines are concerned with the time-critical task of mapping input + * colors to the nearest color in the selected colormap. + * + * We re-use the histogram space as an "inverse color map", essentially a + * cache for the results of nearest-color searches. All colors within a + * histogram cell will be mapped to the same colormap entry, namely the one + * closest to the cell's center. This may not be quite the closest entry to + * the actual input color, but it's almost as good. A zero in the cache + * indicates we haven't found the nearest color for that cell yet; the array + * is cleared to zeroes before starting the mapping pass. When we find the + * nearest color for a cell, its colormap index plus one is recorded in the + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + * when they need to use an unfilled entry in the cache. + * + * Our method of efficiently finding nearest colors is based on the "locally + * sorted search" idea described by Heckbert and on the incremental distance + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + * the distances from a given colormap entry to each cell of the histogram can + * be computed quickly using an incremental method: the differences between + * distances to adjacent cells themselves differ by a constant. This allows a + * fairly fast implementation of the "brute force" approach of computing the + * distance from every colormap entry to every histogram cell. Unfortunately, + * it needs a work array to hold the best-distance-so-far for each histogram + * cell (because the inner loop has to be over cells, not colormap entries). + * The work array elements have to be ints, so the work array would need + * 256Kb at our recommended precision. This is not feasible in DOS machines. + * + * To get around these problems, we apply Thomas' method to compute the + * nearest colors for only the cells within a small subbox of the histogram. + * The work array need be only as big as the subbox, so the memory usage + * problem is solved. Furthermore, we need not fill subboxes that are never + * referenced in pass2; many images use only part of the color gamut, so a + * fair amount of work is saved. An additional advantage of this + * approach is that we can apply Heckbert's locality criterion to quickly + * eliminate colormap entries that are far away from the subbox; typically + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + * and we need not compute their distances to individual cells in the subbox. + * The speed of this approach is heavily influenced by the subbox size: too + * small means too much overhead, too big loses because Heckbert's criterion + * can't eliminate as many colormap entries. Empirically the best subbox + * size seems to be about 1/512th of the histogram (1/8th in each direction). + * + * Thomas' article also describes a refined method which is asymptotically + * faster than the brute-force method, but it is also far more complex and + * cannot efficiently be applied to small subboxes. It is therefore not + * useful for programs intended to be portable to DOS machines. On machines + * with plenty of memory, filling the whole histogram in one shot with Thomas' + * refined method might be faster than the present code --- but then again, + * it might not be any faster, and it's certainly more complicated. + */ + + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ +/*#define BOX_R_LOG (PRECISION_R-3) + #define BOX_G_LOG (PRECISION_G-3) + #define BOX_B_LOG (PRECISION_B-3)*/ + +/*adam*/ +#define BOX_R_LOG 0 +#define BOX_G_LOG 0 +#define BOX_B_LOG 0 + +#define BOX_R_ELEMS (1<actual_number_of_colors; + int maxR, maxG, maxB; + int centerR, centerG, centerB; + int i, x, ncolors; + int minmaxdist, min_dist, max_dist, tdist; + int mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxR = minR + ((1 << BOX_R_SHIFT) - (1 << R_SHIFT)); + centerR = (minR + maxR + 1) >> 1; + maxG = minG + ((1 << BOX_G_SHIFT) - (1 << G_SHIFT)); + centerG = (minG + maxG + 1) >> 1; + maxB = minB + ((1 << BOX_B_SHIFT) - (1 << B_SHIFT)); + centerB = (minB + maxB + 1) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) + { + /* We compute the squared-R-distance term, then add in the other two. */ + x = quantobj->clin[i].red; + if (x < minR) + { + tdist = (x - minR) * R_SCALE; + min_dist = tdist*tdist; + tdist = (x - maxR) * R_SCALE; + max_dist = tdist*tdist; + } + else if (x > maxR) + { + tdist = (x - maxR) * R_SCALE; + min_dist = tdist*tdist; + tdist = (x - minR) * R_SCALE; + max_dist = tdist*tdist; + } + else + { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerR) + { + tdist = (x - maxR) * R_SCALE; + max_dist = tdist*tdist; + } + else + { + tdist = (x - minR) * R_SCALE; + max_dist = tdist*tdist; + } + } + + x = quantobj->clin[i].green; + if (x < minG) + { + tdist = (x - minG) * G_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxG) * G_SCALE; + max_dist += tdist*tdist; + } + else if (x > maxG) + { + tdist = (x - maxG) * G_SCALE; + min_dist += tdist*tdist; + tdist = (x - minG) * G_SCALE; + max_dist += tdist*tdist; + } + else + { + /* within cell range so no contribution to min_dist */ + if (x <= centerG) + { + tdist = (x - maxG) * G_SCALE; + max_dist += tdist*tdist; + } + else + { + tdist = (x - minG) * G_SCALE; + max_dist += tdist*tdist; + } + } + + x = quantobj->clin[i].blue; + if (x < minB) + { + tdist = (x - minB) * B_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxB) * B_SCALE; + max_dist += tdist*tdist; + } + else if (x > maxB) + { + tdist = (x - maxB) * B_SCALE; + min_dist += tdist*tdist; + tdist = (x - minB) * B_SCALE; + max_dist += tdist*tdist; + } + else + { + /* within cell range so no contribution to min_dist */ + if (x <= centerB) + { + tdist = (x - maxB) * B_SCALE; + max_dist += tdist*tdist; + } + else + { + tdist = (x - minB) * B_SCALE; + max_dist += tdist*tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; + } + + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) + { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = i; + } + + return ncolors; +} + + +/* Find the closest colormap entry for each cell in the update box, + * given the list of candidate colors prepared by find_nearby_colors. + * Return the indexes of the closest entries in the bestcolor[] array. + * This routine uses Thomas' incremental distance calculation method + * to find the distance from a colormap entry to successive cells in + * the box. + */ +static void +find_best_colors (QuantizeObj *quantobj, + gint minR, + gint minG, + gint minB, + gint numcolors, + gint colorlist[], + gint bestcolor[]) +{ + gint iR, iG, iB; + gint i, icolor; + gint *bptr; /* pointer into bestdist[] array */ + gint *cptr; /* pointer into bestcolor[] array */ + gint dist0, dist1; /* initial distance values */ + gint dist2; /* current distance in inner loop */ + gint xx0, xx1; /* distance increments */ + gint xx2; + gint inR, inG, inB; /* initial values for increments */ + + /* This array holds the distance to the nearest-so-far color for each cell */ + gint bestdist[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, }; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_R_ELEMS*BOX_G_ELEMS*BOX_B_ELEMS-1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_R ((1 << R_SHIFT) * R_SCALE) +#define STEP_G ((1 << G_SHIFT) * G_SCALE) +#define STEP_B ((1 << B_SHIFT) * B_SCALE) + + for (i = 0; i < numcolors; i++) + { + icolor = colorlist[i]; + /* Compute (square of) distance from minR/G/B to this color */ + inR = (minR - quantobj->clin[icolor].red) * R_SCALE; + dist0 = inR*inR; + /* special-case for L*==0: chroma diffs irrelevant */ + /* if (minR > 0 || quantobj->clin[icolor].red > 0) */ + { + inG = (minG - quantobj->clin[icolor].green) * G_SCALE; + dist0 += inG*inG; + inB = (minB - quantobj->clin[icolor].blue) * B_SCALE; + dist0 += inB*inB; + } + /* else + { + inG = 0; + inB = 0; + } */ + /* Form the initial difference increments */ + inR = inR * (2 * STEP_R) + STEP_R * STEP_R; + inG = inG * (2 * STEP_G) + STEP_G * STEP_G; + inB = inB * (2 * STEP_B) + STEP_B * STEP_B; + /* Now loop over all cells in box, updating distance per Thomas method */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inR; + for (iR = BOX_R_ELEMS-1; iR >= 0; iR--) + { + dist1 = dist0; + xx1 = inG; + for (iG = BOX_G_ELEMS-1; iG >= 0; iG--) + { + dist2 = dist1; + xx2 = inB; + for (iB = BOX_B_ELEMS-1; iB >= 0; iB--) + { + if (dist2 < *bptr) + { + *bptr = dist2; + *cptr = icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_B * STEP_B; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_G * STEP_G; + } + dist0 += xx0; + xx0 += 2 * STEP_R * STEP_R; + } + } +} + + +/* Fill the inverse-colormap entries in the update box that contains + * histogram cell R/G/B. (Only that one cell MUST be filled, but we + * can fill as many others as we wish.) + */ +static void +fill_inverse_cmap_gray (QuantizeObj *quantobj, + CFHistogram histogram, + gint pixel) +{ + Color *cmap = quantobj->cmap; + gint64 mindist; + gint mindisti; + gint i; + + g_return_if_fail (quantobj->actual_number_of_colors > 0); + + mindist = G_MAXLONG; + mindisti = -1; + + for (i = 0; i < quantobj->actual_number_of_colors; i++) + { + gint64 dist = ABS (pixel - cmap[i].red); + + if (dist < mindist) + { + mindist = dist; + mindisti = i; + + if (mindist == 0) + break; + } + } + + histogram[pixel] = mindisti + 1; +} + + +/* Fill the inverse-colormap entries in the update box that contains + * histogram cell R/G/B. (Only that one cell MUST be filled, but we + * can fill as many others as we wish.) + */ +static void +fill_inverse_cmap_rgb (QuantizeObj *quantobj, + CFHistogram histogram, + gint R, + gint G, + gint B) +{ + gint minR, minG, minB; /* lower left corner of update box */ + gint iR, iG, iB; + gint *cptr; /* pointer into bestcolor[] array */ + /* This array lists the candidate colormap indexes. */ + gint colorlist[MAXNUMCOLORS]; + gint numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + gint bestcolor[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, }; + + /* Convert cell coordinates to update box id */ + R >>= BOX_R_LOG; + G >>= BOX_G_LOG; + B >>= BOX_B_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minR = (R << BOX_R_SHIFT) + ((1 << R_SHIFT) >> 1); + minG = (G << BOX_G_SHIFT) + ((1 << G_SHIFT) >> 1); + minB = (B << BOX_B_SHIFT) + ((1 << B_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors (quantobj, minR, minG, minB, colorlist); + + /* Determine the actually nearest colors. */ + find_best_colors (quantobj, minR, minG, minB, numcolors, colorlist, + bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + R <<= BOX_R_LOG; /* convert id back to base cell indexes */ + G <<= BOX_G_LOG; + B <<= BOX_B_LOG; + cptr = bestcolor; + for (iR = 0; iR < BOX_R_ELEMS; iR++) + { + for (iG = 0; iG < BOX_G_ELEMS; iG++) + { + for (iB = 0; iB < BOX_B_ELEMS; iB++) + { + *HIST_LIN (histogram, R + iR, G + iG, B + iB) = (*cptr++) + 1; + } + } + } +} + + +/* This is pass 1 */ + +static void +median_cut_pass1_gray (QuantizeObj *quantobj) +{ + select_colors_gray (quantobj, quantobj->histogram); +} + +static void +snap_to_black_and_white (QuantizeObj *quantobj) +{ + /* find whitest and blackest colors in palette, if they are closer + * than 24 units of euclidian distance in sRGB snap them to pure + * black / white. + */ +#define POW2(a) ((a)*(a)) + gint desired = quantobj->desired_number_of_colors; + gint whitest = 0; + gint blackest = 0; + + gint64 white_dist = POW2(255) * 3; + gint64 black_dist = POW2(255) * 3; + gint i; + + for (i = 0; i < desired; i ++) + { + int dist; + + dist = POW2 (quantobj->cmap[i].red - 255) + + POW2 (quantobj->cmap[i].green - 255) + + POW2( quantobj->cmap[i].blue - 255); + if (dist < white_dist) + { + white_dist = dist; + whitest = i; + } + + dist = POW2(quantobj->cmap[i].red - 0) + + POW2(quantobj->cmap[i].green - 0) + + POW2(quantobj->cmap[i].blue - 0); + if (dist < black_dist) + { + black_dist = dist; + blackest = i; + } + } + + if (desired > 2 && + had_white && + white_dist < POW2(128)) + { + quantobj->cmap[whitest].red = + quantobj->cmap[whitest].green = + quantobj->cmap[whitest].blue = 255; + } + if (desired > 2 && + had_black && + black_dist < POW2(128)) + { + quantobj->cmap[blackest].red = + quantobj->cmap[blackest].green = + quantobj->cmap[blackest].blue = 0; + } +#undef POW2 +} + +static void +median_cut_pass1_rgb (QuantizeObj *quantobj) +{ + select_colors_rgb (quantobj, quantobj->histogram); + snap_to_black_and_white (quantobj); +} + + +static void +monopal_pass1 (QuantizeObj *quantobj) +{ + quantobj->actual_number_of_colors = 2; + + quantobj->cmap[0].red = 0; + quantobj->cmap[0].green = 0; + quantobj->cmap[0].blue = 0; + quantobj->cmap[1].red = 255; + quantobj->cmap[1].green = 255; + quantobj->cmap[1].blue = 255; +} + +static void +webpal_pass1 (QuantizeObj *quantobj) +{ + int i; + + quantobj->actual_number_of_colors = 216; + + for (i=0; i < 216; i++) + { + quantobj->cmap[i].red = webpal[i * 3]; + quantobj->cmap[i].green = webpal[i * 3 +1]; + quantobj->cmap[i].blue = webpal[i * 3 +2]; + } +} + +static void +custompal_pass1 (QuantizeObj *quantobj) +{ + gint i; + GList *list; + + /* fprintf(stderr, + "custompal_pass1: using (theCustomPalette %s) from (file %s)\n", + theCustomPalette->name, theCustomPalette->filename); */ + + for (i = 0, list = gimp_palette_get_colors (quantobj->custom_palette); + list; + i++, list = g_list_next (list)) + { + GimpPaletteEntry *entry = list->data; + guchar r, g, b; + + gimp_rgb_get_uchar (&entry->color, &r, &g, &b); + + quantobj->cmap[i].red = (gint) r; + quantobj->cmap[i].green = (gint) g; + quantobj->cmap[i].blue = (gint) b; + } + + quantobj -> actual_number_of_colors = i; +} + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +static void +median_cut_pass2_no_dither_gray (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBufferIterator *iter; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + const Babl *src_format; + const Babl *dest_format; + GeglRectangle *src_roi; + gint src_bpp; + gint dest_bpp; + gint has_alpha; + guint64 *index_used_count = quantobj->index_used_count; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint offsetx, offsety; + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + src_roi = &iter->items[0].roi; + + gegl_buffer_iterator_add (iter, new_buffer, + NULL, 0, NULL, + 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 < src_roi->height; row++) + { + gint col; + + for (col = 0; col < src_roi->width; col++) + { + /* get pixel value and index into the cache */ + gint pixel = src[GRAY]; + + cachep = &histogram[pixel]; + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_gray (quantobj, histogram, pixel); + + if (has_alpha) + { + gboolean transparent = FALSE; + + if (dither_alpha) + { + gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK; + gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK; + + if ((src[ALPHA_G]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[ALPHA_G] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + } + else + { + dest[ALPHA_I] = 255; + index_used_count[dest[INDEXED] = *cachep - 1]++; + } + } + else + { + /* Now emit the colormap index for this cell */ + index_used_count[dest[INDEXED] = *cachep - 1]++; + } + + src += src_bpp; + dest += dest_bpp; + } + } + } +} + +static void +median_cut_pass2_fixed_dither_gray (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBufferIterator *iter; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + const Babl *src_format; + const Babl *dest_format; + GeglRectangle *src_roi; + gint src_bpp; + gint dest_bpp; + gboolean has_alpha; + gint pixval1 = 0; + gint pixval2 = 0; + gint err1; + gint err2; + Color *color1; + Color *color2; + guint64 *index_used_count = quantobj->index_used_count; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint offsetx, offsety; + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + src_roi = &iter->items[0].roi; + + gegl_buffer_iterator_add (iter, new_buffer, + NULL, 0, NULL, + 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 < src_roi->height; row++) + { + gint col; + + for (col = 0; col < src_roi->width; col++) + { + gint pixel; + const gint dmval = + DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK] + [(row + offsety + src_roi->y) & DM_HEIGHTMASK]; + + /* get pixel value and index into the cache */ + pixel = src[GRAY]; + + cachep = &histogram[pixel]; + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_gray (quantobj, histogram, pixel); + + pixval1 = *cachep - 1; + color1 = &quantobj->cmap[pixval1]; + + if (quantobj->actual_number_of_colors > 2) + { + const int re = src[GRAY] - (int)color1->red; + int RV = src[GRAY] + re; + + do + { + const gint R = CLAMP0255 (RV); + + cachep = &histogram[R]; + /* If we have not seen this color before, find + * nearest colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_gray (quantobj, histogram, R); + + pixval2 = *cachep - 1; + RV += re; + } + while ((pixval1 == pixval2) && + (! (RV>255 || RV<0) ) && + re); + } + else + { + /* not enough colors to bother looking for an 'alternative' + color (we may fail to do so anyway), so decide that + the alternative color is simply the other cmap entry. */ + pixval2 = (pixval1 + 1) % + (quantobj->actual_number_of_colors); + } + + /* always deterministically sort pixval1 and pixval2, to + avoid artifacts in the dither range due to inverting our + relative color viewpoint -- most obvious in 1-bit dither. */ + if (pixval1 > pixval2) + { + gint tmpval = pixval1; + pixval1 = pixval2; + pixval2 = tmpval; + color1 = &quantobj->cmap[pixval1]; + } + + color2 = &quantobj->cmap[pixval2]; + + err1 = ABS(color1->red - src[GRAY]); + err2 = ABS(color2->red - src[GRAY]); + if (err1 || err2) + { + const int proportion2 = (256 * 255 * err2) / (err1 + err2); + + if ((dmval * 256) > proportion2) + { + pixval1 = pixval2; /* use color2 instead of color1*/ + } + } + + if (has_alpha) + { + gboolean transparent = FALSE; + + if (dither_alpha) + { + if (src[ALPHA_G] < dmval) + transparent = TRUE; + } + else + { + if (src[ALPHA_G] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + } + else + { + dest[ALPHA_I] = 255; + index_used_count[dest[INDEXED] = pixval1]++; + } + } + else + { + /* Now emit the colormap index for this cell, barfbarf */ + index_used_count[dest[INDEXED] = pixval1]++; + } + + src += src_bpp; + dest += dest_bpp; + } + } + } +} + +static void +median_cut_pass2_no_dither_rgb (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBufferIterator *iter; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + const Babl *src_format; + const Babl *dest_format; + GeglRectangle *src_roi; + gint src_bpp; + gint dest_bpp; + gint has_alpha; + gint R, G, B; + gint red_pix = RED; + gint green_pix = GREEN; + gint blue_pix = BLUE; + gint alpha_pix = ALPHA; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint offsetx, offsety; + guint64 *index_used_count = quantobj->index_used_count; + gint64 total_size = 0; + gint64 layer_size; + gint count = 0; + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + /* In the case of web/mono palettes, we actually force + * grayscale drawables through the rgb pass2 functions + */ + if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer))) + { + red_pix = green_pix = blue_pix = GRAY; + alpha_pix = ALPHA_G; + } + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + src_roi = &iter->items[0].roi; + + gegl_buffer_iterator_add (iter, new_buffer, + NULL, 0, NULL, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) * + gimp_item_get_height (GIMP_ITEM (layer))); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *src = iter->items[0].data; + guchar *dest = iter->items[1].data; + gint row; + + total_size += src_roi->height * src_roi->width; + + for (row = 0; row < src_roi->height; row++) + { + gint col; + + for (col = 0; col < src_roi->width; col++) + { + if (has_alpha) + { + gboolean transparent = FALSE; + + if (dither_alpha) + { + gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK; + gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK; + + if ((src[alpha_pix]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[alpha_pix] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + + /* get pixel value and index into the cache */ + rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix], + &R, &G, &B); + + cachep = HIST_LIN (histogram, R, G, B); + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_rgb (quantobj, histogram, R, G, B); + + /* Now emit the colormap index for this cell, barfbarf */ + index_used_count[dest[INDEXED] = *cachep - 1]++; + + next_pixel: + + src += src_bpp; + dest += dest_bpp; + } + } + + if (quantobj->progress && (count % 16 == 0)) + gimp_progress_set_value (quantobj->progress, + (gdouble) total_size / (gdouble) layer_size); + } +} + +static void +median_cut_pass2_fixed_dither_rgb (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBufferIterator *iter; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + const Babl *src_format; + const Babl *dest_format; + GeglRectangle *src_roi; + gint src_bpp; + gint dest_bpp; + gint has_alpha; + gint pixval1 = 0; + gint pixval2 = 0; + Color *color1; + Color *color2; + gint R, G, B; + gint err1; + gint err2; + gint red_pix = RED; + gint green_pix = GREEN; + gint blue_pix = BLUE; + gint alpha_pix = ALPHA; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint offsetx, offsety; + guint64 *index_used_count = quantobj->index_used_count; + gint64 total_size = 0; + gint64 layer_size; + gint count = 0; + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + /* In the case of web/mono palettes, we actually force + * grayscale drawables through the rgb pass2 functions + */ + if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer))) + { + red_pix = green_pix = blue_pix = GRAY; + alpha_pix = ALPHA_G; + } + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + src_roi = &iter->items[0].roi; + + gegl_buffer_iterator_add (iter, new_buffer, + NULL, 0, NULL, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) * + gimp_item_get_height (GIMP_ITEM (layer))); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *src = iter->items[0].data; + guchar *dest = iter->items[1].data; + gint row; + + total_size += src_roi->height * src_roi->width; + + for (row = 0; row < src_roi->height; row++) + { + gint col; + + for (col = 0; col < src_roi->width; col++) + { + const int dmval = + DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK] + [(row + offsety + src_roi->y) & DM_HEIGHTMASK]; + + if (has_alpha) + { + gboolean transparent = FALSE; + + if (dither_alpha) + { + if (src[alpha_pix] < dmval) + transparent = TRUE; + } + else + { + if (src[alpha_pix] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + + /* get pixel value and index into the cache */ + rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix], + &R, &G, &B); + + cachep = HIST_LIN (histogram, R, G, B); + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_rgb (quantobj, histogram, R, G, B); + + /* We now try to find a color which, when mixed in some + * fashion with the closest match, yields something + * closer to the desired color. We do this by + * repeatedly extrapolating the color vector from one to + * the other until we find another color cell. Then we + * assess the distance of both mixer colors from the + * intended color to determine their relative + * probabilities of being chosen. + */ + pixval1 = *cachep - 1; + color1 = &quantobj->cmap[pixval1]; + + if (quantobj->actual_number_of_colors > 2) + { + const gint re = src[red_pix] - (gint) color1->red; + const gint ge = src[green_pix] - (gint) color1->green; + const gint be = src[blue_pix] - (gint) color1->blue; + gint RV = src[red_pix] + re; + gint GV = src[green_pix] + ge; + gint BV = src[blue_pix] + be; + + do + { + rgb_to_lin ((CLAMP0255(RV)), + (CLAMP0255(GV)), + (CLAMP0255(BV)), + &R, &G, &B); + + cachep = HIST_LIN (histogram, R, G, B); + /* If we have not seen this color before, find + * nearest colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_rgb (quantobj, histogram, R, G, B); + + pixval2 = *cachep - 1; + RV += re; GV += ge; BV += be; + } + while ((pixval1 == pixval2) && + (!( (RV>255 || RV<0) || (GV>255 || GV<0) || (BV>255 || BV<0) )) && + (re || ge || be)); + } + + if (quantobj->actual_number_of_colors <= 2 + /* || pixval1 == pixval2 */) { + /* not enough colors to bother looking for an 'alternative' + color (we may fail to do so anyway), so decide that + the alternative color is simply the other cmap entry. */ + pixval2 = (pixval1 + 1) % + (quantobj->actual_number_of_colors); + } + + /* always deterministically sort pixval1 and pixval2, to + avoid artifacts in the dither range due to inverting our + relative color viewpoint -- most obvious in 1-bit dither. */ + if (pixval1 > pixval2) + { + gint tmpval = pixval1; + pixval1 = pixval2; + pixval2 = tmpval; + color1 = &quantobj->cmap[pixval1]; + } + + color2 = &quantobj->cmap[pixval2]; + + /* now figure out the relative probabilites of choosing + either of our candidates. */ +#define DISTP(R1,G1,B1,R2,G2,B2,D) do {D = sqrt( 30*SQR((R1)-(R2)) + \ + 59*SQR((G1)-(G2)) + \ + 11*SQR((B1)-(B2)) ); }while(0) +#define LIN_DISTP(R1,G1,B1,R2,G2,B2,D) do { \ + int spacer1, spaceg1, spaceb1; \ + int spacer2, spaceg2, spaceb2; \ + rgb_to_unshifted_lin (R1,G1,B1, &spacer1, &spaceg1, &spaceb1); \ + rgb_to_unshifted_lin (R2,G2,B2, &spacer2, &spaceg2, &spaceb2); \ + D = sqrt(R_SCALE * SQR((spacer1)-(spacer2)) + \ + G_SCALE * SQR((spaceg1)-(spaceg2)) + \ + B_SCALE * SQR((spaceb1)-(spaceb2))); \ + } while(0) + + /* although LIN_DISTP is more correct, DISTP is much faster and + barely distinguishable. */ + DISTP (color1->red, color1->green, color1->blue, + src[red_pix], src[green_pix], src[blue_pix], + err1); + DISTP (color2->red, color2->green, color2->blue, + src[red_pix], src[green_pix], src[blue_pix], + err2); + + if (err1 || err2) + { + const int proportion2 = (255 * err2) / (err1 + err2); + if (dmval > proportion2) + { + pixval1 = pixval2; /* use color2 instead of color1*/ + } + } + + /* Now emit the colormap index for this cell, barfbarf */ + index_used_count[dest[INDEXED] = pixval1]++; + + next_pixel: + + src += src_bpp; + dest += dest_bpp; + } + } + + if (quantobj->progress && (count % 16 == 0)) + gimp_progress_set_value (quantobj->progress, + (gdouble) total_size / (gdouble) layer_size); + } +} + +static void +median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBufferIterator *iter; + const Babl *src_format; + const Babl *dest_format; + GeglRectangle *src_roi; + gint src_bpp; + gint dest_bpp; + gint has_alpha; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint red_pix = RED; + gint green_pix = GREEN; + gint blue_pix = BLUE; + gint alpha_pix = ALPHA; + gint lastindex = 0; + gint lastred = -1; + gint lastgreen = -1; + gint lastblue = -1; + gint offsetx, offsety; + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + src_roi = &iter->items[0].roi; + + gegl_buffer_iterator_add (iter, new_buffer, + NULL, 0, NULL, + 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 < src_roi->height; row++) + { + gint col; + + for (col = 0; col < src_roi->width; col++) + { + gboolean transparent = FALSE; + + if (has_alpha) + { + if (dither_alpha) + { + gint dither_x = (col + src_roi->x + offsetx) & DM_WIDTHMASK; + gint dither_y = (row + src_roi->y + offsety) & DM_HEIGHTMASK; + + if ((src[alpha_pix]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[alpha_pix] < 128) + transparent = TRUE; + } + } + + if (! transparent) + { + if ((lastred == src[red_pix]) && + (lastgreen == src[green_pix]) && + (lastblue == src[blue_pix])) + { + /* same pixel color as last time */ + dest[INDEXED] = lastindex; + if (has_alpha) + dest[ALPHA_I] = 255; + } + else + { + gint i; + + for (i = 0 ; + i < quantobj->actual_number_of_colors; + i++) + { + if ((quantobj->cmap[i].green == src[green_pix]) && + (quantobj->cmap[i].red == src[red_pix]) && + (quantobj->cmap[i].blue == src[blue_pix])) + { + lastred = src[red_pix]; + lastgreen = src[green_pix]; + lastblue = src[blue_pix]; + lastindex = i; + + goto got_color; + } + } + g_error ("Non-existant color was expected to " + "be in non-destructive colormap."); + got_color: + dest[INDEXED] = lastindex; + if (has_alpha) + dest[ALPHA_I] = 255; + } + } + else + { /* have alpha, and transparent */ + dest[ALPHA_I] = 0; + } + + src += src_bpp; + dest += dest_bpp; + } + } + } +} + + +/* + * Initialize the error-limiting transfer function (lookup table). + * The raw F-S error computation can potentially compute error values of up to + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * much less, otherwise obviously wrong pixels will be created. (Typical + * effects include weird fringes at color-area boundaries, isolated bright + * pixels in a dark area, etc.) The standard advice for avoiding this problem + * is to ensure that the "corners" of the color cube are allocated as output + * colors; then repeated errors in the same direction cannot cause cascading + * error buildup. However, that only prevents the error from getting + * completely out of hand; Aaron Giles reports that error limiting improves + * the results even with corner colors allocated. + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * well, but the smoother transfer function used below is even better. Thanks + * to Aaron Giles for this idea. + */ + +static gint * +init_error_limit (const int error_freedom) +/* Allocate and fill in the error_limiter table */ +{ + gint *table; + gint inp, out; + + /* #define STEPSIZE 16 */ + /* #define STEPSIZE 200 */ + + table = g_new (gint, 255 * 2 + 1); + table += 255; /* so we can index -255 ... +255 */ + + if (error_freedom == 0) + { + /* Coarse function, much bleeding. */ + + const gint STEPSIZE = 190; + + for (inp = 0; inp < STEPSIZE; inp++) + { + table[inp] = inp; + table[-inp] = -inp; + } + + for (; inp <= 255; inp++) + { + table[inp] = STEPSIZE; + table[-inp] = -STEPSIZE; + } + + return (table); + } + else + { + /* Smooth function, bleeding more constrained */ + + const gint STEPSIZE = 24; + + /* Map errors 1:1 up to +- STEPSIZE */ + out = 0; + for (inp = 0; inp < STEPSIZE; inp++, out++) + { + table[inp] = out; + table[-inp] = -out; + } + + /* Map errors 1:2 up to +- 3*STEPSIZE */ + for (; inp < STEPSIZE*3; inp++, out += (inp&1) ? 0 : 1) + { + table[inp] = out; + table[-inp] = -out; + } + + /* Clamp the rest to final out value (which is STEPSIZE*2) */ + for (; inp <= 255; inp++) + { + table[inp] = out; + table[-inp] = -out; + } + + return table; + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + * Perform floyd-steinberg dithering. + */ + +static void +median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBuffer *src_buffer; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + Color *color; + gint *error_limiter; + const gshort *fs_err1, *fs_err2; + const gshort *fs_err3, *fs_err4; + const guchar *range_limiter; + const Babl *src_format; + const Babl *dest_format; + gint src_bpp; + gint dest_bpp; + guchar *src_buf, *dest_buf; + gint *next_row, *prev_row; + gint *nr, *pr; + gint *tmp; + gint pixel; + gint pixele; + gint row, col; + gint index; + gint step_dest, step_src; + gint odd_row; + gboolean has_alpha; + gint offsetx, offsety; + gboolean dither_alpha = quantobj->want_dither_alpha; + gint width, height; + guint64 *index_used_count = quantobj->index_used_count; + + src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + width = gimp_item_get_width (GIMP_ITEM (layer)); + height = gimp_item_get_height (GIMP_ITEM (layer)); + + error_limiter = init_error_limit (quantobj->error_freedom); + range_limiter = range_array + 256; + + src_buf = g_malloc (width * src_bpp); + dest_buf = g_malloc (width * dest_bpp); + + next_row = g_new (gint, width + 2); + prev_row = g_new0 (gint, width + 2); + + fs_err1 = floyd_steinberg_error1 + 511; + fs_err2 = floyd_steinberg_error2 + 511; + fs_err3 = floyd_steinberg_error3 + 511; + fs_err4 = floyd_steinberg_error4 + 511; + + odd_row = 0; + + for (row = 0; row < height; row++) + { + const guchar *src; + guchar *dest; + + gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1), + 1.0, NULL, src_buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + src = src_buf; + dest = dest_buf; + + nr = next_row; + pr = prev_row + 1; + + if (odd_row) + { + step_dest = -dest_bpp; + step_src = -src_bpp; + + src += (width * src_bpp) - src_bpp; + dest += (width * dest_bpp) - dest_bpp; + + nr += width + 1; + pr += width; + + *(nr - 1) = 0; + } + else + { + step_dest = dest_bpp; + step_src = src_bpp; + + *(nr + 1) = 0; + } + + *nr = 0; + + for (col = 0; col < width; col++) + { + pixel = range_limiter[src[GRAY] + error_limiter[*pr]]; + + cachep = &histogram[pixel]; + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_gray (quantobj, histogram, pixel); + + if (has_alpha) + { + gboolean transparent = FALSE; + + if (odd_row) + { + if (dither_alpha) + { + gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK; + gint dither_y = (row+offsety) & DM_HEIGHTMASK; + + if ((src[ALPHA_G]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[ALPHA_G] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + pr--; + nr--; + *(nr - 1) = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + else + { + if (dither_alpha) + { + gint dither_x = (col + offsetx) & DM_WIDTHMASK; + gint dither_y = (row + offsety) & DM_HEIGHTMASK; + + if ((src[ALPHA_G]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[ALPHA_G] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + pr++; + nr++; + *(nr + 1) = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + } + + index = *cachep - 1; + index_used_count[dest[INDEXED] = index]++; + + color = &quantobj->cmap[index]; + pixele = pixel - color->red; + + if (odd_row) + { + *(--pr) += fs_err1[pixele]; + *nr-- += fs_err2[pixele]; + *nr += fs_err3[pixele]; + *(nr-1) = fs_err4[pixele]; + } + else + { + *(++pr) += fs_err1[pixele]; + *nr++ += fs_err2[pixele]; + *nr += fs_err3[pixele]; + *(nr+1) = fs_err4[pixele]; + } + + next_pixel: + + dest += step_dest; + src += step_src; + } + + tmp = next_row; + next_row = prev_row; + prev_row = tmp; + + odd_row = !odd_row; + + gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1), + 0, NULL, dest_buf, + GEGL_AUTO_ROWSTRIDE); + } + + g_free (error_limiter - 255); /* good lord. */ + g_free (next_row); + g_free (prev_row); + g_free (src_buf); + g_free (dest_buf); +} + +static void +median_cut_pass2_rgb_init (QuantizeObj *quantobj) +{ + int i; + + zero_histogram_rgb (quantobj->histogram); + + /* Mark all indices as currently unused */ + memset (quantobj->index_used_count, 0, 256 * sizeof (guint64)); + + /* Make a version of our discovered colormap in linear space */ + for (i = 0; i < quantobj->actual_number_of_colors; i++) + { + rgb_to_unshifted_lin (quantobj->cmap[i].red, + quantobj->cmap[i].green, + quantobj->cmap[i].blue, + &quantobj->clin[i].red, + &quantobj->clin[i].green, + &quantobj->clin[i].blue); + } +} + +static void +median_cut_pass2_gray_init (QuantizeObj *quantobj) +{ + zero_histogram_gray (quantobj->histogram); + + /* Mark all indices as currently unused */ + memset (quantobj->index_used_count, 0, 256 * sizeof (guint64)); +} + +static void +median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj, + GimpLayer *layer, + GeglBuffer *new_buffer) +{ + GeglBuffer *src_buffer; + CFHistogram histogram = quantobj->histogram; + ColorFreq *cachep; + Color *color; + gint *error_limiter; + const gshort *fs_err1, *fs_err2; + const gshort *fs_err3, *fs_err4; + const guchar *range_limiter; + const Babl *src_format; + const Babl *dest_format; + gint src_bpp; + gint dest_bpp; + guchar *src_buf, *dest_buf; + gint *red_n_row, *red_p_row; + gint *grn_n_row, *grn_p_row; + gint *blu_n_row, *blu_p_row; + gint *rnr, *rpr; + gint *gnr, *gpr; + gint *bnr, *bpr; + gint *tmp; + gint re, ge, be; + gint row, col; + gint index; + gint step_dest, step_src; + gint odd_row; + gboolean has_alpha; + gint width, height; + gint red_pix = RED; + gint green_pix = GREEN; + gint blue_pix = BLUE; + gint alpha_pix = ALPHA; + gint offsetx, offsety; + gboolean dither_alpha = quantobj->want_dither_alpha; + guint64 *index_used_count = quantobj->index_used_count; + gint global_rmax = 0, global_rmin = G_MAXINT; + gint global_gmax = 0, global_gmin = G_MAXINT; + gint global_bmax = 0, global_bmin = G_MAXINT; + + src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety); + + /* In the case of web/mono palettes, we actually force + * grayscale drawables through the rgb pass2 functions + */ + if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer))) + red_pix = green_pix = blue_pix = GRAY; + + src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer)); + dest_format = gegl_buffer_get_format (new_buffer); + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (dest_format); + + has_alpha = babl_format_has_alpha (src_format); + + width = gimp_item_get_width (GIMP_ITEM (layer)); + height = gimp_item_get_height (GIMP_ITEM (layer)); + + error_limiter = init_error_limit (quantobj->error_freedom); + range_limiter = range_array + 256; + + /* find the bounding box of the palette colors -- + we use this for hard-clamping our error-corrected + values so that we can't continuously accelerate outside + of our attainable gamut, which looks icky. */ + for (index = 0; index < quantobj->actual_number_of_colors; index++) + { + global_rmax = MAX(global_rmax, quantobj->clin[index].red); + global_rmin = MIN(global_rmin, quantobj->clin[index].red); + global_gmax = MAX(global_gmax, quantobj->clin[index].green); + global_gmin = MIN(global_gmin, quantobj->clin[index].green); + global_bmax = MAX(global_bmax, quantobj->clin[index].blue); + global_bmin = MIN(global_bmin, quantobj->clin[index].blue); + } + + src_buf = g_malloc (width * src_bpp); + dest_buf = g_malloc (width * dest_bpp); + + red_n_row = g_new (gint, width + 2); + red_p_row = g_new0 (gint, width + 2); + grn_n_row = g_new (gint, width + 2); + grn_p_row = g_new0 (gint, width + 2); + blu_n_row = g_new (gint, width + 2); + blu_p_row = g_new0 (gint, width + 2); + + fs_err1 = floyd_steinberg_error1 + 511; + fs_err2 = floyd_steinberg_error2 + 511; + fs_err3 = floyd_steinberg_error3 + 511; + fs_err4 = floyd_steinberg_error4 + 511; + + odd_row = 0; + + for (row = 0; row < height; row++) + { + const guchar *src; + guchar *dest; + + gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1), + 1.0, NULL, src_buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + src = src_buf; + dest = dest_buf; + + rnr = red_n_row; + gnr = grn_n_row; + bnr = blu_n_row; + rpr = red_p_row + 1; + gpr = grn_p_row + 1; + bpr = blu_p_row + 1; + + if (odd_row) + { + step_dest = -dest_bpp; + step_src = -src_bpp; + + src += (width * src_bpp) - src_bpp; + dest += (width * dest_bpp) - dest_bpp; + + rnr += width + 1; + gnr += width + 1; + bnr += width + 1; + rpr += width; + gpr += width; + bpr += width; + + *(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0; + } + else + { + step_dest = dest_bpp; + step_src = src_bpp; + + *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0; + } + + *rnr = *gnr = *bnr = 0; + + for (col = 0; col < width; col++) + { + if (has_alpha) + { + gboolean transparent = FALSE; + + if (odd_row) + { + if (dither_alpha) + { + gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK; + gint dither_y = (row+offsety) & DM_HEIGHTMASK; + + if ((src[alpha_pix]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[alpha_pix] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + rpr--; gpr--; bpr--; + rnr--; gnr--; bnr--; + *(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + else + { + if (dither_alpha) + { + gint dither_x = (col + offsetx) & DM_WIDTHMASK; + gint dither_y = (row + offsety) & DM_HEIGHTMASK; + + if ((src[alpha_pix]) < DM[dither_x][dither_y]) + transparent = TRUE; + } + else + { + if (src[alpha_pix] <= 127) + transparent = TRUE; + } + + if (transparent) + { + dest[ALPHA_I] = 0; + rpr++; gpr++; bpr++; + rnr++; gnr++; bnr++; + *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0; + goto next_pixel; + } + else + { + dest[ALPHA_I] = 255; + } + } + } + +#if 0 + /* hmm. */ + + r = range_limiter[src[red_pix] + error_limiter[*rpr]]; + g = range_limiter[src[green_pix] + error_limiter[*gpr]]; + b = range_limiter[src[blue_pix] + error_limiter[*bpr]]; + + re = r >> R_SHIFT; + ge = g >> G_SHIFT; + be = b >> B_SHIFT; + + rgb_to_lin (r, g, b, &re, &ge, &be); +#endif + rgb_to_unshifted_lin (src[red_pix], src[green_pix], src[blue_pix], + &re, &ge, &be); + + /* + re = CLAMP(re, global_rmin, global_rmax); + ge = CLAMP(ge, global_gmin, global_gmax); + be = CLAMP(be, global_bmin, global_bmax);*/ + + re = range_limiter[re + error_limiter[*rpr]]; + ge = range_limiter[ge + error_limiter[*gpr]]; + be = range_limiter[be + error_limiter[*bpr]]; + + cachep = HIST_LIN (histogram, + RSDF (re), + GSDF (ge), + BSDF (be)); + /* If we have not seen this color before, find nearest + * colormap entry and update the cache + */ + if (*cachep == 0) + fill_inverse_cmap_rgb (quantobj, histogram, + RSDF (re), + GSDF (ge), + BSDF (be)); + + index = *cachep - 1; + index_used_count[index]++; + dest[INDEXED] = index; + + /*if (re > global_rmax) + re = (re + 3*global_rmax) / 4; + else if (re < global_rmin) + re = (re + 3*global_rmin) / 4;*/ + + /* We constrain chroma error extra-hard so that it + doesn't run away and steal the thunder from the + lightness error where all the detail usually is. */ + if (ge > global_gmax) + ge = (ge + 3*global_gmax) / 4; + else if (ge < global_gmin) + ge = (ge + 3*global_gmin) / 4; + if (be > global_bmax) + be = (be + 3*global_bmax) / 4; + else if (be < global_bmin) + be = (be + 3*global_bmin) / 4; + + color = &quantobj->clin[index]; + +#if 0 + if ((re > 0 && re < 255) /* HMM && + ge >= 0 && ge <= 255 && + be >= 0 && be <= 255*/) + { + ge = ge - color->green; + be = be - color->blue; + re = re - color->red; + } + else + { + /* color pretty much undefined now; nullify error. */ + re = ge = be = 0; + } +#endif + + if (re <= 0 || re >= 255) + re = ge = be = 0; + else + { + re = re - color->red; + ge = ge - color->green; + be = be - color->blue; + } + + if (odd_row) + { + *(--rpr) += fs_err1[re]; + *(--gpr) += fs_err1[ge]; + *(--bpr) += fs_err1[be]; + + *rnr-- += fs_err2[re]; + *gnr-- += fs_err2[ge]; + *bnr-- += fs_err2[be]; + + *rnr += fs_err3[re]; + *gnr += fs_err3[ge]; + *bnr += fs_err3[be]; + + *(rnr-1) = fs_err4[re]; + *(gnr-1) = fs_err4[ge]; + *(bnr-1) = fs_err4[be]; + } + else + { + *(++rpr) += fs_err1[re]; + *(++gpr) += fs_err1[ge]; + *(++bpr) += fs_err1[be]; + + *rnr++ += fs_err2[re]; + *gnr++ += fs_err2[ge]; + *bnr++ += fs_err2[be]; + + *rnr += fs_err3[re]; + *gnr += fs_err3[ge]; + *bnr += fs_err3[be]; + + *(rnr+1) = fs_err4[re]; + *(gnr+1) = fs_err4[ge]; + *(bnr+1) = fs_err4[be]; + } + + next_pixel: + + dest += step_dest; + src += step_src; + } + + tmp = red_n_row; + red_n_row = red_p_row; + red_p_row = tmp; + + tmp = grn_n_row; + grn_n_row = grn_p_row; + grn_p_row = tmp; + + tmp = blu_n_row; + blu_n_row = blu_p_row; + blu_p_row = tmp; + + odd_row = !odd_row; + + gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1), + 0, NULL, dest_buf, + GEGL_AUTO_ROWSTRIDE); + + if (quantobj->progress && (row % 16 == 0)) + gimp_progress_set_value (quantobj->progress, + (gdouble) row / (gdouble) height); + } + + g_free (error_limiter - 255); + g_free (red_n_row); + g_free (red_p_row); + g_free (grn_n_row); + g_free (grn_p_row); + g_free (blu_n_row); + g_free (blu_p_row); + g_free (src_buf); + g_free (dest_buf); +} + + +static void +delete_median_cut (QuantizeObj *quantobj) +{ + g_free (quantobj->histogram); + g_free (quantobj); +} + + +void +gimp_image_convert_indexed_set_dither_matrix (const guchar *matrix, + gint width, + gint height) +{ + gint x; + gint y; + + /* if matrix is invalid, restore the default matrix */ + if (matrix == NULL || width == 0 || height == 0) + { + matrix = (const guchar *) DM_ORIGINAL; + width = DM_WIDTH; + height = DM_HEIGHT; + } + + g_return_if_fail ((DM_WIDTH % width) == 0); + g_return_if_fail ((DM_HEIGHT % height) == 0); + + for (y = 0; y < DM_HEIGHT; y++) + { + for (x = 0; x < DM_WIDTH; x++) + { + DM[x][y] = matrix[((x % width) * height) + (y % height)]; + } + } +} + + +/**************************************************************/ +static QuantizeObj * +initialize_median_cut (GimpImageBaseType type, + gint num_colors, + GimpConvertDitherType dither_type, + GimpConvertPaletteType palette_type, + GimpPalette *custom_palette, + gboolean want_dither_alpha, + GimpProgress *progress) +{ + QuantizeObj *quantobj; + + /* Initialize the data structures */ + quantobj = g_new (QuantizeObj, 1); + + if (type == GIMP_GRAY && palette_type == GIMP_CONVERT_PALETTE_GENERATE) + quantobj->histogram = g_new (ColorFreq, 256); + else + quantobj->histogram = g_new (ColorFreq, + HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS); + + quantobj->custom_palette = custom_palette; + quantobj->desired_number_of_colors = num_colors; + quantobj->want_dither_alpha = want_dither_alpha; + quantobj->progress = progress; + + switch (type) + { + case GIMP_GRAY: + switch (palette_type) + { + case GIMP_CONVERT_PALETTE_GENERATE: + quantobj->first_pass = median_cut_pass1_gray; + break; + case GIMP_CONVERT_PALETTE_WEB: + quantobj->first_pass = webpal_pass1; + break; + case GIMP_CONVERT_PALETTE_CUSTOM: + quantobj->first_pass = custompal_pass1; + needs_quantize = TRUE; + break; + case GIMP_CONVERT_PALETTE_MONO: + default: + quantobj->first_pass = monopal_pass1; + } + + if (palette_type == GIMP_CONVERT_PALETTE_WEB || + palette_type == GIMP_CONVERT_PALETTE_CUSTOM) + { + switch (dither_type) + { + case GIMP_CONVERT_DITHER_NODESTRUCT: + default: + g_warning("Uh-oh, bad dither type, W1"); + case GIMP_CONVERT_DITHER_NONE: + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_no_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FS: + quantobj->error_freedom = 0; + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fs_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FS_LOWBLEED: + quantobj->error_freedom = 1; + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fs_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FIXED: + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; + break; + } + } + else + { + switch (dither_type) + { + case GIMP_CONVERT_DITHER_NODESTRUCT: + default: + g_warning("Uh-oh, bad dither type, W2"); + case GIMP_CONVERT_DITHER_NONE: + quantobj->second_pass_init = median_cut_pass2_gray_init; + quantobj->second_pass = median_cut_pass2_no_dither_gray; + break; + case GIMP_CONVERT_DITHER_FS: + quantobj->error_freedom = 0; + quantobj->second_pass_init = median_cut_pass2_gray_init; + quantobj->second_pass = median_cut_pass2_fs_dither_gray; + break; + case GIMP_CONVERT_DITHER_FS_LOWBLEED: + quantobj->error_freedom = 1; + quantobj->second_pass_init = median_cut_pass2_gray_init; + quantobj->second_pass = median_cut_pass2_fs_dither_gray; + break; + case GIMP_CONVERT_DITHER_FIXED: + quantobj->second_pass_init = median_cut_pass2_gray_init; + quantobj->second_pass = median_cut_pass2_fixed_dither_gray; + break; + } + } + break; + + case GIMP_RGB: + switch (palette_type) + { + case GIMP_CONVERT_PALETTE_GENERATE: + quantobj->first_pass = median_cut_pass1_rgb; + break; + case GIMP_CONVERT_PALETTE_WEB: + quantobj->first_pass = webpal_pass1; + needs_quantize = TRUE; + break; + case GIMP_CONVERT_PALETTE_CUSTOM: + quantobj->first_pass = custompal_pass1; + needs_quantize = TRUE; + break; + case GIMP_CONVERT_PALETTE_MONO: + default: + quantobj->first_pass = monopal_pass1; + } + + switch (dither_type) + { + case GIMP_CONVERT_DITHER_NONE: + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_no_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FS: + quantobj->error_freedom = 0; + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fs_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FS_LOWBLEED: + quantobj->error_freedom = 1; + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fs_dither_rgb; + break; + case GIMP_CONVERT_DITHER_NODESTRUCT: + quantobj->second_pass_init = NULL; + quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb; + break; + case GIMP_CONVERT_DITHER_FIXED: + quantobj->second_pass_init = median_cut_pass2_rgb_init; + quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; + break; + } + break; + + default: + break; + } + + quantobj->delete_func = delete_median_cut; + + return quantobj; +} diff --git a/app/core/gimpimage-convert-indexed.h b/app/core/gimpimage-convert-indexed.h new file mode 100644 index 0000000..ba51f02 --- /dev/null +++ b/app/core/gimpimage-convert-indexed.h @@ -0,0 +1,41 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_CONVERT_INDEXED_H__ +#define __GIMP_IMAGE_CONVERT_INDEXED_H__ + + +#define MAXNUMCOLORS 256 + + +gboolean gimp_image_convert_indexed (GimpImage *image, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + GimpProgress *progress, + GError **error); + +void gimp_image_convert_indexed_set_dither_matrix (const guchar *matrix, + gint width, + gint height); + + +#endif /* __GIMP_IMAGE_CONVERT_INDEXED_H__ */ diff --git a/app/core/gimpimage-convert-precision.c b/app/core/gimpimage-convert-precision.c new file mode 100644 index 0000000..71a613c --- /dev/null +++ b/app/core/gimpimage-convert-precision.c @@ -0,0 +1,304 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-convert-precision.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimpchannel.h" +#include "gimpdrawable.h" +#include "gimpdrawable-operation.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-convert-precision.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + +#include "text/gimptextlayer.h" + +#include "gimp-intl.h" + + +void +gimp_image_convert_precision (GimpImage *image, + GimpPrecision precision, + GeglDitherMethod layer_dither_type, + GeglDitherMethod text_layer_dither_type, + GeglDitherMethod mask_dither_type, + GimpProgress *progress) +{ + GimpColorProfile *old_profile; + GimpColorProfile *new_profile = NULL; + const Babl *old_format; + const Babl *new_format; + GimpObjectQueue *queue; + GimpProgress *sub_progress; + GList *layers; + GimpDrawable *drawable; + const gchar *undo_desc = NULL; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (precision != gimp_image_get_precision (image)); + g_return_if_fail (gimp_babl_is_valid (gimp_image_get_base_type (image), + precision)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 8 bit linear integer"); + break; + case GIMP_PRECISION_U8_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 8 bit gamma integer"); + break; + case GIMP_PRECISION_U16_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 16 bit linear integer"); + break; + case GIMP_PRECISION_U16_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 16 bit gamma integer"); + break; + case GIMP_PRECISION_U32_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 32 bit linear integer"); + break; + case GIMP_PRECISION_U32_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 32 bit gamma integer"); + break; + case GIMP_PRECISION_HALF_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 16 bit linear floating point"); + break; + case GIMP_PRECISION_HALF_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 16 bit gamma floating point"); + break; + case GIMP_PRECISION_FLOAT_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 32 bit linear floating point"); + break; + case GIMP_PRECISION_FLOAT_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 32 bit gamma floating point"); + break; + case GIMP_PRECISION_DOUBLE_LINEAR: + undo_desc = C_("undo-type", "Convert Image to 64 bit linear floating point"); + break; + case GIMP_PRECISION_DOUBLE_GAMMA: + undo_desc = C_("undo-type", "Convert Image to 64 bit gamma floating point"); + break; + } + + if (progress) + gimp_progress_start (progress, FALSE, "%s", undo_desc); + + queue = gimp_object_queue_new (progress); + sub_progress = GIMP_PROGRESS (queue); + + layers = gimp_image_get_layer_list (image); + gimp_object_queue_push_list (queue, layers); + g_list_free (layers); + + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, + undo_desc); + + /* Push the image precision to the stack */ + gimp_image_undo_push_image_precision (image, NULL); + + old_profile = gimp_image_get_color_profile (image); + old_format = gimp_image_get_layer_format (image, FALSE); + + gimp_image_set_converting (image, TRUE); + + /* Set the new precision */ + g_object_set (image, "precision", precision, NULL); + + new_format = gimp_image_get_layer_format (image, FALSE); + + if (old_profile) + { + if (gimp_babl_format_get_linear (old_format) != + gimp_babl_format_get_linear (new_format)) + { + /* when converting between linear and gamma, we create a new + * profile using the original profile's chromacities and + * whitepoint, but a linear/sRGB-gamma TRC. + */ + + if (gimp_babl_format_get_linear (new_format)) + { + new_profile = + gimp_color_profile_new_linear_from_color_profile (old_profile); + } + else + { + new_profile = + gimp_color_profile_new_srgb_trc_from_color_profile (old_profile); + } + + /* if a new profile cannot be be generated, convert to the + * builtin profile, which is better than leaving the user with + * broken colors + */ + if (! new_profile) + { + new_profile = gimp_image_get_builtin_color_profile (image); + g_object_ref (new_profile); + } + } + + if (! new_profile) + new_profile = g_object_ref (old_profile); + } + + while ((drawable = gimp_object_queue_pop (queue))) + { + if (drawable == GIMP_DRAWABLE (gimp_image_get_mask (image))) + { + GeglBuffer *buffer; + + gimp_image_undo_push_mask_precision (image, NULL, + GIMP_CHANNEL (drawable)); + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image)), + gimp_image_get_mask_format (image)); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL, + GEGL_ABYSS_NONE, + buffer, NULL); + + gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); + g_object_unref (buffer); + + gimp_progress_set_value (sub_progress, 1.0); + } + else + { + gint dither_type; + + if (gimp_item_is_text_layer (GIMP_ITEM (drawable))) + dither_type = text_layer_dither_type; + else + dither_type = layer_dither_type; + + gimp_drawable_convert_type (drawable, image, + gimp_drawable_get_base_type (drawable), + precision, + gimp_drawable_has_alpha (drawable), + new_profile, + dither_type, + mask_dither_type, + TRUE, sub_progress); + } + } + + if (new_profile) + { + if (new_profile != old_profile) + gimp_image_set_color_profile (image, new_profile, NULL); + + g_object_unref (new_profile); + } + + gimp_image_set_converting (image, FALSE); + + gimp_image_undo_group_end (image); + + gimp_image_precision_changed (image); + g_object_thaw_notify (G_OBJECT (image)); + + g_object_unref (queue); + + if (progress) + gimp_progress_end (progress); +} + +void +gimp_image_convert_dither_u8 (GimpImage *image, + GimpProgress *progress) +{ + GeglNode *dither; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + dither = gegl_node_new_child (NULL, + "operation", "gegl:noise-rgb", + "red", 1.0 / 256.0, + "green", 1.0 / 256.0, + "blue", 1.0 / 256.0, + "linear", FALSE, + "gaussian", FALSE, + NULL); + + if (dither) + { + GimpObjectQueue *queue; + GimpProgress *sub_progress; + GList *layers; + GList *list; + GimpDrawable *drawable; + + if (progress) + gimp_progress_start (progress, FALSE, "%s", _("Dithering")); + + queue = gimp_object_queue_new (progress); + sub_progress = GIMP_PROGRESS (queue); + + layers = gimp_image_get_layer_list (image); + + for (list = layers; list; list = g_list_next (list)) + { + if (! gimp_viewable_get_children (list->data) && + ! gimp_item_is_text_layer (list->data)) + { + gimp_object_queue_push (queue, list->data); + } + } + + g_list_free (layers); + + while ((drawable = gimp_object_queue_pop (queue))) + { + gimp_drawable_apply_operation (drawable, sub_progress, + _("Dithering"), + dither); + } + + g_object_unref (queue); + + if (progress) + gimp_progress_end (progress); + + g_object_unref (dither); + } +} diff --git a/app/core/gimpimage-convert-precision.h b/app/core/gimpimage-convert-precision.h new file mode 100644 index 0000000..cd3a010 --- /dev/null +++ b/app/core/gimpimage-convert-precision.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-convert-precision.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_CONVERT_PRECISION_H__ +#define __GIMP_IMAGE_CONVERT_PRECISION_H__ + + +void gimp_image_convert_precision (GimpImage *image, + GimpPrecision precision, + GeglDitherMethod layer_dither_type, + GeglDitherMethod text_layer_dither_type, + GeglDitherMethod mask_dither_type, + GimpProgress *progress); + +void gimp_image_convert_dither_u8 (GimpImage *image, + GimpProgress *progress); + + +#endif /* __GIMP_IMAGE_CONVERT_PRECISION_H__ */ diff --git a/app/core/gimpimage-convert-type.c b/app/core/gimpimage-convert-type.c new file mode 100644 index 0000000..4298a49 --- /dev/null +++ b/app/core/gimpimage-convert-type.c @@ -0,0 +1,162 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimp.h" +#include "gimpdrawable.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-convert-type.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +gboolean +gimp_image_convert_type (GimpImage *image, + GimpImageBaseType new_type, + GimpColorProfile *dest_profile, + GimpProgress *progress, + GError **error) +{ + GimpImageBaseType old_type; + const Babl *new_layer_format; + GimpObjectQueue *queue; + GList *all_layers; + GimpDrawable *drawable; + const gchar *undo_desc = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (new_type != gimp_image_get_base_type (image), FALSE); + g_return_val_if_fail (new_type != GIMP_INDEXED, FALSE); + g_return_val_if_fail (gimp_babl_is_valid (new_type, + gimp_image_get_precision (image)), + FALSE); + g_return_val_if_fail (dest_profile == NULL || GIMP_IS_COLOR_PROFILE (dest_profile), + FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + new_layer_format = gimp_babl_format (new_type, + gimp_image_get_precision (image), + TRUE); + + if (dest_profile && + ! gimp_image_validate_color_profile_by_format (new_layer_format, + dest_profile, + NULL, error)) + { + return FALSE; + } + + switch (new_type) + { + case GIMP_RGB: + undo_desc = C_("undo-type", "Convert Image to RGB"); + break; + + case GIMP_GRAY: + undo_desc = C_("undo-type", "Convert Image to Grayscale"); + break; + + default: + g_return_val_if_reached (FALSE); + } + + gimp_set_busy (image->gimp); + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + all_layers = gimp_image_get_layer_list (image); + gimp_object_queue_push_list (queue, all_layers); + g_list_free (all_layers); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, + undo_desc); + + /* Push the image type to the stack */ + gimp_image_undo_push_image_type (image, NULL); + + /* Set the new base type */ + old_type = gimp_image_get_base_type (image); + + g_object_set (image, "base-type", new_type, NULL); + + /* When converting to/from GRAY, convert to the new type's builtin + * profile if none was passed. + */ + if (old_type == GIMP_GRAY || + new_type == GIMP_GRAY) + { + if (! dest_profile && gimp_image_get_color_profile (image)) + dest_profile = gimp_image_get_builtin_color_profile (image); + } + + while ((drawable = gimp_object_queue_pop (queue))) + { + gimp_drawable_convert_type (drawable, image, + new_type, + gimp_drawable_get_precision (drawable), + gimp_drawable_has_alpha (drawable), + dest_profile, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + TRUE, progress); + } + + if (old_type == GIMP_INDEXED) + gimp_image_unset_colormap (image, TRUE); + + /* When converting to/from GRAY, set the new profile. + */ + if (old_type == GIMP_GRAY || + new_type == GIMP_GRAY) + { + if (gimp_image_get_color_profile (image)) + gimp_image_set_color_profile (image, dest_profile, NULL); + else + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); + } + + gimp_image_undo_group_end (image); + + gimp_image_mode_changed (image); + g_object_thaw_notify (G_OBJECT (image)); + + g_object_unref (queue); + + gimp_unset_busy (image->gimp); + + return TRUE; +} diff --git a/app/core/gimpimage-convert-type.h b/app/core/gimpimage-convert-type.h new file mode 100644 index 0000000..53c6950 --- /dev/null +++ b/app/core/gimpimage-convert-type.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 . + */ + +#ifndef __GIMP_IMAGE_CONVERT_TYPE_H__ +#define __GIMP_IMAGE_CONVERT_TYPE_H__ + + +gboolean gimp_image_convert_type (GimpImage *image, + GimpImageBaseType new_type, + GimpColorProfile *dest_profile, + GimpProgress *progress, + GError **error); + + +#endif /* __GIMP_IMAGE_CONVERT_TYPE_H__ */ diff --git a/app/core/gimpimage-crop.c b/app/core/gimpimage-crop.c new file mode 100644 index 0000000..c6814d4 --- /dev/null +++ b/app/core/gimpimage-crop.c @@ -0,0 +1,234 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-crop.h" +#include "gimpimage-guides.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimpsamplepoint.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +gimp_image_crop (GimpImage *image, + GimpContext *context, + GimpFillType fill_type, + gint x, + gint y, + gint width, + gint height, + gboolean crop_layers) +{ + GList *list; + gint previous_width; + gint previous_height; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + previous_width = gimp_image_get_width (image); + previous_height = gimp_image_get_height (image); + + /* Make sure new width and height are non-zero */ + if (width < 1 || height < 1) + return; + + gimp_set_busy (image->gimp); + + g_object_freeze_notify (G_OBJECT (image)); + + if (crop_layers) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CROP, + C_("undo-type", "Crop Image")); + else + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_RESIZE, + C_("undo-type", "Resize Image")); + + /* Push the image size to the stack */ + gimp_image_undo_push_image_size (image, NULL, + x, y, width, height); + + /* Set the new width and height */ + g_object_set (image, + "width", width, + "height", height, + NULL); + + /* Resize all channels */ + for (list = gimp_image_get_channel_iter (image); + list; + list = g_list_next (list)) + { + GimpItem *item = list->data; + + gimp_item_resize (item, context, GIMP_FILL_TRANSPARENT, + width, height, -x, -y); + } + + /* Resize all vectors */ + for (list = gimp_image_get_vectors_iter (image); + list; + list = g_list_next (list)) + { + GimpItem *item = list->data; + + gimp_item_resize (item, context, GIMP_FILL_TRANSPARENT, + width, height, -x, -y); + } + + /* Don't forget the selection mask! */ + gimp_item_resize (GIMP_ITEM (gimp_image_get_mask (image)), + context, GIMP_FILL_TRANSPARENT, + width, height, -x, -y); + + /* crop all layers */ + list = gimp_image_get_layer_iter (image); + + while (list) + { + GimpItem *item = list->data; + + list = g_list_next (list); + + gimp_item_translate (item, -x, -y, TRUE); + + if (crop_layers && ! gimp_item_is_content_locked (item)) + { + gint off_x, off_y; + gint lx1, ly1, lx2, ly2; + + gimp_item_get_offset (item, &off_x, &off_y); + + lx1 = CLAMP (off_x, 0, gimp_image_get_width (image)); + ly1 = CLAMP (off_y, 0, gimp_image_get_height (image)); + lx2 = CLAMP (gimp_item_get_width (item) + off_x, + 0, gimp_image_get_width (image)); + ly2 = CLAMP (gimp_item_get_height (item) + off_y, + 0, gimp_image_get_height (image)); + + width = lx2 - lx1; + height = ly2 - ly1; + + if (width > 0 && height > 0) + { + gimp_item_resize (item, context, fill_type, + width, height, + -(lx1 - off_x), + -(ly1 - off_y)); + } + else + { + gimp_image_remove_layer (image, GIMP_LAYER (item), + TRUE, NULL); + } + } + } + + /* Reposition or remove guides */ + list = gimp_image_get_guides (image); + + while (list) + { + GimpGuide *guide = list->data; + gboolean remove_guide = FALSE; + gint position = gimp_guide_get_position (guide); + + list = g_list_next (list); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + position -= y; + if ((position < 0) || (position > height)) + remove_guide = TRUE; + break; + + case GIMP_ORIENTATION_VERTICAL: + position -= x; + if ((position < 0) || (position > width)) + remove_guide = TRUE; + break; + + default: + break; + } + + if (remove_guide) + gimp_image_remove_guide (image, guide, TRUE); + else if (position != gimp_guide_get_position (guide)) + gimp_image_move_guide (image, guide, position, TRUE); + } + + /* Reposition or remove sample points */ + list = gimp_image_get_sample_points (image); + + while (list) + { + GimpSamplePoint *sample_point = list->data; + gboolean remove_sample_point = FALSE; + gint old_x; + gint old_y; + gint new_x; + gint new_y; + + list = g_list_next (list); + + gimp_sample_point_get_position (sample_point, &old_x, &old_y); + new_x = old_x; + new_y = old_y; + + new_y -= y; + if ((new_y < 0) || (new_y > height)) + remove_sample_point = TRUE; + + new_x -= x; + if ((new_x < 0) || (new_x > width)) + remove_sample_point = TRUE; + + if (remove_sample_point) + gimp_image_remove_sample_point (image, sample_point, TRUE); + else if (new_x != old_x || new_y != old_y) + gimp_image_move_sample_point (image, sample_point, + new_x, new_y, TRUE); + } + + gimp_image_undo_group_end (image); + + gimp_image_size_changed_detailed (image, + -x, -y, + previous_width, previous_height); + + g_object_thaw_notify (G_OBJECT (image)); + + gimp_unset_busy (image->gimp); +} diff --git a/app/core/gimpimage-crop.h b/app/core/gimpimage-crop.h new file mode 100644 index 0000000..19304da --- /dev/null +++ b/app/core/gimpimage-crop.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 . + */ + +#ifndef __GIMP_IMAGE_CROP_H__ +#define __GIMP_IMAGE_CROP_H__ + + +void gimp_image_crop (GimpImage *image, + GimpContext *context, + GimpFillType fill_type, + gint x, + gint y, + gint width, + gint height, + gboolean crop_layers); + + +#endif /* __GIMP_IMAGE_CROP_H__ */ diff --git a/app/core/gimpimage-duplicate.c b/app/core/gimpimage-duplicate.c new file mode 100644 index 0000000..4dcaedd --- /dev/null +++ b/app/core/gimpimage-duplicate.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "vectors/gimpvectors.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-duplicate.h" +#include "gimpimage-grid.h" +#include "gimpimage-guides.h" +#include "gimpimage-metadata.h" +#include "gimpimage-private.h" +#include "gimpimage-undo.h" +#include "gimpimage-sample-points.h" +#include "gimpitemstack.h" +#include "gimplayer.h" +#include "gimplayermask.h" +#include "gimplayer-floating-selection.h" +#include "gimpparasitelist.h" +#include "gimpsamplepoint.h" + + +static void gimp_image_duplicate_resolution (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_save_source_file (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_colormap (GimpImage *image, + GimpImage *new_image); +static GimpItem * gimp_image_duplicate_item (GimpItem *item, + GimpImage *new_image); +static GimpLayer * gimp_image_duplicate_layers (GimpImage *image, + GimpImage *new_image); +static GimpChannel * gimp_image_duplicate_channels (GimpImage *image, + GimpImage *new_image); +static GimpVectors * gimp_image_duplicate_vectors (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_floating_sel (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_mask (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_components (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_guides (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_sample_points (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_grid (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_metadata (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_quick_mask (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_parasites (GimpImage *image, + GimpImage *new_image); +static void gimp_image_duplicate_color_profile (GimpImage *image, + GimpImage *new_image); + + +GimpImage * +gimp_image_duplicate (GimpImage *image) +{ + GimpImage *new_image; + GimpLayer *active_layer; + GimpChannel *active_channel; + GimpVectors *active_vectors; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + gimp_set_busy_until_idle (image->gimp); + + /* Create a new image */ + new_image = gimp_create_image (image->gimp, + gimp_image_get_width (image), + gimp_image_get_height (image), + gimp_image_get_base_type (image), + gimp_image_get_precision (image), + FALSE); + gimp_image_undo_disable (new_image); + + /* Store the source uri to be used by the save dialog */ + gimp_image_duplicate_save_source_file (image, new_image); + + /* Copy the colormap if necessary */ + gimp_image_duplicate_colormap (image, new_image); + + /* Copy resolution information */ + gimp_image_duplicate_resolution (image, new_image); + + /* Copy parasites first so we have a color profile */ + gimp_image_duplicate_parasites (image, new_image); + gimp_image_duplicate_color_profile (image, new_image); + + /* Copy the layers */ + active_layer = gimp_image_duplicate_layers (image, new_image); + + /* Copy the channels */ + active_channel = gimp_image_duplicate_channels (image, new_image); + + /* Copy any vectors */ + active_vectors = gimp_image_duplicate_vectors (image, new_image); + + /* Copy floating layer */ + gimp_image_duplicate_floating_sel (image, new_image); + + /* Copy the selection mask */ + gimp_image_duplicate_mask (image, new_image); + + /* Set active layer, active channel, active vectors */ + if (active_layer) + gimp_image_set_active_layer (new_image, active_layer); + + if (active_channel) + gimp_image_set_active_channel (new_image, active_channel); + + if (active_vectors) + gimp_image_set_active_vectors (new_image, active_vectors); + + /* Copy state of all color components */ + gimp_image_duplicate_components (image, new_image); + + /* Copy any guides */ + gimp_image_duplicate_guides (image, new_image); + + /* Copy any sample points */ + gimp_image_duplicate_sample_points (image, new_image); + + /* Copy the grid */ + gimp_image_duplicate_grid (image, new_image); + + /* Copy the metadata */ + gimp_image_duplicate_metadata (image, new_image); + + /* Copy the quick mask info */ + gimp_image_duplicate_quick_mask (image, new_image); + + gimp_image_undo_enable (new_image); + + /* Explicitly mark image as dirty, so that its dirty time is set */ + gimp_image_dirty (new_image, GIMP_DIRTY_ALL); + + return new_image; +} + +static void +gimp_image_duplicate_resolution (GimpImage *image, + GimpImage *new_image) +{ + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + gimp_image_set_resolution (new_image, xres, yres); + gimp_image_set_unit (new_image, gimp_image_get_unit (image)); +} + +static void +gimp_image_duplicate_save_source_file (GimpImage *image, + GimpImage *new_image) +{ + GFile *file = gimp_image_get_file (image); + + if (file) + g_object_set_data_full (G_OBJECT (new_image), "gimp-image-source-file", + g_object_ref (file), + (GDestroyNotify) g_object_unref); +} + +static void +gimp_image_duplicate_colormap (GimpImage *image, + GimpImage *new_image) +{ + if (gimp_image_get_base_type (new_image) == GIMP_INDEXED) + gimp_image_set_colormap (new_image, + gimp_image_get_colormap (image), + gimp_image_get_colormap_size (image), + FALSE); +} + +static GimpItem * +gimp_image_duplicate_item (GimpItem *item, + GimpImage *new_image) +{ + GimpItem *new_item; + + new_item = gimp_item_convert (item, new_image, + G_TYPE_FROM_INSTANCE (item)); + + /* Make sure the copied item doesn't say: " copy" */ + gimp_object_set_name (GIMP_OBJECT (new_item), + gimp_object_get_name (item)); + + return new_item; +} + +static GimpLayer * +gimp_image_duplicate_layers (GimpImage *image, + GimpImage *new_image) +{ + GimpLayer *active_layer = NULL; + GList *list; + gint count; + + for (list = gimp_image_get_layer_iter (image), count = 0; + list; + list = g_list_next (list)) + { + GimpLayer *layer = list->data; + GimpLayer *new_layer; + + if (gimp_layer_is_floating_sel (layer)) + continue; + + new_layer = GIMP_LAYER (gimp_image_duplicate_item (GIMP_ITEM (layer), + new_image)); + + /* Make sure that if the layer has a layer mask, + * its name isn't screwed up + */ + if (new_layer->mask) + gimp_object_set_name (GIMP_OBJECT (new_layer->mask), + gimp_object_get_name (layer->mask)); + + if (gimp_image_get_active_layer (image) == layer) + active_layer = new_layer; + + gimp_image_add_layer (new_image, new_layer, + NULL, count++, FALSE); + } + + return active_layer; +} + +static GimpChannel * +gimp_image_duplicate_channels (GimpImage *image, + GimpImage *new_image) +{ + GimpChannel *active_channel = NULL; + GList *list; + gint count; + + for (list = gimp_image_get_channel_iter (image), count = 0; + list; + list = g_list_next (list)) + { + GimpChannel *channel = list->data; + GimpChannel *new_channel; + + new_channel = GIMP_CHANNEL (gimp_image_duplicate_item (GIMP_ITEM (channel), + new_image)); + + if (gimp_image_get_active_channel (image) == channel) + active_channel = new_channel; + + gimp_image_add_channel (new_image, new_channel, + NULL, count++, FALSE); + } + + return active_channel; +} + +static GimpVectors * +gimp_image_duplicate_vectors (GimpImage *image, + GimpImage *new_image) +{ + GimpVectors *active_vectors = NULL; + GList *list; + gint count; + + for (list = gimp_image_get_vectors_iter (image), count = 0; + list; + list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + GimpVectors *new_vectors; + + new_vectors = GIMP_VECTORS (gimp_image_duplicate_item (GIMP_ITEM (vectors), + new_image)); + + if (gimp_image_get_active_vectors (image) == vectors) + active_vectors = new_vectors; + + gimp_image_add_vectors (new_image, new_vectors, + NULL, count++, FALSE); + } + + return active_vectors; +} + +static void +gimp_image_duplicate_floating_sel (GimpImage *image, + GimpImage *new_image) +{ + GimpLayer *floating_sel; + GimpDrawable *floating_sel_drawable; + GList *floating_sel_path; + GimpItemStack *new_item_stack; + GimpLayer *new_floating_sel; + GimpDrawable *new_floating_sel_drawable; + + floating_sel = gimp_image_get_floating_selection (image); + + if (! floating_sel) + return; + + floating_sel_drawable = gimp_layer_get_floating_sel_drawable (floating_sel); + + if (GIMP_IS_LAYER_MASK (floating_sel_drawable)) + { + GimpLayer *layer; + + layer = gimp_layer_mask_get_layer (GIMP_LAYER_MASK (floating_sel_drawable)); + + floating_sel_path = gimp_item_get_path (GIMP_ITEM (layer)); + + new_item_stack = GIMP_ITEM_STACK (gimp_image_get_layers (new_image)); + } + else + { + floating_sel_path = gimp_item_get_path (GIMP_ITEM (floating_sel_drawable)); + + if (GIMP_IS_LAYER (floating_sel_drawable)) + new_item_stack = GIMP_ITEM_STACK (gimp_image_get_layers (new_image)); + else + new_item_stack = GIMP_ITEM_STACK (gimp_image_get_channels (new_image)); + } + + /* adjust path[0] for the floating layer missing in new_image */ + floating_sel_path->data = + GUINT_TO_POINTER (GPOINTER_TO_UINT (floating_sel_path->data) - 1); + + if (GIMP_IS_LAYER (floating_sel_drawable)) + { + new_floating_sel = + GIMP_LAYER (gimp_image_duplicate_item (GIMP_ITEM (floating_sel), + new_image)); + } + else + { + /* can't use gimp_item_convert() for floating selections of channels + * or layer masks because they maybe don't have a normal layer's type + */ + new_floating_sel = + GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (floating_sel), + G_TYPE_FROM_INSTANCE (floating_sel))); + gimp_item_set_image (GIMP_ITEM (new_floating_sel), new_image); + + gimp_object_set_name (GIMP_OBJECT (new_floating_sel), + gimp_object_get_name (floating_sel)); + } + + /* Make sure the copied layer doesn't say: " copy" */ + gimp_object_set_name (GIMP_OBJECT (new_floating_sel), + gimp_object_get_name (floating_sel)); + + new_floating_sel_drawable = + GIMP_DRAWABLE (gimp_item_stack_get_item_by_path (new_item_stack, + floating_sel_path)); + + if (GIMP_IS_LAYER_MASK (floating_sel_drawable)) + new_floating_sel_drawable = + GIMP_DRAWABLE (gimp_layer_get_mask (GIMP_LAYER (new_floating_sel_drawable))); + + floating_sel_attach (new_floating_sel, new_floating_sel_drawable); + + g_list_free (floating_sel_path); +} + +static void +gimp_image_duplicate_mask (GimpImage *image, + GimpImage *new_image) +{ + GimpDrawable *mask; + GimpDrawable *new_mask; + + mask = GIMP_DRAWABLE (gimp_image_get_mask (image)); + new_mask = GIMP_DRAWABLE (gimp_image_get_mask (new_image)); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (mask), NULL, GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (new_mask), NULL); + + GIMP_CHANNEL (new_mask)->bounds_known = FALSE; + GIMP_CHANNEL (new_mask)->boundary_known = FALSE; +} + +static void +gimp_image_duplicate_components (GimpImage *image, + GimpImage *new_image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpImagePrivate *new_private = GIMP_IMAGE_GET_PRIVATE (new_image); + gint count; + + for (count = 0; count < MAX_CHANNELS; count++) + { + new_private->visible[count] = private->visible[count]; + new_private->active[count] = private->active[count]; + } +} + +static void +gimp_image_duplicate_guides (GimpImage *image, + GimpImage *new_image) +{ + GList *list; + + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_add_hguide (new_image, position, FALSE); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_image_add_vguide (new_image, position, FALSE); + break; + + default: + g_error ("Unknown guide orientation.\n"); + } + } +} + +static void +gimp_image_duplicate_sample_points (GimpImage *image, + GimpImage *new_image) +{ + GList *list; + + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + GimpSamplePoint *sample_point = list->data; + gint x; + gint y; + + gimp_sample_point_get_position (sample_point, &x, &y); + + gimp_image_add_sample_point_at_pos (new_image, x, y, FALSE); + } +} + +static void +gimp_image_duplicate_grid (GimpImage *image, + GimpImage *new_image) +{ + if (gimp_image_get_grid (image)) + gimp_image_set_grid (new_image, gimp_image_get_grid (image), FALSE); +} + +static void +gimp_image_duplicate_metadata (GimpImage *image, + GimpImage *new_image) +{ + GimpMetadata *metadata = gimp_image_get_metadata (image); + + if (metadata) + { + metadata = gimp_metadata_duplicate (metadata); + gimp_image_set_metadata (new_image, metadata, FALSE); + g_object_unref (metadata); + } +} + +static void +gimp_image_duplicate_quick_mask (GimpImage *image, + GimpImage *new_image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpImagePrivate *new_private = GIMP_IMAGE_GET_PRIVATE (new_image); + + new_private->quick_mask_state = private->quick_mask_state; + new_private->quick_mask_inverted = private->quick_mask_inverted; + new_private->quick_mask_color = private->quick_mask_color; +} + +static void +gimp_image_duplicate_parasites (GimpImage *image, + GimpImage *new_image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpImagePrivate *new_private = GIMP_IMAGE_GET_PRIVATE (new_image); + + if (private->parasites) + { + g_object_unref (new_private->parasites); + new_private->parasites = gimp_parasite_list_copy (private->parasites); + } +} + +static void +gimp_image_duplicate_color_profile (GimpImage *image, + GimpImage *new_image) +{ + GimpColorProfile *profile = gimp_image_get_color_profile (image); + gboolean is_color_managed = gimp_image_get_is_color_managed (image); + + gimp_image_set_color_profile (new_image, profile, NULL); + gimp_image_set_is_color_managed (new_image, is_color_managed, FALSE); +} diff --git a/app/core/gimpimage-duplicate.h b/app/core/gimpimage-duplicate.h new file mode 100644 index 0000000..cbf55a2 --- /dev/null +++ b/app/core/gimpimage-duplicate.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 . + */ + +#ifndef __GIMP_IMAGE_DUPLICATE_H__ +#define __GIMP_IMAGE_DUPLICATE_H__ + + +GimpImage * gimp_image_duplicate (GimpImage *image); + + +#endif /* __GIMP_IMAGE_DUPLICATE_H__ */ diff --git a/app/core/gimpimage-flip.c b/app/core/gimpimage-flip.c new file mode 100644 index 0000000..5db05fe --- /dev/null +++ b/app/core/gimpimage-flip.c @@ -0,0 +1,276 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-flip.h" +#include "gimpimage-guides.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" +#include "gimpsamplepoint.h" + + +/* local function prototypes */ + +static void gimp_image_flip_guides (GimpImage *image, + GimpOrientationType flip_type, + gdouble axis); +static void gimp_image_flip_sample_points (GimpImage *image, + GimpOrientationType flip_type, + gdouble axis); + + +/* private functions */ + +static void +gimp_image_flip_guides (GimpImage *image, + GimpOrientationType flip_type, + gdouble axis) +{ + gint width = gimp_image_get_width (image); + gint height = gimp_image_get_height (image); + GList *iter; + + for (iter = gimp_image_get_guides (image); iter;) + { + GimpGuide *guide = iter->data; + gint position = gimp_guide_get_position (guide); + + iter = g_list_next (iter); + + position = SIGNED_ROUND (2.0 * axis - position); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + if (flip_type == GIMP_ORIENTATION_VERTICAL) + { + if (position >= 0 && position <= height) + gimp_image_move_guide (image, guide, position, TRUE); + else + gimp_image_remove_guide (image, guide, TRUE); + } + break; + + case GIMP_ORIENTATION_VERTICAL: + if (flip_type == GIMP_ORIENTATION_HORIZONTAL) + { + if (position >= 0 && position <= width) + gimp_image_move_guide (image, guide, position, TRUE); + else + gimp_image_remove_guide (image, guide, TRUE); + } + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_if_reached (); + } + } +} + +static void +gimp_image_flip_sample_points (GimpImage *image, + GimpOrientationType flip_type, + gdouble axis) +{ + gint width = gimp_image_get_width (image); + gint height = gimp_image_get_height (image); + GList *iter; + + for (iter = gimp_image_get_sample_points (image); iter;) + { + GimpSamplePoint *sample_point = iter->data; + gint x; + gint y; + + iter = g_list_next (iter); + + gimp_sample_point_get_position (sample_point, &x, &y); + + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + x = SIGNED_ROUND (2.0 * axis - x); + break; + + case GIMP_ORIENTATION_VERTICAL: + y = SIGNED_ROUND (2.0 * axis - y); + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_if_reached (); + } + + if (x >= 0 && x < width && + y >= 0 && y < height) + { + gimp_image_move_sample_point (image, sample_point, x, y, TRUE); + } + else + { + gimp_image_remove_sample_point (image, sample_point, TRUE); + } + } +} + + +/* public functions */ + +void +gimp_image_flip (GimpImage *image, + GimpContext *context, + GimpOrientationType flip_type, + GimpProgress *progress) +{ + gdouble axis = 0.0; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + axis = gimp_image_get_width (image) / 2.0; + break; + + case GIMP_ORIENTATION_VERTICAL: + axis = gimp_image_get_height (image) / 2.0; + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_if_reached (); + } + + gimp_image_flip_full (image, context, flip_type, axis, + GIMP_TRANSFORM_RESIZE_CLIP, progress); +} + +void +gimp_image_flip_full (GimpImage *image, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GimpItem *item; + gint width; + gint height; + gint offset_x = 0; + gint offset_y = 0; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + + if (! clip_result) + { + switch (flip_type) + { + case GIMP_ORIENTATION_HORIZONTAL: + offset_x = SIGNED_ROUND (2.0 * axis - width); + axis = width / 2.0; + break; + + case GIMP_ORIENTATION_VERTICAL: + offset_y = SIGNED_ROUND (2.0 * axis - height); + axis = height / 2.0; + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_if_reached (); + } + } + + gimp_set_busy (image->gimp); + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, gimp_image_get_layers (image)); + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + gimp_object_queue_push_container (queue, gimp_image_get_vectors (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_FLIP, NULL); + + /* Flip all layers, channels (including selection mask), and vectors */ + while ((item = gimp_object_queue_pop (queue))) + { + gboolean clip = FALSE; + + if (GIMP_IS_CHANNEL (item)) + clip = clip_result; + + gimp_item_flip (item, context, flip_type, axis, clip); + + gimp_progress_set_value (progress, 1.0); + } + + /* Flip all Guides */ + gimp_image_flip_guides (image, flip_type, axis); + + /* Flip all sample points */ + gimp_image_flip_sample_points (image, flip_type, axis); + + if (offset_x || offset_y) + { + gimp_image_undo_push_image_size (image, + NULL, + offset_x, + offset_y, + width, + height); + } + + gimp_image_undo_group_end (image); + + g_object_unref (queue); + + if (offset_x || offset_y) + { + gimp_image_size_changed_detailed (image, + -offset_x, + -offset_y, + width, + height); + } + + gimp_unset_busy (image->gimp); +} diff --git a/app/core/gimpimage-flip.h b/app/core/gimpimage-flip.h new file mode 100644 index 0000000..5fa6ce6 --- /dev/null +++ b/app/core/gimpimage-flip.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_FLIP_H__ +#define __GIMP_IMAGE_FLIP_H__ + + +void gimp_image_flip (GimpImage *image, + GimpContext *context, + GimpOrientationType flip_type, + GimpProgress *progress); +void gimp_image_flip_full (GimpImage *image, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result, + GimpProgress *progress); + + +#endif /* __GIMP_IMAGE_FLIP_H__ */ diff --git a/app/core/gimpimage-grid.c b/app/core/gimpimage-grid.c new file mode 100644 index 0000000..dec1850 --- /dev/null +++ b/app/core/gimpimage-grid.c @@ -0,0 +1,67 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpGrid + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpgrid.h" +#include "gimpimage.h" +#include "gimpimage-grid.h" +#include "gimpimage-private.h" +#include "gimpimage-undo-push.h" + +#include "gimp-intl.h" + + +GimpGrid * +gimp_image_get_grid (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->grid; +} + +void +gimp_image_set_grid (GimpImage *image, + GimpGrid *grid, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GRID (grid)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (gimp_config_is_equal_to (GIMP_CONFIG (private->grid), GIMP_CONFIG (grid))) + return; + + if (push_undo) + gimp_image_undo_push_image_grid (image, + C_("undo-type", "Grid"), private->grid); + + gimp_config_sync (G_OBJECT (grid), G_OBJECT (private->grid), 0); +} diff --git a/app/core/gimpimage-grid.h b/app/core/gimpimage-grid.h new file mode 100644 index 0000000..c2a4a3f --- /dev/null +++ b/app/core/gimpimage-grid.h @@ -0,0 +1,31 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * GimpGrid + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_GRID_H__ +#define __GIMP_IMAGE_GRID_H__ + + +GimpGrid * gimp_image_get_grid (GimpImage *image); +void gimp_image_set_grid (GimpImage *image, + GimpGrid *grid, + gboolean push_undo); + + +#endif /* __GIMP_IMAGE_GRID_H__ */ diff --git a/app/core/gimpimage-guides.c b/app/core/gimpimage-guides.c new file mode 100644 index 0000000..a265d1e --- /dev/null +++ b/app/core/gimpimage-guides.c @@ -0,0 +1,216 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpimage.h" +#include "gimpguide.h" +#include "gimpimage-guides.h" +#include "gimpimage-private.h" +#include "gimpimage-undo-push.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GimpGuide * +gimp_image_add_hguide (GimpImage *image, + gint position, + gboolean push_undo) +{ + GimpGuide *guide; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (position >= 0 && + position <= gimp_image_get_height (image), NULL); + + guide = gimp_guide_new (GIMP_ORIENTATION_HORIZONTAL, + image->gimp->next_guide_ID++); + + if (push_undo) + gimp_image_undo_push_guide (image, + C_("undo-type", "Add Horizontal Guide"), guide); + + gimp_image_add_guide (image, guide, position); + g_object_unref (G_OBJECT (guide)); + + return guide; +} + +GimpGuide * +gimp_image_add_vguide (GimpImage *image, + gint position, + gboolean push_undo) +{ + GimpGuide *guide; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (position >= 0 && + position <= gimp_image_get_width (image), NULL); + + guide = gimp_guide_new (GIMP_ORIENTATION_VERTICAL, + image->gimp->next_guide_ID++); + + if (push_undo) + gimp_image_undo_push_guide (image, + C_("undo-type", "Add Vertical Guide"), guide); + + gimp_image_add_guide (image, guide, position); + g_object_unref (guide); + + return guide; +} + +void +gimp_image_add_guide (GimpImage *image, + GimpGuide *guide, + gint position) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->guides = g_list_prepend (private->guides, guide); + + gimp_guide_set_position (guide, position); + g_object_ref (guide); + + gimp_image_guide_added (image, guide); +} + +void +gimp_image_remove_guide (GimpImage *image, + GimpGuide *guide, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (gimp_guide_is_custom (guide)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_guide (image, C_("undo-type", "Remove Guide"), guide); + + private->guides = g_list_remove (private->guides, guide); + gimp_aux_item_removed (GIMP_AUX_ITEM (guide)); + + gimp_image_guide_removed (image, guide); + + gimp_guide_set_position (guide, GIMP_GUIDE_POSITION_UNDEFINED); + g_object_unref (guide); +} + +void +gimp_image_move_guide (GimpImage *image, + GimpGuide *guide, + gint position, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + g_return_if_fail (position >= 0); + + if (gimp_guide_get_orientation (guide) == GIMP_ORIENTATION_HORIZONTAL) + g_return_if_fail (position <= gimp_image_get_height (image)); + else + g_return_if_fail (position <= gimp_image_get_width (image)); + + if (gimp_guide_is_custom (guide)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_push_guide (image, C_("undo-type", "Move Guide"), guide); + + gimp_guide_set_position (guide, position); + + gimp_image_guide_moved (image, guide); +} + +GList * +gimp_image_get_guides (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->guides; +} + +GimpGuide * +gimp_image_get_guide (GimpImage *image, + guint32 id) +{ + GList *guides; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + for (guides = GIMP_IMAGE_GET_PRIVATE (image)->guides; + guides; + guides = g_list_next (guides)) + { + GimpGuide *guide = guides->data; + + if (gimp_aux_item_get_ID (GIMP_AUX_ITEM (guide)) == id) + return guide; + } + + return NULL; +} + +GimpGuide * +gimp_image_get_next_guide (GimpImage *image, + guint32 id, + gboolean *guide_found) +{ + GList *guides; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (guide_found != NULL, NULL); + + if (id == 0) + *guide_found = TRUE; + else + *guide_found = FALSE; + + for (guides = GIMP_IMAGE_GET_PRIVATE (image)->guides; + guides; + guides = g_list_next (guides)) + { + GimpGuide *guide = guides->data; + + if (*guide_found) /* this is the first guide after the found one */ + return guide; + + if (gimp_aux_item_get_ID (GIMP_AUX_ITEM (guide)) == id) /* found it, next one will be returned */ + *guide_found = TRUE; + } + + return NULL; +} diff --git a/app/core/gimpimage-guides.h b/app/core/gimpimage-guides.h new file mode 100644 index 0000000..7f9706e --- /dev/null +++ b/app/core/gimpimage-guides.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 . + */ + +#ifndef __GIMP_IMAGE_GUIDES_H__ +#define __GIMP_IMAGE_GUIDES_H__ + + +/* public guide adding API + */ +GimpGuide * gimp_image_add_hguide (GimpImage *image, + gint position, + gboolean push_undo); +GimpGuide * gimp_image_add_vguide (GimpImage *image, + gint position, + gboolean push_undo); + +/* internal guide adding API, does not check the guide's position and + * is publicly declared only to be used from undo + */ +void gimp_image_add_guide (GimpImage *image, + GimpGuide *guide, + gint position); + +void gimp_image_remove_guide (GimpImage *image, + GimpGuide *guide, + gboolean push_undo); +void gimp_image_move_guide (GimpImage *image, + GimpGuide *guide, + gint position, + gboolean push_undo); + +GList * gimp_image_get_guides (GimpImage *image); +GimpGuide * gimp_image_get_guide (GimpImage *image, + guint32 id); +GimpGuide * gimp_image_get_next_guide (GimpImage *image, + guint32 id, + gboolean *guide_found); + + +#endif /* __GIMP_IMAGE_GUIDES_H__ */ diff --git a/app/core/gimpimage-item-list.c b/app/core/gimpimage-item-list.c new file mode 100644 index 0000000..6b6bad4 --- /dev/null +++ b/app/core/gimpimage-item-list.c @@ -0,0 +1,401 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpcontext.h" +#include "gimpimage.h" +#include "gimpimage-item-list.h" +#include "gimpimage-undo.h" +#include "gimpitem.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* public functions */ + +gboolean +gimp_image_item_list_bounds (GimpImage *image, + GList *list, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GList *l; + gboolean bounds = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (x != 0, FALSE); + g_return_val_if_fail (y != 0, FALSE); + g_return_val_if_fail (width != 0, FALSE); + g_return_val_if_fail (height != 0, FALSE); + + for (l = list; l; l = g_list_next (l)) + { + GimpItem *item = l->data; + gint tmp_x, tmp_y; + gint tmp_w, tmp_h; + + if (gimp_item_bounds (item, &tmp_x, &tmp_y, &tmp_w, &tmp_h)) + { + gint off_x, off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + if (bounds) + { + gimp_rectangle_union (*x, *y, *width, *height, + tmp_x + off_x, tmp_y + off_y, + tmp_w, tmp_h, + x, y, width, height); + } + else + { + *x = tmp_x + off_x; + *y = tmp_y + off_y; + *width = tmp_w; + *height = tmp_h; + + bounds = TRUE; + } + } + } + + if (! bounds) + { + *x = 0; + *y = 0; + *width = gimp_image_get_width (image); + *height = gimp_image_get_height (image); + } + + return bounds; +} + +void +gimp_image_item_list_translate (GimpImage *image, + GList *list, + gint offset_x, + gint offset_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + if (list) + { + GList *l; + + if (list->next) + { + if (push_undo) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, + C_("undo-type", "Translate Items")); + } + + for (l = list; l; l = g_list_next (l)) + gimp_item_start_transform (GIMP_ITEM (l->data), push_undo); + } + + for (l = list; l; l = g_list_next (l)) + gimp_item_translate (GIMP_ITEM (l->data), + offset_x, offset_y, push_undo); + + if (list->next) + { + for (l = list; l; l = g_list_next (l)) + gimp_item_end_transform (GIMP_ITEM (l->data), push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); + } + } +} + +void +gimp_image_item_list_flip (GimpImage *image, + GList *list, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (list) + { + GList *l; + + if (list->next) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Flip Items")); + + for (l = list; l; l = g_list_next (l)) + gimp_item_start_transform (GIMP_ITEM (l->data), TRUE); + } + + for (l = list; l; l = g_list_next (l)) + { + GimpItem *item = l->data; + + gimp_item_flip (item, context, + flip_type, axis, + gimp_item_get_clip (item, clip_result)); + } + + if (list->next) + { + for (l = list; l; l = g_list_next (l)) + gimp_item_end_transform (GIMP_ITEM (l->data), TRUE); + + gimp_image_undo_group_end (image); + } + } +} + +void +gimp_image_item_list_rotate (GimpImage *image, + GList *list, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (list) + { + GList *l; + + if (list->next) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Rotate Items")); + + for (l = list; l; l = g_list_next (l)) + gimp_item_start_transform (GIMP_ITEM (l->data), TRUE); + } + + for (l = list; l; l = g_list_next (l)) + { + GimpItem *item = l->data; + + gimp_item_rotate (item, context, + rotate_type, center_x, center_y, + gimp_item_get_clip (item, clip_result)); + } + + if (list->next) + { + for (l = list; l; l = g_list_next (l)) + gimp_item_end_transform (GIMP_ITEM (l->data), TRUE); + + gimp_image_undo_group_end (image); + } + } +} + +void +gimp_image_item_list_transform (GimpImage *image, + GList *list, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (list) + { + GimpObjectQueue *queue = NULL; + GList *l; + + if (progress) + { + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_list (queue, list); + } + + if (list->next) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + C_("undo-type", "Transform Items")); + + for (l = list; l; l = g_list_next (l)) + gimp_item_start_transform (GIMP_ITEM (l->data), TRUE); + } + + for (l = list; l; l = g_list_next (l)) + { + GimpItem *item = l->data; + + if (queue) + gimp_object_queue_pop (queue); + + gimp_item_transform (item, context, + matrix, direction, + interpolation_type, + gimp_item_get_clip (item, clip_result), + progress); + } + + if (list->next) + { + for (l = list; l; l = g_list_next (l)) + gimp_item_end_transform (GIMP_ITEM (l->data), TRUE); + + gimp_image_undo_group_end (image); + } + + g_clear_object (&queue); + } +} + +/** + * gimp_image_item_list_get_list: + * @image: An @image. + * @type: Which type of items to return. + * @set: Set the returned items are part of. + * + * This function returns a #GList of #GimpItems for which the + * @type and @set criterions match. + * + * Return value: The list of items. + **/ +GList * +gimp_image_item_list_get_list (GimpImage *image, + GimpItemTypeMask type, + GimpItemSet set) +{ + GList *all_items; + GList *list; + GList *return_list = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + if (type & GIMP_ITEM_TYPE_LAYERS) + { + all_items = gimp_image_get_layer_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + + if (gimp_item_is_in_set (item, set)) + return_list = g_list_prepend (return_list, item); + } + + g_list_free (all_items); + } + + if (type & GIMP_ITEM_TYPE_CHANNELS) + { + all_items = gimp_image_get_channel_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + + if (gimp_item_is_in_set (item, set)) + return_list = g_list_prepend (return_list, item); + } + + g_list_free (all_items); + } + + if (type & GIMP_ITEM_TYPE_VECTORS) + { + all_items = gimp_image_get_vectors_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + + if (gimp_item_is_in_set (item, set)) + return_list = g_list_prepend (return_list, item); + } + + g_list_free (all_items); + } + + return g_list_reverse (return_list); +} + +static GList * +gimp_image_item_list_remove_children (GList *list, + const GimpItem *parent) +{ + GList *l = list; + + while (l) + { + GimpItem *item = l->data; + + l = g_list_next (l); + + if (gimp_viewable_is_ancestor (GIMP_VIEWABLE (parent), + GIMP_VIEWABLE (item))) + { + list = g_list_remove (list, item); + } + } + + return list; +} + +GList * +gimp_image_item_list_filter (GList *list) +{ + GList *l; + + if (! list) + return NULL; + + for (l = list; l; l = g_list_next (l)) + { + GimpItem *item = l->data; + GList *next; + + next = gimp_image_item_list_remove_children (g_list_next (l), item); + + l->next = next; + if (next) + next->prev = l; + } + + return list; +} diff --git a/app/core/gimpimage-item-list.h b/app/core/gimpimage-item-list.h new file mode 100644 index 0000000..cd69637 --- /dev/null +++ b/app/core/gimpimage-item-list.h @@ -0,0 +1,63 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_ITEM_LIST_H__ +#define __GIMP_IMAGE_ITEM_LIST_H__ + + +gboolean gimp_image_item_list_bounds (GimpImage *image, + GList *list, + gint *x, + gint *y, + gint *width, + gint *height); + +void gimp_image_item_list_translate (GimpImage *image, + GList *list, + gint offset_x, + gint offset_y, + gboolean push_undo); +void gimp_image_item_list_flip (GimpImage *image, + GList *list, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +void gimp_image_item_list_rotate (GimpImage *image, + GList *list, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +void gimp_image_item_list_transform (GimpImage *image, + GList *list, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + +GList * gimp_image_item_list_get_list (GimpImage *image, + GimpItemTypeMask type, + GimpItemSet set); + +GList * gimp_image_item_list_filter (GList *list); + + +#endif /* __GIMP_IMAGE_ITEM_LIST_H__ */ diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c new file mode 100644 index 0000000..3d96860 --- /dev/null +++ b/app/core/gimpimage-merge.c @@ -0,0 +1,745 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimp-babl-compat.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-nodes.h" +#include "gegl/gimp-gegl-utils.h" + +#include "vectors/gimpvectors.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimperror.h" +#include "gimpgrouplayer.h" +#include "gimpimage.h" +#include "gimpimage-merge.h" +#include "gimpimage-undo.h" +#include "gimpitemstack.h" +#include "gimplayer-floating-selection.h" +#include "gimplayer-new.h" +#include "gimplayermask.h" +#include "gimpmarshal.h" +#include "gimpparasitelist.h" +#include "gimppickable.h" +#include "gimpprogress.h" +#include "gimpprojectable.h" +#include "gimpundostack.h" + +#include "gimp-intl.h" + + +static GimpLayer * gimp_image_merge_layers (GimpImage *image, + GimpContainer *container, + GSList *merge_list, + GimpContext *context, + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress); + + +/* public functions */ + +GimpLayer * +gimp_image_merge_visible_layers (GimpImage *image, + GimpContext *context, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + GimpProgress *progress) +{ + GimpContainer *container; + GList *list; + GSList *merge_list = NULL; + GSList *invisible_list = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + if (merge_active_group) + { + GimpLayer *active_layer = gimp_image_get_active_layer (image); + + /* if the active layer is the floating selection, get the + * underlying drawable, but only if it is a layer + */ + if (active_layer && gimp_layer_is_floating_sel (active_layer)) + { + GimpDrawable *fs_drawable; + + fs_drawable = gimp_layer_get_floating_sel_drawable (active_layer); + + if (GIMP_IS_LAYER (fs_drawable)) + active_layer = GIMP_LAYER (fs_drawable); + } + + if (active_layer) + container = gimp_item_get_container (GIMP_ITEM (active_layer)); + else + container = gimp_image_get_layers (image); + } + else + { + container = gimp_image_get_layers (image); + } + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (container)); + list; + list = g_list_next (list)) + { + GimpLayer *layer = list->data; + + if (gimp_layer_is_floating_sel (layer)) + continue; + + if (gimp_item_get_visible (GIMP_ITEM (layer))) + { + merge_list = g_slist_append (merge_list, layer); + } + else if (discard_invisible) + { + invisible_list = g_slist_append (invisible_list, layer); + } + } + + if (merge_list) + { + GimpLayer *layer; + const gchar *undo_desc = C_("undo-type", "Merge Visible Layers"); + + gimp_set_busy (image->gimp); + + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, + undo_desc); + + /* if there's a floating selection, anchor it */ + if (gimp_image_get_floating_selection (image)) + floating_sel_anchor (gimp_image_get_floating_selection (image)); + + layer = gimp_image_merge_layers (image, + container, + merge_list, context, merge_type, + undo_desc, progress); + g_slist_free (merge_list); + + if (invisible_list) + { + GSList *list; + + for (list = invisible_list; list; list = g_slist_next (list)) + gimp_image_remove_layer (image, list->data, TRUE, NULL); + + g_slist_free (invisible_list); + } + + gimp_image_undo_group_end (image); + + gimp_unset_busy (image->gimp); + + return layer; + } + + return gimp_image_get_active_layer (image); +} + +GimpLayer * +gimp_image_flatten (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + GError **error) +{ + GList *list; + GSList *merge_list = NULL; + GimpLayer *layer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + for (list = gimp_image_get_layer_iter (image); + list; + list = g_list_next (list)) + { + layer = list->data; + + if (gimp_layer_is_floating_sel (layer)) + continue; + + if (gimp_item_get_visible (GIMP_ITEM (layer))) + merge_list = g_slist_append (merge_list, layer); + } + + if (merge_list) + { + const gchar *undo_desc = C_("undo-type", "Flatten Image"); + + gimp_set_busy (image->gimp); + + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, + undo_desc); + + /* if there's a floating selection, anchor it */ + if (gimp_image_get_floating_selection (image)) + floating_sel_anchor (gimp_image_get_floating_selection (image)); + + layer = gimp_image_merge_layers (image, + gimp_image_get_layers (image), + merge_list, context, + GIMP_FLATTEN_IMAGE, + undo_desc, progress); + g_slist_free (merge_list); + + gimp_image_alpha_changed (image); + + gimp_image_undo_group_end (image); + + gimp_unset_busy (image->gimp); + + return layer; + } + + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot flatten an image without any visible layer.")); + return NULL; +} + +GimpLayer * +gimp_image_merge_down (GimpImage *image, + GimpLayer *current_layer, + GimpContext *context, + GimpMergeType merge_type, + GimpProgress *progress, + GError **error) +{ + GimpLayer *layer; + GList *list; + GList *layer_list = NULL; + GSList *merge_list = NULL; + const gchar *undo_desc; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (current_layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (current_layer)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (gimp_layer_is_floating_sel (current_layer)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot merge down a floating selection.")); + return NULL; + } + + if (! gimp_item_get_visible (GIMP_ITEM (current_layer))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot merge down an invisible layer.")); + return NULL; + } + + for (list = gimp_item_get_container_iter (GIMP_ITEM (current_layer)); + list; + list = g_list_next (list)) + { + layer = list->data; + + if (layer == current_layer) + break; + } + + for (layer_list = g_list_next (list); + layer_list; + layer_list = g_list_next (layer_list)) + { + layer = layer_list->data; + + if (gimp_item_get_visible (GIMP_ITEM (layer))) + { + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot merge down to a layer group.")); + return NULL; + } + + if (gimp_item_is_content_locked (GIMP_ITEM (layer))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("The layer to merge down to is locked.")); + return NULL; + } + + merge_list = g_slist_append (NULL, layer); + break; + } + } + + if (! merge_list) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("There is no visible layer to merge down to.")); + return NULL; + } + + merge_list = g_slist_prepend (merge_list, current_layer); + + undo_desc = C_("undo-type", "Merge Down"); + + gimp_set_busy (image->gimp); + + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, + undo_desc); + + layer = gimp_image_merge_layers (image, + gimp_item_get_container (GIMP_ITEM (current_layer)), + merge_list, context, merge_type, + undo_desc, progress); + g_slist_free (merge_list); + + gimp_image_undo_group_end (image); + + gimp_unset_busy (image->gimp); + + return layer; +} + +GimpLayer * +gimp_image_merge_group_layer (GimpImage *image, + GimpGroupLayer *group) +{ + GimpLayer *parent; + GimpLayer *layer; + gint index; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + g_return_val_if_fail (gimp_item_get_image (GIMP_ITEM (group)) == image, NULL); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, + C_("undo-type", "Merge Layer Group")); + + parent = gimp_layer_get_parent (GIMP_LAYER (group)); + index = gimp_item_get_index (GIMP_ITEM (group)); + + /* if this is a pass-through group, change its mode to NORMAL *before* + * duplicating it, since PASS_THROUGH mode is invalid for regular layers. + * see bug #793714. + */ + if (gimp_layer_get_mode (GIMP_LAYER (group)) == GIMP_LAYER_MODE_PASS_THROUGH) + { + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + + /* keep the group's current blend space, composite space, and composite + * mode. + */ + blend_space = gimp_layer_get_blend_space (GIMP_LAYER (group)); + composite_space = gimp_layer_get_composite_space (GIMP_LAYER (group)); + composite_mode = gimp_layer_get_composite_mode (GIMP_LAYER (group)); + + gimp_layer_set_mode (GIMP_LAYER (group), GIMP_LAYER_MODE_NORMAL, TRUE); + gimp_layer_set_blend_space (GIMP_LAYER (group), blend_space, TRUE); + gimp_layer_set_composite_space (GIMP_LAYER (group), composite_space, TRUE); + gimp_layer_set_composite_mode (GIMP_LAYER (group), composite_mode, TRUE); + } + + layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (group), + GIMP_TYPE_LAYER)); + + gimp_object_set_name (GIMP_OBJECT (layer), gimp_object_get_name (group)); + + gimp_image_remove_layer (image, GIMP_LAYER (group), TRUE, NULL); + gimp_image_add_layer (image, layer, parent, index, TRUE); + + gimp_image_undo_group_end (image); + + return layer; +} + + +/* merging vectors */ + +GimpVectors * +gimp_image_merge_visible_vectors (GimpImage *image, + GError **error) +{ + GList *list; + GList *merge_list = NULL; + GimpVectors *vectors; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + for (list = gimp_image_get_vectors_iter (image); + list; + list = g_list_next (list)) + { + vectors = list->data; + + if (gimp_item_get_visible (GIMP_ITEM (vectors))) + merge_list = g_list_prepend (merge_list, vectors); + } + + merge_list = g_list_reverse (merge_list); + + if (merge_list && merge_list->next) + { + GimpVectors *target_vectors; + gchar *name; + gint pos; + + gimp_set_busy (image->gimp); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, + C_("undo-type", "Merge Visible Paths")); + + vectors = GIMP_VECTORS (merge_list->data); + + name = g_strdup (gimp_object_get_name (vectors)); + pos = gimp_item_get_index (GIMP_ITEM (vectors)); + + target_vectors = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), + GIMP_TYPE_VECTORS)); + gimp_image_remove_vectors (image, vectors, TRUE, NULL); + + for (list = g_list_next (merge_list); + list; + list = g_list_next (list)) + { + vectors = list->data; + + gimp_vectors_add_strokes (vectors, target_vectors); + gimp_image_remove_vectors (image, vectors, TRUE, NULL); + } + + gimp_object_take_name (GIMP_OBJECT (target_vectors), name); + + g_list_free (merge_list); + + /* FIXME tree */ + gimp_image_add_vectors (image, target_vectors, NULL, pos, TRUE); + gimp_unset_busy (image->gimp); + + gimp_image_undo_group_end (image); + + return target_vectors; + } + else + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Not enough visible paths for a merge. " + "There must be at least two.")); + return NULL; + } +} + + +/* private functions */ + +static GimpLayer * +gimp_image_merge_layers (GimpImage *image, + GimpContainer *container, + GSList *merge_list, + GimpContext *context, + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress) +{ + GimpLayer *parent; + gint x1, y1; + gint x2, y2; + GSList *layers; + GimpLayer *layer; + GimpLayer *top_layer; + GimpLayer *bottom_layer; + GimpLayer *merge_layer; + gint position; + GeglNode *node; + GeglNode *source_node; + GeglNode *flatten_node; + GeglNode *offset_node; + GeglNode *last_node; + GeglNode *last_node_source; + GimpParasiteList *parasites; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + top_layer = merge_list->data; + parent = gimp_layer_get_parent (top_layer); + + /* Make sure the image's graph is constructed, so that top-level layers have + * a parent node. + */ + (void) gimp_projectable_get_graph (GIMP_PROJECTABLE (image)); + + /* Make sure the parent's graph is constructed, so that the top layer has a + * parent node, even if it is the child of a group layer (in particular, of + * an invisible group layer, whose graph may not have been constructed as a + * result of the above call. see issue #2095.) + */ + if (parent) + (void) gimp_filter_get_node (GIMP_FILTER (parent)); + + /* Build our graph inside the top-layer's parent node */ + source_node = gimp_filter_get_node (GIMP_FILTER (top_layer)); + node = gegl_node_get_parent (source_node); + + g_return_val_if_fail (node, NULL); + + /* Get the layer extents */ + x1 = y1 = 0; + x2 = y2 = 0; + for (layers = merge_list; layers; layers = g_slist_next (layers)) + { + gint off_x, off_y; + + layer = layers->data; + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + + switch (merge_type) + { + case GIMP_EXPAND_AS_NECESSARY: + case GIMP_CLIP_TO_IMAGE: + if (layers == merge_list) + { + x1 = off_x; + y1 = off_y; + x2 = off_x + gimp_item_get_width (GIMP_ITEM (layer)); + y2 = off_y + gimp_item_get_height (GIMP_ITEM (layer)); + } + else + { + if (off_x < x1) + x1 = off_x; + if (off_y < y1) + y1 = off_y; + if ((off_x + gimp_item_get_width (GIMP_ITEM (layer))) > x2) + x2 = (off_x + gimp_item_get_width (GIMP_ITEM (layer))); + if ((off_y + gimp_item_get_height (GIMP_ITEM (layer))) > y2) + y2 = (off_y + gimp_item_get_height (GIMP_ITEM (layer))); + } + + if (merge_type == GIMP_CLIP_TO_IMAGE) + { + x1 = CLAMP (x1, 0, gimp_image_get_width (image)); + y1 = CLAMP (y1, 0, gimp_image_get_height (image)); + x2 = CLAMP (x2, 0, gimp_image_get_width (image)); + y2 = CLAMP (y2, 0, gimp_image_get_height (image)); + } + break; + + case GIMP_CLIP_TO_BOTTOM_LAYER: + if (layers->next == NULL) + { + x1 = off_x; + y1 = off_y; + x2 = off_x + gimp_item_get_width (GIMP_ITEM (layer)); + y2 = off_y + gimp_item_get_height (GIMP_ITEM (layer)); + } + break; + + case GIMP_FLATTEN_IMAGE: + if (layers->next == NULL) + { + x1 = 0; + y1 = 0; + x2 = gimp_image_get_width (image); + y2 = gimp_image_get_height (image); + } + break; + } + } + + if ((x2 - x1) == 0 || (y2 - y1) == 0) + return NULL; + + bottom_layer = layer; + + flatten_node = NULL; + + if (merge_type == GIMP_FLATTEN_IMAGE || + (gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)) && + ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))) + { + GimpRGB bg; + + merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1), + gimp_image_get_layer_format (image, FALSE), + gimp_object_get_name (bottom_layer), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + + if (! merge_layer) + { + g_warning ("%s: could not allocate merge layer", G_STRFUNC); + + return NULL; + } + + /* get the background for compositing */ + gimp_context_get_background (context, &bg); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (layer), + &bg, &bg); + + flatten_node = gimp_gegl_create_flatten_node ( + &bg, gimp_layer_get_real_composite_space (bottom_layer)); + } + else + { + /* The final merged layer inherits the name of the bottom most layer + * and the resulting layer has an alpha channel whether or not the + * original did. Opacity is set to 100% and the MODE is set to normal. + */ + + merge_layer = + gimp_layer_new (image, (x2 - x1), (y2 - y1), + gimp_drawable_get_format_with_alpha (GIMP_DRAWABLE (bottom_layer)), + gimp_object_get_name (bottom_layer), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + + if (! merge_layer) + { + g_warning ("%s: could not allocate merge layer", G_STRFUNC); + + return NULL; + } + } + + if (merge_type == GIMP_FLATTEN_IMAGE) + { + position = 0; + } + else + { + /* Find the index in the layer list of the bottom layer--we need this + * in order to add the final, merged layer to the layer list correctly + */ + position = + gimp_container_get_n_children (container) - + gimp_container_get_child_index (container, GIMP_OBJECT (bottom_layer)); + } + + gimp_item_set_offset (GIMP_ITEM (merge_layer), x1, y1); + + offset_node = gegl_node_new_child (node, + "operation", "gegl:translate", + "x", (gdouble) -x1, + "y", (gdouble) -y1, + NULL); + + if (flatten_node) + { + gegl_node_add_child (node, flatten_node); + g_object_unref (flatten_node); + + gegl_node_link_many (source_node, flatten_node, offset_node, NULL); + } + else + { + gegl_node_link_many (source_node, offset_node, NULL); + } + + /* Disconnect the bottom-layer node's input */ + last_node = gimp_filter_get_node (GIMP_FILTER (bottom_layer)); + last_node_source = gegl_node_get_producer (last_node, "input", NULL); + + gegl_node_disconnect (last_node, "input"); + + /* Render the graph into the merge layer */ + gimp_gegl_apply_operation (NULL, progress, undo_desc, offset_node, + gimp_drawable_get_buffer ( + GIMP_DRAWABLE (merge_layer)), + NULL, FALSE); + + /* Reconnect the bottom-layer node's input */ + if (last_node_source) + gegl_node_link (last_node_source, last_node); + + /* Clean up the graph */ + gegl_node_remove_child (node, offset_node); + + if (flatten_node) + gegl_node_remove_child (node, flatten_node); + + /* Copy the tattoo and parasites of the bottom layer to the new layer */ + gimp_item_set_tattoo (GIMP_ITEM (merge_layer), + gimp_item_get_tattoo (GIMP_ITEM (bottom_layer))); + + parasites = gimp_item_get_parasites (GIMP_ITEM (bottom_layer)); + parasites = gimp_parasite_list_copy (parasites); + gimp_item_set_parasites (GIMP_ITEM (merge_layer), parasites); + g_object_unref (parasites); + + /* Remove the merged layers from the image */ + for (layers = merge_list; layers; layers = g_slist_next (layers)) + gimp_image_remove_layer (image, layers->data, TRUE, NULL); + + gimp_item_set_visible (GIMP_ITEM (merge_layer), TRUE, FALSE); + + /* if the type is flatten, remove all the remaining layers */ + if (merge_type == GIMP_FLATTEN_IMAGE) + { + GList *list = gimp_image_get_layer_iter (image); + + while (list) + { + layer = list->data; + + list = g_list_next (list); + gimp_image_remove_layer (image, layer, TRUE, NULL); + } + + gimp_image_add_layer (image, merge_layer, parent, + position, TRUE); + } + else + { + /* Add the layer to the image */ + gimp_image_add_layer (image, merge_layer, parent, + gimp_container_get_n_children (container) - + position + 1, + TRUE); + } + + gimp_drawable_update (GIMP_DRAWABLE (merge_layer), 0, 0, -1, -1); + + return merge_layer; +} diff --git a/app/core/gimpimage-merge.h b/app/core/gimpimage-merge.h new file mode 100644 index 0000000..93aab4e --- /dev/null +++ b/app/core/gimpimage-merge.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_MERGE_H__ +#define __GIMP_IMAGE_MERGE_H__ + + +GimpLayer * gimp_image_merge_visible_layers (GimpImage *image, + GimpContext *context, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + GimpProgress *progress); +GimpLayer * gimp_image_merge_down (GimpImage *image, + GimpLayer *current_layer, + GimpContext *context, + GimpMergeType merge_type, + GimpProgress *progress, + GError **error); +GimpLayer * gimp_image_merge_group_layer (GimpImage *image, + GimpGroupLayer *group); + +GimpLayer * gimp_image_flatten (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + GError **error); + +GimpVectors * gimp_image_merge_visible_vectors (GimpImage *image, + GError **error); + + +#endif /* __GIMP_IMAGE_MERGE_H__ */ diff --git a/app/core/gimpimage-metadata.c b/app/core/gimpimage-metadata.c new file mode 100644 index 0000000..ae0774f --- /dev/null +++ b/app/core/gimpimage-metadata.c @@ -0,0 +1,184 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-metadata.h" +#include "gimpimage-private.h" +#include "gimpimage-undo-push.h" + + +/* public functions */ + + +GimpMetadata * +gimp_image_get_metadata (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return private->metadata; +} + +void +gimp_image_set_metadata (GimpImage *image, + GimpMetadata *metadata, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (metadata != private->metadata) + { + if (push_undo) + gimp_image_undo_push_image_metadata (image, NULL); + + g_set_object (&private->metadata, metadata); + + if (private->metadata) + { + gimp_image_metadata_update_pixel_size (image); + gimp_image_metadata_update_bits_per_sample (image); + gimp_image_metadata_update_resolution (image); + gimp_image_metadata_update_colorspace (image); + } + + g_object_notify (G_OBJECT (image), "metadata"); + } +} + +void +gimp_image_metadata_update_pixel_size (GimpImage *image) +{ + GimpMetadata *metadata; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + metadata = gimp_image_get_metadata (image); + + if (metadata) + { + gimp_metadata_set_pixel_size (metadata, + gimp_image_get_width (image), + gimp_image_get_height (image)); + } +} + +void +gimp_image_metadata_update_bits_per_sample (GimpImage *image) +{ + GimpMetadata *metadata; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + metadata = gimp_image_get_metadata (image); + + if (metadata) + { + switch (gimp_image_get_component_type (image)) + { + case GIMP_COMPONENT_TYPE_U8: + gimp_metadata_set_bits_per_sample (metadata, 8); + break; + + case GIMP_COMPONENT_TYPE_U16: + case GIMP_COMPONENT_TYPE_HALF: + gimp_metadata_set_bits_per_sample (metadata, 16); + break; + + case GIMP_COMPONENT_TYPE_U32: + case GIMP_COMPONENT_TYPE_FLOAT: + gimp_metadata_set_bits_per_sample (metadata, 32); + break; + + case GIMP_COMPONENT_TYPE_DOUBLE: + gimp_metadata_set_bits_per_sample (metadata, 64); + break; + } + } +} + +void +gimp_image_metadata_update_resolution (GimpImage *image) +{ + GimpMetadata *metadata; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + metadata = gimp_image_get_metadata (image); + + if (metadata) + { + gdouble xres, yres; + + gimp_image_get_resolution (image, &xres, &yres); + gimp_metadata_set_resolution (metadata, xres, yres, + gimp_image_get_unit (image)); + } +} + +void +gimp_image_metadata_update_colorspace (GimpImage *image) +{ + GimpMetadata *metadata; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + metadata = gimp_image_get_metadata (image); + + if (metadata) + { + /* See the discussions in issue #3532 and issue #301 */ + + GimpColorProfile *profile = gimp_image_get_color_profile (image); + GimpMetadataColorspace space = GIMP_METADATA_COLORSPACE_UNSPECIFIED; + + if (profile) + { + static GimpColorProfile *adobe = NULL; + + if (! adobe) + adobe = gimp_color_profile_new_rgb_adobe (); + + if (gimp_color_profile_is_equal (profile, adobe)) + space = GIMP_METADATA_COLORSPACE_ADOBERGB; + } + else + { + space = GIMP_METADATA_COLORSPACE_SRGB; + } + + gimp_metadata_set_colorspace (metadata, space); + } +} diff --git a/app/core/gimpimage-metadata.h b/app/core/gimpimage-metadata.h new file mode 100644 index 0000000..2a74be8 --- /dev/null +++ b/app/core/gimpimage-metadata.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 . + */ + +#ifndef __GIMP_IMAGE_METADATA_H__ +#define __GIMP_IMAGE_METADATA_H__ + + +GimpMetadata * gimp_image_get_metadata (GimpImage *image); +void gimp_image_set_metadata (GimpImage *image, + GimpMetadata *metadata, + gboolean push_undo); + +void gimp_image_metadata_update_pixel_size (GimpImage *image); +void gimp_image_metadata_update_bits_per_sample (GimpImage *image); +void gimp_image_metadata_update_resolution (GimpImage *image); +void gimp_image_metadata_update_colorspace (GimpImage *image); + + +#endif /* __GIMP_IMAGE_METADATA_H__ */ diff --git a/app/core/gimpimage-new.c b/app/core/gimpimage-new.c new file mode 100644 index 0000000..4d0fa7b --- /dev/null +++ b/app/core/gimpimage-new.c @@ -0,0 +1,393 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gegl/gimp-babl.h" + +#include "gimp.h" +#include "gimpbuffer.h" +#include "gimpchannel.h" +#include "gimpcontext.h" +#include "gimpdrawable-fill.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-new.h" +#include "gimpimage-undo.h" +#include "gimplayer.h" +#include "gimplayer-new.h" +#include "gimptemplate.h" + +#include "gimp-intl.h" + + +GimpTemplate * +gimp_image_new_get_last_template (Gimp *gimp, + GimpImage *image) +{ + GimpTemplate *template; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL); + + template = gimp_template_new ("image new values"); + + if (image) + { + gimp_config_sync (G_OBJECT (gimp->config->default_image), + G_OBJECT (template), 0); + gimp_template_set_from_image (template, image); + } + else + { + gimp_config_sync (G_OBJECT (gimp->image_new_last_template), + G_OBJECT (template), 0); + } + + return template; +} + +void +gimp_image_new_set_last_template (Gimp *gimp, + GimpTemplate *template) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_TEMPLATE (template)); + + gimp_config_sync (G_OBJECT (template), + G_OBJECT (gimp->image_new_last_template), 0); +} + +GimpImage * +gimp_image_new_from_template (Gimp *gimp, + GimpTemplate *template, + GimpContext *context) +{ + GimpImage *image; + GimpLayer *layer; + GimpColorProfile *profile; + gint width, height; + gboolean has_alpha; + const gchar *comment; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + image = gimp_create_image (gimp, + gimp_template_get_width (template), + gimp_template_get_height (template), + gimp_template_get_base_type (template), + gimp_template_get_precision (template), + FALSE); + + gimp_image_undo_disable (image); + + comment = gimp_template_get_comment (template); + + if (comment) + { + GimpParasite *parasite; + + parasite = gimp_parasite_new ("gimp-comment", + GIMP_PARASITE_PERSISTENT, + strlen (comment) + 1, + comment); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + } + + gimp_image_set_resolution (image, + gimp_template_get_resolution_x (template), + gimp_template_get_resolution_y (template)); + gimp_image_set_unit (image, gimp_template_get_resolution_unit (template)); + + gimp_image_set_is_color_managed (image, + gimp_template_get_color_managed (template), + FALSE); + profile = gimp_template_get_color_profile (template); + gimp_image_set_color_profile (image, profile, NULL); + if (profile) + g_object_unref (profile); + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + + if (gimp_template_get_fill_type (template) == GIMP_FILL_TRANSPARENT) + has_alpha = TRUE; + else + has_alpha = FALSE; + + layer = gimp_layer_new (image, width, height, + gimp_image_get_layer_format (image, has_alpha), + _("Background"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + + gimp_drawable_fill (GIMP_DRAWABLE (layer), + context, gimp_template_get_fill_type (template)); + + gimp_image_add_layer (image, layer, NULL, 0, FALSE); + + gimp_image_undo_enable (image); + gimp_image_clean_all (image); + + return image; +} + +GimpImage * +gimp_image_new_from_drawable (Gimp *gimp, + GimpDrawable *drawable) +{ + GimpItem *item; + GimpImage *image; + GimpImage *new_image; + GimpLayer *new_layer; + GType new_type; + gint off_x, off_y; + GimpImageBaseType type; + gdouble xres; + gdouble yres; + GimpColorProfile *profile; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + + item = GIMP_ITEM (drawable); + image = gimp_item_get_image (item); + + type = gimp_drawable_get_base_type (drawable); + + new_image = gimp_create_image (gimp, + gimp_item_get_width (item), + gimp_item_get_height (item), + type, + gimp_drawable_get_precision (drawable), + TRUE); + gimp_image_undo_disable (new_image); + + if (type == GIMP_INDEXED) + gimp_image_set_colormap (new_image, + gimp_image_get_colormap (image), + gimp_image_get_colormap_size (image), + FALSE); + + gimp_image_get_resolution (image, &xres, &yres); + gimp_image_set_resolution (new_image, xres, yres); + gimp_image_set_unit (new_image, gimp_image_get_unit (image)); + + gimp_image_set_is_color_managed (new_image, + gimp_image_get_is_color_managed (image), + FALSE); + profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); + gimp_image_set_color_profile (new_image, profile, NULL); + + if (GIMP_IS_LAYER (drawable)) + new_type = G_TYPE_FROM_INSTANCE (drawable); + else + new_type = GIMP_TYPE_LAYER; + + new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (drawable), + new_image, new_type)); + + gimp_object_set_name (GIMP_OBJECT (new_layer), + gimp_object_get_name (drawable)); + + gimp_item_get_offset (GIMP_ITEM (new_layer), &off_x, &off_y); + gimp_item_translate (GIMP_ITEM (new_layer), -off_x, -off_y, FALSE); + gimp_item_set_visible (GIMP_ITEM (new_layer), TRUE, FALSE); + gimp_item_set_linked (GIMP_ITEM (new_layer), FALSE, FALSE); + gimp_layer_set_mode (new_layer, + gimp_image_get_default_new_layer_mode (new_image), + FALSE); + gimp_layer_set_opacity (new_layer, GIMP_OPACITY_OPAQUE, FALSE); + if (gimp_layer_can_lock_alpha (new_layer)) + gimp_layer_set_lock_alpha (new_layer, FALSE, FALSE); + + gimp_image_add_layer (new_image, new_layer, NULL, 0, TRUE); + + gimp_image_undo_enable (new_image); + + return new_image; +} + +GimpImage * +gimp_image_new_from_component (Gimp *gimp, + GimpImage *image, + GimpChannelType component) +{ + GimpImage *new_image; + GimpChannel *channel; + GimpLayer *layer; + const gchar *desc; + gdouble xres; + gdouble yres; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + new_image = gimp_create_image (gimp, + gimp_image_get_width (image), + gimp_image_get_height (image), + GIMP_GRAY, + gimp_image_get_precision (image), + TRUE); + + gimp_image_undo_disable (new_image); + + gimp_image_get_resolution (image, &xres, &yres); + gimp_image_set_resolution (new_image, xres, yres); + gimp_image_set_unit (new_image, gimp_image_get_unit (image)); + + channel = gimp_channel_new_from_component (image, component, NULL, NULL); + + layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (channel), + new_image, GIMP_TYPE_LAYER)); + g_object_unref (channel); + + gimp_enum_get_value (GIMP_TYPE_CHANNEL_TYPE, component, + NULL, NULL, &desc, NULL); + gimp_object_take_name (GIMP_OBJECT (layer), + g_strdup_printf (_("%s Channel Copy"), desc)); + + gimp_image_add_layer (new_image, layer, NULL, 0, TRUE); + + gimp_image_undo_enable (new_image); + + return new_image; +} + +GimpImage * +gimp_image_new_from_buffer (Gimp *gimp, + GimpBuffer *buffer) +{ + GimpImage *image; + GimpLayer *layer; + const Babl *format; + gboolean has_alpha; + gdouble res_x; + gdouble res_y; + GimpColorProfile *profile; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); + + format = gimp_buffer_get_format (buffer); + has_alpha = babl_format_has_alpha (format); + + image = gimp_create_image (gimp, + gimp_buffer_get_width (buffer), + gimp_buffer_get_height (buffer), + gimp_babl_format_get_base_type (format), + gimp_babl_format_get_precision (format), + TRUE); + gimp_image_undo_disable (image); + + if (gimp_buffer_get_resolution (buffer, &res_x, &res_y)) + { + gimp_image_set_resolution (image, res_x, res_y); + gimp_image_set_unit (image, gimp_buffer_get_unit (buffer)); + } + + profile = gimp_buffer_get_color_profile (buffer); + gimp_image_set_color_profile (image, profile, NULL); + + layer = gimp_layer_new_from_buffer (buffer, image, + gimp_image_get_layer_format (image, + has_alpha), + _("Pasted Layer"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + + gimp_image_add_layer (image, layer, NULL, 0, TRUE); + + gimp_image_undo_enable (image); + + return image; +} + +GimpImage * +gimp_image_new_from_pixbuf (Gimp *gimp, + GdkPixbuf *pixbuf, + const gchar *layer_name) +{ + GimpImage *new_image; + GimpLayer *layer; + GimpImageBaseType base_type; + gboolean has_alpha = FALSE; + guint8 *icc_data; + gsize icc_len; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); + + switch (gdk_pixbuf_get_n_channels (pixbuf)) + { + case 2: has_alpha = TRUE; + case 1: base_type = GIMP_GRAY; + break; + + case 4: has_alpha = TRUE; + case 3: base_type = GIMP_RGB; + break; + + default: + g_return_val_if_reached (NULL); + } + + new_image = gimp_create_image (gimp, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + base_type, + GIMP_PRECISION_U8_GAMMA, + FALSE); + + gimp_image_undo_disable (new_image); + + icc_data = gimp_pixbuf_get_icc_profile (pixbuf, &icc_len); + if (icc_data) + { + gimp_image_set_icc_profile (new_image, icc_data, icc_len, NULL); + g_free (icc_data); + } + + layer = gimp_layer_new_from_pixbuf (pixbuf, new_image, + gimp_image_get_layer_format (new_image, + has_alpha), + layer_name, + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (new_image)); + + gimp_image_add_layer (new_image, layer, NULL, 0, TRUE); + + gimp_image_undo_enable (new_image); + + return new_image; +} diff --git a/app/core/gimpimage-new.h b/app/core/gimpimage-new.h new file mode 100644 index 0000000..6efd3cc --- /dev/null +++ b/app/core/gimpimage-new.h @@ -0,0 +1,42 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_IMAGE_NEW_H__ +#define __GIMP_IMAGE_NEW_H__ + + +GimpTemplate * gimp_image_new_get_last_template (Gimp *gimp, + GimpImage *image); +void gimp_image_new_set_last_template (Gimp *gimp, + GimpTemplate *template); + +GimpImage * gimp_image_new_from_template (Gimp *gimp, + GimpTemplate *template, + GimpContext *context); +GimpImage * gimp_image_new_from_drawable (Gimp *gimp, + GimpDrawable *drawable); +GimpImage * gimp_image_new_from_component (Gimp *gimp, + GimpImage *image, + GimpChannelType component); +GimpImage * gimp_image_new_from_buffer (Gimp *gimp, + GimpBuffer *buffer); +GimpImage * gimp_image_new_from_pixbuf (Gimp *gimp, + GdkPixbuf *pixbuf, + const gchar *layer_name); + + +#endif /* __GIMP_IMAGE_NEW__ */ diff --git a/app/core/gimpimage-pick-color.c b/app/core/gimpimage-pick-color.c new file mode 100644 index 0000000..f8168cb --- /dev/null +++ b/app/core/gimpimage-pick-color.c @@ -0,0 +1,147 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "gimpchannel.h" +#include "gimpdrawable.h" +#include "gimpimage.h" +#include "gimpimage-pick-color.h" +#include "gimplayer.h" +#include "gimppickable.h" + + +gboolean +gimp_image_pick_color (GimpImage *image, + GimpDrawable *drawable, + gint x, + gint y, + gboolean show_all, + gboolean sample_merged, + gboolean sample_average, + gdouble average_radius, + const Babl **sample_format, + gpointer pixel, + GimpRGB *color) +{ + GimpPickable *pickable; + gboolean result; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (drawable == NULL || + gimp_item_get_image (GIMP_ITEM (drawable)) == image, + FALSE); + + if (sample_merged && drawable) + { + if ((GIMP_IS_LAYER (drawable) && + gimp_image_get_n_layers (image) == 1) || + (GIMP_IS_CHANNEL (drawable) && + gimp_image_get_n_channels (image) == 1)) + { + /* Let's add a special exception when an image has only one + * layer. This was useful in particular for indexed image as + * it allows to pick the right index value even when "Sample + * merged" is checked. There are more possible exceptions, but + * we can't just take them all in considerations unless we + * want to make code extra-complicated). + * See #3041. + */ + sample_merged = FALSE; + } + } + + if (! sample_merged) + { + if (! drawable) + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + return FALSE; + } + + if (sample_merged) + { + if (! show_all) + pickable = GIMP_PICKABLE (image); + else + pickable = GIMP_PICKABLE (gimp_image_get_projection (image)); + } + else + { + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + x -= off_x; + y -= off_y; + + pickable = GIMP_PICKABLE (drawable); + } + + /* Do *not* call gimp_pickable_flush() here because it's too expensive + * to call it unconditionally each time e.g. the cursor view is updated. + * Instead, call gimp_pickable_flush() in the callers if needed. + */ + + if (sample_format) + *sample_format = gimp_pickable_get_format (pickable); + + result = gimp_pickable_pick_color (pickable, x, y, + sample_average && + ! (show_all && sample_merged), + average_radius, + pixel, color); + + if (show_all && sample_merged) + { + const Babl *format = babl_format ("RaGaBaA double"); + gdouble sample[4] = {}; + + if (! result) + memset (pixel, 0, babl_format_get_bytes_per_pixel (*sample_format)); + + if (sample_average) + { + GeglBuffer *buffer = gimp_pickable_get_buffer (pickable); + gint radius = floor (average_radius); + + gimp_gegl_average_color (buffer, + GEGL_RECTANGLE (x - radius, + y - radius, + 2 * radius + 1, + 2 * radius + 1), + FALSE, GEGL_ABYSS_NONE, format, sample); + } + + if (! result || sample_average) + gimp_pickable_pixel_to_srgb (pickable, format, sample, color); + + result = TRUE; + } + + return result; +} diff --git a/app/core/gimpimage-pick-color.h b/app/core/gimpimage-pick-color.h new file mode 100644 index 0000000..1ca2947 --- /dev/null +++ b/app/core/gimpimage-pick-color.h @@ -0,0 +1,35 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_PICK_COLOR_H__ +#define __GIMP_IMAGE_PICK_COLOR_H__ + + +gboolean gimp_image_pick_color (GimpImage *image, + GimpDrawable *drawable, + gint x, + gint y, + gboolean show_all, + gboolean sample_merged, + gboolean sample_average, + gdouble average_radius, + const Babl **sample_format, + gpointer pixel, + GimpRGB *color); + + +#endif /* __GIMP_IMAGE_PICK_COLOR_H__ */ diff --git a/app/core/gimpimage-pick-item.c b/app/core/gimpimage-pick-item.c new file mode 100644 index 0000000..b2257f3 --- /dev/null +++ b/app/core/gimpimage-pick-item.c @@ -0,0 +1,381 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpgrouplayer.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-pick-item.h" +#include "gimpimage-private.h" +#include "gimppickable.h" +#include "gimpsamplepoint.h" + +#include "text/gimptextlayer.h" + +#include "vectors/gimpstroke.h" +#include "vectors/gimpvectors.h" + + +GimpLayer * +gimp_image_pick_layer (GimpImage *image, + gint x, + gint y, + GimpLayer *previously_picked) +{ + GList *all_layers; + GList *list; + gint off_x, off_y; + gint tries = 1; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + all_layers = gimp_image_get_layer_list (image); + + if (previously_picked) + { + gimp_item_get_offset (GIMP_ITEM (previously_picked), &off_x, &off_y); + if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (previously_picked), + x - off_x, y - off_y) <= 0.25) + previously_picked = NULL; + else + tries++; + } + + while (tries) + { + for (list = all_layers; list; list = g_list_next (list)) + { + GimpLayer *layer = list->data; + + if (previously_picked) + { + /* Take the first layer with a pixel at given coordinates + * after the previously picked one. + */ + if (layer == previously_picked) + previously_picked = NULL; + continue; + } + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + + if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer), + x - off_x, y - off_y) > 0.25) + { + g_list_free (all_layers); + + return layer; + } + } + tries--; + } + + g_list_free (all_layers); + + return NULL; +} + +GimpLayer * +gimp_image_pick_layer_by_bounds (GimpImage *image, + gint x, + gint y) +{ + GList *all_layers; + GList *list; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + all_layers = gimp_image_get_layer_list (image); + + for (list = all_layers; list; list = g_list_next (list)) + { + GimpLayer *layer = list->data; + + if (gimp_item_is_visible (GIMP_ITEM (layer))) + { + gint off_x, off_y; + gint width, height; + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + width = gimp_item_get_width (GIMP_ITEM (layer)); + height = gimp_item_get_height (GIMP_ITEM (layer)); + + if (x >= off_x && + y >= off_y && + x < off_x + width && + y < off_y + height) + { + g_list_free (all_layers); + + return layer; + } + } + } + + g_list_free (all_layers); + + return NULL; +} + +GimpTextLayer * +gimp_image_pick_text_layer (GimpImage *image, + gint x, + gint y) +{ + GList *all_layers; + GList *list; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + all_layers = gimp_image_get_layer_list (image); + + for (list = all_layers; list; list = g_list_next (list)) + { + GimpLayer *layer = list->data; + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + + if (GIMP_IS_TEXT_LAYER (layer) && + x >= off_x && + y >= off_y && + x < off_x + gimp_item_get_width (GIMP_ITEM (layer)) && + y < off_y + gimp_item_get_height (GIMP_ITEM (layer)) && + gimp_item_is_visible (GIMP_ITEM (layer))) + { + g_list_free (all_layers); + + return GIMP_TEXT_LAYER (layer); + } + else if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer), + x - off_x, y - off_y) > 0.25) + { + /* a normal layer covers any possible text layers below, + * bail out + */ + + break; + } + } + + g_list_free (all_layers); + + return NULL; +} + +GimpVectors * +gimp_image_pick_vectors (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y) +{ + GimpVectors *ret = NULL; + GList *all_vectors; + GList *list; + gdouble mindist = G_MAXDOUBLE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + all_vectors = gimp_image_get_vectors_list (image); + + for (list = all_vectors; list; list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + + if (gimp_item_is_visible (GIMP_ITEM (vectors))) + { + GimpStroke *stroke = NULL; + GimpCoords coords = GIMP_COORDS_DEFAULT_VALUES; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + gdouble dist; + + coords.x = x; + coords.y = y; + + dist = gimp_stroke_nearest_point_get (stroke, &coords, 1.0, + NULL, NULL, NULL, NULL); + + if (dist >= 0.0 && + dist < MIN (epsilon_y, mindist)) + { + mindist = dist; + ret = vectors; + } + } + } + } + + g_list_free (all_vectors); + + return ret; +} + +static GimpGuide * +gimp_image_pick_guide_internal (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y, + GimpOrientationType orientation) +{ + GList *list; + GimpGuide *ret = NULL; + gdouble mindist = G_MAXDOUBLE; + + for (list = GIMP_IMAGE_GET_PRIVATE (image)->guides; + list; + list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + gdouble dist; + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + if (orientation == GIMP_ORIENTATION_HORIZONTAL || + orientation == GIMP_ORIENTATION_UNKNOWN) + { + dist = ABS (position - y); + if (dist < MIN (epsilon_y, mindist)) + { + mindist = dist; + ret = guide; + } + } + break; + + /* mindist always is in vertical resolution to make it comparable */ + case GIMP_ORIENTATION_VERTICAL: + if (orientation == GIMP_ORIENTATION_VERTICAL || + orientation == GIMP_ORIENTATION_UNKNOWN) + { + dist = ABS (position - x); + if (dist < MIN (epsilon_x, mindist / epsilon_y * epsilon_x)) + { + mindist = dist * epsilon_y / epsilon_x; + ret = guide; + } + } + break; + + default: + continue; + } + } + + return ret; +} + +GimpGuide * +gimp_image_pick_guide (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL); + + return gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y, + GIMP_ORIENTATION_UNKNOWN); +} + +GList * +gimp_image_pick_guides (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y) +{ + GimpGuide *guide; + GList *result = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL); + + guide = gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y, + GIMP_ORIENTATION_HORIZONTAL); + + if (guide) + result = g_list_append (result, guide); + + guide = gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y, + GIMP_ORIENTATION_VERTICAL); + + if (guide) + result = g_list_append (result, guide); + + return result; +} + +GimpSamplePoint * +gimp_image_pick_sample_point (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y) +{ + GList *list; + GimpSamplePoint *ret = NULL; + gdouble mindist = G_MAXDOUBLE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL); + + if (x < 0 || x >= gimp_image_get_width (image) || + y < 0 || y >= gimp_image_get_height (image)) + { + return NULL; + } + + for (list = GIMP_IMAGE_GET_PRIVATE (image)->sample_points; + list; + list = g_list_next (list)) + { + GimpSamplePoint *sample_point = list->data; + gint sp_x; + gint sp_y; + gdouble dist; + + gimp_sample_point_get_position (sample_point, &sp_x, &sp_y); + + if (sp_x < 0 || sp_y < 0) + continue; + + dist = hypot ((sp_x + 0.5) - x, + (sp_y + 0.5) - y); + if (dist < MIN (epsilon_y, mindist)) + { + mindist = dist; + ret = sample_point; + } + } + + return ret; +} diff --git a/app/core/gimpimage-pick-item.h b/app/core/gimpimage-pick-item.h new file mode 100644 index 0000000..46da4ef --- /dev/null +++ b/app/core/gimpimage-pick-item.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_PICK_ITEM_H__ +#define __GIMP_IMAGE_PICK_ITEM_H__ + + +GimpLayer * gimp_image_pick_layer (GimpImage *image, + gint x, + gint y, + GimpLayer *previously_picked); +GimpLayer * gimp_image_pick_layer_by_bounds (GimpImage *image, + gint x, + gint y); +GimpTextLayer * gimp_image_pick_text_layer (GimpImage *image, + gint x, + gint y); + +GimpVectors * gimp_image_pick_vectors (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y); + +GimpGuide * gimp_image_pick_guide (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y); +GList * gimp_image_pick_guides (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y); + +GimpSamplePoint * gimp_image_pick_sample_point (GimpImage *image, + gdouble x, + gdouble y, + gdouble epsilon_x, + gdouble epsilon_y); + + +#endif /* __GIMP_IMAGE_PICK_ITEM_H__ */ diff --git a/app/core/gimpimage-preview.c b/app/core/gimpimage-preview.c new file mode 100644 index 0000000..e28865e --- /dev/null +++ b/app/core/gimpimage-preview.c @@ -0,0 +1,214 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-preview.h" +#include "gimppickable.h" +#include "gimpprojectable.h" +#include "gimpprojection.h" +#include "gimptempbuf.h" + + +const Babl * +gimp_image_get_preview_format (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: + case GIMP_GRAY: + return gimp_babl_format_change_component_type ( + gimp_projectable_get_format (GIMP_PROJECTABLE (image)), + GIMP_COMPONENT_TYPE_U8); + + case GIMP_INDEXED: + return babl_format ("R'G'B'A u8"); + } + + g_return_val_if_reached (NULL); +} + +void +gimp_image_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + GimpImage *image = GIMP_IMAGE (viewable); + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_viewable_calc_preview_size (gimp_image_get_width (image), + gimp_image_get_height (image), + size, + size, + dot_for_dot, + xres, + yres, + width, + height, + NULL); +} + +gboolean +gimp_image_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + GimpImage *image = GIMP_IMAGE (viewable); + + if (gimp_image_get_width (image) > width || + gimp_image_get_height (image) > height) + { + gboolean scaling_up; + + gimp_viewable_calc_preview_size (gimp_image_get_width (image), + gimp_image_get_height (image), + width * 2, + height * 2, + dot_for_dot, 1.0, 1.0, + popup_width, + popup_height, + &scaling_up); + + if (scaling_up) + { + *popup_width = gimp_image_get_width (image); + *popup_height = gimp_image_get_height (image); + } + + return TRUE; + } + + return FALSE; +} + +GimpTempBuf * +gimp_image_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpImage *image = GIMP_IMAGE (viewable); + const Babl *format; + GimpTempBuf *buf; + gdouble scale_x; + gdouble scale_y; + + scale_x = (gdouble) width / (gdouble) gimp_image_get_width (image); + scale_y = (gdouble) height / (gdouble) gimp_image_get_height (image); + + format = gimp_image_get_preview_format (image); + + buf = gimp_temp_buf_new (width, height, format); + + gegl_buffer_get (gimp_pickable_get_buffer (GIMP_PICKABLE (image)), + GEGL_RECTANGLE (0, 0, width, height), + MIN (scale_x, scale_y), + gimp_temp_buf_get_format (buf), + gimp_temp_buf_get_data (buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + return buf; +} + +GdkPixbuf * +gimp_image_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpImage *image = GIMP_IMAGE (viewable); + GdkPixbuf *pixbuf; + gdouble scale_x; + gdouble scale_y; + GimpColorTransform *transform; + + scale_x = (gdouble) width / (gdouble) gimp_image_get_width (image); + scale_y = (gdouble) height / (gdouble) gimp_image_get_height (image); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + width, height); + + transform = gimp_image_get_color_transform_to_srgb_u8 (image); + + if (transform) + { + GimpTempBuf *temp_buf; + GeglBuffer *src_buf; + GeglBuffer *dest_buf; + + temp_buf = gimp_temp_buf_new (width, height, + gimp_pickable_get_format (GIMP_PICKABLE (image))); + + gegl_buffer_get (gimp_pickable_get_buffer (GIMP_PICKABLE (image)), + GEGL_RECTANGLE (0, 0, width, height), + MIN (scale_x, scale_y), + gimp_temp_buf_get_format (temp_buf), + gimp_temp_buf_get_data (temp_buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + src_buf = gimp_temp_buf_create_buffer (temp_buf); + dest_buf = gimp_pixbuf_create_buffer (pixbuf); + + gimp_temp_buf_unref (temp_buf); + + gimp_color_transform_process_buffer (transform, + src_buf, + GEGL_RECTANGLE (0, 0, + width, height), + dest_buf, + GEGL_RECTANGLE (0, 0, 0, 0)); + + g_object_unref (src_buf); + g_object_unref (dest_buf); + } + else + { + gegl_buffer_get (gimp_pickable_get_buffer (GIMP_PICKABLE (image)), + GEGL_RECTANGLE (0, 0, width, height), + MIN (scale_x, scale_y), + gimp_pixbuf_get_format (pixbuf), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + GEGL_ABYSS_CLAMP); + } + + return pixbuf; +} diff --git a/app/core/gimpimage-preview.h b/app/core/gimpimage-preview.h new file mode 100644 index 0000000..f8b22c0 --- /dev/null +++ b/app/core/gimpimage-preview.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_PREVIEW_H__ +#define __GIMP_IMAGE_PREVIEW_H__ + + +const Babl * gimp_image_get_preview_format (GimpImage *image); + + +/* + * virtual functions of GimpImage -- don't call directly + */ + +void gimp_image_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height); +gboolean gimp_image_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +GimpTempBuf * gimp_image_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +GdkPixbuf * gimp_image_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + + +#endif /* __GIMP_IMAGE_PREVIEW_H__ */ diff --git a/app/core/gimpimage-private.h b/app/core/gimpimage-private.h new file mode 100644 index 0000000..77687f0 --- /dev/null +++ b/app/core/gimpimage-private.h @@ -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 . + */ + +#ifndef __GIMP_IMAGE_PRIVATE_H__ +#define __GIMP_IMAGE_PRIVATE_H__ + + +typedef struct _GimpImageFlushAccumulator GimpImageFlushAccumulator; + +struct _GimpImageFlushAccumulator +{ + gboolean alpha_changed; + gboolean mask_changed; + gboolean floating_selection_changed; + gboolean preview_invalidated; +}; + + +struct _GimpImagePrivate +{ + gint ID; /* provides a unique ID */ + + GimpPlugInProcedure *load_proc; /* procedure used for loading */ + GimpPlugInProcedure *save_proc; /* last save procedure used */ + GimpPlugInProcedure *export_proc; /* last export procedure used */ + + gchar *display_name; /* display basename */ + gchar *display_path; /* display full path */ + gint width; /* width in pixels */ + gint height; /* height in pixels */ + gdouble xresolution; /* image x-res, in dpi */ + gdouble yresolution; /* image y-res, in dpi */ + GimpUnit resolution_unit; /* resolution unit */ + gboolean resolution_set; /* resolution explicitly set */ + GimpImageBaseType base_type; /* base gimp_image type */ + GimpPrecision precision; /* image's precision */ + GimpLayerMode new_layer_mode; /* default mode of new layers */ + + gint show_all; /* render full image content */ + GeglRectangle bounding_box; /* image content bounding box */ + gint bounding_box_freeze_count; + gboolean bounding_box_update_pending; + GeglBuffer *pickable_buffer; + + guchar *colormap; /* colormap (for indexed) */ + gint n_colors; /* # of colors (for indexed) */ + GimpPalette *palette; /* palette of colormap */ + const Babl *babl_palette_rgb; /* palette's RGB Babl format */ + const Babl *babl_palette_rgba; /* palette's RGBA Babl format */ + + gboolean is_color_managed; /* is this image color managed */ + GimpColorProfile *color_profile; /* image's color profile */ + gboolean converting; /* color model or profile in middle of conversion? */ + + /* Cached color transforms: from layer to sRGB u8 and double, and back */ + gboolean color_transforms_created; + GimpColorTransform *transform_to_srgb_u8; + GimpColorTransform *transform_from_srgb_u8; + GimpColorTransform *transform_to_srgb_double; + GimpColorTransform *transform_from_srgb_double; + + GimpMetadata *metadata; /* image's metadata */ + + GFile *file; /* the image's XCF file */ + GFile *imported_file; /* the image's source file */ + GFile *exported_file; /* the image's export file */ + GFile *save_a_copy_file; /* the image's save-a-copy file */ + GFile *untitled_file; /* a file saying "Untitled" */ + + gboolean xcf_compression; /* XCF compression enabled? */ + + gint dirty; /* dirty flag -- # of ops */ + gint64 dirty_time; /* time when image became dirty */ + gint export_dirty; /* 'dirty' but for export */ + + gint undo_freeze_count; /* counts the _freeze's */ + + gint instance_count; /* number of instances */ + gint disp_count; /* number of displays */ + + GimpTattoo tattoo_state; /* the last used tattoo */ + + GimpProjection *projection; /* projection layers & channels */ + GeglNode *graph; /* GEGL projection graph */ + GeglNode *visible_mask; /* component visibility node */ + + GList *symmetries; /* Painting symmetries */ + GimpSymmetry *active_symmetry; /* Active symmetry */ + + GList *guides; /* guides */ + GimpGrid *grid; /* grid */ + GList *sample_points; /* color sample points */ + + /* Layer/Channel attributes */ + GimpItemTree *layers; /* the tree of layers */ + GimpItemTree *channels; /* the tree of masks */ + GimpItemTree *vectors; /* the tree of vectors */ + GSList *layer_stack; /* the layers in MRU order */ + + GQuark layer_offset_x_handler; + GQuark layer_offset_y_handler; + GQuark layer_bounding_box_handler; + GQuark layer_alpha_handler; + GQuark channel_name_changed_handler; + GQuark channel_color_changed_handler; + + GimpLayer *floating_sel; /* the FS layer */ + GimpChannel *selection_mask; /* the selection mask channel */ + + GimpParasiteList *parasites; /* Plug-in parasite data */ + + gboolean visible[MAX_CHANNELS]; /* visible channels */ + gboolean active[MAX_CHANNELS]; /* active channels */ + + gboolean quick_mask_state; /* TRUE if quick mask is on */ + gboolean quick_mask_inverted; /* TRUE if quick mask is inverted */ + GimpRGB quick_mask_color; /* rgba triplet of the color */ + + /* Undo apparatus */ + GimpUndoStack *undo_stack; /* stack for undo operations */ + GimpUndoStack *redo_stack; /* stack for redo operations */ + gint group_count; /* nested undo groups */ + GimpUndoType pushing_undo_group; /* undo group status flag */ + + /* Signal emission accumulator */ + GimpImageFlushAccumulator flush_accum; +}; + +#define GIMP_IMAGE_GET_PRIVATE(image) (((GimpImage *) (image))->priv) + +void gimp_image_take_mask (GimpImage *image, + GimpChannel *mask); + + +#endif /* __GIMP_IMAGE_PRIVATE_H__ */ diff --git a/app/core/gimpimage-quick-mask.c b/app/core/gimpimage-quick-mask.c new file mode 100644 index 0000000..9473bde --- /dev/null +++ b/app/core/gimpimage-quick-mask.c @@ -0,0 +1,212 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpchannel.h" +#include "gimpimage.h" +#include "gimpimage-private.h" +#include "gimpimage-quick-mask.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimplayer-floating-selection.h" + +#include "gimp-intl.h" + + +#define CHANNEL_WAS_ACTIVE (0x2) + + +/* public functions */ + +void +gimp_image_set_quick_mask_state (GimpImage *image, + gboolean active) +{ + GimpImagePrivate *private; + GimpChannel *selection; + GimpChannel *mask; + gboolean channel_was_active; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + if (active == gimp_image_get_quick_mask_state (image)) + return; + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* Keep track of the state so that we can make the right drawable + * active again when deactiviting quick mask (see bug #134371). + */ + if (private->quick_mask_state) + channel_was_active = (private->quick_mask_state & CHANNEL_WAS_ACTIVE) != 0; + else + channel_was_active = gimp_image_get_active_channel (image) != NULL; + + /* Set private->quick_mask_state early so we can return early when + * being called recursively. + */ + private->quick_mask_state = (active + ? TRUE | (channel_was_active ? + CHANNEL_WAS_ACTIVE : 0) + : FALSE); + + selection = gimp_image_get_mask (image); + mask = gimp_image_get_quick_mask (image); + + if (active) + { + if (! mask) + { + GimpLayer *floating_sel; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, + C_("undo-type", "Enable Quick Mask")); + + floating_sel = gimp_image_get_floating_selection (image); + + if (floating_sel) + floating_sel_to_layer (floating_sel, NULL); + + mask = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (selection), + GIMP_TYPE_CHANNEL)); + + if (! gimp_channel_is_empty (selection)) + gimp_channel_clear (selection, NULL, TRUE); + + gimp_channel_set_color (mask, &private->quick_mask_color, FALSE); + gimp_item_rename (GIMP_ITEM (mask), GIMP_IMAGE_QUICK_MASK_NAME, + NULL); + + if (private->quick_mask_inverted) + gimp_channel_invert (mask, FALSE); + + gimp_image_add_channel (image, mask, NULL, 0, TRUE); + + gimp_image_undo_group_end (image); + } + } + else + { + if (mask) + { + GimpLayer *floating_sel = gimp_image_get_floating_selection (image); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, + C_("undo-type", "Disable Quick Mask")); + + if (private->quick_mask_inverted) + gimp_channel_invert (mask, TRUE); + + if (floating_sel && + gimp_layer_get_floating_sel_drawable (floating_sel) == GIMP_DRAWABLE (mask)) + floating_sel_anchor (floating_sel); + + gimp_item_to_selection (GIMP_ITEM (mask), + GIMP_CHANNEL_OP_REPLACE, + TRUE, FALSE, 0.0, 0.0); + gimp_image_remove_channel (image, mask, TRUE, NULL); + + if (! channel_was_active) + gimp_image_unset_active_channel (image); + + gimp_image_undo_group_end (image); + } + } + + gimp_image_quick_mask_changed (image); +} + +gboolean +gimp_image_get_quick_mask_state (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_state; +} + +void +gimp_image_set_quick_mask_color (GimpImage *image, + const GimpRGB *color) +{ + GimpChannel *quick_mask; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (color != NULL); + + GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_color = *color; + + quick_mask = gimp_image_get_quick_mask (image); + if (quick_mask) + gimp_channel_set_color (quick_mask, color, TRUE); +} + +void +gimp_image_get_quick_mask_color (GimpImage *image, + GimpRGB *color) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (color != NULL); + + *color = GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_color; +} + +GimpChannel * +gimp_image_get_quick_mask (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_get_channel_by_name (image, GIMP_IMAGE_QUICK_MASK_NAME); +} + +void +gimp_image_quick_mask_invert (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->quick_mask_state) + { + GimpChannel *quick_mask = gimp_image_get_quick_mask (image); + + if (quick_mask) + gimp_channel_invert (quick_mask, TRUE); + } + + private->quick_mask_inverted = ! private->quick_mask_inverted; +} + +gboolean +gimp_image_get_quick_mask_inverted (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_inverted; +} diff --git a/app/core/gimpimage-quick-mask.h b/app/core/gimpimage-quick-mask.h new file mode 100644 index 0000000..0eca62e --- /dev/null +++ b/app/core/gimpimage-quick-mask.h @@ -0,0 +1,43 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_QUICK_MASK_H__ +#define __GIMP_IMAGE_QUICK_MASK_H__ + + +/* don't change this string, it's used to identify the Quick Mask + * when opening files. + */ +#define GIMP_IMAGE_QUICK_MASK_NAME "Qmask" + + +void gimp_image_set_quick_mask_state (GimpImage *image, + gboolean active); +gboolean gimp_image_get_quick_mask_state (GimpImage *image); + +void gimp_image_set_quick_mask_color (GimpImage *image, + const GimpRGB *color); +void gimp_image_get_quick_mask_color (GimpImage *image, + GimpRGB *color); + +GimpChannel * gimp_image_get_quick_mask (GimpImage *image); + +void gimp_image_quick_mask_invert (GimpImage *image); +gboolean gimp_image_get_quick_mask_inverted (GimpImage *image); + + +#endif /* __GIMP_IMAGE_QUICK_MASK_H__ */ diff --git a/app/core/gimpimage-resize.c b/app/core/gimpimage-resize.c new file mode 100644 index 0000000..e1c46ca --- /dev/null +++ b/app/core/gimpimage-resize.c @@ -0,0 +1,327 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-item-list.h" +#include "gimpimage-resize.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" +#include "gimpsamplepoint.h" + +#include "text/gimptextlayer.h" + +#include "gimp-intl.h" + + +void +gimp_image_resize (GimpImage *image, + GimpContext *context, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y, + GimpProgress *progress) +{ + gimp_image_resize_with_layers (image, context, GIMP_FILL_TRANSPARENT, + new_width, new_height, offset_x, offset_y, + GIMP_ITEM_SET_NONE, TRUE, + progress); +} + +void +gimp_image_resize_with_layers (GimpImage *image, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y, + GimpItemSet layer_set, + gboolean resize_text_layers, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GList *resize_layers; + GList *list; + GimpItem *item; + gint old_width, old_height; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (new_width > 0 && new_height > 0); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + gimp_set_busy (image->gimp); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_RESIZE, + C_("undo-type", "Resize Image")); + + resize_layers = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_LAYERS, + layer_set); + + old_width = gimp_image_get_width (image); + old_height = gimp_image_get_height (image); + + /* Push the image size to the stack */ + gimp_image_undo_push_image_size (image, NULL, + -offset_x, -offset_y, + new_width, new_height); + + /* Set the new width and height */ + g_object_set (image, + "width", new_width, + "height", new_height, + NULL); + + /* Reposition all layers */ + for (list = gimp_image_get_layer_iter (image); + list; + list = g_list_next (list)) + { + GimpItem *item = list->data; + + gimp_item_translate (item, offset_x, offset_y, TRUE); + } + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + for (list = resize_layers; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + + /* group layers can't be resized here */ + if (gimp_viewable_get_children (GIMP_VIEWABLE (item))) + continue; + + if (! resize_text_layers && gimp_item_is_text_layer (item)) + continue; + + /* note that we call gimp_item_start_move(), and not + * gimp_item_start_transform(). see the comment in gimp_item_resize() + * for more information. + */ + gimp_item_start_move (item, TRUE); + + gimp_object_queue_push (queue, item); + } + + g_list_free (resize_layers); + + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + gimp_object_queue_push_container (queue, gimp_image_get_vectors (image)); + + /* Resize all resize_layers, channels (including selection mask), and + * vectors + */ + while ((item = gimp_object_queue_pop (queue))) + { + if (GIMP_IS_LAYER (item)) + { + gint old_offset_x; + gint old_offset_y; + + gimp_item_get_offset (item, &old_offset_x, &old_offset_y); + + gimp_item_resize (item, context, fill_type, + new_width, new_height, + old_offset_x, old_offset_y); + + gimp_item_end_move (item, TRUE); + } + else + { + gimp_item_resize (item, context, GIMP_FILL_TRANSPARENT, + new_width, new_height, offset_x, offset_y); + } + } + + /* Reposition or remove all guides */ + list = gimp_image_get_guides (image); + + while (list) + { + GimpGuide *guide = list->data; + gboolean remove_guide = FALSE; + gint new_position = gimp_guide_get_position (guide); + + list = g_list_next (list); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + new_position += offset_y; + if (new_position < 0 || new_position > new_height) + remove_guide = TRUE; + break; + + case GIMP_ORIENTATION_VERTICAL: + new_position += offset_x; + if (new_position < 0 || new_position > new_width) + remove_guide = TRUE; + break; + + default: + break; + } + + if (remove_guide) + gimp_image_remove_guide (image, guide, TRUE); + else if (new_position != gimp_guide_get_position (guide)) + gimp_image_move_guide (image, guide, new_position, TRUE); + } + + /* Reposition or remove sample points */ + list = gimp_image_get_sample_points (image); + + while (list) + { + GimpSamplePoint *sample_point = list->data; + gboolean remove_sample_point = FALSE; + gint old_x; + gint old_y; + gint new_x; + gint new_y; + + list = g_list_next (list); + + gimp_sample_point_get_position (sample_point, &old_x, &old_y); + + new_y = old_y + offset_y; + if ((new_y < 0) || (new_y >= new_height)) + remove_sample_point = TRUE; + + new_x = old_x + offset_x; + if ((new_x < 0) || (new_x >= new_width)) + remove_sample_point = TRUE; + + if (remove_sample_point) + gimp_image_remove_sample_point (image, sample_point, TRUE); + else if (new_x != old_x || new_y != old_y) + gimp_image_move_sample_point (image, sample_point, + new_x, new_y, TRUE); + } + + g_object_unref (queue); + + gimp_image_undo_group_end (image); + + gimp_image_size_changed_detailed (image, + offset_x, offset_y, + old_width, old_height); + + g_object_thaw_notify (G_OBJECT (image)); + + gimp_unset_busy (image->gimp); +} + +void +gimp_image_resize_to_layers (GimpImage *image, + GimpContext *context, + gint *offset_x, + gint *offset_y, + gint *new_width, + gint *new_height, + GimpProgress *progress) +{ + GList *list; + GimpItem *item; + gint x, y; + gint width, height; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + list = gimp_image_get_layer_iter (image); + + if (! list) + return; + + /* figure out starting values */ + item = list->data; + + x = gimp_item_get_offset_x (item); + y = gimp_item_get_offset_y (item); + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + + /* Respect all layers */ + for (list = g_list_next (list); list; list = g_list_next (list)) + { + item = list->data; + + gimp_rectangle_union (x, y, + width, height, + gimp_item_get_offset_x (item), + gimp_item_get_offset_y (item), + gimp_item_get_width (item), + gimp_item_get_height (item), + &x, &y, + &width, &height); + } + + gimp_image_resize (image, context, + width, height, -x, -y, + progress); + if (offset_x) + *offset_x = -x; + if (offset_y) + *offset_y = -y; + if (new_width) + *new_width = width; + if (new_height) + *new_height = height; +} + +void +gimp_image_resize_to_selection (GimpImage *image, + GimpContext *context, + GimpProgress *progress) +{ + GimpChannel *selection = gimp_image_get_mask (image); + gint x, y, w, h; + + if (gimp_item_bounds (GIMP_ITEM (selection), &x, &y, &w, &h)) + { + gimp_image_resize (image, context, + w, h, -x, -y, + progress); + } +} diff --git a/app/core/gimpimage-resize.h b/app/core/gimpimage-resize.h new file mode 100644 index 0000000..edcc64c --- /dev/null +++ b/app/core/gimpimage-resize.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_RESIZE_H__ +#define __GIMP_IMAGE_RESIZE_H__ + + +void gimp_image_resize (GimpImage *image, + GimpContext *context, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y, + GimpProgress *progress); + +void gimp_image_resize_with_layers (GimpImage *image, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y, + GimpItemSet layer_set, + gboolean resize_text_layers, + GimpProgress *progress); + +void gimp_image_resize_to_layers (GimpImage *image, + GimpContext *context, + gint *offset_x, + gint *offset_y, + gint *new_width, + gint *new_height, + GimpProgress *progress); +void gimp_image_resize_to_selection (GimpImage *image, + GimpContext *context, + GimpProgress *progress); + + +#endif /* __GIMP_IMAGE_RESIZE_H__ */ diff --git a/app/core/gimpimage-rotate.c b/app/core/gimpimage-rotate.c new file mode 100644 index 0000000..682319f --- /dev/null +++ b/app/core/gimpimage-rotate.c @@ -0,0 +1,377 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "vectors/gimpvectors.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-rotate.h" +#include "gimpimage-guides.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimplayer.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" +#include "gimpsamplepoint.h" + + +static void gimp_image_rotate_item_offset (GimpImage *image, + GimpRotationType rotate_type, + GimpItem *item, + gint off_x, + gint off_y); +static void gimp_image_rotate_guides (GimpImage *image, + GimpRotationType rotate_type); +static void gimp_image_rotate_sample_points (GimpImage *image, + GimpRotationType rotate_type); + + +void +gimp_image_rotate (GimpImage *image, + GimpContext *context, + GimpRotationType rotate_type, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GimpItem *item; + GList *list; + gdouble center_x; + gdouble center_y; + gint new_image_width; + gint new_image_height; + gint previous_image_width; + gint previous_image_height; + gint offset_x; + gint offset_y; + gboolean size_changed; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + previous_image_width = gimp_image_get_width (image); + previous_image_height = gimp_image_get_height (image); + + center_x = previous_image_width / 2.0; + center_y = previous_image_height / 2.0; + + /* Resize the image (if needed) */ + switch (rotate_type) + { + case GIMP_ROTATE_90: + case GIMP_ROTATE_270: + new_image_width = gimp_image_get_height (image); + new_image_height = gimp_image_get_width (image); + size_changed = TRUE; + offset_x = (gimp_image_get_width (image) - new_image_width) / 2; + offset_y = (gimp_image_get_height (image) - new_image_height) / 2; + break; + + case GIMP_ROTATE_180: + new_image_width = gimp_image_get_width (image); + new_image_height = gimp_image_get_height (image); + size_changed = FALSE; + offset_x = 0; + offset_y = 0; + break; + + default: + g_return_if_reached (); + return; + } + + gimp_set_busy (image->gimp); + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, gimp_image_get_layers (image)); + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + gimp_object_queue_push_container (queue, gimp_image_get_vectors (image)); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ROTATE, NULL); + + /* Rotate all layers, channels (including selection mask), and vectors */ + while ((item = gimp_object_queue_pop (queue))) + { + gint off_x; + gint off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + gimp_item_rotate (item, context, rotate_type, center_x, center_y, FALSE); + + if (GIMP_IS_LAYER (item)) + { + gimp_image_rotate_item_offset (image, rotate_type, item, off_x, off_y); + } + else + { + gimp_item_set_offset (item, 0, 0); + + if (GIMP_IS_VECTORS (item)) + { + gimp_item_set_size (item, new_image_width, new_image_height); + + gimp_item_translate (item, + (new_image_width - gimp_image_get_width (image)) / 2, + (new_image_height - gimp_image_get_height (image)) / 2, + FALSE); + } + } + + gimp_progress_set_value (progress, 1.0); + } + + /* Rotate all Guides */ + gimp_image_rotate_guides (image, rotate_type); + + /* Rotate all sample points */ + gimp_image_rotate_sample_points (image, rotate_type); + + /* Resize the image (if needed) */ + if (size_changed) + { + gdouble xres; + gdouble yres; + + gimp_image_undo_push_image_size (image, + NULL, + offset_x, + offset_y, + new_image_width, + new_image_height); + + g_object_set (image, + "width", new_image_width, + "height", new_image_height, + NULL); + + gimp_image_get_resolution (image, &xres, &yres); + + if (xres != yres) + gimp_image_set_resolution (image, yres, xres); + } + + /* Notify guide movements */ + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + gimp_image_guide_moved (image, list->data); + } + + /* Notify sample point movements */ + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + gimp_image_sample_point_moved (image, list->data); + } + + gimp_image_undo_group_end (image); + + g_object_unref (queue); + + if (size_changed) + gimp_image_size_changed_detailed (image, + -offset_x, + -offset_y, + previous_image_width, + previous_image_height); + + g_object_thaw_notify (G_OBJECT (image)); + + gimp_unset_busy (image->gimp); +} + + +static void +gimp_image_rotate_item_offset (GimpImage *image, + GimpRotationType rotate_type, + GimpItem *item, + gint off_x, + gint off_y) +{ + gint x = 0; + gint y = 0; + + switch (rotate_type) + { + case GIMP_ROTATE_90: + x = gimp_image_get_height (image) - off_y - gimp_item_get_width (item); + y = off_x; + break; + + case GIMP_ROTATE_270: + x = off_y; + y = gimp_image_get_width (image) - off_x - gimp_item_get_height (item); + break; + + case GIMP_ROTATE_180: + return; + + default: + g_return_if_reached (); + } + + gimp_item_get_offset (item, &off_x, &off_y); + + x -= off_x; + y -= off_y; + + if (x || y) + gimp_item_translate (item, x, y, FALSE); +} + +static void +gimp_image_rotate_guides (GimpImage *image, + GimpRotationType rotate_type) +{ + GList *list; + + /* Rotate all Guides */ + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + GimpGuide *guide = list->data; + GimpOrientationType orientation = gimp_guide_get_orientation (guide); + gint position = gimp_guide_get_position (guide); + + switch (rotate_type) + { + case GIMP_ROTATE_90: + switch (orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_undo_push_guide (image, NULL, guide); + gimp_guide_set_orientation (guide, GIMP_ORIENTATION_VERTICAL); + gimp_guide_set_position (guide, + gimp_image_get_height (image) - position); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_image_undo_push_guide (image, NULL, guide); + gimp_guide_set_orientation (guide, GIMP_ORIENTATION_HORIZONTAL); + break; + + default: + break; + } + break; + + case GIMP_ROTATE_180: + switch (orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_move_guide (image, guide, + gimp_image_get_height (image) - position, + TRUE); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_image_move_guide (image, guide, + gimp_image_get_width (image) - position, + TRUE); + break; + + default: + break; + } + break; + + case GIMP_ROTATE_270: + switch (orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_undo_push_guide (image, NULL, guide); + gimp_guide_set_orientation (guide, GIMP_ORIENTATION_VERTICAL); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_image_undo_push_guide (image, NULL, guide); + gimp_guide_set_orientation (guide, GIMP_ORIENTATION_HORIZONTAL); + gimp_guide_set_position (guide, + gimp_image_get_width (image) - position); + break; + + default: + break; + } + break; + } + } +} + + +static void +gimp_image_rotate_sample_points (GimpImage *image, + GimpRotationType rotate_type) +{ + GList *list; + + /* Rotate all sample points */ + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + GimpSamplePoint *sample_point = list->data; + gint old_x; + gint old_y; + + gimp_image_undo_push_sample_point (image, NULL, sample_point); + + gimp_sample_point_get_position (sample_point, &old_x, &old_y); + + switch (rotate_type) + { + case GIMP_ROTATE_90: + gimp_sample_point_set_position (sample_point, + gimp_image_get_height (image) - old_y, + old_x); + break; + + case GIMP_ROTATE_180: + gimp_sample_point_set_position (sample_point, + gimp_image_get_width (image) - old_x, + gimp_image_get_height (image) - old_y); + break; + + case GIMP_ROTATE_270: + gimp_sample_point_set_position (sample_point, + old_y, + gimp_image_get_width (image) - old_x); + break; + } + } +} diff --git a/app/core/gimpimage-rotate.h b/app/core/gimpimage-rotate.h new file mode 100644 index 0000000..cad0de8 --- /dev/null +++ b/app/core/gimpimage-rotate.h @@ -0,0 +1,28 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_ROTATE_H__ +#define __GIMP_IMAGE_ROTATE_H__ + + +void gimp_image_rotate (GimpImage *image, + GimpContext *context, + GimpRotationType rotate_type, + GimpProgress *progress); + + +#endif /* __GIMP_IMAGE_ROTATE_H__ */ diff --git a/app/core/gimpimage-sample-points.c b/app/core/gimpimage-sample-points.c new file mode 100644 index 0000000..24fc66f --- /dev/null +++ b/app/core/gimpimage-sample-points.c @@ -0,0 +1,213 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpimage.h" +#include "gimpimage-private.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-undo-push.h" +#include "gimpsamplepoint.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GimpSamplePoint * +gimp_image_add_sample_point_at_pos (GimpImage *image, + gint x, + gint y, + gboolean push_undo) +{ + GimpSamplePoint *sample_point; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (x >= 0 && x < gimp_image_get_width (image), NULL); + g_return_val_if_fail (y >= 0 && y < gimp_image_get_height (image), NULL); + + sample_point = gimp_sample_point_new (image->gimp->next_sample_point_ID++); + + if (push_undo) + gimp_image_undo_push_sample_point (image, C_("undo-type", "Add Sample Point"), + sample_point); + + gimp_image_add_sample_point (image, sample_point, x, y); + g_object_unref (sample_point); + + return sample_point; +} + +void +gimp_image_add_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gint x, + gint y) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->sample_points = g_list_append (private->sample_points, sample_point); + + gimp_sample_point_set_position (sample_point, x, y); + g_object_ref (sample_point); + + gimp_image_sample_point_added (image, sample_point); +} + +void +gimp_image_remove_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (push_undo) + gimp_image_undo_push_sample_point (image, + C_("undo-type", "Remove Sample Point"), + sample_point); + + private->sample_points = g_list_remove (private->sample_points, sample_point); + gimp_aux_item_removed (GIMP_AUX_ITEM (sample_point)); + + gimp_image_sample_point_removed (image, sample_point); + + gimp_sample_point_set_position (sample_point, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED); + g_object_unref (sample_point); +} + +void +gimp_image_move_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gint x, + gint y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + g_return_if_fail (x >= 0); + g_return_if_fail (y >= 0); + g_return_if_fail (x < gimp_image_get_width (image)); + g_return_if_fail (y < gimp_image_get_height (image)); + + if (push_undo) + gimp_image_undo_push_sample_point (image, + C_("undo-type", "Move Sample Point"), + sample_point); + + gimp_sample_point_set_position (sample_point, x, y); + + gimp_image_sample_point_moved (image, sample_point); +} + +void +gimp_image_set_sample_point_pick_mode (GimpImage *image, + GimpSamplePoint *sample_point, + GimpColorPickMode pick_mode, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + if (push_undo) + gimp_image_undo_push_sample_point (image, + C_("undo-type", + "Set Sample Point Pick Mode"), + sample_point); + + gimp_sample_point_set_pick_mode (sample_point, pick_mode); + + /* well... */ + gimp_image_sample_point_moved (image, sample_point); +} + +GList * +gimp_image_get_sample_points (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->sample_points; +} + +GimpSamplePoint * +gimp_image_get_sample_point (GimpImage *image, + guint32 id) +{ + GList *sample_points; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + for (sample_points = GIMP_IMAGE_GET_PRIVATE (image)->sample_points; + sample_points; + sample_points = g_list_next (sample_points)) + { + GimpSamplePoint *sample_point = sample_points->data; + + if (gimp_aux_item_get_ID (GIMP_AUX_ITEM (sample_point)) == id) + return sample_point; + } + + return NULL; +} + +GimpSamplePoint * +gimp_image_get_next_sample_point (GimpImage *image, + guint32 id, + gboolean *sample_point_found) +{ + GList *sample_points; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (sample_point_found != NULL, NULL); + + if (id == 0) + *sample_point_found = TRUE; + else + *sample_point_found = FALSE; + + for (sample_points = GIMP_IMAGE_GET_PRIVATE (image)->sample_points; + sample_points; + sample_points = g_list_next (sample_points)) + { + GimpSamplePoint *sample_point = sample_points->data; + + if (*sample_point_found) /* this is the first guide after the found one */ + return sample_point; + + if (gimp_aux_item_get_ID (GIMP_AUX_ITEM (sample_point)) == id) /* found it, next one will be returned */ + *sample_point_found = TRUE; + } + + return NULL; +} diff --git a/app/core/gimpimage-sample-points.h b/app/core/gimpimage-sample-points.h new file mode 100644 index 0000000..c84a02b --- /dev/null +++ b/app/core/gimpimage-sample-points.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 . + */ + +#ifndef __GIMP_IMAGE_SAMPLE_POINTS_H__ +#define __GIMP_IMAGE_SAMPLE_POINTS_H__ + + +/* public sample point adding API + */ +GimpSamplePoint * gimp_image_add_sample_point_at_pos (GimpImage *image, + gint x, + gint y, + gboolean push_undo); + +/* internal sample point adding API, does not check the sample + * point's position and is publicly declared only to be used from + * undo + */ +void gimp_image_add_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gint x, + gint y); + +void gimp_image_remove_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gboolean push_undo); +void gimp_image_move_sample_point (GimpImage *image, + GimpSamplePoint *sample_point, + gint x, + gint y, + gboolean push_undo); +void gimp_image_set_sample_point_pick_mode + (GimpImage *image, + GimpSamplePoint *sample_point, + GimpColorPickMode pick_mode, + gboolean push_undo); + +GList * gimp_image_get_sample_points (GimpImage *image); +GimpSamplePoint * gimp_image_get_sample_point (GimpImage *image, + guint32 id); +GimpSamplePoint * gimp_image_get_next_sample_point (GimpImage *image, + guint32 id, + gboolean *sample_point_found); + + +#endif /* __GIMP_IMAGE_SAMPLE_POINTS_H__ */ diff --git a/app/core/gimpimage-scale.c b/app/core/gimpimage-scale.c new file mode 100644 index 0000000..04ae6fb --- /dev/null +++ b/app/core/gimpimage-scale.c @@ -0,0 +1,260 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpguide.h" +#include "gimpgrouplayer.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-scale.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" +#include "gimpprojection.h" +#include "gimpsamplepoint.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +void +gimp_image_scale (GimpImage *image, + gint new_width, + gint new_height, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GimpItem *item; + GList *list; + gint old_width; + gint old_height; + gint offset_x; + gint offset_y; + gdouble img_scale_w = 1.0; + gdouble img_scale_h = 1.0; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (new_width > 0 && new_height > 0); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + gimp_set_busy (image->gimp); + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, gimp_image_get_layers (image)); + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + gimp_object_queue_push_container (queue, gimp_image_get_vectors (image)); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_SCALE, + C_("undo-type", "Scale Image")); + + old_width = gimp_image_get_width (image); + old_height = gimp_image_get_height (image); + img_scale_w = (gdouble) new_width / (gdouble) old_width; + img_scale_h = (gdouble) new_height / (gdouble) old_height; + + offset_x = (old_width - new_width) / 2; + offset_y = (old_height - new_height) / 2; + + /* Push the image size to the stack */ + gimp_image_undo_push_image_size (image, + NULL, + offset_x, + offset_y, + new_width, + new_height); + + /* Set the new width and height early, so below image item setters + * (esp. guides and sample points) don't choke about moving stuff + * out of the image + */ + g_object_set (image, + "width", new_width, + "height", new_height, + NULL); + + /* Scale all layers, channels (including selection mask), and vectors */ + while ((item = gimp_object_queue_pop (queue))) + { + if (! gimp_item_scale_by_factors (item, + img_scale_w, img_scale_h, + interpolation_type, progress)) + { + /* Since 0 < img_scale_w, img_scale_h, failure due to one or more + * vanishing scaled layer dimensions. Implicit delete implemented + * here. Upstream warning implemented in resize_check_layer_scaling(), + * which offers the user the chance to bail out. + */ + g_return_if_fail (GIMP_IS_LAYER (item)); + gimp_image_remove_layer (image, GIMP_LAYER (item), TRUE, NULL); + } + } + + /* Scale all Guides */ + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_image_move_guide (image, guide, + (position * new_height) / old_height, + TRUE); + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_image_move_guide (image, guide, + (position * new_width) / old_width, + TRUE); + break; + + default: + break; + } + } + + /* Scale all sample points */ + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + GimpSamplePoint *sample_point = list->data; + gint x; + gint y; + + gimp_sample_point_get_position (sample_point, &x, &y); + + gimp_image_move_sample_point (image, sample_point, + x * new_width / old_width, + y * new_height / old_height, + TRUE); + } + + gimp_image_undo_group_end (image); + + g_object_unref (queue); + + gimp_image_size_changed_detailed (image, + -offset_x, + -offset_y, + old_width, + old_height); + + g_object_thaw_notify (G_OBJECT (image)); + + gimp_unset_busy (image->gimp); +} + +/** + * gimp_image_scale_check: + * @image: A #GimpImage. + * @new_width: The new width. + * @new_height: The new height. + * @max_memsize: The maximum new memory size. + * @new_memsize: The new memory size. + * + * Inventory the layer list in image and check that it may be + * scaled to @new_height and @new_width without problems. + * + * Return value: #GIMP_IMAGE_SCALE_OK if scaling the image will shrink none + * of its layers completely away, and the new image size + * is smaller than @max_memsize. + * #GIMP_IMAGE_SCALE_TOO_SMALL if scaling would remove some + * existing layers. + * #GIMP_IMAGE_SCALE_TOO_BIG if the new image size would + * exceed the maximum specified in the preferences. + **/ +GimpImageScaleCheckType +gimp_image_scale_check (GimpImage *image, + gint new_width, + gint new_height, + gint64 max_memsize, + gint64 *new_memsize) +{ + GList *all_layers; + GList *list; + gint64 current_size; + gint64 undo_size; + gint64 redo_size; + gint64 new_size; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_IMAGE_SCALE_TOO_SMALL); + g_return_val_if_fail (new_memsize != NULL, GIMP_IMAGE_SCALE_TOO_SMALL); + + current_size = gimp_object_get_memsize (GIMP_OBJECT (image), NULL); + + new_size = gimp_image_estimate_memsize (image, + gimp_image_get_component_type (image), + new_width, new_height); + + undo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_undo_stack (image)), NULL); + redo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_redo_stack (image)), NULL); + + current_size -= undo_size + redo_size; + new_size -= undo_size + redo_size; + + GIMP_LOG (IMAGE_SCALE, + "old_size = %"G_GINT64_FORMAT" new_size = %"G_GINT64_FORMAT, + current_size, new_size); + + *new_memsize = new_size; + + if (new_size > current_size && new_size > max_memsize) + return GIMP_IMAGE_SCALE_TOO_BIG; + + all_layers = gimp_image_get_layer_list (image); + + for (list = all_layers; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + + /* group layers are updated automatically */ + if (gimp_viewable_get_children (GIMP_VIEWABLE (item))) + continue; + + if (! gimp_item_check_scaling (item, new_width, new_height)) + { + g_list_free (all_layers); + + return GIMP_IMAGE_SCALE_TOO_SMALL; + } + } + + g_list_free (all_layers); + + return GIMP_IMAGE_SCALE_OK; +} diff --git a/app/core/gimpimage-scale.h b/app/core/gimpimage-scale.h new file mode 100644 index 0000000..1073e38 --- /dev/null +++ b/app/core/gimpimage-scale.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_SCALE_H__ +#define __GIMP_IMAGE_SCALE_H__ + + +void gimp_image_scale (GimpImage *image, + gint new_width, + gint new_height, + GimpInterpolationType interpolation_type, + GimpProgress *progress); + +GimpImageScaleCheckType + gimp_image_scale_check (GimpImage *image, + gint new_width, + gint new_height, + gint64 max_memsize, + gint64 *new_memsize); + + +#endif /* __GIMP_IMAGE_SCALE_H__ */ diff --git a/app/core/gimpimage-snap.c b/app/core/gimpimage-snap.c new file mode 100644 index 0000000..e35b646 --- /dev/null +++ b/app/core/gimpimage-snap.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpgrid.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-grid.h" +#include "gimpimage-guides.h" +#include "gimpimage-snap.h" + +#include "vectors/gimpstroke.h" +#include "vectors/gimpvectors.h" + +#include "gimp-intl.h" + + +static gboolean gimp_image_snap_distance (const gdouble unsnapped, + const gdouble nearest, + const gdouble epsilon, + gdouble *mindist, + gdouble *target); + + + +/* public functions */ + +gboolean +gimp_image_snap_x (GimpImage *image, + gdouble x, + gdouble *tx, + gdouble epsilon_x, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas) +{ + gdouble mindist = G_MAXDOUBLE; + gboolean snapped = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (tx != NULL, FALSE); + + *tx = x; + + if (! gimp_image_get_guides (image)) snap_to_guides = FALSE; + if (! gimp_image_get_grid (image)) snap_to_grid = FALSE; + + if (! (snap_to_guides || snap_to_grid || snap_to_canvas)) + return FALSE; + + if (x < -epsilon_x || x >= (gimp_image_get_width (image) + epsilon_x)) + return FALSE; + + if (snap_to_guides) + { + GList *list; + + for (list = gimp_image_get_guides (image); list; list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + + if (gimp_guide_is_custom (guide)) + continue; + + if (gimp_guide_get_orientation (guide) == GIMP_ORIENTATION_VERTICAL) + { + snapped |= gimp_image_snap_distance (x, position, + epsilon_x, + &mindist, tx); + } + } + } + + if (snap_to_grid) + { + GimpGrid *grid = gimp_image_get_grid (image); + gdouble xspacing; + gdouble xoffset; + + gimp_grid_get_spacing (grid, &xspacing, NULL); + gimp_grid_get_offset (grid, &xoffset, NULL); + + if (xspacing > 0.0) + { + gdouble nearest; + + nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing; + + snapped |= gimp_image_snap_distance (x, nearest, + epsilon_x, + &mindist, tx); + } + } + + if (snap_to_canvas) + { + snapped |= gimp_image_snap_distance (x, 0, + epsilon_x, + &mindist, tx); + snapped |= gimp_image_snap_distance (x, gimp_image_get_width (image), + epsilon_x, + &mindist, tx); + } + + return snapped; +} + +gboolean +gimp_image_snap_y (GimpImage *image, + gdouble y, + gdouble *ty, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas) +{ + gdouble mindist = G_MAXDOUBLE; + gboolean snapped = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (ty != NULL, FALSE); + + *ty = y; + + if (! gimp_image_get_guides (image)) snap_to_guides = FALSE; + if (! gimp_image_get_grid (image)) snap_to_grid = FALSE; + + if (! (snap_to_guides || snap_to_grid || snap_to_canvas)) + return FALSE; + + if (y < -epsilon_y || y >= (gimp_image_get_height (image) + epsilon_y)) + return FALSE; + + if (snap_to_guides) + { + GList *list; + + for (list = gimp_image_get_guides (image); list; list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + + if (gimp_guide_is_custom (guide)) + continue; + + if (gimp_guide_get_orientation (guide) == GIMP_ORIENTATION_HORIZONTAL) + { + snapped |= gimp_image_snap_distance (y, position, + epsilon_y, + &mindist, ty); + } + } + } + + if (snap_to_grid) + { + GimpGrid *grid = gimp_image_get_grid (image); + gdouble yspacing; + gdouble yoffset; + + gimp_grid_get_spacing (grid, NULL, &yspacing); + gimp_grid_get_offset (grid, NULL, &yoffset); + + if (yspacing > 0.0) + { + gdouble nearest; + + nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing; + + snapped |= gimp_image_snap_distance (y, nearest, + epsilon_y, + &mindist, ty); + } + } + + if (snap_to_canvas) + { + snapped |= gimp_image_snap_distance (y, 0, + epsilon_y, + &mindist, ty); + snapped |= gimp_image_snap_distance (y, gimp_image_get_height (image), + epsilon_y, + &mindist, ty); + } + + return snapped; +} + +gboolean +gimp_image_snap_point (GimpImage *image, + gdouble x, + gdouble y, + gdouble *tx, + gdouble *ty, + gdouble epsilon_x, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors, + gboolean show_all) +{ + gdouble mindist_x = G_MAXDOUBLE; + gdouble mindist_y = G_MAXDOUBLE; + gboolean snapped = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (tx != NULL, FALSE); + g_return_val_if_fail (ty != NULL, FALSE); + + *tx = x; + *ty = y; + + if (! gimp_image_get_guides (image)) snap_to_guides = FALSE; + if (! gimp_image_get_grid (image)) snap_to_grid = FALSE; + if (! gimp_image_get_active_vectors (image)) snap_to_vectors = FALSE; + + if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors)) + return FALSE; + + if (! show_all && + (x < -epsilon_x || x >= (gimp_image_get_width (image) + epsilon_x) || + y < -epsilon_y || y >= (gimp_image_get_height (image) + epsilon_y))) + { + /* Off-canvas grid is invisible unless "show all" option is + * enabled. So let's not snap to the invisible grid. + */ + snap_to_grid = FALSE; + snap_to_canvas = FALSE; + } + + if (snap_to_guides) + { + GList *list; + + for (list = gimp_image_get_guides (image); list; list = g_list_next (list)) + { + GimpGuide *guide = list->data; + gint position = gimp_guide_get_position (guide); + + if (gimp_guide_is_custom (guide)) + continue; + + switch (gimp_guide_get_orientation (guide)) + { + case GIMP_ORIENTATION_HORIZONTAL: + snapped |= gimp_image_snap_distance (y, position, + epsilon_y, + &mindist_y, ty); + break; + + case GIMP_ORIENTATION_VERTICAL: + snapped |= gimp_image_snap_distance (x, position, + epsilon_x, + &mindist_x, tx); + break; + + default: + break; + } + } + } + + if (snap_to_grid) + { + GimpGrid *grid = gimp_image_get_grid (image); + gdouble xspacing, yspacing; + gdouble xoffset, yoffset; + + gimp_grid_get_spacing (grid, &xspacing, &yspacing); + gimp_grid_get_offset (grid, &xoffset, &yoffset); + + if (xspacing > 0.0) + { + gdouble nearest; + + nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing; + + snapped |= gimp_image_snap_distance (x, nearest, + epsilon_x, + &mindist_x, tx); + } + + if (yspacing > 0.0) + { + gdouble nearest; + + nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing; + + snapped |= gimp_image_snap_distance (y, nearest, + epsilon_y, + &mindist_y, ty); + } + } + + if (snap_to_canvas) + { + snapped |= gimp_image_snap_distance (x, 0, + epsilon_x, + &mindist_x, tx); + snapped |= gimp_image_snap_distance (x, gimp_image_get_width (image), + epsilon_x, + &mindist_x, tx); + + snapped |= gimp_image_snap_distance (y, 0, + epsilon_y, + &mindist_y, ty); + snapped |= gimp_image_snap_distance (y, gimp_image_get_height (image), + epsilon_y, + &mindist_y, ty); + } + + if (snap_to_vectors) + { + GimpVectors *vectors = gimp_image_get_active_vectors (image); + GimpStroke *stroke = NULL; + GimpCoords coords = { 0, 0, 0, 0, 0 }; + + coords.x = x; + coords.y = y; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + GimpCoords nearest; + + if (gimp_stroke_nearest_point_get (stroke, &coords, 1.0, + &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (x, nearest.x, + epsilon_x, + &mindist_x, tx); + snapped |= gimp_image_snap_distance (y, nearest.y, + epsilon_y, + &mindist_y, ty); + } + } + } + + return snapped; +} + +gboolean +gimp_image_snap_rectangle (GimpImage *image, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *tx1, + gdouble *ty1, + gdouble epsilon_x, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors) +{ + gdouble nx, ny; + gdouble mindist_x = G_MAXDOUBLE; + gdouble mindist_y = G_MAXDOUBLE; + gdouble x_center = (x1 + x2) / 2.0; + gdouble y_center = (y1 + y2) / 2.0; + gboolean snapped = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (tx1 != NULL, FALSE); + g_return_val_if_fail (ty1 != NULL, FALSE); + + *tx1 = x1; + *ty1 = y1; + + if (! gimp_image_get_guides (image)) snap_to_guides = FALSE; + if (! gimp_image_get_grid (image)) snap_to_grid = FALSE; + if (! gimp_image_get_active_vectors (image)) snap_to_vectors = FALSE; + + if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors)) + return FALSE; + + /* left edge */ + if (gimp_image_snap_x (image, x1, &nx, + MIN (epsilon_x, mindist_x), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_x = ABS (nx - x1); + *tx1 = nx; + snapped = TRUE; + } + + /* right edge */ + if (gimp_image_snap_x (image, x2, &nx, + MIN (epsilon_x, mindist_x), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_x = ABS (nx - x2); + *tx1 = RINT (x1 + (nx - x2)); + snapped = TRUE; + } + + /* center, vertical */ + if (gimp_image_snap_x (image, x_center, &nx, + MIN (epsilon_x, mindist_x), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_x = ABS (nx - x_center); + *tx1 = RINT (x1 + (nx - x_center)); + snapped = TRUE; + } + + /* top edge */ + if (gimp_image_snap_y (image, y1, &ny, + MIN (epsilon_y, mindist_y), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_y = ABS (ny - y1); + *ty1 = ny; + snapped = TRUE; + } + + /* bottom edge */ + if (gimp_image_snap_y (image, y2, &ny, + MIN (epsilon_y, mindist_y), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_y = ABS (ny - y2); + *ty1 = RINT (y1 + (ny - y2)); + snapped = TRUE; + } + + /* center, horizontal */ + if (gimp_image_snap_y (image, y_center, &ny, + MIN (epsilon_y, mindist_y), + snap_to_guides, + snap_to_grid, + snap_to_canvas)) + { + mindist_y = ABS (ny - y_center); + *ty1 = RINT (y1 + (ny - y_center)); + snapped = TRUE; + } + + if (snap_to_vectors) + { + GimpVectors *vectors = gimp_image_get_active_vectors (image); + GimpStroke *stroke = NULL; + GimpCoords coords1 = GIMP_COORDS_DEFAULT_VALUES; + GimpCoords coords2 = GIMP_COORDS_DEFAULT_VALUES; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + GimpCoords nearest; + gdouble dist; + + /* top edge */ + + coords1.x = x1; + coords1.y = y1; + coords2.x = x2; + coords2.y = y1; + + if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (y1, nearest.y, + epsilon_y, + &mindist_y, ty1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (x1, nearest.x, + epsilon_x, + &mindist_x, tx1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.x - x2); + + if (dist < MIN (epsilon_x, mindist_x)) + { + mindist_x = dist; + *tx1 = RINT (x1 + (nearest.x - x2)); + snapped = TRUE; + } + } + + /* bottom edge */ + + coords1.x = x1; + coords1.y = y2; + coords2.x = x2; + coords2.y = y2; + + if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.y - y2); + + if (dist < MIN (epsilon_y, mindist_y)) + { + mindist_y = dist; + *ty1 = RINT (y1 + (nearest.y - y2)); + snapped = TRUE; + } + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (x1, nearest.x, + epsilon_x, + &mindist_x, tx1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.x - x2); + + if (dist < MIN (epsilon_x, mindist_x)) + { + mindist_x = dist; + *tx1 = RINT (x1 + (nearest.x - x2)); + snapped = TRUE; + } + } + + /* left edge */ + + coords1.x = x1; + coords1.y = y1; + coords2.x = x1; + coords2.y = y2; + + if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (x1, nearest.x, + epsilon_x, + &mindist_x, tx1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (y1, nearest.y, + epsilon_y, + &mindist_y, ty1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.y - y2); + + if (dist < MIN (epsilon_y, mindist_y)) + { + mindist_y = dist; + *ty1 = RINT (y1 + (nearest.y - y2)); + snapped = TRUE; + } + } + + /* right edge */ + + coords1.x = x2; + coords1.y = y1; + coords2.x = x2; + coords2.y = y2; + + if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.x - x2); + + if (dist < MIN (epsilon_x, mindist_x)) + { + mindist_x = dist; + *tx1 = RINT (x1 + (nearest.x - x2)); + snapped = TRUE; + } + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + snapped |= gimp_image_snap_distance (y1, nearest.y, + epsilon_y, + &mindist_y, ty1); + } + + if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1, + 1.0, &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.y - y2); + + if (dist < MIN (epsilon_y, mindist_y)) + { + mindist_y = dist; + *ty1 = RINT (y1 + (nearest.y - y2)); + snapped = TRUE; + } + } + + /* center */ + + coords1.x = x_center; + coords1.y = y_center; + + if (gimp_stroke_nearest_point_get (stroke, &coords1, 1.0, + &nearest, + NULL, NULL, NULL) >= 0) + { + if (gimp_image_snap_distance (x_center, nearest.x, + epsilon_x, + &mindist_x, &nx)) + { + mindist_x = ABS (nx - x_center); + *tx1 = RINT (x1 + (nx - x_center)); + snapped = TRUE; + } + + if (gimp_image_snap_distance (y_center, nearest.y, + epsilon_y, + &mindist_y, &ny)) + { + mindist_y = ABS (ny - y_center); + *ty1 = RINT (y1 + (ny - y_center)); + snapped = TRUE; + } + } + } + } + + return snapped; +} + +/* private functions */ + +/** + * gimp_image_snap_distance: + * @unsnapped: One coordinate of the unsnapped position + * @nearest: One coordinate of a snapping position candidate + * @epsilon: The snapping threshold + * @mindist: The distance to the currently closest snapping target + * @target: The currently closest snapping target + * + * Finds out if snapping occurs from position to a snapping candidate + * and sets the target accordingly. + * + * Return value: %TRUE if snapping occurred, %FALSE otherwise + */ +static gboolean +gimp_image_snap_distance (const gdouble unsnapped, + const gdouble nearest, + const gdouble epsilon, + gdouble *mindist, + gdouble *target) +{ + const gdouble dist = ABS (nearest - unsnapped); + + if (dist < MIN (epsilon, *mindist)) + { + *mindist = dist; + *target = nearest; + + return TRUE; + } + + return FALSE; +} diff --git a/app/core/gimpimage-snap.h b/app/core/gimpimage-snap.h new file mode 100644 index 0000000..b0d5e4b --- /dev/null +++ b/app/core/gimpimage-snap.h @@ -0,0 +1,63 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_SNAP_H__ +#define __GIMP_IMAGE_SNAP_H__ + + +gboolean gimp_image_snap_x (GimpImage *image, + gdouble x, + gdouble *tx, + gdouble epsilon_x, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas); +gboolean gimp_image_snap_y (GimpImage *image, + gdouble y, + gdouble *ty, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas); +gboolean gimp_image_snap_point (GimpImage *image, + gdouble x, + gdouble y, + gdouble *tx, + gdouble *ty, + gdouble epsilon_x, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors, + gboolean show_all); +gboolean gimp_image_snap_rectangle (GimpImage *image, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *tx1, + gdouble *ty1, + gdouble epsilon_x, + gdouble epsilon_y, + gboolean snap_to_guides, + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors); + + +#endif /* __GIMP_IMAGE_SNAP_H__ */ diff --git a/app/core/gimpimage-symmetry.c b/app/core/gimpimage-symmetry.c new file mode 100644 index 0000000..366f63e --- /dev/null +++ b/app/core/gimpimage-symmetry.c @@ -0,0 +1,189 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-symmetry.c + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpsymmetry.h" +#include "gimpimage.h" +#include "gimpimage-private.h" +#include "gimpimage-symmetry.h" +#include "gimpsymmetry-mandala.h" +#include "gimpsymmetry-mirror.h" +#include "gimpsymmetry-tiling.h" + + +/** + * gimp_image_symmetry_list: + * + * Returns a list of #GType of all existing symmetries. + **/ +GList * +gimp_image_symmetry_list (void) +{ + GList *list = NULL; + + list = g_list_prepend (list, GINT_TO_POINTER (GIMP_TYPE_MIRROR)); + list = g_list_prepend (list, GINT_TO_POINTER (GIMP_TYPE_TILING)); + list = g_list_prepend (list, GINT_TO_POINTER (GIMP_TYPE_MANDALA)); + + return list; +} + +/** + * gimp_image_symmetry_new: + * @image: the #GimpImage + * @type: the #GType of the symmetry + * + * Creates a new #GimpSymmetry of @type attached to @image. + * @type must be a subtype of `GIMP_TYPE_SYMMETRY`. + * Note that using the base @type `GIMP_TYPE_SYMMETRY` creates an + * identity transformation. + * + * Returns: the new #GimpSymmetry. + **/ +GimpSymmetry * +gimp_image_symmetry_new (GimpImage *image, + GType type) +{ + GimpSymmetry *sym = NULL; + + g_return_val_if_fail (g_type_is_a (type, GIMP_TYPE_SYMMETRY), NULL); + + sym = g_object_new (type, + "image", image, + NULL); + + return sym; +} + +/** + * gimp_image_symmetry_add: + * @image: the #GimpImage + * @type: the #GType of the symmetry + * + * Add a symmetry of type @type to @image and make it the + * active transformation. + **/ +void +gimp_image_symmetry_add (GimpImage *image, + GimpSymmetry *sym) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->symmetries = g_list_prepend (private->symmetries, + g_object_ref (sym)); +} + +/** + * gimp_image_symmetry_remove: + * @image: the #GimpImage + * @sym: the #GimpSymmetry + * + * Remove @sym from the list of symmetries of @image. + * If it was the active transformation, unselect it first. + **/ +void +gimp_image_symmetry_remove (GimpImage *image, + GimpSymmetry *sym) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->active_symmetry == sym) + gimp_image_set_active_symmetry (image, GIMP_TYPE_SYMMETRY); + + private->symmetries = g_list_remove (private->symmetries, sym); + g_object_unref (sym); +} + +/** + * gimp_image_symmetry_get: + * @image: the #GimpImage + * + * Returns: the list of #GimpSymmetry set on @image. + * The returned list belongs to @image and should not be freed. + **/ +GList * +gimp_image_symmetry_get (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return private->symmetries; +} + +/** + * gimp_image_set_active_symmetry: + * @image: the #GimpImage + * @type: the #GType of the symmetry + * + * Select the symmetry of type @type. + * Using the GType allows to select a transformation without + * knowing whether one of the same @type was already created. + * + * Returns TRUE on success, FALSE if no such symmetry was found. + **/ +gboolean +gimp_image_set_active_symmetry (GimpImage *image, + GType type) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + g_object_set (image, + "symmetry", type, + NULL); + + return TRUE; +} + +/** + * gimp_image_get_active_symmetry: + * @image: the #GimpImage + * + * Returns the #GimpSymmetry transformation active on @image. + **/ +GimpSymmetry * +gimp_image_get_active_symmetry (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return private->active_symmetry; +} diff --git a/app/core/gimpimage-symmetry.h b/app/core/gimpimage-symmetry.h new file mode 100644 index 0000000..9211382 --- /dev/null +++ b/app/core/gimpimage-symmetry.h @@ -0,0 +1,40 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-symmetry.h + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_SYMMETRY_H__ +#define __GIMP_IMAGE_SYMMETRY_H__ + + +GList * gimp_image_symmetry_list (void); + +GimpSymmetry * gimp_image_symmetry_new (GimpImage *image, + GType type); +void gimp_image_symmetry_add (GimpImage *image, + GimpSymmetry *sym); +void gimp_image_symmetry_remove (GimpImage *image, + GimpSymmetry *sym); +GList * gimp_image_symmetry_get (GimpImage *image); + +gboolean gimp_image_set_active_symmetry (GimpImage *image, + GType type); +GimpSymmetry * gimp_image_get_active_symmetry (GimpImage *image); + + +#endif /* __GIMP_IMAGE_SYMMETRY_H__ */ diff --git a/app/core/gimpimage-transform.c b/app/core/gimpimage-transform.c new file mode 100644 index 0000000..afe4cfc --- /dev/null +++ b/app/core/gimpimage-transform.c @@ -0,0 +1,338 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-transform.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "vectors/gimpvectors.h" + +#include "gimp.h" +#include "gimp-transform-resize.h" +#include "gimp-transform-utils.h" +#include "gimpchannel.h" +#include "gimpcontext.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-transform.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" +#include "gimpsamplepoint.h" + + +#define EPSILON 1e-6 + + +/* local function prototypes */ + +static void gimp_image_transform_guides (GimpImage *image, + const GimpMatrix3 *matrix, + const GeglRectangle *old_bounds); +static void gimp_image_transform_sample_points (GimpImage *image, + const GimpMatrix3 *matrix, + const GeglRectangle *old_bounds); + + +/* private functions */ + +static void +gimp_image_transform_guides (GimpImage *image, + const GimpMatrix3 *matrix, + const GeglRectangle *old_bounds) +{ + GList *iter; + + for (iter = gimp_image_get_guides (image); iter;) + { + GimpGuide *guide = iter->data; + GimpOrientationType old_orientation = gimp_guide_get_orientation (guide); + gint old_position = gimp_guide_get_position (guide); + GimpOrientationType new_orientation; + gint new_position; + GimpVector2 vertices[2]; + gint n_vertices; + GimpVector2 diff; + + iter = g_list_next (iter); + + switch (old_orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + vertices[0].x = old_bounds->x; + vertices[0].y = old_bounds->y + old_position; + + vertices[1].x = old_bounds->x + old_bounds->width / 2.0; + vertices[1].y = old_bounds->y + old_position; + break; + + case GIMP_ORIENTATION_VERTICAL: + vertices[0].x = old_bounds->x + old_position; + vertices[0].y = old_bounds->y; + + vertices[1].x = old_bounds->x + old_position; + vertices[1].y = old_bounds->y + old_bounds->height / 2.0; + break; + + case GIMP_ORIENTATION_UNKNOWN: + g_return_if_reached (); + } + + gimp_transform_polygon (matrix, + vertices, 2, FALSE, + vertices, &n_vertices); + + if (n_vertices < 2) + { + gimp_image_remove_guide (image, guide, TRUE); + + continue; + } + + gimp_vector2_sub (&diff, &vertices[1], &vertices[0]); + + if (gimp_vector2_length (&diff) <= EPSILON) + { + gimp_image_remove_guide (image, guide, TRUE); + + continue; + } + + if (fabs (diff.x) >= fabs (diff.y)) + { + new_orientation = GIMP_ORIENTATION_HORIZONTAL; + new_position = SIGNED_ROUND (vertices[1].y); + + if (new_position < 0 || new_position > gimp_image_get_height (image)) + { + gimp_image_remove_guide (image, guide, TRUE); + + continue; + } + } + else + { + new_orientation = GIMP_ORIENTATION_VERTICAL; + new_position = SIGNED_ROUND (vertices[1].x); + + if (new_position < 0 || new_position > gimp_image_get_width (image)) + { + gimp_image_remove_guide (image, guide, TRUE); + + continue; + } + } + + if (new_orientation != old_orientation || + new_position != old_position) + { + gimp_image_undo_push_guide (image, NULL, guide); + + gimp_guide_set_orientation (guide, new_orientation); + gimp_guide_set_position (guide, new_position); + + gimp_image_guide_moved (image, guide); + } + } +} + +static void +gimp_image_transform_sample_points (GimpImage *image, + const GimpMatrix3 *matrix, + const GeglRectangle *old_bounds) +{ + GList *iter; + + for (iter = gimp_image_get_sample_points (image); iter;) + { + GimpSamplePoint *sample_point = iter->data; + gint old_x; + gint old_y; + gint new_x; + gint new_y; + GimpVector2 vertices[1]; + gint n_vertices; + + iter = g_list_next (iter); + + gimp_sample_point_get_position (sample_point, &old_x, &old_y); + + vertices[0].x = old_x; + vertices[0].y = old_y; + + gimp_transform_polygon (matrix, + vertices, 1, FALSE, + vertices, &n_vertices); + + if (n_vertices < 1) + { + gimp_image_remove_sample_point (image, sample_point, TRUE); + + continue; + } + + new_x = SIGNED_ROUND (vertices[0].x); + new_y = SIGNED_ROUND (vertices[0].y); + + if (new_x < 0 || new_x >= gimp_image_get_width (image) || + new_y < 0 || new_y >= gimp_image_get_height (image)) + { + gimp_image_remove_sample_point (image, sample_point, TRUE); + + continue; + } + + if (new_x != old_x || new_y != old_y) + gimp_image_move_sample_point (image, sample_point, new_x, new_y, TRUE); + } +} + + +/* public functions */ + +void +gimp_image_transform (GimpImage *image, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GimpItem *item; + GimpMatrix3 transform; + GeglRectangle old_bounds; + GeglRectangle new_bounds; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (matrix != NULL); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + gimp_set_busy (image->gimp); + + old_bounds.x = 0; + old_bounds.y = 0; + old_bounds.width = gimp_image_get_width (image); + old_bounds.height = gimp_image_get_height (image); + + transform = *matrix; + + if (direction == GIMP_TRANSFORM_BACKWARD) + gimp_matrix3_invert (&transform); + + gimp_transform_resize_boundary (&transform, clip_result, + + old_bounds.x, + old_bounds.y, + old_bounds.x + old_bounds.width, + old_bounds.y + old_bounds.height, + + &new_bounds.x, + &new_bounds.y, + &new_bounds.width, + &new_bounds.height); + + new_bounds.width -= new_bounds.x; + new_bounds.height -= new_bounds.y; + + gimp_matrix3_translate (&transform, + old_bounds.x - new_bounds.x, + old_bounds.y - new_bounds.y); + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + gimp_object_queue_push_container (queue, gimp_image_get_layers (image)); + gimp_object_queue_push (queue, gimp_image_get_mask (image)); + gimp_object_queue_push_container (queue, gimp_image_get_channels (image)); + gimp_object_queue_push_container (queue, gimp_image_get_vectors (image)); + + g_object_freeze_notify (G_OBJECT (image)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_TRANSFORM, NULL); + + /* Transform all layers, channels (including selection mask), and vectors */ + while ((item = gimp_object_queue_pop (queue))) + { + GimpTransformResize clip = GIMP_TRANSFORM_RESIZE_ADJUST; + + if (GIMP_IS_CHANNEL (item)) + clip = clip_result; + + gimp_item_transform (item, + context, + &transform, direction, + interpolation_type, clip, + progress); + + if (GIMP_IS_VECTORS (item)) + gimp_item_set_size (item, new_bounds.width, new_bounds.height); + } + + /* Resize the image (if needed) */ + if (! gegl_rectangle_equal (&new_bounds, &old_bounds)) + { + gimp_image_undo_push_image_size (image, + NULL, + new_bounds.x, + new_bounds.y, + new_bounds.width, + new_bounds.height); + + g_object_set (image, + "width", new_bounds.width, + "height", new_bounds.height, + NULL); + } + + /* Transform all Guides */ + gimp_image_transform_guides (image, &transform, &old_bounds); + + /* Transform all sample points */ + gimp_image_transform_sample_points (image, &transform, &old_bounds); + + gimp_image_undo_group_end (image); + + g_object_unref (queue); + + if (! gegl_rectangle_equal (&new_bounds, &old_bounds)) + { + gimp_image_size_changed_detailed (image, + old_bounds.x - new_bounds.x, + old_bounds.y - new_bounds.y, + old_bounds.width, + old_bounds.height); + } + + g_object_thaw_notify (G_OBJECT (image)); + + gimp_unset_busy (image->gimp); +} diff --git a/app/core/gimpimage-transform.h b/app/core/gimpimage-transform.h new file mode 100644 index 0000000..63a851d --- /dev/null +++ b/app/core/gimpimage-transform.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * gimpimage-transform.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_IMAGE_TRANSFORM_H__ +#define __GIMP_IMAGE_TRANSFORM_H__ + + +void gimp_image_transform (GimpImage *image, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + + +#endif /* __GIMP_IMAGE_TRANSFORM_H__ */ diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c new file mode 100644 index 0000000..b96c4b4 --- /dev/null +++ b/app/core/gimpimage-undo-push.c @@ -0,0 +1,1060 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpchannelpropundo.h" +#include "gimpchannelundo.h" +#include "gimpdrawablemodundo.h" +#include "gimpdrawableundo.h" +#include "gimpfloatingselectionundo.h" +#include "gimpgrid.h" +#include "gimpgrouplayer.h" +#include "gimpgrouplayerundo.h" +#include "gimpguide.h" +#include "gimpguideundo.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpimageundo.h" +#include "gimpitempropundo.h" +#include "gimplayermask.h" +#include "gimplayermaskpropundo.h" +#include "gimplayermaskundo.h" +#include "gimplayerpropundo.h" +#include "gimplayerundo.h" +#include "gimpmaskundo.h" +#include "gimpsamplepoint.h" +#include "gimpsamplepointundo.h" +#include "gimpselection.h" + +#include "text/gimptextlayer.h" +#include "text/gimptextundo.h" + +#include "vectors/gimpvectors.h" +#include "vectors/gimpvectorsmodundo.h" +#include "vectors/gimpvectorspropundo.h" +#include "vectors/gimpvectorsundo.h" + +#include "gimp-intl.h" + + +/**************************/ +/* Image Property Undos */ +/**************************/ + +GimpUndo * +gimp_image_undo_push_image_type (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_TYPE, undo_desc, + GIMP_DIRTY_IMAGE, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_precision (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_PRECISION, undo_desc, + GIMP_DIRTY_IMAGE, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_size (GimpImage *image, + const gchar *undo_desc, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_SIZE, undo_desc, + GIMP_DIRTY_IMAGE | GIMP_DIRTY_IMAGE_SIZE, + "previous-origin-x", previous_origin_x, + "previous-origin-y", previous_origin_y, + "previous-width", previous_width, + "previous-height", previous_height, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_resolution (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_RESOLUTION, undo_desc, + GIMP_DIRTY_IMAGE, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_grid (GimpImage *image, + const gchar *undo_desc, + GimpGrid *grid) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GRID (grid), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_GRID, undo_desc, + GIMP_DIRTY_IMAGE_META, + "grid", grid, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_colormap (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_COLORMAP, undo_desc, + GIMP_DIRTY_IMAGE, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_color_managed (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_COLOR_MANAGED, undo_desc, + GIMP_DIRTY_IMAGE, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_metadata (GimpImage *image, + const gchar *undo_desc) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_IMAGE_METADATA, undo_desc, + GIMP_DIRTY_IMAGE_META, + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_parasite (GimpImage *image, + const gchar *undo_desc, + const GimpParasite *parasite) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (parasite != NULL, NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_PARASITE_ATTACH, undo_desc, + GIMP_DIRTY_IMAGE_META, + "parasite-name", gimp_parasite_name (parasite), + NULL); +} + +GimpUndo * +gimp_image_undo_push_image_parasite_remove (GimpImage *image, + const gchar *undo_desc, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO, + GIMP_UNDO_PARASITE_REMOVE, undo_desc, + GIMP_DIRTY_IMAGE_META, + "parasite-name", name, + NULL); +} + + +/********************************/ +/* Guide & Sample Point Undos */ +/********************************/ + +GimpUndo * +gimp_image_undo_push_guide (GimpImage *image, + const gchar *undo_desc, + GimpGuide *guide) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GUIDE (guide), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GUIDE_UNDO, + GIMP_UNDO_GUIDE, undo_desc, + GIMP_DIRTY_IMAGE_META, + "aux-item", guide, + NULL); +} + +GimpUndo * +gimp_image_undo_push_sample_point (GimpImage *image, + const gchar *undo_desc, + GimpSamplePoint *sample_point) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_SAMPLE_POINT (sample_point), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_SAMPLE_POINT_UNDO, + GIMP_UNDO_SAMPLE_POINT, undo_desc, + GIMP_DIRTY_IMAGE_META, + "aux-item", sample_point, + NULL); +} + + +/********************/ +/* Drawable Undos */ +/********************/ + +GimpUndo * +gimp_image_undo_push_drawable (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + item = GIMP_ITEM (drawable); + + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_UNDO, + GIMP_UNDO_DRAWABLE, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", item, + "buffer", buffer, + "x", x, + "y", y, + NULL); +} + +GimpUndo * +gimp_image_undo_push_drawable_mod (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + gboolean copy_buffer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_MOD_UNDO, + GIMP_UNDO_DRAWABLE_MOD, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", drawable, + "copy-buffer", copy_buffer, + NULL); +} + + +/****************/ +/* Mask Undos */ +/****************/ + +GimpUndo * +gimp_image_undo_push_mask (GimpImage *image, + const gchar *undo_desc, + GimpChannel *mask) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CHANNEL (mask), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_MASK_UNDO, + GIMP_UNDO_MASK, undo_desc, + GIMP_IS_SELECTION (mask) ? + GIMP_DIRTY_SELECTION : + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", mask, + NULL); +} + +GimpUndo * +gimp_image_undo_push_mask_precision (GimpImage *image, + const gchar *undo_desc, + GimpChannel *mask) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CHANNEL (mask), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_MASK_UNDO, + GIMP_UNDO_MASK, undo_desc, + GIMP_IS_SELECTION (mask) ? + GIMP_DIRTY_SELECTION : + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", mask, + "convert-format", TRUE, + NULL); +} + + +/****************/ +/* Item Undos */ +/****************/ + +GimpUndo * +gimp_image_undo_push_item_reorder (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_REORDER, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_rename (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_RENAME, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_displace (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_DISPLACE, undo_desc, + GIMP_IS_DRAWABLE (item) ? + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE : + GIMP_DIRTY_ITEM | GIMP_DIRTY_VECTORS, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_visibility (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_VISIBILITY, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_linked (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_LINKED, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_color_tag (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_COLOR_TAG, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_lock_content (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_LOCK_CONTENT, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_lock_position (GimpImage *image, + const gchar *undo_desc, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_ITEM_LOCK_POSITION, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_parasite (GimpImage *image, + const gchar *undo_desc, + GimpItem *item, + const GimpParasite *parasite) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + g_return_val_if_fail (parasite != NULL, NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_PARASITE_ATTACH, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + "parasite-name", gimp_parasite_name (parasite), + NULL); +} + +GimpUndo * +gimp_image_undo_push_item_parasite_remove (GimpImage *image, + const gchar *undo_desc, + GimpItem *item, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_ITEM_PROP_UNDO, + GIMP_UNDO_PARASITE_REMOVE, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", item, + "parasite-name", name, + NULL); +} + + +/*****************/ +/* Layer Undos */ +/*****************/ + +GimpUndo * +gimp_image_undo_push_layer_add (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayer *prev_layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + g_return_val_if_fail (prev_layer == NULL || GIMP_IS_LAYER (prev_layer), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_UNDO, + GIMP_UNDO_LAYER_ADD, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", layer, + "prev-layer", prev_layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_remove (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayer *prev_parent, + gint prev_position, + GimpLayer *prev_layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + g_return_val_if_fail (prev_parent == NULL || GIMP_IS_LAYER (prev_parent), + NULL); + g_return_val_if_fail (prev_layer == NULL || GIMP_IS_LAYER (prev_layer), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_UNDO, + GIMP_UNDO_LAYER_REMOVE, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", layer, + "prev-parent", prev_parent, + "prev-position", prev_position, + "prev-layer", prev_layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_mode (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_MODE, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_opacity (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_OPACITY, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_lock_alpha (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_PROP_UNDO, + GIMP_UNDO_LAYER_LOCK_ALPHA, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + + +/***********************/ +/* Group Layer Undos */ +/***********************/ + +GimpUndo * +gimp_image_undo_push_group_layer_suspend_resize (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_resume_resize (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_suspend_mask (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_resume_mask (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_RESUME_MASK, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_start_transform (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_START_TRANSFORM, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_end_transform (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_END_TRANSFORM, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + +GimpUndo * +gimp_image_undo_push_group_layer_convert (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO, + GIMP_UNDO_GROUP_LAYER_CONVERT, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", group, + NULL); +} + + +/**********************/ +/* Text Layer Undos */ +/**********************/ + +GimpUndo * +gimp_image_undo_push_text_layer (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer, + const GParamSpec *pspec) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_TEXT_UNDO, + GIMP_UNDO_TEXT_LAYER, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", layer, + "param", pspec, + NULL); +} + +GimpUndo * +gimp_image_undo_push_text_layer_modified (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_TEXT_UNDO, + GIMP_UNDO_TEXT_LAYER_MODIFIED, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_text_layer_convert (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_TEXT_UNDO, + GIMP_UNDO_TEXT_LAYER_CONVERT, undo_desc, + GIMP_DIRTY_ITEM, + "item", layer, + NULL); +} + + +/**********************/ +/* Layer Mask Undos */ +/**********************/ + +GimpUndo * +gimp_image_undo_push_layer_mask_add (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayerMask *mask) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), NULL); + g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (mask)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_MASK_UNDO, + GIMP_UNDO_LAYER_MASK_ADD, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", layer, + "layer-mask", mask, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_mask_remove (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayerMask *mask) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), NULL); + g_return_val_if_fail (gimp_layer_mask_get_layer (mask) == layer, NULL); + g_return_val_if_fail (gimp_layer_get_mask (layer) == mask, NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_MASK_UNDO, + GIMP_UNDO_LAYER_MASK_REMOVE, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", layer, + "layer-mask", mask, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_mask_apply (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_MASK_PROP_UNDO, + GIMP_UNDO_LAYER_MASK_APPLY, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + +GimpUndo * +gimp_image_undo_push_layer_mask_show (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_LAYER_MASK_PROP_UNDO, + GIMP_UNDO_LAYER_MASK_SHOW, undo_desc, + GIMP_DIRTY_ITEM_META, + "item", layer, + NULL); +} + + +/*******************/ +/* Channel Undos */ +/*******************/ + +GimpUndo * +gimp_image_undo_push_channel_add (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel, + GimpChannel *prev_channel) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (channel)), NULL); + g_return_val_if_fail (prev_channel == NULL || GIMP_IS_CHANNEL (prev_channel), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_CHANNEL_UNDO, + GIMP_UNDO_CHANNEL_ADD, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", channel, + "prev-channel", prev_channel, + NULL); +} + +GimpUndo * +gimp_image_undo_push_channel_remove (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel, + GimpChannel *prev_parent, + gint prev_position, + GimpChannel *prev_channel) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)), NULL); + g_return_val_if_fail (prev_parent == NULL || GIMP_IS_CHANNEL (prev_parent), + NULL); + g_return_val_if_fail (prev_channel == NULL || GIMP_IS_CHANNEL (prev_channel), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_CHANNEL_UNDO, + GIMP_UNDO_CHANNEL_REMOVE, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", channel, + "prev-parent", prev_parent, + "prev-position", prev_position, + "prev-channel", prev_channel, + NULL); +} + +GimpUndo * +gimp_image_undo_push_channel_color (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_CHANNEL_PROP_UNDO, + GIMP_UNDO_CHANNEL_COLOR, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, + "item", channel, + NULL); +} + + +/*******************/ +/* Vectors Undos */ +/*******************/ + +GimpUndo * +gimp_image_undo_push_vectors_add (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors, + GimpVectors *prev_vectors) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL); + g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (vectors)), NULL); + g_return_val_if_fail (prev_vectors == NULL || GIMP_IS_VECTORS (prev_vectors), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_VECTORS_UNDO, + GIMP_UNDO_VECTORS_ADD, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", vectors, + "prev-vectors", prev_vectors, + NULL); +} + +GimpUndo * +gimp_image_undo_push_vectors_remove (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors, + GimpVectors *prev_parent, + gint prev_position, + GimpVectors *prev_vectors) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)), NULL); + g_return_val_if_fail (prev_parent == NULL || GIMP_IS_VECTORS (prev_parent), + NULL); + g_return_val_if_fail (prev_vectors == NULL || GIMP_IS_VECTORS (prev_vectors), + NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_VECTORS_UNDO, + GIMP_UNDO_VECTORS_REMOVE, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", vectors, + "prev-parent", prev_parent, + "prev-position", prev_position, + "prev-vectors", prev_vectors, + NULL); +} + +GimpUndo * +gimp_image_undo_push_vectors_mod (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_VECTORS_MOD_UNDO, + GIMP_UNDO_VECTORS_MOD, undo_desc, + GIMP_DIRTY_ITEM | GIMP_DIRTY_VECTORS, + "item", vectors, + NULL); +} + + +/******************************/ +/* Floating Selection Undos */ +/******************************/ + +GimpUndo * +gimp_image_undo_push_fs_to_layer (GimpImage *image, + const gchar *undo_desc, + GimpLayer *floating_layer) +{ + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_LAYER (floating_layer), NULL); + + undo = gimp_image_undo_push (image, GIMP_TYPE_FLOATING_SELECTION_UNDO, + GIMP_UNDO_FS_TO_LAYER, undo_desc, + GIMP_DIRTY_IMAGE_STRUCTURE, + "item", floating_layer, + NULL); + + return undo; +} + + +/******************************************************************************/ +/* Something for which programmer is too lazy to write an undo function for */ +/******************************************************************************/ + +static void +undo_pop_cantundo (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + switch (undo_mode) + { + case GIMP_UNDO_MODE_UNDO: + gimp_message (undo->image->gimp, NULL, GIMP_MESSAGE_WARNING, + _("Can't undo %s"), gimp_object_get_name (undo)); + break; + + case GIMP_UNDO_MODE_REDO: + break; + } +} + +GimpUndo * +gimp_image_undo_push_cantundo (GimpImage *image, + const gchar *undo_desc) +{ + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + /* This is the sole purpose of this type of undo: the ability to + * mark an image as having been mutated, without really providing + * any adequate undo facility. + */ + + undo = gimp_image_undo_push (image, GIMP_TYPE_UNDO, + GIMP_UNDO_CANT, undo_desc, + GIMP_DIRTY_ALL, + NULL); + + if (undo) + g_signal_connect (undo, "pop", + G_CALLBACK (undo_pop_cantundo), + NULL); + + return undo; +} diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h new file mode 100644 index 0000000..f06fcd8 --- /dev/null +++ b/app/core/gimpimage-undo-push.h @@ -0,0 +1,256 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_UNDO_PUSH_H__ +#define __GIMP_IMAGE_UNDO_PUSH_H__ + + +/* image undos */ + +GimpUndo * gimp_image_undo_push_image_type (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_precision (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_size (GimpImage *image, + const gchar *undo_desc, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint prevoius_height); +GimpUndo * gimp_image_undo_push_image_resolution (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_grid (GimpImage *image, + const gchar *undo_desc, + GimpGrid *grid); +GimpUndo * gimp_image_undo_push_image_colormap (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_color_managed (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_metadata (GimpImage *image, + const gchar *undo_desc); +GimpUndo * gimp_image_undo_push_image_parasite (GimpImage *image, + const gchar *undo_desc, + const GimpParasite *parasite); +GimpUndo * gimp_image_undo_push_image_parasite_remove (GimpImage *image, + const gchar *undo_desc, + const gchar *name); + + +/* guide & sample point undos */ + +GimpUndo * gimp_image_undo_push_guide (GimpImage *image, + const gchar *undo_desc, + GimpGuide *guide); +GimpUndo * gimp_image_undo_push_sample_point (GimpImage *image, + const gchar *undo_desc, + GimpSamplePoint *sample_point); + + +/* drawable undos */ + +GimpUndo * gimp_image_undo_push_drawable (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + GeglBuffer *buffer, + gint x, + gint y); +GimpUndo * gimp_image_undo_push_drawable_mod (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + gboolean copy_buffer); + + +/* mask undos */ + +GimpUndo * gimp_image_undo_push_mask (GimpImage *image, + const gchar *undo_desc, + GimpChannel *mask); +GimpUndo * gimp_image_undo_push_mask_precision (GimpImage *image, + const gchar *undo_desc, + GimpChannel *mask); + + +/* item undos */ + +GimpUndo * gimp_image_undo_push_item_reorder (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_rename (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_displace (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_visibility (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_linked (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_color_tag (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_lock_content (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_lock_position (GimpImage *image, + const gchar *undo_desc, + GimpItem *item); +GimpUndo * gimp_image_undo_push_item_parasite (GimpImage *image, + const gchar *undo_desc, + GimpItem *item, + const GimpParasite *parasite); +GimpUndo * gimp_image_undo_push_item_parasite_remove(GimpImage *image, + const gchar *undo_desc, + GimpItem *item, + const gchar *name); + + +/* layer undos */ + +GimpUndo * gimp_image_undo_push_layer_add (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayer *prev_layer); +GimpUndo * gimp_image_undo_push_layer_remove (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayer *prev_parent, + gint prev_position, + GimpLayer *prev_layer); +GimpUndo * gimp_image_undo_push_layer_mode (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer); +GimpUndo * gimp_image_undo_push_layer_opacity (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer); +GimpUndo * gimp_image_undo_push_layer_lock_alpha (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer); + + +/* group layer undos */ + +GimpUndo * + gimp_image_undo_push_group_layer_suspend_resize (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * + gimp_image_undo_push_group_layer_resume_resize (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * + gimp_image_undo_push_group_layer_suspend_mask (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * + gimp_image_undo_push_group_layer_resume_mask (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * + gimp_image_undo_push_group_layer_start_transform (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * + gimp_image_undo_push_group_layer_end_transform (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); +GimpUndo * gimp_image_undo_push_group_layer_convert (GimpImage *image, + const gchar *undo_desc, + GimpGroupLayer *group); + + +/* text layer undos */ + +GimpUndo * gimp_image_undo_push_text_layer (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer, + const GParamSpec *pspec); +GimpUndo * gimp_image_undo_push_text_layer_modified (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer); +GimpUndo * gimp_image_undo_push_text_layer_convert (GimpImage *image, + const gchar *undo_desc, + GimpTextLayer *layer); + + +/* layer mask undos */ + +GimpUndo * gimp_image_undo_push_layer_mask_add (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayerMask *mask); +GimpUndo * gimp_image_undo_push_layer_mask_remove (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer, + GimpLayerMask *mask); +GimpUndo * gimp_image_undo_push_layer_mask_apply (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer); +GimpUndo * gimp_image_undo_push_layer_mask_show (GimpImage *image, + const gchar *undo_desc, + GimpLayer *layer); + + +/* channel undos */ + +GimpUndo * gimp_image_undo_push_channel_add (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel, + GimpChannel *prev_channel); +GimpUndo * gimp_image_undo_push_channel_remove (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel, + GimpChannel *prev_parent, + gint prev_position, + GimpChannel *prev_channel); +GimpUndo * gimp_image_undo_push_channel_color (GimpImage *image, + const gchar *undo_desc, + GimpChannel *channel); + + +/* vectors undos */ + +GimpUndo * gimp_image_undo_push_vectors_add (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors, + GimpVectors *prev_vectors); +GimpUndo * gimp_image_undo_push_vectors_remove (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors, + GimpVectors *prev_parent, + gint prev_position, + GimpVectors *prev_vectors); +GimpUndo * gimp_image_undo_push_vectors_mod (GimpImage *image, + const gchar *undo_desc, + GimpVectors *vectors); + + +/* floating selection undos */ + +GimpUndo * gimp_image_undo_push_fs_to_layer (GimpImage *image, + const gchar *undo_desc, + GimpLayer *floating_layer); + + +/* EEK undo */ + +GimpUndo * gimp_image_undo_push_cantundo (GimpImage *image, + const gchar *undo_desc); + + +#endif /* __GIMP_IMAGE_UNDO_PUSH_H__ */ diff --git a/app/core/gimpimage-undo.c b/app/core/gimpimage-undo.c new file mode 100644 index 0000000..fbf808b --- /dev/null +++ b/app/core/gimpimage-undo.c @@ -0,0 +1,695 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimp-utils.h" +#include "gimpimage.h" +#include "gimpimage-private.h" +#include "gimpimage-undo.h" +#include "gimpitem.h" +#include "gimplist.h" +#include "gimpundostack.h" + + +/* local function prototypes */ + +static void gimp_image_undo_pop_stack (GimpImage *image, + GimpUndoStack *undo_stack, + GimpUndoStack *redo_stack, + GimpUndoMode undo_mode); +static void gimp_image_undo_free_space (GimpImage *image); +static void gimp_image_undo_free_redo (GimpImage *image); + +static GimpDirtyMask gimp_image_undo_dirty_from_type (GimpUndoType undo_type); + + +/* public functions */ + +gboolean +gimp_image_undo_is_enabled (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return (GIMP_IMAGE_GET_PRIVATE (image)->undo_freeze_count == 0); +} + +gboolean +gimp_image_undo_enable (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + /* Free all undo steps as they are now invalidated */ + gimp_image_undo_free (image); + + return gimp_image_undo_thaw (image); +} + +gboolean +gimp_image_undo_disable (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return gimp_image_undo_freeze (image); +} + +gboolean +gimp_image_undo_freeze (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->undo_freeze_count++; + + if (private->undo_freeze_count == 1) + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_FREEZE, NULL); + + return TRUE; +} + +gboolean +gimp_image_undo_thaw (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_val_if_fail (private->undo_freeze_count > 0, FALSE); + + private->undo_freeze_count--; + + if (private->undo_freeze_count == 0) + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_THAW, NULL); + + return TRUE; +} + +gboolean +gimp_image_undo (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_val_if_fail (private->pushing_undo_group == GIMP_UNDO_GROUP_NONE, + FALSE); + + gimp_image_undo_pop_stack (image, + private->undo_stack, + private->redo_stack, + GIMP_UNDO_MODE_UNDO); + + return TRUE; +} + +gboolean +gimp_image_redo (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_val_if_fail (private->pushing_undo_group == GIMP_UNDO_GROUP_NONE, + FALSE); + + gimp_image_undo_pop_stack (image, + private->redo_stack, + private->undo_stack, + GIMP_UNDO_MODE_REDO); + + return TRUE; +} + +/* + * this function continues to undo as long as it only sees certain + * undo types, in particular visibility changes. + */ +gboolean +gimp_image_strong_undo (GimpImage *image) +{ + GimpImagePrivate *private; + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_val_if_fail (private->pushing_undo_group == GIMP_UNDO_GROUP_NONE, + FALSE); + + undo = gimp_undo_stack_peek (private->undo_stack); + + gimp_image_undo (image); + + while (gimp_undo_is_weak (undo)) + { + undo = gimp_undo_stack_peek (private->undo_stack); + if (gimp_undo_is_weak (undo)) + gimp_image_undo (image); + } + + return TRUE; +} + +/* + * this function continues to redo as long as it only sees certain + * undo types, in particular visibility changes. Note that the + * order of events is set up to make it exactly reverse + * gimp_image_strong_undo(). + */ +gboolean +gimp_image_strong_redo (GimpImage *image) +{ + GimpImagePrivate *private; + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_return_val_if_fail (private->pushing_undo_group == GIMP_UNDO_GROUP_NONE, + FALSE); + + undo = gimp_undo_stack_peek (private->redo_stack); + + gimp_image_redo (image); + + while (gimp_undo_is_weak (undo)) + { + undo = gimp_undo_stack_peek (private->redo_stack); + if (gimp_undo_is_weak (undo)) + gimp_image_redo (image); + } + + return TRUE; +} + +GimpUndoStack * +gimp_image_get_undo_stack (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->undo_stack; +} + +GimpUndoStack * +gimp_image_get_redo_stack (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->redo_stack; +} + +void +gimp_image_undo_free (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* Emit the UNDO_FREE event before actually freeing everything + * so the views can properly detach from the undo items + */ + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_FREE, NULL); + + gimp_undo_free (GIMP_UNDO (private->undo_stack), GIMP_UNDO_MODE_UNDO); + gimp_undo_free (GIMP_UNDO (private->redo_stack), GIMP_UNDO_MODE_REDO); + + /* If the image was dirty, but could become clean by redo-ing + * some actions, then it should now become 'infinitely' dirty. + * This is because we've just nuked the actions that would allow + * the image to become clean again. + */ + if (private->dirty < 0) + private->dirty = 100000; + + /* The same applies to the case where the image would become clean + * due to undo actions, but since user can't undo without an undo + * stack, that's not so much a problem. + */ +} + +gint +gimp_image_get_undo_group_count (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->group_count; +} + +gboolean +gimp_image_undo_group_start (GimpImage *image, + GimpUndoType undo_type, + const gchar *name) +{ + GimpImagePrivate *private; + GimpUndoStack *undo_group; + GimpDirtyMask dirty_mask; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (undo_type > GIMP_UNDO_GROUP_FIRST && + undo_type <= GIMP_UNDO_GROUP_LAST, FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! name) + name = gimp_undo_type_to_name (undo_type); + + dirty_mask = gimp_image_undo_dirty_from_type (undo_type); + + /* Notify listeners that the image will be modified */ + if (private->group_count == 0 && dirty_mask != GIMP_DIRTY_NONE) + gimp_image_dirty (image, dirty_mask); + + if (private->undo_freeze_count > 0) + return FALSE; + + private->group_count++; + + /* If we're already in a group...ignore */ + if (private->group_count > 1) + return TRUE; + + /* nuke the redo stack */ + gimp_image_undo_free_redo (image); + + undo_group = gimp_undo_stack_new (image); + + gimp_object_set_name (GIMP_OBJECT (undo_group), name); + GIMP_UNDO (undo_group)->undo_type = undo_type; + GIMP_UNDO (undo_group)->dirty_mask = dirty_mask; + + gimp_undo_stack_push_undo (private->undo_stack, GIMP_UNDO (undo_group)); + + private->pushing_undo_group = undo_type; + + return TRUE; +} + +gboolean +gimp_image_undo_group_end (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->undo_freeze_count > 0) + return FALSE; + + g_return_val_if_fail (private->group_count > 0, FALSE); + + private->group_count--; + + if (private->group_count == 0) + { + private->pushing_undo_group = GIMP_UNDO_GROUP_NONE; + + /* Do it here, since undo_push doesn't emit this event while in + * the middle of a group + */ + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_PUSHED, + gimp_undo_stack_peek (private->undo_stack)); + + gimp_image_undo_free_space (image); + } + + return TRUE; +} + +GimpUndo * +gimp_image_undo_push (GimpImage *image, + GType object_type, + GimpUndoType undo_type, + const gchar *name, + GimpDirtyMask dirty_mask, + ...) +{ + GimpImagePrivate *private; + gint n_properties = 0; + gchar **names = NULL; + GValue *values = NULL; + va_list args; + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (g_type_is_a (object_type, GIMP_TYPE_UNDO), NULL); + g_return_val_if_fail (undo_type > GIMP_UNDO_GROUP_LAST, NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* Does this undo dirty the image? If so, we always want to mark + * image dirty, even if we can't actually push the undo. + */ + if (dirty_mask != GIMP_DIRTY_NONE) + gimp_image_dirty (image, dirty_mask); + + if (private->undo_freeze_count > 0) + return NULL; + + if (! name) + name = gimp_undo_type_to_name (undo_type); + + names = gimp_properties_append (object_type, + &n_properties, names, &values, + "name", name, + "image", image, + "undo-type", undo_type, + "dirty-mask", dirty_mask, + NULL); + + va_start (args, dirty_mask); + names = gimp_properties_append_valist (object_type, + &n_properties, names, &values, + args); + va_end (args); + + undo = (GimpUndo *) g_object_new_with_properties (object_type, + n_properties, + (const gchar **) names, + (const GValue *) values); + + gimp_properties_free (n_properties, names, values); + + /* nuke the redo stack */ + gimp_image_undo_free_redo (image); + + if (private->pushing_undo_group == GIMP_UNDO_GROUP_NONE) + { + gimp_undo_stack_push_undo (private->undo_stack, undo); + + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_PUSHED, undo); + + gimp_image_undo_free_space (image); + + /* freeing undo space may have freed the newly pushed undo */ + if (gimp_undo_stack_peek (private->undo_stack) == undo) + return undo; + } + else + { + GimpUndoStack *undo_group; + + undo_group = GIMP_UNDO_STACK (gimp_undo_stack_peek (private->undo_stack)); + + gimp_undo_stack_push_undo (undo_group, undo); + + return undo; + } + + return NULL; +} + +GimpUndo * +gimp_image_undo_can_compress (GimpImage *image, + GType object_type, + GimpUndoType undo_type) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (gimp_image_is_dirty (image) && + ! gimp_undo_stack_peek (private->redo_stack)) + { + GimpUndo *undo = gimp_undo_stack_peek (private->undo_stack); + + if (undo && undo->undo_type == undo_type && + g_type_is_a (G_TYPE_FROM_INSTANCE (undo), object_type)) + { + return undo; + } + } + + return NULL; +} + + +/* private functions */ + +static void +gimp_image_undo_pop_stack (GimpImage *image, + GimpUndoStack *undo_stack, + GimpUndoStack *redo_stack, + GimpUndoMode undo_mode) +{ + GimpUndo *undo; + GimpUndoAccumulator accum = { 0, }; + + g_object_freeze_notify (G_OBJECT (image)); + + undo = gimp_undo_stack_pop_undo (undo_stack, undo_mode, &accum); + + if (undo) + { + if (GIMP_IS_UNDO_STACK (undo)) + gimp_list_reverse (GIMP_LIST (GIMP_UNDO_STACK (undo)->undos)); + + gimp_undo_stack_push_undo (redo_stack, undo); + + if (accum.mode_changed) + gimp_image_mode_changed (image); + + if (accum.precision_changed) + gimp_image_precision_changed (image); + + if (accum.size_changed) + gimp_image_size_changed_detailed (image, + accum.previous_origin_x, + accum.previous_origin_y, + accum.previous_width, + accum.previous_height); + + if (accum.resolution_changed) + gimp_image_resolution_changed (image); + + if (accum.unit_changed) + gimp_image_unit_changed (image); + + /* let others know that we just popped an action */ + gimp_image_undo_event (image, + (undo_mode == GIMP_UNDO_MODE_UNDO) ? + GIMP_UNDO_EVENT_UNDO : GIMP_UNDO_EVENT_REDO, + undo); + } + + g_object_thaw_notify (G_OBJECT (image)); +} + +static void +gimp_image_undo_free_space (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpContainer *container; + gint min_undo_levels; + gint max_undo_levels; + gint64 undo_size; + + container = private->undo_stack->undos; + + min_undo_levels = image->gimp->config->levels_of_undo; + max_undo_levels = 1024; /* FIXME */ + undo_size = image->gimp->config->undo_size; + +#ifdef DEBUG_IMAGE_UNDO + g_printerr ("undo_steps: %d undo_bytes: %ld\n", + gimp_container_get_n_children (container), + (glong) gimp_object_get_memsize (GIMP_OBJECT (container), NULL)); +#endif + + /* keep at least min_undo_levels undo steps */ + if (gimp_container_get_n_children (container) <= min_undo_levels) + return; + + while ((gimp_object_get_memsize (GIMP_OBJECT (container), NULL) > undo_size) || + (gimp_container_get_n_children (container) > max_undo_levels)) + { + GimpUndo *freed = gimp_undo_stack_free_bottom (private->undo_stack, + GIMP_UNDO_MODE_UNDO); + +#ifdef DEBUG_IMAGE_UNDO + g_printerr ("freed one step: undo_steps: %d undo_bytes: %ld\n", + gimp_container_get_n_children (container), + (glong) gimp_object_get_memsize (GIMP_OBJECT (container), + NULL)); +#endif + + gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_EXPIRED, freed); + + g_object_unref (freed); + + if (gimp_container_get_n_children (container) <= min_undo_levels) + return; + } +} + +static void +gimp_image_undo_free_redo (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpContainer *container = private->redo_stack->undos; + +#ifdef DEBUG_IMAGE_UNDO + g_printerr ("redo_steps: %d redo_bytes: %ld\n", + gimp_container_get_n_children (container), + (glong) gimp_object_get_memsize (GIMP_OBJECT (container), NULL)); +#endif + + if (gimp_container_is_empty (container)) + return; + + while (gimp_container_get_n_children (container) > 0) + { + GimpUndo *freed = gimp_undo_stack_free_bottom (private->redo_stack, + GIMP_UNDO_MODE_REDO); + +#ifdef DEBUG_IMAGE_UNDO + g_printerr ("freed one step: redo_steps: %d redo_bytes: %ld\n", + gimp_container_get_n_children (container), + (glong )gimp_object_get_memsize (GIMP_OBJECT (container), + NULL)); +#endif + + gimp_image_undo_event (image, GIMP_UNDO_EVENT_REDO_EXPIRED, freed); + + g_object_unref (freed); + } + + /* We need to use <= here because the undo counter has already been + * incremented at this point. + */ + if (private->dirty <= 0) + { + /* If the image was dirty, but could become clean by redo-ing + * some actions, then it should now become 'infinitely' dirty. + * This is because we've just nuked the actions that would allow + * the image to become clean again. + */ + private->dirty = 100000; + } +} + +static GimpDirtyMask +gimp_image_undo_dirty_from_type (GimpUndoType undo_type) +{ + switch (undo_type) + { + case GIMP_UNDO_GROUP_IMAGE_SCALE: + case GIMP_UNDO_GROUP_IMAGE_RESIZE: + case GIMP_UNDO_GROUP_IMAGE_FLIP: + case GIMP_UNDO_GROUP_IMAGE_ROTATE: + case GIMP_UNDO_GROUP_IMAGE_TRANSFORM: + case GIMP_UNDO_GROUP_IMAGE_CROP: + return GIMP_DIRTY_IMAGE | GIMP_DIRTY_IMAGE_SIZE; + + case GIMP_UNDO_GROUP_IMAGE_CONVERT: + return GIMP_DIRTY_IMAGE | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE: + return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE: + return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_VECTORS; + + case GIMP_UNDO_GROUP_IMAGE_QUICK_MASK: /* FIXME */ + return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_SELECTION; + + case GIMP_UNDO_GROUP_IMAGE_GRID: + case GIMP_UNDO_GROUP_GUIDE: + return GIMP_DIRTY_IMAGE_META; + + case GIMP_UNDO_GROUP_DRAWABLE: + case GIMP_UNDO_GROUP_DRAWABLE_MOD: + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_MASK: /* FIXME */ + return GIMP_DIRTY_SELECTION; + + case GIMP_UNDO_GROUP_ITEM_VISIBILITY: + case GIMP_UNDO_GROUP_ITEM_LINKED: + case GIMP_UNDO_GROUP_ITEM_PROPERTIES: + return GIMP_DIRTY_ITEM_META; + + case GIMP_UNDO_GROUP_ITEM_DISPLACE: /* FIXME */ + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE | GIMP_DIRTY_VECTORS; + + case GIMP_UNDO_GROUP_ITEM_SCALE: /* FIXME */ + case GIMP_UNDO_GROUP_ITEM_RESIZE: /* FIXME */ + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE | GIMP_DIRTY_VECTORS; + + case GIMP_UNDO_GROUP_LAYER_ADD_MASK: + case GIMP_UNDO_GROUP_LAYER_APPLY_MASK: + return GIMP_DIRTY_IMAGE_STRUCTURE; + + case GIMP_UNDO_GROUP_FS_TO_LAYER: + case GIMP_UNDO_GROUP_FS_FLOAT: + case GIMP_UNDO_GROUP_FS_ANCHOR: + return GIMP_DIRTY_IMAGE_STRUCTURE; + + case GIMP_UNDO_GROUP_EDIT_PASTE: + return GIMP_DIRTY_IMAGE_STRUCTURE; + + case GIMP_UNDO_GROUP_EDIT_CUT: + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_TEXT: + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_TRANSFORM: /* FIXME */ + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE | GIMP_DIRTY_VECTORS; + + case GIMP_UNDO_GROUP_PAINT: + return GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE; + + case GIMP_UNDO_GROUP_PARASITE_ATTACH: + case GIMP_UNDO_GROUP_PARASITE_REMOVE: + return GIMP_DIRTY_IMAGE_META | GIMP_DIRTY_ITEM_META; + + case GIMP_UNDO_GROUP_VECTORS_IMPORT: + return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_VECTORS; + + case GIMP_UNDO_GROUP_MISC: + return GIMP_DIRTY_ALL; + + default: + break; + } + + return GIMP_DIRTY_ALL; +} diff --git a/app/core/gimpimage-undo.h b/app/core/gimpimage-undo.h new file mode 100644 index 0000000..34bd2f4 --- /dev/null +++ b/app/core/gimpimage-undo.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 . + */ + +#ifndef __GIMP_IMAGE__UNDO_H__ +#define __GIMP_IMAGE__UNDO_H__ + + +gboolean gimp_image_undo_is_enabled (GimpImage *image); +gboolean gimp_image_undo_enable (GimpImage *image); +gboolean gimp_image_undo_disable (GimpImage *image); +gboolean gimp_image_undo_freeze (GimpImage *image); +gboolean gimp_image_undo_thaw (GimpImage *image); + +gboolean gimp_image_undo (GimpImage *image); +gboolean gimp_image_redo (GimpImage *image); + +gboolean gimp_image_strong_undo (GimpImage *image); +gboolean gimp_image_strong_redo (GimpImage *image); + +GimpUndoStack * gimp_image_get_undo_stack (GimpImage *image); +GimpUndoStack * gimp_image_get_redo_stack (GimpImage *image); + +void gimp_image_undo_free (GimpImage *image); + +gint gimp_image_get_undo_group_count (GimpImage *image); +gboolean gimp_image_undo_group_start (GimpImage *image, + GimpUndoType undo_type, + const gchar *name); +gboolean gimp_image_undo_group_end (GimpImage *image); + +GimpUndo * gimp_image_undo_push (GimpImage *image, + GType object_type, + GimpUndoType undo_type, + const gchar *name, + GimpDirtyMask dirty_mask, + ...) G_GNUC_NULL_TERMINATED; + +GimpUndo * gimp_image_undo_can_compress (GimpImage *image, + GType object_type, + GimpUndoType undo_type); + + +#endif /* __GIMP_IMAGE__UNDO_H__ */ diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c new file mode 100644 index 0000000..9908d1b --- /dev/null +++ b/app/core/gimpimage.c @@ -0,0 +1,5191 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp.h" +#include "gimp-memsize.h" +#include "gimp-parasites.h" +#include "gimp-utils.h" +#include "gimpcontext.h" +#include "gimpdrawable-floating-selection.h" +#include "gimpdrawablestack.h" +#include "gimpgrid.h" +#include "gimperror.h" +#include "gimpguide.h" +#include "gimpidtable.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-guides.h" +#include "gimpimage-item-list.h" +#include "gimpimage-metadata.h" +#include "gimpimage-sample-points.h" +#include "gimpimage-preview.h" +#include "gimpimage-private.h" +#include "gimpimage-quick-mask.h" +#include "gimpimage-symmetry.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitemtree.h" +#include "gimplayer.h" +#include "gimplayer-floating-selection.h" +#include "gimplayermask.h" +#include "gimplayerstack.h" +#include "gimpmarshal.h" +#include "gimpparasitelist.h" +#include "gimppickable.h" +#include "gimpprojectable.h" +#include "gimpprojection.h" +#include "gimpsamplepoint.h" +#include "gimpselection.h" +#include "gimpsymmetry.h" +#include "gimptempbuf.h" +#include "gimptemplate.h" +#include "gimpundostack.h" + +#include "vectors/gimpvectors.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +#ifdef DEBUG +#define TRC(x) g_printerr x +#else +#define TRC(x) +#endif + + +enum +{ + MODE_CHANGED, + PRECISION_CHANGED, + ALPHA_CHANGED, + FLOATING_SELECTION_CHANGED, + ACTIVE_LAYER_CHANGED, + ACTIVE_CHANNEL_CHANGED, + ACTIVE_VECTORS_CHANGED, + LINKED_ITEMS_CHANGED, + COMPONENT_VISIBILITY_CHANGED, + COMPONENT_ACTIVE_CHANGED, + MASK_CHANGED, + RESOLUTION_CHANGED, + SIZE_CHANGED_DETAILED, + UNIT_CHANGED, + QUICK_MASK_CHANGED, + SELECTION_INVALIDATE, + CLEAN, + DIRTY, + SAVING, + SAVED, + EXPORTED, + GUIDE_ADDED, + GUIDE_REMOVED, + GUIDE_MOVED, + SAMPLE_POINT_ADDED, + SAMPLE_POINT_REMOVED, + SAMPLE_POINT_MOVED, + PARASITE_ATTACHED, + PARASITE_DETACHED, + COLORMAP_CHANGED, + UNDO_EVENT, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_GIMP, + PROP_ID, + PROP_WIDTH, + PROP_HEIGHT, + PROP_BASE_TYPE, + PROP_PRECISION, + PROP_METADATA, + PROP_BUFFER, + PROP_SYMMETRY, + PROP_CONVERTING, +}; + + +/* local function prototypes */ + +static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface); +static void gimp_projectable_iface_init (GimpProjectableInterface *iface); +static void gimp_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_image_constructed (GObject *object); +static void gimp_image_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_image_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_image_dispose (GObject *object); +static void gimp_image_finalize (GObject *object); + +static void gimp_image_name_changed (GimpObject *object); +static gint64 gimp_image_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_image_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static void gimp_image_size_changed (GimpViewable *viewable); +static gchar * gimp_image_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_image_real_mode_changed (GimpImage *image); +static void gimp_image_real_precision_changed(GimpImage *image); +static void gimp_image_real_resolution_changed(GimpImage *image); +static void gimp_image_real_size_changed_detailed + (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height); +static void gimp_image_real_unit_changed (GimpImage *image); +static void gimp_image_real_colormap_changed (GimpImage *image, + gint color_index); + +static const guint8 * + gimp_image_color_managed_get_icc_profile (GimpColorManaged *managed, + gsize *len); +static GimpColorProfile * + gimp_image_color_managed_get_color_profile (GimpColorManaged *managed); +static void + gimp_image_color_managed_profile_changed (GimpColorManaged *managed); + +static void gimp_image_projectable_flush (GimpProjectable *projectable, + gboolean invalidate_preview); +static GeglRectangle gimp_image_get_bounding_box (GimpProjectable *projectable); +static GeglNode * gimp_image_get_graph (GimpProjectable *projectable); +static GimpImage * gimp_image_get_image (GimpProjectable *projectable); +static const Babl * gimp_image_get_proj_format (GimpProjectable *projectable); + +static void gimp_image_pickable_flush (GimpPickable *pickable); +static GeglBuffer * gimp_image_get_buffer (GimpPickable *pickable); +static gboolean gimp_image_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +static gdouble gimp_image_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +static void gimp_image_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); +static void gimp_image_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +static void gimp_image_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); + +static void gimp_image_projection_buffer_notify + (GimpProjection *projection, + const GParamSpec *pspec, + GimpImage *image); +static void gimp_image_mask_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpImage *image); +static void gimp_image_layers_changed (GimpContainer *container, + GimpChannel *channel, + GimpImage *image); +static void gimp_image_layer_offset_changed (GimpDrawable *drawable, + const GParamSpec *pspec, + GimpImage *image); +static void gimp_image_layer_bounding_box_changed + (GimpDrawable *drawable, + GimpImage *image); +static void gimp_image_layer_alpha_changed (GimpDrawable *drawable, + GimpImage *image); +static void gimp_image_channel_add (GimpContainer *container, + GimpChannel *channel, + GimpImage *image); +static void gimp_image_channel_remove (GimpContainer *container, + GimpChannel *channel, + GimpImage *image); +static void gimp_image_channel_name_changed (GimpChannel *channel, + GimpImage *image); +static void gimp_image_channel_color_changed (GimpChannel *channel, + GimpImage *image); +static void gimp_image_active_layer_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image); +static void gimp_image_active_channel_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image); +static void gimp_image_active_vectors_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image); + +static void gimp_image_freeze_bounding_box (GimpImage *image); +static void gimp_image_thaw_bounding_box (GimpImage *image); +static void gimp_image_update_bounding_box (GimpImage *image); + + +G_DEFINE_TYPE_WITH_CODE (GimpImage, gimp_image, GIMP_TYPE_VIEWABLE, + G_ADD_PRIVATE (GimpImage) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_color_managed_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROJECTABLE, + gimp_projectable_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_pickable_iface_init)) + +#define parent_class gimp_image_parent_class + +static guint gimp_image_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_image_class_init (GimpImageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + gimp_image_signals[MODE_CHANGED] = + g_signal_new ("mode-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, mode_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[PRECISION_CHANGED] = + g_signal_new ("precision-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, precision_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[ALPHA_CHANGED] = + g_signal_new ("alpha-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, alpha_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[FLOATING_SELECTION_CHANGED] = + g_signal_new ("floating-selection-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, floating_selection_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[ACTIVE_LAYER_CHANGED] = + g_signal_new ("active-layer-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, active_layer_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[ACTIVE_CHANNEL_CHANGED] = + g_signal_new ("active-channel-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, active_channel_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[ACTIVE_VECTORS_CHANGED] = + g_signal_new ("active-vectors-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, active_vectors_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[LINKED_ITEMS_CHANGED] = + g_signal_new ("linked-items-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, linked_items_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[COMPONENT_VISIBILITY_CHANGED] = + g_signal_new ("component-visibility-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, component_visibility_changed), + NULL, NULL, + gimp_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GIMP_TYPE_CHANNEL_TYPE); + + gimp_image_signals[COMPONENT_ACTIVE_CHANGED] = + g_signal_new ("component-active-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, component_active_changed), + NULL, NULL, + gimp_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GIMP_TYPE_CHANNEL_TYPE); + + gimp_image_signals[MASK_CHANGED] = + g_signal_new ("mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[RESOLUTION_CHANGED] = + g_signal_new ("resolution-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, resolution_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[SIZE_CHANGED_DETAILED] = + g_signal_new ("size-changed-detailed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, size_changed_detailed), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + gimp_image_signals[UNIT_CHANGED] = + g_signal_new ("unit-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, unit_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[QUICK_MASK_CHANGED] = + g_signal_new ("quick-mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, quick_mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[SELECTION_INVALIDATE] = + g_signal_new ("selection-invalidate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, selection_invalidate), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[CLEAN] = + g_signal_new ("clean", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, clean), + NULL, NULL, + gimp_marshal_VOID__FLAGS, + G_TYPE_NONE, 1, + GIMP_TYPE_DIRTY_MASK); + + gimp_image_signals[DIRTY] = + g_signal_new ("dirty", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, dirty), + NULL, NULL, + gimp_marshal_VOID__FLAGS, + G_TYPE_NONE, 1, + GIMP_TYPE_DIRTY_MASK); + + gimp_image_signals[SAVING] = + g_signal_new ("saving", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, saving), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_image_signals[SAVED] = + g_signal_new ("saved", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, saved), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_FILE); + + gimp_image_signals[EXPORTED] = + g_signal_new ("exported", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, exported), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_FILE); + + gimp_image_signals[GUIDE_ADDED] = + g_signal_new ("guide-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, guide_added), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_GUIDE); + + gimp_image_signals[GUIDE_REMOVED] = + g_signal_new ("guide-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, guide_removed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_GUIDE); + + gimp_image_signals[GUIDE_MOVED] = + g_signal_new ("guide-moved", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, guide_moved), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_GUIDE); + + gimp_image_signals[SAMPLE_POINT_ADDED] = + g_signal_new ("sample-point-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, sample_point_added), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_SAMPLE_POINT); + + gimp_image_signals[SAMPLE_POINT_REMOVED] = + g_signal_new ("sample-point-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, sample_point_removed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_SAMPLE_POINT); + + gimp_image_signals[SAMPLE_POINT_MOVED] = + g_signal_new ("sample-point-moved", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, sample_point_moved), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_SAMPLE_POINT); + + gimp_image_signals[PARASITE_ATTACHED] = + g_signal_new ("parasite-attached", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, parasite_attached), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + gimp_image_signals[PARASITE_DETACHED] = + g_signal_new ("parasite-detached", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, parasite_detached), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + gimp_image_signals[COLORMAP_CHANGED] = + g_signal_new ("colormap-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, colormap_changed), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + gimp_image_signals[UNDO_EVENT] = + g_signal_new ("undo-event", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImageClass, undo_event), + NULL, NULL, + gimp_marshal_VOID__ENUM_OBJECT, + G_TYPE_NONE, 2, + GIMP_TYPE_UNDO_EVENT, + GIMP_TYPE_UNDO); + + object_class->constructed = gimp_image_constructed; + object_class->set_property = gimp_image_set_property; + object_class->get_property = gimp_image_get_property; + object_class->dispose = gimp_image_dispose; + object_class->finalize = gimp_image_finalize; + + gimp_object_class->name_changed = gimp_image_name_changed; + gimp_object_class->get_memsize = gimp_image_get_memsize; + + viewable_class->default_icon_name = "gimp-image"; + viewable_class->get_size = gimp_image_get_size; + viewable_class->size_changed = gimp_image_size_changed; + viewable_class->get_preview_size = gimp_image_get_preview_size; + viewable_class->get_popup_size = gimp_image_get_popup_size; + viewable_class->get_new_preview = gimp_image_get_new_preview; + viewable_class->get_new_pixbuf = gimp_image_get_new_pixbuf; + viewable_class->get_description = gimp_image_get_description; + + klass->mode_changed = gimp_image_real_mode_changed; + klass->precision_changed = gimp_image_real_precision_changed; + klass->alpha_changed = NULL; + klass->floating_selection_changed = NULL; + klass->active_layer_changed = NULL; + klass->active_channel_changed = NULL; + klass->active_vectors_changed = NULL; + klass->linked_items_changed = NULL; + klass->component_visibility_changed = NULL; + klass->component_active_changed = NULL; + klass->mask_changed = NULL; + klass->resolution_changed = gimp_image_real_resolution_changed; + klass->size_changed_detailed = gimp_image_real_size_changed_detailed; + klass->unit_changed = gimp_image_real_unit_changed; + klass->quick_mask_changed = NULL; + klass->selection_invalidate = NULL; + + klass->clean = NULL; + klass->dirty = NULL; + klass->saving = NULL; + klass->saved = NULL; + klass->exported = NULL; + klass->guide_added = NULL; + klass->guide_removed = NULL; + klass->guide_moved = NULL; + klass->sample_point_added = NULL; + klass->sample_point_removed = NULL; + klass->sample_point_moved = NULL; + klass->parasite_attached = NULL; + klass->parasite_detached = NULL; + klass->colormap_changed = gimp_image_real_colormap_changed; + klass->undo_event = NULL; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_int ("id", NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + 1, GIMP_MAX_IMAGE_SIZE, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", NULL, NULL, + 1, GIMP_MAX_IMAGE_SIZE, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_BASE_TYPE, + g_param_spec_enum ("base-type", NULL, NULL, + GIMP_TYPE_IMAGE_BASE_TYPE, + GIMP_RGB, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PRECISION, + g_param_spec_enum ("precision", NULL, NULL, + GIMP_TYPE_PRECISION, + GIMP_PRECISION_U8_GAMMA, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_METADATA, + g_param_spec_object ("metadata", NULL, NULL, + GEXIV2_TYPE_METADATA, + GIMP_PARAM_READABLE)); + + g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); + + g_object_class_install_property (object_class, PROP_SYMMETRY, + g_param_spec_gtype ("symmetry", + NULL, _("Symmetry"), + GIMP_TYPE_SYMMETRY, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONVERTING, + g_param_spec_boolean ("converting", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_icc_profile = gimp_image_color_managed_get_icc_profile; + iface->get_color_profile = gimp_image_color_managed_get_color_profile; + iface->profile_changed = gimp_image_color_managed_profile_changed; +} + +static void +gimp_projectable_iface_init (GimpProjectableInterface *iface) +{ + iface->flush = gimp_image_projectable_flush; + iface->get_image = gimp_image_get_image; + iface->get_format = gimp_image_get_proj_format; + iface->get_bounding_box = gimp_image_get_bounding_box; + iface->get_graph = gimp_image_get_graph; + iface->invalidate_preview = (void (*) (GimpProjectable*)) gimp_viewable_invalidate_preview; +} + +static void +gimp_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->flush = gimp_image_pickable_flush; + iface->get_image = (GimpImage * (*) (GimpPickable *pickable)) gimp_image_get_image; + iface->get_format = (const Babl * (*) (GimpPickable *pickable)) gimp_image_get_proj_format; + iface->get_format_with_alpha = (const Babl * (*) (GimpPickable *pickable)) gimp_image_get_proj_format; + iface->get_buffer = gimp_image_get_buffer; + iface->get_pixel_at = gimp_image_get_pixel_at; + iface->get_opacity_at = gimp_image_get_opacity_at; + iface->get_pixel_average = gimp_image_get_pixel_average; + iface->pixel_to_srgb = gimp_image_pixel_to_srgb; + iface->srgb_to_pixel = gimp_image_srgb_to_pixel; +} + +static void +gimp_image_init (GimpImage *image) +{ + GimpImagePrivate *private = gimp_image_get_instance_private (image); + gint i; + + image->priv = private; + + private->ID = 0; + + private->load_proc = NULL; + private->save_proc = NULL; + + private->width = 0; + private->height = 0; + private->xresolution = 1.0; + private->yresolution = 1.0; + private->resolution_set = FALSE; + private->resolution_unit = GIMP_UNIT_INCH; + private->base_type = GIMP_RGB; + private->precision = GIMP_PRECISION_U8_GAMMA; + private->new_layer_mode = -1; + + private->show_all = 0; + private->bounding_box.x = 0; + private->bounding_box.y = 0; + private->bounding_box.width = 0; + private->bounding_box.height = 0; + private->pickable_buffer = NULL; + + private->colormap = NULL; + private->n_colors = 0; + private->palette = NULL; + + private->is_color_managed = TRUE; + + private->metadata = NULL; + + private->dirty = 1; + private->dirty_time = 0; + private->undo_freeze_count = 0; + + private->export_dirty = 1; + + private->instance_count = 0; + private->disp_count = 0; + + private->tattoo_state = 0; + + private->projection = gimp_projection_new (GIMP_PROJECTABLE (image)); + + private->symmetries = NULL; + private->active_symmetry = NULL; + + private->guides = NULL; + private->grid = NULL; + private->sample_points = NULL; + + private->layers = gimp_item_tree_new (image, + GIMP_TYPE_LAYER_STACK, + GIMP_TYPE_LAYER); + private->channels = gimp_item_tree_new (image, + GIMP_TYPE_DRAWABLE_STACK, + GIMP_TYPE_CHANNEL); + private->vectors = gimp_item_tree_new (image, + GIMP_TYPE_ITEM_STACK, + GIMP_TYPE_VECTORS); + private->layer_stack = NULL; + + g_signal_connect (private->projection, "notify::buffer", + G_CALLBACK (gimp_image_projection_buffer_notify), + image); + + g_signal_connect (private->layers, "notify::active-item", + G_CALLBACK (gimp_image_active_layer_notify), + image); + g_signal_connect (private->channels, "notify::active-item", + G_CALLBACK (gimp_image_active_channel_notify), + image); + g_signal_connect (private->vectors, "notify::active-item", + G_CALLBACK (gimp_image_active_vectors_notify), + image); + + g_signal_connect_swapped (private->layers->container, "update", + G_CALLBACK (gimp_image_invalidate), + image); + + private->layer_offset_x_handler = + gimp_container_add_handler (private->layers->container, "notify::offset-x", + G_CALLBACK (gimp_image_layer_offset_changed), + image); + private->layer_offset_y_handler = + gimp_container_add_handler (private->layers->container, "notify::offset-y", + G_CALLBACK (gimp_image_layer_offset_changed), + image); + private->layer_bounding_box_handler = + gimp_container_add_handler (private->layers->container, "bounding-box-changed", + G_CALLBACK (gimp_image_layer_bounding_box_changed), + image); + private->layer_alpha_handler = + gimp_container_add_handler (private->layers->container, "alpha-changed", + G_CALLBACK (gimp_image_layer_alpha_changed), + image); + + g_signal_connect (private->layers->container, "add", + G_CALLBACK (gimp_image_layers_changed), + image); + g_signal_connect (private->layers->container, "remove", + G_CALLBACK (gimp_image_layers_changed), + image); + + g_signal_connect_swapped (private->channels->container, "update", + G_CALLBACK (gimp_image_invalidate), + image); + + private->channel_name_changed_handler = + gimp_container_add_handler (private->channels->container, "name-changed", + G_CALLBACK (gimp_image_channel_name_changed), + image); + private->channel_color_changed_handler = + gimp_container_add_handler (private->channels->container, "color-changed", + G_CALLBACK (gimp_image_channel_color_changed), + image); + + g_signal_connect (private->channels->container, "add", + G_CALLBACK (gimp_image_channel_add), + image); + g_signal_connect (private->channels->container, "remove", + G_CALLBACK (gimp_image_channel_remove), + image); + + private->floating_sel = NULL; + private->selection_mask = NULL; + + private->parasites = gimp_parasite_list_new (); + + for (i = 0; i < MAX_CHANNELS; i++) + { + private->visible[i] = TRUE; + private->active[i] = TRUE; + } + + private->quick_mask_state = FALSE; + private->quick_mask_inverted = FALSE; + gimp_rgba_set (&private->quick_mask_color, 1.0, 0.0, 0.0, 0.5); + + private->undo_stack = gimp_undo_stack_new (image); + private->redo_stack = gimp_undo_stack_new (image); + private->group_count = 0; + private->pushing_undo_group = GIMP_UNDO_GROUP_NONE; + + private->flush_accum.alpha_changed = FALSE; + private->flush_accum.mask_changed = FALSE; + private->flush_accum.floating_selection_changed = FALSE; + private->flush_accum.preview_invalidated = FALSE; +} + +static void +gimp_image_constructed (GObject *object) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpChannel *selection; + GimpCoreConfig *config; + GimpTemplate *template; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (image->gimp)); + + config = image->gimp->config; + + private->ID = gimp_id_table_insert (image->gimp->image_table, image); + + template = config->default_image; + + private->xresolution = gimp_template_get_resolution_x (template); + private->yresolution = gimp_template_get_resolution_y (template); + private->resolution_unit = gimp_template_get_resolution_unit (template); + + private->grid = gimp_config_duplicate (GIMP_CONFIG (config->default_grid)); + + private->quick_mask_color = config->quick_mask_color; + + gimp_image_update_bounding_box (image); + + if (private->base_type == GIMP_INDEXED) + gimp_image_colormap_init (image); + + selection = gimp_selection_new (image, + gimp_image_get_width (image), + gimp_image_get_height (image)); + gimp_image_take_mask (image, selection); + + g_signal_connect_object (config, "notify::transparency-type", + G_CALLBACK (gimp_item_stack_invalidate_previews), + private->layers->container, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::transparency-size", + G_CALLBACK (gimp_item_stack_invalidate_previews), + private->layers->container, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::layer-previews", + G_CALLBACK (gimp_viewable_size_changed), + image, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::group-layer-previews", + G_CALLBACK (gimp_viewable_size_changed), + image, G_CONNECT_SWAPPED); + + gimp_container_add (image->gimp->images, GIMP_OBJECT (image)); +} + +static void +gimp_image_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + switch (property_id) + { + case PROP_GIMP: + image->gimp = g_value_get_object (value); + break; + + case PROP_WIDTH: + private->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_int (value); + break; + + case PROP_BASE_TYPE: + private->base_type = g_value_get_enum (value); + _gimp_image_free_color_transforms (image); + break; + + case PROP_PRECISION: + private->precision = g_value_get_enum (value); + _gimp_image_free_color_transforms (image); + break; + + case PROP_SYMMETRY: + { + GList *iter; + GType type = g_value_get_gtype (value); + + if (private->active_symmetry) + g_object_set (private->active_symmetry, + "active", FALSE, + NULL); + private->active_symmetry = NULL; + + for (iter = private->symmetries; iter; iter = g_list_next (iter)) + { + GimpSymmetry *sym = iter->data; + + if (type == G_TYPE_FROM_INSTANCE (sym)) + private->active_symmetry = iter->data; + } + + if (! private->active_symmetry && + g_type_is_a (type, GIMP_TYPE_SYMMETRY)) + { + GimpSymmetry *sym = gimp_image_symmetry_new (image, type); + + gimp_image_symmetry_add (image, sym); + g_object_unref (sym); + + private->active_symmetry = sym; + } + + if (private->active_symmetry) + g_object_set (private->active_symmetry, + "active", TRUE, + NULL); + } + break; + + case PROP_CONVERTING: + private->converting = g_value_get_boolean (value); + break; + + case PROP_ID: + case PROP_METADATA: + case PROP_BUFFER: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, image->gimp); + break; + case PROP_ID: + g_value_set_int (value, private->ID); + break; + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, private->height); + break; + case PROP_BASE_TYPE: + g_value_set_enum (value, private->base_type); + break; + case PROP_PRECISION: + g_value_set_enum (value, private->precision); + break; + case PROP_METADATA: + g_value_set_object (value, gimp_image_get_metadata (image)); + break; + case PROP_BUFFER: + g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image))); + break; + case PROP_SYMMETRY: + g_value_set_gtype (value, + private->active_symmetry ? + G_TYPE_FROM_INSTANCE (private->active_symmetry) : + G_TYPE_NONE); + break; + case PROP_CONVERTING: + g_value_set_boolean (value, private->converting); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_dispose (GObject *object) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->colormap) + gimp_image_colormap_dispose (image); + + gimp_image_undo_free (image); + + g_signal_handlers_disconnect_by_func (private->layers->container, + gimp_image_invalidate, + image); + + gimp_container_remove_handler (private->layers->container, + private->layer_offset_x_handler); + gimp_container_remove_handler (private->layers->container, + private->layer_offset_y_handler); + gimp_container_remove_handler (private->layers->container, + private->layer_bounding_box_handler); + gimp_container_remove_handler (private->layers->container, + private->layer_alpha_handler); + + g_signal_handlers_disconnect_by_func (private->layers->container, + gimp_image_layers_changed, + image); + + g_signal_handlers_disconnect_by_func (private->channels->container, + gimp_image_invalidate, + image); + + gimp_container_remove_handler (private->channels->container, + private->channel_name_changed_handler); + gimp_container_remove_handler (private->channels->container, + private->channel_color_changed_handler); + + g_signal_handlers_disconnect_by_func (private->channels->container, + gimp_image_channel_add, + image); + g_signal_handlers_disconnect_by_func (private->channels->container, + gimp_image_channel_remove, + image); + + g_object_run_dispose (G_OBJECT (private->layers)); + g_object_run_dispose (G_OBJECT (private->channels)); + g_object_run_dispose (G_OBJECT (private->vectors)); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_image_finalize (GObject *object) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + g_clear_object (&private->projection); + g_clear_object (&private->graph); + private->visible_mask = NULL; + + if (private->colormap) + gimp_image_colormap_free (image); + + if (private->color_profile) + _gimp_image_free_color_profile (image); + + g_clear_object (&private->pickable_buffer); + g_clear_object (&private->metadata); + g_clear_object (&private->file); + g_clear_object (&private->imported_file); + g_clear_object (&private->exported_file); + g_clear_object (&private->save_a_copy_file); + g_clear_object (&private->untitled_file); + g_clear_object (&private->layers); + g_clear_object (&private->channels); + g_clear_object (&private->vectors); + + if (private->layer_stack) + { + g_slist_free (private->layer_stack); + private->layer_stack = NULL; + } + + g_clear_object (&private->selection_mask); + g_clear_object (&private->parasites); + + if (private->guides) + { + g_list_free_full (private->guides, (GDestroyNotify) g_object_unref); + private->guides = NULL; + } + + if (private->symmetries) + { + g_list_free_full (private->symmetries, g_object_unref); + private->symmetries = NULL; + } + + g_clear_object (&private->grid); + + if (private->sample_points) + { + g_list_free_full (private->sample_points, + (GDestroyNotify) g_object_unref); + private->sample_points = NULL; + } + + g_clear_object (&private->undo_stack); + g_clear_object (&private->redo_stack); + + if (image->gimp && image->gimp->image_table) + { + gimp_id_table_remove (image->gimp->image_table, private->ID); + image->gimp = NULL; + } + + g_clear_pointer (&private->display_name, g_free); + g_clear_pointer (&private->display_path, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_image_name_changed (GimpObject *object) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + const gchar *name; + + if (GIMP_OBJECT_CLASS (parent_class)->name_changed) + GIMP_OBJECT_CLASS (parent_class)->name_changed (object); + + g_clear_pointer (&private->display_name, g_free); + g_clear_pointer (&private->display_path, g_free); + + /* We never want the empty string as a name, so change empty strings + * to NULL strings (without emitting the "name-changed" signal + * again) + */ + name = gimp_object_get_name (object); + if (name && strlen (name) == 0) + { + gimp_object_name_free (object); + name = NULL; + } + + g_clear_object (&private->file); + + if (name) + private->file = g_file_new_for_uri (name); +} + +static gint64 +gimp_image_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpImage *image = GIMP_IMAGE (object); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + gint64 memsize = 0; + + if (gimp_image_get_colormap (image)) + memsize += GIMP_IMAGE_COLORMAP_SIZE; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->palette), + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->projection), + gui_size); + + memsize += gimp_g_list_get_memsize (gimp_image_get_guides (image), + sizeof (GimpGuide)); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->grid), gui_size); + + memsize += gimp_g_list_get_memsize (gimp_image_get_sample_points (image), + sizeof (GimpSamplePoint)); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->layers), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->channels), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->vectors), + gui_size); + + memsize += gimp_g_slist_get_memsize (private->layer_stack, 0); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->selection_mask), + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->parasites), + gui_size); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->undo_stack), + gui_size); + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->redo_stack), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_image_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpImage *image = GIMP_IMAGE (viewable); + + *width = gimp_image_get_width (image); + *height = gimp_image_get_height (image); + + return TRUE; +} + +static void +gimp_image_size_changed (GimpViewable *viewable) +{ + GimpImage *image = GIMP_IMAGE (viewable); + GList *all_items; + GList *list; + + if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed) + GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable); + + all_items = gimp_image_get_layer_list (image); + for (list = all_items; list; list = g_list_next (list)) + { + GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (list->data)); + + gimp_viewable_size_changed (GIMP_VIEWABLE (list->data)); + + if (mask) + gimp_viewable_size_changed (GIMP_VIEWABLE (mask)); + } + g_list_free (all_items); + + all_items = gimp_image_get_channel_list (image); + g_list_free_full (all_items, (GDestroyNotify) gimp_viewable_size_changed); + + all_items = gimp_image_get_vectors_list (image); + g_list_free_full (all_items, (GDestroyNotify) gimp_viewable_size_changed); + + gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (image))); + + gimp_image_metadata_update_pixel_size (image); + + g_clear_object (&GIMP_IMAGE_GET_PRIVATE (image)->pickable_buffer); + + gimp_image_update_bounding_box (image); +} + +static gchar * +gimp_image_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpImage *image = GIMP_IMAGE (viewable); + + if (tooltip) + *tooltip = g_strdup (gimp_image_get_display_path (image)); + + return g_strdup_printf ("%s-%d", + gimp_image_get_display_name (image), + gimp_image_get_ID (image)); +} + +static void +gimp_image_real_mode_changed (GimpImage *image) +{ + gimp_projectable_structure_changed (GIMP_PROJECTABLE (image)); +} + +static void +gimp_image_real_precision_changed (GimpImage *image) +{ + gimp_image_metadata_update_bits_per_sample (image); + + gimp_projectable_structure_changed (GIMP_PROJECTABLE (image)); +} + +static void +gimp_image_real_resolution_changed (GimpImage *image) +{ + gimp_image_metadata_update_resolution (image); +} + +static void +gimp_image_real_size_changed_detailed (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height) +{ + /* Whenever GimpImage::size-changed-detailed is emitted, so is + * GimpViewable::size-changed. Clients choose what signal to listen + * to depending on how much info they need. + */ + gimp_viewable_size_changed (GIMP_VIEWABLE (image)); +} + +static void +gimp_image_real_unit_changed (GimpImage *image) +{ + gimp_image_metadata_update_resolution (image); +} + +static void +gimp_image_real_colormap_changed (GimpImage *image, + gint color_index) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->colormap && private->n_colors > 0) + { + babl_palette_set_palette (private->babl_palette_rgb, + gimp_babl_format (GIMP_RGB, + private->precision, FALSE), + private->colormap, + private->n_colors); + babl_palette_set_palette (private->babl_palette_rgba, + gimp_babl_format (GIMP_RGB, + private->precision, FALSE), + private->colormap, + private->n_colors); + } + + if (gimp_image_get_base_type (image) == GIMP_INDEXED) + { + /* A colormap alteration affects the whole image */ + gimp_image_invalidate_all (image); + + gimp_item_stack_invalidate_previews (GIMP_ITEM_STACK (private->layers->container)); + } +} + +static const guint8 * +gimp_image_color_managed_get_icc_profile (GimpColorManaged *managed, + gsize *len) +{ + return gimp_image_get_icc_profile (GIMP_IMAGE (managed), len); +} + +static GimpColorProfile * +gimp_image_color_managed_get_color_profile (GimpColorManaged *managed) +{ + GimpImage *image = GIMP_IMAGE (managed); + GimpColorProfile *profile = NULL; + + if (gimp_image_get_is_color_managed (image)) + profile = gimp_image_get_color_profile (image); + + if (! profile) + profile = gimp_image_get_builtin_color_profile (image); + + return profile; +} + +static void +gimp_image_color_managed_profile_changed (GimpColorManaged *managed) +{ + GimpImage *image = GIMP_IMAGE (managed); + GimpItemStack *layers = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + + gimp_image_metadata_update_colorspace (image); + + gimp_projectable_structure_changed (GIMP_PROJECTABLE (image)); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (image)); + gimp_item_stack_profile_changed (layers); +} + +static void +gimp_image_projectable_flush (GimpProjectable *projectable, + gboolean invalidate_preview) +{ + GimpImage *image = GIMP_IMAGE (projectable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->flush_accum.alpha_changed) + { + gimp_image_alpha_changed (image); + private->flush_accum.alpha_changed = FALSE; + } + + if (private->flush_accum.mask_changed) + { + gimp_image_mask_changed (image); + private->flush_accum.mask_changed = FALSE; + } + + if (private->flush_accum.floating_selection_changed) + { + gimp_image_floating_selection_changed (image); + private->flush_accum.floating_selection_changed = FALSE; + } + + if (private->flush_accum.preview_invalidated) + { + /* don't invalidate the preview here, the projection does this when + * it is completely constructed. + */ + private->flush_accum.preview_invalidated = FALSE; + } +} + +static GimpImage * +gimp_image_get_image (GimpProjectable *projectable) +{ + return GIMP_IMAGE (projectable); +} + +static const Babl * +gimp_image_get_proj_format (GimpProjectable *projectable) +{ + GimpImage *image = GIMP_IMAGE (projectable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + switch (private->base_type) + { + case GIMP_RGB: + case GIMP_INDEXED: + return gimp_image_get_format (image, GIMP_RGB, + gimp_image_get_precision (image), TRUE); + + case GIMP_GRAY: + return gimp_image_get_format (image, GIMP_GRAY, + gimp_image_get_precision (image), TRUE); + } + + g_return_val_if_reached (NULL); +} + +static void +gimp_image_pickable_flush (GimpPickable *pickable) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (pickable); + + return gimp_pickable_flush (GIMP_PICKABLE (private->projection)); +} + +static GeglBuffer * +gimp_image_get_buffer (GimpPickable *pickable) +{ + GimpImage *image = GIMP_IMAGE (pickable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! private->pickable_buffer) + { + GeglBuffer *buffer; + + buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (private->projection)); + + if (! private->show_all) + { + private->pickable_buffer = g_object_ref (buffer); + } + else + { + private->pickable_buffer = gegl_buffer_create_sub_buffer ( + buffer, + GEGL_RECTANGLE (0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image))); + } + } + + return private->pickable_buffer; +} + +static gboolean +gimp_image_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel) +{ + GimpImage *image = GIMP_IMAGE (pickable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (x >= 0 && + y >= 0 && + x < gimp_image_get_width (image) && + y < gimp_image_get_height (image)) + { + return gimp_pickable_get_pixel_at (GIMP_PICKABLE (private->projection), + x, y, format, pixel); + } + + return FALSE; +} + +static gdouble +gimp_image_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + GimpImage *image = GIMP_IMAGE (pickable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (x >= 0 && + y >= 0 && + x < gimp_image_get_width (image) && + y < gimp_image_get_height (image)) + { + return gimp_pickable_get_opacity_at (GIMP_PICKABLE (private->projection), + x, y); + } + + return FALSE; +} + +static void +gimp_image_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GeglBuffer *buffer = gimp_pickable_get_buffer (pickable); + + return gimp_gegl_average_color (buffer, rect, TRUE, GEGL_ABYSS_NONE, format, + pixel); +} + +static void +gimp_image_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color) +{ + gimp_image_color_profile_pixel_to_srgb (GIMP_IMAGE (pickable), + format, pixel, color); +} + +static void +gimp_image_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel) +{ + gimp_image_color_profile_srgb_to_pixel (GIMP_IMAGE (pickable), + color, format, pixel); +} + +static GeglRectangle +gimp_image_get_bounding_box (GimpProjectable *projectable) +{ + GimpImage *image = GIMP_IMAGE (projectable); + + return GIMP_IMAGE_GET_PRIVATE (image)->bounding_box; +} + +static GeglNode * +gimp_image_get_graph (GimpProjectable *projectable) +{ + GimpImage *image = GIMP_IMAGE (projectable); + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GeglNode *layers_node; + GeglNode *channels_node; + GeglNode *output; + GimpComponentMask mask; + + if (private->graph) + return private->graph; + + private->graph = gegl_node_new (); + + layers_node = + gimp_filter_stack_get_graph (GIMP_FILTER_STACK (private->layers->container)); + + gegl_node_add_child (private->graph, layers_node); + + mask = ~gimp_image_get_visible_mask (image) & GIMP_COMPONENT_MASK_ALL; + + private->visible_mask = + gegl_node_new_child (private->graph, + "operation", "gimp:mask-components", + "mask", mask, + "alpha", 1.0, + NULL); + + gegl_node_connect_to (layers_node, "output", + private->visible_mask, "input"); + + channels_node = + gimp_filter_stack_get_graph (GIMP_FILTER_STACK (private->channels->container)); + + gegl_node_add_child (private->graph, channels_node); + + gegl_node_connect_to (private->visible_mask, "output", + channels_node, "input"); + + output = gegl_node_get_output_proxy (private->graph, "output"); + + gegl_node_connect_to (channels_node, "output", + output, "input"); + + return private->graph; +} + +static void +gimp_image_projection_buffer_notify (GimpProjection *projection, + const GParamSpec *pspec, + GimpImage *image) +{ + g_clear_object (&GIMP_IMAGE_GET_PRIVATE (image)->pickable_buffer); +} + +static void +gimp_image_mask_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpImage *image) +{ + GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.mask_changed = TRUE; +} + +static void +gimp_image_layers_changed (GimpContainer *container, + GimpChannel *channel, + GimpImage *image) +{ + gimp_image_update_bounding_box (image); +} + +static void +gimp_image_layer_offset_changed (GimpDrawable *drawable, + const GParamSpec *pspec, + GimpImage *image) +{ + gimp_image_update_bounding_box (image); +} + +static void +gimp_image_layer_bounding_box_changed (GimpDrawable *drawable, + GimpImage *image) +{ + gimp_image_update_bounding_box (image); +} + +static void +gimp_image_layer_alpha_changed (GimpDrawable *drawable, + GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (gimp_container_get_n_children (private->layers->container) == 1) + private->flush_accum.alpha_changed = TRUE; +} + +static void +gimp_image_channel_add (GimpContainer *container, + GimpChannel *channel, + GimpImage *image) +{ + if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME, + gimp_object_get_name (channel))) + { + gimp_image_set_quick_mask_state (image, TRUE); + } +} + +static void +gimp_image_channel_remove (GimpContainer *container, + GimpChannel *channel, + GimpImage *image) +{ + if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME, + gimp_object_get_name (channel))) + { + gimp_image_set_quick_mask_state (image, FALSE); + } +} + +static void +gimp_image_channel_name_changed (GimpChannel *channel, + GimpImage *image) +{ + if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME, + gimp_object_get_name (channel))) + { + gimp_image_set_quick_mask_state (image, TRUE); + } + else if (gimp_image_get_quick_mask_state (image) && + ! gimp_image_get_quick_mask (image)) + { + gimp_image_set_quick_mask_state (image, FALSE); + } +} + +static void +gimp_image_channel_color_changed (GimpChannel *channel, + GimpImage *image) +{ + if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME, + gimp_object_get_name (channel))) + { + GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_color = channel->color; + } +} + +static void +gimp_image_active_layer_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GimpLayer *layer = gimp_image_get_active_layer (image); + + if (layer) + { + /* Configure the layer stack to reflect this change */ + private->layer_stack = g_slist_remove (private->layer_stack, layer); + private->layer_stack = g_slist_prepend (private->layer_stack, layer); + } + + g_signal_emit (image, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0); + + if (layer && gimp_image_get_active_channel (image)) + gimp_image_set_active_channel (image, NULL); +} + +static void +gimp_image_active_channel_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image) +{ + GimpChannel *channel = gimp_image_get_active_channel (image); + + g_signal_emit (image, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0); + + if (channel && gimp_image_get_active_layer (image)) + gimp_image_set_active_layer (image, NULL); +} + +static void +gimp_image_active_vectors_notify (GimpItemTree *tree, + const GParamSpec *pspec, + GimpImage *image) +{ + g_signal_emit (image, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0); +} + +static void +gimp_image_freeze_bounding_box (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + private->bounding_box_freeze_count++; +} + +static void +gimp_image_thaw_bounding_box (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + private->bounding_box_freeze_count--; + + if (private->bounding_box_freeze_count == 0 && + private->bounding_box_update_pending) + { + private->bounding_box_update_pending = FALSE; + + gimp_image_update_bounding_box (image); + } +} + +static void +gimp_image_update_bounding_box (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + GeglRectangle bounding_box; + + if (private->bounding_box_freeze_count > 0) + { + private->bounding_box_update_pending = TRUE; + + return; + } + + bounding_box.x = 0; + bounding_box.y = 0; + bounding_box.width = gimp_image_get_width (image); + bounding_box.height = gimp_image_get_height (image); + + if (private->show_all) + { + GList *iter; + + for (iter = gimp_image_get_layer_iter (image); + iter; + iter = g_list_next (iter)) + { + GimpLayer *layer = iter->data; + GeglRectangle layer_bounding_box; + gint offset_x; + gint offset_y; + + gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); + + layer_bounding_box = gimp_drawable_get_bounding_box ( + GIMP_DRAWABLE (layer)); + + layer_bounding_box.x += offset_x; + layer_bounding_box.y += offset_y; + + gegl_rectangle_bounding_box (&bounding_box, + &bounding_box, &layer_bounding_box); + } + } + + if (! gegl_rectangle_equal (&bounding_box, &private->bounding_box)) + { + private->bounding_box = bounding_box; + + gimp_projectable_bounds_changed (GIMP_PROJECTABLE (image), 0, 0); + } +} + + +/* public functions */ + +GimpImage * +gimp_image_new (Gimp *gimp, + gint width, + gint height, + GimpImageBaseType base_type, + GimpPrecision precision) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (gimp_babl_is_valid (base_type, precision), NULL); + + return g_object_new (GIMP_TYPE_IMAGE, + "gimp", gimp, + "width", width, + "height", height, + "base-type", base_type, + "precision", precision, + NULL); +} + +gint64 +gimp_image_estimate_memsize (GimpImage *image, + GimpComponentType component_type, + gint width, + gint height) +{ + GList *drawables; + GList *list; + gint current_width; + gint current_height; + gint64 current_size; + gint64 scalable_size = 0; + gint64 scaled_size = 0; + gint64 new_size; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + current_width = gimp_image_get_width (image); + current_height = gimp_image_get_height (image); + current_size = gimp_object_get_memsize (GIMP_OBJECT (image), NULL); + + /* the part of the image's memsize that scales linearly with the image */ + drawables = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_LAYERS | + GIMP_ITEM_TYPE_CHANNELS, + GIMP_ITEM_SET_ALL); + + gimp_image_item_list_filter (drawables); + + drawables = g_list_prepend (drawables, gimp_image_get_mask (image)); + + for (list = drawables; list; list = g_list_next (list)) + { + GimpDrawable *drawable = list->data; + gdouble drawable_width; + gdouble drawable_height; + + drawable_width = gimp_item_get_width (GIMP_ITEM (drawable)); + drawable_height = gimp_item_get_height (GIMP_ITEM (drawable)); + + scalable_size += gimp_drawable_estimate_memsize (drawable, + gimp_drawable_get_component_type (drawable), + drawable_width, + drawable_height); + + scaled_size += gimp_drawable_estimate_memsize (drawable, + component_type, + drawable_width * width / + current_width, + drawable_height * height / + current_height); + } + + g_list_free (drawables); + + scalable_size += + gimp_projection_estimate_memsize (gimp_image_get_base_type (image), + gimp_image_get_component_type (image), + gimp_image_get_width (image), + gimp_image_get_height (image)); + + scaled_size += + gimp_projection_estimate_memsize (gimp_image_get_base_type (image), + component_type, + width, height); + + GIMP_LOG (IMAGE_SCALE, + "scalable_size = %"G_GINT64_FORMAT" scaled_size = %"G_GINT64_FORMAT, + scalable_size, scaled_size); + + new_size = current_size - scalable_size + scaled_size; + + return new_size; +} + +GimpImageBaseType +gimp_image_get_base_type (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), -1); + + return GIMP_IMAGE_GET_PRIVATE (image)->base_type; +} + +GimpComponentType +gimp_image_get_component_type (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), -1); + + return gimp_babl_component_type (GIMP_IMAGE_GET_PRIVATE (image)->precision); +} + +GimpPrecision +gimp_image_get_precision (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), -1); + + return GIMP_IMAGE_GET_PRIVATE (image)->precision; +} + +const Babl * +gimp_image_get_format (GimpImage *image, + GimpImageBaseType base_type, + GimpPrecision precision, + gboolean with_alpha) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + switch (base_type) + { + case GIMP_RGB: + case GIMP_GRAY: + return gimp_babl_format (base_type, precision, with_alpha); + + case GIMP_INDEXED: + if (precision == GIMP_PRECISION_U8_GAMMA) + { + if (with_alpha) + return gimp_image_colormap_get_rgba_format (image); + else + return gimp_image_colormap_get_rgb_format (image); + } + } + + g_return_val_if_reached (NULL); +} + +const Babl * +gimp_image_get_layer_format (GimpImage *image, + gboolean with_alpha) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_get_format (image, + gimp_image_get_base_type (image), + gimp_image_get_precision (image), + with_alpha); +} + +const Babl * +gimp_image_get_channel_format (GimpImage *image) +{ + GimpPrecision precision; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + precision = gimp_image_get_precision (image); + + if (precision == GIMP_PRECISION_U8_GAMMA) + return gimp_image_get_format (image, GIMP_GRAY, + gimp_image_get_precision (image), + FALSE); + + return gimp_babl_mask_format (precision); +} + +const Babl * +gimp_image_get_mask_format (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_babl_mask_format (gimp_image_get_precision (image)); +} + +GimpLayerMode +gimp_image_get_default_new_layer_mode (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_LAYER_MODE_NORMAL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->new_layer_mode == -1) + { + GList *layers = gimp_image_get_layer_list (image); + + if (layers) + { + GList *list; + + for (list = layers; list; list = g_list_next (list)) + { + GimpLayer *layer = list->data; + GimpLayerMode mode = gimp_layer_get_mode (layer); + + if (! gimp_layer_mode_is_legacy (mode)) + { + /* any non-legacy layer switches the mode to non-legacy + */ + private->new_layer_mode = GIMP_LAYER_MODE_NORMAL; + break; + } + } + + /* only if all layers are legacy, the mode is also legacy + */ + if (! list) + private->new_layer_mode = GIMP_LAYER_MODE_NORMAL_LEGACY; + + g_list_free (layers); + } + else + { + /* empty images are never considered legacy + */ + private->new_layer_mode = GIMP_LAYER_MODE_NORMAL; + } + } + + return private->new_layer_mode; +} + +void +gimp_image_unset_default_new_layer_mode (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->new_layer_mode = -1; +} + +gint +gimp_image_get_ID (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), -1); + + return GIMP_IMAGE_GET_PRIVATE (image)->ID; +} + +GimpImage * +gimp_image_get_by_ID (Gimp *gimp, + gint image_id) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->image_table == NULL) + return NULL; + + return (GimpImage *) gimp_id_table_lookup (gimp->image_table, image_id); +} + +void +gimp_image_set_file (GimpImage *image, + GFile *file) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->file != file) + { + gimp_object_take_name (GIMP_OBJECT (image), + file ? g_file_get_uri (file) : NULL); + } +} + +/** + * gimp_image_get_untitled_file: + * + * Returns: A #GFile saying "Untitled" for newly created images. + **/ +GFile * +gimp_image_get_untitled_file (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! private->untitled_file) + private->untitled_file = g_file_new_for_uri (_("Untitled")); + + return private->untitled_file; +} + +/** + * gimp_image_get_file_or_untitled: + * @image: A #GimpImage. + * + * Get the file of the XCF image, or the "Untitled" file if there is no file. + * + * Returns: A #GFile. + **/ +GFile * +gimp_image_get_file_or_untitled (GimpImage *image) +{ + GFile *file; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + file = gimp_image_get_file (image); + + if (! file) + file = gimp_image_get_untitled_file (image); + + return file; +} + +/** + * gimp_image_get_file: + * @image: A #GimpImage. + * + * Get the file of the XCF image, or NULL if there is no file. + * + * Returns: The file, or NULL. + **/ +GFile * +gimp_image_get_file (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->file; +} + +/** + * gimp_image_get_imported_file: + * @image: A #GimpImage. + * + * Returns: The file of the imported image, or NULL if the image has + * been saved as XCF after it was imported. + **/ +GFile * +gimp_image_get_imported_file (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->imported_file; +} + +/** + * gimp_image_get_exported_file: + * @image: A #GimpImage. + * + * Returns: The file of the image last exported from this XCF file, or + * NULL if the image has never been exported. + **/ +GFile * +gimp_image_get_exported_file (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->exported_file; +} + +/** + * gimp_image_get_save_a_copy_file: + * @image: A #GimpImage. + * + * Returns: The URI of the last copy that was saved of this XCF file. + **/ +GFile * +gimp_image_get_save_a_copy_file (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->save_a_copy_file; +} + +/** + * gimp_image_get_any_file: + * @image: A #GimpImage. + * + * Returns: The XCF file, the imported file, or the exported file, in + * that order of precedence. + **/ +GFile * +gimp_image_get_any_file (GimpImage *image) +{ + GFile *file; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + file = gimp_image_get_file (image); + if (! file) + { + file = gimp_image_get_imported_file (image); + if (! file) + { + file = gimp_image_get_exported_file (image); + } + } + + return file; +} + +/** + * gimp_image_set_imported_uri: + * @image: A #GimpImage. + * @file: + * + * Sets the URI this file was imported from. + **/ +void +gimp_image_set_imported_file (GimpImage *image, + GFile *file) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (g_set_object (&private->imported_file, file)) + { + gimp_object_name_changed (GIMP_OBJECT (image)); + } + + if (! private->resolution_set && file != NULL) + { + /* Unlike new files (which follow technological progress and will + * use higher default resolution, or explicitly chosen templates), + * imported files have a more backward-compatible value. + * + * 72 PPI is traditionnally the default value when none other had + * been explicitly set (for instance it is the default when no + * resolution metadata was set in Exif version 2.32, and below, + * standard). This historical value will only ever apply to loaded + * images. New images will continue having more modern or + * templated defaults. + */ + private->xresolution = 72.0; + private->yresolution = 72.0; + private->resolution_unit = GIMP_UNIT_INCH; + } +} + +/** + * gimp_image_set_exported_file: + * @image: A #GimpImage. + * @file: + * + * Sets the file this image was last exported to. Note that saving as + * XCF is not "exporting". + **/ +void +gimp_image_set_exported_file (GimpImage *image, + GFile *file) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (g_set_object (&private->exported_file, file)) + { + gimp_object_name_changed (GIMP_OBJECT (image)); + } +} + +/** + * gimp_image_set_save_a_copy_file: + * @image: A #GimpImage. + * @uri: + * + * Set the URI to the last copy this XCF file was saved to through the + * "save a copy" action. + **/ +void +gimp_image_set_save_a_copy_file (GimpImage *image, + GFile *file) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + g_set_object (&private->save_a_copy_file, file); +} + +static gchar * +gimp_image_format_display_uri (GimpImage *image, + gboolean basename) +{ + const gchar *uri_format = NULL; + const gchar *export_status = NULL; + GFile *file = NULL; + GFile *source = NULL; + GFile *dest = NULL; + GFile *display_file = NULL; + gboolean is_imported; + gboolean is_exported; + gchar *display_uri = NULL; + gchar *format_string; + gchar *tmp; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + file = gimp_image_get_file (image); + source = gimp_image_get_imported_file (image); + dest = gimp_image_get_exported_file (image); + + is_imported = (source != NULL); + is_exported = (dest != NULL); + + if (file) + { + display_file = g_object_ref (file); + uri_format = "%s"; + } + else + { + if (is_imported) + display_file = source; + + /* Calculate filename suffix */ + if (! gimp_image_is_export_dirty (image)) + { + if (is_exported) + { + display_file = dest; + export_status = _(" (exported)"); + } + else if (is_imported) + { + export_status = _(" (overwritten)"); + } + else + { + g_warning ("Unexpected code path, Save+export implementation is buggy!"); + } + } + else if (is_imported) + { + export_status = _(" (imported)"); + } + + if (display_file) + display_file = gimp_file_with_new_extension (display_file, NULL); + + uri_format = "[%s]"; + } + + if (! display_file) + display_file = g_object_ref (gimp_image_get_untitled_file (image)); + + if (basename) + display_uri = g_path_get_basename (gimp_file_get_utf8_name (display_file)); + else + display_uri = g_strdup (gimp_file_get_utf8_name (display_file)); + + g_object_unref (display_file); + + format_string = g_strconcat (uri_format, export_status, NULL); + + tmp = g_strdup_printf (format_string, display_uri); + g_free (display_uri); + display_uri = tmp; + + g_free (format_string); + + return display_uri; +} + +const gchar * +gimp_image_get_display_name (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! private->display_name) + private->display_name = gimp_image_format_display_uri (image, TRUE); + + return private->display_name; +} + +const gchar * +gimp_image_get_display_path (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! private->display_path) + private->display_path = gimp_image_format_display_uri (image, FALSE); + + return private->display_path; +} + +void +gimp_image_set_load_proc (GimpImage *image, + GimpPlugInProcedure *proc) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->load_proc = proc; +} + +GimpPlugInProcedure * +gimp_image_get_load_proc (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->load_proc; +} + +void +gimp_image_set_save_proc (GimpImage *image, + GimpPlugInProcedure *proc) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->save_proc = proc; +} + +GimpPlugInProcedure * +gimp_image_get_save_proc (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->save_proc; +} + +void +gimp_image_set_export_proc (GimpImage *image, + GimpPlugInProcedure *proc) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->export_proc = proc; +} + +GimpPlugInProcedure * +gimp_image_get_export_proc (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->export_proc; +} + +gint +gimp_image_get_xcf_version (GimpImage *image, + gboolean zlib_compression, + gint *gimp_version, + const gchar **version_string, + gchar **version_reason) +{ + GList *layers; + GList *list; + GList *reasons = NULL; + gint version = 0; /* default to oldest */ + const gchar *enum_desc; + +#define ADD_REASON(_reason) \ + if (version_reason) { \ + gchar *tmp = _reason; \ + if (g_list_find_custom (reasons, tmp, (GCompareFunc) strcmp)) \ + g_free (tmp); \ + else \ + reasons = g_list_prepend (reasons, tmp); } + + /* need version 1 for colormaps */ + if (gimp_image_get_colormap (image)) + version = 1; + + layers = gimp_image_get_layer_list (image); + + for (list = layers; list; list = g_list_next (list)) + { + GimpLayer *layer = GIMP_LAYER (list->data); + + switch (gimp_layer_get_mode (layer)) + { + /* Modes that exist since ancient times */ + case GIMP_LAYER_MODE_NORMAL_LEGACY: + case GIMP_LAYER_MODE_DISSOLVE: + case GIMP_LAYER_MODE_BEHIND_LEGACY: + case GIMP_LAYER_MODE_MULTIPLY_LEGACY: + case GIMP_LAYER_MODE_SCREEN_LEGACY: + case GIMP_LAYER_MODE_OVERLAY_LEGACY: + case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: + case GIMP_LAYER_MODE_ADDITION_LEGACY: + case GIMP_LAYER_MODE_SUBTRACT_LEGACY: + case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: + case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: + case GIMP_LAYER_MODE_HSV_HUE_LEGACY: + case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: + case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: + case GIMP_LAYER_MODE_HSV_VALUE_LEGACY: + case GIMP_LAYER_MODE_DIVIDE_LEGACY: + case GIMP_LAYER_MODE_DODGE_LEGACY: + case GIMP_LAYER_MODE_BURN_LEGACY: + case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: + break; + + /* Since 2.6 */ + case GIMP_LAYER_MODE_SOFTLIGHT_LEGACY: + case GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY: + case GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY: + case GIMP_LAYER_MODE_COLOR_ERASE_LEGACY: + gimp_enum_get_value (GIMP_TYPE_LAYER_MODE, + gimp_layer_get_mode (layer), + NULL, NULL, &enum_desc, NULL); + ADD_REASON (g_strdup_printf (_("Layer mode '%s' was added in %s"), + enum_desc, "GIMP 2.6")); + version = MAX (2, version); + break; + + /* Since 2.10 */ + case GIMP_LAYER_MODE_OVERLAY: + case GIMP_LAYER_MODE_LCH_HUE: + case GIMP_LAYER_MODE_LCH_CHROMA: + case GIMP_LAYER_MODE_LCH_COLOR: + case GIMP_LAYER_MODE_LCH_LIGHTNESS: + gimp_enum_get_value (GIMP_TYPE_LAYER_MODE, + gimp_layer_get_mode (layer), + NULL, NULL, &enum_desc, NULL); + ADD_REASON (g_strdup_printf (_("Layer mode '%s' was added in %s"), + enum_desc, "GIMP 2.10")); + version = MAX (9, version); + break; + + /* Since 2.10 */ + case GIMP_LAYER_MODE_NORMAL: + case GIMP_LAYER_MODE_BEHIND: + case GIMP_LAYER_MODE_MULTIPLY: + case GIMP_LAYER_MODE_SCREEN: + case GIMP_LAYER_MODE_DIFFERENCE: + case GIMP_LAYER_MODE_ADDITION: + case GIMP_LAYER_MODE_SUBTRACT: + case GIMP_LAYER_MODE_DARKEN_ONLY: + case GIMP_LAYER_MODE_LIGHTEN_ONLY: + case GIMP_LAYER_MODE_HSV_HUE: + case GIMP_LAYER_MODE_HSV_SATURATION: + case GIMP_LAYER_MODE_HSL_COLOR: + case GIMP_LAYER_MODE_HSV_VALUE: + case GIMP_LAYER_MODE_DIVIDE: + case GIMP_LAYER_MODE_DODGE: + case GIMP_LAYER_MODE_BURN: + case GIMP_LAYER_MODE_HARDLIGHT: + case GIMP_LAYER_MODE_SOFTLIGHT: + case GIMP_LAYER_MODE_GRAIN_EXTRACT: + case GIMP_LAYER_MODE_GRAIN_MERGE: + case GIMP_LAYER_MODE_VIVID_LIGHT: + case GIMP_LAYER_MODE_PIN_LIGHT: + case GIMP_LAYER_MODE_LINEAR_LIGHT: + case GIMP_LAYER_MODE_HARD_MIX: + case GIMP_LAYER_MODE_EXCLUSION: + case GIMP_LAYER_MODE_LINEAR_BURN: + case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY: + case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY: + case GIMP_LAYER_MODE_LUMINANCE: + case GIMP_LAYER_MODE_COLOR_ERASE: + case GIMP_LAYER_MODE_ERASE: + case GIMP_LAYER_MODE_MERGE: + case GIMP_LAYER_MODE_SPLIT: + case GIMP_LAYER_MODE_PASS_THROUGH: + gimp_enum_get_value (GIMP_TYPE_LAYER_MODE, + gimp_layer_get_mode (layer), + NULL, NULL, &enum_desc, NULL); + ADD_REASON (g_strdup_printf (_("Layer mode '%s' was added in %s"), + enum_desc, "GIMP 2.10")); + version = MAX (10, version); + break; + + /* Just here instead of default so we get compiler warnings */ + case GIMP_LAYER_MODE_REPLACE: + case GIMP_LAYER_MODE_ANTI_ERASE: + case GIMP_LAYER_MODE_SEPARATOR: + break; + } + + /* need version 3 for layer trees */ + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + { + ADD_REASON (g_strdup_printf (_("Layer groups were added in %s"), + "GIMP 2.8")); + version = MAX (3, version); + + /* need version 13 for group layers with masks */ + if (gimp_layer_get_mask (layer)) + { + ADD_REASON (g_strdup_printf (_("Masks on layer groups were " + "added in %s"), "GIMP 2.10")); + version = MAX (13, version); + } + } + } + + g_list_free (layers); + + /* version 6 for new metadata has been dropped since they are + * saved through parasites, which is compatible with older versions. + */ + + /* need version 7 for != 8-bit gamma images */ + if (gimp_image_get_precision (image) != GIMP_PRECISION_U8_GAMMA) + { + ADD_REASON (g_strdup_printf (_("High bit-depth images were added " + "in %s"), "GIMP 2.10")); + version = MAX (7, version); + } + + /* need version 12 for > 8-bit images for proper endian swapping */ + if (gimp_image_get_precision (image) > GIMP_PRECISION_U8_GAMMA) + version = MAX (12, version); + + /* need version 8 for zlib compression */ + if (zlib_compression) + { + ADD_REASON (g_strdup_printf (_("Internal zlib compression was " + "added in %s"), "GIMP 2.10")); + version = MAX (8, version); + } + + /* if version is 10 (lots of new layer modes), go to version 11 with + * 64 bit offsets right away + */ + if (version == 10) + version = 11; + + /* use the image's in-memory size as an upper bound to estimate the + * need for 64 bit file offsets inside the XCF, this is a *very* + * conservative estimate and should never fail + */ + if (gimp_object_get_memsize (GIMP_OBJECT (image), NULL) >= ((gint64) 1 << 32)) + { + ADD_REASON (g_strdup_printf (_("Support for image files larger than " + "4GB was added in %s"), "GIMP 2.10")); + version = MAX (11, version); + } + +#undef ADD_REASON + + switch (version) + { + case 0: + case 1: + case 2: + if (gimp_version) *gimp_version = 206; + if (version_string) *version_string = "GIMP 2.6"; + break; + + case 3: + if (gimp_version) *gimp_version = 208; + if (version_string) *version_string = "GIMP 2.8"; + break; + + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + if (gimp_version) *gimp_version = 210; + if (version_string) *version_string = "GIMP 2.10"; + break; + } + + if (version_reason && reasons) + { + GString *reason = g_string_new (NULL); + + reasons = g_list_sort (reasons, (GCompareFunc) strcmp); + + for (list = reasons; list; list = g_list_next (list)) + { + g_string_append (reason, list->data); + if (g_list_next (list)) + g_string_append_c (reason, '\n'); + } + + *version_reason = g_string_free (reason, FALSE); + } + if (reasons) + g_list_free_full (reasons, g_free); + + return version; +} + +void +gimp_image_set_xcf_compression (GimpImage *image, + gboolean compression) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->xcf_compression = compression; +} + +gboolean +gimp_image_get_xcf_compression (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->xcf_compression; +} + +void +gimp_image_set_resolution (GimpImage *image, + gdouble xresolution, + gdouble yresolution) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* don't allow to set the resolution out of bounds */ + if (xresolution < GIMP_MIN_RESOLUTION || xresolution > GIMP_MAX_RESOLUTION || + yresolution < GIMP_MIN_RESOLUTION || yresolution > GIMP_MAX_RESOLUTION) + return; + + private->resolution_set = TRUE; + + if ((ABS (private->xresolution - xresolution) >= 1e-5) || + (ABS (private->yresolution - yresolution) >= 1e-5)) + { + gimp_image_undo_push_image_resolution (image, + C_("undo-type", "Change Image Resolution")); + + private->xresolution = xresolution; + private->yresolution = yresolution; + + gimp_image_resolution_changed (image); + gimp_image_size_changed_detailed (image, + 0, + 0, + gimp_image_get_width (image), + gimp_image_get_height (image)); + } +} + +void +gimp_image_get_resolution (GimpImage *image, + gdouble *xresolution, + gdouble *yresolution) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (xresolution != NULL && yresolution != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + *xresolution = private->xresolution; + *yresolution = private->yresolution; +} + +void +gimp_image_resolution_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[RESOLUTION_CHANGED], 0); +} + +void +gimp_image_set_unit (GimpImage *image, + GimpUnit unit) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (unit > GIMP_UNIT_PIXEL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->resolution_unit != unit) + { + gimp_image_undo_push_image_resolution (image, + C_("undo-type", "Change Image Unit")); + + private->resolution_unit = unit; + gimp_image_unit_changed (image); + } +} + +GimpUnit +gimp_image_get_unit (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_UNIT_INCH); + + return GIMP_IMAGE_GET_PRIVATE (image)->resolution_unit; +} + +void +gimp_image_unit_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[UNIT_CHANGED], 0); +} + +gint +gimp_image_get_width (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->width; +} + +gint +gimp_image_get_height (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->height; +} + +gboolean +gimp_image_has_alpha (GimpImage *image) +{ + GimpImagePrivate *private; + GimpLayer *layer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), TRUE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + layer = GIMP_LAYER (gimp_container_get_first_child (private->layers->container)); + + return ((gimp_image_get_n_layers (image) > 1) || + (layer && gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))); +} + +gboolean +gimp_image_is_empty (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), TRUE); + + return gimp_container_is_empty (GIMP_IMAGE_GET_PRIVATE (image)->layers->container); +} + +void +gimp_image_set_floating_selection (GimpImage *image, + GimpLayer *floating_sel) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (floating_sel == NULL || GIMP_IS_LAYER (floating_sel)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->floating_sel != floating_sel) + { + private->floating_sel = floating_sel; + + private->flush_accum.floating_selection_changed = TRUE; + } +} + +GimpLayer * +gimp_image_get_floating_selection (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->floating_sel; +} + +void +gimp_image_floating_selection_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[FLOATING_SELECTION_CHANGED], 0); +} + +GimpChannel * +gimp_image_get_mask (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->selection_mask; +} + +void +gimp_image_mask_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[MASK_CHANGED], 0); +} + +void +gimp_image_take_mask (GimpImage *image, + GimpChannel *mask) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SELECTION (mask)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->selection_mask) + g_object_unref (private->selection_mask); + + private->selection_mask = g_object_ref_sink (mask); + + g_signal_connect (private->selection_mask, "update", + G_CALLBACK (gimp_image_mask_update), + image); +} + + +/* image components */ + +const Babl * +gimp_image_get_component_format (GimpImage *image, + GimpChannelType channel) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + switch (channel) + { + case GIMP_CHANNEL_RED: + return gimp_babl_component_format (GIMP_RGB, + gimp_image_get_precision (image), + RED); + + case GIMP_CHANNEL_GREEN: + return gimp_babl_component_format (GIMP_RGB, + gimp_image_get_precision (image), + GREEN); + + case GIMP_CHANNEL_BLUE: + return gimp_babl_component_format (GIMP_RGB, + gimp_image_get_precision (image), + BLUE); + + case GIMP_CHANNEL_ALPHA: + return gimp_babl_component_format (GIMP_RGB, + gimp_image_get_precision (image), + ALPHA); + + case GIMP_CHANNEL_GRAY: + return gimp_babl_component_format (GIMP_GRAY, + gimp_image_get_precision (image), + GRAY); + + case GIMP_CHANNEL_INDEXED: + return babl_format ("Y u8"); /* will extract grayscale, the best + * we can do here */ + } + + return NULL; +} + +gint +gimp_image_get_component_index (GimpImage *image, + GimpChannelType channel) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), -1); + + switch (channel) + { + case GIMP_CHANNEL_RED: return RED; + case GIMP_CHANNEL_GREEN: return GREEN; + case GIMP_CHANNEL_BLUE: return BLUE; + case GIMP_CHANNEL_GRAY: return GRAY; + case GIMP_CHANNEL_INDEXED: return INDEXED; + case GIMP_CHANNEL_ALPHA: + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: return ALPHA; + case GIMP_GRAY: return ALPHA_G; + case GIMP_INDEXED: return ALPHA_I; + } + } + + return -1; +} + +void +gimp_image_set_component_active (GimpImage *image, + GimpChannelType channel, + gboolean active) +{ + GimpImagePrivate *private; + gint index = -1; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + index = gimp_image_get_component_index (image, channel); + + if (index != -1 && active != private->active[index]) + { + private->active[index] = active ? TRUE : FALSE; + + /* If there is an active channel and we mess with the components, + * the active channel gets unset... + */ + gimp_image_unset_active_channel (image); + + g_signal_emit (image, + gimp_image_signals[COMPONENT_ACTIVE_CHANGED], 0, + channel); + } +} + +gboolean +gimp_image_get_component_active (GimpImage *image, + GimpChannelType channel) +{ + gint index = -1; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + index = gimp_image_get_component_index (image, channel); + + if (index != -1) + return GIMP_IMAGE_GET_PRIVATE (image)->active[index]; + + return FALSE; +} + +void +gimp_image_get_active_array (GimpImage *image, + gboolean *components) +{ + GimpImagePrivate *private; + gint i; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (components != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + for (i = 0; i < MAX_CHANNELS; i++) + components[i] = private->active[i]; +} + +GimpComponentMask +gimp_image_get_active_mask (GimpImage *image) +{ + GimpImagePrivate *private; + GimpComponentMask mask = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: + mask |= (private->active[RED]) ? GIMP_COMPONENT_MASK_RED : 0; + mask |= (private->active[GREEN]) ? GIMP_COMPONENT_MASK_GREEN : 0; + mask |= (private->active[BLUE]) ? GIMP_COMPONENT_MASK_BLUE : 0; + mask |= (private->active[ALPHA]) ? GIMP_COMPONENT_MASK_ALPHA : 0; + break; + + case GIMP_GRAY: + case GIMP_INDEXED: + mask |= (private->active[GRAY]) ? GIMP_COMPONENT_MASK_RED : 0; + mask |= (private->active[GRAY]) ? GIMP_COMPONENT_MASK_GREEN : 0; + mask |= (private->active[GRAY]) ? GIMP_COMPONENT_MASK_BLUE : 0; + mask |= (private->active[ALPHA_G]) ? GIMP_COMPONENT_MASK_ALPHA : 0; + break; + } + + return mask; +} + +void +gimp_image_set_component_visible (GimpImage *image, + GimpChannelType channel, + gboolean visible) +{ + GimpImagePrivate *private; + gint index = -1; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + index = gimp_image_get_component_index (image, channel); + + if (index != -1 && visible != private->visible[index]) + { + private->visible[index] = visible ? TRUE : FALSE; + + if (private->visible_mask) + { + GimpComponentMask mask; + + mask = ~gimp_image_get_visible_mask (image) & GIMP_COMPONENT_MASK_ALL; + + gegl_node_set (private->visible_mask, + "mask", mask, + NULL); + } + + g_signal_emit (image, + gimp_image_signals[COMPONENT_VISIBILITY_CHANGED], 0, + channel); + + gimp_image_invalidate_all (image); + } +} + +gboolean +gimp_image_get_component_visible (GimpImage *image, + GimpChannelType channel) +{ + gint index = -1; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + index = gimp_image_get_component_index (image, channel); + + if (index != -1) + return GIMP_IMAGE_GET_PRIVATE (image)->visible[index]; + + return FALSE; +} + +void +gimp_image_get_visible_array (GimpImage *image, + gboolean *components) +{ + GimpImagePrivate *private; + gint i; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (components != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + for (i = 0; i < MAX_CHANNELS; i++) + components[i] = private->visible[i]; +} + +GimpComponentMask +gimp_image_get_visible_mask (GimpImage *image) +{ + GimpImagePrivate *private; + GimpComponentMask mask = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: + mask |= (private->visible[RED]) ? GIMP_COMPONENT_MASK_RED : 0; + mask |= (private->visible[GREEN]) ? GIMP_COMPONENT_MASK_GREEN : 0; + mask |= (private->visible[BLUE]) ? GIMP_COMPONENT_MASK_BLUE : 0; + mask |= (private->visible[ALPHA]) ? GIMP_COMPONENT_MASK_ALPHA : 0; + break; + + case GIMP_GRAY: + case GIMP_INDEXED: + mask |= (private->visible[GRAY]) ? GIMP_COMPONENT_MASK_RED : 0; + mask |= (private->visible[GRAY]) ? GIMP_COMPONENT_MASK_GREEN : 0; + mask |= (private->visible[GRAY]) ? GIMP_COMPONENT_MASK_BLUE : 0; + mask |= (private->visible[ALPHA]) ? GIMP_COMPONENT_MASK_ALPHA : 0; + break; + } + + return mask; +} + + +/* emitting image signals */ + +void +gimp_image_mode_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[MODE_CHANGED], 0); +} + +void +gimp_image_precision_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[PRECISION_CHANGED], 0); +} + +void +gimp_image_alpha_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[ALPHA_CHANGED], 0); +} + +void +gimp_image_linked_items_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[LINKED_ITEMS_CHANGED], 0); +} + +void +gimp_image_invalidate (GimpImage *image, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + gimp_projectable_invalidate (GIMP_PROJECTABLE (image), + x, y, width, height); + + GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.preview_invalidated = TRUE; +} + +void +gimp_image_invalidate_all (GimpImage *image) +{ + const GeglRectangle *bounding_box; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + bounding_box = &GIMP_IMAGE_GET_PRIVATE (image)->bounding_box; + + gimp_image_invalidate (image, + bounding_box->x, bounding_box->y, + bounding_box->width, bounding_box->height); +} + +void +gimp_image_guide_added (GimpImage *image, + GimpGuide *guide) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + g_signal_emit (image, gimp_image_signals[GUIDE_ADDED], 0, + guide); +} + +void +gimp_image_guide_removed (GimpImage *image, + GimpGuide *guide) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + g_signal_emit (image, gimp_image_signals[GUIDE_REMOVED], 0, + guide); +} + +void +gimp_image_guide_moved (GimpImage *image, + GimpGuide *guide) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_GUIDE (guide)); + + g_signal_emit (image, gimp_image_signals[GUIDE_MOVED], 0, + guide); +} + +void +gimp_image_sample_point_added (GimpImage *image, + GimpSamplePoint *sample_point) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_ADDED], 0, + sample_point); +} + +void +gimp_image_sample_point_removed (GimpImage *image, + GimpSamplePoint *sample_point) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_REMOVED], 0, + sample_point); +} + +void +gimp_image_sample_point_moved (GimpImage *image, + GimpSamplePoint *sample_point) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_MOVED], 0, + sample_point); +} + +/** + * gimp_image_size_changed_detailed: + * @image: + * @previous_origin_x: + * @previous_origin_y: + * + * Emits the size-changed-detailed signal that is typically used to adjust the + * position of the image in the display shell on various operations, + * e.g. crop. + * + * This function makes sure that GimpViewable::size-changed is also emitted. + **/ +void +gimp_image_size_changed_detailed (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[SIZE_CHANGED_DETAILED], 0, + previous_origin_x, + previous_origin_y, + previous_width, + previous_height); +} + +void +gimp_image_colormap_changed (GimpImage *image, + gint color_index) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (color_index >= -1 && + color_index < GIMP_IMAGE_GET_PRIVATE (image)->n_colors); + + g_signal_emit (image, gimp_image_signals[COLORMAP_CHANGED], 0, + color_index); +} + +void +gimp_image_selection_invalidate (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[SELECTION_INVALIDATE], 0); +} + +void +gimp_image_quick_mask_changed (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[QUICK_MASK_CHANGED], 0); +} + +void +gimp_image_undo_event (GimpImage *image, + GimpUndoEvent event, + GimpUndo *undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (((event == GIMP_UNDO_EVENT_UNDO_FREE || + event == GIMP_UNDO_EVENT_UNDO_FREEZE || + event == GIMP_UNDO_EVENT_UNDO_THAW) && undo == NULL) || + GIMP_IS_UNDO (undo)); + + g_signal_emit (image, gimp_image_signals[UNDO_EVENT], 0, event, undo); +} + + +/* dirty counters */ + +/* NOTE about the image->dirty counter: + * If 0, then the image is clean (ie, copy on disk is the same as the one + * in memory). + * If positive, then that's the number of dirtying operations done + * on the image since the last save. + * If negative, then user has hit undo and gone back in time prior + * to the saved copy. Hitting redo will eventually come back to + * the saved copy. + * + * The image is dirty (ie, needs saving) if counter is non-zero. + * + * If the counter is around 100000, this is due to undo-ing back + * before a saved version, then changing the image (thus destroying + * the redo stack). Once this has happened, it's impossible to get + * the image back to the state on disk, since the redo info has been + * freed. See gimpimage-undo.c for the gory details. + */ + +/* + * NEVER CALL gimp_image_dirty() directly! + * + * If your code has just dirtied the image, push an undo instead. + * Failing that, push the trivial undo which tells the user the + * command is not undoable: undo_push_cantundo() (But really, it would + * be best to push a proper undo). If you just dirty the image + * without pushing an undo then the dirty count is increased, but + * popping that many undo actions won't lead to a clean image. + */ + +gint +gimp_image_dirty (GimpImage *image, + GimpDirtyMask dirty_mask) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->dirty++; + private->export_dirty++; + + if (! private->dirty_time) + private->dirty_time = time (NULL); + + g_signal_emit (image, gimp_image_signals[DIRTY], 0, dirty_mask); + + TRC (("dirty %d -> %d\n", private->dirty - 1, private->dirty)); + + return private->dirty; +} + +gint +gimp_image_clean (GimpImage *image, + GimpDirtyMask dirty_mask) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->dirty--; + private->export_dirty--; + + g_signal_emit (image, gimp_image_signals[CLEAN], 0, dirty_mask); + + TRC (("clean %d -> %d\n", private->dirty + 1, private->dirty)); + + return private->dirty; +} + +void +gimp_image_clean_all (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->dirty = 0; + private->dirty_time = 0; + + g_signal_emit (image, gimp_image_signals[CLEAN], 0, GIMP_DIRTY_ALL); + + gimp_object_name_changed (GIMP_OBJECT (image)); +} + +void +gimp_image_export_clean_all (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->export_dirty = 0; + + g_signal_emit (image, gimp_image_signals[CLEAN], 0, GIMP_DIRTY_ALL); + + gimp_object_name_changed (GIMP_OBJECT (image)); +} + +/** + * gimp_image_is_dirty: + * @image: + * + * Returns: True if the image is dirty, false otherwise. + **/ +gint +gimp_image_is_dirty (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->dirty != 0; +} + +/** + * gimp_image_is_export_dirty: + * @image: + * + * Returns: True if the image export is dirty, false otherwise. + **/ +gboolean +gimp_image_is_export_dirty (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->export_dirty != 0; +} + +gint64 +gimp_image_get_dirty_time (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->dirty_time; +} + +/** + * gimp_image_saving: + * @image: + * + * Emits the "saving" signal, indicating that @image is about to be saved, + * or exported. + */ +void +gimp_image_saving (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_emit (image, gimp_image_signals[SAVING], 0); +} + +/** + * gimp_image_saved: + * @image: + * @file: + * + * Emits the "saved" signal, indicating that @image was saved to the + * location specified by @file. + */ +void +gimp_image_saved (GimpImage *image, + GFile *file) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (G_IS_FILE (file)); + + g_signal_emit (image, gimp_image_signals[SAVED], 0, file); +} + +/** + * gimp_image_exported: + * @image: + * @file: + * + * Emits the "exported" signal, indicating that @image was exported to the + * location specified by @file. + */ +void +gimp_image_exported (GimpImage *image, + GFile *file) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (G_IS_FILE (file)); + + g_signal_emit (image, gimp_image_signals[EXPORTED], 0, file); +} + + +/* flush this image's displays */ + +void +gimp_image_flush (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + gimp_projectable_flush (GIMP_PROJECTABLE (image), + GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.preview_invalidated); +} + + +/* display / instance counters */ + +gint +gimp_image_get_display_count (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->disp_count; +} + +void +gimp_image_inc_display_count (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->disp_count++; +} + +void +gimp_image_dec_display_count (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->disp_count--; +} + +gint +gimp_image_get_instance_count (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->instance_count; +} + +void +gimp_image_inc_instance_count (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->instance_count++; +} + +void +gimp_image_inc_show_all_count (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->show_all++; + + if (GIMP_IMAGE_GET_PRIVATE (image)->show_all == 1) + { + g_clear_object (&GIMP_IMAGE_GET_PRIVATE (image)->pickable_buffer); + + gimp_image_update_bounding_box (image); + } +} + +void +gimp_image_dec_show_all_count (GimpImage *image) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + GIMP_IMAGE_GET_PRIVATE (image)->show_all--; + + if (GIMP_IMAGE_GET_PRIVATE (image)->show_all == 0) + { + g_clear_object (&GIMP_IMAGE_GET_PRIVATE (image)->pickable_buffer); + + gimp_image_update_bounding_box (image); + } +} + + +/* parasites */ + +const GimpParasite * +gimp_image_parasite_find (GimpImage *image, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_parasite_list_find (GIMP_IMAGE_GET_PRIVATE (image)->parasites, + name); +} + +static void +list_func (gchar *key, + GimpParasite *p, + gchar ***cur) +{ + *(*cur)++ = (gchar *) g_strdup (key); +} + +gchar ** +gimp_image_parasite_list (GimpImage *image, + gint *count) +{ + GimpImagePrivate *private; + gchar **list; + gchar **cur; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + *count = gimp_parasite_list_length (private->parasites); + cur = list = g_new (gchar *, *count); + + gimp_parasite_list_foreach (private->parasites, (GHFunc) list_func, &cur); + + return list; +} + +gboolean +gimp_image_parasite_validate (GimpImage *image, + const GimpParasite *parasite, + GError **error) +{ + const gchar *name; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (parasite != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + name = gimp_parasite_name (parasite); + + if (strcmp (name, GIMP_ICC_PROFILE_PARASITE_NAME) == 0) + { + return gimp_image_validate_icc_parasite (image, parasite, NULL, error); + } + else if (strcmp (name, "gimp-comment") == 0) + { + const gchar *data = gimp_parasite_data (parasite); + gssize length = gimp_parasite_data_size (parasite); + gboolean valid = FALSE; + + if (length > 0) + { + if (data[length - 1] == '\0') + valid = g_utf8_validate (data, -1, NULL); + else + valid = g_utf8_validate (data, length, NULL); + } + + if (! valid) + { + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("'gimp-comment' parasite validation failed: " + "comment contains invalid UTF-8")); + return FALSE; + } + } + + return TRUE; +} + +void +gimp_image_parasite_attach (GimpImage *image, + const GimpParasite *parasite, + gboolean push_undo) +{ + GimpImagePrivate *private; + GimpParasite copy; + const gchar *name; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (parasite != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + name = gimp_parasite_name (parasite); + + /* this is so ugly and is only for the PDB */ + if (strcmp (name, GIMP_ICC_PROFILE_PARASITE_NAME) == 0) + { + GimpColorProfile *profile; + GimpColorProfile *builtin; + + profile = + gimp_color_profile_new_from_icc_profile (gimp_parasite_data (parasite), + gimp_parasite_data_size (parasite), + NULL); + builtin = gimp_image_get_builtin_color_profile (image); + + if (gimp_color_profile_is_equal (profile, builtin)) + { + /* setting the builtin profile is equal to removing the profile */ + gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME, + push_undo); + g_object_unref (profile); + return; + } + + g_object_unref (profile); + } + + /* make a temporary copy of the GimpParasite struct because + * gimp_parasite_shift_parent() changes it + */ + copy = *parasite; + + /* only set the dirty bit manually if we can be saved and the new + * parasite differs from the current one and we aren't undoable + */ + if (push_undo && gimp_parasite_is_undoable (©)) + gimp_image_undo_push_image_parasite (image, + C_("undo-type", "Attach Parasite to Image"), + ©); + + /* We used to push a cantundo on the stack here. This made the undo stack + * unusable (NULL on the stack) and prevented people from undoing after a + * save (since most save plug-ins attach an undoable comment parasite). + * Now we simply attach the parasite without pushing an undo. That way + * it's undoable but does not block the undo system. --Sven + */ + gimp_parasite_list_add (private->parasites, ©); + + if (push_undo && gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT)) + { + gimp_parasite_shift_parent (©); + gimp_parasite_attach (image->gimp, ©); + } + + if (strcmp (name, GIMP_ICC_PROFILE_PARASITE_NAME) == 0) + _gimp_image_update_color_profile (image, parasite); + + g_signal_emit (image, gimp_image_signals[PARASITE_ATTACHED], 0, + name); +} + +void +gimp_image_parasite_detach (GimpImage *image, + const gchar *name, + gboolean push_undo) +{ + GimpImagePrivate *private; + const GimpParasite *parasite; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (name != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (! (parasite = gimp_parasite_list_find (private->parasites, name))) + return; + + if (push_undo && gimp_parasite_is_undoable (parasite)) + gimp_image_undo_push_image_parasite_remove (image, + C_("undo-type", "Remove Parasite from Image"), + name); + + gimp_parasite_list_remove (private->parasites, name); + + if (strcmp (name, GIMP_ICC_PROFILE_PARASITE_NAME) == 0) + _gimp_image_update_color_profile (image, NULL); + + g_signal_emit (image, gimp_image_signals[PARASITE_DETACHED], 0, + name); +} + + +/* tattoos */ + +GimpTattoo +gimp_image_get_new_tattoo (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + private->tattoo_state++; + + if (G_UNLIKELY (private->tattoo_state == 0)) + g_warning ("%s: Tattoo state corrupted (integer overflow).", G_STRFUNC); + + return private->tattoo_state; +} + +GimpTattoo +gimp_image_get_tattoo_state (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + return GIMP_IMAGE_GET_PRIVATE (image)->tattoo_state; +} + +gboolean +gimp_image_set_tattoo_state (GimpImage *image, + GimpTattoo val) +{ + GList *all_items; + GList *list; + gboolean retval = TRUE; + GimpTattoo maxval = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + /* Check that the layer tattoos don't overlap with channel or vector ones */ + all_items = gimp_image_get_layer_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpTattoo ltattoo; + + ltattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data)); + if (ltattoo > maxval) + maxval = ltattoo; + + if (gimp_image_get_channel_by_tattoo (image, ltattoo)) + retval = FALSE; /* Oopps duplicated tattoo in channel */ + + if (gimp_image_get_vectors_by_tattoo (image, ltattoo)) + retval = FALSE; /* Oopps duplicated tattoo in vectors */ + } + + g_list_free (all_items); + + /* Now check that the channel and vectors tattoos don't overlap */ + all_items = gimp_image_get_channel_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpTattoo ctattoo; + + ctattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data)); + if (ctattoo > maxval) + maxval = ctattoo; + + if (gimp_image_get_vectors_by_tattoo (image, ctattoo)) + retval = FALSE; /* Oopps duplicated tattoo in vectors */ + } + + g_list_free (all_items); + + /* Find the max tattoo value in the vectors */ + all_items = gimp_image_get_vectors_list (image); + + for (list = all_items; list; list = g_list_next (list)) + { + GimpTattoo vtattoo; + + vtattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data)); + if (vtattoo > maxval) + maxval = vtattoo; + } + + g_list_free (all_items); + + if (val < maxval) + retval = FALSE; + + /* Must check if the state is valid */ + if (retval == TRUE) + GIMP_IMAGE_GET_PRIVATE (image)->tattoo_state = val; + + return retval; +} + + +/* projection */ + +GimpProjection * +gimp_image_get_projection (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->projection; +} + + +/* layers / channels / vectors */ + +GimpItemTree * +gimp_image_get_layer_tree (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->layers; +} + +GimpItemTree * +gimp_image_get_channel_tree (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->channels; +} + +GimpItemTree * +gimp_image_get_vectors_tree (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->vectors; +} + +GimpContainer * +gimp_image_get_layers (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->layers->container; +} + +GimpContainer * +gimp_image_get_channels (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->channels->container; +} + +GimpContainer * +gimp_image_get_vectors (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->vectors->container; +} + +gint +gimp_image_get_n_layers (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + stack = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + + return gimp_item_stack_get_n_items (stack); +} + +gint +gimp_image_get_n_channels (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + stack = GIMP_ITEM_STACK (gimp_image_get_channels (image)); + + return gimp_item_stack_get_n_items (stack); +} + +gint +gimp_image_get_n_vectors (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), 0); + + stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image)); + + return gimp_item_stack_get_n_items (stack); +} + +GList * +gimp_image_get_layer_iter (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + + return gimp_item_stack_get_item_iter (stack); +} + +GList * +gimp_image_get_channel_iter (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_channels (image)); + + return gimp_item_stack_get_item_iter (stack); +} + +GList * +gimp_image_get_vectors_iter (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image)); + + return gimp_item_stack_get_item_iter (stack); +} + +GList * +gimp_image_get_layer_list (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + + return gimp_item_stack_get_item_list (stack); +} + +GList * +gimp_image_get_channel_list (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_channels (image)); + + return gimp_item_stack_get_item_list (stack); +} + +GList * +gimp_image_get_vectors_list (GimpImage *image) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image)); + + return gimp_item_stack_get_item_list (stack); +} + + +/* active drawable, layer, channel, vectors */ + +GimpDrawable * +gimp_image_get_active_drawable (GimpImage *image) +{ + GimpImagePrivate *private; + GimpItem *active_channel; + GimpItem *active_layer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + active_channel = gimp_item_tree_get_active_item (private->channels); + active_layer = gimp_item_tree_get_active_item (private->layers); + + /* If there is an active channel (a saved selection, etc.), + * we ignore the active layer + */ + if (active_channel) + { + return GIMP_DRAWABLE (active_channel); + } + else if (active_layer) + { + GimpLayer *layer = GIMP_LAYER (active_layer); + GimpLayerMask *mask = gimp_layer_get_mask (layer); + + if (mask && gimp_layer_get_edit_mask (layer)) + return GIMP_DRAWABLE (mask); + else + return GIMP_DRAWABLE (layer); + } + + return NULL; +} + +GimpLayer * +gimp_image_get_active_layer (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return GIMP_LAYER (gimp_item_tree_get_active_item (private->layers)); +} + +GimpChannel * +gimp_image_get_active_channel (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return GIMP_CHANNEL (gimp_item_tree_get_active_item (private->channels)); +} + +GimpVectors * +gimp_image_get_active_vectors (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return GIMP_VECTORS (gimp_item_tree_get_active_item (private->vectors)); +} + +GimpLayer * +gimp_image_set_active_layer (GimpImage *image, + GimpLayer *layer) +{ + GimpImagePrivate *private; + GimpLayer *floating_sel; + GimpLayer *active_layer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (layer == NULL || + (gimp_item_is_attached (GIMP_ITEM (layer)) && + gimp_item_get_image (GIMP_ITEM (layer)) == image), + NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + floating_sel = gimp_image_get_floating_selection (image); + + /* Make sure the floating_sel always is the active layer */ + if (floating_sel && layer != floating_sel) + return floating_sel; + + active_layer = gimp_image_get_active_layer (image); + + if (layer != active_layer) + { + /* Don't cache selection info for the previous active layer */ + if (active_layer) + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (active_layer)); + + gimp_item_tree_set_active_item (private->layers, GIMP_ITEM (layer)); + } + + return gimp_image_get_active_layer (image); +} + +GimpChannel * +gimp_image_set_active_channel (GimpImage *image, + GimpChannel *channel) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (channel == NULL || GIMP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (channel == NULL || + (gimp_item_is_attached (GIMP_ITEM (channel)) && + gimp_item_get_image (GIMP_ITEM (channel)) == image), + NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* Not if there is a floating selection */ + if (channel && gimp_image_get_floating_selection (image)) + return NULL; + + if (channel != gimp_image_get_active_channel (image)) + { + gimp_item_tree_set_active_item (private->channels, GIMP_ITEM (channel)); + } + + return gimp_image_get_active_channel (image); +} + +GimpChannel * +gimp_image_unset_active_channel (GimpImage *image) +{ + GimpImagePrivate *private; + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + channel = gimp_image_get_active_channel (image); + + if (channel) + { + gimp_image_set_active_channel (image, NULL); + + if (private->layer_stack) + gimp_image_set_active_layer (image, private->layer_stack->data); + } + + return channel; +} + +GimpVectors * +gimp_image_set_active_vectors (GimpImage *image, + GimpVectors *vectors) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors), NULL); + g_return_val_if_fail (vectors == NULL || + (gimp_item_is_attached (GIMP_ITEM (vectors)) && + gimp_item_get_image (GIMP_ITEM (vectors)) == image), + NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (vectors != gimp_image_get_active_vectors (image)) + { + gimp_item_tree_set_active_item (private->vectors, GIMP_ITEM (vectors)); + } + + return gimp_image_get_active_vectors (image); +} + + +/* layer, channel, vectors by tattoo */ + +GimpLayer * +gimp_image_get_layer_by_tattoo (GimpImage *image, + GimpTattoo tattoo) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + + return GIMP_LAYER (gimp_item_stack_get_item_by_tattoo (stack, tattoo)); +} + +GimpChannel * +gimp_image_get_channel_by_tattoo (GimpImage *image, + GimpTattoo tattoo) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_channels (image)); + + return GIMP_CHANNEL (gimp_item_stack_get_item_by_tattoo (stack, tattoo)); +} + +GimpVectors * +gimp_image_get_vectors_by_tattoo (GimpImage *image, + GimpTattoo tattoo) +{ + GimpItemStack *stack; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image)); + + return GIMP_VECTORS (gimp_item_stack_get_item_by_tattoo (stack, tattoo)); +} + + +/* layer, channel, vectors by name */ + +GimpLayer * +gimp_image_get_layer_by_name (GimpImage *image, + const gchar *name) +{ + GimpItemTree *tree; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + + tree = gimp_image_get_layer_tree (image); + + return GIMP_LAYER (gimp_item_tree_get_item_by_name (tree, name)); +} + +GimpChannel * +gimp_image_get_channel_by_name (GimpImage *image, + const gchar *name) +{ + GimpItemTree *tree; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + + tree = gimp_image_get_channel_tree (image); + + return GIMP_CHANNEL (gimp_item_tree_get_item_by_name (tree, name)); +} + +GimpVectors * +gimp_image_get_vectors_by_name (GimpImage *image, + const gchar *name) +{ + GimpItemTree *tree; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (name != NULL, NULL); + + tree = gimp_image_get_vectors_tree (image); + + return GIMP_VECTORS (gimp_item_tree_get_item_by_name (tree, name)); +} + + +/* items */ + +gboolean +gimp_image_reorder_item (GimpImage *image, + GimpItem *item, + GimpItem *new_parent, + gint new_index, + gboolean push_undo, + const gchar *undo_desc) +{ + GimpItemTree *tree; + gboolean result; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_get_image (item) == image, FALSE); + + tree = gimp_item_get_tree (item); + + g_return_val_if_fail (tree != NULL, FALSE); + + if (push_undo) + { + if (! undo_desc) + undo_desc = GIMP_ITEM_GET_CLASS (item)->reorder_desc; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REORDER, + undo_desc); + } + + gimp_image_freeze_bounding_box (image); + + gimp_item_start_move (item, push_undo); + + /* item and new_parent are type-checked in GimpItemTree + */ + result = gimp_item_tree_reorder_item (tree, item, + new_parent, new_index, + push_undo, undo_desc); + + gimp_item_end_move (item, push_undo); + + gimp_image_thaw_bounding_box (image); + + if (push_undo) + gimp_image_undo_group_end (image); + + return result; +} + +gboolean +gimp_image_raise_item (GimpImage *image, + GimpItem *item, + GError **error) +{ + gint index; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + index = gimp_item_get_index (item); + + g_return_val_if_fail (index != -1, FALSE); + + if (index == 0) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + GIMP_ITEM_GET_CLASS (item)->raise_failed); + return FALSE; + } + + return gimp_image_reorder_item (image, item, + gimp_item_get_parent (item), index - 1, + TRUE, GIMP_ITEM_GET_CLASS (item)->raise_desc); +} + +gboolean +gimp_image_raise_item_to_top (GimpImage *image, + GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return gimp_image_reorder_item (image, item, + gimp_item_get_parent (item), 0, + TRUE, GIMP_ITEM_GET_CLASS (item)->raise_to_top_desc); +} + +gboolean +gimp_image_lower_item (GimpImage *image, + GimpItem *item, + GError **error) +{ + GimpContainer *container; + gint index; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + container = gimp_item_get_container (item); + + g_return_val_if_fail (container != NULL, FALSE); + + index = gimp_item_get_index (item); + + if (index == gimp_container_get_n_children (container) - 1) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + GIMP_ITEM_GET_CLASS (item)->lower_failed); + return FALSE; + } + + return gimp_image_reorder_item (image, item, + gimp_item_get_parent (item), index + 1, + TRUE, GIMP_ITEM_GET_CLASS (item)->lower_desc); +} + +gboolean +gimp_image_lower_item_to_bottom (GimpImage *image, + GimpItem *item) +{ + GimpContainer *container; + gint length; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + container = gimp_item_get_container (item); + + g_return_val_if_fail (container != NULL, FALSE); + + length = gimp_container_get_n_children (container); + + return gimp_image_reorder_item (image, item, + gimp_item_get_parent (item), length - 1, + TRUE, GIMP_ITEM_GET_CLASS (item)->lower_to_bottom_desc); +} + + +/* layers */ + +gboolean +gimp_image_add_layer (GimpImage *image, + GimpLayer *layer, + GimpLayer *parent, + gint position, + gboolean push_undo) +{ + GimpImagePrivate *private; + gboolean old_has_alpha; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* item and parent are type-checked in GimpItemTree + */ + if (! gimp_item_tree_get_insert_pos (private->layers, + (GimpItem *) layer, + (GimpItem **) &parent, + &position)) + return FALSE; + + gimp_image_unset_default_new_layer_mode (image); + + /* If there is a floating selection (and this isn't it!), + * make sure the insert position is greater than 0 + */ + if (parent == NULL && position == 0 && + gimp_image_get_floating_selection (image)) + position = 1; + + old_has_alpha = gimp_image_has_alpha (image); + + if (push_undo) + gimp_image_undo_push_layer_add (image, C_("undo-type", "Add Layer"), + layer, + gimp_image_get_active_layer (image)); + + gimp_item_tree_add_item (private->layers, GIMP_ITEM (layer), + GIMP_ITEM (parent), position); + + gimp_image_set_active_layer (image, layer); + + /* If the layer is a floating selection, attach it to the drawable */ + if (gimp_layer_is_floating_sel (layer)) + gimp_drawable_attach_floating_sel (gimp_layer_get_floating_sel_drawable (layer), + layer); + + if (old_has_alpha != gimp_image_has_alpha (image)) + private->flush_accum.alpha_changed = TRUE; + + return TRUE; +} + +void +gimp_image_remove_layer (GimpImage *image, + GimpLayer *layer, + gboolean push_undo, + GimpLayer *new_active) +{ + GimpImagePrivate *private; + GimpLayer *active_layer; + gboolean old_has_alpha; + const gchar *undo_desc; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer))); + g_return_if_fail (gimp_item_get_image (GIMP_ITEM (layer)) == image); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_unset_default_new_layer_mode (image); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, + C_("undo-type", "Remove Layer")); + + gimp_item_start_move (GIMP_ITEM (layer), push_undo); + + if (gimp_drawable_get_floating_sel (GIMP_DRAWABLE (layer))) + { + if (! push_undo) + { + g_warning ("%s() was called from an undo function while the layer " + "had a floating selection. Please report this at " + "https://www.gimp.org/bugs/", G_STRFUNC); + return; + } + + gimp_image_remove_layer (image, + gimp_drawable_get_floating_sel (GIMP_DRAWABLE (layer)), + TRUE, NULL); + } + + active_layer = gimp_image_get_active_layer (image); + + old_has_alpha = gimp_image_has_alpha (image); + + if (gimp_layer_is_floating_sel (layer)) + { + undo_desc = C_("undo-type", "Remove Floating Selection"); + + gimp_drawable_detach_floating_sel (gimp_layer_get_floating_sel_drawable (layer)); + } + else + { + undo_desc = C_("undo-type", "Remove Layer"); + } + + if (push_undo) + gimp_image_undo_push_layer_remove (image, undo_desc, layer, + gimp_layer_get_parent (layer), + gimp_item_get_index (GIMP_ITEM (layer)), + active_layer); + + g_object_ref (layer); + + /* Make sure we're not caching any old selection info */ + if (layer == active_layer) + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); + + private->layer_stack = g_slist_remove (private->layer_stack, layer); + + /* Also remove all children of a group layer from the layer_stack */ + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + { + GimpContainer *stack = gimp_viewable_get_children (GIMP_VIEWABLE (layer)); + GList *children; + GList *list; + + children = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (stack)); + + for (list = children; list; list = g_list_next (list)) + { + private->layer_stack = g_slist_remove (private->layer_stack, + list->data); + } + + g_list_free (children); + } + + new_active = + GIMP_LAYER (gimp_item_tree_remove_item (private->layers, + GIMP_ITEM (layer), + GIMP_ITEM (new_active))); + + if (gimp_layer_is_floating_sel (layer)) + { + /* If this was the floating selection, activate the underlying drawable + */ + floating_sel_activate_drawable (layer); + } + else if (active_layer && + (layer == active_layer || + gimp_viewable_is_ancestor (GIMP_VIEWABLE (layer), + GIMP_VIEWABLE (active_layer)))) + { + gimp_image_set_active_layer (image, new_active); + } + + gimp_item_end_move (GIMP_ITEM (layer), push_undo); + + g_object_unref (layer); + + if (old_has_alpha != gimp_image_has_alpha (image)) + private->flush_accum.alpha_changed = TRUE; + + if (push_undo) + gimp_image_undo_group_end (image); +} + +void +gimp_image_add_layers (GimpImage *image, + GList *layers, + GimpLayer *parent, + gint position, + gint x, + gint y, + gint width, + gint height, + const gchar *undo_desc) +{ + GimpImagePrivate *private; + GList *list; + gint layers_x = G_MAXINT; + gint layers_y = G_MAXINT; + gint layers_width = 0; + gint layers_height = 0; + gint offset_x; + gint offset_y; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (layers != NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* item and parent are type-checked in GimpItemTree + */ + if (! gimp_item_tree_get_insert_pos (private->layers, + (GimpItem *) layers->data, + (GimpItem **) &parent, + &position)) + return; + + for (list = layers; list; list = g_list_next (list)) + { + GimpItem *item = GIMP_ITEM (list->data); + gint off_x, off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + layers_x = MIN (layers_x, off_x); + layers_y = MIN (layers_y, off_y); + + layers_width = MAX (layers_width, + off_x + gimp_item_get_width (item) - layers_x); + layers_height = MAX (layers_height, + off_y + gimp_item_get_height (item) - layers_y); + } + + offset_x = x + (width - layers_width) / 2 - layers_x; + offset_y = y + (height - layers_height) / 2 - layers_y; + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, undo_desc); + + for (list = layers; list; list = g_list_next (list)) + { + GimpItem *new_item = GIMP_ITEM (list->data); + + gimp_item_translate (new_item, offset_x, offset_y, FALSE); + + gimp_image_add_layer (image, GIMP_LAYER (new_item), + parent, position, TRUE); + position++; + } + + if (layers) + gimp_image_set_active_layer (image, layers->data); + + gimp_image_undo_group_end (image); +} + + +/* channels */ + +gboolean +gimp_image_add_channel (GimpImage *image, + GimpChannel *channel, + GimpChannel *parent, + gint position, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* item and parent are type-checked in GimpItemTree + */ + if (! gimp_item_tree_get_insert_pos (private->channels, + (GimpItem *) channel, + (GimpItem **) &parent, + &position)) + return FALSE; + + if (push_undo) + gimp_image_undo_push_channel_add (image, C_("undo-type", "Add Channel"), + channel, + gimp_image_get_active_channel (image)); + + gimp_item_tree_add_item (private->channels, GIMP_ITEM (channel), + GIMP_ITEM (parent), position); + + gimp_image_set_active_channel (image, channel); + + return TRUE; +} + +void +gimp_image_remove_channel (GimpImage *image, + GimpChannel *channel, + gboolean push_undo, + GimpChannel *new_active) +{ + GimpImagePrivate *private; + GimpChannel *active_channel; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CHANNEL (channel)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); + g_return_if_fail (gimp_item_get_image (GIMP_ITEM (channel)) == image); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, + C_("undo-type", "Remove Channel")); + + gimp_item_start_move (GIMP_ITEM (channel), push_undo); + + if (gimp_drawable_get_floating_sel (GIMP_DRAWABLE (channel))) + { + if (! push_undo) + { + g_warning ("%s() was called from an undo function while the channel " + "had a floating selection. Please report this at " + "https://www.gimp.org/bugs/", G_STRFUNC); + return; + } + + gimp_image_remove_layer (image, + gimp_drawable_get_floating_sel (GIMP_DRAWABLE (channel)), + TRUE, NULL); + } + + private = GIMP_IMAGE_GET_PRIVATE (image); + + active_channel = gimp_image_get_active_channel (image); + + if (push_undo) + gimp_image_undo_push_channel_remove (image, C_("undo-type", "Remove Channel"), channel, + gimp_channel_get_parent (channel), + gimp_item_get_index (GIMP_ITEM (channel)), + active_channel); + + g_object_ref (channel); + + new_active = + GIMP_CHANNEL (gimp_item_tree_remove_item (private->channels, + GIMP_ITEM (channel), + GIMP_ITEM (new_active))); + + if (active_channel && + (channel == active_channel || + gimp_viewable_is_ancestor (GIMP_VIEWABLE (channel), + GIMP_VIEWABLE (active_channel)))) + { + if (new_active) + gimp_image_set_active_channel (image, new_active); + else + gimp_image_unset_active_channel (image); + } + + gimp_item_end_move (GIMP_ITEM (channel), push_undo); + + g_object_unref (channel); + + if (push_undo) + gimp_image_undo_group_end (image); +} + + +/* vectors */ + +gboolean +gimp_image_add_vectors (GimpImage *image, + GimpVectors *vectors, + GimpVectors *parent, + gint position, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + /* item and parent are type-checked in GimpItemTree + */ + if (! gimp_item_tree_get_insert_pos (private->vectors, + (GimpItem *) vectors, + (GimpItem **) &parent, + &position)) + return FALSE; + + if (push_undo) + gimp_image_undo_push_vectors_add (image, C_("undo-type", "Add Path"), + vectors, + gimp_image_get_active_vectors (image)); + + gimp_item_tree_add_item (private->vectors, GIMP_ITEM (vectors), + GIMP_ITEM (parent), position); + + gimp_image_set_active_vectors (image, vectors); + + return TRUE; +} + +void +gimp_image_remove_vectors (GimpImage *image, + GimpVectors *vectors, + gboolean push_undo, + GimpVectors *new_active) +{ + GimpImagePrivate *private; + GimpVectors *active_vectors; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_VECTORS (vectors)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors))); + g_return_if_fail (gimp_item_get_image (GIMP_ITEM (vectors)) == image); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, + C_("undo-type", "Remove Path")); + + gimp_item_start_move (GIMP_ITEM (vectors), push_undo); + + active_vectors = gimp_image_get_active_vectors (image); + + if (push_undo) + gimp_image_undo_push_vectors_remove (image, C_("undo-type", "Remove Path"), vectors, + gimp_vectors_get_parent (vectors), + gimp_item_get_index (GIMP_ITEM (vectors)), + active_vectors); + + g_object_ref (vectors); + + new_active = + GIMP_VECTORS (gimp_item_tree_remove_item (private->vectors, + GIMP_ITEM (vectors), + GIMP_ITEM (new_active))); + + if (active_vectors && + (vectors == active_vectors || + gimp_viewable_is_ancestor (GIMP_VIEWABLE (vectors), + GIMP_VIEWABLE (active_vectors)))) + { + gimp_image_set_active_vectors (image, new_active); + } + + gimp_item_end_move (GIMP_ITEM (vectors), push_undo); + + g_object_unref (vectors); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +gboolean +gimp_image_coords_in_active_pickable (GimpImage *image, + const GimpCoords *coords, + gboolean show_all, + gboolean sample_merged, + gboolean selected_only) +{ + gint x, y; + gboolean in_pickable = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + x = floor (coords->x); + y = floor (coords->y); + + if (sample_merged) + { + if (show_all || (x >= 0 && x < gimp_image_get_width (image) && + y >= 0 && y < gimp_image_get_height (image))) + { + in_pickable = TRUE; + } + } + else + { + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + GimpItem *item = GIMP_ITEM (drawable); + gint off_x, off_y; + gint d_x, d_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + d_x = x - off_x; + d_y = y - off_y; + + if (d_x >= 0 && d_x < gimp_item_get_width (item) && + d_y >= 0 && d_y < gimp_item_get_height (item)) + in_pickable = TRUE; + } + } + + if (in_pickable && selected_only) + { + GimpChannel *selection = gimp_image_get_mask (image); + + if (! gimp_channel_is_empty (selection) && + ! gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection), + x, y)) + { + in_pickable = FALSE; + } + } + + return in_pickable; +} + +void +gimp_image_invalidate_previews (GimpImage *image) +{ + GimpItemStack *layers; + GimpItemStack *channels; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + layers = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + channels = GIMP_ITEM_STACK (gimp_image_get_channels (image)); + + gimp_item_stack_invalidate_previews (layers); + gimp_item_stack_invalidate_previews (channels); +} + +/* Sets the image into a "converting" state, which is there to warn other code + * (such as shell render code) that the image properties might be in an + * inconsistent state. For instance when converting to another precision with + * gimp_image_convert_precision(), the babl format may be updated first, and the + * profile later, after all drawables are converted. Rendering the image + * in-between would at best render broken previews (at worst, crash, e.g. + * because we depend on allocated data which might have become too small). + */ +void +gimp_image_set_converting (GimpImage *image, + gboolean converting) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_object_set (image, + "converting", converting, + NULL); +} + +gboolean +gimp_image_get_converting (GimpImage *image) +{ + GimpImagePrivate *private; + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + return private->converting; +} diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h new file mode 100644 index 0000000..50654ab --- /dev/null +++ b/app/core/gimpimage.h @@ -0,0 +1,463 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGE_H__ +#define __GIMP_IMAGE_H__ + + +#include "gimpviewable.h" + + +#define GIMP_IMAGE_ACTIVE_PARENT ((gpointer) 1) + + +#define GIMP_TYPE_IMAGE (gimp_image_get_type ()) +#define GIMP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGE, GimpImage)) +#define GIMP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_IMAGE, GimpImageClass)) +#define GIMP_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGE)) +#define GIMP_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_IMAGE)) +#define GIMP_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_IMAGE, GimpImageClass)) + + +typedef struct _GimpImageClass GimpImageClass; +typedef struct _GimpImagePrivate GimpImagePrivate; + +struct _GimpImage +{ + GimpViewable parent_instance; + + Gimp *gimp; /* the GIMP the image belongs to */ + + GimpImagePrivate *priv; +}; + +struct _GimpImageClass +{ + GimpViewableClass parent_class; + + /* signals */ + void (* mode_changed) (GimpImage *image); + void (* precision_changed) (GimpImage *image); + void (* alpha_changed) (GimpImage *image); + void (* floating_selection_changed) (GimpImage *image); + void (* active_layer_changed) (GimpImage *image); + void (* active_channel_changed) (GimpImage *image); + void (* active_vectors_changed) (GimpImage *image); + void (* linked_items_changed) (GimpImage *image); + void (* component_visibility_changed) (GimpImage *image, + GimpChannelType channel); + void (* component_active_changed) (GimpImage *image, + GimpChannelType channel); + void (* mask_changed) (GimpImage *image); + void (* resolution_changed) (GimpImage *image); + void (* size_changed_detailed) (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height); + void (* unit_changed) (GimpImage *image); + void (* quick_mask_changed) (GimpImage *image); + void (* selection_invalidate) (GimpImage *image); + + void (* clean) (GimpImage *image, + GimpDirtyMask dirty_mask); + void (* dirty) (GimpImage *image, + GimpDirtyMask dirty_mask); + void (* saving) (GimpImage *image); + void (* saved) (GimpImage *image, + GFile *file); + void (* exported) (GimpImage *image, + GFile *file); + + void (* guide_added) (GimpImage *image, + GimpGuide *guide); + void (* guide_removed) (GimpImage *image, + GimpGuide *guide); + void (* guide_moved) (GimpImage *image, + GimpGuide *guide); + void (* sample_point_added) (GimpImage *image, + GimpSamplePoint *sample_point); + void (* sample_point_removed) (GimpImage *image, + GimpSamplePoint *sample_point); + void (* sample_point_moved) (GimpImage *image, + GimpSamplePoint *sample_point); + void (* parasite_attached) (GimpImage *image, + const gchar *name); + void (* parasite_detached) (GimpImage *image, + const gchar *name); + void (* colormap_changed) (GimpImage *image, + gint color_index); + void (* undo_event) (GimpImage *image, + GimpUndoEvent event, + GimpUndo *undo); +}; + + +GType gimp_image_get_type (void) G_GNUC_CONST; + +GimpImage * gimp_image_new (Gimp *gimp, + gint width, + gint height, + GimpImageBaseType base_type, + GimpPrecision precision); + +gint64 gimp_image_estimate_memsize (GimpImage *image, + GimpComponentType component_type, + gint width, + gint height); + +GimpImageBaseType gimp_image_get_base_type (GimpImage *image); +GimpComponentType gimp_image_get_component_type (GimpImage *image); +GimpPrecision gimp_image_get_precision (GimpImage *image); + +const Babl * gimp_image_get_format (GimpImage *image, + GimpImageBaseType base_type, + GimpPrecision precision, + gboolean with_alpha); +const Babl * gimp_image_get_layer_format (GimpImage *image, + gboolean with_alpha); +const Babl * gimp_image_get_channel_format (GimpImage *image); +const Babl * gimp_image_get_mask_format (GimpImage *image); + +GimpLayerMode gimp_image_get_default_new_layer_mode + (GimpImage *image); +void gimp_image_unset_default_new_layer_mode + (GimpImage *image); + +gint gimp_image_get_ID (GimpImage *image); +GimpImage * gimp_image_get_by_ID (Gimp *gimp, + gint id); + +GFile * gimp_image_get_file (GimpImage *image); +GFile * gimp_image_get_untitled_file (GimpImage *image); +GFile * gimp_image_get_file_or_untitled (GimpImage *image); +GFile * gimp_image_get_imported_file (GimpImage *image); +GFile * gimp_image_get_exported_file (GimpImage *image); +GFile * gimp_image_get_save_a_copy_file (GimpImage *image); +GFile * gimp_image_get_any_file (GimpImage *image); + +void gimp_image_set_file (GimpImage *image, + GFile *file); +void gimp_image_set_imported_file (GimpImage *image, + GFile *file); +void gimp_image_set_exported_file (GimpImage *image, + GFile *file); +void gimp_image_set_save_a_copy_file (GimpImage *image, + GFile *file); + +const gchar * gimp_image_get_display_name (GimpImage *image); +const gchar * gimp_image_get_display_path (GimpImage *image); + +void gimp_image_set_load_proc (GimpImage *image, + GimpPlugInProcedure *proc); +GimpPlugInProcedure * gimp_image_get_load_proc (GimpImage *image); +void gimp_image_set_save_proc (GimpImage *image, + GimpPlugInProcedure *proc); +GimpPlugInProcedure * gimp_image_get_save_proc (GimpImage *image); +void gimp_image_saving (GimpImage *image); +void gimp_image_saved (GimpImage *image, + GFile *file); +void gimp_image_set_export_proc (GimpImage *image, + GimpPlugInProcedure *proc); +GimpPlugInProcedure * gimp_image_get_export_proc (GimpImage *image); +void gimp_image_exported (GimpImage *image, + GFile *file); + +gint gimp_image_get_xcf_version (GimpImage *image, + gboolean zlib_compression, + gint *gimp_version, + const gchar **version_string, + gchar **version_reason); + +void gimp_image_set_xcf_compression (GimpImage *image, + gboolean compression); +gboolean gimp_image_get_xcf_compression (GimpImage *image); + +void gimp_image_set_resolution (GimpImage *image, + gdouble xres, + gdouble yres); +void gimp_image_get_resolution (GimpImage *image, + gdouble *xres, + gdouble *yres); +void gimp_image_resolution_changed (GimpImage *image); + +void gimp_image_set_unit (GimpImage *image, + GimpUnit unit); +GimpUnit gimp_image_get_unit (GimpImage *image); +void gimp_image_unit_changed (GimpImage *image); + +gint gimp_image_get_width (GimpImage *image); +gint gimp_image_get_height (GimpImage *image); + +gboolean gimp_image_has_alpha (GimpImage *image); +gboolean gimp_image_is_empty (GimpImage *image); + +void gimp_image_set_floating_selection (GimpImage *image, + GimpLayer *floating_sel); +GimpLayer * gimp_image_get_floating_selection (GimpImage *image); +void gimp_image_floating_selection_changed (GimpImage *image); + +GimpChannel * gimp_image_get_mask (GimpImage *image); +void gimp_image_mask_changed (GimpImage *image); + + +/* image components */ + +const Babl * gimp_image_get_component_format (GimpImage *image, + GimpChannelType channel); +gint gimp_image_get_component_index (GimpImage *image, + GimpChannelType channel); + +void gimp_image_set_component_active (GimpImage *image, + GimpChannelType type, + gboolean active); +gboolean gimp_image_get_component_active (GimpImage *image, + GimpChannelType type); +void gimp_image_get_active_array (GimpImage *image, + gboolean *components); +GimpComponentMask gimp_image_get_active_mask (GimpImage *image); + +void gimp_image_set_component_visible (GimpImage *image, + GimpChannelType type, + gboolean visible); +gboolean gimp_image_get_component_visible (GimpImage *image, + GimpChannelType type); +void gimp_image_get_visible_array (GimpImage *image, + gboolean *components); +GimpComponentMask gimp_image_get_visible_mask (GimpImage *image); + + +/* emitting image signals */ + +void gimp_image_mode_changed (GimpImage *image); +void gimp_image_precision_changed (GimpImage *image); +void gimp_image_alpha_changed (GimpImage *image); +void gimp_image_linked_items_changed (GimpImage *image); +void gimp_image_invalidate (GimpImage *image, + gint x, + gint y, + gint width, + gint height); +void gimp_image_invalidate_all (GimpImage *image); +void gimp_image_guide_added (GimpImage *image, + GimpGuide *guide); +void gimp_image_guide_removed (GimpImage *image, + GimpGuide *guide); +void gimp_image_guide_moved (GimpImage *image, + GimpGuide *guide); + +void gimp_image_sample_point_added (GimpImage *image, + GimpSamplePoint *sample_point); +void gimp_image_sample_point_removed (GimpImage *image, + GimpSamplePoint *sample_point); +void gimp_image_sample_point_moved (GimpImage *image, + GimpSamplePoint *sample_point); +void gimp_image_colormap_changed (GimpImage *image, + gint col); +void gimp_image_selection_invalidate (GimpImage *image); +void gimp_image_quick_mask_changed (GimpImage *image); +void gimp_image_size_changed_detailed (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height); +void gimp_image_undo_event (GimpImage *image, + GimpUndoEvent event, + GimpUndo *undo); + + +/* dirty counters */ + +gint gimp_image_dirty (GimpImage *image, + GimpDirtyMask dirty_mask); +gint gimp_image_clean (GimpImage *image, + GimpDirtyMask dirty_mask); +void gimp_image_clean_all (GimpImage *image); +void gimp_image_export_clean_all (GimpImage *image); +gint gimp_image_is_dirty (GimpImage *image); +gboolean gimp_image_is_export_dirty (GimpImage *image); +gint64 gimp_image_get_dirty_time (GimpImage *image); + + +/* flush this image's displays */ + +void gimp_image_flush (GimpImage *image); + + +/* display / instance counters */ + +gint gimp_image_get_display_count (GimpImage *image); +void gimp_image_inc_display_count (GimpImage *image); +void gimp_image_dec_display_count (GimpImage *image); + +gint gimp_image_get_instance_count (GimpImage *image); +void gimp_image_inc_instance_count (GimpImage *image); + +void gimp_image_inc_show_all_count (GimpImage *image); +void gimp_image_dec_show_all_count (GimpImage *image); + + +/* parasites */ + +const GimpParasite * gimp_image_parasite_find (GimpImage *image, + const gchar *name); +gchar ** gimp_image_parasite_list (GimpImage *image, + gint *count); +gboolean gimp_image_parasite_validate (GimpImage *image, + const GimpParasite *parasite, + GError **error); +void gimp_image_parasite_attach (GimpImage *image, + const GimpParasite *parasite, + gboolean push_undo); +void gimp_image_parasite_detach (GimpImage *image, + const gchar *name, + gboolean push_undo); + + +/* tattoos */ + +GimpTattoo gimp_image_get_new_tattoo (GimpImage *image); +gboolean gimp_image_set_tattoo_state (GimpImage *image, + GimpTattoo val); +GimpTattoo gimp_image_get_tattoo_state (GimpImage *image); + + +/* projection */ + +GimpProjection * gimp_image_get_projection (GimpImage *image); + + +/* layers / channels / vectors */ + +GimpItemTree * gimp_image_get_layer_tree (GimpImage *image); +GimpItemTree * gimp_image_get_channel_tree (GimpImage *image); +GimpItemTree * gimp_image_get_vectors_tree (GimpImage *image); + +GimpContainer * gimp_image_get_layers (GimpImage *image); +GimpContainer * gimp_image_get_channels (GimpImage *image); +GimpContainer * gimp_image_get_vectors (GimpImage *image); + +gint gimp_image_get_n_layers (GimpImage *image); +gint gimp_image_get_n_channels (GimpImage *image); +gint gimp_image_get_n_vectors (GimpImage *image); + +GList * gimp_image_get_layer_iter (GimpImage *image); +GList * gimp_image_get_channel_iter (GimpImage *image); +GList * gimp_image_get_vectors_iter (GimpImage *image); + +GList * gimp_image_get_layer_list (GimpImage *image); +GList * gimp_image_get_channel_list (GimpImage *image); +GList * gimp_image_get_vectors_list (GimpImage *image); + +GimpDrawable * gimp_image_get_active_drawable (GimpImage *image); +GimpLayer * gimp_image_get_active_layer (GimpImage *image); +GimpChannel * gimp_image_get_active_channel (GimpImage *image); +GimpVectors * gimp_image_get_active_vectors (GimpImage *image); + +GimpLayer * gimp_image_set_active_layer (GimpImage *image, + GimpLayer *layer); +GimpChannel * gimp_image_set_active_channel (GimpImage *image, + GimpChannel *channel); +GimpChannel * gimp_image_unset_active_channel (GimpImage *image); +GimpVectors * gimp_image_set_active_vectors (GimpImage *image, + GimpVectors *vectors); + +GimpLayer * gimp_image_get_layer_by_tattoo (GimpImage *image, + GimpTattoo tattoo); +GimpChannel * gimp_image_get_channel_by_tattoo (GimpImage *image, + GimpTattoo tattoo); +GimpVectors * gimp_image_get_vectors_by_tattoo (GimpImage *image, + GimpTattoo tattoo); + +GimpLayer * gimp_image_get_layer_by_name (GimpImage *image, + const gchar *name); +GimpChannel * gimp_image_get_channel_by_name (GimpImage *image, + const gchar *name); +GimpVectors * gimp_image_get_vectors_by_name (GimpImage *image, + const gchar *name); + +gboolean gimp_image_reorder_item (GimpImage *image, + GimpItem *item, + GimpItem *new_parent, + gint new_index, + gboolean push_undo, + const gchar *undo_desc); +gboolean gimp_image_raise_item (GimpImage *image, + GimpItem *item, + GError **error); +gboolean gimp_image_raise_item_to_top (GimpImage *image, + GimpItem *item); +gboolean gimp_image_lower_item (GimpImage *image, + GimpItem *item, + GError **error); +gboolean gimp_image_lower_item_to_bottom (GimpImage *image, + GimpItem *item); + +gboolean gimp_image_add_layer (GimpImage *image, + GimpLayer *layer, + GimpLayer *parent, + gint position, + gboolean push_undo); +void gimp_image_remove_layer (GimpImage *image, + GimpLayer *layer, + gboolean push_undo, + GimpLayer *new_active); + +void gimp_image_add_layers (GimpImage *image, + GList *layers, + GimpLayer *parent, + gint position, + gint x, + gint y, + gint width, + gint height, + const gchar *undo_desc); + +gboolean gimp_image_add_channel (GimpImage *image, + GimpChannel *channel, + GimpChannel *parent, + gint position, + gboolean push_undo); +void gimp_image_remove_channel (GimpImage *image, + GimpChannel *channel, + gboolean push_undo, + GimpChannel *new_active); + +gboolean gimp_image_add_vectors (GimpImage *image, + GimpVectors *vectors, + GimpVectors *parent, + gint position, + gboolean push_undo); +void gimp_image_remove_vectors (GimpImage *image, + GimpVectors *vectors, + gboolean push_undo, + GimpVectors *new_active); + +gboolean gimp_image_coords_in_active_pickable (GimpImage *image, + const GimpCoords *coords, + gboolean show_all, + gboolean sample_merged, + gboolean selected_only); + +void gimp_image_invalidate_previews (GimpImage *image); + +void gimp_image_set_converting (GimpImage *image, + gboolean converting); +gboolean gimp_image_get_converting (GimpImage *image); + + +#endif /* __GIMP_IMAGE_H__ */ diff --git a/app/core/gimpimagefile.c b/app/core/gimpimagefile.c new file mode 100644 index 0000000..cf079d8 --- /dev/null +++ b/app/core/gimpimagefile.c @@ -0,0 +1,1078 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimagefile.c + * + * Copyright (C) 2001-2004 Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpthumb/gimpthumb.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpimage.h" +#include "gimpimagefile.h" +#include "gimpmarshal.h" +#include "gimppickable.h" +#include "gimpprogress.h" + +#include "file/file-open.h" + +#include "gimp-intl.h" + + +enum +{ + INFO_CHANGED, + LAST_SIGNAL +}; + + +typedef struct _GimpImagefilePrivate GimpImagefilePrivate; + +struct _GimpImagefilePrivate +{ + Gimp *gimp; + + GFile *file; + GimpThumbnail *thumbnail; + GIcon *icon; + GCancellable *icon_cancellable; + + gchar *description; + gboolean static_desc; +}; + +#define GET_PRIVATE(imagefile) ((GimpImagefilePrivate *) gimp_imagefile_get_instance_private ((GimpImagefile *) (imagefile))) + + +static void gimp_imagefile_dispose (GObject *object); +static void gimp_imagefile_finalize (GObject *object); + +static void gimp_imagefile_name_changed (GimpObject *object); + +static GdkPixbuf * gimp_imagefile_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_imagefile_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_imagefile_info_changed (GimpImagefile *imagefile); +static void gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile, + GParamSpec *pspec); + +static void gimp_imagefile_icon_callback (GObject *source_object, + GAsyncResult *result, + gpointer data); + +static GdkPixbuf * gimp_imagefile_load_thumb (GimpImagefile *imagefile, + gint width, + gint height); +static gboolean gimp_imagefile_save_thumb (GimpImagefile *imagefile, + GimpImage *image, + gint size, + gboolean replace, + GError **error); + +static void gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail, + const gchar *mime_type, + GimpImage *image); +static void gimp_thumbnail_set_info (GimpThumbnail *thumbnail, + const gchar *mime_type, + gint width, + gint height, + const Babl *format, + gint num_layers); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpImagefile, gimp_imagefile, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_imagefile_parent_class + +static guint gimp_imagefile_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_imagefile_class_init (GimpImagefileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + gchar *creator; + + gimp_imagefile_signals[INFO_CHANGED] = + g_signal_new ("info-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpImagefileClass, info_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = gimp_imagefile_dispose; + object_class->finalize = gimp_imagefile_finalize; + + gimp_object_class->name_changed = gimp_imagefile_name_changed; + + viewable_class->name_changed_signal = "info-changed"; + viewable_class->get_new_pixbuf = gimp_imagefile_get_new_pixbuf; + viewable_class->get_description = gimp_imagefile_get_description; + + g_type_class_ref (GIMP_TYPE_IMAGE_TYPE); + + creator = g_strdup_printf ("gimp-%d.%d", + GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION); + + gimp_thumb_init (creator, NULL); + + g_free (creator); +} + +static void +gimp_imagefile_init (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private = GET_PRIVATE (imagefile); + + private->thumbnail = gimp_thumbnail_new (); + + g_signal_connect_object (private->thumbnail, "notify", + G_CALLBACK (gimp_imagefile_notify_thumbnail), + imagefile, G_CONNECT_SWAPPED); +} + +static void +gimp_imagefile_dispose (GObject *object) +{ + GimpImagefilePrivate *private = GET_PRIVATE (object); + + if (private->icon_cancellable) + { + g_cancellable_cancel (private->icon_cancellable); + g_clear_object (&private->icon_cancellable); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_imagefile_finalize (GObject *object) +{ + GimpImagefilePrivate *private = GET_PRIVATE (object); + + if (private->description) + { + if (! private->static_desc) + g_free (private->description); + + private->description = NULL; + } + + g_clear_object (&private->thumbnail); + g_clear_object (&private->icon); + g_clear_object (&private->file); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_imagefile_name_changed (GimpObject *object) +{ + GimpImagefilePrivate *private = GET_PRIVATE (object); + + if (GIMP_OBJECT_CLASS (parent_class)->name_changed) + GIMP_OBJECT_CLASS (parent_class)->name_changed (object); + + gimp_thumbnail_set_uri (private->thumbnail, gimp_object_get_name (object)); + + g_clear_object (&private->file); + + if (gimp_object_get_name (object)) + private->file = g_file_new_for_uri (gimp_object_get_name (object)); +} + +static GdkPixbuf * +gimp_imagefile_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable); + + if (! gimp_object_get_name (imagefile)) + return NULL; + + return gimp_imagefile_load_thumb (imagefile, width, height); +} + +static gchar * +gimp_imagefile_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable); + GimpImagefilePrivate *private = GET_PRIVATE (imagefile); + GimpThumbnail *thumbnail = private->thumbnail; + gchar *basename; + + if (! private->file) + return NULL; + + if (tooltip) + { + const gchar *name; + const gchar *desc; + + name = gimp_file_get_utf8_name (private->file); + desc = gimp_imagefile_get_desc_string (imagefile); + + if (desc) + *tooltip = g_strdup_printf ("%s\n%s", name, desc); + else + *tooltip = g_strdup (name); + } + + basename = g_path_get_basename (gimp_file_get_utf8_name (private->file)); + + if (thumbnail->image_width > 0 && thumbnail->image_height > 0) + { + gchar *tmp = basename; + + basename = g_strdup_printf ("%s (%d × %d)", + tmp, + thumbnail->image_width, + thumbnail->image_height); + g_free (tmp); + } + + return basename; +} + + +/* public functions */ + +GimpImagefile * +gimp_imagefile_new (Gimp *gimp, + GFile *file) +{ + GimpImagefile *imagefile; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL); + + imagefile = g_object_new (GIMP_TYPE_IMAGEFILE, NULL); + + GET_PRIVATE (imagefile)->gimp = gimp; + + if (file) + { + gimp_object_take_name (GIMP_OBJECT (imagefile), g_file_get_uri (file)); + + /* file member gets created by gimp_imagefile_name_changed() */ + } + + return imagefile; +} + +GFile * +gimp_imagefile_get_file (GimpImagefile *imagefile) +{ + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL); + + return GET_PRIVATE (imagefile)->file; +} + +void +gimp_imagefile_set_file (GimpImagefile *imagefile, + GFile *file) +{ + g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + if (GET_PRIVATE (imagefile)->file != file) + { + gimp_object_take_name (GIMP_OBJECT (imagefile), + file ? g_file_get_uri (file) : NULL); + } +} + +GimpThumbnail * +gimp_imagefile_get_thumbnail (GimpImagefile *imagefile) +{ + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL); + + return GET_PRIVATE (imagefile)->thumbnail; +} + +GIcon * +gimp_imagefile_get_gicon (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL); + + private = GET_PRIVATE (imagefile); + + if (private->icon) + return private->icon; + + if (private->file && ! private->icon_cancellable) + { + private->icon_cancellable = g_cancellable_new (); + + g_file_query_info_async (private->file, "standard::icon", + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + private->icon_cancellable, + gimp_imagefile_icon_callback, + imagefile); + } + + return NULL; +} + +void +gimp_imagefile_set_mime_type (GimpImagefile *imagefile, + const gchar *mime_type) +{ + g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile)); + + g_object_set (GET_PRIVATE (imagefile)->thumbnail, + "image-mimetype", mime_type, + NULL); +} + +void +gimp_imagefile_update (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private; + gchar *uri; + + g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile)); + + private = GET_PRIVATE (imagefile); + + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile)); + + g_object_get (private->thumbnail, + "image-uri", &uri, + NULL); + + if (uri) + { + GimpImagefile *documents_imagefile = (GimpImagefile *) + gimp_container_get_child_by_name (private->gimp->documents, uri); + + if (documents_imagefile != imagefile && + GIMP_IS_IMAGEFILE (documents_imagefile)) + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (documents_imagefile)); + + g_free (uri); + } +} + +gboolean +gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, + GimpContext *context, + GimpProgress *progress, + gint size, + gboolean replace, + GError **error) +{ + GimpImagefilePrivate *private; + GimpThumbnail *thumbnail; + GimpThumbState image_state; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* thumbnailing is disabled, we successfully did nothing */ + if (size < 1) + return TRUE; + + private = GET_PRIVATE (imagefile); + + thumbnail = private->thumbnail; + + gimp_thumbnail_set_uri (thumbnail, + gimp_object_get_name (imagefile)); + + image_state = gimp_thumbnail_peek_image (thumbnail); + + if (image_state == GIMP_THUMB_STATE_REMOTE || + image_state >= GIMP_THUMB_STATE_EXISTS) + { + GimpImage *image; + gboolean success; + gint width = 0; + gint height = 0; + const gchar *mime_type = NULL; + const Babl *format = NULL; + gint num_layers = -1; + + /* we only want to attempt thumbnailing on readable, regular files */ + if (g_file_is_native (private->file)) + { + GFileInfo *file_info; + gboolean regular; + gboolean readable; + + file_info = g_file_query_info (private->file, + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_READ, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + regular = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR); + readable = g_file_info_get_attribute_boolean (file_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ); + + g_object_unref (file_info); + + if (! (regular && readable)) + return TRUE; + } + + g_object_ref (imagefile); + + /* don't pass the error, we're only interested in errors from + * actual thumbnail saving + */ + image = file_open_thumbnail (private->gimp, context, progress, + private->file, size, + &mime_type, &width, &height, + &format, &num_layers, NULL); + + if (image) + { + gimp_thumbnail_set_info (private->thumbnail, + mime_type, width, height, + format, num_layers); + } + else + { + GimpPDBStatusType status; + + /* don't pass the error, we're only interested in errors + * from actual thumbnail saving + */ + image = file_open_image (private->gimp, context, progress, + private->file, + private->file, + FALSE, NULL, GIMP_RUN_NONINTERACTIVE, + &status, &mime_type, NULL); + + if (image) + gimp_thumbnail_set_info_from_image (private->thumbnail, + mime_type, image); + } + + if (image) + { + success = gimp_imagefile_save_thumb (imagefile, + image, size, replace, + error); + + g_object_unref (image); + } + else + { + success = gimp_thumbnail_save_failure (thumbnail, + "GIMP " GIMP_VERSION, + error); + gimp_imagefile_update (imagefile); + } + + g_object_unref (imagefile); + + if (! success) + { + g_object_set (thumbnail, + "thumb-state", GIMP_THUMB_STATE_FAILED, + NULL); + } + + return success; + } + + return TRUE; +} + +/* The weak version doesn't ref the imagefile but deals gracefully + * with an imagefile that is destroyed while the thumbnail is + * created. This allows one to use this function w/o the need to + * block the user interface. + */ +void +gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, + GimpContext *context, + GimpProgress *progress, + gint size, + gboolean replace) +{ + GimpImagefilePrivate *private; + GimpImagefile *local; + + g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile)); + + if (size < 1) + return; + + private = GET_PRIVATE (imagefile); + + if (! private->file) + return; + + local = gimp_imagefile_new (private->gimp, private->file); + + g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile); + + if (! gimp_imagefile_create_thumbnail (local, context, progress, size, replace, + NULL)) + { + /* The weak version works on a local copy so the thumbnail + * status of the actual image is not properly updated in case of + * creation failure, thus it would end up in a generic + * GIMP_THUMB_STATE_NOT_FOUND, which is less informative. + */ + g_object_set (private->thumbnail, + "thumb-state", GIMP_THUMB_STATE_FAILED, + NULL); + } + + if (imagefile) + { + GFile *file = gimp_imagefile_get_file (imagefile); + + if (file && g_file_equal (file, gimp_imagefile_get_file (local))) + { + gimp_imagefile_update (imagefile); + } + + g_object_remove_weak_pointer (G_OBJECT (imagefile), + (gpointer) &imagefile); + } + + g_object_unref (local); +} + +gboolean +gimp_imagefile_check_thumbnail (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private; + gint size; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE); + + private = GET_PRIVATE (imagefile); + + size = private->gimp->config->thumbnail_size; + + if (size > 0) + { + GimpThumbState state; + + state = gimp_thumbnail_check_thumb (private->thumbnail, size); + + return (state == GIMP_THUMB_STATE_OK); + } + + return TRUE; +} + +gboolean +gimp_imagefile_save_thumbnail (GimpImagefile *imagefile, + const gchar *mime_type, + GimpImage *image, + GError **error) +{ + GimpImagefilePrivate *private; + gint size; + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE); + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + private = GET_PRIVATE (imagefile); + + size = private->gimp->config->thumbnail_size; + + if (size > 0) + { + gimp_thumbnail_set_info_from_image (private->thumbnail, + mime_type, image); + + success = gimp_imagefile_save_thumb (imagefile, + image, size, FALSE, + error); + } + + return success; +} + + +/* private functions */ + +static void +gimp_imagefile_info_changed (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private = GET_PRIVATE (imagefile); + + if (private->description) + { + if (! private->static_desc) + g_free (private->description); + + private->description = NULL; + } + + g_clear_object (&private->icon); + + g_signal_emit (imagefile, gimp_imagefile_signals[INFO_CHANGED], 0); +} + +static void +gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile, + GParamSpec *pspec) +{ + if (strcmp (pspec->name, "image-state") == 0 || + strcmp (pspec->name, "thumb-state") == 0) + { + gimp_imagefile_info_changed (imagefile); + } +} + +static void +gimp_imagefile_icon_callback (GObject *source_object, + GAsyncResult *result, + gpointer data) +{ + GimpImagefile *imagefile; + GimpImagefilePrivate *private; + GFile *file = G_FILE (source_object); + GError *error = NULL; + GFileInfo *file_info; + + file_info = g_file_query_info_finish (file, result, &error); + + if (error) + { + /* we were cancelled from dispose() and the imagefile is + * long gone, bail out + */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_clear_error (&error); + return; + } + +#ifdef GIMP_UNSTABLE + g_printerr ("%s: %s\n", G_STRFUNC, error->message); +#endif + + g_clear_error (&error); + } + + imagefile = GIMP_IMAGEFILE (data); + private = GET_PRIVATE (imagefile); + + if (file_info) + { + private->icon = g_object_ref (g_file_info_get_icon (file_info)); + g_object_unref (file_info); + } + + g_clear_object (&private->icon_cancellable); + + if (private->icon) + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile)); +} + +const gchar * +gimp_imagefile_get_desc_string (GimpImagefile *imagefile) +{ + GimpImagefilePrivate *private; + GimpThumbnail *thumbnail; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL); + + private = GET_PRIVATE (imagefile); + + if (private->description) + return (const gchar *) private->description; + + thumbnail = private->thumbnail; + + switch (thumbnail->image_state) + { + case GIMP_THUMB_STATE_UNKNOWN: + private->description = NULL; + private->static_desc = TRUE; + break; + + case GIMP_THUMB_STATE_FOLDER: + private->description = (gchar *) _("Folder"); + private->static_desc = TRUE; + break; + + case GIMP_THUMB_STATE_SPECIAL: + private->description = (gchar *) _("Special File"); + private->static_desc = TRUE; + break; + + case GIMP_THUMB_STATE_NOT_FOUND: + private->description = + (gchar *) g_strerror (thumbnail->image_not_found_errno); + private->static_desc = TRUE; + break; + + default: + { + GString *str = g_string_new (NULL); + + if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE) + { + g_string_append (str, _("Remote File")); + } + + if (thumbnail->image_filesize > 0) + { + gchar *size = g_format_size (thumbnail->image_filesize); + + if (str->len > 0) + g_string_append_c (str, '\n'); + + g_string_append (str, size); + g_free (size); + } + + switch (thumbnail->thumb_state) + { + case GIMP_THUMB_STATE_NOT_FOUND: + if (str->len > 0) + g_string_append_c (str, '\n'); + g_string_append (str, _("Click to create preview")); + break; + + case GIMP_THUMB_STATE_EXISTS: + if (str->len > 0) + g_string_append_c (str, '\n'); + g_string_append (str, _("Loading preview...")); + break; + + case GIMP_THUMB_STATE_OLD: + if (str->len > 0) + g_string_append_c (str, '\n'); + g_string_append (str, _("Preview is out of date")); + break; + + case GIMP_THUMB_STATE_FAILED: + if (str->len > 0) + g_string_append_c (str, '\n'); + g_string_append (str, _("Cannot create preview")); + break; + + case GIMP_THUMB_STATE_OK: + { + if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE) + { + if (str->len > 0) + g_string_append_c (str, '\n'); + + g_string_append (str, _("(Preview may be out of date)")); + } + + if (thumbnail->image_width > 0 && thumbnail->image_height > 0) + { + if (str->len > 0) + g_string_append_c (str, '\n'); + + g_string_append_printf (str, + ngettext ("%d × %d pixel", + "%d × %d pixels", + thumbnail->image_height), + thumbnail->image_width, + thumbnail->image_height); + } + + if (thumbnail->image_type) + { + if (str->len > 0) + g_string_append_c (str, '\n'); + + g_string_append (str, gettext (thumbnail->image_type)); + } + + if (thumbnail->image_num_layers > 0) + { + if (thumbnail->image_type) + g_string_append_len (str, ", ", 2); + else if (str->len > 0) + g_string_append_c (str, '\n'); + + g_string_append_printf (str, + ngettext ("%d layer", + "%d layers", + thumbnail->image_num_layers), + thumbnail->image_num_layers); + } + } + break; + + default: + break; + } + + private->description = g_string_free (str, FALSE); + private->static_desc = FALSE; + } + } + + return (const gchar *) private->description; +} + +static GdkPixbuf * +gimp_imagefile_load_thumb (GimpImagefile *imagefile, + gint width, + gint height) +{ + GimpImagefilePrivate *private = GET_PRIVATE (imagefile); + GimpThumbnail *thumbnail = private->thumbnail; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + gint size = MAX (width, height); + gint pixbuf_width; + gint pixbuf_height; + gint preview_width; + gint preview_height; + + if (gimp_thumbnail_peek_thumb (thumbnail, size) < GIMP_THUMB_STATE_EXISTS) + return NULL; + + if (thumbnail->image_state == GIMP_THUMB_STATE_NOT_FOUND) + return NULL; + + pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, &error); + + if (! pixbuf) + { + if (error) + { + gimp_message (private->gimp, NULL, GIMP_MESSAGE_ERROR, + _("Could not open thumbnail '%s': %s"), + thumbnail->thumb_filename, error->message); + g_clear_error (&error); + } + + return NULL; + } + + pixbuf_width = gdk_pixbuf_get_width (pixbuf); + pixbuf_height = gdk_pixbuf_get_height (pixbuf); + + gimp_viewable_calc_preview_size (pixbuf_width, + pixbuf_height, + width, + height, + TRUE, 1.0, 1.0, + &preview_width, + &preview_height, + NULL); + + if (preview_width < pixbuf_width || preview_height < pixbuf_height) + { + GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, + preview_width, + preview_height, + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + pixbuf = scaled; + + pixbuf_width = preview_width; + pixbuf_height = preview_height; + } + + if (gdk_pixbuf_get_n_channels (pixbuf) != 3) + { + GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + pixbuf_width, pixbuf_height); + + gdk_pixbuf_composite_color (pixbuf, tmp, + 0, 0, pixbuf_width, pixbuf_height, + 0.0, 0.0, 1.0, 1.0, + GDK_INTERP_NEAREST, 255, + 0, 0, GIMP_CHECK_SIZE_SM, + 0x66666666, 0x99999999); + + g_object_unref (pixbuf); + pixbuf = tmp; + } + + return pixbuf; +} + +static gboolean +gimp_imagefile_save_thumb (GimpImagefile *imagefile, + GimpImage *image, + gint size, + gboolean replace, + GError **error) +{ + GimpImagefilePrivate *private = GET_PRIVATE (imagefile); + GimpThumbnail *thumbnail = private->thumbnail; + GdkPixbuf *pixbuf; + gint width, height; + gboolean success = FALSE; + + if (size < 1) + return TRUE; + + if (gimp_image_get_width (image) <= size && + gimp_image_get_height (image) <= size) + { + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + + size = MAX (width, height); + } + else + { + if (gimp_image_get_width (image) < gimp_image_get_height (image)) + { + height = size; + width = MAX (1, (size * gimp_image_get_width (image) / + gimp_image_get_height (image))); + } + else + { + width = size; + height = MAX (1, (size * gimp_image_get_height (image) / + gimp_image_get_width (image))); + } + } + + /* we need the projection constructed NOW, not some time later */ + gimp_pickable_flush (GIMP_PICKABLE (image)); + + pixbuf = gimp_viewable_get_new_pixbuf (GIMP_VIEWABLE (image), + /* random context, unused */ + gimp_get_user_context (image->gimp), + width, height); + + /* when layer previews are disabled, we won't get a pixbuf */ + if (! pixbuf) + return TRUE; + + success = gimp_thumbnail_save_thumb (thumbnail, + pixbuf, + "GIMP " GIMP_VERSION, + error); + + g_object_unref (pixbuf); + + if (success) + { + if (replace) + gimp_thumbnail_delete_others (thumbnail, size); + else + gimp_thumbnail_delete_failure (thumbnail); + + gimp_imagefile_update (imagefile); + } + + return success; +} + +static void +gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail, + const gchar *mime_type, + GimpImage *image) +{ + const Babl *format; + + /* peek the thumbnail to make sure that mtime and filesize are set */ + gimp_thumbnail_peek_image (thumbnail); + + format = gimp_image_get_layer_format (image, + gimp_image_has_alpha (image)); + + g_object_set (thumbnail, + "image-mimetype", mime_type, + "image-width", gimp_image_get_width (image), + "image-height", gimp_image_get_height (image), + "image-type", gimp_babl_format_get_description (format), + "image-num-layers", gimp_image_get_n_layers (image), + NULL); +} + +/** + * gimp_thumbnail_set_info: + * @thumbnail: #GimpThumbnail object + * @mime_type: MIME type of the image associated with this thumbnail + * @width: width of the image associated with this thumbnail + * @height: height of the image associated with this thumbnail + * @format: format of the image (or NULL if the type is not known) + * @num_layers: number of layers in the image + * (or -1 if the number of layers is not known) + * + * Set information about the image associated with the @thumbnail object. + */ +static void +gimp_thumbnail_set_info (GimpThumbnail *thumbnail, + const gchar *mime_type, + gint width, + gint height, + const Babl *format, + gint num_layers) +{ + /* peek the thumbnail to make sure that mtime and filesize are set */ + gimp_thumbnail_peek_image (thumbnail); + + g_object_set (thumbnail, + "image-mimetype", mime_type, + "image-width", width, + "image-height", height, + NULL); + + if (format) + g_object_set (thumbnail, + "image-type", gimp_babl_format_get_description (format), + NULL); + + if (num_layers != -1) + g_object_set (thumbnail, + "image-num-layers", num_layers, + NULL); +} diff --git a/app/core/gimpimagefile.h b/app/core/gimpimagefile.h new file mode 100644 index 0000000..9ea29b0 --- /dev/null +++ b/app/core/gimpimagefile.h @@ -0,0 +1,90 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimagefile.h + * + * Thumbnail handling according to the Thumbnail Managing Standard. + * https://specifications.freedesktop.org/thumbnail-spec/ + * + * Copyright (C) 2001-2002 Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_IMAGEFILE_H__ +#define __GIMP_IMAGEFILE_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_IMAGEFILE (gimp_imagefile_get_type ()) +#define GIMP_IMAGEFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGEFILE, GimpImagefile)) +#define GIMP_IMAGEFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_IMAGEFILE, GimpImagefileClass)) +#define GIMP_IS_IMAGEFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGEFILE)) +#define GIMP_IS_IMAGEFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_IMAGEFILE)) +#define GIMP_IMAGEFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_IMAGEFILE, GimpImagefileClass)) + + +typedef struct _GimpImagefileClass GimpImagefileClass; + +struct _GimpImagefile +{ + GimpViewable parent_instance; +}; + +struct _GimpImagefileClass +{ + GimpViewableClass parent_class; + + void (* info_changed) (GimpImagefile *imagefile); +}; + + +GType gimp_imagefile_get_type (void) G_GNUC_CONST; + +GimpImagefile * gimp_imagefile_new (Gimp *gimp, + GFile *file); + +GFile * gimp_imagefile_get_file (GimpImagefile *imagefile); +void gimp_imagefile_set_file (GimpImagefile *imagefile, + GFile *file); + +GimpThumbnail * gimp_imagefile_get_thumbnail (GimpImagefile *imagefile); +GIcon * gimp_imagefile_get_gicon (GimpImagefile *imagefile); + +void gimp_imagefile_set_mime_type (GimpImagefile *imagefile, + const gchar *mime_type); +void gimp_imagefile_update (GimpImagefile *imagefile); +gboolean gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, + GimpContext *context, + GimpProgress *progress, + gint size, + gboolean replace, + GError **error); +void gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, + GimpContext *context, + GimpProgress *progress, + gint size, + gboolean replace); +gboolean gimp_imagefile_check_thumbnail (GimpImagefile *imagefile); +gboolean gimp_imagefile_save_thumbnail (GimpImagefile *imagefile, + const gchar *mime_type, + GimpImage *image, + GError **error); +const gchar * gimp_imagefile_get_desc_string (GimpImagefile *imagefile); + + +#endif /* __GIMP_IMAGEFILE_H__ */ diff --git a/app/core/gimpimageproxy.c b/app/core/gimpimageproxy.c new file mode 100644 index 0000000..6007d6c --- /dev/null +++ b/app/core/gimpimageproxy.c @@ -0,0 +1,877 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimageproxy.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-preview.h" +#include "gimpimageproxy.h" +#include "gimppickable.h" +#include "gimpprojectable.h" +#include "gimptempbuf.h" + + +enum +{ + PROP_0, + PROP_IMAGE, + PROP_SHOW_ALL, + PROP_BUFFER +}; + + +struct _GimpImageProxyPrivate +{ + GimpImage *image; + gboolean show_all; + + GeglRectangle bounding_box; + gboolean frozen; +}; + + +/* local function prototypes */ + +static void gimp_image_proxy_pickable_iface_init (GimpPickableInterface *iface); +static void gimp_image_proxy_color_managed_iface_init (GimpColorManagedInterface *iface); + +static void gimp_image_proxy_finalize (GObject *object); +static void gimp_image_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_image_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gboolean gimp_image_proxy_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static void gimp_image_proxy_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_image_proxy_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_image_proxy_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static GdkPixbuf * gimp_image_proxy_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_image_proxy_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_image_proxy_flush (GimpPickable *pickable); +static const Babl * gimp_image_proxy_get_format (GimpPickable *pickable); +static const Babl * gimp_image_proxy_get_format_with_alpha (GimpPickable *pickable); +static GeglBuffer * gimp_image_proxy_get_buffer (GimpPickable *pickable); +static gboolean gimp_image_proxy_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +static gdouble gimp_image_proxy_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +static void gimp_image_proxy_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); +static void gimp_image_proxy_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +static void gimp_image_proxy_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); + +static const guint8 * gimp_image_proxy_get_icc_profile (GimpColorManaged *managed, + gsize *len); +static GimpColorProfile * gimp_image_proxy_get_color_profile (GimpColorManaged *managed); +static void gimp_image_proxy_profile_changed (GimpColorManaged *managed); + +static void gimp_image_proxy_image_frozen_notify (GimpImage *image, + const GParamSpec *pspec, + GimpImageProxy *image_proxy); +static void gimp_image_proxy_image_invalidate_preview (GimpImage *image, + GimpImageProxy *image_proxy); +static void gimp_image_proxy_image_size_changed (GimpImage *image, + GimpImageProxy *image_proxy); +static void gimp_image_proxy_image_bounds_changed (GimpImage *image, + gint old_x, + gint old_y, + GimpImageProxy *image_proxy); +static void gimp_image_proxy_image_profile_changed (GimpImage *image, + GimpImageProxy *image_proxy); + +static void gimp_image_proxy_set_image (GimpImageProxy *image_proxy, + GimpImage *image); +static GimpPickable * gimp_image_proxy_get_pickable (GimpImageProxy *image_proxy); +static void gimp_image_proxy_update_bounding_box (GimpImageProxy *image_proxy); +static void gimp_image_proxy_update_frozen (GimpImageProxy *image_proxy); + + +G_DEFINE_TYPE_WITH_CODE (GimpImageProxy, gimp_image_proxy, GIMP_TYPE_VIEWABLE, + G_ADD_PRIVATE (GimpImageProxy) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_image_proxy_pickable_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_image_proxy_color_managed_iface_init)) + +#define parent_class gimp_image_proxy_parent_class + + +/* private functions */ + + +static void +gimp_image_proxy_class_init (GimpImageProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->finalize = gimp_image_proxy_finalize; + object_class->set_property = gimp_image_proxy_set_property; + object_class->get_property = gimp_image_proxy_get_property; + + viewable_class->default_icon_name = "gimp-image"; + viewable_class->get_size = gimp_image_proxy_get_size; + viewable_class->get_preview_size = gimp_image_proxy_get_preview_size; + viewable_class->get_popup_size = gimp_image_proxy_get_popup_size; + viewable_class->get_new_preview = gimp_image_proxy_get_new_preview; + viewable_class->get_new_pixbuf = gimp_image_proxy_get_new_pixbuf; + viewable_class->get_description = gimp_image_proxy_get_description; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", + NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SHOW_ALL, + g_param_spec_boolean ("show-all", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); +} + +static void +gimp_image_proxy_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->flush = gimp_image_proxy_flush; + iface->get_image = (gpointer) gimp_image_proxy_get_image; + iface->get_format = gimp_image_proxy_get_format; + iface->get_format_with_alpha = gimp_image_proxy_get_format_with_alpha; + iface->get_buffer = gimp_image_proxy_get_buffer; + iface->get_pixel_at = gimp_image_proxy_get_pixel_at; + iface->get_opacity_at = gimp_image_proxy_get_opacity_at; + iface->get_pixel_average = gimp_image_proxy_get_pixel_average; + iface->pixel_to_srgb = gimp_image_proxy_pixel_to_srgb; + iface->srgb_to_pixel = gimp_image_proxy_srgb_to_pixel; +} + +static void +gimp_image_proxy_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_icc_profile = gimp_image_proxy_get_icc_profile; + iface->get_color_profile = gimp_image_proxy_get_color_profile; + iface->profile_changed = gimp_image_proxy_profile_changed; +} + +static void +gimp_image_proxy_init (GimpImageProxy *image_proxy) +{ + image_proxy->priv = gimp_image_proxy_get_instance_private (image_proxy); +} + +static void +gimp_image_proxy_finalize (GObject *object) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (object); + + gimp_image_proxy_set_image (image_proxy, NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_image_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (object); + + switch (property_id) + { + case PROP_IMAGE: + gimp_image_proxy_set_image (image_proxy, + g_value_get_object (value)); + break; + + case PROP_SHOW_ALL: + gimp_image_proxy_set_show_all (image_proxy, + g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, + gimp_image_proxy_get_image (image_proxy)); + break; + + case PROP_SHOW_ALL: + g_value_set_boolean (value, + gimp_image_proxy_get_show_all (image_proxy)); + break; + + case PROP_BUFFER: + g_value_set_object (value, + gimp_pickable_get_buffer ( + GIMP_PICKABLE (image_proxy))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_image_proxy_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (viewable); + + *width = image_proxy->priv->bounding_box.width; + *height = image_proxy->priv->bounding_box.height; + + return TRUE; +} + +static void +gimp_image_proxy_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (viewable); + GimpImage *image = image_proxy->priv->image; + gdouble xres; + gdouble yres; + gint viewable_width; + gint viewable_height; + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_viewable_get_size (viewable, &viewable_width, &viewable_height); + + gimp_viewable_calc_preview_size (viewable_width, + viewable_height, + size, + size, + dot_for_dot, + xres, + yres, + width, + height, + NULL); +} + +static gboolean +gimp_image_proxy_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + gint viewable_width; + gint viewable_height; + + gimp_viewable_get_size (viewable, &viewable_width, &viewable_height); + + if (viewable_width > width || viewable_height > height) + { + gboolean scaling_up; + + gimp_viewable_calc_preview_size (viewable_width, + viewable_height, + width * 2, + height * 2, + dot_for_dot, 1.0, 1.0, + popup_width, + popup_height, + &scaling_up); + + if (scaling_up) + { + *popup_width = viewable_width; + *popup_height = viewable_height; + } + + return TRUE; + } + + return FALSE; +} + +static GimpTempBuf * +gimp_image_proxy_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (viewable); + GimpImage *image = image_proxy->priv->image; + GimpPickable *pickable; + const Babl *format; + GeglRectangle bounding_box; + GimpTempBuf *buf; + gdouble scale_x; + gdouble scale_y; + gdouble scale; + + pickable = gimp_image_proxy_get_pickable (image_proxy); + bounding_box = gimp_image_proxy_get_bounding_box (image_proxy); + + scale_x = (gdouble) width / (gdouble) bounding_box.width; + scale_y = (gdouble) height / (gdouble) bounding_box.height; + + scale = MIN (scale_x, scale_y); + + format = gimp_image_get_preview_format (image); + + buf = gimp_temp_buf_new (width, height, format); + + gegl_buffer_get (gimp_pickable_get_buffer (pickable), + GEGL_RECTANGLE (bounding_box.x * scale, + bounding_box.y * scale, + width, + height), + scale, + gimp_temp_buf_get_format (buf), + gimp_temp_buf_get_data (buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + return buf; +} + +static GdkPixbuf * +gimp_image_proxy_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (viewable); + GimpImage *image = image_proxy->priv->image; + GimpPickable *pickable; + GeglRectangle bounding_box; + GdkPixbuf *pixbuf; + gdouble scale_x; + gdouble scale_y; + gdouble scale; + GimpColorTransform *transform; + + pickable = gimp_image_proxy_get_pickable (image_proxy); + bounding_box = gimp_image_proxy_get_bounding_box (image_proxy); + + scale_x = (gdouble) width / (gdouble) bounding_box.width; + scale_y = (gdouble) height / (gdouble) bounding_box.height; + + scale = MIN (scale_x, scale_y); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + width, height); + + transform = gimp_image_get_color_transform_to_srgb_u8 (image); + + if (transform) + { + GimpTempBuf *temp_buf; + GeglBuffer *src_buf; + GeglBuffer *dest_buf; + + temp_buf = gimp_temp_buf_new (width, height, + gimp_pickable_get_format (pickable)); + + gegl_buffer_get (gimp_pickable_get_buffer (pickable), + GEGL_RECTANGLE (bounding_box.x * scale, + bounding_box.y * scale, + width, + height), + scale, + gimp_temp_buf_get_format (temp_buf), + gimp_temp_buf_get_data (temp_buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + + src_buf = gimp_temp_buf_create_buffer (temp_buf); + dest_buf = gimp_pixbuf_create_buffer (pixbuf); + + gimp_temp_buf_unref (temp_buf); + + gimp_color_transform_process_buffer (transform, + src_buf, + GEGL_RECTANGLE (0, 0, + width, height), + dest_buf, + GEGL_RECTANGLE (0, 0, 0, 0)); + + g_object_unref (src_buf); + g_object_unref (dest_buf); + } + else + { + gegl_buffer_get (gimp_pickable_get_buffer (pickable), + GEGL_RECTANGLE (bounding_box.x * scale, + bounding_box.y * scale, + width, + height), + scale, + gimp_pixbuf_get_format (pixbuf), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + GEGL_ABYSS_CLAMP); + } + + return pixbuf; +} + +static gchar * +gimp_image_proxy_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (viewable); + GimpImage *image = image_proxy->priv->image; + + if (tooltip) + *tooltip = g_strdup (gimp_image_get_display_path (image)); + + return g_strdup_printf ("%s-%d", + gimp_image_get_display_name (image), + gimp_image_get_ID (image)); +} + +static void +gimp_image_proxy_flush (GimpPickable *pickable) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + gimp_pickable_flush (proxy_pickable); +} + +static const Babl * +gimp_image_proxy_get_format (GimpPickable *pickable) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + return gimp_pickable_get_format (proxy_pickable); +} + +static const Babl * +gimp_image_proxy_get_format_with_alpha (GimpPickable *pickable) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + return gimp_pickable_get_format_with_alpha (proxy_pickable); +} + +static GeglBuffer * +gimp_image_proxy_get_buffer (GimpPickable *pickable) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + return gimp_pickable_get_buffer (proxy_pickable); +} + +static gboolean +gimp_image_proxy_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + return gimp_pickable_get_pixel_at (proxy_pickable, x, y, format, pixel); +} + +static gdouble +gimp_image_proxy_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + return gimp_pickable_get_opacity_at (proxy_pickable, x, y); +} + +static void +gimp_image_proxy_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + gimp_pickable_get_pixel_average (proxy_pickable, rect, format, pixel); +} + +static void +gimp_image_proxy_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + gimp_pickable_pixel_to_srgb (proxy_pickable, format, pixel, color); +} + +static void +gimp_image_proxy_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (pickable); + GimpPickable *proxy_pickable; + + proxy_pickable = gimp_image_proxy_get_pickable (image_proxy); + + gimp_pickable_srgb_to_pixel (proxy_pickable, color, format, pixel); +} + +static const guint8 * +gimp_image_proxy_get_icc_profile (GimpColorManaged *managed, + gsize *len) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (managed); + + return gimp_color_managed_get_icc_profile ( + GIMP_COLOR_MANAGED (image_proxy->priv->image), + len); +} + +static GimpColorProfile * +gimp_image_proxy_get_color_profile (GimpColorManaged *managed) +{ + GimpImageProxy *image_proxy = GIMP_IMAGE_PROXY (managed); + + return gimp_color_managed_get_color_profile ( + GIMP_COLOR_MANAGED (image_proxy->priv->image)); +} + +static void +gimp_image_proxy_profile_changed (GimpColorManaged *managed) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (managed)); +} + +static void +gimp_image_proxy_image_frozen_notify (GimpImage *image, + const GParamSpec *pspec, + GimpImageProxy *image_proxy) +{ + gimp_image_proxy_update_frozen (image_proxy); +} + +static void +gimp_image_proxy_image_invalidate_preview (GimpImage *image, + GimpImageProxy *image_proxy) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (image_proxy)); +} + +static void +gimp_image_proxy_image_size_changed (GimpImage *image, + GimpImageProxy *image_proxy) +{ + gimp_image_proxy_update_bounding_box (image_proxy); +} + +static void +gimp_image_proxy_image_bounds_changed (GimpImage *image, + gint old_x, + gint old_y, + GimpImageProxy *image_proxy) +{ + gimp_image_proxy_update_bounding_box (image_proxy); +} + +static void +gimp_image_proxy_image_profile_changed (GimpImage *image, + GimpImageProxy *image_proxy) +{ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image_proxy)); +} + +static void +gimp_image_proxy_set_image (GimpImageProxy *image_proxy, + GimpImage *image) +{ + if (image_proxy->priv->image) + { + g_signal_handlers_disconnect_by_func ( + image_proxy->priv->image, + gimp_image_proxy_image_frozen_notify, + image_proxy); + g_signal_handlers_disconnect_by_func ( + image_proxy->priv->image, + gimp_image_proxy_image_invalidate_preview, + image_proxy); + g_signal_handlers_disconnect_by_func ( + image_proxy->priv->image, + gimp_image_proxy_image_size_changed, + image_proxy); + g_signal_handlers_disconnect_by_func ( + image_proxy->priv->image, + gimp_image_proxy_image_bounds_changed, + image_proxy); + g_signal_handlers_disconnect_by_func ( + image_proxy->priv->image, + gimp_image_proxy_image_profile_changed, + image_proxy); + + g_object_unref (image_proxy->priv->image); + } + + image_proxy->priv->image = image; + + if (image_proxy->priv->image) + { + g_object_ref (image_proxy->priv->image); + + g_signal_connect ( + image_proxy->priv->image, "notify::frozen", + G_CALLBACK (gimp_image_proxy_image_frozen_notify), + image_proxy); + g_signal_connect ( + image_proxy->priv->image, "invalidate-preview", + G_CALLBACK (gimp_image_proxy_image_invalidate_preview), + image_proxy); + g_signal_connect ( + image_proxy->priv->image, "size-changed", + G_CALLBACK (gimp_image_proxy_image_size_changed), + image_proxy); + g_signal_connect ( + image_proxy->priv->image, "bounds-changed", + G_CALLBACK (gimp_image_proxy_image_bounds_changed), + image_proxy); + g_signal_connect ( + image_proxy->priv->image, "profile-changed", + G_CALLBACK (gimp_image_proxy_image_profile_changed), + image_proxy); + + gimp_image_proxy_update_bounding_box (image_proxy); + gimp_image_proxy_update_frozen (image_proxy); + + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (image_proxy)); + } +} + +static GimpPickable * +gimp_image_proxy_get_pickable (GimpImageProxy *image_proxy) +{ + GimpImage *image = image_proxy->priv->image; + + if (! image_proxy->priv->show_all) + return GIMP_PICKABLE (image); + else + return GIMP_PICKABLE (gimp_image_get_projection (image)); +} + +static void +gimp_image_proxy_update_bounding_box (GimpImageProxy *image_proxy) +{ + GimpImage *image = image_proxy->priv->image; + GeglRectangle bounding_box; + + if (gimp_viewable_preview_is_frozen (GIMP_VIEWABLE (image_proxy))) + return; + + if (! image_proxy->priv->show_all) + { + bounding_box.x = 0; + bounding_box.y = 0; + bounding_box.width = gimp_image_get_width (image); + bounding_box.height = gimp_image_get_height (image); + } + else + { + bounding_box = gimp_projectable_get_bounding_box ( + GIMP_PROJECTABLE (image)); + } + + if (! gegl_rectangle_equal (&bounding_box, + &image_proxy->priv->bounding_box)) + { + image_proxy->priv->bounding_box = bounding_box; + + gimp_viewable_size_changed (GIMP_VIEWABLE (image_proxy)); + } +} + +static void +gimp_image_proxy_update_frozen (GimpImageProxy *image_proxy) +{ + gboolean frozen; + + frozen = gimp_viewable_preview_is_frozen ( + GIMP_VIEWABLE (image_proxy->priv->image)); + + if (frozen != image_proxy->priv->frozen) + { + image_proxy->priv->frozen = frozen; + + if (frozen) + { + gimp_viewable_preview_freeze (GIMP_VIEWABLE (image_proxy)); + } + else + { + gimp_viewable_preview_thaw (GIMP_VIEWABLE (image_proxy)); + + gimp_image_proxy_update_bounding_box (image_proxy); + } + } +} + + +/* public functions */ + + +GimpImageProxy * +gimp_image_proxy_new (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return g_object_new (GIMP_TYPE_IMAGE_PROXY, + "image", image, + NULL); +} + +GimpImage * +gimp_image_proxy_get_image (GimpImageProxy *image_proxy) +{ + g_return_val_if_fail (GIMP_IS_IMAGE_PROXY (image_proxy), NULL); + + return image_proxy->priv->image; +} + +void +gimp_image_proxy_set_show_all (GimpImageProxy *image_proxy, + gboolean show_all) +{ + g_return_if_fail (GIMP_IS_IMAGE_PROXY (image_proxy)); + + if (show_all != image_proxy->priv->show_all) + { + image_proxy->priv->show_all = show_all; + + gimp_image_proxy_update_bounding_box (image_proxy); + } +} + +gboolean +gimp_image_proxy_get_show_all (GimpImageProxy *image_proxy) +{ + g_return_val_if_fail (GIMP_IS_IMAGE_PROXY (image_proxy), FALSE); + + return image_proxy->priv->show_all; +} + +GeglRectangle +gimp_image_proxy_get_bounding_box (GimpImageProxy *image_proxy) +{ + g_return_val_if_fail (GIMP_IS_IMAGE_PROXY (image_proxy), + *GEGL_RECTANGLE (0, 0, 0, 0)); + + return image_proxy->priv->bounding_box; +} diff --git a/app/core/gimpimageproxy.h b/app/core/gimpimageproxy.h new file mode 100644 index 0000000..a6c9922 --- /dev/null +++ b/app/core/gimpimageproxy.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimageproxy.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_IMAGE_PROXY_H__ +#define __GIMP_IMAGE_PROXY_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_IMAGE_PROXY (gimp_image_proxy_get_type ()) +#define GIMP_IMAGE_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGE_PROXY, GimpImageProxy)) +#define GIMP_IMAGE_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_IMAGE_PROXY, GimpImageProxyClass)) +#define GIMP_IS_IMAGE_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGE_PROXY)) +#define GIMP_IS_IMAGE_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_IMAGE_PROXY)) +#define GIMP_IMAGE_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_IMAGE_PROXY, GimpImageProxyClass)) + + +typedef struct _GimpImageProxyPrivate GimpImageProxyPrivate; +typedef struct _GimpImageProxyClass GimpImageProxyClass; + +struct _GimpImageProxy +{ + GimpViewable parent_instance; + + GimpImageProxyPrivate *priv; +}; + +struct _GimpImageProxyClass +{ + GimpViewableClass parent_class; +}; + + +GType gimp_image_proxy_get_type (void) G_GNUC_CONST; + +GimpImageProxy * gimp_image_proxy_new (GimpImage *image); + +GimpImage * gimp_image_proxy_get_image (GimpImageProxy *image_proxy); + +void gimp_image_proxy_set_show_all (GimpImageProxy *image_proxy, + gboolean show_all); +gboolean gimp_image_proxy_get_show_all (GimpImageProxy *image_proxy); + +GeglRectangle gimp_image_proxy_get_bounding_box (GimpImageProxy *image_proxy); + + +#endif /* __GIMP_IMAGE_PROXY_H__ */ diff --git a/app/core/gimpimageundo.c b/app/core/gimpimageundo.c new file mode 100644 index 0000000..8aa9e11 --- /dev/null +++ b/app/core/gimpimageundo.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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpdrawable.h" +#include "gimpgrid.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-grid.h" +#include "gimpimage-metadata.h" +#include "gimpimage-private.h" +#include "gimpimageundo.h" + + +enum +{ + PROP_0, + PROP_PREVIOUS_ORIGIN_X, + PROP_PREVIOUS_ORIGIN_Y, + PROP_PREVIOUS_WIDTH, + PROP_PREVIOUS_HEIGHT, + PROP_GRID, + PROP_PARASITE_NAME +}; + + +static void gimp_image_undo_constructed (GObject *object); +static void gimp_image_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_image_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_image_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_image_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_image_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpImageUndo, gimp_image_undo, GIMP_TYPE_UNDO) + +#define parent_class gimp_image_undo_parent_class + + +static void +gimp_image_undo_class_init (GimpImageUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_image_undo_constructed; + object_class->set_property = gimp_image_undo_set_property; + object_class->get_property = gimp_image_undo_get_property; + + gimp_object_class->get_memsize = gimp_image_undo_get_memsize; + + undo_class->pop = gimp_image_undo_pop; + undo_class->free = gimp_image_undo_free; + + g_object_class_install_property (object_class, PROP_PREVIOUS_ORIGIN_X, + g_param_spec_int ("previous-origin-x", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PREVIOUS_ORIGIN_Y, + g_param_spec_int ("previous-origin-y", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PREVIOUS_WIDTH, + g_param_spec_int ("previous-width", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PREVIOUS_HEIGHT, + g_param_spec_int ("previous-height", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_GRID, + g_param_spec_object ("grid", NULL, NULL, + GIMP_TYPE_GRID, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PARASITE_NAME, + g_param_spec_string ("parasite-name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_image_undo_init (GimpImageUndo *undo) +{ +} + +static void +gimp_image_undo_constructed (GObject *object) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object); + GimpImage *image; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + image = GIMP_UNDO (object)->image; + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_IMAGE_TYPE: + image_undo->base_type = gimp_image_get_base_type (image); + break; + + case GIMP_UNDO_IMAGE_PRECISION: + image_undo->precision = gimp_image_get_precision (image); + break; + + case GIMP_UNDO_IMAGE_SIZE: + image_undo->width = gimp_image_get_width (image); + image_undo->height = gimp_image_get_height (image); + break; + + case GIMP_UNDO_IMAGE_RESOLUTION: + gimp_image_get_resolution (image, + &image_undo->xresolution, + &image_undo->yresolution); + image_undo->resolution_unit = gimp_image_get_unit (image); + break; + + case GIMP_UNDO_IMAGE_GRID: + gimp_assert (GIMP_IS_GRID (image_undo->grid)); + break; + + case GIMP_UNDO_IMAGE_COLORMAP: + image_undo->num_colors = gimp_image_get_colormap_size (image); + image_undo->colormap = g_memdup (gimp_image_get_colormap (image), + GIMP_IMAGE_COLORMAP_SIZE); + break; + + case GIMP_UNDO_IMAGE_COLOR_MANAGED: + image_undo->is_color_managed = gimp_image_get_is_color_managed (image); + break; + + case GIMP_UNDO_IMAGE_METADATA: + image_undo->metadata = + gimp_metadata_duplicate (gimp_image_get_metadata (image)); + break; + + case GIMP_UNDO_PARASITE_ATTACH: + case GIMP_UNDO_PARASITE_REMOVE: + gimp_assert (image_undo->parasite_name != NULL); + + image_undo->parasite = gimp_parasite_copy + (gimp_image_parasite_find (image, image_undo->parasite_name)); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_image_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object); + + switch (property_id) + { + case PROP_PREVIOUS_ORIGIN_X: + image_undo->previous_origin_x = g_value_get_int (value); + break; + case PROP_PREVIOUS_ORIGIN_Y: + image_undo->previous_origin_y = g_value_get_int (value); + break; + case PROP_PREVIOUS_WIDTH: + image_undo->previous_width = g_value_get_int (value); + break; + case PROP_PREVIOUS_HEIGHT: + image_undo->previous_height = g_value_get_int (value); + break; + case PROP_GRID: + { + GimpGrid *grid = g_value_get_object (value); + + if (grid) + image_undo->grid = gimp_config_duplicate (GIMP_CONFIG (grid)); + } + break; + case PROP_PARASITE_NAME: + image_undo->parasite_name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object); + + switch (property_id) + { + case PROP_PREVIOUS_ORIGIN_X: + g_value_set_int (value, image_undo->previous_origin_x); + break; + case PROP_PREVIOUS_ORIGIN_Y: + g_value_set_int (value, image_undo->previous_origin_y); + break; + case PROP_PREVIOUS_WIDTH: + g_value_set_int (value, image_undo->previous_width); + break; + case PROP_PREVIOUS_HEIGHT: + g_value_set_int (value, image_undo->previous_height); + break; + case PROP_GRID: + g_value_set_object (value, image_undo->grid); + break; + case PROP_PARASITE_NAME: + g_value_set_string (value, image_undo->parasite_name); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_image_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object); + gint64 memsize = 0; + + if (image_undo->colormap) + memsize += GIMP_IMAGE_COLORMAP_SIZE; + + if (image_undo->metadata) + memsize += gimp_g_object_get_memsize (G_OBJECT (image_undo->metadata)); + + memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid), + gui_size); + memsize += gimp_string_get_memsize (image_undo->parasite_name); + memsize += gimp_parasite_get_memsize (image_undo->parasite, gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_image_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (undo); + GimpImage *image = undo->image; + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_IMAGE_TYPE: + { + GimpImageBaseType base_type; + + base_type = image_undo->base_type; + image_undo->base_type = gimp_image_get_base_type (image); + g_object_set (image, "base-type", base_type, NULL); + + gimp_image_colormap_changed (image, -1); + + if (image_undo->base_type != gimp_image_get_base_type (image)) + { + if ((image_undo->base_type == GIMP_GRAY) || + (gimp_image_get_base_type (image) == GIMP_GRAY)) + { + /* in case there was no profile undo, we need to emit + * profile-changed anyway + */ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); + } + + accum->mode_changed = TRUE; + } + } + break; + + case GIMP_UNDO_IMAGE_PRECISION: + { + GimpPrecision precision; + + precision = image_undo->precision; + image_undo->precision = gimp_image_get_precision (image); + g_object_set (image, "precision", precision, NULL); + + if (image_undo->precision != gimp_image_get_precision (image)) + accum->precision_changed = TRUE; + } + break; + + case GIMP_UNDO_IMAGE_SIZE: + { + gint width; + gint height; + gint previous_origin_x; + gint previous_origin_y; + gint previous_width; + gint previous_height; + + width = image_undo->width; + height = image_undo->height; + previous_origin_x = image_undo->previous_origin_x; + previous_origin_y = image_undo->previous_origin_y; + previous_width = image_undo->previous_width; + previous_height = image_undo->previous_height; + + /* Transform to a redo */ + image_undo->width = gimp_image_get_width (image); + image_undo->height = gimp_image_get_height (image); + image_undo->previous_origin_x = -previous_origin_x; + image_undo->previous_origin_y = -previous_origin_y; + image_undo->previous_width = width; + image_undo->previous_height = height; + + g_object_set (image, + "width", width, + "height", height, + NULL); + + gimp_drawable_invalidate_boundary + (GIMP_DRAWABLE (gimp_image_get_mask (image))); + + if (gimp_image_get_width (image) != image_undo->width || + gimp_image_get_height (image) != image_undo->height) + { + accum->size_changed = TRUE; + accum->previous_origin_x = previous_origin_x; + accum->previous_origin_y = previous_origin_y; + accum->previous_width = previous_width; + accum->previous_height = previous_height; + } + } + break; + + case GIMP_UNDO_IMAGE_RESOLUTION: + { + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + if (ABS (image_undo->xresolution - xres) >= 1e-5 || + ABS (image_undo->yresolution - yres) >= 1e-5) + { + private->xresolution = image_undo->xresolution; + private->yresolution = image_undo->yresolution; + + image_undo->xresolution = xres; + image_undo->yresolution = yres; + + accum->resolution_changed = TRUE; + } + } + + if (image_undo->resolution_unit != gimp_image_get_unit (image)) + { + GimpUnit unit; + + unit = gimp_image_get_unit (image); + private->resolution_unit = image_undo->resolution_unit; + image_undo->resolution_unit = unit; + + accum->unit_changed = TRUE; + } + break; + + case GIMP_UNDO_IMAGE_GRID: + { + GimpGrid *grid; + + grid = gimp_config_duplicate (GIMP_CONFIG (gimp_image_get_grid (image))); + + gimp_image_set_grid (image, image_undo->grid, FALSE); + + g_object_unref (image_undo->grid); + image_undo->grid = grid; + } + break; + + case GIMP_UNDO_IMAGE_COLORMAP: + { + guchar *colormap; + gint num_colors; + + num_colors = gimp_image_get_colormap_size (image); + colormap = g_memdup (gimp_image_get_colormap (image), + GIMP_IMAGE_COLORMAP_SIZE); + + if (image_undo->colormap) + gimp_image_set_colormap (image, + image_undo->colormap, image_undo->num_colors, + FALSE); + else + gimp_image_unset_colormap (image, FALSE); + + if (image_undo->colormap) + g_free (image_undo->colormap); + + image_undo->num_colors = num_colors; + image_undo->colormap = colormap; + } + break; + + case GIMP_UNDO_IMAGE_COLOR_MANAGED: + { + gboolean is_color_managed; + + is_color_managed = gimp_image_get_is_color_managed (image); + gimp_image_set_is_color_managed (image, image_undo->is_color_managed, + FALSE); + image_undo->is_color_managed = is_color_managed; + } + break; + + case GIMP_UNDO_IMAGE_METADATA: + { + GimpMetadata *metadata; + + metadata = gimp_metadata_duplicate (gimp_image_get_metadata (image)); + + gimp_image_set_metadata (image, image_undo->metadata, FALSE); + + if (image_undo->metadata) + g_object_unref (image_undo->metadata); + image_undo->metadata = metadata; + } + break; + + case GIMP_UNDO_PARASITE_ATTACH: + case GIMP_UNDO_PARASITE_REMOVE: + { + GimpParasite *parasite = image_undo->parasite; + + image_undo->parasite = gimp_parasite_copy + (gimp_image_parasite_find (image, image_undo->parasite_name)); + + if (parasite) + gimp_image_parasite_attach (image, parasite, FALSE); + else + gimp_image_parasite_detach (image, image_undo->parasite_name, FALSE); + + if (parasite) + gimp_parasite_free (parasite); + } + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_image_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (undo); + + g_clear_object (&image_undo->grid); + g_clear_pointer (&image_undo->colormap, g_free); + g_clear_object (&image_undo->metadata); + g_clear_pointer (&image_undo->parasite_name, g_free); + g_clear_pointer (&image_undo->parasite, gimp_parasite_free); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpimageundo.h b/app/core/gimpimageundo.h new file mode 100644 index 0000000..0290b40 --- /dev/null +++ b/app/core/gimpimageundo.h @@ -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 . + */ + +#ifndef __GIMP_IMAGE_UNDO_H__ +#define __GIMP_IMAGE_UNDO_H__ + + +#include "gimpundo.h" + + +#define GIMP_TYPE_IMAGE_UNDO (gimp_image_undo_get_type ()) +#define GIMP_IMAGE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGE_UNDO, GimpImageUndo)) +#define GIMP_IMAGE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_IMAGE_UNDO, GimpImageUndoClass)) +#define GIMP_IS_IMAGE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGE_UNDO)) +#define GIMP_IS_IMAGE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_IMAGE_UNDO)) +#define GIMP_IMAGE_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_IMAGE_UNDO, GimpImageUndoClass)) + + +typedef struct _GimpImageUndo GimpImageUndo; +typedef struct _GimpImageUndoClass GimpImageUndoClass; + +struct _GimpImageUndo +{ + GimpUndo parent_instance; + + GimpImageBaseType base_type; + GimpPrecision precision; + gint width; + gint height; + gint previous_origin_x; + gint previous_origin_y; + gint previous_width; + gint previous_height; + gdouble xresolution; + gdouble yresolution; + GimpUnit resolution_unit; + GimpGrid *grid; + gint num_colors; + guchar *colormap; + gboolean is_color_managed; + GimpMetadata *metadata; + gchar *parasite_name; + GimpParasite *parasite; +}; + +struct _GimpImageUndoClass +{ + GimpUndoClass parent_class; +}; + + +GType gimp_image_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_IMAGE_UNDO_H__ */ diff --git a/app/core/gimpitem-exclusive.c b/app/core/gimpitem-exclusive.c new file mode 100644 index 0000000..5cdb45f --- /dev/null +++ b/app/core/gimpitem-exclusive.c @@ -0,0 +1,276 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpitem-exclusive.c + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpcontext.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimpitem-exclusive.h" +#include "gimpitemstack.h" +#include "gimpitemtree.h" +#include "gimpundostack.h" + +#include "gimp-intl.h" + + +static GList * gimp_item_exclusive_get_ancestry (GimpItem *item); +static void gimp_item_exclusive_get_lists (GimpItem *item, + const gchar *property, + GList **on, + GList **off); + + +/* public functions */ + +void +gimp_item_toggle_exclusive_visible (GimpItem *item, + GimpContext *context) +{ + GList *ancestry; + GList *on; + GList *off; + GList *list; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + ancestry = gimp_item_exclusive_get_ancestry (item); + gimp_item_exclusive_get_lists (item, "visible", &on, &off); + + if (on || off || ! gimp_item_is_visible (item)) + { + GimpImage *image = gimp_item_get_image (item); + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_UNDO_STACK, + GIMP_UNDO_GROUP_ITEM_VISIBILITY); + + if (undo && (g_object_get_data (G_OBJECT (undo), "exclusive-visible-item") == + (gpointer) item)) + push_undo = FALSE; + + if (push_undo) + { + if (gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_VISIBILITY, + _("Set Item Exclusive Visible"))) + { + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_UNDO_STACK, + GIMP_UNDO_GROUP_ITEM_VISIBILITY); + + if (undo) + g_object_set_data (G_OBJECT (undo), "exclusive-visible-item", + (gpointer) item); + } + + for (list = ancestry; list; list = g_list_next (list)) + gimp_image_undo_push_item_visibility (image, NULL, list->data); + + for (list = on; list; list = g_list_next (list)) + gimp_image_undo_push_item_visibility (image, NULL, list->data); + + for (list = off; list; list = g_list_next (list)) + gimp_image_undo_push_item_visibility (image, NULL, list->data); + + gimp_image_undo_group_end (image); + } + else + { + gimp_undo_refresh_preview (undo, context); + } + + for (list = ancestry; list; list = g_list_next (list)) + gimp_item_set_visible (list->data, TRUE, FALSE); + + if (on) + { + for (list = on; list; list = g_list_next (list)) + gimp_item_set_visible (list->data, FALSE, FALSE); + } + else if (off) + { + for (list = off; list; list = g_list_next (list)) + gimp_item_set_visible (list->data, TRUE, FALSE); + } + + g_list_free (on); + g_list_free (off); + } + + g_list_free (ancestry); +} + +void +gimp_item_toggle_exclusive_linked (GimpItem *item, + GimpContext *context) +{ + GList *on = NULL; + GList *off = NULL; + GList *list; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + for (list = gimp_item_get_container_iter (item); + list; + list = g_list_next (list)) + { + GimpItem *other = list->data; + + if (other != item) + { + if (gimp_item_get_linked (other)) + on = g_list_prepend (on, other); + else + off = g_list_prepend (off, other); + } + } + + if (on || off || ! gimp_item_get_linked (item)) + { + GimpImage *image = gimp_item_get_image (item); + GimpUndo *undo; + gboolean push_undo = TRUE; + + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_UNDO_STACK, + GIMP_UNDO_GROUP_ITEM_LINKED); + + if (undo && (g_object_get_data (G_OBJECT (undo), "exclusive-linked-item") == + (gpointer) item)) + push_undo = FALSE; + + if (push_undo) + { + if (gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_LINKED, + _("Set Item Exclusive Linked"))) + { + undo = gimp_image_undo_can_compress (image, GIMP_TYPE_UNDO_STACK, + GIMP_UNDO_GROUP_ITEM_LINKED); + + if (undo) + g_object_set_data (G_OBJECT (undo), "exclusive-linked-item", + (gpointer) item); + } + + gimp_image_undo_push_item_linked (image, NULL, item); + + for (list = on; list; list = g_list_next (list)) + gimp_image_undo_push_item_linked (image, NULL, list->data); + + for (list = off; list; list = g_list_next (list)) + gimp_image_undo_push_item_linked (image, NULL, list->data); + + gimp_image_undo_group_end (image); + } + else + { + gimp_undo_refresh_preview (undo, context); + } + + if (off || ! gimp_item_get_linked (item)) + { + gimp_item_set_linked (item, TRUE, FALSE); + + for (list = off; list; list = g_list_next (list)) + gimp_item_set_linked (list->data, TRUE, FALSE); + } + else + { + for (list = on; list; list = g_list_next (list)) + gimp_item_set_linked (list->data, FALSE, FALSE); + } + + g_list_free (on); + g_list_free (off); + } +} + + +/* private functions */ + +static GList * +gimp_item_exclusive_get_ancestry (GimpItem *item) +{ + GimpViewable *parent; + GList *ancestry = NULL; + + for (parent = GIMP_VIEWABLE (item); + parent; + parent = gimp_viewable_get_parent (parent)) + { + ancestry = g_list_prepend (ancestry, parent); + } + + return ancestry; +} + +static void +gimp_item_exclusive_get_lists (GimpItem *item, + const gchar *property, + GList **on, + GList **off) +{ + GimpItemTree *tree; + GList *items; + GList *list; + + *on = NULL; + *off = NULL; + + tree = gimp_item_get_tree (item); + + items = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (tree->container)); + + for (list = items; list; list = g_list_next (list)) + { + GimpItem *other = list->data; + + if (other != item) + { + /* we are only interested in same level items. + */ + if (gimp_viewable_get_parent (GIMP_VIEWABLE (other)) == + gimp_viewable_get_parent (GIMP_VIEWABLE (item))) + { + gboolean value; + + g_object_get (other, property, &value, NULL); + + if (value) + *on = g_list_prepend (*on, other); + else + *off = g_list_prepend (*off, other); + } + } + } + + g_list_free (items); +} diff --git a/app/core/gimpitem-exclusive.h b/app/core/gimpitem-exclusive.h new file mode 100644 index 0000000..98838c5 --- /dev/null +++ b/app/core/gimpitem-exclusive.h @@ -0,0 +1,31 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpitem-exclusive.h + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_ITEM_EXCLUSIVE_H__ +#define __GIMP_ITEM_EXCLUSIVE_H__ + + +void gimp_item_toggle_exclusive_visible (GimpItem *item, + GimpContext *context); +void gimp_item_toggle_exclusive_linked (GimpItem *item, + GimpContext *context); + + +#endif /* __GIMP_ITEM_EXCLUSIVE_H__ */ diff --git a/app/core/gimpitem-linked.c b/app/core/gimpitem-linked.c new file mode 100644 index 0000000..9c876e3 --- /dev/null +++ b/app/core/gimpitem-linked.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpcontext.h" +#include "gimpimage.h" +#include "gimpimage-item-list.h" +#include "gimpimage-undo.h" +#include "gimpitem.h" +#include "gimpitem-linked.h" +#include "gimplist.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* public functions */ + +gboolean +gimp_item_linked_is_locked (GimpItem *item) +{ + GList *list; + GList *l; + gboolean locked = FALSE; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_get_linked (item) == TRUE, FALSE); + g_return_val_if_fail (gimp_item_is_attached (item), FALSE); + + list = gimp_image_item_list_get_list (gimp_item_get_image (item), + GIMP_ITEM_TYPE_ALL, + GIMP_ITEM_SET_LINKED); + + list = gimp_image_item_list_filter (list); + + for (l = list; l && ! locked; l = g_list_next (l)) + { + /* We must not use gimp_item_is_position_locked(), especially + * since a child implementation may call the current function and + * end up in infinite loop. + * We are only interested in the value of `lock_position` flag. + */ + if (gimp_item_get_lock_position (l->data)) + locked = TRUE; + } + + g_list_free (list); + + return locked; +} + +void +gimp_item_linked_translate (GimpItem *item, + gint offset_x, + gint offset_y, + gboolean push_undo) +{ + GimpImage *image; + GList *items; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_get_linked (item) == TRUE); + g_return_if_fail (gimp_item_is_attached (item)); + + image = gimp_item_get_image (item); + + items = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_ALL, + GIMP_ITEM_SET_LINKED); + + items = gimp_image_item_list_filter (items); + + gimp_image_item_list_translate (gimp_item_get_image (item), items, + offset_x, offset_y, push_undo); + + g_list_free (items); +} + +void +gimp_item_linked_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpImage *image; + GList *items; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (gimp_item_get_linked (item) == TRUE); + g_return_if_fail (gimp_item_is_attached (item)); + + image = gimp_item_get_image (item); + + items = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_ALL, + GIMP_ITEM_SET_LINKED); + items = gimp_image_item_list_filter (items); + + gimp_image_item_list_flip (image, items, context, + flip_type, axis, clip_result); + + g_list_free (items); +} + +void +gimp_item_linked_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpImage *image; + GList *items; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (gimp_item_get_linked (item) == TRUE); + g_return_if_fail (gimp_item_is_attached (item)); + + image = gimp_item_get_image (item); + + items = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_ALL, + GIMP_ITEM_SET_LINKED); + items = gimp_image_item_list_filter (items); + + gimp_image_item_list_rotate (image, items, context, + rotate_type, center_x, center_y, clip_result); + + g_list_free (items); +} + +void +gimp_item_linked_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpImage *image; + GList *items; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (gimp_item_get_linked (item) == TRUE); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + image = gimp_item_get_image (item); + + items = gimp_image_item_list_get_list (image, + GIMP_ITEM_TYPE_ALL, + GIMP_ITEM_SET_LINKED); + items = gimp_image_item_list_filter (items); + + gimp_image_item_list_transform (image, items, context, + matrix, direction, + interpolation_type, + clip_result, progress); + + g_list_free (items); +} diff --git a/app/core/gimpitem-linked.h b/app/core/gimpitem-linked.h new file mode 100644 index 0000000..c82d85a --- /dev/null +++ b/app/core/gimpitem-linked.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GIMP_ITEM_LINKED_H__ +#define __GIMP_ITEM_LINKED_H__ + + +gboolean gimp_item_linked_is_locked (GimpItem *item); + +void gimp_item_linked_translate (GimpItem *item, + gint offset_x, + gint offset_y, + gboolean push_undo); +void gimp_item_linked_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +void gimp_item_linked_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +void gimp_item_linked_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + + +#endif /* __GIMP_ITEM_LINKED_H__ */ diff --git a/app/core/gimpitem-preview.c b/app/core/gimpitem-preview.c new file mode 100644 index 0000000..f24017b --- /dev/null +++ b/app/core/gimpitem-preview.c @@ -0,0 +1,133 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimpimage.h" +#include "gimpitem.h" +#include "gimpitem-preview.h" + + +/* public functions */ + +void +gimp_item_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + GimpItem *item = GIMP_ITEM (viewable); + GimpImage *image = gimp_item_get_image (item); + + if (image && ! image->gimp->config->layer_previews && ! is_popup) + { + *width = size; + *height = size; + return; + } + + if (image && ! is_popup) + { + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_viewable_calc_preview_size (gimp_image_get_width (image), + gimp_image_get_height (image), + size, + size, + dot_for_dot, + xres, + yres, + width, + height, + NULL); + } + else + { + gimp_viewable_calc_preview_size (gimp_item_get_width (item), + gimp_item_get_height (item), + size, + size, + dot_for_dot, 1.0, 1.0, + width, + height, + NULL); + } +} + +gboolean +gimp_item_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + GimpItem *item = GIMP_ITEM (viewable); + GimpImage *image = gimp_item_get_image (item); + + if (image && ! image->gimp->config->layer_previews) + return FALSE; + + if (gimp_item_get_width (item) > width || + gimp_item_get_height (item) > height) + { + gboolean scaling_up; + gdouble xres = 1.0; + gdouble yres = 1.0; + + if (image) + gimp_image_get_resolution (image, &xres, &yres); + + gimp_viewable_calc_preview_size (gimp_item_get_width (item), + gimp_item_get_height (item), + width * 2, + height * 2, + dot_for_dot, + xres, + yres, + popup_width, + popup_height, + &scaling_up); + + if (scaling_up) + { + *popup_width = gimp_item_get_width (item); + *popup_height = gimp_item_get_height (item); + } + + return TRUE; + } + + return FALSE; +} diff --git a/app/core/gimpitem-preview.h b/app/core/gimpitem-preview.h new file mode 100644 index 0000000..6bfed24 --- /dev/null +++ b/app/core/gimpitem-preview.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 . + */ + +#ifndef __GIMP_ITEM__PREVIEW_H__ +#define __GIMP_ITEM__PREVIEW_H__ + + +/* + * virtual functions of GimpItem -- don't call directly + */ + +void gimp_item_get_preview_size (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height); +gboolean gimp_item_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); + + +#endif /* __GIMP_ITEM__PREVIEW_H__ */ diff --git a/app/core/gimpitem.c b/app/core/gimpitem.c new file mode 100644 index 0000000..c0f732c --- /dev/null +++ b/app/core/gimpitem.c @@ -0,0 +1,2689 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-parasites.h" +#include "gimpchannel.h" +#include "gimpcontainer.h" +#include "gimpidtable.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimpitem-linked.h" +#include "gimpitem-preview.h" +#include "gimpitemtree.h" +#include "gimplist.h" +#include "gimpmarshal.h" +#include "gimpparasitelist.h" +#include "gimpprogress.h" +#include "gimpstrokeoptions.h" + +#include "paint/gimppaintoptions.h" + +#include "gimp-intl.h" + + +enum +{ + REMOVED, + VISIBILITY_CHANGED, + LINKED_CHANGED, + COLOR_TAG_CHANGED, + LOCK_CONTENT_CHANGED, + LOCK_POSITION_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_IMAGE, + PROP_ID, + PROP_WIDTH, + PROP_HEIGHT, + PROP_OFFSET_X, + PROP_OFFSET_Y, + PROP_VISIBLE, + PROP_LINKED, + PROP_COLOR_TAG, + PROP_LOCK_CONTENT, + PROP_LOCK_POSITION +}; + + +typedef struct _GimpItemPrivate GimpItemPrivate; + +struct _GimpItemPrivate +{ + gint ID; /* provides a unique ID */ + guint32 tattoo; /* provides a permanent ID */ + + GimpImage *image; /* item owner */ + + GimpParasiteList *parasites; /* Plug-in parasite data */ + + gint width, height; /* size in pixels */ + gint offset_x, offset_y; /* pixel offset in image */ + + guint visible : 1; /* item visibility */ + guint bind_visible_to_active : 1; /* visibility bound to active */ + + guint linked : 1; /* control linkage */ + guint lock_content : 1; /* content editability */ + guint lock_position : 1; /* content movability */ + + guint removed : 1; /* removed from the image? */ + + GimpColorTag color_tag; /* color tag */ + + GList *offset_nodes; /* offset nodes to manage */ +}; + +#define GET_PRIVATE(item) ((GimpItemPrivate *) gimp_item_get_instance_private ((GimpItem *) (item))) + + +/* local function prototypes */ + +static void gimp_item_constructed (GObject *object); +static void gimp_item_finalize (GObject *object); +static void gimp_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_item_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_item_real_is_content_locked (GimpItem *item); +static gboolean gimp_item_real_is_position_locked (GimpItem *item); +static gboolean gimp_item_real_bounds (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height); +static GimpItem * gimp_item_real_duplicate (GimpItem *item, + GType new_type); +static void gimp_item_real_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type); +static gboolean gimp_item_real_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error); +static void gimp_item_real_start_transform (GimpItem *item, + gboolean push_undo); +static void gimp_item_real_end_transform (GimpItem *item, + gboolean push_undo); +static void gimp_item_real_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo); +static void gimp_item_real_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation, + GimpProgress *progress); +static void gimp_item_real_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static GimpTransformResize + gimp_item_real_get_clip (GimpItem *item, + GimpTransformResize clip_result); + + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpItem, gimp_item, GIMP_TYPE_FILTER) + +#define parent_class gimp_item_parent_class + +static guint gimp_item_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_item_class_init (GimpItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + gimp_item_signals[REMOVED] = + g_signal_new ("removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, removed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_item_signals[VISIBILITY_CHANGED] = + g_signal_new ("visibility-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, visibility_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_item_signals[LINKED_CHANGED] = + g_signal_new ("linked-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, linked_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_item_signals[COLOR_TAG_CHANGED] = + g_signal_new ("color-tag-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, color_tag_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_item_signals[LOCK_CONTENT_CHANGED] = + g_signal_new ("lock-content-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, lock_content_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_item_signals[LOCK_POSITION_CHANGED] = + g_signal_new ("lock-position-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpItemClass, lock_position_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->constructed = gimp_item_constructed; + object_class->finalize = gimp_item_finalize; + object_class->set_property = gimp_item_set_property; + object_class->get_property = gimp_item_get_property; + + gimp_object_class->get_memsize = gimp_item_get_memsize; + + viewable_class->name_editable = TRUE; + viewable_class->get_preview_size = gimp_item_get_preview_size; + viewable_class->get_popup_size = gimp_item_get_popup_size; + + klass->removed = NULL; + klass->visibility_changed = NULL; + klass->linked_changed = NULL; + klass->color_tag_changed = NULL; + klass->lock_content_changed = NULL; + klass->lock_position_changed = NULL; + + klass->unset_removed = NULL; + klass->is_attached = NULL; + klass->is_content_locked = gimp_item_real_is_content_locked; + klass->is_position_locked = gimp_item_real_is_position_locked; + klass->get_tree = NULL; + klass->bounds = gimp_item_real_bounds; + klass->duplicate = gimp_item_real_duplicate; + klass->convert = gimp_item_real_convert; + klass->rename = gimp_item_real_rename; + klass->start_move = NULL; + klass->end_move = NULL; + klass->start_transform = gimp_item_real_start_transform; + klass->end_transform = gimp_item_real_end_transform; + klass->translate = gimp_item_real_translate; + klass->scale = gimp_item_real_scale; + klass->resize = gimp_item_real_resize; + klass->flip = NULL; + klass->rotate = NULL; + klass->transform = NULL; + klass->get_clip = gimp_item_real_get_clip; + klass->fill = NULL; + klass->stroke = NULL; + klass->to_selection = NULL; + + klass->default_name = NULL; + klass->rename_desc = NULL; + klass->translate_desc = NULL; + klass->scale_desc = NULL; + klass->resize_desc = NULL; + klass->flip_desc = NULL; + klass->rotate_desc = NULL; + klass->transform_desc = NULL; + klass->fill_desc = NULL; + klass->stroke_desc = NULL; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_int ("id", NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + 1, GIMP_MAX_IMAGE_SIZE, 1, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", NULL, NULL, + 1, GIMP_MAX_IMAGE_SIZE, 1, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_OFFSET_X, + g_param_spec_int ("offset-x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_OFFSET_Y, + g_param_spec_int ("offset-y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_VISIBLE, + g_param_spec_boolean ("visible", NULL, NULL, + TRUE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_LINKED, + g_param_spec_boolean ("linked", NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_COLOR_TAG, + g_param_spec_enum ("color-tag", NULL, NULL, + GIMP_TYPE_COLOR_TAG, + GIMP_COLOR_TAG_NONE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_LOCK_CONTENT, + g_param_spec_boolean ("lock-content", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_LOCK_POSITION, + g_param_spec_boolean ("lock-position", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_item_init (GimpItem *item) +{ + GimpItemPrivate *private = GET_PRIVATE (item); + + g_object_force_floating (G_OBJECT (item)); + + private->parasites = gimp_parasite_list_new (); + private->visible = TRUE; + private->bind_visible_to_active = TRUE; +} + +static void +gimp_item_constructed (GObject *object) +{ + GimpItemPrivate *private = GET_PRIVATE (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_IMAGE (private->image)); + gimp_assert (private->ID != 0); +} + +static void +gimp_item_finalize (GObject *object) +{ + GimpItemPrivate *private = GET_PRIVATE (object); + + if (private->offset_nodes) + { + g_list_free_full (private->offset_nodes, + (GDestroyNotify) g_object_unref); + private->offset_nodes = NULL; + } + + if (private->image && private->image->gimp) + { + gimp_id_table_remove (private->image->gimp->item_table, private->ID); + private->image = NULL; + } + + g_clear_object (&private->parasites); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpItem *item = GIMP_ITEM (object); + + switch (property_id) + { + case PROP_IMAGE: + gimp_item_set_image (item, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpItemPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, private->image); + break; + case PROP_ID: + g_value_set_int (value, private->ID); + break; + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, private->height); + break; + case PROP_OFFSET_X: + g_value_set_int (value, private->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_int (value, private->offset_y); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, private->visible); + break; + case PROP_LINKED: + g_value_set_boolean (value, private->linked); + break; + case PROP_COLOR_TAG: + g_value_set_enum (value, private->color_tag); + break; + case PROP_LOCK_CONTENT: + g_value_set_boolean (value, private->lock_content); + break; + case PROP_LOCK_POSITION: + g_value_set_boolean (value, private->lock_position); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_item_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpItemPrivate *private = GET_PRIVATE (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (private->parasites), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_item_real_is_content_locked (GimpItem *item) +{ + GimpItem *parent = gimp_item_get_parent (item); + + if (parent && gimp_item_is_content_locked (parent)) + return TRUE; + + return GET_PRIVATE (item)->lock_content; +} + +static gboolean +gimp_item_real_is_position_locked (GimpItem *item) +{ + if (gimp_item_get_linked (item)) + if (gimp_item_linked_is_locked (item)) + return TRUE; + + return GET_PRIVATE (item)->lock_position; +} + +static gboolean +gimp_item_real_bounds (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height) +{ + GimpItemPrivate *private = GET_PRIVATE (item); + + *x = 0; + *y = 0; + *width = private->width; + *height = private->height; + + return TRUE; +} + +static GimpItem * +gimp_item_real_duplicate (GimpItem *item, + GType new_type) +{ + GimpItemPrivate *private; + GimpItem *new_item; + gchar *new_name; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + private = GET_PRIVATE (item); + + g_return_val_if_fail (GIMP_IS_IMAGE (private->image), NULL); + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL); + + /* formulate the new name */ + { + const gchar *name; + gint len; + + name = gimp_object_get_name (item); + + g_return_val_if_fail (name != NULL, NULL); + + len = strlen (_("copy")); + + if ((strlen (name) >= len && + strcmp (&name[strlen (name) - len], _("copy")) == 0) || + g_regex_match_simple ("#([0-9]+)\\s*$", name, 0, 0)) + { + /* don't have redundant "copy"s */ + new_name = g_strdup (name); + } + else + { + new_name = g_strdup_printf (_("%s copy"), name); + } + } + + new_item = gimp_item_new (new_type, + gimp_item_get_image (item), new_name, + private->offset_x, private->offset_y, + gimp_item_get_width (item), + gimp_item_get_height (item)); + + g_free (new_name); + + gimp_viewable_set_expanded (GIMP_VIEWABLE (new_item), + gimp_viewable_get_expanded (GIMP_VIEWABLE (item))); + + g_object_unref (GET_PRIVATE (new_item)->parasites); + GET_PRIVATE (new_item)->parasites = gimp_parasite_list_copy (private->parasites); + + gimp_item_set_visible (new_item, gimp_item_get_visible (item), FALSE); + gimp_item_set_linked (new_item, gimp_item_get_linked (item), FALSE); + gimp_item_set_color_tag (new_item, gimp_item_get_color_tag (item), FALSE); + + if (gimp_item_can_lock_content (new_item)) + gimp_item_set_lock_content (new_item, gimp_item_get_lock_content (item), + FALSE); + + if (gimp_item_can_lock_position (new_item)) + gimp_item_set_lock_position (new_item, gimp_item_get_lock_position (item), + FALSE); + + return new_item; +} + +static void +gimp_item_real_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type) +{ + gimp_item_set_image (item, dest_image); +} + +static gboolean +gimp_item_real_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error) +{ + if (gimp_item_is_attached (item)) + gimp_item_tree_rename_item (gimp_item_get_tree (item), item, + new_name, TRUE, undo_desc); + else + gimp_object_set_name (GIMP_OBJECT (item), new_name); + + return TRUE; +} + +static void +gimp_item_real_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo) +{ + GimpItemPrivate *private = GET_PRIVATE (item); + + gimp_item_set_offset (item, + private->offset_x + SIGNED_ROUND (offset_x), + private->offset_y + SIGNED_ROUND (offset_y)); +} + +static void +gimp_item_real_start_transform (GimpItem *item, + gboolean push_undo) +{ + gimp_item_start_move (item, push_undo); +} + +static void +gimp_item_real_end_transform (GimpItem *item, + gboolean push_undo) +{ + gimp_item_end_move (item, push_undo); +} + +static void +gimp_item_real_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation, + GimpProgress *progress) +{ + GimpItemPrivate *private = GET_PRIVATE (item); + + if (private->width != new_width) + { + private->width = new_width; + g_object_notify (G_OBJECT (item), "width"); + } + + if (private->height != new_height) + { + private->height = new_height; + g_object_notify (G_OBJECT (item), "height"); + } + + gimp_item_set_offset (item, new_offset_x, new_offset_y); +} + +static void +gimp_item_real_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GimpItemPrivate *private = GET_PRIVATE (item); + + if (private->width != new_width) + { + private->width = new_width; + g_object_notify (G_OBJECT (item), "width"); + } + + if (private->height != new_height) + { + private->height = new_height; + g_object_notify (G_OBJECT (item), "height"); + } + + gimp_item_set_offset (item, + private->offset_x - offset_x, + private->offset_y - offset_y); +} + +static GimpTransformResize +gimp_item_real_get_clip (GimpItem *item, + GimpTransformResize clip_result) +{ + if (gimp_item_get_lock_position (item)) + return GIMP_TRANSFORM_RESIZE_CLIP; + else + return clip_result; +} + + +/* public functions */ + +/** + * gimp_item_new: + * @type: The new item's type. + * @image: The new item's #GimpImage. + * @name: The name to assign the item. + * @offset_x: The X offset to assign the item. + * @offset_y: The Y offset to assign the item. + * @width: The width to assign the item. + * @height: The height to assign the item. + * + * Return value: The newly created item. + */ +GimpItem * +gimp_item_new (GType type, + GimpImage *image, + const gchar *name, + gint offset_x, + gint offset_y, + gint width, + gint height) +{ + GimpItem *item; + GimpItemPrivate *private; + + g_return_val_if_fail (g_type_is_a (type, GIMP_TYPE_ITEM), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + item = g_object_new (type, + "image", image, + NULL); + + private = GET_PRIVATE (item); + + private->width = width; + private->height = height; + gimp_item_set_offset (item, offset_x, offset_y); + + if (name && strlen (name)) + gimp_object_set_name (GIMP_OBJECT (item), name); + else + gimp_object_set_static_name (GIMP_OBJECT (item), + GIMP_ITEM_GET_CLASS (item)->default_name); + + return item; +} + +/** + * gimp_item_remove: + * @item: the #GimpItem to remove. + * + * This function sets the 'removed' flag on @item to #TRUE, and emits + * a 'removed' signal on the item. + */ +void +gimp_item_removed (GimpItem *item) +{ + GimpContainer *children; + + g_return_if_fail (GIMP_IS_ITEM (item)); + + GET_PRIVATE (item)->removed = TRUE; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + gimp_container_foreach (children, (GFunc) gimp_item_removed, NULL); + + g_signal_emit (item, gimp_item_signals[REMOVED], 0); +} + +/** + * gimp_item_is_removed: + * @item: the #GimpItem to check. + * + * Returns: %TRUE if the 'removed' flag is set for @item, %FALSE otherwise. + */ +gboolean +gimp_item_is_removed (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GET_PRIVATE (item)->removed; +} + +/** + * gimp_item_unset_removed: + * @item: a #GimpItem which was on the undo stack + * + * Unsets an item's "removed" state. This function is called when an + * item was on the undo stack and is added back to its parent + * container during and undo or redo. It must never be called from + * anywhere else. + **/ +void +gimp_item_unset_removed (GimpItem *item) +{ + GimpContainer *children; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_removed (item)); + + GET_PRIVATE (item)->removed = FALSE; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + gimp_container_foreach (children, (GFunc) gimp_item_unset_removed, NULL); + + if (GIMP_ITEM_GET_CLASS (item)->unset_removed) + GIMP_ITEM_GET_CLASS (item)->unset_removed (item); +} + +/** + * gimp_item_is_attached: + * @item: The #GimpItem to check. + * + * Returns: %TRUE if the item is attached to an image, %FALSE otherwise. + */ +gboolean +gimp_item_is_attached (GimpItem *item) +{ + GimpItem *parent; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + parent = gimp_item_get_parent (item); + + if (parent) + return gimp_item_is_attached (parent); + + return GIMP_ITEM_GET_CLASS (item)->is_attached (item); +} + +GimpItem * +gimp_item_get_parent (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + return GIMP_ITEM (gimp_viewable_get_parent (GIMP_VIEWABLE (item))); +} + +GimpItemTree * +gimp_item_get_tree (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + if (GIMP_ITEM_GET_CLASS (item)->get_tree) + return GIMP_ITEM_GET_CLASS (item)->get_tree (item); + + return NULL; +} + +GimpContainer * +gimp_item_get_container (GimpItem *item) +{ + GimpItem *parent; + GimpItemTree *tree; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + parent = gimp_item_get_parent (item); + + if (parent) + return gimp_viewable_get_children (GIMP_VIEWABLE (parent)); + + tree = gimp_item_get_tree (item); + + if (tree) + return tree->container; + + return NULL; +} + +GList * +gimp_item_get_container_iter (GimpItem *item) +{ + GimpContainer *container; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + container = gimp_item_get_container (item); + + if (container) + return GIMP_LIST (container)->queue->head; + + return NULL; +} + +gint +gimp_item_get_index (GimpItem *item) +{ + GimpContainer *container; + + g_return_val_if_fail (GIMP_IS_ITEM (item), -1); + + container = gimp_item_get_container (item); + + if (container) + return gimp_container_get_child_index (container, GIMP_OBJECT (item)); + + return -1; +} + +GList * +gimp_item_get_path (GimpItem *item) +{ + GimpContainer *container; + GList *path = NULL; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (gimp_item_is_attached (item), NULL); + + container = gimp_item_get_container (item); + + while (container) + { + guint32 index = gimp_container_get_child_index (container, + GIMP_OBJECT (item)); + + path = g_list_prepend (path, GUINT_TO_POINTER (index)); + + item = gimp_item_get_parent (item); + + if (item) + container = gimp_item_get_container (item); + else + container = NULL; + } + + return path; +} + +gboolean +gimp_item_bounds (GimpItem *item, + gint *x, + gint *y, + gint *width, + gint *height) +{ + gdouble tmp_x, tmp_y, tmp_width, tmp_height; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + retval = GIMP_ITEM_GET_CLASS (item)->bounds (item, + &tmp_x, &tmp_y, + &tmp_width, &tmp_height); + + if (x) *x = floor (tmp_x); + if (y) *y = floor (tmp_y); + if (width) *width = ceil (tmp_x + tmp_width) - floor (tmp_x); + if (height) *height = ceil (tmp_y + tmp_height) - floor (tmp_y); + + return retval; +} + +gboolean +gimp_item_bounds_f (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height) +{ + gdouble tmp_x, tmp_y, tmp_width, tmp_height; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + retval = GIMP_ITEM_GET_CLASS (item)->bounds (item, + &tmp_x, &tmp_y, + &tmp_width, &tmp_height); + + if (x) *x = tmp_x; + if (y) *y = tmp_y; + if (width) *width = tmp_width; + if (height) *height = tmp_height; + + return retval; +} + +/** + * gimp_item_duplicate: + * @item: The #GimpItem to duplicate. + * @new_type: The type to make the new item. + * + * Returns: the newly created item. + */ +GimpItem * +gimp_item_duplicate (GimpItem *item, + GType new_type) +{ + GimpItemPrivate *private; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + private = GET_PRIVATE (item); + + g_return_val_if_fail (GIMP_IS_IMAGE (private->image), NULL); + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL); + + return GIMP_ITEM_GET_CLASS (item)->duplicate (item, new_type); +} + +/** + * gimp_item_convert: + * @item: The #GimpItem to convert. + * @dest_image: The #GimpImage in which to place the converted item. + * @new_type: The type to convert the item to. + * + * Returns: the new item that results from the conversion. + */ +GimpItem * +gimp_item_convert (GimpItem *item, + GimpImage *dest_image, + GType new_type) +{ + GimpItem *new_item; + GType old_type; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (GET_PRIVATE (item)->image), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL); + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL); + + old_type = G_TYPE_FROM_INSTANCE (item); + + new_item = gimp_item_duplicate (item, new_type); + + if (new_item) + GIMP_ITEM_GET_CLASS (new_item)->convert (new_item, dest_image, old_type); + + return new_item; +} + +/** + * gimp_item_rename: + * @item: The #GimpItem to rename. + * @new_name: The new name to give the item. + * @error: Return location for error message. + * + * This function assigns a new name to the item, if the desired name is + * different from the name it already has, and pushes an entry onto the + * undo stack for the item's image. If @new_name is NULL or empty, the + * default name for the item's class is used. If the name is changed, + * the GimpObject::name-changed signal is emitted for the item. + * + * Returns: %TRUE if the @item could be renamed, %FALSE otherwise. + */ +gboolean +gimp_item_rename (GimpItem *item, + const gchar *new_name, + GError **error) +{ + GimpItemClass *item_class; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item_class = GIMP_ITEM_GET_CLASS (item); + + if (! new_name || ! *new_name) + new_name = item_class->default_name; + + if (strcmp (new_name, gimp_object_get_name (item))) + return item_class->rename (item, new_name, item_class->rename_desc, error); + + return TRUE; +} + +/** + * gimp_item_get_width: + * @item: The #GimpItem to check. + * + * Returns: The width of the item. + */ +gint +gimp_item_get_width (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), -1); + + return GET_PRIVATE (item)->width; +} + +/** + * gimp_item_get_height: + * @item: The #GimpItem to check. + * + * Returns: The height of the item. + */ +gint +gimp_item_get_height (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), -1); + + return GET_PRIVATE (item)->height; +} + +void +gimp_item_set_size (GimpItem *item, + gint width, + gint height) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + + private = GET_PRIVATE (item); + + if (private->width != width || + private->height != height) + { + g_object_freeze_notify (G_OBJECT (item)); + + if (private->width != width) + { + private->width = width; + g_object_notify (G_OBJECT (item), "width"); + } + + if (private->height != height) + { + private->height = height; + g_object_notify (G_OBJECT (item), "height"); + } + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_viewable_size_changed (GIMP_VIEWABLE (item)); + } +} + +/** + * gimp_item_get_offset: + * @item: The #GimpItem to check. + * @offset_x: Return location for the item's X offset. + * @offset_y: Return location for the item's Y offset. + * + * Reveals the X and Y offsets of the item. + */ +void +gimp_item_get_offset (GimpItem *item, + gint *offset_x, + gint *offset_y) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + + private = GET_PRIVATE (item); + + if (offset_x) *offset_x = private->offset_x; + if (offset_y) *offset_y = private->offset_y; +} + +void +gimp_item_set_offset (GimpItem *item, + gint offset_x, + gint offset_y) +{ + GimpItemPrivate *private; + GList *list; + + g_return_if_fail (GIMP_IS_ITEM (item)); + + private = GET_PRIVATE (item); + + g_object_freeze_notify (G_OBJECT (item)); + + if (private->offset_x != offset_x) + { + private->offset_x = offset_x; + g_object_notify (G_OBJECT (item), "offset-x"); + } + + if (private->offset_y != offset_y) + { + private->offset_y = offset_y; + g_object_notify (G_OBJECT (item), "offset-y"); + } + + for (list = private->offset_nodes; list; list = g_list_next (list)) + { + GeglNode *node = list->data; + + gegl_node_set (node, + "x", (gdouble) private->offset_x, + "y", (gdouble) private->offset_y, + NULL); + } + + g_object_thaw_notify (G_OBJECT (item)); +} + +gint +gimp_item_get_offset_x (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), 0); + + return GET_PRIVATE (item)->offset_x; +} + +gint +gimp_item_get_offset_y (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), 0); + + return GET_PRIVATE (item)->offset_y; +} + +void +gimp_item_start_move (GimpItem *item, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + if (GIMP_ITEM_GET_CLASS (item)->start_move) + GIMP_ITEM_GET_CLASS (item)->start_move (item, push_undo); +} + +void +gimp_item_end_move (GimpItem *item, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + if (GIMP_ITEM_GET_CLASS (item)->end_move) + GIMP_ITEM_GET_CLASS (item)->end_move (item, push_undo); +} + +void +gimp_item_start_transform (GimpItem *item, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + if (GIMP_ITEM_GET_CLASS (item)->start_transform) + GIMP_ITEM_GET_CLASS (item)->start_transform (item, push_undo); +} + +void +gimp_item_end_transform (GimpItem *item, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + if (GIMP_ITEM_GET_CLASS (item)->end_transform) + GIMP_ITEM_GET_CLASS (item)->end_transform (item, push_undo); +} + +/** + * gimp_item_translate: + * @item: The #GimpItem to move. + * @offset_x: Increment to the X offset of the item. + * @offset_y: Increment to the Y offset of the item. + * @push_undo: If #TRUE, create an entry in the image's undo stack + * for this action. + * + * Adds the specified increments to the X and Y offsets for the item. + */ +void +gimp_item_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo) +{ + GimpItemClass *item_class; + GimpImage *image; + + g_return_if_fail (GIMP_IS_ITEM (item)); + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, + item_class->translate_desc); + + gimp_item_start_transform (item, push_undo); + + item_class->translate (item, offset_x, offset_y, push_undo); + + gimp_item_end_transform (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +/** + * gimp_item_check_scaling: + * @item: Item to check + * @new_width: proposed width of item, in pixels + * @new_height: proposed height of item, in pixels + * + * Scales item dimensions, then snaps them to pixel centers + * + * Returns: #FALSE if any dimension reduces to zero as a result + * of this; otherwise, returns #TRUE. + **/ +gboolean +gimp_item_check_scaling (GimpItem *item, + gint new_width, + gint new_height) +{ + GimpItemPrivate *private; + GimpImage *image; + gdouble img_scale_w; + gdouble img_scale_h; + gint new_item_offset_x; + gint new_item_offset_y; + gint new_item_width; + gint new_item_height; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + private = GET_PRIVATE (item); + image = gimp_item_get_image (item); + + img_scale_w = ((gdouble) new_width / + (gdouble) gimp_image_get_width (image)); + img_scale_h = ((gdouble) new_height / + (gdouble) gimp_image_get_height (image)); + new_item_offset_x = SIGNED_ROUND (img_scale_w * private->offset_x); + new_item_offset_y = SIGNED_ROUND (img_scale_h * private->offset_y); + new_item_width = SIGNED_ROUND (img_scale_w * (private->offset_x + + gimp_item_get_width (item))) - + new_item_offset_x; + new_item_height = SIGNED_ROUND (img_scale_h * (private->offset_y + + gimp_item_get_height (item))) - + new_item_offset_y; + + return (new_item_width > 0 && new_item_height > 0); +} + +void +gimp_item_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation, + GimpProgress *progress) +{ + GimpItemClass *item_class; + GimpImage *image; + gboolean push_undo; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (new_width < 1 || new_height < 1) + return; + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + push_undo = gimp_item_is_attached (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_SCALE, + item_class->scale_desc); + + gimp_item_start_transform (item, push_undo); + + g_object_freeze_notify (G_OBJECT (item)); + + item_class->scale (item, new_width, new_height, new_offset_x, new_offset_y, + interpolation, progress); + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_item_end_transform (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +/** + * gimp_item_scale_by_factors: + * @item: Item to be transformed by explicit width and height factors. + * @w_factor: scale factor to apply to width and horizontal offset + * @h_factor: scale factor to apply to height and vertical offset + * @interpolation: + * @progress: + * + * Scales item dimensions and offsets by uniform width and + * height factors. + * + * Use gimp_item_scale_by_factors() in circumstances when the same + * width and height scaling factors are to be uniformly applied to a + * set of items. In this context, the item's dimensions and offsets + * from the sides of the containing image all change by these + * predetermined factors. By fiat, the fixed point of the transform is + * the upper left hand corner of the image. Returns #FALSE if a + * requested scale factor is zero or if a scaling zero's out a item + * dimension; returns #TRUE otherwise. + * + * Use gimp_item_scale() in circumstances where new item width + * and height dimensions are predetermined instead. + * + * Side effects: Undo set created for item. Old item imagery + * scaled & painted to new item tiles. + * + * Returns: #TRUE, if the scaled item has positive dimensions + * #FALSE if the scaled item has at least one zero dimension + **/ +gboolean +gimp_item_scale_by_factors (GimpItem *item, + gdouble w_factor, + gdouble h_factor, + GimpInterpolationType interpolation, + GimpProgress *progress) +{ + return gimp_item_scale_by_factors_with_origin (item, + w_factor, h_factor, + 0, 0, 0, 0, + interpolation, progress); +} + +/** + * gimp_item_scale_by_factors: + * @item: Item to be transformed by explicit width and height factors. + * @w_factor: scale factor to apply to width and horizontal offset + * @h_factor: scale factor to apply to height and vertical offset + * @origin_x: x-coordinate of the transformation input origin + * @origin_y: y-coordinate of the transformation input origin + * @new_origin_x: x-coordinate of the transformation output origin + * @new_origin_y: y-coordinate of the transformation output origin + * @interpolation: + * @progress: + * + * Same as gimp_item_scale_by_factors(), but with the option to specify + * custom input and output points of origin for the transformation. + * + * Returns: #TRUE, if the scaled item has positive dimensions + * #FALSE if the scaled item has at least one zero dimension + **/ +gboolean +gimp_item_scale_by_factors_with_origin (GimpItem *item, + gdouble w_factor, + gdouble h_factor, + gint origin_x, + gint origin_y, + gint new_origin_x, + gint new_origin_y, + GimpInterpolationType interpolation, + GimpProgress *progress) +{ + GimpItemPrivate *private; + GimpContainer *children; + gint new_width, new_height; + gint new_offset_x, new_offset_y; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + + private = GET_PRIVATE (item); + + if (w_factor <= 0.0 || h_factor <= 0.0) + { + g_warning ("%s: requested width or height scale is non-positive", + G_STRFUNC); + return FALSE; + } + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + /* avoid discarding empty layer groups */ + if (children && gimp_container_is_empty (children)) + return TRUE; + + new_offset_x = SIGNED_ROUND (w_factor * (private->offset_x - origin_x)); + new_offset_y = SIGNED_ROUND (h_factor * (private->offset_y - origin_y)); + new_width = SIGNED_ROUND (w_factor * (private->offset_x - origin_x + + gimp_item_get_width (item))) - + new_offset_x; + new_height = SIGNED_ROUND (h_factor * (private->offset_y - origin_y + + gimp_item_get_height (item))) - + new_offset_y; + + new_offset_x += new_origin_x; + new_offset_y += new_origin_y; + + if (new_width > 0 && new_height > 0) + { + gimp_item_scale (item, + new_width, new_height, + new_offset_x, new_offset_y, + interpolation, progress); + return TRUE; + } + + return FALSE; +} + +/** + * gimp_item_scale_by_origin: + * @item: The item to be transformed by width & height scale factors + * @new_width: The width that item will acquire + * @new_height: The height that the item will acquire + * @interpolation: + * @progress: + * @local_origin: sets fixed point of the scaling transform. See below. + * + * Sets item dimensions to new_width and + * new_height. Derives vertical and horizontal scaling + * transforms from new width and height. If local_origin is + * #TRUE, the fixed point of the scaling transform coincides + * with the item's center point. Otherwise, the fixed + * point is taken to be [-GimpItem::offset_x, -GimpItem::->offset_y]. + * + * Since this function derives scale factors from new and + * current item dimensions, these factors will vary from + * item to item because of aliasing artifacts; factor + * variations among items can be quite large where item + * dimensions approach pixel dimensions. Use + * gimp_item_scale_by_factors() where constant scales are to + * be uniformly applied to a number of items. + * + * Side effects: undo set created for item. + * Old item imagery scaled + * & painted to new item tiles + **/ +void +gimp_item_scale_by_origin (GimpItem *item, + gint new_width, + gint new_height, + GimpInterpolationType interpolation, + GimpProgress *progress, + gboolean local_origin) +{ + GimpItemPrivate *private; + gint new_offset_x, new_offset_y; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + private = GET_PRIVATE (item); + + if (new_width == 0 || new_height == 0) + { + g_warning ("%s: requested width or height equals zero", G_STRFUNC); + return; + } + + if (local_origin) + { + new_offset_x = (private->offset_x + + ((gimp_item_get_width (item) - new_width) / 2.0)); + new_offset_y = (private->offset_y + + ((gimp_item_get_height (item) - new_height) / 2.0)); + } + else + { + new_offset_x = (gint) (((gdouble) new_width * + (gdouble) private->offset_x / + (gdouble) gimp_item_get_width (item))); + + new_offset_y = (gint) (((gdouble) new_height * + (gdouble) private->offset_y / + (gdouble) gimp_item_get_height (item))); + } + + gimp_item_scale (item, + new_width, new_height, + new_offset_x, new_offset_y, + interpolation, progress); +} + +void +gimp_item_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GimpItemClass *item_class; + GimpImage *image; + gboolean push_undo; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (new_width < 1 || new_height < 1) + return; + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + push_undo = gimp_item_is_attached (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, + item_class->resize_desc); + + /* note that we call gimp_item_start_move(), and not + * gimp_item_start_transform(). whether or not a resize operation should be + * considered a transform operation, or a move operation, depends on the + * intended use of these functions by subclasses. atm, we only use + * gimp_item_{start,end}_transform() to suspend mask resizing in group + * layers, which should not happen when reisizing a group, hence the call to + * gimp_item_start_move(). + * + * see the comment in gimp_group_layer_resize() for more information. + */ + gimp_item_start_move (item, push_undo); + + g_object_freeze_notify (G_OBJECT (item)); + + item_class->resize (item, context, fill_type, + new_width, new_height, offset_x, offset_y); + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_item_end_move (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +void +gimp_item_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpItemClass *item_class; + GimpImage *image; + gboolean push_undo; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + push_undo = gimp_item_is_attached (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + item_class->flip_desc); + + gimp_item_start_transform (item, push_undo); + + g_object_freeze_notify (G_OBJECT (item)); + + item_class->flip (item, context, flip_type, axis, clip_result); + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_item_end_transform (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +void +gimp_item_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpItemClass *item_class; + GimpImage *image; + gboolean push_undo; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + push_undo = gimp_item_is_attached (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + item_class->rotate_desc); + + gimp_item_start_transform (item, push_undo); + + g_object_freeze_notify (G_OBJECT (item)); + + item_class->rotate (item, context, rotate_type, center_x, center_y, + clip_result); + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_item_end_transform (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +void +gimp_item_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpItemClass *item_class; + GimpImage *image; + gboolean push_undo; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (matrix != NULL); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + item_class = GIMP_ITEM_GET_CLASS (item); + image = gimp_item_get_image (item); + + push_undo = gimp_item_is_attached (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, + item_class->transform_desc); + + gimp_item_start_transform (item, push_undo); + + g_object_freeze_notify (G_OBJECT (item)); + + item_class->transform (item, context, matrix, direction, interpolation, + clip_result, progress); + + g_object_thaw_notify (G_OBJECT (item)); + + gimp_item_end_transform (item, push_undo); + + if (push_undo) + gimp_image_undo_group_end (image); +} + +GimpTransformResize +gimp_item_get_clip (GimpItem *item, + GimpTransformResize clip_result) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), GIMP_TRANSFORM_RESIZE_ADJUST); + + return GIMP_ITEM_GET_CLASS (item)->get_clip (item, clip_result); +} + +gboolean +gimp_item_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpItemClass *item_class; + gboolean retval = FALSE; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_is_attached (item), FALSE); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (fill_options), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item_class = GIMP_ITEM_GET_CLASS (item); + + if (item_class->fill) + { + GimpImage *image = gimp_item_get_image (item); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_PAINT, + item_class->fill_desc); + + retval = item_class->fill (item, drawable, fill_options, push_undo, + progress, error); + + if (push_undo) + gimp_image_undo_group_end (image); + } + + return retval; +} + +gboolean +gimp_item_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpStrokeOptions *stroke_options, + GimpPaintOptions *paint_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpItemClass *item_class; + gboolean retval = FALSE; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_is_attached (item), FALSE); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (stroke_options), FALSE); + g_return_val_if_fail (paint_options == NULL || + GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item_class = GIMP_ITEM_GET_CLASS (item); + + if (item_class->stroke) + { + GimpImage *image = gimp_item_get_image (item); + + gimp_stroke_options_prepare (stroke_options, context, paint_options); + + if (push_undo) + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_PAINT, + item_class->stroke_desc); + + retval = item_class->stroke (item, drawable, stroke_options, push_undo, + progress, error); + + if (push_undo) + gimp_image_undo_group_end (image); + + gimp_stroke_options_finish (stroke_options); + } + + return retval; +} + +void +gimp_item_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpItemClass *item_class; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + + item_class = GIMP_ITEM_GET_CLASS (item); + + if (item_class->to_selection) + item_class->to_selection (item, op, antialias, + feather, feather_radius_x, feather_radius_y); +} + +void +gimp_item_add_offset_node (GimpItem *item, + GeglNode *node) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GEGL_IS_NODE (node)); + + private = GET_PRIVATE (item); + + g_return_if_fail (g_list_find (private->offset_nodes, node) == NULL); + + gegl_node_set (node, + "x", (gdouble) private->offset_x, + "y", (gdouble) private->offset_y, + NULL); + + private->offset_nodes = g_list_append (private->offset_nodes, + g_object_ref (node)); +} + +void +gimp_item_remove_offset_node (GimpItem *item, + GeglNode *node) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GEGL_IS_NODE (node)); + + private = GET_PRIVATE (item); + + g_return_if_fail (g_list_find (private->offset_nodes, node) != NULL); + + private->offset_nodes = g_list_remove (private->offset_nodes, node); + g_object_unref (node); +} + +gint +gimp_item_get_ID (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), -1); + + return GET_PRIVATE (item)->ID; +} + +GimpItem * +gimp_item_get_by_ID (Gimp *gimp, + gint item_id) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp->item_table == NULL) + return NULL; + + return (GimpItem *) gimp_id_table_lookup (gimp->item_table, item_id); +} + +GimpTattoo +gimp_item_get_tattoo (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), 0); + + return GET_PRIVATE (item)->tattoo; +} + +void +gimp_item_set_tattoo (GimpItem *item, + GimpTattoo tattoo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + GET_PRIVATE (item)->tattoo = tattoo; +} + +GimpImage * +gimp_item_get_image (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + return GET_PRIVATE (item)->image; +} + +void +gimp_item_set_image (GimpItem *item, + GimpImage *image) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (! gimp_item_is_attached (item)); + g_return_if_fail (! gimp_item_is_removed (item)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GET_PRIVATE (item); + + if (image == private->image) + return; + + g_object_freeze_notify (G_OBJECT (item)); + + if (private->ID == 0) + { + private->ID = gimp_id_table_insert (image->gimp->item_table, item); + + g_object_notify (G_OBJECT (item), "id"); + } + + if (private->tattoo == 0 || private->image != image) + { + private->tattoo = gimp_image_get_new_tattoo (image); + } + + private->image = image; + g_object_notify (G_OBJECT (item), "image"); + + g_object_thaw_notify (G_OBJECT (item)); +} + +/** + * gimp_item_replace_item: + * @item: a newly allocated #GimpItem + * @replace: the #GimpItem to be replaced by @item + * + * This function shouly only be called right after @item has been + * newly allocated. + * + * Replaces @replace by @item, as far as possible within the #GimpItem + * class. The new @item takes over @replace's ID, tattoo, offset, size + * etc. and all these properties are set to %NULL on @replace. + * + * This function *only* exists to allow subclasses to do evil hacks + * like in XCF text layer loading. Don't ever use this function if you + * are not sure. + * + * After this function returns, @replace has become completely + * unusable, should only be used to steal everything it has (like its + * drawable properties if it's a drawable), and then be destroyed. + **/ +void +gimp_item_replace_item (GimpItem *item, + GimpItem *replace) +{ + GimpItemPrivate *private; + gint offset_x; + gint offset_y; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (! gimp_item_is_attached (item)); + g_return_if_fail (! gimp_item_is_removed (item)); + g_return_if_fail (GIMP_IS_ITEM (replace)); + + private = GET_PRIVATE (item); + + gimp_object_set_name (GIMP_OBJECT (item), gimp_object_get_name (replace)); + + if (private->ID) + gimp_id_table_remove (gimp_item_get_image (item)->gimp->item_table, + gimp_item_get_ID (item)); + + private->ID = gimp_item_get_ID (replace); + gimp_id_table_replace (gimp_item_get_image (item)->gimp->item_table, + gimp_item_get_ID (item), + item); + + /* Set image before tattoo so that the explicitly set tattoo overrides + * the one implicitly set when setting the image + */ + gimp_item_set_image (item, gimp_item_get_image (replace)); + GET_PRIVATE (replace)->image = NULL; + + gimp_item_set_tattoo (item, gimp_item_get_tattoo (replace)); + gimp_item_set_tattoo (replace, 0); + + g_object_unref (private->parasites); + private->parasites = GET_PRIVATE (replace)->parasites; + GET_PRIVATE (replace)->parasites = NULL; + + gimp_item_get_offset (replace, &offset_x, &offset_y); + gimp_item_set_offset (item, offset_x, offset_y); + + gimp_item_set_size (item, + gimp_item_get_width (replace), + gimp_item_get_height (replace)); + + gimp_item_set_visible (item, gimp_item_get_visible (replace), FALSE); + gimp_item_set_linked (item, gimp_item_get_linked (replace), FALSE); + gimp_item_set_color_tag (item, gimp_item_get_color_tag (replace), FALSE); + gimp_item_set_lock_content (item, gimp_item_get_lock_content (replace), FALSE); + gimp_item_set_lock_position (item, gimp_item_get_lock_position (replace), FALSE); +} + +/** + * gimp_item_set_parasites: + * @item: a #GimpItem + * @parasites: a #GimpParasiteList + * + * Set an @item's #GimpParasiteList. It's usually never needed to + * fiddle with an item's parasite list directly. This function exists + * for special purposes only, like when creating items from unusual + * sources. + **/ +void +gimp_item_set_parasites (GimpItem *item, + GimpParasiteList *parasites) +{ + GimpItemPrivate *private; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (GIMP_IS_PARASITE_LIST (parasites)); + + private = GET_PRIVATE (item); + + g_set_object (&private->parasites, parasites); +} + +/** + * gimp_item_get_parasites: + * @item: a #GimpItem + * + * Get an @item's #GimpParasiteList. It's usually never needed to + * fiddle with an item's parasite list directly. This function exists + * for special purposes only, like when saving an item to XCF. + * + * Return value: The @item's #GimpParasiteList. + **/ +GimpParasiteList * +gimp_item_get_parasites (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + return GET_PRIVATE (item)->parasites; +} + +gboolean +gimp_item_parasite_validate (GimpItem *item, + const GimpParasite *parasite, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (parasite != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return TRUE; +} + +void +gimp_item_parasite_attach (GimpItem *item, + const GimpParasite *parasite, + gboolean push_undo) +{ + GimpItemPrivate *private; + GimpParasite copy; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (parasite != NULL); + + private = GET_PRIVATE (item); + + /* make a temporary copy of the GimpParasite struct because + * gimp_parasite_shift_parent() changes it + */ + copy = *parasite; + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + { + /* only set the dirty bit manually if we can be saved and the new + * parasite differs from the current one and we aren't undoable + */ + if (gimp_parasite_is_undoable (©)) + { + /* do a group in case we have attach_parent set */ + gimp_image_undo_group_start (private->image, + GIMP_UNDO_GROUP_PARASITE_ATTACH, + C_("undo-type", "Attach Parasite")); + + gimp_image_undo_push_item_parasite (private->image, NULL, item, ©); + } + else if (gimp_parasite_is_persistent (©) && + ! gimp_parasite_compare (©, + gimp_item_parasite_find + (item, gimp_parasite_name (©)))) + { + gimp_image_undo_push_cantundo (private->image, + C_("undo-type", "Attach Parasite to Item")); + } + } + + gimp_parasite_list_add (private->parasites, ©); + + if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT)) + { + gimp_parasite_shift_parent (©); + gimp_image_parasite_attach (private->image, ©, TRUE); + } + else if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_GRANDPARENT)) + { + gimp_parasite_shift_parent (©); + gimp_parasite_shift_parent (©); + gimp_parasite_attach (private->image->gimp, ©); + } + + if (gimp_item_is_attached (item) && + gimp_parasite_is_undoable (©)) + { + gimp_image_undo_group_end (private->image); + } +} + +void +gimp_item_parasite_detach (GimpItem *item, + const gchar *name, + gboolean push_undo) +{ + GimpItemPrivate *private; + const GimpParasite *parasite; + + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (name != NULL); + + private = GET_PRIVATE (item); + + parasite = gimp_parasite_list_find (private->parasites, name); + + if (! parasite) + return; + + if (! gimp_item_is_attached (item)) + push_undo = FALSE; + + if (push_undo) + { + if (gimp_parasite_is_undoable (parasite)) + { + gimp_image_undo_push_item_parasite_remove (private->image, + C_("undo-type", "Remove Parasite from Item"), + item, + gimp_parasite_name (parasite)); + } + else if (gimp_parasite_is_persistent (parasite)) + { + gimp_image_undo_push_cantundo (private->image, + C_("undo-type", "Remove Parasite from Item")); + } + } + + gimp_parasite_list_remove (private->parasites, name); +} + +const GimpParasite * +gimp_item_parasite_find (GimpItem *item, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + + return gimp_parasite_list_find (GET_PRIVATE (item)->parasites, name); +} + +static void +gimp_item_parasite_list_foreach_func (gchar *name, + GimpParasite *parasite, + gchar ***cur) +{ + *(*cur)++ = (gchar *) g_strdup (name); +} + +gchar ** +gimp_item_parasite_list (GimpItem *item, + gint *count) +{ + GimpItemPrivate *private; + gchar **list; + gchar **cur; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (count != NULL, NULL); + + private = GET_PRIVATE (item); + + *count = gimp_parasite_list_length (private->parasites); + + cur = list = g_new (gchar *, *count); + + gimp_parasite_list_foreach (private->parasites, + (GHFunc) gimp_item_parasite_list_foreach_func, + &cur); + + return list; +} + +void +gimp_item_set_visible (GimpItem *item, + gboolean visible, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + visible = visible ? TRUE : FALSE; + + if (gimp_item_get_visible (item) != visible) + { + if (push_undo && gimp_item_is_attached (item)) + { + GimpImage *image = gimp_item_get_image (item); + + if (image) + gimp_image_undo_push_item_visibility (image, NULL, item); + } + + GET_PRIVATE (item)->visible = visible; + + if (GET_PRIVATE (item)->bind_visible_to_active) + gimp_filter_set_active (GIMP_FILTER (item), visible); + + g_signal_emit (item, gimp_item_signals[VISIBILITY_CHANGED], 0); + + g_object_notify (G_OBJECT (item), "visible"); + } +} + +gboolean +gimp_item_get_visible (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GET_PRIVATE (item)->visible; +} + +gboolean +gimp_item_is_visible (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + if (gimp_item_get_visible (item)) + { + GimpItem *parent; + + parent = GIMP_ITEM (gimp_viewable_get_parent (GIMP_VIEWABLE (item))); + + if (parent) + return gimp_item_is_visible (parent); + + return TRUE; + } + + return FALSE; +} + +void +gimp_item_bind_visible_to_active (GimpItem *item, + gboolean bind) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + GET_PRIVATE (item)->bind_visible_to_active = bind; + + if (bind) + gimp_filter_set_active (GIMP_FILTER (item), gimp_item_get_visible (item)); +} + +void +gimp_item_set_linked (GimpItem *item, + gboolean linked, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + linked = linked ? TRUE : FALSE; + + if (gimp_item_get_linked (item) != linked) + { + GimpImage *image = gimp_item_get_image (item); + gboolean is_attached = gimp_item_is_attached (item); + + if (push_undo && is_attached && image) + gimp_image_undo_push_item_linked (image, NULL, item); + + GET_PRIVATE (item)->linked = linked; + + g_signal_emit (item, gimp_item_signals[LINKED_CHANGED], 0); + + if (is_attached && image) + gimp_image_linked_items_changed (image); + + g_object_notify (G_OBJECT (item), "linked"); + } +} + +gboolean +gimp_item_get_linked (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GET_PRIVATE (item)->linked; +} + +void +gimp_item_set_color_tag (GimpItem *item, + GimpColorTag color_tag, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + + if (gimp_item_get_color_tag (item) != color_tag) + { + if (push_undo && gimp_item_is_attached (item)) + { + GimpImage *image = gimp_item_get_image (item); + + if (image) + gimp_image_undo_push_item_color_tag (image, NULL, item); + } + + GET_PRIVATE (item)->color_tag = color_tag; + + g_signal_emit (item, gimp_item_signals[COLOR_TAG_CHANGED], 0); + + g_object_notify (G_OBJECT (item), "color-tag"); + } +} + +GimpColorTag +gimp_item_get_color_tag (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), GIMP_COLOR_TAG_NONE); + + return GET_PRIVATE (item)->color_tag; +} + +GimpColorTag +gimp_item_get_merged_color_tag (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), GIMP_COLOR_TAG_NONE); + + if (gimp_item_get_color_tag (item) == GIMP_COLOR_TAG_NONE) + { + GimpItem *parent; + + parent = GIMP_ITEM (gimp_viewable_get_parent (GIMP_VIEWABLE (item))); + + if (parent) + return gimp_item_get_merged_color_tag (parent); + } + + return gimp_item_get_color_tag (item); +} + +void +gimp_item_set_lock_content (GimpItem *item, + gboolean lock_content, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_can_lock_content (item)); + + lock_content = lock_content ? TRUE : FALSE; + + if (gimp_item_get_lock_content (item) != lock_content) + { + if (push_undo && gimp_item_is_attached (item)) + { + /* Right now I don't think this should be pushed. */ +#if 0 + GimpImage *image = gimp_item_get_image (item); + + gimp_image_undo_push_item_lock_content (image, NULL, item); +#endif + } + + GET_PRIVATE (item)->lock_content = lock_content; + + g_signal_emit (item, gimp_item_signals[LOCK_CONTENT_CHANGED], 0); + + g_object_notify (G_OBJECT (item), "lock-content"); + } +} + +gboolean +gimp_item_get_lock_content (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GET_PRIVATE (item)->lock_content; +} + +gboolean +gimp_item_can_lock_content (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return TRUE; +} + +gboolean +gimp_item_is_content_locked (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GIMP_ITEM_GET_CLASS (item)->is_content_locked (item); +} + +void +gimp_item_set_lock_position (GimpItem *item, + gboolean lock_position, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_can_lock_position (item)); + + lock_position = lock_position ? TRUE : FALSE; + + if (gimp_item_get_lock_position (item) != lock_position) + { + if (push_undo && gimp_item_is_attached (item)) + { + GimpImage *image = gimp_item_get_image (item); + + gimp_image_undo_push_item_lock_position (image, NULL, item); + } + + GET_PRIVATE (item)->lock_position = lock_position; + + g_signal_emit (item, gimp_item_signals[LOCK_POSITION_CHANGED], 0); + + g_object_notify (G_OBJECT (item), "lock-position"); + } +} + +gboolean +gimp_item_get_lock_position (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GET_PRIVATE (item)->lock_position; +} + +gboolean +gimp_item_can_lock_position (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (item))) + return FALSE; + + return TRUE; +} + +gboolean +gimp_item_is_position_locked (GimpItem *item) +{ + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + return GIMP_ITEM_GET_CLASS (item)->is_position_locked (item); +} + +gboolean +gimp_item_mask_bounds (GimpItem *item, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + GimpImage *image; + GimpChannel *selection; + gint x, y, width, height; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_is_attached (item), FALSE); + + image = gimp_item_get_image (item); + selection = gimp_image_get_mask (image); + + /* check for is_empty() before intersecting so we ignore the + * selection if it is suspended (like when stroking) + */ + if (GIMP_ITEM (selection) != item && + ! gimp_channel_is_empty (selection) && + gimp_item_bounds (GIMP_ITEM (selection), &x, &y, &width, &height)) + { + gint off_x, off_y; + gint x2, y2; + + gimp_item_get_offset (item, &off_x, &off_y); + + x2 = x + width; + y2 = y + height; + + x = CLAMP (x - off_x, 0, gimp_item_get_width (item)); + y = CLAMP (y - off_y, 0, gimp_item_get_height (item)); + x2 = CLAMP (x2 - off_x, 0, gimp_item_get_width (item)); + y2 = CLAMP (y2 - off_y, 0, gimp_item_get_height (item)); + + width = x2 - x; + height = y2 - y; + + retval = TRUE; + } + else + { + x = 0; + y = 0; + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + + retval = FALSE; + } + + if (x1) *x1 = x; + if (y1) *y1 = y; + if (x2) *x2 = x + width; + if (y2) *y2 = y + height; + + return retval; +} + +/** + * gimp_item_mask_intersect: + * @item: a #GimpItem + * @x: return location for x + * @y: return location for y + * @width: return location for the width + * @height: return location for the height + * + * Intersect the area of the @item and its image's selection mask. + * The computed area is the bounding box of he selection within the + * item. + **/ +gboolean +gimp_item_mask_intersect (GimpItem *item, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GimpImage *image; + GimpChannel *selection; + gint tmp_x, tmp_y; + gint tmp_width, tmp_height; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + g_return_val_if_fail (gimp_item_is_attached (item), FALSE); + + image = gimp_item_get_image (item); + selection = gimp_image_get_mask (image); + + /* check for is_empty() before intersecting so we ignore the + * selection if it is suspended (like when stroking) + */ + if (GIMP_ITEM (selection) != item && + ! gimp_channel_is_empty (selection) && + gimp_item_bounds (GIMP_ITEM (selection), + &tmp_x, &tmp_y, &tmp_width, &tmp_height)) + { + gint off_x, off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + + retval = gimp_rectangle_intersect (tmp_x - off_x, tmp_y - off_y, + tmp_width, tmp_height, + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item), + &tmp_x, &tmp_y, + &tmp_width, &tmp_height); + } + else + { + tmp_x = 0; + tmp_y = 0; + tmp_width = gimp_item_get_width (item); + tmp_height = gimp_item_get_height (item); + + retval = TRUE; + } + + if (x) *x = tmp_x; + if (y) *y = tmp_y; + if (width) *width = tmp_width; + if (height) *height = tmp_height; + + return retval; +} + +gboolean +gimp_item_is_in_set (GimpItem *item, + GimpItemSet set) +{ + GimpItemPrivate *private; + + g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); + + private = GET_PRIVATE (item); + + switch (set) + { + case GIMP_ITEM_SET_NONE: + return FALSE; + + case GIMP_ITEM_SET_ALL: + return TRUE; + + case GIMP_ITEM_SET_IMAGE_SIZED: + return (gimp_item_get_width (item) == gimp_image_get_width (private->image) && + gimp_item_get_height (item) == gimp_image_get_height (private->image)); + + case GIMP_ITEM_SET_VISIBLE: + return gimp_item_get_visible (item); + + case GIMP_ITEM_SET_LINKED: + return gimp_item_get_linked (item); + } + + return FALSE; +} diff --git a/app/core/gimpitem.h b/app/core/gimpitem.h new file mode 100644 index 0000000..6bc56e3 --- /dev/null +++ b/app/core/gimpitem.h @@ -0,0 +1,405 @@ +/* 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 . + */ + +#ifndef __GIMP_ITEM_H__ +#define __GIMP_ITEM_H__ + + +#include "gimpfilter.h" + + +#define GIMP_TYPE_ITEM (gimp_item_get_type ()) +#define GIMP_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM, GimpItem)) +#define GIMP_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM, GimpItemClass)) +#define GIMP_IS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM)) +#define GIMP_IS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM)) +#define GIMP_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ITEM, GimpItemClass)) + + +typedef struct _GimpItemClass GimpItemClass; + +struct _GimpItem +{ + GimpFilter parent_instance; +}; + +struct _GimpItemClass +{ + GimpFilterClass parent_class; + + /* signals */ + void (* removed) (GimpItem *item); + void (* visibility_changed) (GimpItem *item); + void (* linked_changed) (GimpItem *item); + void (* color_tag_changed) (GimpItem *item); + void (* lock_content_changed) (GimpItem *item); + void (* lock_position_changed) (GimpItem *item); + + /* virtual functions */ + void (* unset_removed) (GimpItem *item); + gboolean (* is_attached) (GimpItem *item); + gboolean (* is_content_locked) (GimpItem *item); + gboolean (* is_position_locked) (GimpItem *item); + GimpItemTree * (* get_tree) (GimpItem *item); + gboolean (* bounds) (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height); + GimpItem * (* duplicate) (GimpItem *item, + GType new_type); + void (* convert) (GimpItem *item, + GimpImage *dest_image, + GType old_type); + gboolean (* rename) (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error); + void (* start_move) (GimpItem *item, + gboolean push_undo); + void (* end_move) (GimpItem *item, + gboolean push_undo); + void (* start_transform) (GimpItem *item, + gboolean push_undo); + void (* end_transform) (GimpItem *item, + gboolean push_undo); + void (* translate) (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo); + void (* scale) (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress); + void (* resize) (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); + void (* flip) (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); + void (* rotate) (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); + void (* transform) (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + GimpTransformResize (* get_clip) (GimpItem *item, + GimpTransformResize clip_result); + gboolean (* fill) (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); + gboolean (* stroke) (GimpItem *item, + GimpDrawable *drawable, + GimpStrokeOptions *stroke_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); + void (* to_selection) (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + + const gchar *default_name; + const gchar *rename_desc; + const gchar *translate_desc; + const gchar *scale_desc; + const gchar *resize_desc; + const gchar *flip_desc; + const gchar *rotate_desc; + const gchar *transform_desc; + const gchar *to_selection_desc; + const gchar *fill_desc; + const gchar *stroke_desc; + + const gchar *reorder_desc; + const gchar *raise_desc; + const gchar *raise_to_top_desc; + const gchar *lower_desc; + const gchar *lower_to_bottom_desc; + + const gchar *raise_failed; + const gchar *lower_failed; +}; + + +GType gimp_item_get_type (void) G_GNUC_CONST; + +GimpItem * gimp_item_new (GType type, + GimpImage *image, + const gchar *name, + gint offset_x, + gint offset_y, + gint width, + gint height); + +void gimp_item_removed (GimpItem *item); +gboolean gimp_item_is_removed (GimpItem *item); +void gimp_item_unset_removed (GimpItem *item); + +gboolean gimp_item_is_attached (GimpItem *item); + +GimpItem * gimp_item_get_parent (GimpItem *item); + +GimpItemTree * gimp_item_get_tree (GimpItem *item); +GimpContainer * gimp_item_get_container (GimpItem *item); +GList * gimp_item_get_container_iter (GimpItem *item); +gint gimp_item_get_index (GimpItem *item); +GList * gimp_item_get_path (GimpItem *item); + +gboolean gimp_item_bounds (GimpItem *item, + gint *x, + gint *y, + gint *width, + gint *height); +gboolean gimp_item_bounds_f (GimpItem *item, + gdouble *x, + gdouble *y, + gdouble *width, + gdouble *height); + +GimpItem * gimp_item_duplicate (GimpItem *item, + GType new_type); +GimpItem * gimp_item_convert (GimpItem *item, + GimpImage *dest_image, + GType new_type); + +gboolean gimp_item_rename (GimpItem *item, + const gchar *new_name, + GError **error); + +gint gimp_item_get_width (GimpItem *item); +gint gimp_item_get_height (GimpItem *item); +void gimp_item_set_size (GimpItem *item, + gint width, + gint height); + +void gimp_item_get_offset (GimpItem *item, + gint *offset_x, + gint *offset_y); +void gimp_item_set_offset (GimpItem *item, + gint offset_x, + gint offset_y); +gint gimp_item_get_offset_x (GimpItem *item); +gint gimp_item_get_offset_y (GimpItem *item); + +void gimp_item_start_move (GimpItem *item, + gboolean push_undo); +void gimp_item_end_move (GimpItem *item, + gboolean push_undo); + +void gimp_item_start_transform (GimpItem *item, + gboolean push_undo); +void gimp_item_end_transform (GimpItem *item, + gboolean push_undo); + +void gimp_item_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo); + +gboolean gimp_item_check_scaling (GimpItem *item, + gint new_width, + gint new_height); +void gimp_item_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation, + GimpProgress *progress); +gboolean gimp_item_scale_by_factors (GimpItem *item, + gdouble w_factor, + gdouble h_factor, + GimpInterpolationType interpolation, + GimpProgress *progress); +gboolean + gimp_item_scale_by_factors_with_origin (GimpItem *item, + gdouble w_factor, + gdouble h_factor, + gint origin_x, + gint origin_y, + gint new_origin_x, + gint new_origin_y, + GimpInterpolationType interpolation, + GimpProgress *progress); +void gimp_item_scale_by_origin (GimpItem *item, + gint new_width, + gint new_height, + GimpInterpolationType interpolation, + GimpProgress *progress, + gboolean local_origin); +void gimp_item_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +void gimp_item_resize_to_image (GimpItem *item); + +void gimp_item_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +void gimp_item_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +void gimp_item_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); +GimpTransformResize gimp_item_get_clip (GimpItem *item, + GimpTransformResize clip_result); + +gboolean gimp_item_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); +gboolean gimp_item_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpStrokeOptions *stroke_options, + GimpPaintOptions *paint_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); + +void gimp_item_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + +void gimp_item_add_offset_node (GimpItem *item, + GeglNode *node); +void gimp_item_remove_offset_node (GimpItem *item, + GeglNode *node); + +gint gimp_item_get_ID (GimpItem *item); +GimpItem * gimp_item_get_by_ID (Gimp *gimp, + gint id); + +GimpTattoo gimp_item_get_tattoo (GimpItem *item); +void gimp_item_set_tattoo (GimpItem *item, + GimpTattoo tattoo); + +GimpImage * gimp_item_get_image (GimpItem *item); +void gimp_item_set_image (GimpItem *item, + GimpImage *image); + +void gimp_item_replace_item (GimpItem *item, + GimpItem *replace); + +void gimp_item_set_parasites (GimpItem *item, + GimpParasiteList *parasites); +GimpParasiteList * gimp_item_get_parasites (GimpItem *item); + +gboolean gimp_item_parasite_validate (GimpItem *item, + const GimpParasite *parasite, + GError **error); +void gimp_item_parasite_attach (GimpItem *item, + const GimpParasite *parasite, + gboolean push_undo); +void gimp_item_parasite_detach (GimpItem *item, + const gchar *name, + gboolean push_undo); +const GimpParasite * gimp_item_parasite_find (GimpItem *item, + const gchar *name); +gchar ** gimp_item_parasite_list (GimpItem *item, + gint *count); + +void gimp_item_set_visible (GimpItem *item, + gboolean visible, + gboolean push_undo); +gboolean gimp_item_get_visible (GimpItem *item); +gboolean gimp_item_is_visible (GimpItem *item); + +void gimp_item_bind_visible_to_active (GimpItem *item, + gboolean bind); + +void gimp_item_set_linked (GimpItem *item, + gboolean linked, + gboolean push_undo); +gboolean gimp_item_get_linked (GimpItem *item); + +void gimp_item_set_color_tag (GimpItem *item, + GimpColorTag color_tag, + gboolean push_undo); +GimpColorTag gimp_item_get_color_tag (GimpItem *item); +GimpColorTag gimp_item_get_merged_color_tag (GimpItem *item); + +void gimp_item_set_lock_content (GimpItem *item, + gboolean lock_content, + gboolean push_undo); +gboolean gimp_item_get_lock_content (GimpItem *item); +gboolean gimp_item_can_lock_content (GimpItem *item); +gboolean gimp_item_is_content_locked (GimpItem *item); + +void gimp_item_set_lock_position (GimpItem *item, + gboolean lock_position, + gboolean push_undo); +gboolean gimp_item_get_lock_position (GimpItem *item); +gboolean gimp_item_can_lock_position (GimpItem *item); +gboolean gimp_item_is_position_locked (GimpItem *item); + +gboolean gimp_item_mask_bounds (GimpItem *item, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +gboolean gimp_item_mask_intersect (GimpItem *item, + gint *x, + gint *y, + gint *width, + gint *height); + +gboolean gimp_item_is_in_set (GimpItem *item, + GimpItemSet set); + + +#endif /* __GIMP_ITEM_H__ */ diff --git a/app/core/gimpitempropundo.c b/app/core/gimpitempropundo.c new file mode 100644 index 0000000..241b8aa --- /dev/null +++ b/app/core/gimpitempropundo.c @@ -0,0 +1,358 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpitem.h" +#include "gimpitemtree.h" +#include "gimpitempropundo.h" +#include "gimpparasitelist.h" + + +enum +{ + PROP_0, + PROP_PARASITE_NAME +}; + + +static void gimp_item_prop_undo_constructed (GObject *object); +static void gimp_item_prop_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_item_prop_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_item_prop_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_item_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_item_prop_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpItemPropUndo, gimp_item_prop_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_item_prop_undo_parent_class + + +static void +gimp_item_prop_undo_class_init (GimpItemPropUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_item_prop_undo_constructed; + object_class->set_property = gimp_item_prop_undo_set_property; + object_class->get_property = gimp_item_prop_undo_get_property; + + gimp_object_class->get_memsize = gimp_item_prop_undo_get_memsize; + + undo_class->pop = gimp_item_prop_undo_pop; + undo_class->free = gimp_item_prop_undo_free; + + g_object_class_install_property (object_class, PROP_PARASITE_NAME, + g_param_spec_string ("parasite-name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_item_prop_undo_init (GimpItemPropUndo *undo) +{ +} + +static void +gimp_item_prop_undo_constructed (GObject *object) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (object); + GimpItem *item; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + item = GIMP_ITEM_UNDO (object)->item; + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_ITEM_REORDER: + item_prop_undo->parent = gimp_item_get_parent (item); + item_prop_undo->position = gimp_item_get_index (item); + break; + + case GIMP_UNDO_ITEM_RENAME: + item_prop_undo->name = g_strdup (gimp_object_get_name (item)); + break; + + case GIMP_UNDO_ITEM_DISPLACE: + gimp_item_get_offset (item, + &item_prop_undo->offset_x, + &item_prop_undo->offset_y); + break; + + case GIMP_UNDO_ITEM_VISIBILITY: + item_prop_undo->visible = gimp_item_get_visible (item); + break; + + case GIMP_UNDO_ITEM_LINKED: + item_prop_undo->linked = gimp_item_get_linked (item); + break; + + case GIMP_UNDO_ITEM_COLOR_TAG: + item_prop_undo->color_tag = gimp_item_get_color_tag (item); + break; + + case GIMP_UNDO_ITEM_LOCK_CONTENT: + item_prop_undo->lock_content = gimp_item_get_lock_content (item); + break; + + case GIMP_UNDO_ITEM_LOCK_POSITION: + item_prop_undo->lock_position = gimp_item_get_lock_position (item); + break; + + case GIMP_UNDO_PARASITE_ATTACH: + case GIMP_UNDO_PARASITE_REMOVE: + gimp_assert (item_prop_undo->parasite_name != NULL); + + item_prop_undo->parasite = gimp_parasite_copy + (gimp_item_parasite_find (item, item_prop_undo->parasite_name)); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_item_prop_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (object); + + switch (property_id) + { + case PROP_PARASITE_NAME: + item_prop_undo->parasite_name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_item_prop_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (object); + + switch (property_id) + { + case PROP_PARASITE_NAME: + g_value_set_string (value, item_prop_undo->parasite_name); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_item_prop_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (object); + gint64 memsize = 0; + + memsize += gimp_string_get_memsize (item_prop_undo->name); + memsize += gimp_string_get_memsize (item_prop_undo->parasite_name); + memsize += gimp_parasite_get_memsize (item_prop_undo->parasite, NULL); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_item_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (undo); + GimpItem *item = GIMP_ITEM_UNDO (undo)->item; + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_ITEM_REORDER: + { + GimpItem *parent; + gint position; + + parent = gimp_item_get_parent (item); + position = gimp_item_get_index (item); + + gimp_item_tree_reorder_item (gimp_item_get_tree (item), item, + item_prop_undo->parent, + item_prop_undo->position, + FALSE, NULL); + + item_prop_undo->parent = parent; + item_prop_undo->position = position; + } + break; + + case GIMP_UNDO_ITEM_RENAME: + { + gchar *name; + + name = g_strdup (gimp_object_get_name (item)); + + gimp_item_tree_rename_item (gimp_item_get_tree (item), item, + item_prop_undo->name, + FALSE, NULL); + + g_free (item_prop_undo->name); + item_prop_undo->name = name; + } + break; + + case GIMP_UNDO_ITEM_DISPLACE: + { + gint offset_x; + gint offset_y; + + gimp_item_get_offset (item, &offset_x, &offset_y); + + gimp_item_translate (item, + item_prop_undo->offset_x - offset_x, + item_prop_undo->offset_y - offset_y, + FALSE); + + item_prop_undo->offset_x = offset_x; + item_prop_undo->offset_y = offset_y; + } + break; + + case GIMP_UNDO_ITEM_VISIBILITY: + { + gboolean visible; + + visible = gimp_item_get_visible (item); + gimp_item_set_visible (item, item_prop_undo->visible, FALSE); + item_prop_undo->visible = visible; + } + break; + + case GIMP_UNDO_ITEM_LINKED: + { + gboolean linked; + + linked = gimp_item_get_linked (item); + gimp_item_set_linked (item, item_prop_undo->linked, FALSE); + item_prop_undo->linked = linked; + } + break; + + case GIMP_UNDO_ITEM_COLOR_TAG: + { + GimpColorTag color_tag; + + color_tag = gimp_item_get_color_tag (item); + gimp_item_set_color_tag (item, item_prop_undo->color_tag, FALSE); + item_prop_undo->color_tag = color_tag; + } + break; + + case GIMP_UNDO_ITEM_LOCK_CONTENT: + { + gboolean lock_content; + + lock_content = gimp_item_get_lock_content (item); + gimp_item_set_lock_content (item, item_prop_undo->lock_content, FALSE); + item_prop_undo->lock_content = lock_content; + } + break; + + case GIMP_UNDO_ITEM_LOCK_POSITION: + { + gboolean lock_position; + + lock_position = gimp_item_get_lock_position (item); + gimp_item_set_lock_position (item, item_prop_undo->lock_position, FALSE); + item_prop_undo->lock_position = lock_position; + } + break; + + case GIMP_UNDO_PARASITE_ATTACH: + case GIMP_UNDO_PARASITE_REMOVE: + { + GimpParasite *parasite; + + parasite = item_prop_undo->parasite; + + item_prop_undo->parasite = gimp_parasite_copy + (gimp_item_parasite_find (item, item_prop_undo->parasite_name)); + + if (parasite) + gimp_item_parasite_attach (item, parasite, FALSE); + else + gimp_item_parasite_detach (item, item_prop_undo->parasite_name, FALSE); + + if (parasite) + gimp_parasite_free (parasite); + } + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_item_prop_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (undo); + + g_clear_pointer (&item_prop_undo->name, g_free); + g_clear_pointer (&item_prop_undo->parasite_name, g_free); + g_clear_pointer (&item_prop_undo->parasite, gimp_parasite_free); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpitempropundo.h b/app/core/gimpitempropundo.h new file mode 100644 index 0000000..8f96e46 --- /dev/null +++ b/app/core/gimpitempropundo.h @@ -0,0 +1,63 @@ +/* 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 . + */ + +#ifndef __GIMP_ITEM_PROP_UNDO_H__ +#define __GIMP_ITEM_PROP_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_ITEM_PROP_UNDO (gimp_item_prop_undo_get_type ()) +#define GIMP_ITEM_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_PROP_UNDO, GimpItemPropUndo)) +#define GIMP_ITEM_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_PROP_UNDO, GimpItemPropUndoClass)) +#define GIMP_IS_ITEM_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_PROP_UNDO)) +#define GIMP_IS_ITEM_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_PROP_UNDO)) +#define GIMP_ITEM_PROP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ITEM_PROP_UNDO, GimpItemPropUndoClass)) + + +typedef struct _GimpItemPropUndo GimpItemPropUndo; +typedef struct _GimpItemPropUndoClass GimpItemPropUndoClass; + +struct _GimpItemPropUndo +{ + GimpItemUndo parent_instance; + + GimpItem *parent; + gint position; + gchar *name; + gint offset_x; + gint offset_y; + guint visible : 1; + guint linked : 1; + guint lock_content : 1; + guint lock_position : 1; + GimpColorTag color_tag; + gchar *parasite_name; + GimpParasite *parasite; +}; + +struct _GimpItemPropUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_item_prop_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_ITEM_PROP_UNDO_H__ */ diff --git a/app/core/gimpitemstack.c b/app/core/gimpitemstack.c new file mode 100644 index 0000000..c7f2fe1 --- /dev/null +++ b/app/core/gimpitemstack.c @@ -0,0 +1,348 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpitemstack.c + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimpitem.h" +#include "gimpitemstack.h" + + +/* local function prototypes */ + +static void gimp_item_stack_constructed (GObject *object); + +static void gimp_item_stack_add (GimpContainer *container, + GimpObject *object); +static void gimp_item_stack_remove (GimpContainer *container, + GimpObject *object); + + +G_DEFINE_TYPE (GimpItemStack, gimp_item_stack, GIMP_TYPE_FILTER_STACK) + +#define parent_class gimp_item_stack_parent_class + + +static void +gimp_item_stack_class_init (GimpItemStackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + object_class->constructed = gimp_item_stack_constructed; + + container_class->add = gimp_item_stack_add; + container_class->remove = gimp_item_stack_remove; +} + +static void +gimp_item_stack_init (GimpItemStack *stack) +{ +} + +static void +gimp_item_stack_constructed (GObject *object) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (g_type_is_a (gimp_container_get_children_type (container), + GIMP_TYPE_ITEM)); +} + +static void +gimp_item_stack_add (GimpContainer *container, + GimpObject *object) +{ + g_object_ref_sink (object); + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); + + g_object_unref (object); +} + +static void +gimp_item_stack_remove (GimpContainer *container, + GimpObject *object) +{ + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); +} + + +/* public functions */ + +GimpContainer * +gimp_item_stack_new (GType item_type) +{ + g_return_val_if_fail (g_type_is_a (item_type, GIMP_TYPE_ITEM), NULL); + + return g_object_new (GIMP_TYPE_ITEM_STACK, + "name", g_type_name (item_type), + "children-type", item_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); +} + +gint +gimp_item_stack_get_n_items (GimpItemStack *stack) +{ + GList *list; + gint n_items = 0; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), 0); + + for (list = GIMP_LIST (stack)->queue->head; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + GimpContainer *children; + + n_items++; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + n_items += gimp_item_stack_get_n_items (GIMP_ITEM_STACK (children)); + } + + return n_items; +} + +gboolean +gimp_item_stack_is_flat (GimpItemStack *stack) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), TRUE); + + for (list = GIMP_LIST (stack)->queue->head; list; list = g_list_next (list)) + { + GimpViewable *viewable = list->data; + + if (gimp_viewable_get_children (viewable)) + return FALSE; + } + + return TRUE; +} + +GList * +gimp_item_stack_get_item_iter (GimpItemStack *stack) +{ + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), NULL); + + return GIMP_LIST (stack)->queue->head; +} + +GList * +gimp_item_stack_get_item_list (GimpItemStack *stack) +{ + GList *list; + GList *result = NULL; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), NULL); + + for (list = GIMP_LIST (stack)->queue->head; + list; + list = g_list_next (list)) + { + GimpViewable *viewable = list->data; + GimpContainer *children; + + result = g_list_prepend (result, viewable); + + children = gimp_viewable_get_children (viewable); + + if (children) + { + GList *child_list; + + child_list = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (children)); + + while (child_list) + { + result = g_list_prepend (result, child_list->data); + + child_list = g_list_remove (child_list, child_list->data); + } + } + } + + return g_list_reverse (result); +} + +GimpItem * +gimp_item_stack_get_item_by_tattoo (GimpItemStack *stack, + GimpTattoo tattoo) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), NULL); + + for (list = GIMP_LIST (stack)->queue->head; list; list = g_list_next (list)) + { + GimpItem *item = list->data; + GimpContainer *children; + + if (gimp_item_get_tattoo (item) == tattoo) + return item; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + { + item = gimp_item_stack_get_item_by_tattoo (GIMP_ITEM_STACK (children), + tattoo); + + if (item) + return item; + } + } + + return NULL; +} + +GimpItem * +gimp_item_stack_get_item_by_path (GimpItemStack *stack, + GList *path) +{ + GimpContainer *container; + GimpItem *item = NULL; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), NULL); + g_return_val_if_fail (path != NULL, NULL); + + container = GIMP_CONTAINER (stack); + + while (path) + { + guint32 i = GPOINTER_TO_UINT (path->data); + + item = GIMP_ITEM (gimp_container_get_child_by_index (container, i)); + + g_return_val_if_fail (GIMP_IS_ITEM (item), item); + + if (path->next) + { + container = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (container), item); + } + + path = path->next; + } + + return item; +} + +GimpItem * +gimp_item_stack_get_parent_by_path (GimpItemStack *stack, + GList *path, + gint *index) +{ + GimpItem *parent = NULL; + guint32 i; + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (stack), NULL); + g_return_val_if_fail (path != NULL, NULL); + + i = GPOINTER_TO_UINT (path->data); + + if (index) + *index = i; + + while (path->next) + { + GimpObject *child; + GimpContainer *children; + + child = gimp_container_get_child_by_index (GIMP_CONTAINER (stack), i); + + g_return_val_if_fail (GIMP_IS_ITEM (child), parent); + + children = gimp_viewable_get_children (GIMP_VIEWABLE (child)); + + g_return_val_if_fail (GIMP_IS_ITEM_STACK (children), parent); + + parent = GIMP_ITEM (child); + stack = GIMP_ITEM_STACK (children); + + path = path->next; + + i = GPOINTER_TO_UINT (path->data); + + if (index) + *index = i; + } + + return parent; +} + +static void +gimp_item_stack_viewable_invalidate_previews (GimpViewable *viewable) +{ + GimpContainer *children = gimp_viewable_get_children (viewable); + + if (children) + gimp_item_stack_invalidate_previews (GIMP_ITEM_STACK (children)); + + gimp_viewable_invalidate_preview (viewable); +} + +void +gimp_item_stack_invalidate_previews (GimpItemStack *stack) +{ + g_return_if_fail (GIMP_IS_ITEM_STACK (stack)); + + gimp_container_foreach (GIMP_CONTAINER (stack), + (GFunc) gimp_item_stack_viewable_invalidate_previews, + NULL); +} + +static void +gimp_item_stack_viewable_profile_changed (GimpViewable *viewable) +{ + GimpContainer *children = gimp_viewable_get_children (viewable); + + if (children) + gimp_item_stack_profile_changed (GIMP_ITEM_STACK (children)); + + if (GIMP_IS_COLOR_MANAGED (viewable)) + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (viewable)); +} + +void +gimp_item_stack_profile_changed (GimpItemStack *stack) +{ + g_return_if_fail (GIMP_IS_ITEM_STACK (stack)); + + gimp_container_foreach (GIMP_CONTAINER (stack), + (GFunc) gimp_item_stack_viewable_profile_changed, + NULL); +} diff --git a/app/core/gimpitemstack.h b/app/core/gimpitemstack.h new file mode 100644 index 0000000..b476076 --- /dev/null +++ b/app/core/gimpitemstack.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpitemstack.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_ITEM_STACK_H__ +#define __GIMP_ITEM_STACK_H__ + +#include "gimpfilterstack.h" + + +#define GIMP_TYPE_ITEM_STACK (gimp_item_stack_get_type ()) +#define GIMP_ITEM_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_STACK, GimpItemStack)) +#define GIMP_ITEM_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_STACK, GimpItemStackClass)) +#define GIMP_IS_ITEM_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_STACK)) +#define GIMP_IS_ITEM_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_STACK)) + + +typedef struct _GimpItemStackClass GimpItemStackClass; + +struct _GimpItemStack +{ + GimpFilterStack parent_instance; +}; + +struct _GimpItemStackClass +{ + GimpFilterStackClass parent_class; +}; + + +GType gimp_item_stack_get_type (void) G_GNUC_CONST; +GimpContainer * gimp_item_stack_new (GType item_type); + +gint gimp_item_stack_get_n_items (GimpItemStack *stack); +gboolean gimp_item_stack_is_flat (GimpItemStack *stack); +GList * gimp_item_stack_get_item_iter (GimpItemStack *stack); +GList * gimp_item_stack_get_item_list (GimpItemStack *stack); +GimpItem * gimp_item_stack_get_item_by_tattoo (GimpItemStack *stack, + GimpTattoo tattoo); +GimpItem * gimp_item_stack_get_item_by_path (GimpItemStack *stack, + GList *path); +GimpItem * gimp_item_stack_get_parent_by_path (GimpItemStack *stack, + GList *path, + gint *index); + +void gimp_item_stack_invalidate_previews (GimpItemStack *stack); +void gimp_item_stack_profile_changed (GimpItemStack *stack); + + +#endif /* __GIMP_ITEM_STACK_H__ */ diff --git a/app/core/gimpitemtree.c b/app/core/gimpitemtree.c new file mode 100644 index 0000000..7746afa --- /dev/null +++ b/app/core/gimpitemtree.c @@ -0,0 +1,714 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpitemtree.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpimage-undo-push.h" +#include "gimpitem.h" +#include "gimpitemstack.h" +#include "gimpitemtree.h" + + +enum +{ + PROP_0, + PROP_IMAGE, + PROP_CONTAINER_TYPE, + PROP_ITEM_TYPE, + PROP_ACTIVE_ITEM +}; + + +typedef struct _GimpItemTreePrivate GimpItemTreePrivate; + +struct _GimpItemTreePrivate +{ + GimpImage *image; + + GType container_type; + GType item_type; + + GimpItem *active_item; + + GHashTable *name_hash; +}; + +#define GIMP_ITEM_TREE_GET_PRIVATE(object) \ + ((GimpItemTreePrivate *) gimp_item_tree_get_instance_private ((GimpItemTree *) (object))) + + +/* local function prototypes */ + +static void gimp_item_tree_constructed (GObject *object); +static void gimp_item_tree_dispose (GObject *object); +static void gimp_item_tree_finalize (GObject *object); +static void gimp_item_tree_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_item_tree_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_item_tree_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_item_tree_uniquefy_name (GimpItemTree *tree, + GimpItem *item, + const gchar *new_name); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpItemTree, gimp_item_tree, GIMP_TYPE_OBJECT) + +#define parent_class gimp_item_tree_parent_class + + +static void +gimp_item_tree_class_init (GimpItemTreeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + object_class->constructed = gimp_item_tree_constructed; + object_class->dispose = gimp_item_tree_dispose; + object_class->finalize = gimp_item_tree_finalize; + object_class->set_property = gimp_item_tree_set_property; + object_class->get_property = gimp_item_tree_get_property; + + gimp_object_class->get_memsize = gimp_item_tree_get_memsize; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", + NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_CONTAINER_TYPE, + g_param_spec_gtype ("container-type", + NULL, NULL, + GIMP_TYPE_ITEM_STACK, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_ITEM_TYPE, + g_param_spec_gtype ("item-type", + NULL, NULL, + GIMP_TYPE_ITEM, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_ACTIVE_ITEM, + g_param_spec_object ("active-item", + NULL, NULL, + GIMP_TYPE_ITEM, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_item_tree_init (GimpItemTree *tree) +{ + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + private->name_hash = g_hash_table_new (g_str_hash, g_str_equal); +} + +static void +gimp_item_tree_constructed (GObject *object) +{ + GimpItemTree *tree = GIMP_ITEM_TREE (object); + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_IMAGE (private->image)); + gimp_assert (g_type_is_a (private->container_type, GIMP_TYPE_ITEM_STACK)); + gimp_assert (g_type_is_a (private->item_type, GIMP_TYPE_ITEM)); + gimp_assert (private->item_type != GIMP_TYPE_ITEM); + + tree->container = g_object_new (private->container_type, + "name", g_type_name (private->item_type), + "children-type", private->item_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); +} + +static void +gimp_item_tree_dispose (GObject *object) +{ + GimpItemTree *tree = GIMP_ITEM_TREE (object); + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + gimp_item_tree_set_active_item (tree, NULL); + + gimp_container_foreach (tree->container, + (GFunc) gimp_item_removed, NULL); + + gimp_container_clear (tree->container); + g_hash_table_remove_all (private->name_hash); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_item_tree_finalize (GObject *object) +{ + GimpItemTree *tree = GIMP_ITEM_TREE (object); + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_clear_pointer (&private->name_hash, g_hash_table_unref); + g_clear_object (&tree->container); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_item_tree_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (object); + + switch (property_id) + { + case PROP_IMAGE: + private->image = g_value_get_object (value); /* don't ref */ + break; + case PROP_CONTAINER_TYPE: + private->container_type = g_value_get_gtype (value); + break; + case PROP_ITEM_TYPE: + private->item_type = g_value_get_gtype (value); + break; + case PROP_ACTIVE_ITEM: + private->active_item = g_value_get_object (value); /* don't ref */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_item_tree_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, private->image); + break; + case PROP_CONTAINER_TYPE: + g_value_set_gtype (value, private->container_type); + break; + case PROP_ITEM_TYPE: + g_value_set_gtype (value, private->item_type); + break; + case PROP_ACTIVE_ITEM: + g_value_set_object (value, private->active_item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_item_tree_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpItemTree *tree = GIMP_ITEM_TREE (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (tree->container), gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + + +/* public functions */ + +GimpItemTree * +gimp_item_tree_new (GimpImage *image, + GType container_type, + GType item_type) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (g_type_is_a (container_type, GIMP_TYPE_ITEM_STACK), NULL); + g_return_val_if_fail (g_type_is_a (item_type, GIMP_TYPE_ITEM), NULL); + + return g_object_new (GIMP_TYPE_ITEM_TREE, + "image", image, + "container-type", container_type, + "item-type", item_type, + NULL); +} + +GimpItem * +gimp_item_tree_get_active_item (GimpItemTree *tree) +{ + g_return_val_if_fail (GIMP_IS_ITEM_TREE (tree), NULL); + + return GIMP_ITEM_TREE_GET_PRIVATE (tree)->active_item; +} + +void +gimp_item_tree_set_active_item (GimpItemTree *tree, + GimpItem *item) +{ + GimpItemTreePrivate *private; + + g_return_if_fail (GIMP_IS_ITEM_TREE (tree)); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_if_fail (item == NULL || + G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type)); + g_return_if_fail (item == NULL || gimp_item_get_tree (item) == tree); + + if (item != private->active_item) + { + private->active_item = item; + + g_object_notify (G_OBJECT (tree), "active-item"); + } +} + +GimpItem * +gimp_item_tree_get_item_by_name (GimpItemTree *tree, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_ITEM_TREE (tree), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_hash_table_lookup (GIMP_ITEM_TREE_GET_PRIVATE (tree)->name_hash, + name); +} + +gboolean +gimp_item_tree_get_insert_pos (GimpItemTree *tree, + GimpItem *item, + GimpItem **parent, + gint *position) +{ + GimpItemTreePrivate *private; + GimpContainer *container; + + g_return_val_if_fail (GIMP_IS_ITEM_TREE (tree), FALSE); + g_return_val_if_fail (parent != NULL, FALSE); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type), + FALSE); + g_return_val_if_fail (! gimp_item_is_attached (item), FALSE); + g_return_val_if_fail (gimp_item_get_image (item) == private->image, FALSE); + g_return_val_if_fail (*parent == NULL || + *parent == GIMP_IMAGE_ACTIVE_PARENT || + G_TYPE_CHECK_INSTANCE_TYPE (*parent, private->item_type), + FALSE); + g_return_val_if_fail (*parent == NULL || + *parent == GIMP_IMAGE_ACTIVE_PARENT || + gimp_item_get_tree (*parent) == tree, FALSE); + g_return_val_if_fail (*parent == NULL || + *parent == GIMP_IMAGE_ACTIVE_PARENT || + gimp_viewable_get_children (GIMP_VIEWABLE (*parent)), + FALSE); + g_return_val_if_fail (position != NULL, FALSE); + + /* if we want to insert in the active item's parent container */ + if (*parent == GIMP_IMAGE_ACTIVE_PARENT) + { + if (private->active_item) + { + /* if the active item is a branch, add to the top of that + * branch; add to the active item's parent container + * otherwise + */ + if (gimp_viewable_get_children (GIMP_VIEWABLE (private->active_item))) + { + *parent = private->active_item; + *position = 0; + } + else + { + *parent = gimp_item_get_parent (private->active_item); + } + } + else + { + /* use the toplevel container if there is no active item */ + *parent = NULL; + } + } + + if (*parent) + container = gimp_viewable_get_children (GIMP_VIEWABLE (*parent)); + else + container = tree->container; + + /* if we want to add on top of the active item */ + if (*position == -1) + { + if (private->active_item) + *position = + gimp_container_get_child_index (container, + GIMP_OBJECT (private->active_item)); + + /* if the active item is not in the specified parent container, + * fall back to index 0 + */ + if (*position == -1) + *position = 0; + } + + /* don't add at a non-existing index */ + *position = CLAMP (*position, 0, gimp_container_get_n_children (container)); + + return TRUE; +} + +void +gimp_item_tree_add_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *parent, + gint position) +{ + GimpItemTreePrivate *private; + GimpContainer *container; + GimpContainer *children; + + g_return_if_fail (GIMP_IS_ITEM_TREE (tree)); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type)); + g_return_if_fail (! gimp_item_is_attached (item)); + g_return_if_fail (gimp_item_get_image (item) == private->image); + g_return_if_fail (parent == NULL || + G_TYPE_CHECK_INSTANCE_TYPE (parent, private->item_type)); + g_return_if_fail (parent == NULL || gimp_item_get_tree (parent) == tree); + g_return_if_fail (parent == NULL || + gimp_viewable_get_children (GIMP_VIEWABLE (parent))); + + gimp_item_tree_uniquefy_name (tree, item, NULL); + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + { + GList *list = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (children)); + + while (list) + { + gimp_item_tree_uniquefy_name (tree, list->data, NULL); + + list = g_list_remove (list, list->data); + } + } + + if (parent) + container = gimp_viewable_get_children (GIMP_VIEWABLE (parent)); + else + container = tree->container; + + if (parent) + gimp_viewable_set_parent (GIMP_VIEWABLE (item), + GIMP_VIEWABLE (parent)); + + gimp_container_insert (container, GIMP_OBJECT (item), position); + + /* if the item came from the undo stack, reset its "removed" state */ + if (gimp_item_is_removed (item)) + gimp_item_unset_removed (item); +} + +GimpItem * +gimp_item_tree_remove_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *new_active) +{ + GimpItemTreePrivate *private; + GimpItem *parent; + GimpContainer *container; + GimpContainer *children; + gint index; + + g_return_val_if_fail (GIMP_IS_ITEM_TREE (tree), NULL); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type), + NULL); + g_return_val_if_fail (gimp_item_get_tree (item) == tree, NULL); + + parent = gimp_item_get_parent (item); + container = gimp_item_get_container (item); + index = gimp_item_get_index (item); + + g_object_ref (item); + + g_hash_table_remove (private->name_hash, + gimp_object_get_name (item)); + + children = gimp_viewable_get_children (GIMP_VIEWABLE (item)); + + if (children) + { + GList *list = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (children)); + + while (list) + { + g_hash_table_remove (private->name_hash, + gimp_object_get_name (list->data)); + + list = g_list_remove (list, list->data); + } + } + + gimp_container_remove (container, GIMP_OBJECT (item)); + + if (parent) + gimp_viewable_set_parent (GIMP_VIEWABLE (item), NULL); + + gimp_item_removed (item); + + if (! new_active) + { + gint n_children = gimp_container_get_n_children (container); + + if (n_children > 0) + { + index = CLAMP (index, 0, n_children - 1); + + new_active = + GIMP_ITEM (gimp_container_get_child_by_index (container, index)); + } + else if (parent) + { + new_active = parent; + } + } + + g_object_unref (item); + + return new_active; +} + +gboolean +gimp_item_tree_reorder_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *new_parent, + gint new_index, + gboolean push_undo, + const gchar *undo_desc) +{ + GimpItemTreePrivate *private; + GimpContainer *container; + GimpContainer *new_container; + gint n_items; + + g_return_val_if_fail (GIMP_IS_ITEM_TREE (tree), FALSE); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type), + FALSE); + g_return_val_if_fail (gimp_item_get_tree (item) == tree, FALSE); + g_return_val_if_fail (new_parent == NULL || + G_TYPE_CHECK_INSTANCE_TYPE (new_parent, + private->item_type), + FALSE); + g_return_val_if_fail (new_parent == NULL || + gimp_item_get_tree (new_parent) == tree, FALSE); + g_return_val_if_fail (new_parent == NULL || + gimp_viewable_get_children (GIMP_VIEWABLE (new_parent)), + FALSE); + g_return_val_if_fail (item != new_parent, FALSE); + g_return_val_if_fail (new_parent == NULL || + ! gimp_viewable_is_ancestor (GIMP_VIEWABLE (item), + GIMP_VIEWABLE (new_parent)), + FALSE); + + container = gimp_item_get_container (item); + + if (new_parent) + new_container = gimp_viewable_get_children (GIMP_VIEWABLE (new_parent)); + else + new_container = tree->container; + + n_items = gimp_container_get_n_children (new_container); + + if (new_container == container) + n_items--; + + new_index = CLAMP (new_index, 0, n_items); + + if (new_container != container || + new_index != gimp_item_get_index (item)) + { + if (push_undo) + gimp_image_undo_push_item_reorder (private->image, undo_desc, item); + + if (new_container != container) + { + g_object_ref (item); + + gimp_container_remove (container, GIMP_OBJECT (item)); + + gimp_viewable_set_parent (GIMP_VIEWABLE (item), + GIMP_VIEWABLE (new_parent)); + + gimp_container_insert (new_container, GIMP_OBJECT (item), new_index); + + g_object_unref (item); + } + else + { + gimp_container_reorder (container, GIMP_OBJECT (item), new_index); + } + } + + return TRUE; +} + +void +gimp_item_tree_rename_item (GimpItemTree *tree, + GimpItem *item, + const gchar *new_name, + gboolean push_undo, + const gchar *undo_desc) +{ + GimpItemTreePrivate *private; + + g_return_if_fail (GIMP_IS_ITEM_TREE (tree)); + + private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, private->item_type)); + g_return_if_fail (gimp_item_get_tree (item) == tree); + g_return_if_fail (new_name != NULL); + + if (strcmp (new_name, gimp_object_get_name (item))) + { + if (push_undo) + gimp_image_undo_push_item_rename (gimp_item_get_image (item), + undo_desc, item); + + gimp_item_tree_uniquefy_name (tree, item, new_name); + } +} + + +/* private functions */ + +static void +gimp_item_tree_uniquefy_name (GimpItemTree *tree, + GimpItem *item, + const gchar *new_name) +{ + GimpItemTreePrivate *private = GIMP_ITEM_TREE_GET_PRIVATE (tree); + + if (new_name) + { + g_hash_table_remove (private->name_hash, + gimp_object_get_name (item)); + + gimp_object_set_name (GIMP_OBJECT (item), new_name); + } + + /* Remove any trailing whitespace. */ + if (gimp_object_get_name (item)) + { + gchar *name = g_strchomp (g_strdup (gimp_object_get_name (item))); + + gimp_object_take_name (GIMP_OBJECT (item), name); + } + + if (g_hash_table_lookup (private->name_hash, + gimp_object_get_name (item))) + { + gchar *name = g_strdup (gimp_object_get_name (item)); + gchar *new_name = NULL; + gint number = 0; + gint precision = 1; + GRegex *end_numbers = g_regex_new (" ?#([0-9]+)\\s*$", 0, 0, NULL); + GMatchInfo *match_info = NULL; + + if (g_regex_match (end_numbers, name, 0, &match_info)) + { + gchar *match; + gint start_pos; + + match = g_match_info_fetch (match_info, 1); + if (match && match[0] == '0') + { + precision = strlen (match); + } + number = atoi (match); + g_free (match); + + g_match_info_fetch_pos (match_info, 0, + &start_pos, NULL); + name[start_pos] = '\0'; + } + g_match_info_free (match_info); + g_regex_unref (end_numbers); + + do + { + number++; + + g_free (new_name); + + new_name = g_strdup_printf ("%s #%.*d", + name, + precision, + number); + } + while (g_hash_table_lookup (private->name_hash, new_name)); + + g_free (name); + + gimp_object_take_name (GIMP_OBJECT (item), new_name); + } + + g_hash_table_insert (private->name_hash, + (gpointer) gimp_object_get_name (item), + item); +} diff --git a/app/core/gimpitemtree.h b/app/core/gimpitemtree.h new file mode 100644 index 0000000..4d3c6ea --- /dev/null +++ b/app/core/gimpitemtree.h @@ -0,0 +1,89 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpitemtree.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_ITEM_TREE_H__ +#define __GIMP_ITEM_TREE_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_ITEM_TREE (gimp_item_tree_get_type ()) +#define GIMP_ITEM_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_TREE, GimpItemTree)) +#define GIMP_ITEM_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_TREE, GimpItemTreeClass)) +#define GIMP_IS_ITEM_TREE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_TREE)) +#define GIMP_IS_ITEM_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_TREE)) + + +typedef struct _GimpItemTreeClass GimpItemTreeClass; + +struct _GimpItemTree +{ + GimpObject parent_instance; + + GimpContainer *container; +}; + +struct _GimpItemTreeClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_item_tree_get_type (void) G_GNUC_CONST; +GimpItemTree * gimp_item_tree_new (GimpImage *image, + GType container_type, + GType item_type); + +GimpItem * gimp_item_tree_get_active_item (GimpItemTree *tree); +void gimp_item_tree_set_active_item (GimpItemTree *tree, + GimpItem *item); + +GimpItem * gimp_item_tree_get_item_by_name (GimpItemTree *tree, + const gchar *name); + +gboolean gimp_item_tree_get_insert_pos (GimpItemTree *tree, + GimpItem *item, + GimpItem **parent, + gint *position); + +void gimp_item_tree_add_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *parent, + gint position); +GimpItem * gimp_item_tree_remove_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *new_active); + +gboolean gimp_item_tree_reorder_item (GimpItemTree *tree, + GimpItem *item, + GimpItem *new_parent, + gint new_index, + gboolean push_undo, + const gchar *undo_desc); + +void gimp_item_tree_rename_item (GimpItemTree *tree, + GimpItem *item, + const gchar *new_name, + gboolean push_undo, + const gchar *undo_desc); + + +#endif /* __GIMP_ITEM_TREE_H__ */ diff --git a/app/core/gimpitemundo.c b/app/core/gimpitemundo.c new file mode 100644 index 0000000..0a50d43 --- /dev/null +++ b/app/core/gimpitemundo.c @@ -0,0 +1,139 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpitem.h" +#include "gimpitemundo.h" + + +enum +{ + PROP_0, + PROP_ITEM +}; + + +static void gimp_item_undo_constructed (GObject *object); +static void gimp_item_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_item_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_item_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpItemUndo, gimp_item_undo, GIMP_TYPE_UNDO) + +#define parent_class gimp_item_undo_parent_class + + +static void +gimp_item_undo_class_init (GimpItemUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_item_undo_constructed; + object_class->set_property = gimp_item_undo_set_property; + object_class->get_property = gimp_item_undo_get_property; + + undo_class->free = gimp_item_undo_free; + + g_object_class_install_property (object_class, PROP_ITEM, + g_param_spec_object ("item", NULL, NULL, + GIMP_TYPE_ITEM, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_item_undo_init (GimpItemUndo *undo) +{ +} + +static void +gimp_item_undo_constructed (GObject *object) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_ITEM (item_undo->item)); +} + +static void +gimp_item_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object); + + switch (property_id) + { + case PROP_ITEM: + item_undo->item = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_item_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object); + + switch (property_id) + { + case PROP_ITEM: + g_value_set_object (value, item_undo->item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_item_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (undo); + + g_clear_object (&item_undo->item); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpitemundo.h b/app/core/gimpitemundo.h new file mode 100644 index 0000000..91fe8d6 --- /dev/null +++ b/app/core/gimpitemundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_ITEM_UNDO_H__ +#define __GIMP_ITEM_UNDO_H__ + + +#include "gimpundo.h" + + +#define GIMP_TYPE_ITEM_UNDO (gimp_item_undo_get_type ()) +#define GIMP_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ITEM_UNDO, GimpItemUndo)) +#define GIMP_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ITEM_UNDO, GimpItemUndoClass)) +#define GIMP_IS_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ITEM_UNDO)) +#define GIMP_IS_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ITEM_UNDO)) +#define GIMP_ITEM_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ITEM_UNDO, GimpItemUndoClass)) + + +typedef struct _GimpItemUndo GimpItemUndo; +typedef struct _GimpItemUndoClass GimpItemUndoClass; + +struct _GimpItemUndo +{ + GimpUndo parent_instance; + + GimpItem *item; /* the item this undo is for */ +}; + +struct _GimpItemUndoClass +{ + GimpUndoClass parent_class; +}; + + +GType gimp_item_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_ITEM_UNDO_H__ */ diff --git a/app/core/gimplayer-floating-selection.c b/app/core/gimplayer-floating-selection.c new file mode 100644 index 0000000..80222a2 --- /dev/null +++ b/app/core/gimplayer-floating-selection.c @@ -0,0 +1,332 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpboundary.h" +#include "gimpdrawable-filters.h" +#include "gimpdrawable-floating-selection.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimplayer-floating-selection.h" +#include "gimplayermask.h" + +#include "gimp-intl.h" + + +/* public functions */ + +void +floating_sel_attach (GimpLayer *layer, + GimpDrawable *drawable) +{ + GimpImage *image; + GimpLayer *floating_sel; + GimpLayer *parent = NULL; + gint position = 0; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); + g_return_if_fail (drawable != GIMP_DRAWABLE (layer)); + g_return_if_fail (gimp_item_get_image (GIMP_ITEM (layer)) == + gimp_item_get_image (GIMP_ITEM (drawable))); + + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + floating_sel = gimp_image_get_floating_selection (image); + + /* If there is already a floating selection, anchor it */ + if (floating_sel) + { + floating_sel_anchor (floating_sel); + + /* if we were pasting to the old floating selection, paste now + * to the drawable + */ + if (drawable == (GimpDrawable *) floating_sel) + drawable = gimp_image_get_active_drawable (image); + } + + gimp_layer_set_lock_alpha (layer, TRUE, FALSE); + + gimp_layer_set_floating_sel_drawable (layer, drawable); + + /* Floating selection layer placement, default to the top of the + * layers stack; parent and position are adapted according to the + * drawable associated with the floating selection. + */ + + if (GIMP_IS_LAYER_MASK (drawable)) + { + GimpLayer *tmp = gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable)); + + parent = GIMP_LAYER (gimp_item_get_parent (GIMP_ITEM (tmp))); + position = gimp_item_get_index (GIMP_ITEM (tmp)); + } + else if (GIMP_IS_LAYER (drawable)) + { + parent = GIMP_LAYER (gimp_item_get_parent (GIMP_ITEM (drawable))); + position = gimp_item_get_index (GIMP_ITEM (drawable)); + } + + gimp_image_add_layer (image, layer, parent, position, TRUE); +} + +void +floating_sel_anchor (GimpLayer *layer) +{ + GimpImage *image; + GimpDrawable *drawable; + GimpFilter *filter = NULL; + GeglRectangle bounding_box; + GeglRectangle dr_bounding_box; + gint off_x, off_y; + gint dr_off_x, dr_off_y; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_layer_is_floating_sel (layer)); + + /* Don't let gimp_image_remove_layer free the layer while we still need it */ + g_object_ref (layer); + + image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_FS_ANCHOR, + C_("undo-type", "Anchor Floating Selection")); + + drawable = gimp_layer_get_floating_sel_drawable (layer); + + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + gimp_item_get_offset (GIMP_ITEM (drawable), &dr_off_x, &dr_off_y); + + bounding_box = gimp_drawable_get_bounding_box (GIMP_DRAWABLE (layer)); + dr_bounding_box = gimp_drawable_get_bounding_box (drawable); + + bounding_box.x += off_x; + bounding_box.y += off_y; + + dr_bounding_box.x += dr_off_x; + dr_bounding_box.y += dr_off_y; + + if (gimp_item_get_visible (GIMP_ITEM (layer)) && + gegl_rectangle_intersect (NULL, &bounding_box, &dr_bounding_box)) + { + filter = gimp_drawable_get_floating_sel_filter (drawable); + } + + if (filter) + { + gimp_drawable_merge_filter (drawable, filter, NULL, NULL, + NULL, FALSE, FALSE, FALSE); + } + + gimp_image_remove_layer (image, layer, TRUE, NULL); + + gimp_image_undo_group_end (image); + + /* invalidate the boundaries */ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (gimp_image_get_mask (image))); + + g_object_unref (layer); +} + +gboolean +floating_sel_to_layer (GimpLayer *layer, + GError **error) +{ + GimpItem *item; + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + g_return_val_if_fail (gimp_layer_is_floating_sel (layer), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + item = GIMP_ITEM (layer); + image = gimp_item_get_image (item); + + /* Check if the floating layer belongs to a channel */ + if (GIMP_IS_CHANNEL (gimp_layer_get_floating_sel_drawable (layer))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot create a new layer from the floating " + "selection because it belongs to a layer mask " + "or channel.")); + return FALSE; + } + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_FS_TO_LAYER, + C_("undo-type", "Floating Selection to Layer")); + + gimp_image_undo_push_fs_to_layer (image, NULL, layer); + + gimp_drawable_detach_floating_sel (gimp_layer_get_floating_sel_drawable (layer)); + gimp_layer_set_floating_sel_drawable (layer, NULL); + + gimp_item_set_visible (item, TRUE, TRUE); + gimp_layer_set_lock_alpha (layer, FALSE, TRUE); + + gimp_image_undo_group_end (image); + + /* When the floating selection is converted to/from a normal layer + * it does something resembling a name change, so emit the + * "name-changed" signal + */ + gimp_object_name_changed (GIMP_OBJECT (layer)); + + gimp_drawable_update (GIMP_DRAWABLE (layer), + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)); + + return TRUE; +} + +void +floating_sel_activate_drawable (GimpLayer *layer) +{ + GimpImage *image; + GimpDrawable *drawable; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_layer_is_floating_sel (layer)); + + image = gimp_item_get_image (GIMP_ITEM (layer)); + + drawable = gimp_layer_get_floating_sel_drawable (layer); + + /* set the underlying drawable to active */ + if (GIMP_IS_LAYER_MASK (drawable)) + { + GimpLayerMask *mask = GIMP_LAYER_MASK (drawable); + + gimp_image_set_active_layer (image, gimp_layer_mask_get_layer (mask)); + } + else if (GIMP_IS_CHANNEL (drawable)) + { + gimp_image_set_active_channel (image, GIMP_CHANNEL (drawable)); + } + else + { + gimp_image_set_active_layer (image, GIMP_LAYER (drawable)); + } +} + +const GimpBoundSeg * +floating_sel_boundary (GimpLayer *layer, + gint *n_segs) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL); + g_return_val_if_fail (n_segs != NULL, NULL); + + if (layer->fs.boundary_known == FALSE) + { + gint width, height; + gint off_x, off_y; + + width = gimp_item_get_width (GIMP_ITEM (layer)); + height = gimp_item_get_height (GIMP_ITEM (layer)); + gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); + + if (layer->fs.segs) + g_free (layer->fs.segs); + + if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + GeglBuffer *buffer; + gint i; + + /* find the segments */ + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + layer->fs.segs = gimp_boundary_find (buffer, NULL, + babl_format ("A float"), + GIMP_BOUNDARY_WITHIN_BOUNDS, + 0, 0, width, height, + GIMP_BOUNDARY_HALF_WAY, + &layer->fs.num_segs); + + /* offset the segments */ + for (i = 0; i < layer->fs.num_segs; i++) + { + layer->fs.segs[i].x1 += off_x; + layer->fs.segs[i].y1 += off_y; + layer->fs.segs[i].x2 += off_x; + layer->fs.segs[i].y2 += off_y; + } + } + else + { + layer->fs.num_segs = 4; + layer->fs.segs = g_new0 (GimpBoundSeg, 4); + + /* top */ + layer->fs.segs[0].x1 = off_x; + layer->fs.segs[0].y1 = off_y; + layer->fs.segs[0].x2 = off_x + width; + layer->fs.segs[0].y2 = off_y; + + /* left */ + layer->fs.segs[1].x1 = off_x; + layer->fs.segs[1].y1 = off_y; + layer->fs.segs[1].x2 = off_x; + layer->fs.segs[1].y2 = off_y + height; + + /* right */ + layer->fs.segs[2].x1 = off_x + width; + layer->fs.segs[2].y1 = off_y; + layer->fs.segs[2].x2 = off_x + width; + layer->fs.segs[2].y2 = off_y + height; + + /* bottom */ + layer->fs.segs[3].x1 = off_x; + layer->fs.segs[3].y1 = off_y + height; + layer->fs.segs[3].x2 = off_x + width; + layer->fs.segs[3].y2 = off_y + height; + } + + layer->fs.boundary_known = TRUE; + } + + *n_segs = layer->fs.num_segs; + + return layer->fs.segs; +} + +void +floating_sel_invalidate (GimpLayer *layer) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_layer_is_floating_sel (layer)); + + /* Invalidate the attached-to drawable's preview */ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimp_layer_get_floating_sel_drawable (layer))); + + /* Invalidate the boundary */ + layer->fs.boundary_known = FALSE; +} diff --git a/app/core/gimplayer-floating-selection.h b/app/core/gimplayer-floating-selection.h new file mode 100644 index 0000000..e111c1a --- /dev/null +++ b/app/core/gimplayer-floating-selection.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 . + */ + +#ifndef __GIMP_LAYER_FLOATING_SELECTION_H__ +#define __GIMP_LAYER_FLOATING_SELECTION_H__ + + +void floating_sel_attach (GimpLayer *layer, + GimpDrawable *drawable); +void floating_sel_anchor (GimpLayer *layer); +gboolean floating_sel_to_layer (GimpLayer *layer, + GError **error); +void floating_sel_activate_drawable (GimpLayer *layer); +const GimpBoundSeg * floating_sel_boundary (GimpLayer *layer, + gint *n_segs); +void floating_sel_invalidate (GimpLayer *layer); + + +#endif /* __GIMP_LAYER_FLOATING_SELECTION_H__ */ diff --git a/app/core/gimplayer-new.c b/app/core/gimplayer-new.c new file mode 100644 index 0000000..7ea2450 --- /dev/null +++ b/app/core/gimplayer-new.c @@ -0,0 +1,253 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimpbuffer.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimplayer.h" +#include "gimplayer-new.h" + + +/* local function prototypes */ + +static void gimp_layer_new_convert_buffer (GimpLayer *layer, + GeglBuffer *src_buffer, + GimpColorProfile *src_profile, + GError **error); + + +/* public functions */ + +GimpLayer * +gimp_layer_new (GimpImage *image, + gint width, + gint height, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode) +{ + GimpLayer *layer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + g_return_val_if_fail (format != NULL, NULL); + + layer = GIMP_LAYER (gimp_drawable_new (GIMP_TYPE_LAYER, + image, name, + 0, 0, width, height, + format)); + + gimp_layer_set_opacity (layer, opacity, FALSE); + gimp_layer_set_mode (layer, mode, FALSE); + + return layer; +} + +/** + * gimp_layer_new_from_buffer: + * @buffer: The buffer to make the new layer from. + * @dest_image: The image the new layer will be added to. + * @format: The #Babl format of the new layer. + * @name: The new layer's name. + * @opacity: The new layer's opacity. + * @mode: The new layer's mode. + * + * Copies %buffer to a layer taking into consideration the + * possibility of transforming the contents to meet the requirements + * of the target image type + * + * Return value: The new layer. + **/ +GimpLayer * +gimp_layer_new_from_buffer (GimpBuffer *buffer, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode) +{ + g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL); + g_return_val_if_fail (format != NULL, NULL); + + return gimp_layer_new_from_gegl_buffer (gimp_buffer_get_buffer (buffer), + dest_image, format, + name, opacity, mode, + gimp_buffer_get_color_profile (buffer)); +} + +/** + * gimp_layer_new_from_gegl_buffer: + * @buffer: The buffer to make the new layer from. + * @dest_image: The image the new layer will be added to. + * @format: The #Babl format of the new layer. + * @name: The new layer's name. + * @opacity: The new layer's opacity. + * @mode: The new layer's mode. + * + * Copies %buffer to a layer taking into consideration the + * possibility of transforming the contents to meet the requirements + * of the target image type + * + * Return value: The new layer. + **/ +GimpLayer * +gimp_layer_new_from_gegl_buffer (GeglBuffer *buffer, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode, + GimpColorProfile *buffer_profile) +{ + GimpLayer *layer; + const GeglRectangle *extent; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL); + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (buffer_profile == NULL || + GIMP_IS_COLOR_PROFILE (buffer_profile), NULL); + + extent = gegl_buffer_get_extent (buffer); + + /* do *not* use the buffer's format because this function gets + * buffers of any format passed, and converts them + */ + layer = gimp_layer_new (dest_image, + extent->width, extent->height, + format, + name, opacity, mode); + + if (extent->x != 0 || extent->y != 0) + gimp_item_translate (GIMP_ITEM (layer), extent->x, extent->y, FALSE); + + gimp_layer_new_convert_buffer (layer, buffer, buffer_profile, NULL); + + return layer; +} + +/** + * gimp_layer_new_from_pixbuf: + * @pixbuf: The pixbuf to make the new layer from. + * @dest_image: The image the new layer will be added to. + * @format: The #Babl format of the new layer. + * @name: The new layer's name. + * @opacity: The new layer's opacity. + * @mode: The new layer's mode. + * + * Copies %pixbuf to a layer taking into consideration the + * possibility of transforming the contents to meet the requirements + * of the target image type + * + * Return value: The new layer. + **/ +GimpLayer * +gimp_layer_new_from_pixbuf (GdkPixbuf *pixbuf, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode) +{ + GimpLayer *layer; + GeglBuffer *buffer; + guint8 *icc_data; + gsize icc_len; + GimpColorProfile *profile = NULL; + + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL); + g_return_val_if_fail (format != NULL, NULL); + + layer = gimp_layer_new (dest_image, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + format, name, opacity, mode); + + buffer = gimp_pixbuf_create_buffer (pixbuf); + + icc_data = gimp_pixbuf_get_icc_profile (pixbuf, &icc_len); + if (icc_data) + { + profile = gimp_color_profile_new_from_icc_profile (icc_data, icc_len, + NULL); + g_free (icc_data); + } + + gimp_layer_new_convert_buffer (layer, buffer, profile, NULL); + + if (profile) + g_object_unref (profile); + + g_object_unref (buffer); + + return layer; +} + + +/* private functions */ + +static void +gimp_layer_new_convert_buffer (GimpLayer *layer, + GeglBuffer *src_buffer, + GimpColorProfile *src_profile, + GError **error) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (layer); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + GeglBuffer *dest_buffer = gimp_drawable_get_buffer (drawable); + GimpColorProfile *dest_profile; + + if (! gimp_image_get_is_color_managed (image)) + { + gimp_gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, + dest_buffer, NULL); + return; + } + + if (! src_profile) + { + const Babl *src_format = gegl_buffer_get_format (src_buffer); + + src_profile = gimp_babl_format_get_color_profile (src_format); + } + + dest_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (layer)); + + gimp_gegl_convert_color_profile (src_buffer, NULL, src_profile, + dest_buffer, NULL, dest_profile, + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + TRUE, NULL); +} diff --git a/app/core/gimplayer-new.h b/app/core/gimplayer-new.h new file mode 100644 index 0000000..58b3aa9 --- /dev/null +++ b/app/core/gimplayer-new.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_LAYER_NEW_H__ +#define __GIMP_LAYER_NEW_H__ + + +GimpLayer * gimp_layer_new (GimpImage *image, + gint width, + gint height, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode); + +GimpLayer * gimp_layer_new_from_buffer (GimpBuffer *buffer, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode); +GimpLayer * gimp_layer_new_from_gegl_buffer (GeglBuffer *buffer, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode, + GimpColorProfile *buffer_profile); +GimpLayer * gimp_layer_new_from_pixbuf (GdkPixbuf *pixbuf, + GimpImage *dest_image, + const Babl *format, + const gchar *name, + gdouble opacity, + GimpLayerMode mode); + + +#endif /* __GIMP_LAYER_NEW_H__ */ diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c new file mode 100644 index 0000000..3887007 --- /dev/null +++ b/app/core/gimplayer.c @@ -0,0 +1,2943 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-nodes.h" + +#include "gimpboundary.h" +#include "gimpchannel-select.h" +#include "gimpcontext.h" +#include "gimpcontainer.h" +#include "gimpdrawable-floating-selection.h" +#include "gimperror.h" +#include "gimpgrouplayer.h" +#include "gimpimage-undo-push.h" +#include "gimpimage-undo.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimplayer-floating-selection.h" +#include "gimplayer.h" +#include "gimplayermask.h" +#include "gimpmarshal.h" +#include "gimpobjectqueue.h" +#include "gimppickable.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +enum +{ + OPACITY_CHANGED, + MODE_CHANGED, + BLEND_SPACE_CHANGED, + COMPOSITE_SPACE_CHANGED, + COMPOSITE_MODE_CHANGED, + EFFECTIVE_MODE_CHANGED, + EXCLUDES_BACKDROP_CHANGED, + LOCK_ALPHA_CHANGED, + MASK_CHANGED, + APPLY_MASK_CHANGED, + EDIT_MASK_CHANGED, + SHOW_MASK_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_OPACITY, + PROP_MODE, + PROP_BLEND_SPACE, + PROP_COMPOSITE_SPACE, + PROP_COMPOSITE_MODE, + PROP_EXCLUDES_BACKDROP, + PROP_LOCK_ALPHA, + PROP_MASK, + PROP_FLOATING_SELECTION +}; + + +static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface); +static void gimp_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_layer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_layer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_layer_dispose (GObject *object); +static void gimp_layer_finalize (GObject *object); +static void gimp_layer_notify (GObject *object, + GParamSpec *pspec); + +static void gimp_layer_name_changed (GimpObject *object); +static gint64 gimp_layer_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_layer_invalidate_preview (GimpViewable *viewable); +static gchar * gimp_layer_get_description (GimpViewable *viewable, + gchar **tooltip); + +static GeglNode * gimp_layer_get_node (GimpFilter *filter); + +static void gimp_layer_removed (GimpItem *item); +static void gimp_layer_unset_removed (GimpItem *item); +static gboolean gimp_layer_is_attached (GimpItem *item); +static GimpItemTree * gimp_layer_get_tree (GimpItem *item); +static GimpItem * gimp_layer_duplicate (GimpItem *item, + GType new_type); +static void gimp_layer_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type); +static gboolean gimp_layer_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error); +static void gimp_layer_start_move (GimpItem *item, + gboolean push_undo); +static void gimp_layer_end_move (GimpItem *item, + gboolean push_undo); +static void gimp_layer_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo); +static void gimp_layer_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_layer_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static void gimp_layer_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +static void gimp_layer_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +static void gimp_layer_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); +static void gimp_layer_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y); + +static void gimp_layer_alpha_changed (GimpDrawable *drawable); +static gint64 gimp_layer_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height); +static gboolean gimp_layer_supports_alpha (GimpDrawable *drawable); +static void gimp_layer_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); +static void gimp_layer_invalidate_boundary (GimpDrawable *drawable); +static void gimp_layer_get_active_components (GimpDrawable *drawable, + gboolean *active); +static GimpComponentMask + gimp_layer_get_active_mask (GimpDrawable *drawable); +static void gimp_layer_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds); +static GeglRectangle + gimp_layer_get_bounding_box (GimpDrawable *drawable); + +static GimpColorProfile * + gimp_layer_get_color_profile (GimpColorManaged *managed); + +static gdouble gimp_layer_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +static void gimp_layer_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +static void gimp_layer_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); + +static void gimp_layer_real_translate (GimpLayer *layer, + gint offset_x, + gint offset_y); +static void gimp_layer_real_scale (GimpLayer *layer, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_layer_real_resize (GimpLayer *layer, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static void gimp_layer_real_flip (GimpLayer *layer, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +static void gimp_layer_real_rotate (GimpLayer *layer, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +static void gimp_layer_real_transform (GimpLayer *layer, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); +static void gimp_layer_real_convert_type (GimpLayer *layer, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); +static GeglRectangle + gimp_layer_real_get_bounding_box (GimpLayer *layer); +static void gimp_layer_real_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode); +static gboolean + gimp_layer_real_get_excludes_backdrop (GimpLayer *layer); + +static void gimp_layer_layer_mask_update (GimpDrawable *layer_mask, + gint x, + gint y, + gint width, + gint height, + GimpLayer *layer); + + +G_DEFINE_TYPE_WITH_CODE (GimpLayer, gimp_layer, GIMP_TYPE_DRAWABLE, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_color_managed_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_pickable_iface_init)) + +#define parent_class gimp_layer_parent_class + +static guint layer_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_layer_class_init (GimpLayerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpFilterClass *filter_class = GIMP_FILTER_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + + layer_signals[OPACITY_CHANGED] = + g_signal_new ("opacity-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, opacity_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[MODE_CHANGED] = + g_signal_new ("mode-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, mode_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[BLEND_SPACE_CHANGED] = + g_signal_new ("blend-space-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, blend_space_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[COMPOSITE_SPACE_CHANGED] = + g_signal_new ("composite-space-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, composite_space_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[COMPOSITE_MODE_CHANGED] = + g_signal_new ("composite-mode-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, composite_mode_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[EFFECTIVE_MODE_CHANGED] = + g_signal_new ("effective-mode-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, effective_mode_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[EXCLUDES_BACKDROP_CHANGED] = + g_signal_new ("excludes-backdrop-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, excludes_backdrop_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[LOCK_ALPHA_CHANGED] = + g_signal_new ("lock-alpha-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, lock_alpha_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[MASK_CHANGED] = + g_signal_new ("mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[APPLY_MASK_CHANGED] = + g_signal_new ("apply-mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, apply_mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[EDIT_MASK_CHANGED] = + g_signal_new ("edit-mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, edit_mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + layer_signals[SHOW_MASK_CHANGED] = + g_signal_new ("show-mask-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLayerClass, show_mask_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->set_property = gimp_layer_set_property; + object_class->get_property = gimp_layer_get_property; + object_class->dispose = gimp_layer_dispose; + object_class->finalize = gimp_layer_finalize; + object_class->notify = gimp_layer_notify; + + gimp_object_class->name_changed = gimp_layer_name_changed; + gimp_object_class->get_memsize = gimp_layer_get_memsize; + + viewable_class->default_icon_name = "gimp-layer"; + viewable_class->invalidate_preview = gimp_layer_invalidate_preview; + viewable_class->get_description = gimp_layer_get_description; + + filter_class->get_node = gimp_layer_get_node; + + item_class->removed = gimp_layer_removed; + item_class->unset_removed = gimp_layer_unset_removed; + item_class->is_attached = gimp_layer_is_attached; + item_class->get_tree = gimp_layer_get_tree; + item_class->duplicate = gimp_layer_duplicate; + item_class->convert = gimp_layer_convert; + item_class->rename = gimp_layer_rename; + item_class->start_move = gimp_layer_start_move; + item_class->end_move = gimp_layer_end_move; + item_class->translate = gimp_layer_translate; + item_class->scale = gimp_layer_scale; + item_class->resize = gimp_layer_resize; + item_class->flip = gimp_layer_flip; + item_class->rotate = gimp_layer_rotate; + item_class->transform = gimp_layer_transform; + item_class->to_selection = gimp_layer_to_selection; + item_class->default_name = _("Layer"); + item_class->rename_desc = C_("undo-type", "Rename Layer"); + item_class->translate_desc = C_("undo-type", "Move Layer"); + item_class->scale_desc = C_("undo-type", "Scale Layer"); + item_class->resize_desc = C_("undo-type", "Resize Layer"); + item_class->flip_desc = C_("undo-type", "Flip Layer"); + item_class->rotate_desc = C_("undo-type", "Rotate Layer"); + item_class->transform_desc = C_("undo-type", "Transform Layer"); + item_class->to_selection_desc = C_("undo-type", "Alpha to Selection"); + item_class->reorder_desc = C_("undo-type", "Reorder Layer"); + item_class->raise_desc = C_("undo-type", "Raise Layer"); + item_class->raise_to_top_desc = C_("undo-type", "Raise Layer to Top"); + item_class->lower_desc = C_("undo-type", "Lower Layer"); + item_class->lower_to_bottom_desc = C_("undo-type", "Lower Layer to Bottom"); + item_class->raise_failed = _("Layer cannot be raised higher."); + item_class->lower_failed = _("Layer cannot be lowered more."); + + drawable_class->alpha_changed = gimp_layer_alpha_changed; + drawable_class->estimate_memsize = gimp_layer_estimate_memsize; + drawable_class->supports_alpha = gimp_layer_supports_alpha; + drawable_class->convert_type = gimp_layer_convert_type; + drawable_class->invalidate_boundary = gimp_layer_invalidate_boundary; + drawable_class->get_active_components = gimp_layer_get_active_components; + drawable_class->get_active_mask = gimp_layer_get_active_mask; + drawable_class->set_buffer = gimp_layer_set_buffer; + drawable_class->get_bounding_box = gimp_layer_get_bounding_box; + + klass->opacity_changed = NULL; + klass->mode_changed = NULL; + klass->blend_space_changed = NULL; + klass->composite_space_changed = NULL; + klass->composite_mode_changed = NULL; + klass->excludes_backdrop_changed = NULL; + klass->lock_alpha_changed = NULL; + klass->mask_changed = NULL; + klass->apply_mask_changed = NULL; + klass->edit_mask_changed = NULL; + klass->show_mask_changed = NULL; + klass->translate = gimp_layer_real_translate; + klass->scale = gimp_layer_real_scale; + klass->resize = gimp_layer_real_resize; + klass->flip = gimp_layer_real_flip; + klass->rotate = gimp_layer_real_rotate; + klass->transform = gimp_layer_real_transform; + klass->convert_type = gimp_layer_real_convert_type; + klass->get_bounding_box = gimp_layer_real_get_bounding_box; + klass->get_effective_mode = gimp_layer_real_get_effective_mode; + klass->get_excludes_backdrop = gimp_layer_real_get_excludes_backdrop; + + g_object_class_install_property (object_class, PROP_OPACITY, + g_param_spec_double ("opacity", NULL, NULL, + GIMP_OPACITY_TRANSPARENT, + GIMP_OPACITY_OPAQUE, + GIMP_OPACITY_OPAQUE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_enum ("mode", NULL, NULL, + GIMP_TYPE_LAYER_MODE, + GIMP_LAYER_MODE_NORMAL, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_BLEND_SPACE, + g_param_spec_enum ("blend-space", + NULL, NULL, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_COMPOSITE_SPACE, + g_param_spec_enum ("composite-space", + NULL, NULL, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_COMPOSITE_MODE, + g_param_spec_enum ("composite-mode", + NULL, NULL, + GIMP_TYPE_LAYER_COMPOSITE_MODE, + GIMP_LAYER_COMPOSITE_AUTO, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_EXCLUDES_BACKDROP, + g_param_spec_boolean ("excludes-backdrop", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_LOCK_ALPHA, + g_param_spec_boolean ("lock-alpha", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_MASK, + g_param_spec_object ("mask", + NULL, NULL, + GIMP_TYPE_LAYER_MASK, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_FLOATING_SELECTION, + g_param_spec_boolean ("floating-selection", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_layer_init (GimpLayer *layer) +{ + layer->opacity = GIMP_OPACITY_OPAQUE; + layer->mode = GIMP_LAYER_MODE_NORMAL; + layer->blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + layer->composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + layer->composite_mode = GIMP_LAYER_COMPOSITE_AUTO; + layer->effective_mode = layer->mode; + layer->effective_blend_space = gimp_layer_get_real_blend_space (layer); + layer->effective_composite_space = gimp_layer_get_real_composite_space (layer); + layer->effective_composite_mode = gimp_layer_get_real_composite_mode (layer); + layer->excludes_backdrop = FALSE; + layer->lock_alpha = FALSE; + + layer->mask = NULL; + layer->apply_mask = TRUE; + layer->edit_mask = TRUE; + layer->show_mask = FALSE; + + /* floating selection */ + layer->fs.drawable = NULL; + layer->fs.boundary_known = FALSE; + layer->fs.segs = NULL; + layer->fs.num_segs = 0; +} + +static void +gimp_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_color_profile = gimp_layer_get_color_profile; +} + +static void +gimp_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->get_opacity_at = gimp_layer_get_opacity_at; + iface->pixel_to_srgb = gimp_layer_pixel_to_srgb; + iface->srgb_to_pixel = gimp_layer_srgb_to_pixel; +} + +static void +gimp_layer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_layer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLayer *layer = GIMP_LAYER (object); + + switch (property_id) + { + case PROP_OPACITY: + g_value_set_double (value, gimp_layer_get_opacity (layer)); + break; + case PROP_MODE: + g_value_set_enum (value, gimp_layer_get_mode (layer)); + break; + case PROP_BLEND_SPACE: + g_value_set_enum (value, gimp_layer_get_blend_space (layer)); + break; + case PROP_COMPOSITE_SPACE: + g_value_set_enum (value, gimp_layer_get_composite_space (layer)); + break; + case PROP_COMPOSITE_MODE: + g_value_set_enum (value, gimp_layer_get_composite_mode (layer)); + break; + case PROP_EXCLUDES_BACKDROP: + g_value_set_boolean (value, gimp_layer_get_excludes_backdrop (layer)); + break; + case PROP_LOCK_ALPHA: + g_value_set_boolean (value, gimp_layer_get_lock_alpha (layer)); + break; + case PROP_MASK: + g_value_set_object (value, gimp_layer_get_mask (layer)); + break; + case PROP_FLOATING_SELECTION: + g_value_set_boolean (value, gimp_layer_is_floating_sel (layer)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_layer_dispose (GObject *object) +{ + GimpLayer *layer = GIMP_LAYER (object); + + if (layer->mask) + g_signal_handlers_disconnect_by_func (layer->mask, + gimp_layer_layer_mask_update, + layer); + + if (gimp_layer_is_floating_sel (layer)) + { + GimpDrawable *fs_drawable = gimp_layer_get_floating_sel_drawable (layer); + + /* only detach if this is actually the drawable's fs because the + * layer might be on the undo stack and not attached to anything + */ + if (gimp_drawable_get_floating_sel (fs_drawable) == layer) + gimp_drawable_detach_floating_sel (fs_drawable); + + gimp_layer_set_floating_sel_drawable (layer, NULL); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_layer_finalize (GObject *object) +{ + GimpLayer *layer = GIMP_LAYER (object); + + g_clear_object (&layer->mask); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_layer_update_mode_node (GimpLayer *layer) +{ + GeglNode *mode_node; + GimpLayerMode visible_mode; + GimpLayerColorSpace visible_blend_space; + GimpLayerColorSpace visible_composite_space; + GimpLayerCompositeMode visible_composite_mode; + + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer)); + + if (layer->mask && layer->show_mask) + { + visible_mode = GIMP_LAYER_MODE_NORMAL; + visible_blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + visible_composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + visible_composite_mode = GIMP_LAYER_COMPOSITE_AUTO; + + /* This makes sure that masks of LEGACY-mode layers are + * composited in PERCEPTUAL space, and non-LEGACY layers in + * LINEAR space, or whatever composite space was chosen in the + * layer attributes dialog + */ + visible_composite_space = gimp_layer_get_real_composite_space (layer); + } + else + { + visible_mode = layer->effective_mode; + visible_blend_space = layer->effective_blend_space; + visible_composite_space = layer->effective_composite_space; + visible_composite_mode = layer->effective_composite_mode; + } + + gimp_gegl_mode_node_set_mode (mode_node, + visible_mode, + visible_blend_space, + visible_composite_space, + visible_composite_mode); + gimp_gegl_mode_node_set_opacity (mode_node, layer->opacity); +} + +static void +gimp_layer_notify (GObject *object, + GParamSpec *pspec) +{ + if (! strcmp (pspec->name, "is-last-node") && + gimp_filter_peek_node (GIMP_FILTER (object))) + { + gimp_layer_update_mode_node (GIMP_LAYER (object)); + + gimp_drawable_update (GIMP_DRAWABLE (object), 0, 0, -1, -1); + } +} + +static void +gimp_layer_name_changed (GimpObject *object) +{ + GimpLayer *layer = GIMP_LAYER (object); + + if (GIMP_OBJECT_CLASS (parent_class)->name_changed) + GIMP_OBJECT_CLASS (parent_class)->name_changed (object); + + if (layer->mask) + { + gchar *mask_name = g_strdup_printf (_("%s mask"), + gimp_object_get_name (object)); + + gimp_object_take_name (GIMP_OBJECT (layer->mask), mask_name); + } +} + +static gint64 +gimp_layer_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpLayer *layer = GIMP_LAYER (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (layer->mask), gui_size); + + *gui_size += layer->fs.num_segs * sizeof (GimpBoundSeg); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_layer_invalidate_preview (GimpViewable *viewable) +{ + GimpLayer *layer = GIMP_LAYER (viewable); + + GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable); + + if (gimp_layer_is_floating_sel (layer)) + floating_sel_invalidate (layer); +} + +static gchar * +gimp_layer_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + if (gimp_layer_is_floating_sel (GIMP_LAYER (viewable))) + { + return g_strdup_printf (_("Floating Selection\n(%s)"), + gimp_object_get_name (viewable)); + } + + return GIMP_VIEWABLE_CLASS (parent_class)->get_description (viewable, + tooltip); +} + +static GeglNode * +gimp_layer_get_node (GimpFilter *filter) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (filter); + GimpLayer *layer = GIMP_LAYER (filter); + GeglNode *node; + GeglNode *input; + GeglNode *source; + GeglNode *mode_node; + gboolean source_node_hijacked = FALSE; + + node = GIMP_FILTER_CLASS (parent_class)->get_node (filter); + + input = gegl_node_get_input_proxy (node, "input"); + + source = gimp_drawable_get_source_node (drawable); + + /* if the source node already has a parent, we are a floating + * selection and the source node has been hijacked by the fs' + * drawable + */ + if (gegl_node_get_parent (source)) + source_node_hijacked = TRUE; + + if (! source_node_hijacked) + gegl_node_add_child (node, source); + + gegl_node_connect_to (input, "output", + source, "input"); + + g_warn_if_fail (layer->layer_offset_node == NULL); + g_warn_if_fail (layer->mask_offset_node == NULL); + + /* the mode node connects it all, and has aux and aux2 inputs for + * the layer and its mask + */ + mode_node = gimp_drawable_get_mode_node (drawable); + gimp_layer_update_mode_node (layer); + + /* the layer's offset node */ + layer->layer_offset_node = gegl_node_new_child (node, + "operation", "gegl:translate", + NULL); + gimp_item_add_offset_node (GIMP_ITEM (layer), layer->layer_offset_node); + + /* the layer mask's offset node */ + layer->mask_offset_node = gegl_node_new_child (node, + "operation", "gegl:translate", + NULL); + gimp_item_add_offset_node (GIMP_ITEM (layer), layer->mask_offset_node); + + if (! source_node_hijacked) + { + gegl_node_connect_to (source, "output", + layer->layer_offset_node, "input"); + } + + if (! (layer->mask && gimp_layer_get_show_mask (layer))) + { + gegl_node_connect_to (layer->layer_offset_node, "output", + mode_node, "aux"); + } + + if (layer->mask) + { + GeglNode *mask; + + mask = gimp_drawable_get_source_node (GIMP_DRAWABLE (layer->mask)); + + gegl_node_connect_to (mask, "output", + layer->mask_offset_node, "input"); + + if (gimp_layer_get_show_mask (layer)) + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux"); + } + else if (gimp_layer_get_apply_mask (layer)) + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux2"); + } + } + + return node; +} + +static void +gimp_layer_removed (GimpItem *item) +{ + GimpLayer *layer = GIMP_LAYER (item); + + if (layer->mask) + gimp_item_removed (GIMP_ITEM (layer->mask)); + + if (GIMP_ITEM_CLASS (parent_class)->removed) + GIMP_ITEM_CLASS (parent_class)->removed (item); +} + +static void +gimp_layer_unset_removed (GimpItem *item) +{ + GimpLayer *layer = GIMP_LAYER (item); + + if (layer->mask) + gimp_item_unset_removed (GIMP_ITEM (layer->mask)); + + if (GIMP_ITEM_CLASS (parent_class)->unset_removed) + GIMP_ITEM_CLASS (parent_class)->unset_removed (item); +} + +static gboolean +gimp_layer_is_attached (GimpItem *item) +{ + GimpImage *image = gimp_item_get_image (item); + + return (GIMP_IS_IMAGE (image) && + gimp_container_have (gimp_image_get_layers (image), + GIMP_OBJECT (item))); +} + +static GimpItemTree * +gimp_layer_get_tree (GimpItem *item) +{ + if (gimp_item_is_attached (item)) + { + GimpImage *image = gimp_item_get_image (item); + + return gimp_image_get_layer_tree (image); + } + + return NULL; +} + +static GimpItem * +gimp_layer_duplicate (GimpItem *item, + GType new_type) +{ + GimpItem *new_item; + + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL); + + new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type); + + if (GIMP_IS_LAYER (new_item)) + { + GimpLayer *layer = GIMP_LAYER (item); + GimpLayer *new_layer = GIMP_LAYER (new_item); + + gimp_layer_set_mode (new_layer, + gimp_layer_get_mode (layer), FALSE); + gimp_layer_set_blend_space (new_layer, + gimp_layer_get_blend_space (layer), FALSE); + gimp_layer_set_composite_space (new_layer, + gimp_layer_get_composite_space (layer), FALSE); + gimp_layer_set_composite_mode (new_layer, + gimp_layer_get_composite_mode (layer), FALSE); + gimp_layer_set_opacity (new_layer, + gimp_layer_get_opacity (layer), FALSE); + + if (gimp_layer_can_lock_alpha (new_layer)) + gimp_layer_set_lock_alpha (new_layer, + gimp_layer_get_lock_alpha (layer), FALSE); + + /* duplicate the layer mask if necessary */ + if (layer->mask) + { + GimpItem *mask; + + mask = gimp_item_duplicate (GIMP_ITEM (layer->mask), + G_TYPE_FROM_INSTANCE (layer->mask)); + gimp_layer_add_mask (new_layer, GIMP_LAYER_MASK (mask), FALSE, NULL); + + new_layer->apply_mask = layer->apply_mask; + new_layer->edit_mask = layer->edit_mask; + new_layer->show_mask = layer->show_mask; + } + } + + return new_item; +} + +static void +gimp_layer_convert (GimpItem *item, + GimpImage *dest_image, + GType old_type) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GimpImageBaseType old_base_type; + GimpImageBaseType new_base_type; + GimpPrecision old_precision; + GimpPrecision new_precision; + GimpColorProfile *dest_profile = NULL; + + old_base_type = gimp_drawable_get_base_type (drawable); + new_base_type = gimp_image_get_base_type (dest_image); + + old_precision = gimp_drawable_get_precision (drawable); + new_precision = gimp_image_get_precision (dest_image); + + if (g_type_is_a (old_type, GIMP_TYPE_LAYER) && + gimp_image_get_is_color_managed (dest_image)) + { + GimpColorProfile *src_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (item)); + + dest_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (dest_image)); + + if (gimp_color_profile_is_equal (dest_profile, src_profile)) + dest_profile = NULL; + } + + if (old_base_type != new_base_type || + old_precision != new_precision || + dest_profile) + { + gimp_drawable_convert_type (drawable, dest_image, + new_base_type, + new_precision, + gimp_drawable_has_alpha (drawable), + dest_profile, + GEGL_DITHER_NONE, GEGL_DITHER_NONE, + FALSE, NULL); + } + + if (layer->mask) + gimp_item_set_image (GIMP_ITEM (layer->mask), dest_image); + + GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type); +} + +static gboolean +gimp_layer_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpImage *image = gimp_item_get_image (item); + gboolean attached; + gboolean floating_sel; + + attached = gimp_item_is_attached (item); + floating_sel = gimp_layer_is_floating_sel (layer); + + if (floating_sel) + { + if (GIMP_IS_CHANNEL (gimp_layer_get_floating_sel_drawable (layer))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot create a new layer from the floating " + "selection because it belongs to a layer mask " + "or channel.")); + return FALSE; + } + + if (attached) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_ITEM_PROPERTIES, + undo_desc); + + floating_sel_to_layer (layer, NULL); + } + } + + GIMP_ITEM_CLASS (parent_class)->rename (item, new_name, undo_desc, error); + + if (attached && floating_sel) + gimp_image_undo_group_end (image); + + return TRUE; +} + +static void +gimp_layer_start_move (GimpItem *item, + gboolean push_undo) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpLayer *ancestor = layer; + GSList *ancestors = NULL; + + /* suspend mask cropping for all of the layer's ancestors */ + while ((ancestor = gimp_layer_get_parent (ancestor))) + { + gimp_group_layer_suspend_mask (GIMP_GROUP_LAYER (ancestor), push_undo); + + ancestors = g_slist_prepend (ancestors, g_object_ref (ancestor)); + } + + /* we keep the ancestor list around, so that we can resume mask cropping for + * the same set of groups in gimp_layer_end_move(). note that + * gimp_image_remove_layer() calls start_move() before removing the layer, + * while it's still part of the layer tree, and end_move() afterwards, when + * it's no longer part of the layer tree, and hence we can't use get_parent() + * in end_move() to get the same set of ancestors. + */ + layer->move_stack = g_slist_prepend (layer->move_stack, ancestors); + + if (GIMP_ITEM_CLASS (parent_class)->start_move) + GIMP_ITEM_CLASS (parent_class)->start_move (item, push_undo); +} + +static void +gimp_layer_end_move (GimpItem *item, + gboolean push_undo) +{ + GimpLayer *layer = GIMP_LAYER (item); + GSList *ancestors; + GSList *iter; + + g_return_if_fail (layer->move_stack != NULL); + + if (GIMP_ITEM_CLASS (parent_class)->end_move) + GIMP_ITEM_CLASS (parent_class)->end_move (item, push_undo); + + ancestors = layer->move_stack->data; + + layer->move_stack = g_slist_remove (layer->move_stack, ancestors); + + /* resume mask cropping for all of the layer's ancestors */ + for (iter = ancestors; iter; iter = g_slist_next (iter)) + { + GimpGroupLayer *ancestor = iter->data; + + gimp_group_layer_resume_mask (ancestor, push_undo); + + g_object_unref (ancestor); + } + + g_slist_free (ancestors); +} + +static void +gimp_layer_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo) +{ + GimpLayer *layer = GIMP_LAYER (item); + + if (push_undo) + gimp_image_undo_push_item_displace (gimp_item_get_image (item), NULL, item); + + GIMP_LAYER_GET_CLASS (layer)->translate (layer, + SIGNED_ROUND (offset_x), + SIGNED_ROUND (offset_y)); + + if (layer->mask) + { + gint off_x, off_y; + + gimp_item_get_offset (item, &off_x, &off_y); + gimp_item_set_offset (GIMP_ITEM (layer->mask), off_x, off_y); + + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer->mask)); + } +} + +static void +gimp_layer_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpObjectQueue *queue = NULL; + + if (progress && layer->mask) + { + GimpLayerMask *mask; + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + /* temporarily set layer->mask to NULL, so that its size won't be counted + * when pushing the layer to the queue. + */ + mask = layer->mask; + layer->mask = NULL; + + gimp_object_queue_push (queue, layer); + gimp_object_queue_push (queue, mask); + + layer->mask = mask; + } + + if (queue) + gimp_object_queue_pop (queue); + + GIMP_LAYER_GET_CLASS (layer)->scale (layer, new_width, new_height, + new_offset_x, new_offset_y, + interpolation_type, progress); + + if (layer->mask) + { + if (queue) + gimp_object_queue_pop (queue); + + gimp_item_scale (GIMP_ITEM (layer->mask), + new_width, new_height, + new_offset_x, new_offset_y, + interpolation_type, progress); + } + + g_clear_object (&queue); +} + +static void +gimp_layer_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GimpLayer *layer = GIMP_LAYER (item); + + GIMP_LAYER_GET_CLASS (layer)->resize (layer, context, fill_type, + new_width, new_height, + offset_x, offset_y); + + if (layer->mask) + gimp_item_resize (GIMP_ITEM (layer->mask), context, GIMP_FILL_TRANSPARENT, + new_width, new_height, offset_x, offset_y); +} + +static void +gimp_layer_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GimpLayer *layer = GIMP_LAYER (item); + + GIMP_LAYER_GET_CLASS (layer)->flip (layer, context, flip_type, axis, + clip_result); + + if (layer->mask) + gimp_item_flip (GIMP_ITEM (layer->mask), context, + flip_type, axis, clip_result); +} + +static void +gimp_layer_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GimpLayer *layer = GIMP_LAYER (item); + + GIMP_LAYER_GET_CLASS (layer)->rotate (layer, context, + rotate_type, center_x, center_y, + clip_result); + + if (layer->mask) + gimp_item_rotate (GIMP_ITEM (layer->mask), context, + rotate_type, center_x, center_y, clip_result); +} + +static void +gimp_layer_transform (GimpItem *item, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpObjectQueue *queue = NULL; + + if (progress && layer->mask) + { + GimpLayerMask *mask; + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + /* temporarily set layer->mask to NULL, so that its size won't be counted + * when pushing the layer to the queue. + */ + mask = layer->mask; + layer->mask = NULL; + + gimp_object_queue_push (queue, layer); + gimp_object_queue_push (queue, mask); + + layer->mask = mask; + } + + if (queue) + gimp_object_queue_pop (queue); + + GIMP_LAYER_GET_CLASS (layer)->transform (layer, context, matrix, direction, + interpolation_type, + clip_result, + progress); + + if (layer->mask) + { + if (queue) + gimp_object_queue_pop (queue); + + gimp_item_transform (GIMP_ITEM (layer->mask), context, + matrix, direction, + interpolation_type, + clip_result, progress); + } + + g_clear_object (&queue); +} + +static void +gimp_layer_to_selection (GimpItem *item, + GimpChannelOps op, + gboolean antialias, + gboolean feather, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + GimpLayer *layer = GIMP_LAYER (item); + GimpImage *image = gimp_item_get_image (item); + + gimp_channel_select_alpha (gimp_image_get_mask (image), + GIMP_DRAWABLE (layer), + op, + feather, feather_radius_x, feather_radius_y); +} + +static void +gimp_layer_alpha_changed (GimpDrawable *drawable) +{ + if (GIMP_DRAWABLE_CLASS (parent_class)->alpha_changed) + GIMP_DRAWABLE_CLASS (parent_class)->alpha_changed (drawable); + + /* When we add/remove alpha, whatever cached color transforms in + * view renderers need to be recreated because they cache the wrong + * lcms formats. See bug 478528. + */ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (drawable)); +} + +static gint64 +gimp_layer_estimate_memsize (GimpDrawable *drawable, + GimpComponentType component_type, + gint width, + gint height) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + gint64 memsize = 0; + + if (layer->mask) + memsize += gimp_drawable_estimate_memsize (GIMP_DRAWABLE (layer->mask), + component_type, + width, height); + + return memsize + + GIMP_DRAWABLE_CLASS (parent_class)->estimate_memsize (drawable, + component_type, + width, height); +} + +static gboolean +gimp_layer_supports_alpha (GimpDrawable *drawable) +{ + return TRUE; +} + +static void +gimp_layer_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + GimpObjectQueue *queue = NULL; + gboolean convert_mask; + + convert_mask = layer->mask && + gimp_babl_format_get_precision (new_format) != + gimp_drawable_get_precision (GIMP_DRAWABLE (layer->mask)); + + if (progress && convert_mask) + { + GimpLayerMask *mask; + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + /* temporarily set layer->mask to NULL, so that its size won't be counted + * when pushing the layer to the queue. + */ + mask = layer->mask; + layer->mask = NULL; + + gimp_object_queue_push (queue, layer); + gimp_object_queue_push (queue, mask); + + layer->mask = mask; + } + + if (queue) + gimp_object_queue_pop (queue); + + GIMP_LAYER_GET_CLASS (layer)->convert_type (layer, dest_image, new_format, + dest_profile, layer_dither_type, + mask_dither_type, push_undo, + progress); + + if (convert_mask) + { + if (queue) + gimp_object_queue_pop (queue); + + gimp_drawable_convert_type (GIMP_DRAWABLE (layer->mask), dest_image, + GIMP_GRAY, + gimp_babl_format_get_precision (new_format), + gimp_drawable_has_alpha (GIMP_DRAWABLE (layer->mask)), + NULL, + layer_dither_type, mask_dither_type, + push_undo, progress); + } + + g_clear_object (&queue); +} + +static void +gimp_layer_invalidate_boundary (GimpDrawable *drawable) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + + if (gimp_item_is_attached (GIMP_ITEM (drawable)) && + gimp_item_is_visible (GIMP_ITEM (drawable))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpChannel *mask = gimp_image_get_mask (image); + + /* Turn the current selection off */ + gimp_image_selection_invalidate (image); + + /* Only bother with the bounds if there is a selection */ + if (! gimp_channel_is_empty (mask)) + { + mask->bounds_known = FALSE; + mask->boundary_known = FALSE; + } + } + + if (gimp_layer_is_floating_sel (layer)) + floating_sel_invalidate (layer); +} + +static void +gimp_layer_get_active_components (GimpDrawable *drawable, + gboolean *active) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + const Babl *format = gimp_drawable_get_format (drawable); + + /* first copy the image active channels */ + gimp_image_get_active_array (image, active); + + if (gimp_drawable_has_alpha (drawable) && layer->lock_alpha) + active[babl_format_get_n_components (format) - 1] = FALSE; +} + +static GimpComponentMask +gimp_layer_get_active_mask (GimpDrawable *drawable) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpComponentMask mask = gimp_image_get_active_mask (image); + + if (gimp_drawable_has_alpha (drawable) && layer->lock_alpha) + mask &= ~GIMP_COMPONENT_MASK_ALPHA; + + return mask; +} + +static void +gimp_layer_set_buffer (GimpDrawable *drawable, + gboolean push_undo, + const gchar *undo_desc, + GeglBuffer *buffer, + const GeglRectangle *bounds) +{ + GeglBuffer *old_buffer = gimp_drawable_get_buffer (drawable); + gint old_linear = -1; + + if (old_buffer) + old_linear = gimp_drawable_get_linear (drawable); + + GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable, + push_undo, undo_desc, + buffer, bounds); + + if (gimp_filter_peek_node (GIMP_FILTER (drawable))) + { + if (gimp_drawable_get_linear (drawable) != old_linear) + gimp_layer_update_mode_node (GIMP_LAYER (drawable)); + } +} + +static GeglRectangle +gimp_layer_get_bounding_box (GimpDrawable *drawable) +{ + GimpLayer *layer = GIMP_LAYER (drawable); + GimpLayerMask *mask = gimp_layer_get_mask (layer); + GeglRectangle bounding_box; + + if (mask && gimp_layer_get_show_mask (layer)) + { + bounding_box = gimp_drawable_get_bounding_box (GIMP_DRAWABLE (mask)); + } + else + { + bounding_box = GIMP_LAYER_GET_CLASS (layer)->get_bounding_box (layer); + + if (mask && gimp_layer_get_apply_mask (layer)) + { + GeglRectangle mask_bounding_box; + + mask_bounding_box = gimp_drawable_get_bounding_box ( + GIMP_DRAWABLE (mask)); + + gegl_rectangle_intersect (&bounding_box, + &bounding_box, &mask_bounding_box); + } + } + + return bounding_box; +} + +static GimpColorProfile * +gimp_layer_get_color_profile (GimpColorManaged *managed) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (managed)); + + return gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); +} + +static gdouble +gimp_layer_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + GimpLayer *layer = GIMP_LAYER (pickable); + gdouble value = GIMP_OPACITY_TRANSPARENT; + + if (x >= 0 && x < gimp_item_get_width (GIMP_ITEM (layer)) && + y >= 0 && y < gimp_item_get_height (GIMP_ITEM (layer)) && + gimp_item_is_visible (GIMP_ITEM (layer))) + { + if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + value = GIMP_OPACITY_OPAQUE; + } + else + { + gegl_buffer_sample (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + x, y, NULL, &value, babl_format ("A double"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + } + + if (gimp_layer_get_mask (layer) && + gimp_layer_get_apply_mask (layer)) + { + gdouble mask_value; + + mask_value = gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer->mask), + x, y); + + value *= mask_value; + } + } + + return value; +} + +static void +gimp_layer_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (pickable)); + + gimp_pickable_pixel_to_srgb (GIMP_PICKABLE (image), format, pixel, color); +} + +static void +gimp_layer_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (pickable)); + + gimp_pickable_srgb_to_pixel (GIMP_PICKABLE (image), color, format, pixel); +} + +static void +gimp_layer_real_translate (GimpLayer *layer, + gint offset_x, + gint offset_y) +{ + /* update the old region */ + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + + /* invalidate the selection boundary because of a layer modification */ + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); + + GIMP_ITEM_CLASS (parent_class)->translate (GIMP_ITEM (layer), + offset_x, offset_y, + FALSE); + + /* update the new region */ + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); +} + +static void +gimp_layer_real_scale (GimpLayer *layer, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress) +{ + GIMP_ITEM_CLASS (parent_class)->scale (GIMP_ITEM (layer), + new_width, new_height, + new_offset_x, new_offset_y, + interpolation_type, progress); +} + +static void +gimp_layer_real_resize (GimpLayer *layer, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + if (fill_type == GIMP_FILL_TRANSPARENT && + ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + fill_type = GIMP_FILL_BACKGROUND; + } + + GIMP_ITEM_CLASS (parent_class)->resize (GIMP_ITEM (layer), + context, fill_type, + new_width, new_height, + offset_x, offset_y); +} + +static void +gimp_layer_real_flip (GimpLayer *layer, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GIMP_ITEM_CLASS (parent_class)->flip (GIMP_ITEM (layer), + context, flip_type, axis, clip_result); +} + +static void +gimp_layer_real_rotate (GimpLayer *layer, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GIMP_ITEM_CLASS (parent_class)->rotate (GIMP_ITEM (layer), + context, rotate_type, + center_x, center_y, + clip_result); +} + +static void +gimp_layer_real_transform (GimpLayer *layer, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress) +{ + /* FIXME: make interpolated transformations work on layers without alpha */ + if (interpolation_type != GIMP_INTERPOLATION_NONE && + ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + gimp_layer_add_alpha (layer); + + GIMP_ITEM_CLASS (parent_class)->transform (GIMP_ITEM (layer), + context, matrix, direction, + interpolation_type, + clip_result, + progress); +} + +static void +gimp_layer_real_convert_type (GimpLayer *layer, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + GimpDrawable *drawable = GIMP_DRAWABLE (layer); + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + + if (layer_dither_type == GEGL_DITHER_NONE) + { + src_buffer = g_object_ref (gimp_drawable_get_buffer (drawable)); + } + else + { + gint bits; + + src_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (layer)), + gimp_item_get_height (GIMP_ITEM (layer))), + gimp_drawable_get_format (drawable)); + + bits = (babl_format_get_bytes_per_pixel (new_format) * 8 / + babl_format_get_n_components (new_format)); + + gimp_gegl_apply_dither (gimp_drawable_get_buffer (drawable), + NULL, NULL, + src_buffer, 1 << bits, layer_dither_type); + } + + dest_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (layer)), + gimp_item_get_height (GIMP_ITEM (layer))), + new_format); + + if (dest_profile) + { + GimpColorProfile *src_profile = + gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (layer)); + + gimp_gegl_convert_color_profile (src_buffer, NULL, src_profile, + dest_buffer, NULL, dest_profile, + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + TRUE, progress); + } + else + { + gimp_gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, + dest_buffer, NULL); + } + + gimp_drawable_set_buffer (drawable, push_undo, NULL, dest_buffer); + + g_object_unref (src_buffer); + g_object_unref (dest_buffer); +} + +static GeglRectangle +gimp_layer_real_get_bounding_box (GimpLayer *layer) +{ + return GIMP_DRAWABLE_CLASS (parent_class)->get_bounding_box ( + GIMP_DRAWABLE (layer)); +} + +static void +gimp_layer_real_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode) +{ + *mode = gimp_layer_get_mode (layer); + *blend_space = gimp_layer_get_real_blend_space (layer); + *composite_space = gimp_layer_get_real_composite_space (layer); + *composite_mode = gimp_layer_get_real_composite_mode (layer); +} + +static gboolean +gimp_layer_real_get_excludes_backdrop (GimpLayer *layer) +{ + GimpLayerCompositeRegion included_region; + + included_region = gimp_layer_mode_get_included_region (layer->mode, + layer->effective_composite_mode); + + return ! (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION); +} + +static void +gimp_layer_layer_mask_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpLayer *layer) +{ + if (gimp_layer_get_apply_mask (layer) || + gimp_layer_get_show_mask (layer)) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), + x, y, width, height); + } +} + + +/* public functions */ + +GimpLayer * +gimp_layer_get_parent (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + + return GIMP_LAYER (gimp_viewable_get_parent (GIMP_VIEWABLE (layer))); +} + +GimpLayerMask * +gimp_layer_get_mask (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + + return layer->mask; +} + +GimpLayerMask * +gimp_layer_add_mask (GimpLayer *layer, + GimpLayerMask *mask, + gboolean push_undo, + GError **error) +{ + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), NULL); + g_return_val_if_fail (gimp_item_get_image (GIMP_ITEM (layer)) == + gimp_item_get_image (GIMP_ITEM (mask)), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (! gimp_item_is_attached (GIMP_ITEM (layer))) + push_undo = FALSE; + + image = gimp_item_get_image (GIMP_ITEM (layer)); + + if (layer->mask) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Unable to add a layer mask since " + "the layer already has one.")); + return NULL; + } + + if ((gimp_item_get_width (GIMP_ITEM (layer)) != + gimp_item_get_width (GIMP_ITEM (mask))) || + (gimp_item_get_height (GIMP_ITEM (layer)) != + gimp_item_get_height (GIMP_ITEM (mask)))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot add layer mask of different " + "dimensions than specified layer.")); + return NULL; + } + + if (push_undo) + gimp_image_undo_push_layer_mask_add (image, C_("undo-type", "Add Layer Mask"), + layer, mask); + + layer->mask = g_object_ref_sink (mask); + layer->apply_mask = TRUE; + layer->edit_mask = TRUE; + layer->show_mask = FALSE; + + gimp_layer_mask_set_layer (mask, layer); + + if (gimp_filter_peek_node (GIMP_FILTER (layer))) + { + GeglNode *mode_node; + GeglNode *mask; + + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer)); + + mask = gimp_drawable_get_source_node (GIMP_DRAWABLE (layer->mask)); + + gegl_node_connect_to (mask, "output", + layer->mask_offset_node, "input"); + + if (layer->show_mask) + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux"); + } + else + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux2"); + } + + gimp_layer_update_mode_node (layer); + } + + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (layer)); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + + if (gimp_layer_get_apply_mask (layer) || + gimp_layer_get_show_mask (layer)) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + } + + g_signal_connect (mask, "update", + G_CALLBACK (gimp_layer_layer_mask_update), + layer); + + g_signal_emit (layer, layer_signals[MASK_CHANGED], 0); + + g_object_notify (G_OBJECT (layer), "mask"); + + /* if the mask came from the undo stack, reset its "removed" state */ + if (gimp_item_is_removed (GIMP_ITEM (mask))) + gimp_item_unset_removed (GIMP_ITEM (mask)); + + return layer->mask; +} + +GimpLayerMask * +gimp_layer_create_mask (GimpLayer *layer, + GimpAddMaskType add_mask_type, + GimpChannel *channel) +{ + GimpDrawable *drawable; + GimpItem *item; + GimpLayerMask *mask; + GimpImage *image; + gchar *mask_name; + GimpRGB black = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE }; + + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (add_mask_type != GIMP_ADD_MASK_CHANNEL || + GIMP_IS_CHANNEL (channel), NULL); + + drawable = GIMP_DRAWABLE (layer); + item = GIMP_ITEM (layer); + image = gimp_item_get_image (item); + + mask_name = g_strdup_printf (_("%s mask"), + gimp_object_get_name (layer)); + + mask = gimp_layer_mask_new (image, + gimp_item_get_width (item), + gimp_item_get_height (item), + mask_name, &black); + + g_free (mask_name); + + switch (add_mask_type) + { + case GIMP_ADD_MASK_WHITE: + gimp_channel_all (GIMP_CHANNEL (mask), FALSE); + break; + + case GIMP_ADD_MASK_BLACK: + gimp_channel_clear (GIMP_CHANNEL (mask), NULL, FALSE); + break; + + case GIMP_ADD_MASK_ALPHA: + case GIMP_ADD_MASK_ALPHA_TRANSFER: + if (gimp_drawable_has_alpha (drawable)) + { + GeglBuffer *dest_buffer; + const Babl *component_format; + + dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + component_format = + gimp_image_get_component_format (image, GIMP_CHANNEL_ALPHA); + + gegl_buffer_set_format (dest_buffer, component_format); + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL, + GEGL_ABYSS_NONE, + dest_buffer, NULL); + gegl_buffer_set_format (dest_buffer, NULL); + + if (add_mask_type == GIMP_ADD_MASK_ALPHA_TRANSFER) + { + gimp_drawable_push_undo (drawable, + C_("undo-type", "Transfer Alpha to Mask"), + NULL, + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)); + + gimp_gegl_apply_set_alpha (gimp_drawable_get_buffer (drawable), + NULL, NULL, + gimp_drawable_get_buffer (drawable), + 1.0); + } + } + break; + + case GIMP_ADD_MASK_SELECTION: + case GIMP_ADD_MASK_CHANNEL: + { + gboolean channel_empty; + gint offset_x, offset_y; + gint copy_x, copy_y; + gint copy_width, copy_height; + + if (add_mask_type == GIMP_ADD_MASK_SELECTION) + channel = GIMP_CHANNEL (gimp_image_get_mask (image)); + + channel_empty = gimp_channel_is_empty (channel); + + gimp_item_get_offset (item, &offset_x, &offset_y); + + gimp_rectangle_intersect (0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image), + offset_x, offset_y, + gimp_item_get_width (item), + gimp_item_get_height (item), + ©_x, ©_y, + ©_width, ©_height); + + if (copy_width < gimp_item_get_width (item) || + copy_height < gimp_item_get_height (item) || + channel_empty) + gimp_channel_clear (GIMP_CHANNEL (mask), NULL, FALSE); + + if ((copy_width || copy_height) && ! channel_empty) + { + GeglBuffer *src; + GeglBuffer *dest; + const Babl *format; + + src = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + dest = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + format = gegl_buffer_get_format (src); + + /* make sure no gamma conversion happens */ + gegl_buffer_set_format (dest, format); + gimp_gegl_buffer_copy (src, + GEGL_RECTANGLE (copy_x, copy_y, + copy_width, copy_height), + GEGL_ABYSS_NONE, + dest, + GEGL_RECTANGLE (copy_x - offset_x, + copy_y - offset_y, + 0, 0)); + gegl_buffer_set_format (dest, NULL); + + GIMP_CHANNEL (mask)->bounds_known = FALSE; + } + } + break; + + case GIMP_ADD_MASK_COPY: + { + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + + if (! gimp_drawable_is_gray (drawable)) + { + const Babl *copy_format = + gimp_image_get_format (image, GIMP_GRAY, + gimp_drawable_get_precision (drawable), + gimp_drawable_has_alpha (drawable)); + + src_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + copy_format); + + gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL, + GEGL_ABYSS_NONE, + src_buffer, NULL); + } + else + { + src_buffer = gimp_drawable_get_buffer (drawable); + g_object_ref (src_buffer); + } + + dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + if (gimp_drawable_has_alpha (drawable)) + { + GimpRGB background; + + gimp_rgba_set (&background, 0.0, 0.0, 0.0, 0.0); + + gimp_gegl_apply_flatten (src_buffer, NULL, NULL, + dest_buffer, &background, + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR); + } + else + { + gimp_gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, + dest_buffer, NULL); + } + + g_object_unref (src_buffer); + } + + GIMP_CHANNEL (mask)->bounds_known = FALSE; + break; + } + + return mask; +} + +void +gimp_layer_apply_mask (GimpLayer *layer, + GimpMaskApplyMode mode, + gboolean push_undo) +{ + GimpItem *item; + GimpImage *image; + GimpLayerMask *mask; + gboolean view_changed = FALSE; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + + mask = gimp_layer_get_mask (layer); + + if (! mask) + return; + + /* APPLY can not be done to group layers */ + g_return_if_fail (! gimp_viewable_get_children (GIMP_VIEWABLE (layer)) || + mode == GIMP_MASK_DISCARD); + + /* APPLY can only be done to layers with an alpha channel */ + g_return_if_fail (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)) || + mode == GIMP_MASK_DISCARD || push_undo == TRUE); + + item = GIMP_ITEM (layer); + image = gimp_item_get_image (item); + + if (! image) + return; + + if (push_undo) + { + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_APPLY_MASK, + (mode == GIMP_MASK_APPLY) ? + C_("undo-type", "Apply Layer Mask") : + C_("undo-type", "Delete Layer Mask")); + + gimp_image_undo_push_layer_mask_show (image, NULL, layer); + gimp_image_undo_push_layer_mask_apply (image, NULL, layer); + gimp_image_undo_push_layer_mask_remove (image, NULL, layer, mask); + + if (mode == GIMP_MASK_APPLY && + ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + { + gimp_layer_add_alpha (layer); + } + } + + /* check if applying the mask changes the projection */ + if (gimp_layer_get_show_mask (layer) || + (mode == GIMP_MASK_APPLY && ! gimp_layer_get_apply_mask (layer)) || + (mode == GIMP_MASK_DISCARD && gimp_layer_get_apply_mask (layer))) + { + view_changed = TRUE; + } + + if (mode == GIMP_MASK_APPLY) + { + GeglBuffer *mask_buffer; + GeglBuffer *dest_buffer; + + if (push_undo) + gimp_drawable_push_undo (GIMP_DRAWABLE (layer), NULL, + NULL, + 0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)); + + /* Combine the current layer's alpha channel and the mask */ + mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + gimp_gegl_apply_opacity (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, NULL, dest_buffer, + mask_buffer, 0, 0, 1.0); + } + + g_signal_handlers_disconnect_by_func (mask, + gimp_layer_layer_mask_update, + layer); + + gimp_item_removed (GIMP_ITEM (mask)); + g_object_unref (mask); + layer->mask = NULL; + + if (push_undo) + gimp_image_undo_group_end (image); + + if (gimp_filter_peek_node (GIMP_FILTER (layer))) + { + GeglNode *mode_node; + + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer)); + + if (layer->show_mask) + { + gegl_node_connect_to (layer->layer_offset_node, "output", + mode_node, "aux"); + } + else + { + gegl_node_disconnect (mode_node, "aux2"); + } + + gimp_layer_update_mode_node (layer); + } + + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (layer)); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + + /* If applying actually changed the view */ + if (view_changed) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + } + else + { + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer)); + } + + g_signal_emit (layer, layer_signals[MASK_CHANGED], 0); + + g_object_notify (G_OBJECT (layer), "mask"); +} + +void +gimp_layer_set_apply_mask (GimpLayer *layer, + gboolean apply, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (layer->mask != NULL); + + if (layer->apply_mask != apply) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + gimp_image_undo_push_layer_mask_apply (image, + apply ? + C_("undo-type", "Enable Layer Mask") : + C_("undo-type", "Disable Layer Mask"), + layer); + + layer->apply_mask = apply ? TRUE : FALSE; + + if (gimp_filter_peek_node (GIMP_FILTER (layer)) && + ! gimp_layer_get_show_mask (layer)) + { + GeglNode *mode_node; + + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer)); + + if (layer->apply_mask) + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux2"); + } + else + { + gegl_node_disconnect (mode_node, "aux2"); + } + } + + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (layer)); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + + g_signal_emit (layer, layer_signals[APPLY_MASK_CHANGED], 0); + } +} + +gboolean +gimp_layer_get_apply_mask (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + g_return_val_if_fail (layer->mask, FALSE); + + return layer->apply_mask; +} + +void +gimp_layer_set_edit_mask (GimpLayer *layer, + gboolean edit) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (layer->mask != NULL); + + if (layer->edit_mask != edit) + { + layer->edit_mask = edit ? TRUE : FALSE; + + g_signal_emit (layer, layer_signals[EDIT_MASK_CHANGED], 0); + } +} + +gboolean +gimp_layer_get_edit_mask (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + g_return_val_if_fail (layer->mask, FALSE); + + return layer->edit_mask; +} + +void +gimp_layer_set_show_mask (GimpLayer *layer, + gboolean show, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (layer->mask != NULL); + + if (layer->show_mask != show) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + if (push_undo) + gimp_image_undo_push_layer_mask_show (image, + C_("undo-type", "Show Layer Mask"), + layer); + + layer->show_mask = show ? TRUE : FALSE; + + if (gimp_filter_peek_node (GIMP_FILTER (layer))) + { + GeglNode *mode_node; + + mode_node = gimp_drawable_get_mode_node (GIMP_DRAWABLE (layer)); + + if (layer->show_mask) + { + gegl_node_disconnect (mode_node, "aux2"); + + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux"); + } + else + { + gegl_node_connect_to (layer->layer_offset_node, "output", + mode_node, "aux"); + + if (gimp_layer_get_apply_mask (layer)) + { + gegl_node_connect_to (layer->mask_offset_node, "output", + mode_node, "aux2"); + } + } + + gimp_layer_update_mode_node (layer); + } + + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (layer)); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + + g_signal_emit (layer, layer_signals[SHOW_MASK_CHANGED], 0); + } +} + +gboolean +gimp_layer_get_show_mask (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + g_return_val_if_fail (layer->mask, FALSE); + + return layer->show_mask; +} + +void +gimp_layer_add_alpha (GimpLayer *layer) +{ + GimpItem *item; + GimpDrawable *drawable; + GeglBuffer *new_buffer; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + return; + + item = GIMP_ITEM (layer); + drawable = GIMP_DRAWABLE (layer); + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + gimp_drawable_get_format_with_alpha (drawable)); + + gimp_gegl_buffer_copy ( + gimp_drawable_get_buffer (drawable), NULL, GEGL_ABYSS_NONE, + new_buffer, NULL); + + gimp_drawable_set_buffer (GIMP_DRAWABLE (layer), + gimp_item_is_attached (GIMP_ITEM (layer)), + C_("undo-type", "Add Alpha Channel"), + new_buffer); + g_object_unref (new_buffer); +} + +void +gimp_layer_remove_alpha (GimpLayer *layer, + GimpContext *context) +{ + GeglBuffer *new_buffer; + GimpRGB background; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) + return; + + new_buffer = + gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (GIMP_ITEM (layer)), + gimp_item_get_height (GIMP_ITEM (layer))), + gimp_drawable_get_format_without_alpha (GIMP_DRAWABLE (layer))); + + gimp_context_get_background (context, &background); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (layer), + &background, &background); + + gimp_gegl_apply_flatten (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)), + NULL, NULL, + new_buffer, &background, + gimp_layer_get_real_composite_space (layer)); + + gimp_drawable_set_buffer (GIMP_DRAWABLE (layer), + gimp_item_is_attached (GIMP_ITEM (layer)), + C_("undo-type", "Remove Alpha Channel"), + new_buffer); + g_object_unref (new_buffer); +} + +void +gimp_layer_resize_to_image (GimpLayer *layer, + GimpContext *context, + GimpFillType fill_type) +{ + GimpImage *image; + gint offset_x; + gint offset_y; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer))); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + + image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, + C_("undo-type", "Layer to Image Size")); + + gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); + gimp_item_resize (GIMP_ITEM (layer), context, fill_type, + gimp_image_get_width (image), + gimp_image_get_height (image), + offset_x, offset_y); + + gimp_image_undo_group_end (image); +} + +/**********************/ +/* access functions */ +/**********************/ + +GimpDrawable * +gimp_layer_get_floating_sel_drawable (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + + return layer->fs.drawable; +} + +void +gimp_layer_set_floating_sel_drawable (GimpLayer *layer, + GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable)); + + if (g_set_object (&layer->fs.drawable, drawable)) + { + if (layer->fs.segs) + { + g_clear_pointer (&layer->fs.segs, g_free); + layer->fs.num_segs = 0; + } + + g_object_notify (G_OBJECT (layer), "floating-selection"); + } +} + +gboolean +gimp_layer_is_floating_sel (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + + return (gimp_layer_get_floating_sel_drawable (layer) != NULL); +} + +void +gimp_layer_set_opacity (GimpLayer *layer, + gdouble opacity, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE); + + if (layer->opacity != opacity) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_push_layer_opacity (image, NULL, layer); + } + + layer->opacity = opacity; + + g_signal_emit (layer, layer_signals[OPACITY_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "opacity"); + + if (gimp_filter_peek_node (GIMP_FILTER (layer))) + gimp_layer_update_mode_node (layer); + + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + } +} + +gdouble +gimp_layer_get_opacity (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_OPACITY_OPAQUE); + + return layer->opacity; +} + +void +gimp_layer_set_mode (GimpLayer *layer, + GimpLayerMode mode, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)) == NULL) + { + g_return_if_fail (gimp_layer_mode_get_context (mode) & + GIMP_LAYER_MODE_CONTEXT_LAYER); + } + else + { + g_return_if_fail (gimp_layer_mode_get_context (mode) & + GIMP_LAYER_MODE_CONTEXT_GROUP); + } + + if (layer->mode != mode) + { + if (gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_unset_default_new_layer_mode (image); + + if (push_undo) + gimp_image_undo_push_layer_mode (image, NULL, layer); + } + + g_object_freeze_notify (G_OBJECT (layer)); + + layer->mode = mode; + + g_signal_emit (layer, layer_signals[MODE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "mode"); + + /* when changing modes, we always switch to AUTO blend and + * composite in order to avoid confusion + */ + if (layer->blend_space != GIMP_LAYER_COLOR_SPACE_AUTO) + { + layer->blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + + g_signal_emit (layer, layer_signals[BLEND_SPACE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "blend-space"); + } + + if (layer->composite_space != GIMP_LAYER_COLOR_SPACE_AUTO) + { + layer->composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + + g_signal_emit (layer, layer_signals[COMPOSITE_SPACE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "composite-space"); + } + + if (layer->composite_mode != GIMP_LAYER_COMPOSITE_AUTO) + { + layer->composite_mode = GIMP_LAYER_COMPOSITE_AUTO; + + g_signal_emit (layer, layer_signals[COMPOSITE_MODE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "composite-mode"); + } + + g_object_thaw_notify (G_OBJECT (layer)); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + } +} + +GimpLayerMode +gimp_layer_get_mode (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_MODE_NORMAL); + + return layer->mode; +} + +void +gimp_layer_set_blend_space (GimpLayer *layer, + GimpLayerColorSpace blend_space, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (! gimp_layer_mode_is_blend_space_mutable (layer->mode)) + return; + + if (layer->blend_space != blend_space) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_push_layer_mode (image, NULL, layer); + } + + layer->blend_space = blend_space; + + g_signal_emit (layer, layer_signals[BLEND_SPACE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "blend-space"); + + gimp_layer_update_effective_mode (layer); + } +} + +GimpLayerColorSpace +gimp_layer_get_blend_space (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COLOR_SPACE_AUTO); + + return layer->blend_space; +} + +GimpLayerColorSpace +gimp_layer_get_real_blend_space (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COLOR_SPACE_RGB_LINEAR); + + if (layer->blend_space == GIMP_LAYER_COLOR_SPACE_AUTO) + return gimp_layer_mode_get_blend_space (layer->mode); + else + return layer->blend_space; +} + +void +gimp_layer_set_composite_space (GimpLayer *layer, + GimpLayerColorSpace composite_space, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (! gimp_layer_mode_is_composite_space_mutable (layer->mode)) + return; + + if (layer->composite_space != composite_space) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_push_layer_mode (image, NULL, layer); + } + + layer->composite_space = composite_space; + + g_signal_emit (layer, layer_signals[COMPOSITE_SPACE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "composite-space"); + + gimp_layer_update_effective_mode (layer); + } +} + +GimpLayerColorSpace +gimp_layer_get_composite_space (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COLOR_SPACE_AUTO); + + return layer->composite_space; +} + +GimpLayerColorSpace +gimp_layer_get_real_composite_space (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COLOR_SPACE_RGB_LINEAR); + + if (layer->composite_space == GIMP_LAYER_COLOR_SPACE_AUTO) + return gimp_layer_mode_get_composite_space (layer->mode); + else + return layer->composite_space; +} + +void +gimp_layer_set_composite_mode (GimpLayer *layer, + GimpLayerCompositeMode composite_mode, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (! gimp_layer_mode_is_composite_mode_mutable (layer->mode)) + return; + + if (layer->composite_mode != composite_mode) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_push_layer_mode (image, NULL, layer); + } + + layer->composite_mode = composite_mode; + + g_signal_emit (layer, layer_signals[COMPOSITE_MODE_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "composite-mode"); + + gimp_layer_update_effective_mode (layer); + gimp_layer_update_excludes_backdrop (layer); + } +} + +GimpLayerCompositeMode +gimp_layer_get_composite_mode (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COMPOSITE_AUTO); + + return layer->composite_mode; +} + +GimpLayerCompositeMode +gimp_layer_get_real_composite_mode (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), GIMP_LAYER_COMPOSITE_UNION); + + if (layer->composite_mode == GIMP_LAYER_COMPOSITE_AUTO) + return gimp_layer_mode_get_composite_mode (layer->mode); + else + return layer->composite_mode; +} + +void +gimp_layer_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (mode) *mode = layer->effective_mode; + if (blend_space) *blend_space = layer->effective_blend_space; + if (composite_space) *composite_space = layer->effective_composite_space; + if (composite_mode) *composite_mode = layer->effective_composite_mode; +} + +gboolean +gimp_layer_get_excludes_backdrop (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + + return layer->excludes_backdrop; +} + +void +gimp_layer_set_lock_alpha (GimpLayer *layer, + gboolean lock_alpha, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_LAYER (layer)); + g_return_if_fail (gimp_layer_can_lock_alpha (layer)); + + lock_alpha = lock_alpha ? TRUE : FALSE; + + if (layer->lock_alpha != lock_alpha) + { + if (push_undo && gimp_item_is_attached (GIMP_ITEM (layer))) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer)); + + gimp_image_undo_push_layer_lock_alpha (image, NULL, layer); + } + + layer->lock_alpha = lock_alpha; + + g_signal_emit (layer, layer_signals[LOCK_ALPHA_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "lock-alpha"); + } +} + +gboolean +gimp_layer_get_lock_alpha (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + + return layer->lock_alpha; +} + +gboolean +gimp_layer_can_lock_alpha (GimpLayer *layer) +{ + g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + return FALSE; + + return TRUE; +} + + +/* protected functions */ + +void +gimp_layer_update_effective_mode (GimpLayer *layer) +{ + GimpLayerMode mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + + if (layer->mask && layer->show_mask) + { + mode = GIMP_LAYER_MODE_NORMAL; + blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + composite_mode = GIMP_LAYER_COMPOSITE_AUTO; + + /* This makes sure that masks of LEGACY-mode layers are + * composited in PERCEPTUAL space, and non-LEGACY layers in + * LINEAR space, or whatever composite space was chosen in the + * layer attributes dialog + */ + composite_space = gimp_layer_get_real_composite_space (layer); + } + else + { + GIMP_LAYER_GET_CLASS (layer)->get_effective_mode (layer, + &mode, + &blend_space, + &composite_space, + &composite_mode); + } + + if (mode != layer->effective_mode || + blend_space != layer->effective_blend_space || + composite_space != layer->effective_composite_space || + composite_mode != layer->effective_composite_mode) + { + layer->effective_mode = mode; + layer->effective_blend_space = blend_space; + layer->effective_composite_space = composite_space; + layer->effective_composite_mode = composite_mode; + + g_signal_emit (layer, layer_signals[EFFECTIVE_MODE_CHANGED], 0); + + if (gimp_filter_peek_node (GIMP_FILTER (layer))) + gimp_layer_update_mode_node (layer); + + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + } +} + +void +gimp_layer_update_excludes_backdrop (GimpLayer *layer) +{ + gboolean excludes_backdrop; + + g_return_if_fail (GIMP_IS_LAYER (layer)); + + excludes_backdrop = + GIMP_LAYER_GET_CLASS (layer)->get_excludes_backdrop (layer); + + if (excludes_backdrop != layer->excludes_backdrop) + { + layer->excludes_backdrop = excludes_backdrop; + + g_signal_emit (layer, layer_signals[EXCLUDES_BACKDROP_CHANGED], 0); + g_object_notify (G_OBJECT (layer), "excludes-backdrop"); + } +} diff --git a/app/core/gimplayer.h b/app/core/gimplayer.h new file mode 100644 index 0000000..f8c9d82 --- /dev/null +++ b/app/core/gimplayer.h @@ -0,0 +1,243 @@ +/* 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 . + */ + +#ifndef __GIMP_LAYER_H__ +#define __GIMP_LAYER_H__ + + +#include "gimpdrawable.h" + + +#define GIMP_TYPE_LAYER (gimp_layer_get_type ()) +#define GIMP_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER, GimpLayer)) +#define GIMP_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER, GimpLayerClass)) +#define GIMP_IS_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER)) +#define GIMP_IS_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER)) +#define GIMP_LAYER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER, GimpLayerClass)) + + +typedef struct _GimpLayerClass GimpLayerClass; + +struct _GimpLayer +{ + GimpDrawable parent_instance; + + gdouble opacity; /* layer opacity */ + GimpLayerMode mode; /* layer combination mode */ + GimpLayerColorSpace blend_space; /* layer blend space */ + GimpLayerColorSpace composite_space; /* layer composite space */ + GimpLayerCompositeMode composite_mode; /* layer composite mode */ + GimpLayerMode effective_mode; /* layer effective combination mode */ + GimpLayerColorSpace effective_blend_space; /* layer effective blend space */ + GimpLayerColorSpace effective_composite_space; /* layer effective composite space */ + GimpLayerCompositeMode effective_composite_mode; /* layer effective composite mode */ + gboolean excludes_backdrop; /* layer clips backdrop */ + gboolean lock_alpha; /* lock the alpha channel */ + + GimpLayerMask *mask; /* possible layer mask */ + gboolean apply_mask; /* controls mask application */ + gboolean edit_mask; /* edit mask or layer? */ + gboolean show_mask; /* show mask or layer? */ + + GSList *move_stack; /* ancestors affected by move */ + + GeglNode *layer_offset_node; + GeglNode *mask_offset_node; + + /* Floating selections */ + struct + { + GimpDrawable *drawable; /* floating sel is attached to */ + gboolean boundary_known; /* is the current boundary valid */ + GimpBoundSeg *segs; /* boundary of floating sel */ + gint num_segs; /* number of segs in boundary */ + } fs; +}; + +struct _GimpLayerClass +{ + GimpDrawableClass parent_class; + + /* signals */ + void (* opacity_changed) (GimpLayer *layer); + void (* mode_changed) (GimpLayer *layer); + void (* blend_space_changed) (GimpLayer *layer); + void (* composite_space_changed) (GimpLayer *layer); + void (* composite_mode_changed) (GimpLayer *layer); + void (* effective_mode_changed) (GimpLayer *layer); + void (* excludes_backdrop_changed) (GimpLayer *layer); + void (* lock_alpha_changed) (GimpLayer *layer); + void (* mask_changed) (GimpLayer *layer); + void (* apply_mask_changed) (GimpLayer *layer); + void (* edit_mask_changed) (GimpLayer *layer); + void (* show_mask_changed) (GimpLayer *layer); + + /* virtual functions */ + void (* translate) (GimpLayer *layer, + gint offset_x, + gint offset_y); + void (* scale) (GimpLayer *layer, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interpolation_type, + GimpProgress *progress); + void (* resize) (GimpLayer *layer, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); + void (* flip) (GimpLayer *layer, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); + void (* rotate) (GimpLayer *layer, + GimpContext *context, + GimpRotationType rotate_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); + void (* transform) (GimpLayer *layer, + GimpContext *context, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + GimpTransformResize clip_result, + GimpProgress *progress); + void (* convert_type) (GimpLayer *layer, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); + GeglRectangle (* get_bounding_box) (GimpLayer *layer); + void (* get_effective_mode) (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode); + gboolean (* get_excludes_backdrop) (GimpLayer *layer); +}; + + +/* function declarations */ + +GType gimp_layer_get_type (void) G_GNUC_CONST; + +GimpLayer * gimp_layer_get_parent (GimpLayer *layer); + +GimpLayerMask * gimp_layer_get_mask (GimpLayer *layer); +GimpLayerMask * gimp_layer_create_mask (GimpLayer *layer, + GimpAddMaskType mask_type, + GimpChannel *channel); +GimpLayerMask * gimp_layer_add_mask (GimpLayer *layer, + GimpLayerMask *mask, + gboolean push_undo, + GError **error); +void gimp_layer_apply_mask (GimpLayer *layer, + GimpMaskApplyMode mode, + gboolean push_undo); + +void gimp_layer_set_apply_mask (GimpLayer *layer, + gboolean apply, + gboolean push_undo); +gboolean gimp_layer_get_apply_mask (GimpLayer *layer); + +void gimp_layer_set_edit_mask (GimpLayer *layer, + gboolean edit); +gboolean gimp_layer_get_edit_mask (GimpLayer *layer); + +void gimp_layer_set_show_mask (GimpLayer *layer, + gboolean show, + gboolean push_undo); +gboolean gimp_layer_get_show_mask (GimpLayer *layer); + +void gimp_layer_add_alpha (GimpLayer *layer); +void gimp_layer_remove_alpha (GimpLayer *layer, + GimpContext *context); + +void gimp_layer_resize_to_image (GimpLayer *layer, + GimpContext *context, + GimpFillType fill_type); + +GimpDrawable * gimp_layer_get_floating_sel_drawable (GimpLayer *layer); +void gimp_layer_set_floating_sel_drawable (GimpLayer *layer, + GimpDrawable *drawable); +gboolean gimp_layer_is_floating_sel (GimpLayer *layer); + +void gimp_layer_set_opacity (GimpLayer *layer, + gdouble opacity, + gboolean push_undo); +gdouble gimp_layer_get_opacity (GimpLayer *layer); + +void gimp_layer_set_mode (GimpLayer *layer, + GimpLayerMode mode, + gboolean push_undo); +GimpLayerMode gimp_layer_get_mode (GimpLayer *layer); + +void gimp_layer_set_blend_space (GimpLayer *layer, + GimpLayerColorSpace blend_space, + gboolean push_undo); +GimpLayerColorSpace + gimp_layer_get_blend_space (GimpLayer *layer); +GimpLayerColorSpace + gimp_layer_get_real_blend_space (GimpLayer *layer); + +void gimp_layer_set_composite_space (GimpLayer *layer, + GimpLayerColorSpace composite_space, + gboolean push_undo); +GimpLayerColorSpace + gimp_layer_get_composite_space (GimpLayer *layer); +GimpLayerColorSpace + gimp_layer_get_real_composite_space (GimpLayer *layer); + +void gimp_layer_set_composite_mode (GimpLayer *layer, + GimpLayerCompositeMode composite_mode, + gboolean push_undo); +GimpLayerCompositeMode + gimp_layer_get_composite_mode (GimpLayer *layer); +GimpLayerCompositeMode + gimp_layer_get_real_composite_mode (GimpLayer *layer); + +void gimp_layer_get_effective_mode (GimpLayer *layer, + GimpLayerMode *mode, + GimpLayerColorSpace *blend_space, + GimpLayerColorSpace *composite_space, + GimpLayerCompositeMode *composite_mode); + +gboolean gimp_layer_get_excludes_backdrop (GimpLayer *layer); + +void gimp_layer_set_lock_alpha (GimpLayer *layer, + gboolean lock_alpha, + gboolean push_undo); +gboolean gimp_layer_get_lock_alpha (GimpLayer *layer); +gboolean gimp_layer_can_lock_alpha (GimpLayer *layer); + + +/* protected */ + +void gimp_layer_update_effective_mode (GimpLayer *layer); +void gimp_layer_update_excludes_backdrop (GimpLayer *layer); + + +#endif /* __GIMP_LAYER_H__ */ diff --git a/app/core/gimplayermask.c b/app/core/gimplayermask.c new file mode 100644 index 0000000..ec0dbfb --- /dev/null +++ b/app/core/gimplayermask.c @@ -0,0 +1,297 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimperror.h" +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayermask.h" + +#include "gimp-intl.h" + + +static void gimp_layer_mask_preview_freeze (GimpViewable *viewable); +static void gimp_layer_mask_preview_thaw (GimpViewable *viewable); + +static gboolean gimp_layer_mask_is_attached (GimpItem *item); +static gboolean gimp_layer_mask_is_content_locked (GimpItem *item); +static gboolean gimp_layer_mask_is_position_locked (GimpItem *item); +static GimpItemTree * gimp_layer_mask_get_tree (GimpItem *item); +static GimpItem * gimp_layer_mask_duplicate (GimpItem *item, + GType new_type); +static gboolean gimp_layer_mask_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error); + +static void gimp_layer_mask_bounding_box_changed (GimpDrawable *drawable); +static void gimp_layer_mask_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); + + +G_DEFINE_TYPE (GimpLayerMask, gimp_layer_mask, GIMP_TYPE_CHANNEL) + +#define parent_class gimp_layer_mask_parent_class + + +static void +gimp_layer_mask_class_init (GimpLayerMaskClass *klass) +{ + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + + viewable_class->default_icon_name = "gimp-layer-mask"; + + viewable_class->preview_freeze = gimp_layer_mask_preview_freeze; + viewable_class->preview_thaw = gimp_layer_mask_preview_thaw; + + item_class->is_attached = gimp_layer_mask_is_attached; + item_class->is_content_locked = gimp_layer_mask_is_content_locked; + item_class->is_position_locked = gimp_layer_mask_is_position_locked; + item_class->get_tree = gimp_layer_mask_get_tree; + item_class->duplicate = gimp_layer_mask_duplicate; + item_class->rename = gimp_layer_mask_rename; + item_class->translate_desc = C_("undo-type", "Move Layer Mask"); + item_class->to_selection_desc = C_("undo-type", "Layer Mask to Selection"); + + drawable_class->bounding_box_changed = gimp_layer_mask_bounding_box_changed; + drawable_class->convert_type = gimp_layer_mask_convert_type; +} + +static void +gimp_layer_mask_init (GimpLayerMask *layer_mask) +{ + layer_mask->layer = NULL; +} + +static void +gimp_layer_mask_preview_freeze (GimpViewable *viewable) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (viewable); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + if (layer) + { + GimpViewable *parent = gimp_viewable_get_parent (GIMP_VIEWABLE (layer)); + + if (! parent && gimp_item_is_attached (GIMP_ITEM (layer))) + parent = GIMP_VIEWABLE (gimp_item_get_image (GIMP_ITEM (layer))); + + if (parent) + gimp_viewable_preview_freeze (parent); + } +} + +static void +gimp_layer_mask_preview_thaw (GimpViewable *viewable) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (viewable); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + if (layer) + { + GimpViewable *parent = gimp_viewable_get_parent (GIMP_VIEWABLE (layer)); + + if (! parent && gimp_item_is_attached (GIMP_ITEM (layer))) + parent = GIMP_VIEWABLE (gimp_item_get_image (GIMP_ITEM (layer))); + + if (parent) + gimp_viewable_preview_thaw (parent); + } +} + +static gboolean +gimp_layer_mask_is_content_locked (GimpItem *item) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (item); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + if (layer) + return gimp_item_is_content_locked (GIMP_ITEM (layer)); + + return FALSE; +} + +static gboolean +gimp_layer_mask_is_position_locked (GimpItem *item) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (item); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + if (layer) + return gimp_item_is_position_locked (GIMP_ITEM (layer)); + + return FALSE; +} + +static gboolean +gimp_layer_mask_is_attached (GimpItem *item) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (item); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + return (GIMP_IS_IMAGE (gimp_item_get_image (item)) && + GIMP_IS_LAYER (layer) && + gimp_layer_get_mask (layer) == mask && + gimp_item_is_attached (GIMP_ITEM (layer))); +} + +static GimpItemTree * +gimp_layer_mask_get_tree (GimpItem *item) +{ + return NULL; +} + +static GimpItem * +gimp_layer_mask_duplicate (GimpItem *item, + GType new_type) +{ + GimpItem *new_item; + + g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL); + + new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type); + + return new_item; +} + +static gboolean +gimp_layer_mask_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error) +{ + /* reject renaming, layer masks are always named " mask" */ + + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot rename layer masks.")); + + return FALSE; +} + +static void +gimp_layer_mask_bounding_box_changed (GimpDrawable *drawable) +{ + GimpLayerMask *mask = GIMP_LAYER_MASK (drawable); + GimpLayer *layer = gimp_layer_mask_get_layer (mask); + + if (GIMP_DRAWABLE_CLASS (parent_class)->bounding_box_changed) + GIMP_DRAWABLE_CLASS (parent_class)->bounding_box_changed (drawable); + + if (layer) + gimp_drawable_update_bounding_box (GIMP_DRAWABLE (layer)); +} + +static void +gimp_layer_mask_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + new_format = + gimp_babl_mask_format (gimp_babl_format_get_precision (new_format)); + + GIMP_DRAWABLE_CLASS (parent_class)->convert_type (drawable, dest_image, + new_format, + dest_profile, + layer_dither_type, + mask_dither_type, + push_undo, + progress); +} + +GimpLayerMask * +gimp_layer_mask_new (GimpImage *image, + gint width, + gint height, + const gchar *name, + const GimpRGB *color) +{ + GimpLayerMask *layer_mask; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + g_return_val_if_fail (color != NULL, NULL); + + layer_mask = + GIMP_LAYER_MASK (gimp_drawable_new (GIMP_TYPE_LAYER_MASK, + image, name, + 0, 0, width, height, + gimp_image_get_mask_format (image))); + + /* set the layer_mask color and opacity */ + gimp_channel_set_color (GIMP_CHANNEL (layer_mask), color, FALSE); + gimp_channel_set_show_masked (GIMP_CHANNEL (layer_mask), TRUE); + + /* selection mask variables */ + GIMP_CHANNEL (layer_mask)->x2 = width; + GIMP_CHANNEL (layer_mask)->y2 = height; + + return layer_mask; +} + +void +gimp_layer_mask_set_layer (GimpLayerMask *layer_mask, + GimpLayer *layer) +{ + g_return_if_fail (GIMP_IS_LAYER_MASK (layer_mask)); + g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer)); + + layer_mask->layer = layer; + + if (layer) + { + gchar *mask_name; + gint offset_x; + gint offset_y; + + gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); + gimp_item_set_offset (GIMP_ITEM (layer_mask), offset_x, offset_y); + + mask_name = g_strdup_printf (_("%s mask"), gimp_object_get_name (layer)); + + gimp_object_take_name (GIMP_OBJECT (layer_mask), mask_name); + } +} + +GimpLayer * +gimp_layer_mask_get_layer (GimpLayerMask *layer_mask) +{ + g_return_val_if_fail (GIMP_IS_LAYER_MASK (layer_mask), NULL); + + return layer_mask->layer; +} diff --git a/app/core/gimplayermask.h b/app/core/gimplayermask.h new file mode 100644 index 0000000..5ee77fd --- /dev/null +++ b/app/core/gimplayermask.h @@ -0,0 +1,61 @@ +/* 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 . + */ + +#ifndef __GIMP_LAYER_MASK_H__ +#define __GIMP_LAYER_MASK_H__ + + +#include "gimpchannel.h" + + +#define GIMP_TYPE_LAYER_MASK (gimp_layer_mask_get_type ()) +#define GIMP_LAYER_MASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_MASK, GimpLayerMask)) +#define GIMP_LAYER_MASK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_MASK, GimpLayerMaskClass)) +#define GIMP_IS_LAYER_MASK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_MASK)) +#define GIMP_IS_LAYER_MASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_MASK)) +#define GIMP_LAYER_MASK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER_MASK, GimpLayerMaskClass)) + + +typedef struct _GimpLayerMaskClass GimpLayerMaskClass; + +struct _GimpLayerMask +{ + GimpChannel parent_instance; + + GimpLayer *layer; +}; + +struct _GimpLayerMaskClass +{ + GimpChannelClass parent_class; +}; + + +GType gimp_layer_mask_get_type (void) G_GNUC_CONST; + +GimpLayerMask * gimp_layer_mask_new (GimpImage *image, + gint width, + gint height, + const gchar *name, + const GimpRGB *color); + +void gimp_layer_mask_set_layer (GimpLayerMask *layer_mask, + GimpLayer *layer); +GimpLayer * gimp_layer_mask_get_layer (GimpLayerMask *layer_mask); + + +#endif /* __GIMP_LAYER_MASK_H__ */ diff --git a/app/core/gimplayermaskpropundo.c b/app/core/gimplayermaskpropundo.c new file mode 100644 index 0000000..7913202 --- /dev/null +++ b/app/core/gimplayermaskpropundo.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimplayer.h" +#include "gimplayermaskpropundo.h" + + +static void gimp_layer_mask_prop_undo_constructed (GObject *object); + +static void gimp_layer_mask_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpLayerMaskPropUndo, gimp_layer_mask_prop_undo, + GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_layer_mask_prop_undo_parent_class + + +static void +gimp_layer_mask_prop_undo_class_init (GimpLayerMaskPropUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_layer_mask_prop_undo_constructed; + + undo_class->pop = gimp_layer_mask_prop_undo_pop; +} + +static void +gimp_layer_mask_prop_undo_init (GimpLayerMaskPropUndo *undo) +{ +} + +static void +gimp_layer_mask_prop_undo_constructed (GObject *object) +{ + GimpLayerMaskPropUndo *layer_mask_prop_undo; + GimpLayer *layer; + + layer_mask_prop_undo = GIMP_LAYER_MASK_PROP_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_LAYER (GIMP_ITEM_UNDO (object)->item)); + + layer = GIMP_LAYER (GIMP_ITEM_UNDO (object)->item); + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_LAYER_MASK_APPLY: + layer_mask_prop_undo->apply = gimp_layer_get_apply_mask (layer); + break; + + case GIMP_UNDO_LAYER_MASK_SHOW: + layer_mask_prop_undo->show = gimp_layer_get_show_mask (layer); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_layer_mask_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpLayerMaskPropUndo *layer_mask_prop_undo = GIMP_LAYER_MASK_PROP_UNDO (undo); + GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_LAYER_MASK_APPLY: + { + gboolean apply; + + apply = gimp_layer_get_apply_mask (layer); + gimp_layer_set_apply_mask (layer, layer_mask_prop_undo->apply, FALSE); + layer_mask_prop_undo->apply = apply; + } + break; + + case GIMP_UNDO_LAYER_MASK_SHOW: + { + gboolean show; + + show = gimp_layer_get_show_mask (layer); + gimp_layer_set_show_mask (layer, layer_mask_prop_undo->show, FALSE); + layer_mask_prop_undo->show = show; + } + break; + + default: + g_return_if_reached (); + } +} diff --git a/app/core/gimplayermaskpropundo.h b/app/core/gimplayermaskpropundo.h new file mode 100644 index 0000000..a416e43 --- /dev/null +++ b/app/core/gimplayermaskpropundo.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __GIMP_LAYER_MASK_PROP_UNDO_H__ +#define __GIMP_LAYER_MASK_PROP_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_LAYER_MASK_PROP_UNDO (gimp_layer_mask_prop_undo_get_type ()) +#define GIMP_LAYER_MASK_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_MASK_PROP_UNDO, GimpLayerMaskPropUndo)) +#define GIMP_LAYER_MASK_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_MASK_PROP_UNDO, GimpLayerMaskPropUndoClass)) +#define GIMP_IS_LAYER_MASK_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_MASK_PROP_UNDO)) +#define GIMP_IS_LAYER_MASK_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_MASK_PROP_UNDO)) +#define GIMP_LAYER_MASK_PROP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER_MASK_PROP_UNDO, GimpLayerMaskPropUndoClass)) + + +typedef struct _GimpLayerMaskPropUndo GimpLayerMaskPropUndo; +typedef struct _GimpLayerMaskPropUndoClass GimpLayerMaskPropUndoClass; + +struct _GimpLayerMaskPropUndo +{ + GimpItemUndo parent_instance; + + gboolean apply; + gboolean show; +}; + +struct _GimpLayerMaskPropUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_layer_mask_prop_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_LAYER_MASK_PROP_UNDO_H__ */ diff --git a/app/core/gimplayermaskundo.c b/app/core/gimplayermaskundo.c new file mode 100644 index 0000000..cc49eb7 --- /dev/null +++ b/app/core/gimplayermaskundo.c @@ -0,0 +1,195 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayermask.h" +#include "gimplayermaskundo.h" + + +enum +{ + PROP_0, + PROP_LAYER_MASK +}; + + +static void gimp_layer_mask_undo_constructed (GObject *object); +static void gimp_layer_mask_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_layer_mask_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_layer_mask_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_layer_mask_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_layer_mask_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpLayerMaskUndo, gimp_layer_mask_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_layer_mask_undo_parent_class + + +static void +gimp_layer_mask_undo_class_init (GimpLayerMaskUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_layer_mask_undo_constructed; + object_class->set_property = gimp_layer_mask_undo_set_property; + object_class->get_property = gimp_layer_mask_undo_get_property; + + gimp_object_class->get_memsize = gimp_layer_mask_undo_get_memsize; + + undo_class->pop = gimp_layer_mask_undo_pop; + undo_class->free = gimp_layer_mask_undo_free; + + g_object_class_install_property (object_class, PROP_LAYER_MASK, + g_param_spec_object ("layer-mask", NULL, NULL, + GIMP_TYPE_LAYER_MASK, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_layer_mask_undo_init (GimpLayerMaskUndo *undo) +{ +} + +static void +gimp_layer_mask_undo_constructed (GObject *object) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_LAYER (GIMP_ITEM_UNDO (object)->item)); + gimp_assert (GIMP_IS_LAYER_MASK (layer_mask_undo->layer_mask)); +} + +static void +gimp_layer_mask_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (object); + + switch (property_id) + { + case PROP_LAYER_MASK: + layer_mask_undo->layer_mask = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_layer_mask_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (object); + + switch (property_id) + { + case PROP_LAYER_MASK: + g_value_set_object (value, layer_mask_undo->layer_mask); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_layer_mask_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (object); + GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (object)->item); + gint64 memsize = 0; + + /* don't use !gimp_item_is_attached() here */ + if (gimp_layer_get_mask (layer) != layer_mask_undo->layer_mask) + memsize += gimp_object_get_memsize (GIMP_OBJECT (layer_mask_undo->layer_mask), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_layer_mask_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (undo); + GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_LAYER_MASK_ADD) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_LAYER_MASK_REMOVE)) + { + /* remove layer mask */ + + gimp_layer_apply_mask (layer, GIMP_MASK_DISCARD, FALSE); + } + else + { + /* restore layer mask */ + + gimp_layer_add_mask (layer, layer_mask_undo->layer_mask, FALSE, NULL); + } +} + +static void +gimp_layer_mask_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (undo); + + g_clear_object (&layer_mask_undo->layer_mask); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimplayermaskundo.h b/app/core/gimplayermaskundo.h new file mode 100644 index 0000000..c8591c1 --- /dev/null +++ b/app/core/gimplayermaskundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_LAYER_MASK_UNDO_H__ +#define __GIMP_LAYER_MASK_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_LAYER_MASK_UNDO (gimp_layer_mask_undo_get_type ()) +#define GIMP_LAYER_MASK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_MASK_UNDO, GimpLayerMaskUndo)) +#define GIMP_LAYER_MASK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_MASK_UNDO, GimpLayerMaskUndoClass)) +#define GIMP_IS_LAYER_MASK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_MASK_UNDO)) +#define GIMP_IS_LAYER_MASK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_MASK_UNDO)) +#define GIMP_LAYER_MASK_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER_MASK_UNDO, GimpLayerMaskUndoClass)) + + +typedef struct _GimpLayerMaskUndo GimpLayerMaskUndo; +typedef struct _GimpLayerMaskUndoClass GimpLayerMaskUndoClass; + +struct _GimpLayerMaskUndo +{ + GimpItemUndo parent_instance; + + GimpLayerMask *layer_mask; +}; + +struct _GimpLayerMaskUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_layer_mask_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_LAYER_MASK_UNDO_H__ */ diff --git a/app/core/gimplayerpropundo.c b/app/core/gimplayerpropundo.c new file mode 100644 index 0000000..73e3e02 --- /dev/null +++ b/app/core/gimplayerpropundo.c @@ -0,0 +1,157 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayerpropundo.h" + + +static void gimp_layer_prop_undo_constructed (GObject *object); + +static void gimp_layer_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpLayerPropUndo, gimp_layer_prop_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_layer_prop_undo_parent_class + + +static void +gimp_layer_prop_undo_class_init (GimpLayerPropUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_layer_prop_undo_constructed; + + undo_class->pop = gimp_layer_prop_undo_pop; +} + +static void +gimp_layer_prop_undo_init (GimpLayerPropUndo *undo) +{ +} + +static void +gimp_layer_prop_undo_constructed (GObject *object) +{ + GimpLayerPropUndo *layer_prop_undo = GIMP_LAYER_PROP_UNDO (object); + GimpLayer *layer; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_LAYER (GIMP_ITEM_UNDO (object)->item)); + + layer = GIMP_LAYER (GIMP_ITEM_UNDO (object)->item); + + switch (GIMP_UNDO (object)->undo_type) + { + case GIMP_UNDO_LAYER_MODE: + layer_prop_undo->mode = gimp_layer_get_mode (layer); + layer_prop_undo->blend_space = gimp_layer_get_blend_space (layer); + layer_prop_undo->composite_space = gimp_layer_get_composite_space (layer); + layer_prop_undo->composite_mode = gimp_layer_get_composite_mode (layer); + break; + + case GIMP_UNDO_LAYER_OPACITY: + layer_prop_undo->opacity = gimp_layer_get_opacity (layer); + break; + + case GIMP_UNDO_LAYER_LOCK_ALPHA: + layer_prop_undo->lock_alpha = gimp_layer_get_lock_alpha (layer); + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_layer_prop_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpLayerPropUndo *layer_prop_undo = GIMP_LAYER_PROP_UNDO (undo); + GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + switch (undo->undo_type) + { + case GIMP_UNDO_LAYER_MODE: + { + GimpLayerMode mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + + mode = gimp_layer_get_mode (layer); + blend_space = gimp_layer_get_blend_space (layer); + composite_space = gimp_layer_get_composite_space (layer); + composite_mode = gimp_layer_get_composite_mode (layer); + + gimp_layer_set_mode (layer, layer_prop_undo->mode, + FALSE); + gimp_layer_set_blend_space (layer, layer_prop_undo->blend_space, + FALSE); + gimp_layer_set_composite_space (layer, layer_prop_undo->composite_space, + FALSE); + gimp_layer_set_composite_mode (layer, layer_prop_undo->composite_mode, + FALSE); + + layer_prop_undo->mode = mode; + layer_prop_undo->blend_space = blend_space; + layer_prop_undo->composite_space = composite_space; + layer_prop_undo->composite_mode = composite_mode; + } + break; + + case GIMP_UNDO_LAYER_OPACITY: + { + gdouble opacity; + + opacity = gimp_layer_get_opacity (layer); + gimp_layer_set_opacity (layer, layer_prop_undo->opacity, FALSE); + layer_prop_undo->opacity = opacity; + } + break; + + case GIMP_UNDO_LAYER_LOCK_ALPHA: + { + gboolean lock_alpha; + + lock_alpha = gimp_layer_get_lock_alpha (layer); + gimp_layer_set_lock_alpha (layer, layer_prop_undo->lock_alpha, FALSE); + layer_prop_undo->lock_alpha = lock_alpha; + } + break; + + default: + g_return_if_reached (); + } +} diff --git a/app/core/gimplayerpropundo.h b/app/core/gimplayerpropundo.h new file mode 100644 index 0000000..5a3b478 --- /dev/null +++ b/app/core/gimplayerpropundo.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 . + */ + +#ifndef __GIMP_LAYER_PROP_UNDO_H__ +#define __GIMP_LAYER_PROP_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_LAYER_PROP_UNDO (gimp_layer_prop_undo_get_type ()) +#define GIMP_LAYER_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_PROP_UNDO, GimpLayerPropUndo)) +#define GIMP_LAYER_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_PROP_UNDO, GimpLayerPropUndoClass)) +#define GIMP_IS_LAYER_PROP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_PROP_UNDO)) +#define GIMP_IS_LAYER_PROP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_PROP_UNDO)) +#define GIMP_LAYER_PROP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER_PROP_UNDO, GimpLayerPropUndoClass)) + + +typedef struct _GimpLayerPropUndo GimpLayerPropUndo; +typedef struct _GimpLayerPropUndoClass GimpLayerPropUndoClass; + +struct _GimpLayerPropUndo +{ + GimpItemUndo parent_instance; + + GimpLayerMode mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + gdouble opacity; + gboolean lock_alpha; +}; + +struct _GimpLayerPropUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_layer_prop_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_LAYER_PROP_UNDO_H__ */ diff --git a/app/core/gimplayerstack.c b/app/core/gimplayerstack.c new file mode 100644 index 0000000..1540189 --- /dev/null +++ b/app/core/gimplayerstack.c @@ -0,0 +1,243 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimplayerstack.c + * Copyright (C) 2017 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimplayer.h" +#include "gimplayerstack.h" + + +/* local function prototypes */ + +static void gimp_layer_stack_constructed (GObject *object); + +static void gimp_layer_stack_add (GimpContainer *container, + GimpObject *object); +static void gimp_layer_stack_remove (GimpContainer *container, + GimpObject *object); +static void gimp_layer_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); + +static void gimp_layer_stack_layer_active (GimpLayer *layer, + GimpLayerStack *stack); +static void gimp_layer_stack_layer_excludes_backdrop (GimpLayer *layer, + GimpLayerStack *stack); + +static void gimp_layer_stack_update_backdrop (GimpLayerStack *stack, + GimpLayer *layer, + gboolean ignore_active, + gboolean ignore_excludes_backdrop); +static void gimp_layer_stack_update_range (GimpLayerStack *stack, + gint first, + gint last); + + +G_DEFINE_TYPE (GimpLayerStack, gimp_layer_stack, GIMP_TYPE_DRAWABLE_STACK) + +#define parent_class gimp_layer_stack_parent_class + + +static void +gimp_layer_stack_class_init (GimpLayerStackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + object_class->constructed = gimp_layer_stack_constructed; + + container_class->add = gimp_layer_stack_add; + container_class->remove = gimp_layer_stack_remove; + container_class->reorder = gimp_layer_stack_reorder; +} + +static void +gimp_layer_stack_init (GimpLayerStack *stack) +{ +} + +static void +gimp_layer_stack_constructed (GObject *object) +{ + GimpContainer *container = GIMP_CONTAINER (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (g_type_is_a (gimp_container_get_children_type (container), + GIMP_TYPE_LAYER)); + + gimp_container_add_handler (container, "active-changed", + G_CALLBACK (gimp_layer_stack_layer_active), + container); + gimp_container_add_handler (container, "excludes-backdrop-changed", + G_CALLBACK (gimp_layer_stack_layer_excludes_backdrop), + container); +} + +static void +gimp_layer_stack_add (GimpContainer *container, + GimpObject *object) +{ + GimpLayerStack *stack = GIMP_LAYER_STACK (container); + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); + + gimp_layer_stack_update_backdrop (stack, GIMP_LAYER (object), FALSE, FALSE); +} + +static void +gimp_layer_stack_remove (GimpContainer *container, + GimpObject *object) +{ + GimpLayerStack *stack = GIMP_LAYER_STACK (container); + gboolean update_backdrop; + gint index; + + update_backdrop = gimp_filter_get_active (GIMP_FILTER (object)) && + gimp_layer_get_excludes_backdrop (GIMP_LAYER (object)); + + if (update_backdrop) + index = gimp_container_get_child_index (container, object); + + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); + + if (update_backdrop) + gimp_layer_stack_update_range (stack, index, -1); +} + +static void +gimp_layer_stack_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + GimpLayerStack *stack = GIMP_LAYER_STACK (container); + gboolean update_backdrop; + gint index; + + update_backdrop = gimp_filter_get_active (GIMP_FILTER (object)) && + gimp_layer_get_excludes_backdrop (GIMP_LAYER (object)); + + if (update_backdrop) + index = gimp_container_get_child_index (container, object); + + GIMP_CONTAINER_CLASS (parent_class)->reorder (container, object, new_index); + + if (update_backdrop) + gimp_layer_stack_update_range (stack, index, new_index); +} + + +/* public functions */ + +GimpContainer * +gimp_layer_stack_new (GType layer_type) +{ + g_return_val_if_fail (g_type_is_a (layer_type, GIMP_TYPE_LAYER), NULL); + + return g_object_new (GIMP_TYPE_LAYER_STACK, + "name", g_type_name (layer_type), + "children-type", layer_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + NULL); +} + + +/* private functions */ + +static void +gimp_layer_stack_layer_active (GimpLayer *layer, + GimpLayerStack *stack) +{ + gimp_layer_stack_update_backdrop (stack, layer, TRUE, FALSE); +} + +static void +gimp_layer_stack_layer_excludes_backdrop (GimpLayer *layer, + GimpLayerStack *stack) +{ + gimp_layer_stack_update_backdrop (stack, layer, FALSE, TRUE); +} + +static void +gimp_layer_stack_update_backdrop (GimpLayerStack *stack, + GimpLayer *layer, + gboolean ignore_active, + gboolean ignore_excludes_backdrop) +{ + if ((ignore_active || gimp_filter_get_active (GIMP_FILTER (layer))) && + (ignore_excludes_backdrop || gimp_layer_get_excludes_backdrop (layer))) + { + gint index; + + index = gimp_container_get_child_index (GIMP_CONTAINER (stack), + GIMP_OBJECT (layer)); + + gimp_layer_stack_update_range (stack, index + 1, -1); + } +} + +static void +gimp_layer_stack_update_range (GimpLayerStack *stack, + gint first, + gint last) +{ + GList *iter; + + g_return_if_fail (first >= 0 && last >= -1); + + /* if the range is reversed, flip first and last; note that last == -1 is + * used to update all layers from first onward. + */ + if (last >= 0 && last < first) + { + gint temp = first; + + first = last + 1; + last = temp + 1; + } + + iter = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (stack)); + + for (iter = g_list_nth (iter, first); + iter && first != last; + iter = g_list_next (iter), first++) + { + GimpItem *item = iter->data; + + if (gimp_filter_get_active (GIMP_FILTER (item))) + { + GeglRectangle bounding_box; + + bounding_box = gimp_drawable_get_bounding_box (GIMP_DRAWABLE (item)); + + bounding_box.x += gimp_item_get_offset_x (item); + bounding_box.y += gimp_item_get_offset_y (item); + + gimp_drawable_stack_update (GIMP_DRAWABLE_STACK (stack), + bounding_box.x, bounding_box.y, + bounding_box.width, bounding_box.height); + } + } +} diff --git a/app/core/gimplayerstack.h b/app/core/gimplayerstack.h new file mode 100644 index 0000000..598d21c --- /dev/null +++ b/app/core/gimplayerstack.h @@ -0,0 +1,51 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimplayerstack.h + * Copyright (C) 2017 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 . + */ + +#ifndef __GIMP_LAYER_STACK_H__ +#define __GIMP_LAYER_STACK_H__ + +#include "gimpdrawablestack.h" + + +#define GIMP_TYPE_LAYER_STACK (gimp_layer_stack_get_type ()) +#define GIMP_LAYER_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_STACK, GimpLayerStack)) +#define GIMP_LAYER_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_STACK, GimpLayerStackClass)) +#define GIMP_IS_LAYER_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_STACK)) +#define GIMP_IS_LAYER_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_STACK)) + + +typedef struct _GimpLayerStackClass GimpLayerStackClass; + +struct _GimpLayerStack +{ + GimpDrawableStack parent_instance; +}; + +struct _GimpLayerStackClass +{ + GimpDrawableStackClass parent_class; +}; + + +GType gimp_layer_stack_get_type (void) G_GNUC_CONST; +GimpContainer * gimp_layer_stack_new (GType layer_type); + + +#endif /* __GIMP_LAYER_STACK_H__ */ diff --git a/app/core/gimplayerundo.c b/app/core/gimplayerundo.c new file mode 100644 index 0000000..64a1de3 --- /dev/null +++ b/app/core/gimplayerundo.c @@ -0,0 +1,212 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayerundo.h" + + +enum +{ + PROP_0, + PROP_PREV_PARENT, + PROP_PREV_POSITION, + PROP_PREV_LAYER +}; + + +static void gimp_layer_undo_constructed (GObject *object); +static void gimp_layer_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_layer_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_layer_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_layer_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpLayerUndo, gimp_layer_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_layer_undo_parent_class + + +static void +gimp_layer_undo_class_init (GimpLayerUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_layer_undo_constructed; + object_class->set_property = gimp_layer_undo_set_property; + object_class->get_property = gimp_layer_undo_get_property; + + gimp_object_class->get_memsize = gimp_layer_undo_get_memsize; + + undo_class->pop = gimp_layer_undo_pop; + + g_object_class_install_property (object_class, PROP_PREV_PARENT, + g_param_spec_object ("prev-parent", + NULL, NULL, + GIMP_TYPE_LAYER, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PREV_POSITION, + g_param_spec_int ("prev-position", NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_PREV_LAYER, + g_param_spec_object ("prev-layer", NULL, NULL, + GIMP_TYPE_LAYER, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_layer_undo_init (GimpLayerUndo *undo) +{ +} + +static void +gimp_layer_undo_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_LAYER (GIMP_ITEM_UNDO (object)->item)); +} + +static void +gimp_layer_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpLayerUndo *layer_undo = GIMP_LAYER_UNDO (object); + + switch (property_id) + { + case PROP_PREV_PARENT: + layer_undo->prev_parent = g_value_get_object (value); + break; + case PROP_PREV_POSITION: + layer_undo->prev_position = g_value_get_int (value); + break; + case PROP_PREV_LAYER: + layer_undo->prev_layer = g_value_get_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_layer_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLayerUndo *layer_undo = GIMP_LAYER_UNDO (object); + + switch (property_id) + { + case PROP_PREV_PARENT: + g_value_set_object (value, layer_undo->prev_parent); + break; + case PROP_PREV_POSITION: + g_value_set_int (value, layer_undo->prev_position); + break; + case PROP_PREV_LAYER: + g_value_set_object (value, layer_undo->prev_layer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_layer_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object); + gint64 memsize = 0; + + if (! gimp_item_is_attached (item_undo->item)) + memsize += gimp_object_get_memsize (GIMP_OBJECT (item_undo->item), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_layer_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpLayerUndo *layer_undo = GIMP_LAYER_UNDO (undo); + GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + if ((undo_mode == GIMP_UNDO_MODE_UNDO && + undo->undo_type == GIMP_UNDO_LAYER_ADD) || + (undo_mode == GIMP_UNDO_MODE_REDO && + undo->undo_type == GIMP_UNDO_LAYER_REMOVE)) + { + /* remove layer */ + + /* record the current parent and position */ + layer_undo->prev_parent = gimp_layer_get_parent (layer); + layer_undo->prev_position = gimp_item_get_index (GIMP_ITEM (layer)); + + gimp_image_remove_layer (undo->image, layer, FALSE, + layer_undo->prev_layer); + } + else + { + /* restore layer */ + + /* record the active layer */ + layer_undo->prev_layer = gimp_image_get_active_layer (undo->image); + + gimp_image_add_layer (undo->image, layer, + layer_undo->prev_parent, + layer_undo->prev_position, FALSE); + } +} diff --git a/app/core/gimplayerundo.h b/app/core/gimplayerundo.h new file mode 100644 index 0000000..700fce8 --- /dev/null +++ b/app/core/gimplayerundo.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 . + */ + +#ifndef __GIMP_LAYER_UNDO_H__ +#define __GIMP_LAYER_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_LAYER_UNDO (gimp_layer_undo_get_type ()) +#define GIMP_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_UNDO, GimpLayerUndo)) +#define GIMP_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LAYER_UNDO, GimpLayerUndoClass)) +#define GIMP_IS_LAYER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_UNDO)) +#define GIMP_IS_LAYER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LAYER_UNDO)) +#define GIMP_LAYER_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LAYER_UNDO, GimpLayerUndoClass)) + + +typedef struct _GimpLayerUndo GimpLayerUndo; +typedef struct _GimpLayerUndoClass GimpLayerUndoClass; + +struct _GimpLayerUndo +{ + GimpItemUndo parent_instance; + + GimpLayer *prev_parent; + gint prev_position; /* former position in list */ + GimpLayer *prev_layer; /* previous active layer */ +}; + +struct _GimpLayerUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_layer_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_LAYER_UNDO_H__ */ diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c new file mode 100644 index 0000000..bf8460c --- /dev/null +++ b/app/core/gimplineart.c @@ -0,0 +1,2979 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2017 Sébastien Fourey & David Tchumperlé + * Copyright (C) 2018 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp-parallel.h" +#include "gimp-priorities.h" +#include "gimp-utils.h" /* GIMP_TIMER */ +#include "gimpasync.h" +#include "gimpcancelable.h" +#include "gimpdrawable.h" +#include "gimpimage.h" +#include "gimplineart.h" +#include "gimpmarshal.h" +#include "gimppickable.h" +#include "gimpprojection.h" +#include "gimpviewable.h" +#include "gimpwaitable.h" + +#include "gimp-intl.h" + +enum +{ + COMPUTING_START, + COMPUTING_END, + LAST_SIGNAL, +}; + +enum +{ + PROP_0, + PROP_SELECT_TRANSPARENT, + PROP_MAX_GROW, + PROP_THRESHOLD, + PROP_SPLINE_MAX_LEN, + PROP_SEGMENT_MAX_LEN, +}; + +typedef struct _GimpLineArtPrivate GimpLineArtPrivate; + +struct _GimpLineArtPrivate +{ + gboolean frozen; + gboolean compute_after_thaw; + + GimpAsync *async; + + gint idle_id; + + GimpPickable *input; + GeglBuffer *closed; + gfloat *distmap; + + /* Used in the closing step. */ + gboolean select_transparent; + gdouble threshold; + gint spline_max_len; + gint segment_max_len; + gboolean max_len_bound; + + /* Used in the grow step. */ + gint max_grow; +}; + +typedef struct +{ + GeglBuffer *buffer; + + gboolean select_transparent; + gdouble threshold; + gint spline_max_len; + gint segment_max_len; +} LineArtData; + +typedef struct +{ + GeglBuffer *closed; + gfloat *distmap; +} LineArtResult; + +static int DeltaX[4] = {+1, -1, 0, 0}; +static int DeltaY[4] = {0, 0, +1, -1}; + +static const GimpVector2 Direction2Normal[4] = +{ + { 1.0f, 0.0f }, + { -1.0f, 0.0f }, + { 0.0f, 1.0f }, + { 0.0f, -1.0f } +}; + +typedef enum _Direction +{ + XPlusDirection = 0, + XMinusDirection = 1, + YPlusDirection = 2, + YMinusDirection = 3 +} Direction; + +typedef GimpVector2 Pixel; + +typedef struct _SplineCandidate +{ + Pixel p1; + Pixel p2; + float quality; +} SplineCandidate; + +typedef struct _Edgel +{ + gint x, y; + Direction direction; + + gfloat x_normal; + gfloat y_normal; + gfloat curvature; + guint next, previous; +} Edgel; + + +static void gimp_line_art_finalize (GObject *object); +static void gimp_line_art_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_line_art_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +/* Functions for asynchronous computation. */ + +static void gimp_line_art_compute (GimpLineArt *line_art); +static void gimp_line_art_compute_cb (GimpAsync *async, + GimpLineArt *line_art); + +static GimpAsync * gimp_line_art_prepare_async (GimpLineArt *line_art, + gint priority); +static void gimp_line_art_prepare_async_func (GimpAsync *async, + LineArtData *data); +static LineArtData * line_art_data_new (GeglBuffer *buffer, + GimpLineArt *line_art); +static void line_art_data_free (LineArtData *data); +static LineArtResult * line_art_result_new (GeglBuffer *line_art, + gfloat *distmap); +static void line_art_result_free (LineArtResult *result); + +static gboolean gimp_line_art_idle (GimpLineArt *line_art); +static void gimp_line_art_input_invalidate_preview (GimpViewable *viewable, + GimpLineArt *line_art); + + +/* All actual computation functions. */ + +static GeglBuffer * gimp_line_art_close (GeglBuffer *buffer, + gboolean select_transparent, + gdouble stroke_threshold, + gint spline_max_length, + gint segment_max_length, + gint minimal_lineart_area, + gint normal_estimate_mask_size, + gfloat end_point_rate, + gfloat spline_max_angle, + gint end_point_connectivity, + gfloat spline_roundness, + gboolean allow_self_intersections, + gint created_regions_significant_area, + gint created_regions_minimum_area, + gboolean small_segments_from_spline_sources, + gfloat **lineart_distmap, + GimpAsync *async); + +static void gimp_lineart_denoise (GeglBuffer *buffer, + int size, + GimpAsync *async); +static void gimp_lineart_compute_normals_curvatures (GeglBuffer *mask, + gfloat *normals, + gfloat *curvatures, + gfloat *smoothed_curvatures, + int normal_estimate_mask_size, + GimpAsync *async); +static gfloat * gimp_lineart_get_smooth_curvatures (GArray *edgelset, + GimpAsync *async); +static GArray * gimp_lineart_curvature_extremums (gfloat *curvatures, + gfloat *smoothed_curvatures, + gint curvatures_width, + gint curvatures_height, + GimpAsync *async); +static gint gimp_spline_candidate_cmp (const SplineCandidate *a, + const SplineCandidate *b, + gpointer user_data); +static GList * gimp_lineart_find_spline_candidates (GArray *max_positions, + gfloat *normals, + gint width, + gint distance_threshold, + gfloat max_angle_deg, + GimpAsync *async); + +static GArray * gimp_lineart_discrete_spline (Pixel p0, + GimpVector2 n0, + Pixel p1, + GimpVector2 n1); + +static gint gimp_number_of_transitions (GArray *pixels, + GeglBuffer *buffer); +static gboolean gimp_line_art_allow_closure (GeglBuffer *mask, + GArray *pixels, + GList **fill_pixels, + int significant_size, + int minimum_size); +static GArray * gimp_lineart_line_segment_until_hit (const GeglBuffer *buffer, + Pixel start, + GimpVector2 direction, + int size); +static gfloat * gimp_lineart_estimate_strokes_radii (GeglBuffer *mask, + GimpAsync *async); +static void gimp_line_art_simple_fill (GeglBuffer *buffer, + gint x, + gint y, + gint *counter); + +/* Some callback-type functions. */ + +static guint visited_hash_fun (Pixel *key); +static gboolean visited_equal_fun (Pixel *e1, + Pixel *e2); + +static inline gboolean border_in_direction (GeglBuffer *mask, + Pixel p, + int direction); +static inline GimpVector2 pair2normal (Pixel p, + gfloat *normals, + gint width); + +/* Edgel */ + +static Edgel * gimp_edgel_new (int x, + int y, + Direction direction); +static void gimp_edgel_init (Edgel *edgel); +static void gimp_edgel_clear (Edgel **edgel); +static int gimp_edgel_cmp (const Edgel *e1, + const Edgel *e2); +static guint edgel2index_hash_fun (Edgel *key); +static gboolean edgel2index_equal_fun (Edgel *e1, + Edgel *e2); + +static glong gimp_edgel_track_mark (GeglBuffer *mask, + Edgel edgel, + long size_limit); +static glong gimp_edgel_region_area (const GeglBuffer *mask, + Edgel start_edgel); + +/* Edgel set */ + +static GArray * gimp_edgelset_new (GeglBuffer *buffer, + GimpAsync *async); +static void gimp_edgelset_add (GArray *set, + int x, + int y, + Direction direction, + GHashTable *edgel2index); +static void gimp_edgelset_init_normals (GArray *set); +static void gimp_edgelset_smooth_normals (GArray *set, + int mask_size, + GimpAsync *async); +static void gimp_edgelset_compute_curvature (GArray *set, + GimpAsync *async); + +static void gimp_edgelset_build_graph (GArray *set, + GeglBuffer *buffer, + GHashTable *edgel2index, + GimpAsync *async); +static void gimp_edgelset_next8 (const GeglBuffer *buffer, + Edgel *it, + Edgel *n); + +G_DEFINE_TYPE_WITH_CODE (GimpLineArt, gimp_line_art, GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpLineArt)) + +static guint gimp_line_art_signals[LAST_SIGNAL] = { 0 }; + +static void +gimp_line_art_class_init (GimpLineArtClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gimp_line_art_signals[COMPUTING_START] = + g_signal_new ("computing-start", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLineArtClass, computing_start), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gimp_line_art_signals[COMPUTING_END] = + g_signal_new ("computing-end", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpLineArtClass, computing_end), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_line_art_finalize; + object_class->set_property = gimp_line_art_set_property; + object_class->get_property = gimp_line_art_get_property; + + g_object_class_install_property (object_class, PROP_SELECT_TRANSPARENT, + g_param_spec_boolean ("select-transparent", + _("Select transparent pixels instead of gray ones"), + _("Select transparent pixels instead of gray ones"), + TRUE, + G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_THRESHOLD, + g_param_spec_double ("threshold", + _("Line art detection threshold"), + _("Threshold to detect contour (higher values will include more pixels)"), + 0.0, 1.0, 0.92, + G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_MAX_GROW, + g_param_spec_int ("max-grow", + _("Maximum growing size"), + _("Maximum number of pixels grown under the line art"), + 1, 100, 3, + G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SPLINE_MAX_LEN, + g_param_spec_int ("spline-max-length", + _("Maximum curved closing length"), + _("Maximum curved length (in pixels) to close the line art"), + 0, 1000, 100, + G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SEGMENT_MAX_LEN, + g_param_spec_int ("segment-max-length", + _("Maximum straight closing length"), + _("Maximum straight length (in pixels) to close the line art"), + 0, 1000, 100, + G_PARAM_CONSTRUCT | GIMP_PARAM_READWRITE)); +} + +static void +gimp_line_art_init (GimpLineArt *line_art) +{ + line_art->priv = gimp_line_art_get_instance_private (line_art); +} + +static void +gimp_line_art_finalize (GObject *object) +{ + GimpLineArt *line_art = GIMP_LINE_ART (object); + + line_art->priv->frozen = FALSE; + + gimp_line_art_set_input (line_art, NULL); +} + +static void +gimp_line_art_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpLineArt *line_art = GIMP_LINE_ART (object); + + switch (property_id) + { + case PROP_SELECT_TRANSPARENT: + if (line_art->priv->select_transparent != g_value_get_boolean (value)) + { + line_art->priv->select_transparent = g_value_get_boolean (value); + gimp_line_art_compute (line_art); + } + break; + case PROP_MAX_GROW: + line_art->priv->max_grow = g_value_get_int (value); + break; + case PROP_THRESHOLD: + if (line_art->priv->threshold != g_value_get_double (value)) + { + line_art->priv->threshold = g_value_get_double (value); + gimp_line_art_compute (line_art); + } + break; + case PROP_SPLINE_MAX_LEN: + if (line_art->priv->spline_max_len != g_value_get_int (value)) + { + line_art->priv->spline_max_len = g_value_get_int (value); + if (line_art->priv->max_len_bound) + line_art->priv->segment_max_len = line_art->priv->spline_max_len; + gimp_line_art_compute (line_art); + } + break; + case PROP_SEGMENT_MAX_LEN: + if (line_art->priv->segment_max_len != g_value_get_int (value)) + { + line_art->priv->segment_max_len = g_value_get_int (value); + if (line_art->priv->max_len_bound) + line_art->priv->spline_max_len = line_art->priv->segment_max_len; + gimp_line_art_compute (line_art); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_line_art_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLineArt *line_art = GIMP_LINE_ART (object); + + switch (property_id) + { + case PROP_SELECT_TRANSPARENT: + g_value_set_boolean (value, line_art->priv->select_transparent); + break; + case PROP_MAX_GROW: + g_value_set_int (value, line_art->priv->max_grow); + break; + case PROP_THRESHOLD: + g_value_set_double (value, line_art->priv->threshold); + break; + case PROP_SPLINE_MAX_LEN: + g_value_set_int (value, line_art->priv->spline_max_len); + break; + case PROP_SEGMENT_MAX_LEN: + g_value_set_int (value, line_art->priv->segment_max_len); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/* Public functions */ + +GimpLineArt * +gimp_line_art_new (void) +{ + return g_object_new (GIMP_TYPE_LINE_ART, + NULL); +} + +void +gimp_line_art_bind_gap_length (GimpLineArt *line_art, + gboolean bound) +{ + line_art->priv->max_len_bound = bound; +} + +void +gimp_line_art_set_input (GimpLineArt *line_art, + GimpPickable *pickable) +{ + g_return_if_fail (pickable == NULL || GIMP_IS_VIEWABLE (pickable)); + + if (pickable != line_art->priv->input) + { + if (line_art->priv->input) + g_signal_handlers_disconnect_by_data (line_art->priv->input, line_art); + + g_set_object (&line_art->priv->input, pickable); + + gimp_line_art_compute (line_art); + + if (pickable) + { + g_signal_connect (pickable, "invalidate-preview", + G_CALLBACK (gimp_line_art_input_invalidate_preview), + line_art); + } + } +} + +GimpPickable * +gimp_line_art_get_input (GimpLineArt *line_art) +{ + return line_art->priv->input; +} + +void +gimp_line_art_freeze (GimpLineArt *line_art) +{ + g_return_if_fail (! line_art->priv->frozen); + + line_art->priv->frozen = TRUE; + line_art->priv->compute_after_thaw = FALSE; +} + +void +gimp_line_art_thaw (GimpLineArt *line_art) +{ + g_return_if_fail (line_art->priv->frozen); + + line_art->priv->frozen = FALSE; + if (line_art->priv->compute_after_thaw) + { + gimp_line_art_compute (line_art); + line_art->priv->compute_after_thaw = FALSE; + } +} + +gboolean +gimp_line_art_is_frozen (GimpLineArt *line_art) +{ + return line_art->priv->frozen; +} + +GeglBuffer * +gimp_line_art_get (GimpLineArt *line_art, + gfloat **distmap) +{ + g_return_val_if_fail (line_art->priv->input, NULL); + + if (line_art->priv->async) + { + gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async)); + } + else if (! line_art->priv->closed) + { + gimp_line_art_compute (line_art); + if (line_art->priv->async) + gimp_waitable_wait (GIMP_WAITABLE (line_art->priv->async)); + } + + g_return_val_if_fail (line_art->priv->closed, NULL); + + if (distmap) + *distmap = line_art->priv->distmap; + + return line_art->priv->closed; +} + +/* Functions for asynchronous computation. */ + +static void +gimp_line_art_compute (GimpLineArt *line_art) +{ + if (line_art->priv->frozen) + { + line_art->priv->compute_after_thaw = TRUE; + return; + } + + if (line_art->priv->async) + { + /* we cancel the async, but don't wait for it to finish, since + * it might take a while to respond. instead gimp_line_art_compute_cb() + * bails if the async has been canceled, to avoid accessing the line art. + */ + g_signal_emit (line_art, gimp_line_art_signals[COMPUTING_END], 0); + gimp_cancelable_cancel (GIMP_CANCELABLE (line_art->priv->async)); + g_clear_object (&line_art->priv->async); + } + + if (line_art->priv->idle_id) + { + g_source_remove (line_art->priv->idle_id); + line_art->priv->idle_id = 0; + } + + g_clear_object (&line_art->priv->closed); + g_clear_pointer (&line_art->priv->distmap, g_free); + + if (line_art->priv->input) + { + /* gimp_line_art_prepare_async() will flush the pickable, which + * may trigger this signal handler, and will leak a line art (as + * line_art->priv->async has not been set yet). + */ + g_signal_handlers_block_by_func ( + line_art->priv->input, + G_CALLBACK (gimp_line_art_input_invalidate_preview), + line_art); + line_art->priv->async = gimp_line_art_prepare_async (line_art, +1); + g_signal_emit (line_art, gimp_line_art_signals[COMPUTING_START], 0); + g_signal_handlers_unblock_by_func ( + line_art->priv->input, + G_CALLBACK (gimp_line_art_input_invalidate_preview), + line_art); + + gimp_async_add_callback_for_object (line_art->priv->async, + (GimpAsyncCallback) gimp_line_art_compute_cb, + line_art, line_art); + } +} + +static void +gimp_line_art_compute_cb (GimpAsync *async, + GimpLineArt *line_art) +{ + if (gimp_async_is_canceled (async)) + return; + + if (gimp_async_is_finished (async)) + { + LineArtResult *result; + + result = gimp_async_get_result (async); + + line_art->priv->closed = g_object_ref (result->closed); + line_art->priv->distmap = result->distmap; + result->distmap = NULL; + g_signal_emit (line_art, gimp_line_art_signals[COMPUTING_END], 0); + } + + g_clear_object (&line_art->priv->async); +} + +static GimpAsync * +gimp_line_art_prepare_async (GimpLineArt *line_art, + gint priority) +{ + GeglBuffer *buffer; + GimpAsync *async; + LineArtData *data; + + g_return_val_if_fail (GIMP_IS_PICKABLE (line_art->priv->input), NULL); + + gimp_pickable_flush (line_art->priv->input); + + buffer = gimp_gegl_buffer_dup ( + gimp_pickable_get_buffer (line_art->priv->input)); + + data = line_art_data_new (buffer, line_art); + + g_object_unref (buffer); + + async = gimp_parallel_run_async_full ( + priority, + (GimpRunAsyncFunc) gimp_line_art_prepare_async_func, + data, (GDestroyNotify) line_art_data_free); + + return async; +} + +static void +gimp_line_art_prepare_async_func (GimpAsync *async, + LineArtData *data) +{ + GeglBuffer *buffer; + GeglBuffer *closed = NULL; + gfloat *distmap = NULL; + gint buffer_x; + gint buffer_y; + gboolean has_alpha; + gboolean select_transparent = FALSE; + + has_alpha = babl_format_has_alpha (gegl_buffer_get_format (data->buffer)); + + if (has_alpha) + { + if (data->select_transparent) + { + /* don't select transparent regions if there are no fully + * transparent pixels. + */ + GeglBufferIterator *gi; + + gi = gegl_buffer_iterator_new (data->buffer, NULL, 0, + babl_format ("A u8"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3); + while (gegl_buffer_iterator_next (gi)) + { + guint8 *p = (guint8*) gi->items[0].data; + gint k; + + if (gimp_async_is_canceled (async)) + { + gegl_buffer_iterator_stop (gi); + + gimp_async_abort (async); + + line_art_data_free (data); + + return; + } + + for (k = 0; k < gi->length; k++) + { + if (! *p) + { + select_transparent = TRUE; + break; + } + p++; + } + if (select_transparent) + break; + } + if (select_transparent) + gegl_buffer_iterator_stop (gi); + } + } + + buffer = data->buffer; + buffer_x = gegl_buffer_get_x (data->buffer); + buffer_y = gegl_buffer_get_y (data->buffer); + + if (buffer_x != 0 || buffer_y != 0) + { + buffer = g_object_new (GEGL_TYPE_BUFFER, + "source", buffer, + "shift-x", buffer_x, + "shift-y", buffer_y, + NULL); + } + + /* For smart selection, we generate a binarized image with close + * regions, then run a composite selection with no threshold on + * this intermediate buffer. + */ + GIMP_TIMER_START(); + + closed = gimp_line_art_close (buffer, + select_transparent, + data->threshold, + data->spline_max_len, + data->segment_max_len, + /*minimal_lineart_area,*/ + 5, + /*normal_estimate_mask_size,*/ + 5, + /*end_point_rate,*/ + 0.85, + /*spline_max_angle,*/ + 90.0, + /*end_point_connectivity,*/ + 2, + /*spline_roundness,*/ + 1.0, + /*allow_self_intersections,*/ + TRUE, + /*created_regions_significant_area,*/ + 4, + /*created_regions_minimum_area,*/ + 100, + /*small_segments_from_spline_sources,*/ + TRUE, + &distmap, + async); + + GIMP_TIMER_END("close line-art"); + + if (buffer != data->buffer) + g_object_unref (buffer); + + if (! gimp_async_is_stopped (async)) + { + if (buffer_x != 0 || buffer_y != 0) + { + buffer = g_object_new (GEGL_TYPE_BUFFER, + "source", closed, + "shift-x", -buffer_x, + "shift-y", -buffer_y, + NULL); + + g_object_unref (closed); + + closed = buffer; + } + + gimp_async_finish_full (async, + line_art_result_new (closed, distmap), + (GDestroyNotify) line_art_result_free); + } + + line_art_data_free (data); +} + +static LineArtData * +line_art_data_new (GeglBuffer *buffer, + GimpLineArt *line_art) +{ + LineArtData *data = g_slice_new (LineArtData); + + data->buffer = g_object_ref (buffer); + data->select_transparent = line_art->priv->select_transparent; + data->threshold = line_art->priv->threshold; + data->spline_max_len = line_art->priv->spline_max_len; + data->segment_max_len = line_art->priv->segment_max_len; + + return data; +} + +static void +line_art_data_free (LineArtData *data) +{ + g_object_unref (data->buffer); + + g_slice_free (LineArtData, data); +} + +static LineArtResult * +line_art_result_new (GeglBuffer *closed, + gfloat *distmap) +{ + LineArtResult *data; + + data = g_slice_new (LineArtResult); + data->closed = closed; + data->distmap = distmap; + + return data; +} + +static void +line_art_result_free (LineArtResult *data) +{ + g_object_unref (data->closed); + g_clear_pointer (&data->distmap, g_free); + + g_slice_free (LineArtResult, data); +} + +static gboolean +gimp_line_art_idle (GimpLineArt *line_art) +{ + line_art->priv->idle_id = 0; + + gimp_line_art_compute (line_art); + + return G_SOURCE_REMOVE; +} + +static void +gimp_line_art_input_invalidate_preview (GimpViewable *viewable, + GimpLineArt *line_art) +{ + if (! line_art->priv->idle_id) + { + line_art->priv->idle_id = g_idle_add_full ( + GIMP_PRIORITY_VIEWABLE_IDLE, + (GSourceFunc) gimp_line_art_idle, + line_art, NULL); + } +} + +/* All actual computation functions. */ + +/** + * gimp_line_art_close: + * @buffer: the input #GeglBuffer. + * @select_transparent: whether we binarize the alpha channel or the + * luminosity. + * @stroke_threshold: [0-1] threshold value for detecting stroke pixels + * (higher values will detect more stroke pixels). + * @spline_max_length: the maximum length for creating splines between + * end points. + * @segment_max_length: the maximum length for creating segments + * between end points. Unlike splines, segments + * are straight lines. + * @minimal_lineart_area: the minimum size in number pixels for area to + * be considered as line art. + * @normal_estimate_mask_size: + * @end_point_rate: threshold to estimate if a curvature is an end-point + * in [0-1] range value. + * @spline_max_angle: the maximum angle between end point normals for + * creating splines between them. + * @end_point_connectivity: + * @spline_roundness: + * @allow_self_intersections: whether to allow created splines and + * segments to intersect. + * @created_regions_significant_area: + * @created_regions_minimum_area: + * @small_segments_from_spline_sources: + * @closed_distmap: a distance map of the closed line art pixels. + * @async: the #GimpAsync associated with the computation + * + * Creates a binarized version of the strokes of @buffer, detected either + * with luminosity (light means background) or alpha values depending on + * @select_transparent. This binary version of the strokes will have closed + * regions allowing adequate selection of "nearly closed regions". + * This algorithm is meant for digital painting (and in particular on the + * sketch-only step), and therefore will likely produce unexpected results on + * other types of input. + * + * The algorithm is the first step from the research paper "A Fast and + * Efficient Semi-guided Algorithm for Flat Coloring Line-arts", by Sébastian + * Fourey, David Tschumperlé, David Revoy. + * https://hal.archives-ouvertes.fr/hal-01891876 + * + * Returns: a new #GeglBuffer of format "Y u8" representing the + * binarized @line_art. If @lineart_distmap is not #NULL, a + * newly allocated float buffer is returned, which can be used + * for overflowing created masks later. + */ +static GeglBuffer * +gimp_line_art_close (GeglBuffer *buffer, + gboolean select_transparent, + gdouble stroke_threshold, + gint spline_max_length, + gint segment_max_length, + gint minimal_lineart_area, + gint normal_estimate_mask_size, + gfloat end_point_rate, + gfloat spline_max_angle, + gint end_point_connectivity, + gfloat spline_roundness, + gboolean allow_self_intersections, + gint created_regions_significant_area, + gint created_regions_minimum_area, + gboolean small_segments_from_spline_sources, + gfloat **closed_distmap, + GimpAsync *async) +{ + const Babl *gray_format; + GeglBufferIterator *gi; + GeglBuffer *closed = NULL; + GeglBuffer *strokes = NULL; + guchar max_value = 0; + gint width = gegl_buffer_get_width (buffer); + gint height = gegl_buffer_get_height (buffer); + gint i; + + if (select_transparent) + /* Keep alpha channel as gray levels */ + gray_format = babl_format ("A u8"); + else + /* Keep luminance */ + gray_format = babl_format ("Y' u8"); + + /* Transform the line art from any format to gray. */ + strokes = gegl_buffer_new (gegl_buffer_get_extent (buffer), + gray_format); + gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, strokes, NULL); + gegl_buffer_set_format (strokes, babl_format ("Y' u8")); + + if (! select_transparent) + { + /* Compute the biggest value */ + gi = gegl_buffer_iterator_new (strokes, NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + while (gegl_buffer_iterator_next (gi)) + { + guchar *data = (guchar*) gi->items[0].data; + gint k; + + if (gimp_async_is_canceled (async)) + { + gegl_buffer_iterator_stop (gi); + + gimp_async_abort (async); + + goto end1; + } + + for (k = 0; k < gi->length; k++) + { + if (*data > max_value) + max_value = *data; + data++; + } + } + } + + /* Make the image binary: 1 is stroke, 0 background */ + gi = gegl_buffer_iterator_new (strokes, NULL, 0, NULL, + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1); + while (gegl_buffer_iterator_next (gi)) + { + guchar *data = (guchar*) gi->items[0].data; + gint k; + + if (gimp_async_is_canceled (async)) + { + gegl_buffer_iterator_stop (gi); + + gimp_async_abort (async); + + goto end1; + } + + for (k = 0; k < gi->length; k++) + { + if (! select_transparent) + /* Negate the value. */ + *data = max_value - *data; + /* Apply a threshold. */ + if (*data > (guchar) (255.0f * (1.0f - stroke_threshold))) + *data = 1; + else + *data = 0; + data++; + } + } + + /* Denoise (remove small connected components) */ + gimp_lineart_denoise (strokes, minimal_lineart_area, async); + if (gimp_async_is_stopped (async)) + goto end1; + + closed = g_object_ref (strokes); + + if (spline_max_length > 0 || segment_max_length > 0) + { + GArray *keypoints = NULL; + GHashTable *visited = NULL; + gfloat *radii = NULL; + gfloat *normals = NULL; + gfloat *curvatures = NULL; + gfloat *smoothed_curvatures = NULL; + gfloat threshold; + gfloat clamped_threshold; + GList *fill_pixels = NULL; + GList *iter; + + normals = g_new0 (gfloat, width * height * 2); + curvatures = g_new0 (gfloat, width * height); + smoothed_curvatures = g_new0 (gfloat, width * height); + + /* Estimate normals & curvature */ + gimp_lineart_compute_normals_curvatures (strokes, normals, curvatures, + smoothed_curvatures, + normal_estimate_mask_size, + async); + if (gimp_async_is_stopped (async)) + goto end2; + + radii = gimp_lineart_estimate_strokes_radii (strokes, async); + if (gimp_async_is_stopped (async)) + goto end2; + threshold = 1.0f - end_point_rate; + clamped_threshold = MAX (0.25f, threshold); + for (i = 0; i < width; i++) + { + gint j; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end2; + } + + for (j = 0; j < height; j++) + { + if (smoothed_curvatures[i + j * width] >= (threshold / MAX (1.0f, radii[i + j * width])) || + curvatures[i + j * width] >= clamped_threshold) + curvatures[i + j * width] = 1.0; + else + curvatures[i + j * width] = 0.0; + } + } + g_clear_pointer (&radii, g_free); + + keypoints = gimp_lineart_curvature_extremums (curvatures, smoothed_curvatures, + width, height, async); + if (gimp_async_is_stopped (async)) + goto end2; + + visited = g_hash_table_new_full ((GHashFunc) visited_hash_fun, + (GEqualFunc) visited_equal_fun, + (GDestroyNotify) g_free, NULL); + + if (spline_max_length > 0) + { + GList *candidates; + SplineCandidate *candidate; + + candidates = gimp_lineart_find_spline_candidates (keypoints, normals, width, + spline_max_length, + spline_max_angle, + async); + if (gimp_async_is_stopped (async)) + goto end3; + + g_object_unref (closed); + closed = gimp_gegl_buffer_dup (strokes); + + /* Draw splines */ + while (candidates) + { + Pixel *p1; + Pixel *p2; + gboolean inserted = FALSE; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end3; + } + + p1 = g_new (Pixel, 1); + p2 = g_new (Pixel, 1); + + candidate = (SplineCandidate *) candidates->data; + p1->x = candidate->p1.x; + p1->y = candidate->p1.y; + p2->x = candidate->p2.x; + p2->y = candidate->p2.y; + + g_free (candidate); + candidates = g_list_delete_link (candidates, candidates); + + if ((! g_hash_table_contains (visited, p1) || + GPOINTER_TO_INT (g_hash_table_lookup (visited, p1)) < end_point_connectivity) && + (! g_hash_table_contains (visited, p2) || + GPOINTER_TO_INT (g_hash_table_lookup (visited, p2)) < end_point_connectivity)) + { + GArray *discrete_curve; + GimpVector2 vect1 = pair2normal (*p1, normals, width); + GimpVector2 vect2 = pair2normal (*p2, normals, width); + gfloat distance = gimp_vector2_length_val (gimp_vector2_sub_val (*p1, *p2)); + gint transitions; + + gimp_vector2_mul (&vect1, distance); + gimp_vector2_mul (&vect1, spline_roundness); + gimp_vector2_mul (&vect2, distance); + gimp_vector2_mul (&vect2, spline_roundness); + + discrete_curve = gimp_lineart_discrete_spline (*p1, vect1, *p2, vect2); + + transitions = allow_self_intersections ? + gimp_number_of_transitions (discrete_curve, strokes) : + gimp_number_of_transitions (discrete_curve, closed); + + if (transitions == 2 && + gimp_line_art_allow_closure (closed, discrete_curve, + &fill_pixels, + created_regions_significant_area, + created_regions_minimum_area)) + { + for (i = 0; i < discrete_curve->len; i++) + { + Pixel p = g_array_index (discrete_curve, Pixel, i); + + if (p.x >= 0 && p.x < gegl_buffer_get_width (closed) && + p.y >= 0 && p.y < gegl_buffer_get_height (closed)) + { + guchar val = 2; + + gegl_buffer_set (closed, GEGL_RECTANGLE ((gint) p.x, (gint) p.y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + } + g_hash_table_replace (visited, p1, + GINT_TO_POINTER (GPOINTER_TO_INT (g_hash_table_lookup (visited, p1)) + 1)); + g_hash_table_replace (visited, p2, + GINT_TO_POINTER (GPOINTER_TO_INT (g_hash_table_lookup (visited, p2)) + 1)); + inserted = TRUE; + } + g_array_free (discrete_curve, TRUE); + } + if (! inserted) + { + g_free (p1); + g_free (p2); + } + } + + end3: + g_list_free_full (candidates, g_free); + + if (gimp_async_is_stopped (async)) + goto end2; + } + + g_clear_object (&strokes); + + /* Draw straight line segments */ + if (segment_max_length > 0) + { + Pixel *point; + + point = (Pixel *) keypoints->data; + for (i = 0; i < keypoints->len; i++) + { + Pixel *p; + gboolean inserted = FALSE; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end2; + } + + p = g_new (Pixel, 1); + *p = *point; + + if (! g_hash_table_contains (visited, p) || + (small_segments_from_spline_sources && + GPOINTER_TO_INT (g_hash_table_lookup (visited, p)) < end_point_connectivity)) + { + GArray *segment = gimp_lineart_line_segment_until_hit (closed, *point, + pair2normal (*point, normals, width), + segment_max_length); + + if (segment->len && + gimp_line_art_allow_closure (closed, segment, &fill_pixels, + created_regions_significant_area, + created_regions_minimum_area)) + { + gint j; + + for (j = 0; j < segment->len; j++) + { + Pixel p2 = g_array_index (segment, Pixel, j); + guchar val = 2; + + gegl_buffer_set (closed, GEGL_RECTANGLE ((gint) p2.x, (gint) p2.y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + g_hash_table_replace (visited, p, + GINT_TO_POINTER (GPOINTER_TO_INT (g_hash_table_lookup (visited, p)) + 1)); + inserted = TRUE; + } + g_array_free (segment, TRUE); + } + if (! inserted) + g_free (p); + point++; + } + } + + for (iter = fill_pixels; iter; iter = iter->next) + { + Pixel *p = iter->data; + gint fill_max = created_regions_significant_area - 1; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end2; + } + + /* XXX A best approach would be to generalize + * gimp_drawable_bucket_fill() to work on any buffer (the code + * is already mostly there) rather than reimplementing a naive + * bucket fill. + * This is mostly a quick'n dirty first implementation which I + * will improve later. + */ + gimp_line_art_simple_fill (closed, (gint) p->x, (gint) p->y, &fill_max); + } + + end2: + g_list_free_full (fill_pixels, g_free); + g_free (normals); + g_free (curvatures); + g_free (smoothed_curvatures); + g_clear_pointer (&radii, g_free); + if (keypoints) + g_array_free (keypoints, TRUE); + g_clear_pointer (&visited, g_hash_table_destroy); + + if (gimp_async_is_stopped (async)) + goto end1; + } + else + { + g_clear_object (&strokes); + } + + if (closed_distmap) + { + GeglNode *graph; + GeglNode *input; + GeglNode *op; + + /* Flooding needs a distance map for closed line art. */ + *closed_distmap = g_new (gfloat, width * height); + + graph = gegl_node_new (); + input = gegl_node_new_child (graph, + "operation", "gegl:buffer-source", + "buffer", closed, + NULL); + op = gegl_node_new_child (graph, + "operation", "gegl:distance-transform", + "metric", GEGL_DISTANCE_METRIC_EUCLIDEAN, + "normalize", FALSE, + NULL); + gegl_node_connect_to (input, "output", + op, "input"); + gegl_node_blit (op, 1.0, gegl_buffer_get_extent (closed), + NULL, *closed_distmap, + GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_DEFAULT); + g_object_unref (graph); + } + + end1: + g_clear_object (&strokes); + + if (gimp_async_is_stopped (async)) + g_clear_object (&closed); + + return closed; +} + +static void +gimp_lineart_denoise (GeglBuffer *buffer, + int minimum_area, + GimpAsync *async) +{ + /* Keep connected regions with significant area. */ + GArray *region; + GQueue *q = g_queue_new (); + gint width = gegl_buffer_get_width (buffer); + gint height = gegl_buffer_get_height (buffer); + gboolean *visited = g_new0 (gboolean, width * height); + gint x, y; + + region = g_array_sized_new (TRUE, TRUE, sizeof (Pixel *), minimum_area); + + for (y = 0; y < height; ++y) + for (x = 0; x < width; ++x) + { + guchar has_stroke; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + gegl_buffer_sample (buffer, x, y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[x + y * width]) + { + Pixel *p = g_new (Pixel, 1); + gint regionSize = 0; + + p->x = x; + p->y = y; + + g_queue_push_tail (q, p); + visited[x + y * width] = TRUE; + + while (! g_queue_is_empty (q)) + { + Pixel *p; + gint p2x; + gint p2y; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + p = (Pixel *) g_queue_pop_head (q); + + p2x = p->x + 1; + p2y = p->y; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x +p2y * width] = TRUE; + } + } + p2x = p->x - 1; + p2y = p->y; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x + 1; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x - 1; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x - 1; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + p2x = p->x + 1; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && p2y >= 0 && p2y < height) + { + gegl_buffer_sample (buffer, p2x, p2y, NULL, &has_stroke, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (has_stroke && ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + } + + ++regionSize; + if (regionSize < minimum_area) + g_array_append_val (region, *p); + g_free (p); + } + if (regionSize < minimum_area) + { + Pixel *pixel = (Pixel *) region->data; + gint i = 0; + + for (; i < region->len; i++) + { + guchar val = 0; + gegl_buffer_set (buffer, GEGL_RECTANGLE (pixel->x, pixel->y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + pixel++; + } + } + g_array_remove_range (region, 0, region->len); + } + } + + end: + g_array_free (region, TRUE); + g_queue_free_full (q, g_free); + g_free (visited); +} + +static void +gimp_lineart_compute_normals_curvatures (GeglBuffer *mask, + gfloat *normals, + gfloat *curvatures, + gfloat *smoothed_curvatures, + int normal_estimate_mask_size, + GimpAsync *async) +{ + gfloat *edgels_curvatures = NULL; + gfloat *smoothed_curvature; + GArray *es = NULL; + Edgel **e; + gint width = gegl_buffer_get_width (mask); + + es = gimp_edgelset_new (mask, async); + if (gimp_async_is_stopped (async)) + goto end; + + e = (Edgel **) es->data; + + gimp_edgelset_smooth_normals (es, normal_estimate_mask_size, async); + if (gimp_async_is_stopped (async)) + goto end; + + gimp_edgelset_compute_curvature (es, async); + if (gimp_async_is_stopped (async)) + goto end; + + while (*e) + { + const float curvature = ((*e)->curvature > 0.0f) ? (*e)->curvature : 0.0f; + const float w = MAX (1e-8f, curvature * curvature); + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + normals[((*e)->x + (*e)->y * width) * 2] += w * (*e)->x_normal; + normals[((*e)->x + (*e)->y * width) * 2 + 1] += w * (*e)->y_normal; + curvatures[(*e)->x + (*e)->y * width] = MAX (curvature, + curvatures[(*e)->x + (*e)->y * width]); + e++; + } + for (int y = 0; y < gegl_buffer_get_height (mask); ++y) + { + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + for (int x = 0; x < gegl_buffer_get_width (mask); ++x) + { + const float _angle = atan2f (normals[(x + y * width) * 2 + 1], + normals[(x + y * width) * 2]); + normals[(x + y * width) * 2] = cosf (_angle); + normals[(x + y * width) * 2 + 1] = sinf (_angle); + } + } + + /* Smooth curvatures on edgels, then take maximum on each pixel. */ + edgels_curvatures = gimp_lineart_get_smooth_curvatures (es, async); + if (gimp_async_is_stopped (async)) + goto end; + + smoothed_curvature = edgels_curvatures; + + e = (Edgel **) es->data; + while (*e) + { + gfloat *pixel_curvature = &smoothed_curvatures[(*e)->x + (*e)->y * width]; + + if (*pixel_curvature < *smoothed_curvature) + *pixel_curvature = *smoothed_curvature; + + ++smoothed_curvature; + e++; + } + + end: + g_free (edgels_curvatures); + + if (es) + g_array_free (es, TRUE); +} + +static gfloat * +gimp_lineart_get_smooth_curvatures (GArray *edgelset, + GimpAsync *async) +{ + Edgel **e; + gfloat *smoothed_curvatures = g_new0 (gfloat, edgelset->len); + gfloat weights[9]; + gfloat smoothed_curvature; + gfloat weights_sum; + gint idx = 0; + + weights[0] = 1.0f; + for (int i = 1; i <= 8; ++i) + weights[i] = expf (-(i * i) / 30.0f); + + e = (Edgel **) edgelset->data; + while (*e) + { + Edgel *edgel_before = g_array_index (edgelset, Edgel*, (*e)->previous); + Edgel *edgel_after = g_array_index (edgelset, Edgel*, (*e)->next); + int n = 5; + int i = 1; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + g_free (smoothed_curvatures); + + return NULL; + } + + smoothed_curvature = (*e)->curvature; + weights_sum = weights[0]; + while (n-- && (edgel_after != edgel_before)) + { + smoothed_curvature += weights[i] * edgel_before->curvature; + smoothed_curvature += weights[i] * edgel_after->curvature; + edgel_before = g_array_index (edgelset, Edgel*, edgel_before->previous); + edgel_after = g_array_index (edgelset, Edgel*, edgel_after->next); + weights_sum += 2 * weights[i]; + i++; + } + smoothed_curvature /= weights_sum; + smoothed_curvatures[idx++] = smoothed_curvature; + + e++; + } + + return smoothed_curvatures; +} + +/** + * Keep one pixel per connected component of curvature extremums. + */ +static GArray * +gimp_lineart_curvature_extremums (gfloat *curvatures, + gfloat *smoothed_curvatures, + gint width, + gint height, + GimpAsync *async) +{ + gboolean *visited = g_new0 (gboolean, width * height); + GQueue *q = g_queue_new (); + GArray *max_positions; + + max_positions = g_array_new (FALSE, TRUE, sizeof (Pixel)); + + for (int y = 0; y < height; ++y) + { + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + for (int x = 0; x < width; ++x) + { + if ((curvatures[x + y * width] > 0.0) && ! visited[x + y * width]) + { + Pixel *p = g_new (Pixel, 1); + Pixel max_smoothed_curvature_pixel; + Pixel max_raw_curvature_pixel; + gfloat max_smoothed_curvature; + gfloat max_raw_curvature; + + max_smoothed_curvature_pixel = gimp_vector2_new (-1.0, -1.0); + max_smoothed_curvature = 0.0f; + + max_raw_curvature_pixel = gimp_vector2_new (x, y); + max_raw_curvature = curvatures[x + y * width]; + + p->x = x; + p->y = y; + g_queue_push_tail (q, p); + visited[x + y * width] = TRUE; + + while (! g_queue_is_empty (q)) + { + gfloat sc; + gfloat c; + gint p2x; + gint p2y; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + goto end; + } + + p = (Pixel *) g_queue_pop_head (q); + sc = smoothed_curvatures[(gint) p->x + (gint) p->y * width]; + c = curvatures[(gint) p->x + (gint) p->y * width]; + + curvatures[(gint) p->x + (gint) p->y * width] = 0.0f; + + p2x = (gint) p->x + 1; + p2y = (gint) p->y; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x - 1; + p2y = p->y; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x + 1; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x - 1; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x - 1; + p2y = p->y + 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + p2x = p->x + 1; + p2y = p->y - 1; + if (p2x >= 0 && p2x < width && + p2y >= 0 && p2y < height && + curvatures[p2x + p2y * width] > 0.0 && + ! visited[p2x + p2y * width]) + { + Pixel *p2 = g_new (Pixel, 1); + + p2->x = p2x; + p2->y = p2y; + g_queue_push_tail (q, p2); + visited[p2x + p2y * width] = TRUE; + } + + if (sc > max_smoothed_curvature) + { + max_smoothed_curvature_pixel = *p; + max_smoothed_curvature = sc; + } + if (c > max_raw_curvature) + { + max_raw_curvature_pixel = *p; + max_raw_curvature = c; + } + g_free (p); + } + if (max_smoothed_curvature > 0.0f) + { + curvatures[(gint) max_smoothed_curvature_pixel.x + (gint) max_smoothed_curvature_pixel.y * width] = max_smoothed_curvature; + g_array_append_val (max_positions, max_smoothed_curvature_pixel); + } + else + { + curvatures[(gint) max_raw_curvature_pixel.x + (gint) max_raw_curvature_pixel.y * width] = max_raw_curvature; + g_array_append_val (max_positions, max_raw_curvature_pixel); + } + } + } + } + + end: + g_queue_free_full (q, g_free); + g_free (visited); + + if (gimp_async_is_stopped (async)) + { + g_array_free (max_positions, TRUE); + max_positions = NULL; + } + + return max_positions; +} + +static gint +gimp_spline_candidate_cmp (const SplineCandidate *a, + const SplineCandidate *b, + gpointer user_data) +{ + /* This comparison actually returns the opposite of common comparison + * functions on purpose, as we want the first element on the list to + * be the "bigger". + */ + if (a->quality < b->quality) + return 1; + else if (a->quality > b->quality) + return -1; + else + return 0; +} + +static GList * +gimp_lineart_find_spline_candidates (GArray *max_positions, + gfloat *normals, + gint width, + gint distance_threshold, + gfloat max_angle_deg, + GimpAsync *async) +{ + GList *candidates = NULL; + const float CosMin = cosf (M_PI * (max_angle_deg / 180.0)); + gint i; + + for (i = 0; i < max_positions->len; i++) + { + Pixel p1 = g_array_index (max_positions, Pixel, i); + gint j; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + g_list_free_full (candidates, g_free); + + return NULL; + } + + for (j = i + 1; j < max_positions->len; j++) + { + Pixel p2 = g_array_index (max_positions, Pixel, j); + const float distance = gimp_vector2_length_val (gimp_vector2_sub_val (p1, p2)); + + if (distance <= distance_threshold) + { + GimpVector2 normalP1; + GimpVector2 normalP2; + GimpVector2 p1f; + GimpVector2 p2f; + GimpVector2 p1p2; + float cosN; + float qualityA; + float qualityB; + float qualityC; + float quality; + + normalP1 = gimp_vector2_new (normals[((gint) p1.x + (gint) p1.y * width) * 2], + normals[((gint) p1.x + (gint) p1.y * width) * 2 + 1]); + normalP2 = gimp_vector2_new (normals[((gint) p2.x + (gint) p2.y * width) * 2], + normals[((gint) p2.x + (gint) p2.y * width) * 2 + 1]); + p1f = gimp_vector2_new (p1.x, p1.y); + p2f = gimp_vector2_new (p2.x, p2.y); + p1p2 = gimp_vector2_sub_val (p2f, p1f); + + cosN = gimp_vector2_inner_product_val (normalP1, (gimp_vector2_neg_val (normalP2))); + qualityA = MAX (0.0f, 1 - distance / distance_threshold); + qualityB = MAX (0.0f, + (float) (gimp_vector2_inner_product_val (normalP1, p1p2) - gimp_vector2_inner_product_val (normalP2, p1p2)) / + distance); + qualityC = MAX (0.0f, cosN - CosMin); + quality = qualityA * qualityB * qualityC; + if (quality > 0) + { + SplineCandidate *candidate = g_new (SplineCandidate, 1); + + candidate->p1 = p1; + candidate->p2 = p2; + candidate->quality = quality; + + candidates = g_list_insert_sorted_with_data (candidates, candidate, + (GCompareDataFunc) gimp_spline_candidate_cmp, + NULL); + } + } + } + } + return candidates; +} + +static GArray * +gimp_lineart_discrete_spline (Pixel p0, + GimpVector2 n0, + Pixel p1, + GimpVector2 n1) +{ + GArray *points = g_array_new (FALSE, TRUE, sizeof (Pixel)); + const double a0 = 2 * p0.x - 2 * p1.x + n0.x - n1.x; + const double b0 = -3 * p0.x + 3 * p1.x - 2 * n0.x + n1.x; + const double c0 = n0.x; + const double d0 = p0.x; + const double a1 = 2 * p0.y - 2 * p1.y + n0.y - n1.y; + const double b1 = -3 * p0.y + 3 * p1.y - 2 * n0.y + n1.y; + const double c1 = n0.y; + const double d1 = p0.y; + + double t = 0.0; + const double dtMin = 1.0 / MAX (fabs (p0.x - p1.x), fabs (p0.y - p1.y)); + Pixel point = gimp_vector2_new ((gint) round (d0), (gint) round (d1)); + + g_array_append_val (points, point); + + while (t <= 1.0) + { + const double t2 = t * t; + const double t3 = t * t2; + double dx; + double dy; + Pixel p = gimp_vector2_new ((gint) round (a0 * t3 + b0 * t2 + c0 * t + d0), + (gint) round (a1 * t3 + b1 * t2 + c1 * t + d1)); + + /* create gimp_vector2_neq () ? */ + if (g_array_index (points, Pixel, points->len - 1).x != p.x || + g_array_index (points, Pixel, points->len - 1).y != p.y) + { + g_array_append_val (points, p); + } + dx = fabs (3 * a0 * t * t + 2 * b0 * t + c0) + 1e-8; + dy = fabs (3 * a1 * t * t + 2 * b1 * t + c1) + 1e-8; + t += MIN (dtMin, 0.75 / MAX (dx, dy)); + } + if (g_array_index (points, Pixel, points->len - 1).x != p1.x || + g_array_index (points, Pixel, points->len - 1).y != p1.y) + { + g_array_append_val (points, p1); + } + return points; +} + +static gint +gimp_number_of_transitions (GArray *pixels, + GeglBuffer *buffer) +{ + int result = 0; + + if (pixels->len > 0) + { + Pixel it = g_array_index (pixels, Pixel, 0); + guchar value; + gboolean previous; + gint i; + + gegl_buffer_sample (buffer, (gint) it.x, (gint) it.y, NULL, &value, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + previous = (gboolean) value; + + /* Starts at the second element. */ + for (i = 1; i < pixels->len; i++) + { + it = g_array_index (pixels, Pixel, i); + + gegl_buffer_sample (buffer, (gint) it.x, (gint) it.y, NULL, &value, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + result += ((gboolean) value != previous); + previous = (gboolean) value; + } + } + + return result; +} + +/** + * gimp_line_art_allow_closure: + * @mask: the current state of line art closure. + * @pixels: the pixels of a candidate closure (spline or segment). + * @fill_pixels: #GList of unsignificant pixels to bucket fill. + * @significant_size: number of pixels for area to be considered + * "significant". + * @minimum_size: number of pixels for area to be allowed. + * + * Checks whether adding the set of points @pixels to @mask will create + * 4-connected background regions whose size (i.e. number of pixels) + * will be below @minimum_size. If it creates such small areas, the + * function will refuse this candidate spline/segment, with the + * exception of very small areas under @significant_size. These + * micro-area are considered "unsignificant" and accepted (because they + * can be created in some conditions, for instance when created curves + * cross or start from a same endpoint), and one pixel for each + * micro-area will be added to @fill_pixels to be later filled along + * with the candidate pixels. + * + * Returns: #TRUE if @pixels should be added to @mask, #FALSE otherwise. + */ +static gboolean +gimp_line_art_allow_closure (GeglBuffer *mask, + GArray *pixels, + GList **fill_pixels, + int significant_size, + int minimum_size) +{ + /* A theorem from the paper is that a zone with more than + * `2 * (@minimum_size - 1)` edgels (border pixels) will have more + * than @minimum_size pixels. + * Since we are following the edges of the area, we can therefore stop + * earlier if we reach this number of edgels. + */ + const glong max_edgel_count = 2 * minimum_size; + + Pixel *p = (Pixel*) pixels->data; + GList *fp = NULL; + gint i; + + /* Mark pixels */ + for (i = 0; i < pixels->len; i++) + { + if (p->x >= 0 && p->x < gegl_buffer_get_width (mask) && + p->y >= 0 && p->y < gegl_buffer_get_height (mask)) + { + guchar val; + + gegl_buffer_sample (mask, (gint) p->x, (gint) p->y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + val = val ? 3 : 2; + gegl_buffer_set (mask, GEGL_RECTANGLE ((gint) p->x, (gint) p->y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + p++; + } + + for (i = 0; i < pixels->len; i++) + { + Pixel p = g_array_index (pixels, Pixel, i); + + for (int direction = 0; direction < 4; ++direction) + { + if (p.x >= 0 && p.x < gegl_buffer_get_width (mask) && + p.y >= 0 && p.y < gegl_buffer_get_height (mask) && + border_in_direction (mask, p, direction)) + { + Edgel e; + guchar val; + glong count; + glong area; + + gegl_buffer_sample (mask, (gint) p.x, (gint) p.y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if ((gboolean) (val & (4 << direction))) + continue; + + gimp_edgel_init (&e); + e.x = p.x; + e.y = p.y; + e.direction = direction; + + count = gimp_edgel_track_mark (mask, e, max_edgel_count); + if ((count != -1) && (count <= max_edgel_count)) + { + area = gimp_edgel_region_area (mask, e); + + if (area >= significant_size && area < minimum_size) + { + gint j; + + /* Remove marks */ + for (j = 0; j < pixels->len; j++) + { + Pixel p2 = g_array_index (pixels, Pixel, j); + + if (p2.x >= 0 && p2.x < gegl_buffer_get_width (mask) && + p2.y >= 0 && p2.y < gegl_buffer_get_height (mask)) + { + guchar val; + + gegl_buffer_sample (mask, (gint) p2.x, (gint) p2.y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + val &= 1; + gegl_buffer_set (mask, GEGL_RECTANGLE ((gint) p2.x, (gint) p2.y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + } + g_list_free_full (fp, g_free); + + return FALSE; + } + else if (area > 0 && area < significant_size) + { + Pixel *np = g_new (Pixel, 1); + + np->x = direction == XPlusDirection ? p.x + 1 : (direction == XMinusDirection ? p.x - 1 : p.x); + np->y = direction == YPlusDirection ? p.y + 1 : (direction == YMinusDirection ? p.y - 1 : p.y); + + if (np->x >= 0 && np->x < gegl_buffer_get_width (mask) && + np->y >= 0 && np->y < gegl_buffer_get_height (mask)) + fp = g_list_prepend (fp, np); + else + g_free (np); + } + } + } + } + } + + *fill_pixels = g_list_concat (*fill_pixels, fp); + /* Remove marks */ + for (i = 0; i < pixels->len; i++) + { + Pixel p = g_array_index (pixels, Pixel, i); + + if (p.x >= 0 && p.x < gegl_buffer_get_width (mask) && + p.y >= 0 && p.y < gegl_buffer_get_height (mask)) + { + guchar val; + + gegl_buffer_sample (mask, (gint) p.x, (gint) p.y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + val &= 1; + gegl_buffer_set (mask, GEGL_RECTANGLE ((gint) p.x, (gint) p.y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + } + return TRUE; +} + +static GArray * +gimp_lineart_line_segment_until_hit (const GeglBuffer *mask, + Pixel start, + GimpVector2 direction, + int size) +{ + GeglBuffer *buffer = (GeglBuffer *) mask; + gboolean out = FALSE; + GArray *points = g_array_new (FALSE, TRUE, sizeof (Pixel)); + int tmax; + GimpVector2 p0 = gimp_vector2_new (start.x, start.y); + + gimp_vector2_mul (&direction, (gdouble) size); + direction.x = round (direction.x); + direction.y = round (direction.y); + + tmax = MAX (abs ((int) direction.x), abs ((int) direction.y)); + + for (int t = 0; t <= tmax; ++t) + { + GimpVector2 v = gimp_vector2_add_val (p0, gimp_vector2_mul_val (direction, (float)t / tmax)); + Pixel p; + + p.x = (gint) round (v.x); + p.y = (gint) round (v.y); + if (p.x >= 0 && p.x < gegl_buffer_get_width (buffer) && + p.y >= 0 && p.y < gegl_buffer_get_height (buffer)) + { + guchar val; + gegl_buffer_sample (buffer, p.x, p.y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (out && val) + { + return points; + } + out = ! val; + } + else if (out) + { + return points; + } + else + { + g_array_free (points, TRUE); + return g_array_new (FALSE, TRUE, sizeof (Pixel)); + } + g_array_append_val (points, p); + } + + g_array_free (points, TRUE); + return g_array_new (FALSE, TRUE, sizeof (Pixel)); +} + +static gfloat * +gimp_lineart_estimate_strokes_radii (GeglBuffer *mask, + GimpAsync *async) +{ + GeglBufferIterator *gi; + gfloat *dist; + gfloat *thickness; + GeglNode *graph; + GeglNode *input; + GeglNode *op; + gint width = gegl_buffer_get_width (mask); + gint height = gegl_buffer_get_height (mask); + + /* Compute a distance map for the line art. */ + dist = g_new (gfloat, width * height); + + graph = gegl_node_new (); + input = gegl_node_new_child (graph, + "operation", "gegl:buffer-source", + "buffer", mask, + NULL); + op = gegl_node_new_child (graph, + "operation", "gegl:distance-transform", + "metric", GEGL_DISTANCE_METRIC_EUCLIDEAN, + "normalize", FALSE, + NULL); + gegl_node_connect_to (input, "output", op, "input"); + gegl_node_blit (op, 1.0, gegl_buffer_get_extent (mask), + NULL, dist, GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_DEFAULT); + g_object_unref (graph); + + thickness = g_new0 (gfloat, width * height); + gi = gegl_buffer_iterator_new (mask, NULL, 0, NULL, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + while (gegl_buffer_iterator_next (gi)) + { + guint8 *m = (guint8*) gi->items[0].data; + gint startx = gi->items[0].roi.x; + gint starty = gi->items[0].roi.y; + gint endy = starty + gi->items[0].roi.height; + gint endx = startx + gi->items[0].roi.width; + gint x; + gint y; + + if (gimp_async_is_canceled (async)) + { + gegl_buffer_iterator_stop (gi); + + gimp_async_abort (async); + + goto end; + } + + for (y = starty; y < endy; y++) + for (x = startx; x < endx; x++) + { + if (*m && dist[x + y * width] == 1.0) + { + gint dx = x; + gint dy = y; + gfloat d = 1.0; + gfloat nd; + gboolean neighbour_thicker = TRUE; + + while (neighbour_thicker) + { + gint px = dx - 1; + gint py = dy - 1; + gint nx = dx + 1; + gint ny = dy + 1; + + neighbour_thicker = FALSE; + if (px >= 0) + { + if ((nd = dist[px + dy * width]) > d) + { + d = nd; + dx = px; + neighbour_thicker = TRUE; + continue; + } + if (py >= 0 && (nd = dist[px + py * width]) > d) + { + d = nd; + dx = px; + dy = py; + neighbour_thicker = TRUE; + continue; + } + if (ny < height && (nd = dist[px + ny * width]) > d) + { + d = nd; + dx = px; + dy = ny; + neighbour_thicker = TRUE; + continue; + } + } + if (nx < width) + { + if ((nd = dist[nx + dy * width]) > d) + { + d = nd; + dx = nx; + neighbour_thicker = TRUE; + continue; + } + if (py >= 0 && (nd = dist[nx + py * width]) > d) + { + d = nd; + dx = nx; + dy = py; + neighbour_thicker = TRUE; + continue; + } + if (ny < height && (nd = dist[nx + ny * width]) > d) + { + d = nd; + dx = nx; + dy = ny; + neighbour_thicker = TRUE; + continue; + } + } + if (py > 0 && (nd = dist[dx + py * width]) > d) + { + d = nd; + dy = py; + neighbour_thicker = TRUE; + continue; + } + if (ny < height && (nd = dist[dx + ny * width]) > d) + { + d = nd; + dy = ny; + neighbour_thicker = TRUE; + continue; + } + } + thickness[(gint) x + (gint) y * width] = d; + } + m++; + } + } + + end: + g_free (dist); + + if (gimp_async_is_stopped (async)) + g_clear_pointer (&thickness, g_free); + + return thickness; +} + +static void +gimp_line_art_simple_fill (GeglBuffer *buffer, + gint x, + gint y, + gint *counter) +{ + guchar val; + + if (x < 0 || x >= gegl_buffer_get_width (buffer) || + y < 0 || y >= gegl_buffer_get_height (buffer) || + *counter <= 0) + return; + + gegl_buffer_sample (buffer, x, y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + if (! val) + { + val = 1; + gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + (*counter)--; + gimp_line_art_simple_fill (buffer, x + 1, y, counter); + gimp_line_art_simple_fill (buffer, x - 1, y, counter); + gimp_line_art_simple_fill (buffer, x, y + 1, counter); + gimp_line_art_simple_fill (buffer, x, y - 1, counter); + } +} + +static guint +visited_hash_fun (Pixel *key) +{ + /* Cantor pairing function. */ + return (key->x + key->y) * (key->x + key->y + 1) / 2 + key->y; +} + +static gboolean +visited_equal_fun (Pixel *e1, + Pixel *e2) +{ + return (e1->x == e2->x && e1->y == e2->y); +} + +static inline gboolean +border_in_direction (GeglBuffer *mask, + Pixel p, + int direction) +{ + gint px = (gint) p.x + DeltaX[direction]; + gint py = (gint) p.y + DeltaY[direction]; + + if (px >= 0 && px < gegl_buffer_get_width (mask) && + py >= 0 && py < gegl_buffer_get_height (mask)) + { + guchar val; + + gegl_buffer_sample (mask, px, py, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + return ! ((gboolean) val); + } + return TRUE; +} + +static inline GimpVector2 +pair2normal (Pixel p, + gfloat *normals, + gint width) +{ + return gimp_vector2_new (normals[((gint) p.x + (gint) p.y * width) * 2], + normals[((gint) p.x + (gint) p.y * width) * 2 + 1]); +} +/* Edgel functions */ + +static Edgel * +gimp_edgel_new (int x, + int y, + Direction direction) +{ + Edgel *edgel = g_new (Edgel, 1); + + edgel->x = x; + edgel->y = y; + edgel->direction = direction; + + gimp_edgel_init (edgel); + + return edgel; +} + +static void +gimp_edgel_init (Edgel *edgel) +{ + edgel->x_normal = 0; + edgel->y_normal = 0; + edgel->curvature = 0; + edgel->next = edgel->previous = G_MAXUINT; +} + +static void +gimp_edgel_clear (Edgel **edgel) +{ + g_clear_pointer (edgel, g_free); +} + +static int +gimp_edgel_cmp (const Edgel* e1, + const Edgel* e2) +{ + gimp_assert (e1 && e2); + + if ((e1->x == e2->x) && (e1->y == e2->y) && + (e1->direction == e2->direction)) + return 0; + else if ((e1->y < e2->y) || (e1->y == e2->y && e1->x < e2->x) || + (e1->y == e2->y && e1->x == e2->x && e1->direction < e2->direction)) + return -1; + else + return 1; +} + +static guint +edgel2index_hash_fun (Edgel *key) +{ + /* Cantor pairing function. + * Was not sure how to use the direction though. :-/ + */ + return (key->x + key->y) * (key->x + key->y + 1) / 2 + key->y * key->direction; +} + +static gboolean +edgel2index_equal_fun (Edgel *e1, + Edgel *e2) +{ + return (e1->x == e2->x && e1->y == e2->y && + e1->direction == e2->direction); +} + +/** + * @mask; + * @edgel: + * @size_limit: + * + * Track a border, marking inner pixels with a bit corresponding to the + * edgel traversed (4 << direction) for direction in {0,1,2,3}. + * Stop tracking after @size_limit edgels have been visited. + * + * Returns: Number of visited edgels, or -1 if an already visited edgel + * has been encountered. + */ +static glong +gimp_edgel_track_mark (GeglBuffer *mask, + Edgel edgel, + long size_limit) +{ + Edgel start = edgel; + long count = 1; + + do + { + guchar val; + + gimp_edgelset_next8 (mask, &edgel, &edgel); + gegl_buffer_sample (mask, edgel.x, edgel.y, NULL, &val, + NULL, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + if (val & 2) + { + /* Only mark pixels of the spline/segment */ + if (val & (4 << edgel.direction)) + return -1; + + /* Mark edgel in pixel (1 == In Mask, 2 == Spline/Segment) */ + val |= (4 << edgel.direction); + gegl_buffer_set (mask, GEGL_RECTANGLE (edgel.x, edgel.y, 1, 1), 0, + NULL, &val, GEGL_AUTO_ROWSTRIDE); + } + if (gimp_edgel_cmp (&edgel, &start) != 0) + ++count; + } + while (gimp_edgel_cmp (&edgel, &start) != 0 && count <= size_limit); + + return count; +} + +/** + * gimp_edgel_region_area: + * @mask: current state of closed line art buffer. + * @start_edgel: edgel to follow. + * + * Follows a line border, starting from @start_edgel to compute the area + * enclosed by this border. + * Unfortunately this may return a negative area when the line does not + * close a zone. In this case, there is an uncertaincy on the size of + * the created zone, and we should consider it a big size. + * + * Returns: the area enclosed by the followed line, or a negative value + * if the zone is not closed (hence actual area unknown). + */ +static glong +gimp_edgel_region_area (const GeglBuffer *mask, + Edgel start_edgel) +{ + Edgel edgel = start_edgel; + glong area = 0; + + do + { + if (edgel.direction == XPlusDirection) + area -= edgel.x; + else if (edgel.direction == XMinusDirection) + area += edgel.x - 1; + + gimp_edgelset_next8 (mask, &edgel, &edgel); + } + while (gimp_edgel_cmp (&edgel, &start_edgel) != 0); + + return area; +} + +/* Edgel sets */ + +static GArray * +gimp_edgelset_new (GeglBuffer *buffer, + GimpAsync *async) +{ + GeglBufferIterator *gi; + GArray *set; + GHashTable *edgel2index; + gint width = gegl_buffer_get_width (buffer); + gint height = gegl_buffer_get_height (buffer); + + set = g_array_new (TRUE, TRUE, sizeof (Edgel *)); + g_array_set_clear_func (set, (GDestroyNotify) gimp_edgel_clear); + + if (width <= 1 || height <= 1) + return set; + + edgel2index = g_hash_table_new ((GHashFunc) edgel2index_hash_fun, + (GEqualFunc) edgel2index_equal_fun); + + gi = gegl_buffer_iterator_new (buffer, GEGL_RECTANGLE (0, 0, width, height), + 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 5); + gegl_buffer_iterator_add (gi, buffer, GEGL_RECTANGLE (0, -1, width, height), + 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + gegl_buffer_iterator_add (gi, buffer, GEGL_RECTANGLE (0, 1, width, height), + 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + gegl_buffer_iterator_add (gi, buffer, GEGL_RECTANGLE (-1, 0, width, height), + 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + gegl_buffer_iterator_add (gi, buffer, GEGL_RECTANGLE (1, 0, width, height), + 0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + while (gegl_buffer_iterator_next (gi)) + { + guint8 *p = (guint8*) gi->items[0].data; + guint8 *prevy = (guint8*) gi->items[1].data; + guint8 *nexty = (guint8*) gi->items[2].data; + guint8 *prevx = (guint8*) gi->items[3].data; + guint8 *nextx = (guint8*) gi->items[4].data; + gint startx = gi->items[0].roi.x; + gint starty = gi->items[0].roi.y; + gint endy = starty + gi->items[0].roi.height; + gint endx = startx + gi->items[0].roi.width; + gint x; + gint y; + + if (gimp_async_is_canceled (async)) + { + gegl_buffer_iterator_stop (gi); + + gimp_async_abort (async); + + goto end; + } + + for (y = starty; y < endy; y++) + for (x = startx; x < endx; x++) + { + if (*(p++)) + { + if (! *prevy) + gimp_edgelset_add (set, x, y, YMinusDirection, edgel2index); + if (! *nexty) + gimp_edgelset_add (set, x, y, YPlusDirection, edgel2index); + if (! *prevx) + gimp_edgelset_add (set, x, y, XMinusDirection, edgel2index); + if (! *nextx) + gimp_edgelset_add (set, x, y, XPlusDirection, edgel2index); + } + prevy++; + nexty++; + prevx++; + nextx++; + } + } + + gimp_edgelset_build_graph (set, buffer, edgel2index, async); + if (gimp_async_is_stopped (async)) + goto end; + + gimp_edgelset_init_normals (set); + + end: + g_hash_table_destroy (edgel2index); + + if (gimp_async_is_stopped (async)) + { + g_array_free (set, TRUE); + set = NULL; + } + + return set; +} + +static void +gimp_edgelset_add (GArray *set, + int x, + int y, + Direction direction, + GHashTable *edgel2index) +{ + Edgel *edgel = gimp_edgel_new (x, y, direction); + unsigned long position = set->len; + + g_array_append_val (set, edgel); + g_hash_table_insert (edgel2index, edgel, GUINT_TO_POINTER (position)); +} + +static void +gimp_edgelset_init_normals (GArray *set) +{ + Edgel **e = (Edgel**) set->data; + + while (*e) + { + GimpVector2 n = Direction2Normal[(*e)->direction]; + + (*e)->x_normal = n.x; + (*e)->y_normal = n.y; + e++; + } +} + +static void +gimp_edgelset_smooth_normals (GArray *set, + int mask_size, + GimpAsync *async) +{ + const gfloat sigma = mask_size * 0.775; + const gfloat den = 2 * sigma * sigma; + gfloat weights[65]; + GimpVector2 smoothed_normal; + gint i; + + gimp_assert (mask_size <= 65); + + weights[0] = 1.0f; + for (int i = 1; i <= mask_size; ++i) + weights[i] = expf (-(i * i) / den); + + for (i = 0; i < set->len; i++) + { + Edgel *it = g_array_index (set, Edgel*, i); + Edgel *edgel_before = g_array_index (set, Edgel*, it->previous); + Edgel *edgel_after = g_array_index (set, Edgel*, it->next); + int n = mask_size; + int i = 1; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + return; + } + + smoothed_normal = Direction2Normal[it->direction]; + while (n-- && (edgel_after != edgel_before)) + { + smoothed_normal = gimp_vector2_add_val (smoothed_normal, + gimp_vector2_mul_val (Direction2Normal[edgel_before->direction], weights[i])); + smoothed_normal = gimp_vector2_add_val (smoothed_normal, + gimp_vector2_mul_val (Direction2Normal[edgel_after->direction], weights[i])); + edgel_before = g_array_index (set, Edgel *, edgel_before->previous); + edgel_after = g_array_index (set, Edgel *, edgel_after->next); + ++i; + } + gimp_vector2_normalize (&smoothed_normal); + it->x_normal = smoothed_normal.x; + it->y_normal = smoothed_normal.y; + } +} + +static void +gimp_edgelset_compute_curvature (GArray *set, + GimpAsync *async) +{ + gint i; + + for (i = 0; i < set->len; i++) + { + Edgel *it = g_array_index (set, Edgel*, i); + Edgel *previous = g_array_index (set, Edgel *, it->previous); + Edgel *next = g_array_index (set, Edgel *, it->next); + GimpVector2 n_prev = gimp_vector2_new (previous->x_normal, previous->y_normal); + GimpVector2 n_next = gimp_vector2_new (next->x_normal, next->y_normal); + GimpVector2 diff = gimp_vector2_mul_val (gimp_vector2_sub_val (n_next, n_prev), + 0.5); + const float c = gimp_vector2_length_val (diff); + const float crossp = n_prev.x * n_next.y - n_prev.y * n_next.x; + + it->curvature = (crossp > 0.0f) ? c : -c; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + return; + } + } +} + +static void +gimp_edgelset_build_graph (GArray *set, + GeglBuffer *buffer, + GHashTable *edgel2index, + GimpAsync *async) +{ + Edgel edgel; + gint i; + + for (i = 0; i < set->len; i++) + { + Edgel *neighbor; + Edgel *it = g_array_index (set, Edgel *, i); + guint neighbor_pos; + + if (gimp_async_is_canceled (async)) + { + gimp_async_abort (async); + + return; + } + + gimp_edgelset_next8 (buffer, it, &edgel); + + gimp_assert (g_hash_table_contains (edgel2index, &edgel)); + neighbor_pos = GPOINTER_TO_UINT (g_hash_table_lookup (edgel2index, &edgel)); + it->next = neighbor_pos; + neighbor = g_array_index (set, Edgel *, neighbor_pos); + neighbor->previous = i; + } +} + +static void +gimp_edgelset_next8 (const GeglBuffer *buffer, + Edgel *it, + Edgel *n) +{ + guint8 pixels[9]; + + n->x = it->x; + n->y = it->y; + n->direction = it->direction; + + gegl_buffer_get ((GeglBuffer *) buffer, + GEGL_RECTANGLE (n->x - 1, n->y - 1, 3, 3), + 1.0, NULL, pixels, GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + switch (n->direction) + { + case XPlusDirection: + if (pixels[8]) + { + ++(n->y); + ++(n->x); + n->direction = YMinusDirection; + } + else if (pixels[7]) + { + ++(n->y); + } + else + { + n->direction = YPlusDirection; + } + break; + case YMinusDirection: + if (pixels[2]) + { + ++(n->x); + --(n->y); + n->direction = XMinusDirection; + } + else if (pixels[5]) + { + ++(n->x); + } + else + { + n->direction = XPlusDirection; + } + break; + case XMinusDirection: + if (pixels[0]) + { + --(n->x); + --(n->y); + n->direction = YPlusDirection; + } + else if (pixels[1]) + { + --(n->y); + } + else + { + n->direction = YMinusDirection; + } + break; + case YPlusDirection: + if (pixels[6]) + { + --(n->x); + ++(n->y); + n->direction = XPlusDirection; + } + else if (pixels[3]) + { + --(n->x); + } + else + { + n->direction = XMinusDirection; + } + break; + default: + g_return_if_reached (); + break; + } +} diff --git a/app/core/gimplineart.h b/app/core/gimplineart.h new file mode 100644 index 0000000..cdec1ee --- /dev/null +++ b/app/core/gimplineart.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2017 Sébastien Fourey & David Tchumperlé + * Copyright (C) 2018 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_LINEART__ +#define __GIMP_LINEART__ + + +#include "gimpobject.h" + +#define GIMP_TYPE_LINE_ART (gimp_line_art_get_type ()) +#define GIMP_LINE_ART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LINE_ART, GimpLineArt)) +#define GIMP_LINE_ART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LINE_ART, GimpLineArtClass)) +#define GIMP_IS_LINE_ART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LINE_ART)) +#define GIMP_IS_LINE_ART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LINE_ART)) +#define GIMP_LINE_ART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LINE_ART, GimpLineArtClass)) + + +typedef struct _GimpLineArtClass GimpLineArtClass; +typedef struct _GimpLineArtPrivate GimpLineArtPrivate; + +struct _GimpLineArt +{ + GimpObject parent_instance; + + GimpLineArtPrivate *priv; +}; + +struct _GimpLineArtClass +{ + GimpObjectClass parent_class; + + /* Signals */ + + void (* computing_start) (GimpLineArt *line_art); + void (* computing_end) (GimpLineArt *line_art); +}; + + +GType gimp_line_art_get_type (void) G_GNUC_CONST; + +GimpLineArt * gimp_line_art_new (void); + +void gimp_line_art_bind_gap_length (GimpLineArt *line_art, + gboolean bound); + +void gimp_line_art_set_input (GimpLineArt *line_art, + GimpPickable *pickable); +GimpPickable * gimp_line_art_get_input (GimpLineArt *line_art); +void gimp_line_art_freeze (GimpLineArt *line_art); +void gimp_line_art_thaw (GimpLineArt *line_art); +gboolean gimp_line_art_is_frozen (GimpLineArt *line_art); + +GeglBuffer * gimp_line_art_get (GimpLineArt *line_art, + gfloat **distmap); + +#endif /* __GIMP_LINEART__ */ diff --git a/app/core/gimplist.c b/app/core/gimplist.c new file mode 100644 index 0000000..0fbca1f --- /dev/null +++ b/app/core/gimplist.c @@ -0,0 +1,691 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimplist.c + * Copyright (C) 2001-2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include /* strcmp */ + +#include + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimplist.h" + + +enum +{ + PROP_0, + PROP_UNIQUE_NAMES, + PROP_SORT_FUNC, + PROP_APPEND +}; + + +static void gimp_list_finalize (GObject *object); +static void gimp_list_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_list_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_list_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_list_add (GimpContainer *container, + GimpObject *object); +static void gimp_list_remove (GimpContainer *container, + GimpObject *object); +static void gimp_list_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); +static void gimp_list_clear (GimpContainer *container); +static gboolean gimp_list_have (GimpContainer *container, + GimpObject *object); +static void gimp_list_foreach (GimpContainer *container, + GFunc func, + gpointer user_data); +static GimpObject * gimp_list_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data); +static gboolean gimp_list_get_unique_names (GimpContainer *container); +static GimpObject * gimp_list_get_child_by_name (GimpContainer *container, + const gchar *name); +static GimpObject * gimp_list_get_child_by_index (GimpContainer *container, + gint index); +static gint gimp_list_get_child_index (GimpContainer *container, + GimpObject *object); + +static void gimp_list_uniquefy_name (GimpList *gimp_list, + GimpObject *object); +static void gimp_list_object_renamed (GimpObject *object, + GimpList *list); + + +G_DEFINE_TYPE (GimpList, gimp_list, GIMP_TYPE_CONTAINER) + +#define parent_class gimp_list_parent_class + + +static void +gimp_list_class_init (GimpListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + object_class->finalize = gimp_list_finalize; + object_class->set_property = gimp_list_set_property; + object_class->get_property = gimp_list_get_property; + + gimp_object_class->get_memsize = gimp_list_get_memsize; + + container_class->add = gimp_list_add; + container_class->remove = gimp_list_remove; + container_class->reorder = gimp_list_reorder; + container_class->clear = gimp_list_clear; + container_class->have = gimp_list_have; + container_class->foreach = gimp_list_foreach; + container_class->search = gimp_list_search; + container_class->get_unique_names = gimp_list_get_unique_names; + container_class->get_child_by_name = gimp_list_get_child_by_name; + container_class->get_child_by_index = gimp_list_get_child_by_index; + container_class->get_child_index = gimp_list_get_child_index; + + g_object_class_install_property (object_class, PROP_UNIQUE_NAMES, + g_param_spec_boolean ("unique-names", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SORT_FUNC, + g_param_spec_pointer ("sort-func", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_APPEND, + g_param_spec_boolean ("append", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_list_init (GimpList *list) +{ + list->queue = g_queue_new (); + list->unique_names = FALSE; + list->sort_func = NULL; + list->append = FALSE; +} + +static void +gimp_list_finalize (GObject *object) +{ + GimpList *list = GIMP_LIST (object); + + if (list->queue) + { + g_queue_free (list->queue); + list->queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_list_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpList *list = GIMP_LIST (object); + + switch (property_id) + { + case PROP_UNIQUE_NAMES: + list->unique_names = g_value_get_boolean (value); + break; + case PROP_SORT_FUNC: + gimp_list_set_sort_func (list, g_value_get_pointer (value)); + break; + case PROP_APPEND: + list->append = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_list_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpList *list = GIMP_LIST (object); + + switch (property_id) + { + case PROP_UNIQUE_NAMES: + g_value_set_boolean (value, list->unique_names); + break; + case PROP_SORT_FUNC: + g_value_set_pointer (value, list->sort_func); + break; + case PROP_APPEND: + g_value_set_boolean (value, list->append); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_list_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpList *list = GIMP_LIST (object); + gint64 memsize = 0; + + if (gimp_container_get_policy (GIMP_CONTAINER (list)) == + GIMP_CONTAINER_POLICY_STRONG) + { + memsize += gimp_g_queue_get_memsize_foreach (list->queue, + (GimpMemsizeFunc) gimp_object_get_memsize, + gui_size); + } + else + { + memsize += gimp_g_queue_get_memsize (list->queue, 0); + } + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gint +gimp_list_sort_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + GCompareFunc func = user_data; + + return func (a, b); +} + +static void +gimp_list_add (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + if (list->unique_names) + gimp_list_uniquefy_name (list, object); + + if (list->unique_names || list->sort_func) + g_signal_connect (object, "name-changed", + G_CALLBACK (gimp_list_object_renamed), + list); + + if (list->sort_func) + { + g_queue_insert_sorted (list->queue, object, gimp_list_sort_func, + list->sort_func); + } + else if (list->append) + { + g_queue_push_tail (list->queue, object); + } + else + { + g_queue_push_head (list->queue, object); + } + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); +} + +static void +gimp_list_remove (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + if (list->unique_names || list->sort_func) + g_signal_handlers_disconnect_by_func (object, + gimp_list_object_renamed, + list); + + g_queue_remove (list->queue, object); + + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); +} + +static void +gimp_list_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + GimpList *list = GIMP_LIST (container); + + g_queue_remove (list->queue, object); + + if (new_index == gimp_container_get_n_children (container) - 1) + g_queue_push_tail (list->queue, object); + else + g_queue_push_nth (list->queue, object, new_index); +} + +static void +gimp_list_clear (GimpContainer *container) +{ + GimpList *list = GIMP_LIST (container); + + while (g_queue_peek_head (list->queue)) + gimp_container_remove (container, g_queue_peek_head (list->queue)); +} + +static gboolean +gimp_list_have (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_find (list->queue, object) ? TRUE : FALSE; +} + +static void +gimp_list_foreach (GimpContainer *container, + GFunc func, + gpointer user_data) +{ + GimpList *list = GIMP_LIST (container); + + g_queue_foreach (list->queue, func, user_data); +} + +static GimpObject * +gimp_list_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data) +{ + GimpList *list = GIMP_LIST (container); + GList *iter; + + iter = list->queue->head; + + while (iter) + { + GimpObject *object = iter->data; + + iter = g_list_next (iter); + + if (func (object, user_data)) + return object; + } + + return NULL; +} + +static gboolean +gimp_list_get_unique_names (GimpContainer *container) +{ + GimpList *list = GIMP_LIST (container); + + return list->unique_names; +} + +static GimpObject * +gimp_list_get_child_by_name (GimpContainer *container, + const gchar *name) +{ + GimpList *list = GIMP_LIST (container); + GList *glist; + + for (glist = list->queue->head; glist; glist = g_list_next (glist)) + { + GimpObject *object = glist->data; + + if (! strcmp (gimp_object_get_name (object), name)) + return object; + } + + return NULL; +} + +static GimpObject * +gimp_list_get_child_by_index (GimpContainer *container, + gint index) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_peek_nth (list->queue, index); +} + +static gint +gimp_list_get_child_index (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_index (list->queue, (gpointer) object); +} + +/** + * gimp_list_new: + * @children_type: the #GType of objects the list is going to hold + * @unique_names: if the list should ensure that all its children + * have unique names. + * + * Creates a new #GimpList object. Since #GimpList is a #GimpContainer + * implementation, it holds GimpObjects. Thus @children_type must be + * GIMP_TYPE_OBJECT or a type derived from it. + * + * The returned list has the #GIMP_CONTAINER_POLICY_STRONG. + * + * Return value: a new #GimpList object + **/ +GimpContainer * +gimp_list_new (GType children_type, + gboolean unique_names) +{ + GimpList *list; + + g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL); + + list = g_object_new (GIMP_TYPE_LIST, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + "unique-names", unique_names ? TRUE : FALSE, + NULL); + + /* for debugging purposes only */ + gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type)); + + return GIMP_CONTAINER (list); +} + +/** + * gimp_list_new_weak: + * @children_type: the #GType of objects the list is going to hold + * @unique_names: if the list should ensure that all its children + * have unique names. + * + * Creates a new #GimpList object. Since #GimpList is a #GimpContainer + * implementation, it holds GimpObjects. Thus @children_type must be + * GIMP_TYPE_OBJECT or a type derived from it. + * + * The returned list has the #GIMP_CONTAINER_POLICY_WEAK. + * + * Return value: a new #GimpList object + **/ +GimpContainer * +gimp_list_new_weak (GType children_type, + gboolean unique_names) +{ + GimpList *list; + + g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL); + + list = g_object_new (GIMP_TYPE_LIST, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "unique-names", unique_names ? TRUE : FALSE, + NULL); + + /* for debugging purposes only */ + gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type)); + + return GIMP_CONTAINER (list); +} + +/** + * gimp_list_reverse: + * @list: a #GimpList + * + * Reverses the order of elements in a #GimpList. + **/ +void +gimp_list_reverse (GimpList *list) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1) + { + gimp_container_freeze (GIMP_CONTAINER (list)); + g_queue_reverse (list->queue); + gimp_container_thaw (GIMP_CONTAINER (list)); + } +} + +/** + * gimp_list_set_sort_func: + * @list: a #GimpList + * @sort_func: a #GCompareFunc + * + * Sorts the elements of @list using gimp_list_sort() and remembers the + * passed @sort_func in order to keep the list ordered across inserting + * or renaming children. + **/ +void +gimp_list_set_sort_func (GimpList *list, + GCompareFunc sort_func) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + if (sort_func != list->sort_func) + { + if (sort_func) + gimp_list_sort (list, sort_func); + + list->sort_func = sort_func; + g_object_notify (G_OBJECT (list), "sort-func"); + } +} + +/** + * gimp_list_get_sort_func: + * @list: a #GimpList + * + * Returns the @list's sort function, see gimp_list_set_sort_func(). + * + * Return Value: The @list's sort function. + **/ +GCompareFunc +gimp_list_get_sort_func (GimpList*list) +{ + g_return_val_if_fail (GIMP_IS_LIST (list), NULL); + + return list->sort_func; +} + +/** + * gimp_list_sort: + * @list: a #GimpList + * @sort_func: a #GCompareFunc + * + * Sorts the elements of a #GimpList according to the given @sort_func. + * See g_list_sort() for a detailed description of this function. + **/ +void +gimp_list_sort (GimpList *list, + GCompareFunc sort_func) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + g_return_if_fail (sort_func != NULL); + + if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1) + { + gimp_container_freeze (GIMP_CONTAINER (list)); + g_queue_sort (list->queue, gimp_list_sort_func, sort_func); + gimp_container_thaw (GIMP_CONTAINER (list)); + } +} + +/** + * gimp_list_sort_by_name: + * @list: a #GimpList + * + * Sorts the #GimpObject elements of a #GimpList by their names. + **/ +void +gimp_list_sort_by_name (GimpList *list) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + gimp_list_sort (list, (GCompareFunc) gimp_object_name_collate); +} + + +/* private functions */ + +static void +gimp_list_uniquefy_name (GimpList *gimp_list, + GimpObject *object) +{ + gchar *name = (gchar *) gimp_object_get_name (object); + GList *list; + + if (! name) + return; + + for (list = gimp_list->queue->head; list; list = g_list_next (list)) + { + GimpObject *object2 = list->data; + const gchar *name2 = gimp_object_get_name (object2); + + if (object != object2 && + name2 && + ! strcmp (name, name2)) + break; + } + + if (list) + { + gchar *ext; + gchar *new_name = NULL; + gint unique_ext = 0; + + name = g_strdup (name); + + ext = strrchr (name, '#'); + + if (ext) + { + gchar ext_str[8]; + + unique_ext = atoi (ext + 1); + + g_snprintf (ext_str, sizeof (ext_str), "%d", unique_ext); + + /* check if the extension really is of the form "#" */ + if (! strcmp (ext_str, ext + 1)) + { + if (ext > name && *(ext - 1) == ' ') + ext--; + + *ext = '\0'; + } + else + { + unique_ext = 0; + } + } + + do + { + unique_ext++; + + g_free (new_name); + + new_name = g_strdup_printf ("%s #%d", name, unique_ext); + + for (list = gimp_list->queue->head; list; list = g_list_next (list)) + { + GimpObject *object2 = list->data; + const gchar *name2 = gimp_object_get_name (object2); + + if (object != object2 && + name2 && + ! strcmp (new_name, name2)) + break; + } + } + while (list); + + g_free (name); + + gimp_object_take_name (object, new_name); + } +} + +static void +gimp_list_object_renamed (GimpObject *object, + GimpList *list) +{ + if (list->unique_names) + { + g_signal_handlers_block_by_func (object, + gimp_list_object_renamed, + list); + + gimp_list_uniquefy_name (list, object); + + g_signal_handlers_unblock_by_func (object, + gimp_list_object_renamed, + list); + } + + if (list->sort_func) + { + GList *glist; + gint old_index; + gint new_index = 0; + + old_index = g_queue_index (list->queue, object); + + for (glist = list->queue->head; glist; glist = g_list_next (glist)) + { + GimpObject *object2 = GIMP_OBJECT (glist->data); + + if (object == object2) + continue; + + if (list->sort_func (object, object2) > 0) + new_index++; + else + break; + } + + if (new_index != old_index) + gimp_container_reorder (GIMP_CONTAINER (list), object, new_index); + } +} diff --git a/app/core/gimplist.h b/app/core/gimplist.h new file mode 100644 index 0000000..194493e --- /dev/null +++ b/app/core/gimplist.h @@ -0,0 +1,70 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimplist.h + * Copyright (C) 2001-2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_LIST_H__ +#define __GIMP_LIST_H__ + + +#include "gimpcontainer.h" + + +#define GIMP_TYPE_LIST (gimp_list_get_type ()) +#define GIMP_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LIST, GimpList)) +#define GIMP_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LIST, GimpListClass)) +#define GIMP_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LIST)) +#define GIMP_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LIST)) +#define GIMP_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LIST, GimpListClass)) + + +typedef struct _GimpListClass GimpListClass; + +struct _GimpList +{ + GimpContainer parent_instance; + + GQueue *queue; + gboolean unique_names; + GCompareFunc sort_func; + gboolean append; +}; + +struct _GimpListClass +{ + GimpContainerClass parent_class; +}; + + +GType gimp_list_get_type (void) G_GNUC_CONST; + +GimpContainer * gimp_list_new (GType children_type, + gboolean unique_names); +GimpContainer * gimp_list_new_weak (GType children_type, + gboolean unique_names); + +void gimp_list_reverse (GimpList *list); +void gimp_list_set_sort_func (GimpList *list, + GCompareFunc sort_func); +GCompareFunc gimp_list_get_sort_func (GimpList *list); +void gimp_list_sort (GimpList *list, + GCompareFunc sort_func); +void gimp_list_sort_by_name (GimpList *list); + + +#endif /* __GIMP_LIST_H__ */ diff --git a/app/core/gimpmarshal.c b/app/core/gimpmarshal.c new file mode 100644 index 0000000..e4efb99 --- /dev/null +++ b/app/core/gimpmarshal.c @@ -0,0 +1,1901 @@ +/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ +#include + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + +/* BOOLEAN: BOOLEAN (../../../app/core/gimpmarshal.list:25) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOOLEAN) (gpointer data1, + gboolean arg1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__BOOLEAN callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boolean (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: DOUBLE (../../../app/core/gimpmarshal.list:26) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__DOUBLE) (gpointer data1, + gdouble arg1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__DOUBLE callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__DOUBLE) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_double (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: ENUM, INT (../../../app/core/gimpmarshal.list:27) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__ENUM_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__ENUM_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__ENUM_INT) (gpointer data1, + gint arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__ENUM_INT callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__ENUM_INT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: INT, UINT, ENUM (../../../app/core/gimpmarshal.list:28) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__INT_UINT_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__INT_UINT_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_UINT_ENUM) (gpointer data1, + gint arg1, + guint arg2, + gint arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__INT_UINT_ENUM callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_UINT_ENUM) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_enum (param_values + 3), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: OBJECT (../../../app/core/gimpmarshal.list:29) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT) (gpointer data1, + gpointer arg1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__OBJECT callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__OBJECT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: OBJECT, POINTER (../../../app/core/gimpmarshal.list:30) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__OBJECT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__OBJECT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT_POINTER) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__OBJECT_POINTER callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__OBJECT_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: OBJECT, POINTER, STRING (../../../app/core/gimpmarshal.list:31) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__OBJECT_POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__OBJECT_POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__OBJECT_POINTER_STRING) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__OBJECT_POINTER_STRING callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__OBJECT_POINTER_STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: STRING (../../../app/core/gimpmarshal.list:32) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer data1, + gpointer arg1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__STRING callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* BOOLEAN: STRING, FLAGS (../../../app/core/gimpmarshal.list:33) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_BOOLEAN__STRING_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_BOOLEAN__STRING_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_FLAGS) (gpointer data1, + gpointer arg1, + guint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_BOOLEAN__STRING_FLAGS callback; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING_FLAGS) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_flags (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* INT: DOUBLE (../../../app/core/gimpmarshal.list:35) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_INT__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_INT__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gint (*GMarshalFunc_INT__DOUBLE) (gpointer data1, + gdouble arg1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_INT__DOUBLE callback; + gint v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + 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_INT__DOUBLE) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_double (param_values + 1), + data2); + + g_value_set_int (return_value, v_return); +} + +/* VOID: BOOLEAN (../../../app/core/gimpmarshal.list:37) */ +#define gimp_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN + +/* VOID: BOOLEAN, INT, INT, INT, INT (../../../app/core/gimpmarshal.list:38) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__BOOLEAN_INT_INT_INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__BOOLEAN_INT_INT_INT_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOOLEAN_INT_INT_INT_INT) (gpointer data1, + gboolean arg1, + gint arg2, + gint arg3, + gint arg4, + gint arg5, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__BOOLEAN_INT_INT_INT_INT callback; + + g_return_if_fail (n_param_values == 6); + + 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__BOOLEAN_INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boolean (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_int (param_values + 3), + g_marshal_value_peek_int (param_values + 4), + g_marshal_value_peek_int (param_values + 5), + data2); +} + +/* VOID: BOXED (../../../app/core/gimpmarshal.list:39) */ +#define gimp_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +/* VOID: BOXED, ENUM (../../../app/core/gimpmarshal.list:40) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__BOXED_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__BOXED_ENUM (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_ENUM) (gpointer data1, + gpointer arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__BOXED_ENUM callback; + + 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__BOXED_ENUM) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_enum (param_values + 2), + data2); +} + +/* VOID: DOUBLE (../../../app/core/gimpmarshal.list:41) */ +#define gimp_marshal_VOID__DOUBLE g_cclosure_marshal_VOID__DOUBLE + +/* VOID: DOUBLE, DOUBLE (../../../app/core/gimpmarshal.list:42) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer data1, + gdouble arg1, + gdouble arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__DOUBLE_DOUBLE callback; + + 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); +} + +/* VOID: DOUBLE, DOUBLE, DOUBLE, DOUBLE (../../../app/core/gimpmarshal.list:43) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer data1, + gdouble arg1, + gdouble arg2, + gdouble arg3, + gdouble arg4, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE callback; + + g_return_if_fail (n_param_values == 5); + + 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_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), + g_marshal_value_peek_double (param_values + 3), + g_marshal_value_peek_double (param_values + 4), + data2); +} + +/* VOID: ENUM (../../../app/core/gimpmarshal.list:44) */ +#define gimp_marshal_VOID__ENUM g_cclosure_marshal_VOID__ENUM + +/* VOID: ENUM, INT (../../../app/core/gimpmarshal.list:45) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__ENUM_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__ENUM_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1, + gint arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__ENUM_INT callback; + + 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__ENUM_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID: ENUM, INT, BOOLEAN (../../../app/core/gimpmarshal.list:46) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__ENUM_INT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__ENUM_INT_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_INT_BOOLEAN) (gpointer data1, + gint arg1, + gint arg2, + gboolean arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__ENUM_INT_BOOLEAN callback; + + g_return_if_fail (n_param_values == 4); + + 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__ENUM_INT_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + data2); +} + +/* VOID: ENUM, OBJECT (../../../app/core/gimpmarshal.list:47) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__ENUM_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__ENUM_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_OBJECT) (gpointer data1, + gint arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__ENUM_OBJECT callback; + + 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__ENUM_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID: ENUM, POINTER (../../../app/core/gimpmarshal.list:48) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__ENUM_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__ENUM_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__ENUM_POINTER) (gpointer data1, + gint arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__ENUM_POINTER callback; + + 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__ENUM_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_enum (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID: FLAGS (../../../app/core/gimpmarshal.list:49) */ +#define gimp_marshal_VOID__FLAGS g_cclosure_marshal_VOID__FLAGS + +/* VOID: INT (../../../app/core/gimpmarshal.list:50) */ +#define gimp_marshal_VOID__INT g_cclosure_marshal_VOID__INT + +/* VOID: INT, BOOLEAN (../../../app/core/gimpmarshal.list:51) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__INT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__INT_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_BOOLEAN) (gpointer data1, + gint arg1, + gboolean arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__INT_BOOLEAN callback; + + 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__INT_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_boolean (param_values + 2), + data2); +} + +/* VOID: INT, INT (../../../app/core/gimpmarshal.list:52) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__INT_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1, + gint arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__INT_INT callback; + + 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__INT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID: INT, INT, INT, INT (../../../app/core/gimpmarshal.list:53) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__INT_INT_INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__INT_INT_INT_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_INT_INT_INT) (gpointer data1, + gint arg1, + gint arg2, + gint arg3, + gint arg4, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__INT_INT_INT_INT callback; + + g_return_if_fail (n_param_values == 5); + + 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__INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_int (param_values + 3), + g_marshal_value_peek_int (param_values + 4), + data2); +} + +/* VOID: INT, INT, BOOLEAN, BOOLEAN (../../../app/core/gimpmarshal.list:54) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__INT_INT_BOOLEAN_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__INT_INT_BOOLEAN_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_INT_BOOLEAN_BOOLEAN) (gpointer data1, + gint arg1, + gint arg2, + gboolean arg3, + gboolean arg4, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__INT_INT_BOOLEAN_BOOLEAN callback; + + g_return_if_fail (n_param_values == 5); + + 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__INT_INT_BOOLEAN_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + g_marshal_value_peek_boolean (param_values + 4), + data2); +} + +/* VOID: INT, OBJECT (../../../app/core/gimpmarshal.list:55) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__INT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__INT_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__INT_OBJECT) (gpointer data1, + gint arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__INT_OBJECT callback; + + 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__INT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_int (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID: OBJECT (../../../app/core/gimpmarshal.list:56) */ +#define gimp_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT + +/* VOID: OBJECT, BOOLEAN (../../../app/core/gimpmarshal.list:57) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__OBJECT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__OBJECT_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_BOOLEAN) (gpointer data1, + gpointer arg1, + gboolean arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__OBJECT_BOOLEAN callback; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_boolean (param_values + 2), + data2); +} + +/* VOID: OBJECT, INT (../../../app/core/gimpmarshal.list:58) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__OBJECT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__OBJECT_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_INT) (gpointer data1, + gpointer arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__OBJECT_INT callback; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID: OBJECT, OBJECT (../../../app/core/gimpmarshal.list:59) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__OBJECT_OBJECT callback; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID: OBJECT, POINTER (../../../app/core/gimpmarshal.list:60) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__OBJECT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__OBJECT_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_POINTER) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__OBJECT_POINTER callback; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + +/* VOID: OBJECT, STRING, STRING (../../../app/core/gimpmarshal.list:61) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__OBJECT_STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__OBJECT_STRING_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_STRING_STRING) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__OBJECT_STRING_STRING callback; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_STRING_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + data2); +} + +/* VOID: POINTER (../../../app/core/gimpmarshal.list:62) */ +#define gimp_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +/* VOID: POINTER, BOXED (../../../app/core/gimpmarshal.list:63) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__POINTER_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_BOXED) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__POINTER_BOXED callback; + + 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__POINTER_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID: POINTER, ENUM (../../../app/core/gimpmarshal.list:64) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__POINTER_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__POINTER_ENUM (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_ENUM) (gpointer data1, + gpointer arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__POINTER_ENUM callback; + + 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__POINTER_ENUM) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_enum (param_values + 2), + data2); +} + +/* VOID: POINTER, FLAGS, BOOLEAN (../../../app/core/gimpmarshal.list:65) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__POINTER_FLAGS_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__POINTER_FLAGS_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_FLAGS_BOOLEAN) (gpointer data1, + gpointer arg1, + guint arg2, + gboolean arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__POINTER_FLAGS_BOOLEAN callback; + + g_return_if_fail (n_param_values == 4); + + 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__POINTER_FLAGS_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_flags (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + data2); +} + +/* VOID: POINTER, OBJECT, ENUM, POINTER, POINTER, BOXED (../../../app/core/gimpmarshal.list:66) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED) (gpointer data1, + gpointer arg1, + gpointer arg2, + gint arg3, + gpointer arg4, + gpointer arg5, + gpointer arg6, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED callback; + + g_return_if_fail (n_param_values == 7); + + 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__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + g_marshal_value_peek_enum (param_values + 3), + g_marshal_value_peek_pointer (param_values + 4), + g_marshal_value_peek_pointer (param_values + 5), + g_marshal_value_peek_boxed (param_values + 6), + data2); +} + +/* VOID: POINTER, UINT, FLAGS (../../../app/core/gimpmarshal.list:67) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__POINTER_UINT_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__POINTER_UINT_FLAGS (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_UINT_FLAGS) (gpointer data1, + gpointer arg1, + guint arg2, + guint arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__POINTER_UINT_FLAGS callback; + + g_return_if_fail (n_param_values == 4); + + 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__POINTER_UINT_FLAGS) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_flags (param_values + 3), + data2); +} + +/* VOID: STRING (../../../app/core/gimpmarshal.list:68) */ +#define gimp_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING + +/* VOID: STRING, BOOLEAN, UINT, FLAGS (../../../app/core/gimpmarshal.list:69) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__STRING_BOOLEAN_UINT_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__STRING_BOOLEAN_UINT_FLAGS (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_BOOLEAN_UINT_FLAGS) (gpointer data1, + gpointer arg1, + gboolean arg2, + guint arg3, + guint arg4, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__STRING_BOOLEAN_UINT_FLAGS callback; + + g_return_if_fail (n_param_values == 5); + + 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__STRING_BOOLEAN_UINT_FLAGS) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_boolean (param_values + 2), + g_marshal_value_peek_uint (param_values + 3), + g_marshal_value_peek_flags (param_values + 4), + data2); +} + +/* VOID: STRING, DOUBLE, STRING, DOUBLE, STRING (../../../app/core/gimpmarshal.list:70) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING) (gpointer data1, + gpointer arg1, + gdouble arg2, + gpointer arg3, + gdouble arg4, + gpointer arg5, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING callback; + + g_return_if_fail (n_param_values == 6); + + 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__STRING_DOUBLE_STRING_DOUBLE_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_double (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + g_marshal_value_peek_double (param_values + 4), + g_marshal_value_peek_string (param_values + 5), + data2); +} + +/* VOID: STRING, FLAGS (../../../app/core/gimpmarshal.list:71) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__STRING_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__STRING_FLAGS (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_FLAGS) (gpointer data1, + gpointer arg1, + guint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__STRING_FLAGS callback; + + 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__STRING_FLAGS) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_flags (param_values + 2), + data2); +} + +/* VOID: STRING, STRING, STRING (../../../app/core/gimpmarshal.list:72) */ +/* Prototype for -Wmissing-prototypes */ +G_BEGIN_DECLS +extern +void gimp_marshal_VOID__STRING_STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +G_END_DECLS +void +gimp_marshal_VOID__STRING_STRING_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_STRING_STRING) (gpointer data1, + gpointer arg1, + gpointer arg2, + gpointer arg3, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_VOID__STRING_STRING_STRING callback; + + g_return_if_fail (n_param_values == 4); + + 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__STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + data2); +} + +/* VOID: VARIANT (../../../app/core/gimpmarshal.list:73) */ +#define gimp_marshal_VOID__VARIANT g_cclosure_marshal_VOID__VARIANT + +/* VOID: VOID (../../../app/core/gimpmarshal.list:74) */ +#define gimp_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID + diff --git a/app/core/gimpmarshal.h b/app/core/gimpmarshal.h new file mode 100644 index 0000000..e8247a2 --- /dev/null +++ b/app/core/gimpmarshal.h @@ -0,0 +1,378 @@ +/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ +#ifndef __GIMP_MARSHAL_MARSHAL_H__ +#define __GIMP_MARSHAL_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* BOOLEAN: BOOLEAN (../../../app/core/gimpmarshal.list:25) */ +extern +void gimp_marshal_BOOLEAN__BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: DOUBLE (../../../app/core/gimpmarshal.list:26) */ +extern +void gimp_marshal_BOOLEAN__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: ENUM, INT (../../../app/core/gimpmarshal.list:27) */ +extern +void gimp_marshal_BOOLEAN__ENUM_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: INT, UINT, ENUM (../../../app/core/gimpmarshal.list:28) */ +extern +void gimp_marshal_BOOLEAN__INT_UINT_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: OBJECT (../../../app/core/gimpmarshal.list:29) */ +extern +void gimp_marshal_BOOLEAN__OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: OBJECT, POINTER (../../../app/core/gimpmarshal.list:30) */ +extern +void gimp_marshal_BOOLEAN__OBJECT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: OBJECT, POINTER, STRING (../../../app/core/gimpmarshal.list:31) */ +extern +void gimp_marshal_BOOLEAN__OBJECT_POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: STRING (../../../app/core/gimpmarshal.list:32) */ +extern +void gimp_marshal_BOOLEAN__STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN: STRING, FLAGS (../../../app/core/gimpmarshal.list:33) */ +extern +void gimp_marshal_BOOLEAN__STRING_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* INT: DOUBLE (../../../app/core/gimpmarshal.list:35) */ +extern +void gimp_marshal_INT__DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: BOOLEAN (../../../app/core/gimpmarshal.list:37) */ +#define gimp_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN + +/* VOID: BOOLEAN, INT, INT, INT, INT (../../../app/core/gimpmarshal.list:38) */ +extern +void gimp_marshal_VOID__BOOLEAN_INT_INT_INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: BOXED (../../../app/core/gimpmarshal.list:39) */ +#define gimp_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +/* VOID: BOXED, ENUM (../../../app/core/gimpmarshal.list:40) */ +extern +void gimp_marshal_VOID__BOXED_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: DOUBLE (../../../app/core/gimpmarshal.list:41) */ +#define gimp_marshal_VOID__DOUBLE g_cclosure_marshal_VOID__DOUBLE + +/* VOID: DOUBLE, DOUBLE (../../../app/core/gimpmarshal.list:42) */ +extern +void gimp_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: DOUBLE, DOUBLE, DOUBLE, DOUBLE (../../../app/core/gimpmarshal.list:43) */ +extern +void gimp_marshal_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: ENUM (../../../app/core/gimpmarshal.list:44) */ +#define gimp_marshal_VOID__ENUM g_cclosure_marshal_VOID__ENUM + +/* VOID: ENUM, INT (../../../app/core/gimpmarshal.list:45) */ +extern +void gimp_marshal_VOID__ENUM_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: ENUM, INT, BOOLEAN (../../../app/core/gimpmarshal.list:46) */ +extern +void gimp_marshal_VOID__ENUM_INT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: ENUM, OBJECT (../../../app/core/gimpmarshal.list:47) */ +extern +void gimp_marshal_VOID__ENUM_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: ENUM, POINTER (../../../app/core/gimpmarshal.list:48) */ +extern +void gimp_marshal_VOID__ENUM_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: FLAGS (../../../app/core/gimpmarshal.list:49) */ +#define gimp_marshal_VOID__FLAGS g_cclosure_marshal_VOID__FLAGS + +/* VOID: INT (../../../app/core/gimpmarshal.list:50) */ +#define gimp_marshal_VOID__INT g_cclosure_marshal_VOID__INT + +/* VOID: INT, BOOLEAN (../../../app/core/gimpmarshal.list:51) */ +extern +void gimp_marshal_VOID__INT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: INT, INT (../../../app/core/gimpmarshal.list:52) */ +extern +void gimp_marshal_VOID__INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: INT, INT, INT, INT (../../../app/core/gimpmarshal.list:53) */ +extern +void gimp_marshal_VOID__INT_INT_INT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: INT, INT, BOOLEAN, BOOLEAN (../../../app/core/gimpmarshal.list:54) */ +extern +void gimp_marshal_VOID__INT_INT_BOOLEAN_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: INT, OBJECT (../../../app/core/gimpmarshal.list:55) */ +extern +void gimp_marshal_VOID__INT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: OBJECT (../../../app/core/gimpmarshal.list:56) */ +#define gimp_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT + +/* VOID: OBJECT, BOOLEAN (../../../app/core/gimpmarshal.list:57) */ +extern +void gimp_marshal_VOID__OBJECT_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: OBJECT, INT (../../../app/core/gimpmarshal.list:58) */ +extern +void gimp_marshal_VOID__OBJECT_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: OBJECT, OBJECT (../../../app/core/gimpmarshal.list:59) */ +extern +void gimp_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: OBJECT, POINTER (../../../app/core/gimpmarshal.list:60) */ +extern +void gimp_marshal_VOID__OBJECT_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: OBJECT, STRING, STRING (../../../app/core/gimpmarshal.list:61) */ +extern +void gimp_marshal_VOID__OBJECT_STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: POINTER (../../../app/core/gimpmarshal.list:62) */ +#define gimp_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +/* VOID: POINTER, BOXED (../../../app/core/gimpmarshal.list:63) */ +extern +void gimp_marshal_VOID__POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: POINTER, ENUM (../../../app/core/gimpmarshal.list:64) */ +extern +void gimp_marshal_VOID__POINTER_ENUM (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: POINTER, FLAGS, BOOLEAN (../../../app/core/gimpmarshal.list:65) */ +extern +void gimp_marshal_VOID__POINTER_FLAGS_BOOLEAN (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: POINTER, OBJECT, ENUM, POINTER, POINTER, BOXED (../../../app/core/gimpmarshal.list:66) */ +extern +void gimp_marshal_VOID__POINTER_OBJECT_ENUM_POINTER_POINTER_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: POINTER, UINT, FLAGS (../../../app/core/gimpmarshal.list:67) */ +extern +void gimp_marshal_VOID__POINTER_UINT_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: STRING (../../../app/core/gimpmarshal.list:68) */ +#define gimp_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING + +/* VOID: STRING, BOOLEAN, UINT, FLAGS (../../../app/core/gimpmarshal.list:69) */ +extern +void gimp_marshal_VOID__STRING_BOOLEAN_UINT_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: STRING, DOUBLE, STRING, DOUBLE, STRING (../../../app/core/gimpmarshal.list:70) */ +extern +void gimp_marshal_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: STRING, FLAGS (../../../app/core/gimpmarshal.list:71) */ +extern +void gimp_marshal_VOID__STRING_FLAGS (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: STRING, STRING, STRING (../../../app/core/gimpmarshal.list:72) */ +extern +void gimp_marshal_VOID__STRING_STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID: VARIANT (../../../app/core/gimpmarshal.list:73) */ +#define gimp_marshal_VOID__VARIANT g_cclosure_marshal_VOID__VARIANT + +/* VOID: VOID (../../../app/core/gimpmarshal.list:74) */ +#define gimp_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID + + +G_END_DECLS + +#endif /* __GIMP_MARSHAL_MARSHAL_H__ */ diff --git a/app/core/gimpmarshal.list b/app/core/gimpmarshal.list new file mode 100644 index 0000000..f09116e --- /dev/null +++ b/app/core/gimpmarshal.list @@ -0,0 +1,74 @@ +# see glib-genmarshal(1) for a detailed description of the file format, +# possible parameter types are: +# VOID indicates no return type, or no extra +# parameters. if VOID is used as the parameter +# list, no additional parameters may be present. +# BOOLEAN for boolean types (gboolean) +# CHAR for signed char types (gchar) +# UCHAR for unsigned char types (guchar) +# INT for signed integer types (gint) +# UINT for unsigned integer types (guint) +# LONG for signed long integer types (glong) +# ULONG for unsigned long integer types (gulong) +# ENUM for enumeration types (gint) +# FLAGS for flag enumeration types (guint) +# FLOAT for single-precision float types (gfloat) +# DOUBLE for double-precision float types (gdouble) +# STRING for string types (gchar*) +# BOXED for boxed (anonymous but reference counted) types (GBoxed*) +# POINTER for anonymous pointer types (gpointer) +# PARAM for GParamSpec or derived types (GParamSpec*) +# OBJECT for GObject or derived types (GObject*) +# NONE deprecated alias for VOID +# BOOL deprecated alias for BOOLEAN + +BOOLEAN: BOOLEAN +BOOLEAN: DOUBLE +BOOLEAN: ENUM, INT +BOOLEAN: INT, UINT, ENUM +BOOLEAN: OBJECT +BOOLEAN: OBJECT, POINTER +BOOLEAN: OBJECT, POINTER, STRING +BOOLEAN: STRING +BOOLEAN: STRING, FLAGS + +INT: DOUBLE + +VOID: BOOLEAN +VOID: BOOLEAN, INT, INT, INT, INT +VOID: BOXED +VOID: BOXED, ENUM +VOID: DOUBLE +VOID: DOUBLE, DOUBLE +VOID: DOUBLE, DOUBLE, DOUBLE, DOUBLE +VOID: ENUM +VOID: ENUM, INT +VOID: ENUM, INT, BOOLEAN +VOID: ENUM, OBJECT +VOID: ENUM, POINTER +VOID: FLAGS +VOID: INT +VOID: INT, BOOLEAN +VOID: INT, INT +VOID: INT, INT, INT, INT +VOID: INT, INT, BOOLEAN, BOOLEAN +VOID: INT, OBJECT +VOID: OBJECT +VOID: OBJECT, BOOLEAN +VOID: OBJECT, INT +VOID: OBJECT, OBJECT +VOID: OBJECT, POINTER +VOID: OBJECT, STRING, STRING +VOID: POINTER +VOID: POINTER, BOXED +VOID: POINTER, ENUM +VOID: POINTER, FLAGS, BOOLEAN +VOID: POINTER, OBJECT, ENUM, POINTER, POINTER, BOXED +VOID: POINTER, UINT, FLAGS +VOID: STRING +VOID: STRING, BOOLEAN, UINT, FLAGS +VOID: STRING, DOUBLE, STRING, DOUBLE, STRING +VOID: STRING, FLAGS +VOID: STRING, STRING, STRING +VOID: VARIANT +VOID: VOID diff --git a/app/core/gimpmaskundo.c b/app/core/gimpmaskundo.c new file mode 100644 index 0000000..d388ee5 --- /dev/null +++ b/app/core/gimpmaskundo.c @@ -0,0 +1,292 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "gimp-memsize.h" +#include "gimpchannel.h" +#include "gimpmaskundo.h" + + +enum +{ + PROP_0, + PROP_CONVERT_FORMAT +}; + + +static void gimp_mask_undo_constructed (GObject *object); +static void gimp_mask_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mask_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_mask_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_mask_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_mask_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpMaskUndo, gimp_mask_undo, GIMP_TYPE_ITEM_UNDO) + +#define parent_class gimp_mask_undo_parent_class + + +static void +gimp_mask_undo_class_init (GimpMaskUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_mask_undo_constructed; + object_class->set_property = gimp_mask_undo_set_property; + object_class->get_property = gimp_mask_undo_get_property; + + gimp_object_class->get_memsize = gimp_mask_undo_get_memsize; + + undo_class->pop = gimp_mask_undo_pop; + undo_class->free = gimp_mask_undo_free; + + g_object_class_install_property (object_class, PROP_CONVERT_FORMAT, + g_param_spec_boolean ("convert-format", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_mask_undo_init (GimpMaskUndo *undo) +{ +} + +static void +gimp_mask_undo_constructed (GObject *object) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); + GimpItem *item; + GimpDrawable *drawable; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_CHANNEL (GIMP_ITEM_UNDO (object)->item)); + + item = GIMP_ITEM_UNDO (object)->item; + drawable = GIMP_DRAWABLE (item); + + mask_undo->format = gimp_drawable_get_format (drawable); + + if (gimp_item_bounds (item, + &mask_undo->bounds.x, + &mask_undo->bounds.y, + &mask_undo->bounds.width, + &mask_undo->bounds.height)) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (drawable); + GeglRectangle rect; + + gegl_rectangle_align_to_buffer (&rect, &mask_undo->bounds, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + mask_undo->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + rect.width, + rect.height), + mask_undo->format); + + gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE, + mask_undo->buffer, GEGL_RECTANGLE (0, 0, 0, 0)); + + mask_undo->x = rect.x; + mask_undo->y = rect.y; + } +} + +static void +gimp_mask_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); + + switch (property_id) + { + case PROP_CONVERT_FORMAT: + mask_undo->convert_format = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mask_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); + + switch (property_id) + { + case PROP_CONVERT_FORMAT: + g_value_set_boolean (value, mask_undo->convert_format); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_mask_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); + gint64 memsize = 0; + + memsize += gimp_gegl_buffer_get_memsize (mask_undo->buffer); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_mask_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (undo); + GimpItem *item = GIMP_ITEM_UNDO (undo)->item; + GimpDrawable *drawable = GIMP_DRAWABLE (item); + GimpChannel *channel = GIMP_CHANNEL (item); + GeglBuffer *new_buffer = NULL; + GeglRectangle bounds = {}; + GeglRectangle rect = {}; + const Babl *format; + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + format = gimp_drawable_get_format (drawable); + + if (gimp_item_bounds (item, + &bounds.x, + &bounds.y, + &bounds.width, + &bounds.height)) + { + GeglBuffer *buffer = gimp_drawable_get_buffer (drawable); + + gegl_rectangle_align_to_buffer (&rect, &bounds, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + rect.width, rect.height), + format); + + gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE, + new_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); + + gegl_buffer_clear (buffer, &rect); + } + + if (mask_undo->convert_format) + { + GeglBuffer *buffer; + gint width = gimp_item_get_width (item); + gint height = gimp_item_get_height (item); + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), + mask_undo->format); + + gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); + g_object_unref (buffer); + } + + if (mask_undo->buffer) + { + gimp_gegl_buffer_copy (mask_undo->buffer, + NULL, + GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (mask_undo->x, mask_undo->y, 0, 0)); + + g_object_unref (mask_undo->buffer); + } + + /* invalidate the current bounds and boundary of the mask */ + gimp_drawable_invalidate_boundary (drawable); + + if (mask_undo->buffer) + { + channel->empty = FALSE; + channel->x1 = mask_undo->bounds.x; + channel->y1 = mask_undo->bounds.y; + channel->x2 = mask_undo->bounds.x + mask_undo->bounds.width; + channel->y2 = mask_undo->bounds.y + mask_undo->bounds.height; + } + else + { + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = gimp_item_get_width (item); + channel->y2 = gimp_item_get_height (item); + } + + /* we know the bounds */ + channel->bounds_known = TRUE; + + /* set the new mask undo parameters */ + mask_undo->format = format; + mask_undo->buffer = new_buffer; + mask_undo->bounds = bounds; + mask_undo->x = rect.x; + mask_undo->y = rect.y; + + gimp_drawable_update (drawable, 0, 0, -1, -1); +} + +static void +gimp_mask_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (undo); + + g_clear_object (&mask_undo->buffer); + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/core/gimpmaskundo.h b/app/core/gimpmaskundo.h new file mode 100644 index 0000000..53b92e8 --- /dev/null +++ b/app/core/gimpmaskundo.h @@ -0,0 +1,58 @@ +/* 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 . + */ + +#ifndef __GIMP_MASK_UNDO_H__ +#define __GIMP_MASK_UNDO_H__ + + +#include "gimpitemundo.h" + + +#define GIMP_TYPE_MASK_UNDO (gimp_mask_undo_get_type ()) +#define GIMP_MASK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MASK_UNDO, GimpMaskUndo)) +#define GIMP_MASK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MASK_UNDO, GimpMaskUndoClass)) +#define GIMP_IS_MASK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MASK_UNDO)) +#define GIMP_IS_MASK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MASK_UNDO)) +#define GIMP_MASK_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MASK_UNDO, GimpMaskUndoClass)) + + +typedef struct _GimpMaskUndo GimpMaskUndo; +typedef struct _GimpMaskUndoClass GimpMaskUndoClass; + +struct _GimpMaskUndo +{ + GimpItemUndo parent_instance; + + gboolean convert_format; + + const Babl *format; + GeglBuffer *buffer; + GeglRectangle bounds; + gint x; + gint y; +}; + +struct _GimpMaskUndoClass +{ + GimpItemUndoClass parent_class; +}; + + +GType gimp_mask_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_MASK_UNDO_H__ */ diff --git a/app/core/gimpmybrush-load.c b/app/core/gimpmybrush-load.c new file mode 100644 index 0000000..1dbb9f5 --- /dev/null +++ b/app/core/gimpmybrush-load.c @@ -0,0 +1,153 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmybrush-load.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 . + */ + +#include "config.h" + +#include +#include + +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpmybrush.h" +#include "gimpmybrush-load.h" +#include "gimpmybrush-private.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GList * +gimp_mybrush_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpMybrush *brush = NULL; + MyPaintBrush *mypaint_brush; + GdkPixbuf *pixbuf; + GFileInfo *info; + guint64 size; + guchar *buffer; + gchar *path; + gchar *basename; + gchar *preview_filename; + gchar *p; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (! info) + return NULL; + + size = g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_STANDARD_SIZE); + g_object_unref (info); + + if (size > 32768) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("MyPaint brush file is unreasonably large, skipping.")); + return NULL; + } + + buffer = g_new0 (guchar, size + 1); + + if (! g_input_stream_read_all (input, buffer, size, NULL, NULL, error)) + { + g_free (buffer); + return NULL; + } + + mypaint_brush = mypaint_brush_new (); + mypaint_brush_from_defaults (mypaint_brush); + + if (! mypaint_brush_from_string (mypaint_brush, (const gchar *) buffer)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Failed to deserialize MyPaint brush.")); + mypaint_brush_unref (mypaint_brush); + g_free (buffer); + return NULL; + } + + path = g_file_get_path (file); + basename = g_strndup (path, strlen (path) - 4); + g_free (path); + + preview_filename = g_strconcat (basename, "_prev.png", NULL); + g_free (basename); + + pixbuf = gdk_pixbuf_new_from_file_at_size (preview_filename, + 48, 48, error); + g_free (preview_filename); + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + + basename[strlen (basename) - 4] = '\0'; + for (p = basename; *p; p++) + if (*p == '_' || *p == '-') + *p = ' '; + + brush = g_object_new (GIMP_TYPE_MYBRUSH, + "name", basename, + "mime-type", "image/x-gimp-myb", + "icon-pixbuf", pixbuf, + NULL); + + g_free (basename); + + if (pixbuf) + g_object_unref (pixbuf); + + brush->priv->brush_json = (gchar *) buffer; + + brush->priv->radius = + mypaint_brush_get_base_value (mypaint_brush, + MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC); + + brush->priv->opaque = + mypaint_brush_get_base_value (mypaint_brush, + MYPAINT_BRUSH_SETTING_OPAQUE); + + brush->priv->hardness = + mypaint_brush_get_base_value (mypaint_brush, + MYPAINT_BRUSH_SETTING_HARDNESS); + + brush->priv->eraser = + mypaint_brush_get_base_value (mypaint_brush, + MYPAINT_BRUSH_SETTING_ERASER) > 0.5f; + + brush->priv->offset_by_random = + mypaint_brush_get_base_value (mypaint_brush, + MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM); + + mypaint_brush_unref (mypaint_brush); + + return g_list_prepend (NULL, brush); +} diff --git a/app/core/gimpmybrush-load.h b/app/core/gimpmybrush-load.h new file mode 100644 index 0000000..4afe203 --- /dev/null +++ b/app/core/gimpmybrush-load.h @@ -0,0 +1,33 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmybrush-load.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 . + */ + +#ifndef __GIMP_MYBRUSH_LOAD_H__ +#define __GIMP_MYBRUSH_LOAD_H__ + + +#define GIMP_MYBRUSH_FILE_EXTENSION ".myb" + + +GList * gimp_mybrush_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_MYBRUSH_LOAD_H__ */ diff --git a/app/core/gimpmybrush-private.h b/app/core/gimpmybrush-private.h new file mode 100644 index 0000000..7966c0b --- /dev/null +++ b/app/core/gimpmybrush-private.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmybrush-private.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 . + */ + +#ifndef __GIMP_MYBRUSH_PRIVATE_H__ +#define __GIMP_MYBRUSH_PRIVATE_H__ + + +struct _GimpMybrushPrivate +{ + gchar *brush_json; + gdouble radius; + gdouble opaque; + gdouble hardness; + gdouble offset_by_random; + gboolean eraser; +}; + + +#endif /* __GIMP_MYBRUSH_PRIVATE_H__ */ diff --git a/app/core/gimpmybrush.c b/app/core/gimpmybrush.c new file mode 100644 index 0000000..a26c86b --- /dev/null +++ b/app/core/gimpmybrush.c @@ -0,0 +1,281 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmybrush.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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpmybrush.h" +#include "gimpmybrush-load.h" +#include "gimpmybrush-private.h" +#include "gimptagged.h" + +#include "gimp-intl.h" + + +static void gimp_mybrush_tagged_iface_init (GimpTaggedInterface *iface); + +static void gimp_mybrush_finalize (GObject *object); +static void gimp_mybrush_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mybrush_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_mybrush_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gchar * gimp_mybrush_get_description (GimpViewable *viewable, + gchar **tooltip); + +static void gimp_mybrush_dirty (GimpData *data); +static const gchar * gimp_mybrush_get_extension (GimpData *data); + +static gchar * gimp_mybrush_get_checksum (GimpTagged *tagged); + + +G_DEFINE_TYPE_WITH_CODE (GimpMybrush, gimp_mybrush, GIMP_TYPE_DATA, + G_ADD_PRIVATE (GimpMybrush) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_mybrush_tagged_iface_init)) + +#define parent_class gimp_mybrush_parent_class + + +static void +gimp_mybrush_class_init (GimpMybrushClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->finalize = gimp_mybrush_finalize; + object_class->get_property = gimp_mybrush_get_property; + object_class->set_property = gimp_mybrush_set_property; + + gimp_object_class->get_memsize = gimp_mybrush_get_memsize; + + viewable_class->default_icon_name = "gimp-tool-mypaint-brush"; + viewable_class->get_description = gimp_mybrush_get_description; + + data_class->dirty = gimp_mybrush_dirty; + data_class->get_extension = gimp_mybrush_get_extension; +} + +static void +gimp_mybrush_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->get_checksum = gimp_mybrush_get_checksum; +} + +static void +gimp_mybrush_init (GimpMybrush *brush) +{ + brush->priv = gimp_mybrush_get_instance_private (brush); + + brush->priv->radius = 1.0; + brush->priv->opaque = 1.0; + brush->priv->hardness = 1.0; + brush->priv->eraser = FALSE; +} + +static void +gimp_mybrush_finalize (GObject *object) +{ + GimpMybrush *brush = GIMP_MYBRUSH (object); + + g_clear_pointer (&brush->priv->brush_json, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_mybrush_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mybrush_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_mybrush_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpMybrush *brush = GIMP_MYBRUSH (object); + gint64 memsize = 0; + + memsize += gimp_string_get_memsize (brush->priv->brush_json); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gchar * +gimp_mybrush_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpMybrush *brush = GIMP_MYBRUSH (viewable); + + return g_strdup_printf ("%s", + gimp_object_get_name (brush)); +} + +static void +gimp_mybrush_dirty (GimpData *data) +{ + GIMP_DATA_CLASS (parent_class)->dirty (data); +} + +static const gchar * +gimp_mybrush_get_extension (GimpData *data) +{ + return GIMP_MYBRUSH_FILE_EXTENSION; +} + +static gchar * +gimp_mybrush_get_checksum (GimpTagged *tagged) +{ + GimpMybrush *brush = GIMP_MYBRUSH (tagged); + gchar *checksum_string = NULL; + + if (brush->priv->brush_json) + { + GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5); + + g_checksum_update (checksum, + (const guchar *) brush->priv->brush_json, + strlen (brush->priv->brush_json)); + + checksum_string = g_strdup (g_checksum_get_string (checksum)); + + g_checksum_free (checksum); + } + + return checksum_string; +} + +/* public functions */ + +GimpData * +gimp_mybrush_new (GimpContext *context, + const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (GIMP_TYPE_MYBRUSH, + "name", name, + "mime-type", "image/x-gimp-myb", + NULL); +} + +GimpData * +gimp_mybrush_get_standard (GimpContext *context) +{ + static GimpData *standard_mybrush = NULL; + + if (! standard_mybrush) + { + standard_mybrush = gimp_mybrush_new (context, "Standard"); + + gimp_data_clean (standard_mybrush); + gimp_data_make_internal (standard_mybrush, "gimp-mybrush-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_mybrush), + (gpointer *) &standard_mybrush); + } + + return standard_mybrush; +} + +const gchar * +gimp_mybrush_get_brush_json (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), NULL); + + return brush->priv->brush_json; +} + +gdouble +gimp_mybrush_get_radius (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0); + + return brush->priv->radius; +} + +gdouble +gimp_mybrush_get_opaque (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0); + + return brush->priv->opaque; +} + +gdouble +gimp_mybrush_get_hardness (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0); + + return brush->priv->hardness; +} + +gdouble +gimp_mybrush_get_offset_by_random (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), 1.0); + + return brush->priv->offset_by_random; +} + +gboolean +gimp_mybrush_get_is_eraser (GimpMybrush *brush) +{ + g_return_val_if_fail (GIMP_IS_MYBRUSH (brush), FALSE); + + return brush->priv->eraser; +} diff --git a/app/core/gimpmybrush.h b/app/core/gimpmybrush.h new file mode 100644 index 0000000..2dad734 --- /dev/null +++ b/app/core/gimpmybrush.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmybrush.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 . + */ + +#ifndef __GIMP_MYBRUSH_H__ +#define __GIMP_MYBRUSH_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_MYBRUSH (gimp_mybrush_get_type ()) +#define GIMP_MYBRUSH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MYBRUSH, GimpMybrush)) +#define GIMP_MYBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MYBRUSH, GimpMybrushClass)) +#define GIMP_IS_MYBRUSH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MYBRUSH)) +#define GIMP_IS_MYBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MYBRUSH)) +#define GIMP_MYBRUSH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MYBRUSH, GimpMybrushClass)) + + +typedef struct _GimpMybrushPrivate GimpMybrushPrivate; +typedef struct _GimpMybrushClass GimpMybrushClass; + +struct _GimpMybrush +{ + GimpData parent_instance; + + GimpMybrushPrivate *priv; +}; + +struct _GimpMybrushClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_mybrush_get_type (void) G_GNUC_CONST; + +GimpData * gimp_mybrush_new (GimpContext *context, + const gchar *name); +GimpData * gimp_mybrush_get_standard (GimpContext *context); + +const gchar * gimp_mybrush_get_brush_json (GimpMybrush *brush); + +gdouble gimp_mybrush_get_radius (GimpMybrush *brush); +gdouble gimp_mybrush_get_opaque (GimpMybrush *brush); +gdouble gimp_mybrush_get_hardness (GimpMybrush *brush); +gdouble gimp_mybrush_get_offset_by_random (GimpMybrush *brush); +gboolean gimp_mybrush_get_is_eraser (GimpMybrush *brush); + + +#endif /* __GIMP_MYBRUSH_H__ */ diff --git a/app/core/gimpobject.c b/app/core/gimpobject.c new file mode 100644 index 0000000..0f3baf7 --- /dev/null +++ b/app/core/gimpobject.c @@ -0,0 +1,512 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpmarshal.h" +#include "gimpobject.h" + +#include "gimp-debug.h" + + +enum +{ + DISCONNECT, + NAME_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_NAME +}; + + +struct _GimpObjectPrivate +{ + gchar *name; + gchar *normalized; + guint static_name : 1; + guint disconnected : 1; +}; + + +static void gimp_object_constructed (GObject *object); +static void gimp_object_dispose (GObject *object); +static void gimp_object_finalize (GObject *object); +static void gimp_object_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_object_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static gint64 gimp_object_real_get_memsize (GimpObject *object, + gint64 *gui_size); +static void gimp_object_name_normalize (GimpObject *object); + + +G_DEFINE_TYPE_WITH_CODE (GimpObject, gimp_object, G_TYPE_OBJECT, + G_ADD_PRIVATE (GimpObject)) + +#define parent_class gimp_object_parent_class + +static guint object_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_object_class_init (GimpObjectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_signals[DISCONNECT] = + g_signal_new ("disconnect", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpObjectClass, disconnect), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_signals[NAME_CHANGED] = + g_signal_new ("name-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpObjectClass, name_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->constructed = gimp_object_constructed; + object_class->dispose = gimp_object_dispose; + object_class->finalize = gimp_object_finalize; + object_class->set_property = gimp_object_set_property; + object_class->get_property = gimp_object_get_property; + + klass->disconnect = NULL; + klass->name_changed = NULL; + klass->get_memsize = gimp_object_real_get_memsize; + + g_object_class_install_property (object_class, PROP_NAME, + g_param_spec_string ("name", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_object_init (GimpObject *object) +{ + object->p = gimp_object_get_instance_private (object); + + object->p->name = NULL; + object->p->normalized = NULL; +} + +static void +gimp_object_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_debug_add_instance (object, G_OBJECT_GET_CLASS (object)); +} + +static void +gimp_object_dispose (GObject *object) +{ + GimpObject *gimp_object = GIMP_OBJECT (object); + + if (! gimp_object->p->disconnected) + { + g_signal_emit (object, object_signals[DISCONNECT], 0); + + gimp_object->p->disconnected = TRUE; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_object_finalize (GObject *object) +{ + gimp_object_name_free (GIMP_OBJECT (object)); + + gimp_debug_remove_instance (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_object_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpObject *gimp_object = GIMP_OBJECT (object); + + switch (property_id) + { + case PROP_NAME: + gimp_object_set_name (gimp_object, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_object_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpObject *gimp_object = GIMP_OBJECT (object); + + switch (property_id) + { + case PROP_NAME: + if (gimp_object->p->static_name) + g_value_set_static_string (value, gimp_object->p->name); + else + g_value_set_string (value, gimp_object->p->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gimp_object_set_name: + * @object: a #GimpObject + * @name: the @object's new name + * + * Sets the @object's name. Takes care of freeing the old name and + * emitting the ::name_changed signal if the old and new name differ. + **/ +void +gimp_object_set_name (GimpObject *object, + const gchar *name) +{ + g_return_if_fail (GIMP_IS_OBJECT (object)); + + if (! g_strcmp0 (object->p->name, name)) + return; + + gimp_object_name_free (object); + + object->p->name = g_strdup (name); + object->p->static_name = FALSE; + + gimp_object_name_changed (object); + g_object_notify (G_OBJECT (object), "name"); +} + +/** + * gimp_object_set_name_safe: + * @object: a #GimpObject + * @name: the @object's new name + * + * A safe version of gimp_object_set_name() that takes care of + * handling newlines and overly long names. The actual name set + * may be different to the @name you pass. + **/ +void +gimp_object_set_name_safe (GimpObject *object, + const gchar *name) +{ + g_return_if_fail (GIMP_IS_OBJECT (object)); + + if (! g_strcmp0 (object->p->name, name)) + return; + + gimp_object_name_free (object); + + object->p->name = gimp_utf8_strtrim (name, 30); + object->p->static_name = FALSE; + + gimp_object_name_changed (object); + g_object_notify (G_OBJECT (object), "name"); +} + +void +gimp_object_set_static_name (GimpObject *object, + const gchar *name) +{ + g_return_if_fail (GIMP_IS_OBJECT (object)); + + if (! g_strcmp0 (object->p->name, name)) + return; + + gimp_object_name_free (object); + + object->p->name = (gchar *) name; + object->p->static_name = TRUE; + + gimp_object_name_changed (object); + g_object_notify (G_OBJECT (object), "name"); +} + +void +gimp_object_take_name (GimpObject *object, + gchar *name) +{ + g_return_if_fail (GIMP_IS_OBJECT (object)); + + if (! g_strcmp0 (object->p->name, name)) + { + g_free (name); + return; + } + + gimp_object_name_free (object); + + object->p->name = name; + object->p->static_name = FALSE; + + gimp_object_name_changed (object); + g_object_notify (G_OBJECT (object), "name"); +} + +/** + * gimp_object_get_name: + * @object: a #GimpObject + * + * This function gives access to the name of a GimpObject. The + * returned name belongs to the object and must not be freed. + * + * Return value: a pointer to the @object's name + **/ +const gchar * +gimp_object_get_name (gconstpointer object) +{ + const GimpObject *object_typed = object; + g_return_val_if_fail (GIMP_IS_OBJECT (object_typed), NULL); + + return object_typed->p->name; +} + +/** + * gimp_object_name_changed: + * @object: a #GimpObject + * + * Causes the ::name-changed signal to be emitted. + **/ +void +gimp_object_name_changed (GimpObject *object) +{ + g_return_if_fail (GIMP_IS_OBJECT (object)); + + g_signal_emit (object, object_signals[NAME_CHANGED], 0); +} + +/** + * gimp_object_name_free: + * @object: a #GimpObject + * + * Frees the name of @object and sets the name pointer to %NULL. Also + * takes care of the normalized name that the object might be caching. + * + * In general you should be using gimp_object_set_name() instead. But + * if you ever need to free the object name but don't want the + * ::name-changed signal to be emitted, then use this function. Never + * ever free the object name directly! + **/ +void +gimp_object_name_free (GimpObject *object) +{ + if (object->p->normalized) + { + if (object->p->normalized != object->p->name) + g_free (object->p->normalized); + + object->p->normalized = NULL; + } + + if (object->p->name) + { + if (! object->p->static_name) + g_free (object->p->name); + + object->p->name = NULL; + object->p->static_name = FALSE; + } +} + +/** + * gimp_object_name_collate: + * @object1: a #GimpObject + * @object2: another #GimpObject + * + * Compares two object names for ordering using the linguistically + * correct rules for the current locale. It caches the normalized + * version of the object name to speed up subsequent calls. + * + * Return value: -1 if object1 compares before object2, + * 0 if they compare equal, + * 1 if object1 compares after object2. + **/ +gint +gimp_object_name_collate (GimpObject *object1, + GimpObject *object2) +{ + if (! object1->p->normalized) + gimp_object_name_normalize (object1); + + if (! object2->p->normalized) + gimp_object_name_normalize (object2); + + return strcmp (object1->p->normalized, object2->p->normalized); +} + +static void +gimp_object_name_normalize (GimpObject *object) +{ + g_return_if_fail (object->p->normalized == NULL); + + if (object->p->name) + { + gchar *key = g_utf8_collate_key (object->p->name, -1); + + if (strcmp (key, object->p->name)) + { + object->p->normalized = key; + } + else + { + g_free (key); + object->p->normalized = object->p->name; + } + } +} + + +#define DEBUG_MEMSIZE 1 + +#ifdef DEBUG_MEMSIZE +gboolean gimp_debug_memsize = FALSE; +#endif + +gint64 +gimp_object_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + gint64 my_size = 0; + gint64 my_gui_size = 0; + + g_return_val_if_fail (object == NULL || GIMP_IS_OBJECT (object), 0); + + if (! object) + { + if (gui_size) + *gui_size = 0; + + return 0; + } + +#ifdef DEBUG_MEMSIZE + if (gimp_debug_memsize) + { + static gint indent_level = 0; + static GList *aggregation_tree = NULL; + static gchar indent_buf[256]; + + gint64 memsize; + gint64 gui_memsize = 0; + gint i; + gint my_indent_level; + gchar *object_size; + + indent_level++; + + my_indent_level = indent_level; + + memsize = GIMP_OBJECT_GET_CLASS (object)->get_memsize (object, + &gui_memsize); + + indent_level--; + + for (i = 0; i < MIN (my_indent_level * 2, sizeof (indent_buf) - 1); i++) + indent_buf[i] = ' '; + + indent_buf[i] = '\0'; + + object_size = g_strdup_printf ("%s%s \"%s\": " + "%" G_GINT64_FORMAT + "(%" G_GINT64_FORMAT ")\n", + indent_buf, + g_type_name (G_TYPE_FROM_INSTANCE (object)), + object->p->name ? object->p->name : "anonymous", + memsize, + gui_memsize); + + aggregation_tree = g_list_prepend (aggregation_tree, object_size); + + if (indent_level == 0) + { + GList *list; + + for (list = aggregation_tree; list; list = g_list_next (list)) + { + g_print ("%s", (gchar *) list->data); + g_free (list->data); + } + + g_list_free (aggregation_tree); + aggregation_tree = NULL; + } + + return memsize; + } +#endif /* DEBUG_MEMSIZE */ + + my_size = GIMP_OBJECT_GET_CLASS (object)->get_memsize (object, + &my_gui_size); + + if (gui_size) + *gui_size = my_gui_size; + + return my_size; +} + +static gint64 +gimp_object_real_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + gint64 memsize = 0; + + if (! object->p->static_name) + memsize += gimp_string_get_memsize (object->p->name); + + return memsize + gimp_g_object_get_memsize ((GObject *) object); +} diff --git a/app/core/gimpobject.h b/app/core/gimpobject.h new file mode 100644 index 0000000..17401c5 --- /dev/null +++ b/app/core/gimpobject.h @@ -0,0 +1,74 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __GIMP_OBJECT_H__ +#define __GIMP_OBJECT_H__ + + +#define GIMP_TYPE_OBJECT (gimp_object_get_type ()) +#define GIMP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OBJECT, GimpObject)) +#define GIMP_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OBJECT, GimpObjectClass)) +#define GIMP_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OBJECT)) +#define GIMP_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OBJECT)) +#define GIMP_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OBJECT, GimpObjectClass)) + + +typedef struct _GimpObjectPrivate GimpObjectPrivate; +typedef struct _GimpObjectClass GimpObjectClass; + +struct _GimpObject +{ + GObject parent_instance; + + GimpObjectPrivate *p; +}; + +struct _GimpObjectClass +{ + GObjectClass parent_class; + + /* signals */ + void (* disconnect) (GimpObject *object); + void (* name_changed) (GimpObject *object); + + /* virtual functions */ + gint64 (* get_memsize) (GimpObject *object, + gint64 *gui_size); +}; + + +GType gimp_object_get_type (void) G_GNUC_CONST; + +void gimp_object_set_name (GimpObject *object, + const gchar *name); +void gimp_object_set_name_safe (GimpObject *object, + const gchar *name); +void gimp_object_set_static_name (GimpObject *object, + const gchar *name); +void gimp_object_take_name (GimpObject *object, + gchar *name); +const gchar * gimp_object_get_name (gconstpointer object); +void gimp_object_name_changed (GimpObject *object); +void gimp_object_name_free (GimpObject *object); + +gint gimp_object_name_collate (GimpObject *object1, + GimpObject *object2); +gint64 gimp_object_get_memsize (GimpObject *object, + gint64 *gui_size); + + +#endif /* __GIMP_OBJECT_H__ */ diff --git a/app/core/gimpobjectqueue.c b/app/core/gimpobjectqueue.c new file mode 100644 index 0000000..edadc09 --- /dev/null +++ b/app/core/gimpobjectqueue.c @@ -0,0 +1,198 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpcontainer.h" +#include "gimpobject.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + + +typedef struct +{ + GimpObject *object; + gint64 memsize; +} Item; + + +static void gimp_object_queue_dispose (GObject *object); + +static void gimp_object_queue_push_swapped (gpointer object, + GimpObjectQueue *queue); + +static Item * gimp_object_queue_item_new (GimpObject *object); +static void gimp_object_queue_item_free (Item *item); + + +G_DEFINE_TYPE (GimpObjectQueue, gimp_object_queue, GIMP_TYPE_SUB_PROGRESS); + +#define parent_class gimp_object_queue_parent_class + + +/* private functions */ + + +static void +gimp_object_queue_class_init (GimpObjectQueueClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_object_queue_dispose; +} + +static void +gimp_object_queue_init (GimpObjectQueue *queue) +{ + g_queue_init (&queue->items); + + queue->processed_memsize = 0; + queue->total_memsize = 0; +} + +static void +gimp_object_queue_dispose (GObject *object) +{ + gimp_object_queue_clear (GIMP_OBJECT_QUEUE (object)); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_object_queue_push_swapped (gpointer object, + GimpObjectQueue *queue) +{ + gimp_object_queue_push (queue, object); +} + +static Item * +gimp_object_queue_item_new (GimpObject *object) +{ + Item *item = g_slice_new (Item); + + item->object = object; + item->memsize = gimp_object_get_memsize (object, NULL); + + return item; +} + +static void +gimp_object_queue_item_free (Item *item) +{ + g_slice_free (Item, item); +} + + +/* public functions */ + + +GimpObjectQueue * +gimp_object_queue_new (GimpProgress *progress) +{ + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + return g_object_new (GIMP_TYPE_OBJECT_QUEUE, + "progress", progress, + NULL); +} + +void +gimp_object_queue_clear (GimpObjectQueue *queue) +{ + Item *item; + + g_return_if_fail (GIMP_IS_OBJECT_QUEUE (queue)); + + while ((item = g_queue_pop_head (&queue->items))) + gimp_object_queue_item_free (item); + + queue->processed_memsize = 0; + queue->total_memsize = 0; + + gimp_sub_progress_set_range (GIMP_SUB_PROGRESS (queue), 0.0, 1.0); +} + +void +gimp_object_queue_push (GimpObjectQueue *queue, + gpointer object) +{ + Item *item; + + g_return_if_fail (GIMP_IS_OBJECT_QUEUE (queue)); + g_return_if_fail (GIMP_IS_OBJECT (object)); + + item = gimp_object_queue_item_new (GIMP_OBJECT (object)); + + g_queue_push_tail (&queue->items, item); + + queue->total_memsize += item->memsize; +} + +void +gimp_object_queue_push_container (GimpObjectQueue *queue, + GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_OBJECT_QUEUE (queue)); + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + gimp_container_foreach (container, + (GFunc) gimp_object_queue_push_swapped, + queue); +} + +void +gimp_object_queue_push_list (GimpObjectQueue *queue, + GList *list) +{ + g_return_if_fail (GIMP_IS_OBJECT_QUEUE (queue)); + + g_list_foreach (list, + (GFunc) gimp_object_queue_push_swapped, + queue); +} + +gpointer +gimp_object_queue_pop (GimpObjectQueue *queue) +{ + Item *item; + GimpObject *object; + + g_return_val_if_fail (GIMP_IS_OBJECT_QUEUE (queue), NULL); + + item = g_queue_pop_head (&queue->items); + + if (! item) + return NULL; + + object = item->object; + + gimp_sub_progress_set_range (GIMP_SUB_PROGRESS (queue), + (gdouble) queue->processed_memsize / + (gdouble) queue->total_memsize, + (gdouble) (queue->processed_memsize + + item->memsize) / + (gdouble) queue->total_memsize); + queue->processed_memsize += item->memsize; + + gimp_object_queue_item_free (item); + + return object; +} diff --git a/app/core/gimpobjectqueue.h b/app/core/gimpobjectqueue.h new file mode 100644 index 0000000..024754f --- /dev/null +++ b/app/core/gimpobjectqueue.h @@ -0,0 +1,66 @@ +/* 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 . + */ + +#ifndef __GIMP_OBJECT_QUEUE_H__ +#define __GIMP_OBJECT_QUEUE_H__ + + +#include "gimpsubprogress.h" + + +#define GIMP_TYPE_OBJECT_QUEUE (gimp_object_queue_get_type ()) +#define GIMP_OBJECT_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OBJECT_QUEUE, GimpObjectQueue)) +#define GIMP_OBJECT_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OBJECT_QUEUE, GimpObjectQueueClass)) +#define GIMP_IS_OBJECT_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OBJECT_QUEUE)) +#define GIMP_IS_OBJECT_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OBJECT_QUEUE)) +#define GIMP_OBJECT_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OBJECT_QUEUE, GimpObjectQueueClass)) + + +typedef struct _GimpObjectQueueClass GimpObjectQueueClass; + +struct _GimpObjectQueue +{ + GimpSubProgress parent_instance; + + GQueue items; + gint64 processed_memsize; + gint64 total_memsize; +}; + +struct _GimpObjectQueueClass +{ + GimpSubProgressClass parent_class; +}; + + +GType gimp_object_queue_get_type (void) G_GNUC_CONST; + +GimpObjectQueue * gimp_object_queue_new (GimpProgress *progress); + +void gimp_object_queue_clear (GimpObjectQueue *queue); + +void gimp_object_queue_push (GimpObjectQueue *queue, + gpointer object); +void gimp_object_queue_push_container (GimpObjectQueue *queue, + GimpContainer *container); +void gimp_object_queue_push_list (GimpObjectQueue *queue, + GList *list); + +gpointer gimp_object_queue_pop (GimpObjectQueue *queue); + + +#endif /* __GIMP_OBJECT_QUEUE_H__ */ diff --git a/app/core/gimppaintinfo.c b/app/core/gimppaintinfo.c new file mode 100644 index 0000000..c7d6d81 --- /dev/null +++ b/app/core/gimppaintinfo.c @@ -0,0 +1,142 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "paint/gimppaintoptions.h" + +#include "gimp.h" +#include "gimppaintinfo.h" + + +static void gimp_paint_info_dispose (GObject *object); +static void gimp_paint_info_finalize (GObject *object); +static gchar * gimp_paint_info_get_description (GimpViewable *viewable, + gchar **tooltip); + + +G_DEFINE_TYPE (GimpPaintInfo, gimp_paint_info, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_paint_info_parent_class + + +static void +gimp_paint_info_class_init (GimpPaintInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->dispose = gimp_paint_info_dispose; + object_class->finalize = gimp_paint_info_finalize; + + viewable_class->get_description = gimp_paint_info_get_description; +} + +static void +gimp_paint_info_init (GimpPaintInfo *paint_info) +{ + paint_info->gimp = NULL; + paint_info->paint_type = G_TYPE_NONE; + paint_info->blurb = NULL; + paint_info->paint_options = NULL; +} + +static void +gimp_paint_info_dispose (GObject *object) +{ + GimpPaintInfo *paint_info = GIMP_PAINT_INFO (object); + + if (paint_info->paint_options) + { + g_object_run_dispose (G_OBJECT (paint_info->paint_options)); + g_clear_object (&paint_info->paint_options); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_paint_info_finalize (GObject *object) +{ + GimpPaintInfo *paint_info = GIMP_PAINT_INFO (object); + + g_clear_pointer (&paint_info->blurb, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gchar * +gimp_paint_info_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpPaintInfo *paint_info = GIMP_PAINT_INFO (viewable); + + return g_strdup (paint_info->blurb); +} + +GimpPaintInfo * +gimp_paint_info_new (Gimp *gimp, + GType paint_type, + GType paint_options_type, + const gchar *identifier, + const gchar *blurb, + const gchar *icon_name) +{ + GimpPaintInfo *paint_info; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (identifier != NULL, NULL); + g_return_val_if_fail (blurb != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + + paint_info = g_object_new (GIMP_TYPE_PAINT_INFO, + "name", identifier, + "icon-name", icon_name, + NULL); + + paint_info->gimp = gimp; + paint_info->paint_type = paint_type; + paint_info->paint_options_type = paint_options_type; + paint_info->blurb = g_strdup (blurb); + + paint_info->paint_options = gimp_paint_options_new (paint_info); + + return paint_info; +} + +void +gimp_paint_info_set_standard (Gimp *gimp, + GimpPaintInfo *paint_info) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (! paint_info || GIMP_IS_PAINT_INFO (paint_info)); + + g_set_object (&gimp->standard_paint_info, paint_info); +} + +GimpPaintInfo * +gimp_paint_info_get_standard (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->standard_paint_info; +} diff --git a/app/core/gimppaintinfo.h b/app/core/gimppaintinfo.h new file mode 100644 index 0000000..c67b326 --- /dev/null +++ b/app/core/gimppaintinfo.h @@ -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 . + */ + +#ifndef __GIMP_PAINT_INFO_H__ +#define __GIMP_PAINT_INFO_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_PAINT_INFO (gimp_paint_info_get_type ()) +#define GIMP_PAINT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PAINT_INFO, GimpPaintInfo)) +#define GIMP_PAINT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PAINT_INFO, GimpPaintInfoClass)) +#define GIMP_IS_PAINT_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PAINT_INFO)) +#define GIMP_IS_PAINT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PAINT_INFO)) +#define GIMP_PAINT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PAINT_INFO, GimpPaintInfoClass)) + + +typedef struct _GimpPaintInfoClass GimpPaintInfoClass; + +struct _GimpPaintInfo +{ + GimpViewable parent_instance; + + Gimp *gimp; + + GType paint_type; + GType paint_options_type; + + gchar *blurb; + + GimpPaintOptions *paint_options; +}; + +struct _GimpPaintInfoClass +{ + GimpViewableClass parent_class; +}; + + +GType gimp_paint_info_get_type (void) G_GNUC_CONST; + +GimpPaintInfo * gimp_paint_info_new (Gimp *gimp, + GType paint_type, + GType paint_options_type, + const gchar *identifier, + const gchar *blurb, + const gchar *icon_name); + +void gimp_paint_info_set_standard (Gimp *gimp, + GimpPaintInfo *paint_info); +GimpPaintInfo * gimp_paint_info_get_standard (Gimp *gimp); + + +#endif /* __GIMP_PAINT_INFO_H__ */ diff --git a/app/core/gimppalette-import.c b/app/core/gimppalette-import.c new file mode 100644 index 0000000..44a91e5 --- /dev/null +++ b/app/core/gimppalette-import.c @@ -0,0 +1,574 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimpchannel.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpgradient.h" +#include "gimpimage.h" +#include "gimpimage-colormap.h" +#include "gimppalette.h" +#include "gimppalette-import.h" +#include "gimppalette-load.h" +#include "gimppickable.h" + +#include "gimp-intl.h" + + +#define MAX_IMAGE_COLORS (10000 * 2) + + +/* create a palette from a gradient ****************************************/ + +GimpPalette * +gimp_palette_import_from_gradient (GimpGradient *gradient, + GimpContext *context, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + const gchar *palette_name, + gint n_colors) +{ + GimpPalette *palette; + GimpGradientSegment *seg = NULL; + gdouble dx, cur_x; + GimpRGB color; + gint i; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + dx = 1.0 / (n_colors - 1); + + for (i = 0, cur_x = 0; i < n_colors; i++, cur_x += dx) + { + seg = gimp_gradient_get_color_at (gradient, context, + seg, cur_x, reverse, blend_color_space, + &color); + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + return palette; +} + + +/* create a palette from a non-indexed image *******************************/ + +typedef struct _ImgColors ImgColors; + +struct _ImgColors +{ + guint count; + guint r_adj; + guint g_adj; + guint b_adj; + guchar r; + guchar g; + guchar b; +}; + +static gint count_color_entries = 0; + +static GHashTable * +gimp_palette_import_store_colors (GHashTable *table, + guchar *colors, + guchar *colors_real, + gint n_colors) +{ + gpointer found_color = NULL; + ImgColors *new_color; + guint key_colors = colors[0] * 256 * 256 + colors[1] * 256 + colors[2]; + + if (table == NULL) + { + table = g_hash_table_new (g_direct_hash, g_direct_equal); + count_color_entries = 0; + } + else + { + found_color = g_hash_table_lookup (table, GUINT_TO_POINTER (key_colors)); + } + + if (found_color == NULL) + { + if (count_color_entries > MAX_IMAGE_COLORS) + { + /* Don't add any more new ones */ + return table; + } + + count_color_entries++; + + new_color = g_slice_new (ImgColors); + + new_color->count = 1; + new_color->r_adj = 0; + new_color->g_adj = 0; + new_color->b_adj = 0; + new_color->r = colors[0]; + new_color->g = colors[1]; + new_color->b = colors[2]; + + g_hash_table_insert (table, GUINT_TO_POINTER (key_colors), new_color); + } + else + { + new_color = found_color; + + if (new_color->count < (G_MAXINT - 1)) + new_color->count++; + + /* Now do the adjustments ...*/ + new_color->r_adj += (colors_real[0] - colors[0]); + new_color->g_adj += (colors_real[1] - colors[1]); + new_color->b_adj += (colors_real[2] - colors[2]); + + /* Boundary conditions */ + if(new_color->r_adj > (G_MAXINT - 255)) + new_color->r_adj /= new_color->count; + + if(new_color->g_adj > (G_MAXINT - 255)) + new_color->g_adj /= new_color->count; + + if(new_color->b_adj > (G_MAXINT - 255)) + new_color->b_adj /= new_color->count; + } + + return table; +} + +static void +gimp_palette_import_create_list (gpointer key, + gpointer value, + gpointer user_data) +{ + GSList **list = user_data; + ImgColors *color_tab = value; + + *list = g_slist_prepend (*list, color_tab); +} + +static gint +gimp_palette_import_sort_colors (gconstpointer a, + gconstpointer b) +{ + const ImgColors *s1 = a; + const ImgColors *s2 = b; + + if(s1->count > s2->count) + return -1; + if(s1->count < s2->count) + return 1; + + return 0; +} + +static void +gimp_palette_import_create_image_palette (gpointer data, + gpointer user_data) +{ + GimpPalette *palette = user_data; + ImgColors *color_tab = data; + gint n_colors; + gchar *lab; + GimpRGB color; + + n_colors = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (palette), + "import-n-colors")); + + if (gimp_palette_get_n_colors (palette) >= n_colors) + return; + + /* TRANSLATORS: the "%s" is an item title and "%u" is the number of + occurrences for this item. */ + lab = g_strdup_printf (_("%s (occurs %u)"), + _("Untitled"), + color_tab->count); + + /* Adjust the colors to the mean of the the sample */ + gimp_rgba_set_uchar + (&color, + (guchar) color_tab->r + (color_tab->r_adj / color_tab->count), + (guchar) color_tab->g + (color_tab->g_adj / color_tab->count), + (guchar) color_tab->b + (color_tab->b_adj / color_tab->count), + 255); + + gimp_palette_add_entry (palette, -1, lab, &color); + + g_free (lab); +} + +static GimpPalette * +gimp_palette_import_make_palette (GHashTable *table, + const gchar *palette_name, + GimpContext *context, + gint n_colors) +{ + GimpPalette *palette; + GSList *list = NULL; + GSList *iter; + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + if (! table) + return palette; + + g_hash_table_foreach (table, gimp_palette_import_create_list, &list); + list = g_slist_sort (list, gimp_palette_import_sort_colors); + + g_object_set_data (G_OBJECT (palette), "import-n-colors", + GINT_TO_POINTER (n_colors)); + + g_slist_foreach (list, gimp_palette_import_create_image_palette, palette); + + g_object_set_data (G_OBJECT (palette), "import-n-colors", NULL); + + /* Free up used memory + * Note the same structure is on both the hash list and the sorted + * list. So only delete it once. + */ + g_hash_table_destroy (table); + + for (iter = list; iter; iter = iter->next) + g_slice_free (ImgColors, iter->data); + + g_slist_free (list); + + return palette; +} + +static GHashTable * +gimp_palette_import_extract (GimpImage *image, + GimpPickable *pickable, + gint pickable_off_x, + gint pickable_off_y, + gboolean selection_only, + gint x, + gint y, + gint width, + gint height, + gint n_colors, + gint threshold) +{ + GeglBuffer *buffer; + GeglBufferIterator *iter; + GeglRectangle *mask_roi = NULL; + GeglRectangle rect = { x, y, width, height }; + GHashTable *colors = NULL; + const Babl *format; + gint bpp; + gint mask_bpp = 0; + + buffer = gimp_pickable_get_buffer (pickable); + format = babl_format ("R'G'B'A u8"); + + iter = gegl_buffer_iterator_new (buffer, &rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + bpp = babl_format_get_bytes_per_pixel (format); + + if (selection_only && + ! gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpDrawable *mask = GIMP_DRAWABLE (gimp_image_get_mask (image)); + + rect.x = x + pickable_off_x; + rect.y = y + pickable_off_y; + + buffer = gimp_drawable_get_buffer (mask); + format = babl_format ("Y u8"); + + gegl_buffer_iterator_add (iter, buffer, &rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + mask_roi = &iter->items[1].roi; + mask_bpp = babl_format_get_bytes_per_pixel (format); + } + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *data = iter->items[0].data; + const guchar *mask_data = NULL; + gint length = iter->length; + + if (mask_roi) + mask_data = iter->items[1].data; + + while (length--) + { + /* ignore unselected, and completely transparent pixels */ + if ((! mask_data || *mask_data) && data[ALPHA]) + { + guchar rgba[MAX_CHANNELS] = { 0, }; + guchar rgb_real[MAX_CHANNELS] = { 0, }; + + memcpy (rgba, data, 4); + memcpy (rgb_real, rgba, 4); + + rgba[0] = (rgba[0] / threshold) * threshold; + rgba[1] = (rgba[1] / threshold) * threshold; + rgba[2] = (rgba[2] / threshold) * threshold; + + colors = gimp_palette_import_store_colors (colors, + rgba, rgb_real, + n_colors); + } + + data += bpp; + + if (mask_data) + mask_data += mask_bpp; + } + } + + return colors; +} + +GimpPalette * +gimp_palette_import_from_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only) +{ + GHashTable *colors; + gint x, y; + gint width, height; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + g_return_val_if_fail (threshold > 0, NULL); + + gimp_pickable_flush (GIMP_PICKABLE (image)); + + if (selection_only) + { + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height); + } + else + { + x = 0; + y = 0; + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + } + + colors = gimp_palette_import_extract (image, + GIMP_PICKABLE (image), + 0, 0, + selection_only, + x, y, width, height, + n_colors, threshold); + + return gimp_palette_import_make_palette (colors, palette_name, context, + n_colors); +} + + +/* create a palette from an indexed image **********************************/ + +GimpPalette * +gimp_palette_import_from_indexed_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name) +{ + GimpPalette *palette; + const guchar *colormap; + guint n_colors; + gint count; + GimpRGB color; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (gimp_image_get_base_type (image) == GIMP_INDEXED, NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + colormap = gimp_image_get_colormap (image); + n_colors = gimp_image_get_colormap_size (image); + + for (count = 0; count < n_colors; ++count) + { + gchar name[256]; + + g_snprintf (name, sizeof (name), _("Index %d"), count); + + gimp_rgba_set_uchar (&color, + colormap[count * 3 + 0], + colormap[count * 3 + 1], + colormap[count * 3 + 2], + 255); + + gimp_palette_add_entry (palette, -1, name, &color); + } + + return palette; +} + + +/* create a palette from a drawable ****************************************/ + +GimpPalette * +gimp_palette_import_from_drawable (GimpDrawable *drawable, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only) +{ + GHashTable *colors = NULL; + gint x, y; + gint width, height; + gint off_x, off_y; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + g_return_val_if_fail (threshold > 0, NULL); + + if (selection_only) + { + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &x, &y, &width, &height)) + return NULL; + } + else + { + x = 0; + y = 0; + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + } + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + colors = + gimp_palette_import_extract (gimp_item_get_image (GIMP_ITEM (drawable)), + GIMP_PICKABLE (drawable), + off_x, off_y, + selection_only, + x, y, width, height, + n_colors, threshold); + + return gimp_palette_import_make_palette (colors, palette_name, context, + n_colors); +} + + +/* create a palette from a file **********************************/ + +GimpPalette * +gimp_palette_import_from_file (GimpContext *context, + GFile *file, + const gchar *palette_name, + GError **error) +{ + GList *palette_list = NULL; + GInputStream *input; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + if (! input) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN, + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), my_error->message); + g_clear_error (&my_error); + return NULL; + } + + switch (gimp_palette_load_detect_format (file, input)) + { + case GIMP_PALETTE_FILE_FORMAT_GPL: + palette_list = gimp_palette_load (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ACT: + palette_list = gimp_palette_load_act (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_RIFF_PAL: + palette_list = gimp_palette_load_riff (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_PSP_PAL: + palette_list = gimp_palette_load_psp (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ACO: + palette_list = gimp_palette_load_aco (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ACB: + palette_list = gimp_palette_load_acb (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ASE: + palette_list = gimp_palette_load_ase (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_CSS: + palette_list = gimp_palette_load_css (context, file, input, error); + break; + + default: + g_set_error (error, + GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unknown type of palette file: %s"), + gimp_file_get_utf8_name (file)); + break; + } + + g_object_unref (input); + + if (palette_list) + { + GimpPalette *palette = g_object_ref (palette_list->data); + + gimp_object_set_name (GIMP_OBJECT (palette), palette_name); + + g_list_free_full (palette_list, (GDestroyNotify) g_object_unref); + + return palette; + } + + return NULL; +} diff --git a/app/core/gimppalette-import.h b/app/core/gimppalette-import.h new file mode 100644 index 0000000..9cd3901 --- /dev/null +++ b/app/core/gimppalette-import.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GIMP_PALETTE_IMPORT__ +#define __GIMP_PALETTE_IMPORT__ + + +GimpPalette * gimp_palette_import_from_gradient (GimpGradient *gradient, + GimpContext *context, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + const gchar *palette_name, + gint n_colors); +GimpPalette * gimp_palette_import_from_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only); +GimpPalette * gimp_palette_import_from_indexed_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name); +GimpPalette * gimp_palette_import_from_drawable (GimpDrawable *drawable, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only); +GimpPalette * gimp_palette_import_from_file (GimpContext *context, + GFile *file, + const gchar *palette_name, + GError **error); + +#endif /* __GIMP_PALETTE_IMPORT_H__ */ diff --git a/app/core/gimppalette-load.c b/app/core/gimppalette-load.c new file mode 100644 index 0000000..7605b92 --- /dev/null +++ b/app/core/gimppalette-load.c @@ -0,0 +1,1328 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp-utils.h" +#include "gimppalette.h" +#include "gimppalette-load.h" + +#include "gimp-intl.h" + + +static gchar * gimp_palette_load_acb_string (GInputStream *input, + goffset file_size, + GError **error); +static gchar * gimp_palette_load_ase_block_name (GInputStream *input, + goffset file_size, + GError **error); + + +GList * +gimp_palette_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette = NULL; + GimpPaletteEntry *entry; + GDataInputStream *data_input; + gchar *str; + gsize str_len; + gchar *tok; + gint r, g, b; + gint linenum; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data_input = g_data_input_stream_new (input); + + r = g = b = 0; + + linenum = 1; + str_len = 1024; + str = gimp_data_input_stream_read_line_always (data_input, &str_len, + NULL, error); + if (! str) + goto failed; + + if (! g_str_has_prefix (str, "GIMP Palette")) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Missing magic header.")); + g_free (str); + goto failed; + } + + g_free (str); + + palette = g_object_new (GIMP_TYPE_PALETTE, + "mime-type", "application/x-gimp-palette", + NULL); + + linenum++; + str_len = 1024; + str = gimp_data_input_stream_read_line_always (data_input, &str_len, + NULL, error); + if (! str) + goto failed; + + if (g_str_has_prefix (str, "Name: ")) + { + gchar *utf8; + + utf8 = gimp_any_to_utf8 (g_strstrip (str + strlen ("Name: ")), -1, + _("Invalid UTF-8 string in palette file '%s'"), + gimp_file_get_utf8_name (file)); + gimp_object_take_name (GIMP_OBJECT (palette), utf8); + g_free (str); + + linenum++; + str_len = 1024; + str = gimp_data_input_stream_read_line_always (data_input, &str_len, + NULL, error); + if (! str) + goto failed; + + if (g_str_has_prefix (str, "Columns: ")) + { + gint columns; + + if (! gimp_ascii_strtoi (g_strstrip (str + strlen ("Columns: ")), + NULL, 10, &columns)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid column count.")); + g_free (str); + goto failed; + } + + if (columns < 0 || columns > 256) + { + g_message (_("Reading palette file '%s': " + "Invalid number of columns in line %d. " + "Using default value."), + gimp_file_get_utf8_name (file), linenum); + columns = 0; + } + + gimp_palette_set_columns (palette, columns); + g_free (str); + + linenum++; + str_len = 1024; + str = gimp_data_input_stream_read_line_always (data_input, &str_len, + NULL, error); + if (! str) + goto failed; + } + } + else /* old palette format */ + { + gimp_object_take_name (GIMP_OBJECT (palette), + g_path_get_basename (gimp_file_get_utf8_name (file))); + } + + while (str) + { + GError *my_error = NULL; + + if (str[0] != '#' && str[0] != '\0') + { + tok = strtok (str, " \t"); + if (tok) + r = atoi (tok); + else + g_message (_("Reading palette file '%s': " + "Missing RED component in line %d."), + gimp_file_get_utf8_name (file), linenum); + + tok = strtok (NULL, " \t"); + if (tok) + g = atoi (tok); + else + g_message (_("Reading palette file '%s': " + "Missing GREEN component in line %d."), + gimp_file_get_utf8_name (file), linenum); + + tok = strtok (NULL, " \t"); + if (tok) + b = atoi (tok); + else + g_message (_("Reading palette file '%s': " + "Missing BLUE component in line %d."), + gimp_file_get_utf8_name (file), linenum); + + /* optional name */ + tok = strtok (NULL, "\n"); + + if (r < 0 || r > 255 || + g < 0 || g > 255 || + b < 0 || b > 255) + g_message (_("Reading palette file '%s': " + "RGB value out of range in line %d."), + gimp_file_get_utf8_name (file), linenum); + + /* don't call gimp_palette_add_entry here, it's rather inefficient */ + entry = g_slice_new0 (GimpPaletteEntry); + + gimp_rgba_set_uchar (&entry->color, + (guchar) r, + (guchar) g, + (guchar) b, + 255); + + entry->name = g_strdup (tok ? tok : _("Untitled")); + entry->position = gimp_palette_get_n_colors (palette); + + palette->colors = g_list_prepend (palette->colors, entry); + palette->n_colors++; + } + + g_free (str); + + linenum++; + str_len = 1024; + str = g_data_input_stream_read_line (data_input, &str_len, + NULL, &my_error); + if (! str && my_error) + { + g_message (_("Reading palette file '%s': " + "Read %d colors from truncated file: %s"), + gimp_file_get_utf8_name (file), + g_list_length (palette->colors), + my_error->message); + g_clear_error (&my_error); + } + } + + palette->colors = g_list_reverse (palette->colors); + + g_object_unref (data_input); + + return g_list_prepend (NULL, palette); + + failed: + + g_object_unref (data_input); + + if (palette) + g_object_unref (palette); + + g_prefix_error (error, _("In line %d of palette file: "), linenum); + + return NULL; +} + +GList * +gimp_palette_load_act (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar *palette_name; + guchar color_bytes[3]; + gsize bytes_read; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + g_free (palette_name); + + while (g_input_stream_read_all (input, color_bytes, sizeof (color_bytes), + &bytes_read, NULL, NULL) && + bytes_read == sizeof (color_bytes)) + { + GimpRGB color; + + gimp_rgba_set_uchar (&color, + color_bytes[0], + color_bytes[1], + color_bytes[2], + 255); + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + return g_list_prepend (NULL, palette); +} + +GList * +gimp_palette_load_riff (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar *palette_name; + guchar color_bytes[4]; + gsize bytes_read; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + g_free (palette_name); + + if (! g_seekable_seek (G_SEEKABLE (input), 28, G_SEEK_SET, NULL, error)) + { + g_object_unref (palette); + return NULL; + } + + while (g_input_stream_read_all (input, color_bytes, sizeof (color_bytes), + &bytes_read, NULL, NULL) && + bytes_read == sizeof (color_bytes)) + { + GimpRGB color; + + gimp_rgba_set_uchar (&color, + color_bytes[0], + color_bytes[1], + color_bytes[2], + 255); + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + return g_list_prepend (NULL, palette); +} + +GList * +gimp_palette_load_psp (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar *palette_name; + guchar color_bytes[4]; + gint number_of_colors; + gsize bytes_read; + gint i, j; + gboolean color_ok; + gchar buffer[4096]; + /*Maximum valid file size: 256 * 4 * 3 + 256 * 2 ~= 3650 bytes */ + gchar **lines; + gchar **ascii_colors; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + g_free (palette_name); + + if (! g_seekable_seek (G_SEEKABLE (input), 16, G_SEEK_SET, NULL, error)) + { + g_object_unref (palette); + return NULL; + } + + if (! g_input_stream_read_all (input, buffer, sizeof (buffer) - 1, + &bytes_read, NULL, error)) + { + g_object_unref (palette); + return NULL; + } + + buffer[bytes_read] = '\0'; + + lines = g_strsplit (buffer, "\x0d\x0a", -1); + + number_of_colors = atoi (lines[0]); + + for (i = 0; i < number_of_colors; i++) + { + if (lines[i + 1] == NULL) + { + g_printerr ("Premature end of file reading %s.", + gimp_file_get_utf8_name (file)); + break; + } + + ascii_colors = g_strsplit (lines[i + 1], " ", 3); + color_ok = TRUE; + + for (j = 0 ; j < 3; j++) + { + if (ascii_colors[j] == NULL) + { + g_printerr ("Corrupted palette file %s.", + gimp_file_get_utf8_name (file)); + color_ok = FALSE; + break; + } + + color_bytes[j] = atoi (ascii_colors[j]); + } + + if (color_ok) + { + GimpRGB color; + + gimp_rgba_set_uchar (&color, + color_bytes[0], + color_bytes[1], + color_bytes[2], + 255); + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + g_strfreev (ascii_colors); + } + + g_strfreev (lines); + + return g_list_prepend (NULL, palette); +} + +GList * +gimp_palette_load_aco (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar *palette_name; + gint format_version; + gint number_of_colors; + gint i; + gchar header[4]; + gsize bytes_read; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (! g_input_stream_read_all (input, header, sizeof (header), + &bytes_read, NULL, error) || + bytes_read != sizeof (header)) + { + g_prefix_error (error, + _("Could not read header from palette file '%s': "), + gimp_file_get_utf8_name (file)); + return NULL; + } + + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + g_free (palette_name); + + format_version = header[1] + (header[0] << 8); + number_of_colors = header[3] + (header[2] << 8); + + for (i = 0; i < number_of_colors; i++) + { + gchar color_info[10]; + gint color_space; + gint w, x, y, z; + gboolean color_ok = FALSE; + GimpRGB color; + GError *my_error = NULL; + + if (! g_input_stream_read_all (input, color_info, sizeof (color_info), + &bytes_read, NULL, &my_error) || + bytes_read != sizeof (color_info)) + { + if (palette->colors) + { + g_message (_("Reading palette file '%s': " + "Read %d colors from truncated file: %s"), + gimp_file_get_utf8_name (file), + g_list_length (palette->colors), + my_error ? + my_error->message : _("Premature end of file.")); + g_clear_error (&my_error); + break; + } + + g_propagate_error (error, my_error); + g_object_unref (palette); + + return NULL; + } + + color_space = color_info[1] + (color_info[0] << 8); + + w = (guchar) color_info[3] + ((guchar) color_info[2] << 8); + x = (guchar) color_info[5] + ((guchar) color_info[4] << 8); + y = (guchar) color_info[7] + ((guchar) color_info[6] << 8); + z = (guchar) color_info[9] + ((guchar) color_info[8] << 8); + + if (color_space == 0) /* RGB */ + { + gdouble R = ((gdouble) w) / 65536.0; + gdouble G = ((gdouble) x) / 65536.0; + gdouble B = ((gdouble) y) / 65536.0; + + gimp_rgba_set (&color, R, G, B, 1.0); + + color_ok = TRUE; + } + else if (color_space == 1) /* HSV */ + { + GimpHSV hsv; + + gdouble H = ((gdouble) w) / 65536.0; + gdouble S = ((gdouble) x) / 65536.0; + gdouble V = ((gdouble) y) / 65536.0; + + gimp_hsva_set (&hsv, H, S, V, 1.0); + gimp_hsv_to_rgb (&hsv, &color); + + color_ok = TRUE; + } + else if (color_space == 2) /* CMYK */ + { + GimpCMYK cmyk; + + gdouble C = 1.0 - (((gdouble) w) / 65536.0); + gdouble M = 1.0 - (((gdouble) x) / 65536.0); + gdouble Y = 1.0 - (((gdouble) y) / 65536.0); + gdouble K = 1.0 - (((gdouble) z) / 65536.0); + + gimp_cmyka_set (&cmyk, C, M, Y, K, 1.0); + gimp_cmyk_to_rgb (&cmyk, &color); + + color_ok = TRUE; + } + else if (color_space == 8) /* Grayscale */ + { + gdouble K = 1.0 - (((gdouble) w) / 10000.0); + + gimp_rgba_set (&color, K, K, K, 1.0); + + color_ok = TRUE; + } + else if (color_space == 9) /* Wide? CMYK */ + { + GimpCMYK cmyk; + + gdouble C = 1.0 - (((gdouble) w) / 10000.0); + gdouble M = 1.0 - (((gdouble) x) / 10000.0); + gdouble Y = 1.0 - (((gdouble) y) / 10000.0); + gdouble K = 1.0 - (((gdouble) z) / 10000.0); + + gimp_cmyka_set (&cmyk, C, M, Y, K, 1.0); + gimp_cmyk_to_rgb (&cmyk, &color); + + color_ok = TRUE; + } + else + { + g_printerr ("Unsupported color space (%d) in ACO file %s\n", + color_space, gimp_file_get_utf8_name (file)); + } + + if (format_version == 2) + { + gchar format2_preamble[4]; + gint number_of_chars; + + if (! g_input_stream_read_all (input, + format2_preamble, + sizeof (format2_preamble), + &bytes_read, NULL, error) || + bytes_read != sizeof (format2_preamble)) + { + g_object_unref (palette); + return NULL; + } + + number_of_chars = format2_preamble[3] + (format2_preamble[2] << 8); + + if (! g_seekable_seek (G_SEEKABLE (input), number_of_chars * 2, + G_SEEK_SET, NULL, error)) + { + g_object_unref (palette); + return NULL; + } + } + + if (color_ok) + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + return g_list_prepend (NULL, palette); +} + +GList * +gimp_palette_load_acb (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar header[4]; + gshort version; + gchar **palette_strings; + gchar *palette_name; + gchar **palette_prefix; + gchar **palette_suffix; + gchar *description; + gushort number_of_colors; + gushort page; + gushort color_space; + const Babl *src_format = NULL; + const Babl *dst_format = babl_format ("R'G'B' double"); + gint i; + goffset file_size; + gsize bytes_read; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* Get file size */ + g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_END, NULL, error); + file_size = g_seekable_tell (G_SEEKABLE (input)); + g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, error); + + if (! g_input_stream_read_all (input, header, sizeof (header), + &bytes_read, NULL, error) || + bytes_read != sizeof (header)) + { + g_prefix_error (error, + _("Could not read header from palette file '%s': "), + gimp_file_get_utf8_name (file)); + return NULL; + } + + /* Version and ID */ + if (! g_input_stream_read_all (input, &version, sizeof (version), + &bytes_read, NULL, error)) + { + g_prefix_error (error, _("Invalid ACB palette version.")); + return NULL; + } + version = GUINT16_FROM_BE (version); + if (version != 1) + { + g_prefix_error (error, _("GIMP only supports version 1 ACB palettes")); + return NULL; + } + + if (! g_input_stream_read_all (input, &version, sizeof (version), + &bytes_read, NULL, error)) + { + g_prefix_error (error, _("Invalid ACB palette identifier.")); + return NULL; + } + + /* Color Book name and palette prefix/suffix */ + palette_name = gimp_palette_load_acb_string (input, file_size, error); + if (palette_name) + palette_strings = g_strsplit (palette_name, "=", -1); + if (! palette_name || g_strv_length (palette_strings) != 2) + { + if (palette_name) + { + g_strfreev (palette_strings); + g_free (palette_name); + } + g_prefix_error (error, _("Invalid ACB palette name.")); + return NULL; + } + palette = GIMP_PALETTE (gimp_palette_new (context, palette_strings[1])); + + g_strfreev (palette_strings); + g_free (palette_name); + + palette_name = gimp_palette_load_acb_string (input, file_size, error); + if (palette_name) + palette_prefix = g_strsplit (palette_name, "=", -1); + if (! palette_name || g_strv_length (palette_prefix) != 2) + { + if (palette_name) + { + g_strfreev (palette_prefix); + g_free (palette_name); + } + g_prefix_error (error, _("Invalid ACB palette prefix.")); + return NULL; + } + g_free (palette_name); + + palette_name = gimp_palette_load_acb_string (input, file_size, error); + if (palette_name) + palette_suffix = g_strsplit (palette_name, "=", -1); + if (! palette_name || g_strv_length (palette_suffix) != 2) + { + if (palette_name) + { + g_strfreev (palette_suffix); + g_free (palette_name); + } + g_prefix_error (error, _("Invalid ACB palette suffix.")); + return NULL; + } + g_free (palette_name); + + /* Description (currently unused) */ + description = gimp_palette_load_acb_string (input, file_size, + error); + g_free (description); + + /* Number of colors */ + if (! g_input_stream_read_all (input, &number_of_colors, + sizeof (number_of_colors), &bytes_read, NULL, + error)) + { + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + g_prefix_error (error, _("Invalid number of colors in palette.")); + return NULL; + } + number_of_colors = GUINT16_FROM_BE (number_of_colors); + if (number_of_colors <= 1) + { + g_prefix_error (error, _("Invalid number of colors: %s."), + gimp_file_get_utf8_name (file)); + return NULL; + } + + /* Page information (currently unused) */ + if (! g_input_stream_read_all (input, &page, sizeof (page), + &bytes_read, NULL, error)) + { + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + g_prefix_error (error, _("Invalid ACB palette page info.")); + return NULL; + } + if (! g_input_stream_read_all (input, &page, sizeof (page), + &bytes_read, NULL, error)) + { + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + g_prefix_error (error, _("Invalid ACB palette page info.")); + return NULL; + } + + /* Color Model */ + if (! g_input_stream_read_all (input, &color_space, sizeof (color_space), + &bytes_read, NULL, error)) + { + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + g_prefix_error (error, _("Invalid ACB palette color space.")); + return NULL; + } + color_space = GUINT16_FROM_BE (color_space); + + if (color_space == 0) + { + src_format = babl_format ("R'G'B' u8"); + } + else if (color_space == 2) + { + src_format = babl_format ("cmyk u8"); + } + else if (color_space == 7) + { + src_format = babl_format ("CIE Lab u8"); + } + else + { + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + g_prefix_error (error, _("Invalid ACB palette color space.")); + return NULL; + } + + /* Color records */ + for (i = 0; i < number_of_colors; i++) + { + gchar color_code[6]; + gchar *palette_entry = NULL; + gchar *full_palette_name = NULL; + void *pixels = NULL; + gboolean color_ok = FALSE; + GimpRGB color; + + palette_entry = gimp_palette_load_acb_string (input, file_size, + error); + + if (palette_entry) + full_palette_name = g_strdup_printf ("%s%s %s", palette_prefix[1], + palette_entry, palette_suffix[1]); + + /* Color Code (not used) */ + if (! g_input_stream_read_all (input, color_code, sizeof (color_code), + &bytes_read, NULL, error)) + { + g_free (palette_entry); + g_free (full_palette_name); + + g_printerr ("Invalid ACB palette color code"); + break; + } + + if (color_space == 0) + { + gchar rgb[3]; + + if (! g_input_stream_read_all (input, rgb, sizeof (rgb), + &bytes_read, NULL, error)) + { + g_free (palette_entry); + g_free (full_palette_name); + + g_printerr ("Invalid ACB palette colors"); + break; + } + + pixels = rgb; + color_ok = TRUE; + } + else if (color_space == 2) + { + gchar cmyk[4]; + + if (! g_input_stream_read_all (input, cmyk, sizeof (cmyk), + &bytes_read, NULL, error)) + { + g_free (palette_entry); + g_free (full_palette_name); + + g_printerr ("Invalid ACB palette colors"); + break; + } + + for (gint j = 0; j < 4; j++) + cmyk[j] = ((255 - cmyk[j]) / 2.55f) + 0.5f; + + pixels = cmyk; + color_ok = TRUE; + } + else if (color_space == 7) + { + gchar lab[3]; + + if (! g_input_stream_read_all (input, lab, sizeof (lab), + &bytes_read, NULL, error)) + { + g_free (palette_entry); + g_free (full_palette_name); + + g_printerr ("Invalid ACB palette colors"); + break; + } + + lab[0] = (lab[0] / 2.55f) + 0.5f; + lab[1] = lab[1] - 128; + lab[2] = lab[2] - 128; + + pixels = lab; + color_ok = TRUE; + } + + if (color_ok && palette_entry) + { + babl_process (babl_fish (src_format, dst_format), (gdouble *) pixels, + &color, 1); + gimp_palette_add_entry (palette, -1, full_palette_name, &color); + } + + g_free (palette_entry); + g_free (full_palette_name); + + if (! color_ok) + { + g_printerr ("Invalid ACB palette colors"); + break; + } + } + + g_strfreev (palette_prefix); + g_strfreev (palette_suffix); + + return g_list_prepend (NULL, palette); +} + +static gchar * +gimp_palette_load_acb_string (GInputStream *input, + goffset file_size, + GError **error) +{ + gint pal_name_len; + gunichar2 *pal_name = NULL; + gchar *pal_name_utf8; + goffset current_pos; + gsize bytes_read; + + if (! g_input_stream_read_all (input, &pal_name_len, sizeof (pal_name_len), + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ACB palette name.")); + return NULL; + } + + pal_name_len = GUINT32_FROM_BE (pal_name_len); + current_pos = g_seekable_tell (G_SEEKABLE (input)); + + if (pal_name_len <= 0 || pal_name_len > (file_size - current_pos)) + { + if (pal_name_len != 0) + g_printerr (_("Invalid ACB name size.")); + + return NULL; + } + pal_name = g_malloc (pal_name_len * 2); + + for (gint i = 0; i < pal_name_len; i++) + { + if (! g_input_stream_read_all (input, &pal_name[i], 2, + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ACB palette name.")); + g_free (pal_name); + return NULL; + } + + pal_name[i] = GUINT16_FROM_BE (pal_name[i]); + } + + pal_name_utf8 = g_utf16_to_utf8 (pal_name, pal_name_len, NULL, NULL, NULL); + g_free (pal_name); + + return pal_name_utf8; +} + +GList * +gimp_palette_load_ase (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + gchar *palette_name; + goffset file_size; + gint num_cols; + gint i; + gchar header[8]; + gshort group; + gsize bytes_read; + gboolean skip_first = FALSE; + const Babl *src_format = NULL; + const Babl *dst_format = babl_format ("R'G'B' double"); + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* Get file size */ + g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_END, NULL, error); + file_size = g_seekable_tell (G_SEEKABLE (input)); + g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, error); + + if (! g_input_stream_read_all (input, header, sizeof (header), + &bytes_read, NULL, error) || + bytes_read != sizeof (header)) + { + g_prefix_error (error, + _("Could not read header from palette file '%s': "), + gimp_file_get_utf8_name (file)); + return NULL; + } + + + /* Checking header values */ + if (! g_str_has_prefix (header + 0, "ASEF") || + header[5] != 0x01) + { + g_prefix_error (error, _("Invalid ASE header: %s"), + gimp_file_get_utf8_name (file)); + return NULL; + } + + if (! g_input_stream_read_all (input, &num_cols, sizeof (num_cols), + &bytes_read, NULL, error)) + { + g_prefix_error (error, + _("Invalid number of colors in palette.")); + return NULL; + } + num_cols = GINT32_FROM_BE (num_cols); + if (num_cols <= 1) + { + g_prefix_error (error, _("Invalid number of colors: %s."), + gimp_file_get_utf8_name (file)); + return NULL; + } + + /* First block contains the palette name if + * one is defined. */ + if (! g_input_stream_read_all (input, &group, sizeof (group), + &bytes_read, NULL, error)) + { + g_prefix_error (error, _("Invalid ASE file: %s."), + gimp_file_get_utf8_name (file)); + return NULL; + } + group = GINT16_FROM_BE (group); + + /* If first marker is 0x01, then the palette has no group name */ + if (group != 1) + { + palette_name = gimp_palette_load_ase_block_name (input, file_size, + error); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + num_cols -= 1; + } + else + { + palette_name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + skip_first = TRUE; + } + g_free (palette_name); + + /* Header blocks are considered a "color" so we offset the count here */ + num_cols -= 1; + + for (i = 0; i < num_cols; i++) + { + gchar color_space[4]; + gchar *color_name; + GimpRGB color; + gshort spot_color; + gfloat *pixels; + gint components = 0; + gboolean valid_color = TRUE; + + if (! skip_first) + { + if (! g_input_stream_read_all (input, &group, sizeof (group), + &bytes_read, NULL, error)) + { + g_printerr ("Invalid ASE color entry: %s.", + gimp_file_get_utf8_name (file)); + break; + } + } + skip_first = FALSE; + + color_name = gimp_palette_load_ase_block_name (input, file_size, error); + if (! color_name) + break; + + g_input_stream_read_all (input, color_space, sizeof (color_space), + &bytes_read, NULL, error); + + /* Color formats */ + if (g_str_has_prefix (g_strstrip (color_space), "RGB")) + { + components = 3; + src_format = babl_format ("R'G'B' float"); + } + else if (g_str_has_prefix (g_strstrip (color_space), "GRAY")) + { + components = 1; + src_format = babl_format ("Y' float"); + } + else if (g_str_has_prefix (g_strstrip (color_space), "CMYK")) + { + components = 4; + src_format = babl_format ("CMYK float"); + } + else if (g_str_has_prefix (g_strstrip (color_space), "LAB")) + { + components = 3; + src_format = babl_format ("CIE Lab float"); + } + + if (components == 0) + { + g_printerr (_("Invalid color components: %s."), color_space); + g_free (color_name); + break; + } + + pixels = g_malloc (sizeof (gfloat) * components); + + for (gint j = 0; j < components; j++) + { + gint tmp; + + if (! g_input_stream_read_all (input, &tmp, sizeof (tmp), + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ASE color entry: %s."), + gimp_file_get_utf8_name (file)); + g_free (color_name); + valid_color = FALSE; + break; + } + + /* Convert 4 bytes to a 32bit float value */ + tmp = GINT32_FROM_BE (tmp); + pixels[j] = *(gfloat *) &tmp; + } + + if (! valid_color) + break; + + /* The L component of LAB goes from 0 to 100 percent */ + if (g_str_has_prefix (color_space, "LAB")) + pixels[0] *= 100; + + babl_process (babl_fish (src_format, dst_format), pixels, &color, 1); + g_free (pixels); + + /* TODO: When GIMP supports spot colors, use this information in + * the palette. */ + if (! g_input_stream_read_all (input, &spot_color, sizeof (spot_color), + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ASE color entry: %s."), + gimp_file_get_utf8_name (file)); + g_free (color_name); + break; + } + + gimp_palette_add_entry (palette, -1, color_name, &color); + g_free (color_name); + } + + return g_list_prepend (NULL, palette); +} + +static gchar * +gimp_palette_load_ase_block_name (GInputStream *input, + goffset file_size, + GError **error) +{ + gint block_length; + gushort pal_name_len; + gunichar2 *pal_name = NULL; + gchar *pal_name_utf8; + goffset current_pos; + gsize bytes_read; + + if (! g_input_stream_read_all (input, &block_length, sizeof (block_length), + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ASE palette name.")); + return NULL; + } + + block_length = GINT32_FROM_BE (block_length); + current_pos = g_seekable_tell (G_SEEKABLE (input)); + + if (block_length <= 0 || block_length > (file_size - current_pos)) + { + g_printerr (_("Invalid ASE block size.")); + return NULL; + } + + if (! g_input_stream_read_all (input, &pal_name_len, sizeof (pal_name_len), + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ASE palette name.")); + return NULL; + } + + pal_name_len = GUINT16_FROM_BE (pal_name_len); + current_pos = g_seekable_tell (G_SEEKABLE (input)); + + if (pal_name_len <= 0 || pal_name_len > (file_size - current_pos)) + { + g_printerr (_("Invalid ASE name size.")); + return NULL; + } + pal_name = g_malloc (pal_name_len * 2); + + for (gint i = 0; i < pal_name_len; i++) + { + if (! g_input_stream_read_all (input, &pal_name[i], 2, + &bytes_read, NULL, error)) + { + g_printerr (_("Invalid ASE palette name.")); + g_free (pal_name); + return NULL; + } + + pal_name[i] = GUINT16_FROM_BE (pal_name[i]); + } + + pal_name_utf8 = g_utf16_to_utf8 (pal_name, pal_name_len, NULL, NULL, NULL); + g_free (pal_name); + + return pal_name_utf8; +} + +GList * +gimp_palette_load_css (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPalette *palette; + GDataInputStream *data_input; + gchar *name; + GRegex *regex; + gchar *buf; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + regex = g_regex_new (".*color.*:(?P.*);", G_REGEX_CASELESS, 0, error); + if (! regex) + return NULL; + + name = g_path_get_basename (gimp_file_get_utf8_name (file)); + palette = GIMP_PALETTE (gimp_palette_new (context, name)); + g_free (name); + + data_input = g_data_input_stream_new (input); + + do + { + gsize buf_len = 1024; + + buf = g_data_input_stream_read_line (data_input, &buf_len, NULL, NULL); + + if (buf) + { + GMatchInfo *matches; + + if (g_regex_match (regex, buf, 0, &matches)) + { + GimpRGB color; + gchar *word = g_match_info_fetch_named (matches, "param"); + + if (gimp_rgb_parse_css (&color, word, -1)) + { + if (! gimp_palette_find_entry (palette, &color, NULL)) + { + gimp_palette_add_entry (palette, -1, NULL, &color); + } + } + + g_free (word); + } + g_match_info_free (matches); + g_free (buf); + } + } + while (buf); + + g_regex_unref (regex); + g_object_unref (data_input); + + return g_list_prepend (NULL, palette); +} + +GimpPaletteFileFormat +gimp_palette_load_detect_format (GFile *file, + GInputStream *input) +{ + GimpPaletteFileFormat format = GIMP_PALETTE_FILE_FORMAT_UNKNOWN; + gchar header[16]; + gsize bytes_read; + + if (g_input_stream_read_all (input, &header, sizeof (header), + &bytes_read, NULL, NULL) && + bytes_read == sizeof (header)) + { + if (g_str_has_prefix (header + 0, "RIFF") && + g_str_has_prefix (header + 8, "PAL data")) + { + format = GIMP_PALETTE_FILE_FORMAT_RIFF_PAL; + } + else if (g_str_has_prefix (header, "GIMP Palette")) + { + format = GIMP_PALETTE_FILE_FORMAT_GPL; + } + else if (g_str_has_prefix (header, "JASC-PAL")) + { + format = GIMP_PALETTE_FILE_FORMAT_PSP_PAL; + } + else if (g_str_has_prefix (header, "8BCB")) + { + format = GIMP_PALETTE_FILE_FORMAT_ACB; + } + } + + if (format == GIMP_PALETTE_FILE_FORMAT_UNKNOWN) + { + gchar *lower = g_ascii_strdown (gimp_file_get_utf8_name (file), -1); + + if (g_str_has_suffix (lower, ".aco")) + { + format = GIMP_PALETTE_FILE_FORMAT_ACO; + } + else if (g_str_has_suffix (lower, ".ase")) + { + format = GIMP_PALETTE_FILE_FORMAT_ASE; + } + else if (g_str_has_suffix (lower, ".css")) + { + format = GIMP_PALETTE_FILE_FORMAT_CSS; + } + + g_free (lower); + } + + if (format == GIMP_PALETTE_FILE_FORMAT_UNKNOWN) + { + GFileInfo *info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (info) + { + goffset size = g_file_info_get_size (info); + + if (size == 768) + format = GIMP_PALETTE_FILE_FORMAT_ACT; + + g_object_unref (info); + } + } + + g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, NULL); + + return format; +} diff --git a/app/core/gimppalette-load.h b/app/core/gimppalette-load.h new file mode 100644 index 0000000..44966c2 --- /dev/null +++ b/app/core/gimppalette-load.h @@ -0,0 +1,76 @@ +/* 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 . + */ + +#ifndef __GIMP_PALETTE_LOAD_H__ +#define __GIMP_PALETTE_LOAD_H__ + + +#define GIMP_PALETTE_FILE_EXTENSION ".gpl" + + +typedef enum +{ + GIMP_PALETTE_FILE_FORMAT_UNKNOWN, + GIMP_PALETTE_FILE_FORMAT_GPL, /* GIMP palette */ + GIMP_PALETTE_FILE_FORMAT_RIFF_PAL, /* RIFF palette */ + GIMP_PALETTE_FILE_FORMAT_ACT, /* Photoshop binary color palette */ + GIMP_PALETTE_FILE_FORMAT_PSP_PAL, /* JASC's Paint Shop Pro color palette */ + GIMP_PALETTE_FILE_FORMAT_ACO, /* Photoshop ACO color file */ + GIMP_PALETTE_FILE_FORMAT_ACB, /* Photoshop ACB color book */ + GIMP_PALETTE_FILE_FORMAT_ASE, /* Photoshop ASE color palette */ + GIMP_PALETTE_FILE_FORMAT_CSS /* Cascaded Stylesheet file (CSS) */ +} GimpPaletteFileFormat; + + +GList * gimp_palette_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_act (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_riff (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_psp (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_aco (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_acb (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_ase (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_palette_load_css (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + +GimpPaletteFileFormat gimp_palette_load_detect_format (GFile *file, + GInputStream *input); + + +#endif /* __GIMP_PALETTE_H__ */ diff --git a/app/core/gimppalette-save.c b/app/core/gimppalette-save.c new file mode 100644 index 0000000..59556bf --- /dev/null +++ b/app/core/gimppalette-save.c @@ -0,0 +1,77 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimppalette.h" +#include "gimppalette-save.h" + +#include "gimp-intl.h" + + +gboolean +gimp_palette_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpPalette *palette = GIMP_PALETTE (data); + GString *string; + GList *list; + + string = g_string_new ("GIMP Palette\n"); + + g_string_append_printf (string, + "Name: %s\n" + "Columns: %d\n" + "#\n", + gimp_object_get_name (palette), + CLAMP (gimp_palette_get_columns (palette), 0, 256)); + + for (list = gimp_palette_get_colors (palette); + list; + list = g_list_next (list)) + { + GimpPaletteEntry *entry = list->data; + guchar r, g, b; + + gimp_rgb_get_uchar (&entry->color, &r, &g, &b); + + g_string_append_printf (string, "%3d %3d %3d\t%s\n", + r, g, b, entry->name); + } + + if (! 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; +} diff --git a/app/core/gimppalette-save.h b/app/core/gimppalette-save.h new file mode 100644 index 0000000..e72c448 --- /dev/null +++ b/app/core/gimppalette-save.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 . + */ + +#ifndef __GIMP_PALETTE_SAVE_H__ +#define __GIMP_PALETTE_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_palette_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_PALETTE_SAVE_H__ */ diff --git a/app/core/gimppalette.c b/app/core/gimppalette.c new file mode 100644 index 0000000..f5b848c --- /dev/null +++ b/app/core/gimppalette.c @@ -0,0 +1,721 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimppalette.h" +#include "gimppalette-load.h" +#include "gimppalette-save.h" +#include "gimptagged.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +#define RGB_EPSILON 1e-6 + + +/* local function prototypes */ + +static void gimp_palette_tagged_iface_init (GimpTaggedInterface *iface); + +static void gimp_palette_finalize (GObject *object); + +static gint64 gimp_palette_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_palette_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_palette_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_palette_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_palette_get_description (GimpViewable *viewable, + gchar **tooltip); +static const gchar * gimp_palette_get_extension (GimpData *data); +static void gimp_palette_copy (GimpData *data, + GimpData *src_data); + +static void gimp_palette_entry_free (GimpPaletteEntry *entry); +static gint64 gimp_palette_entry_get_memsize (GimpPaletteEntry *entry, + gint64 *gui_size); +static gchar * gimp_palette_get_checksum (GimpTagged *tagged); + + +G_DEFINE_TYPE_WITH_CODE (GimpPalette, gimp_palette, GIMP_TYPE_DATA, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_palette_tagged_iface_init)) + +#define parent_class gimp_palette_parent_class + + +static void +gimp_palette_class_init (GimpPaletteClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->finalize = gimp_palette_finalize; + + gimp_object_class->get_memsize = gimp_palette_get_memsize; + + viewable_class->default_icon_name = "gtk-select-color"; + viewable_class->get_preview_size = gimp_palette_get_preview_size; + viewable_class->get_popup_size = gimp_palette_get_popup_size; + viewable_class->get_new_preview = gimp_palette_get_new_preview; + viewable_class->get_description = gimp_palette_get_description; + + data_class->save = gimp_palette_save; + data_class->get_extension = gimp_palette_get_extension; + data_class->copy = gimp_palette_copy; +} + +static void +gimp_palette_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->get_checksum = gimp_palette_get_checksum; +} + +static void +gimp_palette_init (GimpPalette *palette) +{ + palette->colors = NULL; + palette->n_colors = 0; + palette->n_columns = 0; +} + +static void +gimp_palette_finalize (GObject *object) +{ + GimpPalette *palette = GIMP_PALETTE (object); + + if (palette->colors) + { + g_list_free_full (palette->colors, + (GDestroyNotify) gimp_palette_entry_free); + palette->colors = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_palette_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpPalette *palette = GIMP_PALETTE (object); + gint64 memsize = 0; + + memsize += gimp_g_list_get_memsize_foreach (palette->colors, + (GimpMemsizeFunc) + gimp_palette_entry_get_memsize, + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_palette_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + *width = size; + *height = 1 + size / 2; +} + +static gboolean +gimp_palette_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + GimpPalette *palette = GIMP_PALETTE (viewable); + gint p_width; + gint p_height; + + if (! palette->n_colors) + return FALSE; + + if (palette->n_columns) + p_width = palette->n_columns; + else + p_width = MIN (palette->n_colors, 16); + + p_height = MAX (1, palette->n_colors / p_width); + + if (p_width * 4 > width || p_height * 4 > height) + { + *popup_width = p_width * 4; + *popup_height = p_height * 4; + + return TRUE; + } + + return FALSE; +} + +static GimpTempBuf * +gimp_palette_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpPalette *palette = GIMP_PALETTE (viewable); + GimpTempBuf *temp_buf; + guchar *buf; + guchar *b; + GList *list; + gint columns; + gint rows; + gint cell_size; + gint x, y; + + temp_buf = gimp_temp_buf_new (width, height, babl_format ("R'G'B' u8")); + memset (gimp_temp_buf_get_data (temp_buf), 255, width * height * 3); + + if (palette->n_columns > 1) + cell_size = MAX (4, width / palette->n_columns); + else + cell_size = 4; + + columns = width / cell_size; + rows = height / cell_size; + + buf = gimp_temp_buf_get_data (temp_buf); + b = g_new (guchar, width * 3); + + list = palette->colors; + + for (y = 0; y < rows && list; y++) + { + gint i; + + memset (b, 255, width * 3); + + for (x = 0; x < columns && list; x++) + { + GimpPaletteEntry *entry = list->data; + + list = g_list_next (list); + + gimp_rgb_get_uchar (&entry->color, + &b[x * cell_size * 3 + 0], + &b[x * cell_size * 3 + 1], + &b[x * cell_size * 3 + 2]); + + for (i = 1; i < cell_size; i++) + { + b[(x * cell_size + i) * 3 + 0] = b[(x * cell_size) * 3 + 0]; + b[(x * cell_size + i) * 3 + 1] = b[(x * cell_size) * 3 + 1]; + b[(x * cell_size + i) * 3 + 2] = b[(x * cell_size) * 3 + 2]; + } + } + + for (i = 0; i < cell_size; i++) + memcpy (buf + ((y * cell_size + i) * width) * 3, b, width * 3); + } + + g_free (b); + + return temp_buf; +} + +static gchar * +gimp_palette_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpPalette *palette = GIMP_PALETTE (viewable); + + return g_strdup_printf ("%s (%d)", + gimp_object_get_name (palette), + palette->n_colors); +} + +GimpData * +gimp_palette_new (GimpContext *context, + const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + return g_object_new (GIMP_TYPE_PALETTE, + "name", name, + NULL); +} + +GimpData * +gimp_palette_get_standard (GimpContext *context) +{ + static GimpData *standard_palette = NULL; + + if (! standard_palette) + { + standard_palette = gimp_palette_new (context, "Standard"); + + gimp_data_clean (standard_palette); + gimp_data_make_internal (standard_palette, "gimp-palette-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_palette), + (gpointer *) &standard_palette); + } + + return standard_palette; +} + +static const gchar * +gimp_palette_get_extension (GimpData *data) +{ + return GIMP_PALETTE_FILE_EXTENSION; +} + +static void +gimp_palette_copy (GimpData *data, + GimpData *src_data) +{ + GimpPalette *palette = GIMP_PALETTE (data); + GimpPalette *src_palette = GIMP_PALETTE (src_data); + GList *list; + + gimp_data_freeze (data); + + if (palette->colors) + { + g_list_free_full (palette->colors, + (GDestroyNotify) gimp_palette_entry_free); + palette->colors = NULL; + } + + palette->n_colors = 0; + palette->n_columns = src_palette->n_columns; + + for (list = src_palette->colors; list; list = g_list_next (list)) + { + GimpPaletteEntry *entry = list->data; + + gimp_palette_add_entry (palette, -1, entry->name, &entry->color); + } + + gimp_data_thaw (data); +} + +static gchar * +gimp_palette_get_checksum (GimpTagged *tagged) +{ + GimpPalette *palette = GIMP_PALETTE (tagged); + gchar *checksum_string = NULL; + + if (palette->n_colors > 0) + { + GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5); + GList *color_iterator = palette->colors; + + g_checksum_update (checksum, (const guchar *) &palette->n_colors, sizeof (palette->n_colors)); + g_checksum_update (checksum, (const guchar *) &palette->n_columns, sizeof (palette->n_columns)); + + while (color_iterator) + { + GimpPaletteEntry *entry = (GimpPaletteEntry *) color_iterator->data; + + g_checksum_update (checksum, (const guchar *) &entry->color, sizeof (entry->color)); + if (entry->name) + g_checksum_update (checksum, (const guchar *) entry->name, strlen (entry->name)); + + color_iterator = g_list_next (color_iterator); + } + + checksum_string = g_strdup (g_checksum_get_string (checksum)); + + g_checksum_free (checksum); + } + + return checksum_string; +} + + +/* public functions */ + +GList * +gimp_palette_get_colors (GimpPalette *palette) +{ + g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL); + + return palette->colors; +} + +gint +gimp_palette_get_n_colors (GimpPalette *palette) +{ + g_return_val_if_fail (GIMP_IS_PALETTE (palette), 0); + + return palette->n_colors; +} + +void +gimp_palette_move_entry (GimpPalette *palette, + GimpPaletteEntry *entry, + gint position) +{ + GList *list; + gint pos = 0; + + g_return_if_fail (GIMP_IS_PALETTE (palette)); + g_return_if_fail (entry != NULL); + + if (g_list_find (palette->colors, entry)) + { + pos = entry->position; + + if (entry->position == position) + return; + + entry->position = position; + palette->colors = g_list_remove (palette->colors, + entry); + palette->colors = g_list_insert (palette->colors, + entry, position); + + if (pos < position) + { + for (list = g_list_nth (palette->colors, pos); + list && pos < position; + list = g_list_next (list)) + { + entry = (GimpPaletteEntry *) list->data; + + entry->position = pos++; + } + } + else + { + for (list = g_list_nth (palette->colors, position + 1); + list && position < pos; + list = g_list_next (list)) + { + entry = (GimpPaletteEntry *) list->data; + + entry->position += 1; + pos--; + } + } + + gimp_data_dirty (GIMP_DATA (palette)); + } +} + +GimpPaletteEntry * +gimp_palette_add_entry (GimpPalette *palette, + gint position, + const gchar *name, + const GimpRGB *color) +{ + GimpPaletteEntry *entry; + + g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL); + g_return_val_if_fail (color != NULL, NULL); + + entry = g_slice_new0 (GimpPaletteEntry); + + entry->color = *color; + entry->name = g_strdup (name ? name : _("Untitled")); + + if (position < 0 || position >= palette->n_colors) + { + entry->position = palette->n_colors; + palette->colors = g_list_append (palette->colors, entry); + } + else + { + GList *list; + + entry->position = position; + palette->colors = g_list_insert (palette->colors, entry, position); + + /* renumber the displaced entries */ + for (list = g_list_nth (palette->colors, position + 1); + list; + list = g_list_next (list)) + { + GimpPaletteEntry *entry2 = list->data; + + entry2->position += 1; + } + } + + palette->n_colors += 1; + + gimp_data_dirty (GIMP_DATA (palette)); + + return entry; +} + +void +gimp_palette_delete_entry (GimpPalette *palette, + GimpPaletteEntry *entry) +{ + GList *list; + gint pos = 0; + + g_return_if_fail (GIMP_IS_PALETTE (palette)); + g_return_if_fail (entry != NULL); + + if (g_list_find (palette->colors, entry)) + { + pos = entry->position; + gimp_palette_entry_free (entry); + + palette->colors = g_list_remove (palette->colors, entry); + + palette->n_colors--; + + for (list = g_list_nth (palette->colors, pos); + list; + list = g_list_next (list)) + { + entry = (GimpPaletteEntry *) list->data; + + entry->position = pos++; + } + + gimp_data_dirty (GIMP_DATA (palette)); + } +} + +gboolean +gimp_palette_set_entry (GimpPalette *palette, + gint position, + const gchar *name, + const GimpRGB *color) +{ + GimpPaletteEntry *entry; + + g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + entry = gimp_palette_get_entry (palette, position); + + if (! entry) + return FALSE; + + entry->color = *color; + + if (entry->name) + g_free (entry->name); + + entry->name = g_strdup (name); + + gimp_data_dirty (GIMP_DATA (palette)); + + return TRUE; +} + +gboolean +gimp_palette_set_entry_color (GimpPalette *palette, + gint position, + const GimpRGB *color) +{ + GimpPaletteEntry *entry; + + g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + entry = gimp_palette_get_entry (palette, position); + + if (! entry) + return FALSE; + + entry->color = *color; + + gimp_data_dirty (GIMP_DATA (palette)); + + return TRUE; +} + +gboolean +gimp_palette_set_entry_name (GimpPalette *palette, + gint position, + const gchar *name) +{ + GimpPaletteEntry *entry; + + g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE); + + entry = gimp_palette_get_entry (palette, position); + + if (! entry) + return FALSE; + + if (entry->name) + g_free (entry->name); + + entry->name = g_strdup (name); + + gimp_data_dirty (GIMP_DATA (palette)); + + return TRUE; +} + +GimpPaletteEntry * +gimp_palette_get_entry (GimpPalette *palette, + gint position) +{ + g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL); + + return g_list_nth_data (palette->colors, position); +} + +void +gimp_palette_set_columns (GimpPalette *palette, + gint columns) +{ + g_return_if_fail (GIMP_IS_PALETTE (palette)); + + columns = CLAMP (columns, 0, 64); + + if (palette->n_columns != columns) + { + palette->n_columns = columns; + + gimp_data_dirty (GIMP_DATA (palette)); + } +} + +gint +gimp_palette_get_columns (GimpPalette *palette) +{ + g_return_val_if_fail (GIMP_IS_PALETTE (palette), 0); + + return palette->n_columns; +} + +GimpPaletteEntry * +gimp_palette_find_entry (GimpPalette *palette, + const GimpRGB *color, + GimpPaletteEntry *start_from) +{ + GimpPaletteEntry *entry; + + g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL); + g_return_val_if_fail (color != NULL, NULL); + + if (! start_from) + { + GList *list; + + /* search from the start */ + + for (list = palette->colors; list; list = g_list_next (list)) + { + entry = (GimpPaletteEntry *) list->data; + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) + return entry; + } + } + else if (gimp_rgb_distance (&start_from->color, color) < RGB_EPSILON) + { + return start_from; + } + else + { + GList *old = g_list_find (palette->colors, start_from); + GList *next; + GList *prev; + + g_return_val_if_fail (old != NULL, NULL); + + next = old->next; + prev = old->prev; + + /* proximity-based search */ + + while (next || prev) + { + if (next) + { + entry = (GimpPaletteEntry *) next->data; + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) + return entry; + + next = next->next; + } + + if (prev) + { + entry = (GimpPaletteEntry *) prev->data; + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) + return entry; + + prev = prev->prev; + } + } + } + + return NULL; +} + + +/* private functions */ + +static void +gimp_palette_entry_free (GimpPaletteEntry *entry) +{ + g_return_if_fail (entry != NULL); + + g_free (entry->name); + + g_slice_free (GimpPaletteEntry, entry); +} + +static gint64 +gimp_palette_entry_get_memsize (GimpPaletteEntry *entry, + gint64 *gui_size) +{ + gint64 memsize = sizeof (GimpPaletteEntry); + + memsize += gimp_string_get_memsize (entry->name); + + return memsize; +} diff --git a/app/core/gimppalette.h b/app/core/gimppalette.h new file mode 100644 index 0000000..5bc6d8c --- /dev/null +++ b/app/core/gimppalette.h @@ -0,0 +1,103 @@ +/* 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 . + */ + +#ifndef __GIMP_PALETTE_H__ +#define __GIMP_PALETTE_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_PALETTE (gimp_palette_get_type ()) +#define GIMP_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PALETTE, GimpPalette)) +#define GIMP_PALETTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PALETTE, GimpPaletteClass)) +#define GIMP_IS_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PALETTE)) +#define GIMP_IS_PALETTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PALETTE)) +#define GIMP_PALETTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PALETTE, GimpPaletteClass)) + + +struct _GimpPaletteEntry +{ + GimpRGB color; + gchar *name; + + /* EEK */ + gint position; +}; + + +typedef struct _GimpPaletteClass GimpPaletteClass; + +struct _GimpPalette +{ + GimpData parent_instance; + + GList *colors; + gint n_colors; + + gint n_columns; +}; + +struct _GimpPaletteClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_palette_get_type (void) G_GNUC_CONST; + +GimpData * gimp_palette_new (GimpContext *context, + const gchar *name); +GimpData * gimp_palette_get_standard (GimpContext *context); + +GList * gimp_palette_get_colors (GimpPalette *palette); +gint gimp_palette_get_n_colors (GimpPalette *palette); + +void gimp_palette_move_entry (GimpPalette *palette, + GimpPaletteEntry *entry, + gint position); + +GimpPaletteEntry * gimp_palette_add_entry (GimpPalette *palette, + gint position, + const gchar *name, + const GimpRGB *color); +void gimp_palette_delete_entry (GimpPalette *palette, + GimpPaletteEntry *entry); + +gboolean gimp_palette_set_entry (GimpPalette *palette, + gint position, + const gchar *name, + const GimpRGB *color); +gboolean gimp_palette_set_entry_color (GimpPalette *palette, + gint position, + const GimpRGB *color); +gboolean gimp_palette_set_entry_name (GimpPalette *palette, + gint position, + const gchar *name); +GimpPaletteEntry * gimp_palette_get_entry (GimpPalette *palette, + gint position); + +void gimp_palette_set_columns (GimpPalette *palette, + gint columns); +gint gimp_palette_get_columns (GimpPalette *palette); + +GimpPaletteEntry * gimp_palette_find_entry (GimpPalette *palette, + const GimpRGB *color, + GimpPaletteEntry *start_from); + + +#endif /* __GIMP_PALETTE_H__ */ diff --git a/app/core/gimppalettemru.c b/app/core/gimppalettemru.c new file mode 100644 index 0000000..f68c469 --- /dev/null +++ b/app/core/gimppalettemru.c @@ -0,0 +1,230 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppalettemru.c + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimppalettemru.h" + +#include "gimp-intl.h" + + +#define MAX_N_COLORS 256 +#define RGBA_EPSILON 1e-4 + +enum +{ + COLOR_HISTORY = 1 +}; + + +G_DEFINE_TYPE (GimpPaletteMru, gimp_palette_mru, GIMP_TYPE_PALETTE) + +#define parent_class gimp_palette_mru_parent_class + + +static void +gimp_palette_mru_class_init (GimpPaletteMruClass *klass) +{ +} + +static void +gimp_palette_mru_init (GimpPaletteMru *palette) +{ +} + + +/* public functions */ + +GimpData * +gimp_palette_mru_new (const gchar *name) +{ + GimpPaletteMru *palette; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (*name != '\0', NULL); + + palette = g_object_new (GIMP_TYPE_PALETTE_MRU, + "name", name, + "mime-type", "application/x-gimp-palette", + NULL); + + return GIMP_DATA (palette); +} + +void +gimp_palette_mru_load (GimpPaletteMru *mru, + GFile *file) +{ + GimpPalette *palette; + GScanner *scanner; + GTokenType token; + + g_return_if_fail (GIMP_IS_PALETTE_MRU (mru)); + g_return_if_fail (G_IS_FILE (file)); + + palette = GIMP_PALETTE (mru); + + scanner = gimp_scanner_new_gfile (file, NULL); + if (! scanner) + return; + + g_scanner_scope_add_symbol (scanner, 0, "color-history", + GINT_TO_POINTER (COLOR_HISTORY)); + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + if (scanner->value.v_symbol == GINT_TO_POINTER (COLOR_HISTORY)) + { + while (g_scanner_peek_next_token (scanner) == G_TOKEN_LEFT_PAREN) + { + GimpRGB color; + + if (! gimp_scanner_parse_color (scanner, &color)) + goto end; + + gimp_palette_add_entry (palette, -1, + _("History Color"), &color); + + if (gimp_palette_get_n_colors (palette) == MAX_N_COLORS) + goto end; + } + } + token = G_TOKEN_RIGHT_PAREN; + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + end: + gimp_scanner_destroy (scanner); +} + +void +gimp_palette_mru_save (GimpPaletteMru *mru, + GFile *file) +{ + GimpPalette *palette; + GimpConfigWriter *writer; + GList *list; + + g_return_if_fail (GIMP_IS_PALETTE_MRU (mru)); + g_return_if_fail (G_IS_FILE (file)); + + writer = gimp_config_writer_new_gfile (file, + TRUE, + "GIMP colorrc\n\n" + "This file holds a list of " + "recently used colors.", + NULL); + if (! writer) + return; + + palette = GIMP_PALETTE (mru); + + gimp_config_writer_open (writer, "color-history"); + + for (list = palette->colors; list; list = g_list_next (list)) + { + GimpPaletteEntry *entry = list->data; + gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr (buf[0], G_ASCII_DTOSTR_BUF_SIZE, entry->color.r); + g_ascii_dtostr (buf[1], G_ASCII_DTOSTR_BUF_SIZE, entry->color.g); + g_ascii_dtostr (buf[2], G_ASCII_DTOSTR_BUF_SIZE, entry->color.b); + g_ascii_dtostr (buf[3], G_ASCII_DTOSTR_BUF_SIZE, entry->color.a); + + gimp_config_writer_open (writer, "color-rgba"); + gimp_config_writer_printf (writer, "%s %s %s %s", + buf[0], buf[1], buf[2], buf[3]); + gimp_config_writer_close (writer); + } + + gimp_config_writer_close (writer); + + gimp_config_writer_finish (writer, "end of colorrc", NULL); +} + +void +gimp_palette_mru_add (GimpPaletteMru *mru, + const GimpRGB *color) +{ + GimpPalette *palette; + GList *list; + + g_return_if_fail (GIMP_IS_PALETTE_MRU (mru)); + g_return_if_fail (color != NULL); + + palette = GIMP_PALETTE (mru); + + /* is the added color already there? */ + for (list = gimp_palette_get_colors (palette); + list; + list = g_list_next (list)) + { + GimpPaletteEntry *entry = list->data; + + if (gimp_rgba_distance (&entry->color, color) < RGBA_EPSILON) + { + gimp_palette_move_entry (palette, entry, 0); + + /* Even though they are nearly the same color, let's make them + * exactly equal. + */ + gimp_palette_set_entry_color (palette, 0, color); + + return; + } + } + + if (gimp_palette_get_n_colors (palette) == MAX_N_COLORS) + { + gimp_palette_delete_entry (palette, + gimp_palette_get_entry (palette, + MAX_N_COLORS - 1)); + } + + gimp_palette_add_entry (palette, 0, _("History Color"), color); +} diff --git a/app/core/gimppalettemru.h b/app/core/gimppalettemru.h new file mode 100644 index 0000000..0525553 --- /dev/null +++ b/app/core/gimppalettemru.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppalettemru.h + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PALETTE_MRU_H__ +#define __GIMP_PALETTE_MRU_H__ + + +#include "gimppalette.h" + + +#define GIMP_TYPE_PALETTE_MRU (gimp_palette_mru_get_type ()) +#define GIMP_PALETTE_MRU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PALETTE_MRU, GimpPaletteMru)) +#define GIMP_PALETTE_MRU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PALETTE_MRU, GimpPaletteMruClass)) +#define GIMP_IS_PALETTE_MRU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PALETTE_MRU)) +#define GIMP_IS_PALETTE_MRU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PALETTE_MRU)) +#define GIMP_PALETTE_MRU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PALETTE_MRU, GimpPaletteMruClass)) + + +typedef struct _GimpPaletteMruClass GimpPaletteMruClass; + +struct _GimpPaletteMru +{ + GimpPalette parent_instance; +}; + +struct _GimpPaletteMruClass +{ + GimpPaletteClass parent_class; +}; + + +GType gimp_palette_mru_get_type (void) G_GNUC_CONST; + +GimpData * gimp_palette_mru_new (const gchar *name); + +void gimp_palette_mru_load (GimpPaletteMru *mru, + GFile *file); +void gimp_palette_mru_save (GimpPaletteMru *mru, + GFile *file); + +void gimp_palette_mru_add (GimpPaletteMru *mru, + const GimpRGB *color); + + +#endif /* __GIMP_PALETTE_MRU_H__ */ diff --git a/app/core/gimpparamspecs-desc.c b/app/core/gimpparamspecs-desc.c new file mode 100644 index 0000000..a59f14f --- /dev/null +++ b/app/core/gimpparamspecs-desc.c @@ -0,0 +1,193 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpparamspecs.h" +#include "gimpparamspecs-desc.h" + + +static inline const gchar * +gimp_param_spec_get_blurb (GParamSpec *pspec) +{ + const gchar *blurb = g_param_spec_get_blurb (pspec); + + return blurb ? blurb : ""; +} + +static gchar * +gimp_param_spec_boolean_desc (GParamSpec *pspec) +{ + const gchar *blurb = gimp_param_spec_get_blurb (pspec); + + return g_strconcat (blurb, " (TRUE or FALSE)", NULL); +} + +static gchar * +gimp_param_spec_int32_desc (GParamSpec *pspec) +{ + GParamSpecInt *ispec = G_PARAM_SPEC_INT (pspec); + const gchar *blurb = gimp_param_spec_get_blurb (pspec); + + if (ispec->minimum == G_MININT32 && ispec->maximum == G_MAXINT32) + return g_strdup (blurb); + + if (ispec->minimum == G_MININT32) + return g_strdup_printf ("%s (%s <= %d)", blurb, + g_param_spec_get_name (pspec), + ispec->maximum); + + if (ispec->maximum == G_MAXINT32) + return g_strdup_printf ("%s (%s >= %d)", blurb, + g_param_spec_get_name (pspec), + ispec->minimum); + + return g_strdup_printf ("%s (%d <= %s <= %d)", blurb, + ispec->minimum, + g_param_spec_get_name (pspec), + ispec->maximum); +} + +static gchar * +gimp_param_spec_double_desc (GParamSpec *pspec) +{ + GParamSpecDouble *dspec = G_PARAM_SPEC_DOUBLE (pspec); + const gchar *blurb = gimp_param_spec_get_blurb (pspec); + + if (dspec->minimum == - G_MAXDOUBLE && dspec->maximum == G_MAXDOUBLE) + return g_strdup (blurb); + + if (dspec->minimum == - G_MAXDOUBLE) + return g_strdup_printf ("%s (%s <= %g)", blurb, + g_param_spec_get_name (pspec), + dspec->maximum); + + if (dspec->maximum == G_MAXDOUBLE) + return g_strdup_printf ("%s (%s >= %g)", blurb, + g_param_spec_get_name (pspec), + dspec->minimum); + + return g_strdup_printf ("%s (%g <= %s <= %g)", blurb, + dspec->minimum, + g_param_spec_get_name (pspec), + dspec->maximum); +} + +static gchar * +gimp_param_spec_enum_desc (GParamSpec *pspec) +{ + const gchar *blurb = gimp_param_spec_get_blurb (pspec); + GString *str = g_string_new (blurb); + GEnumClass *enum_class = g_type_class_peek (pspec->value_type); + GEnumValue *enum_value; + GSList *excluded; + gint i, n; + + if (GIMP_IS_PARAM_SPEC_ENUM (pspec)) + excluded = GIMP_PARAM_SPEC_ENUM (pspec)->excluded_values; + else + excluded = NULL; + + g_string_append (str, " { "); + + for (i = 0, n = 0, enum_value = enum_class->values; + i < enum_class->n_values; + i++, enum_value++) + { + GSList *list; + gchar *name; + + for (list = excluded; list; list = list->next) + { + gint value = GPOINTER_TO_INT (list->data); + + if (value == enum_value->value) + break; + } + + if (list) + continue; + + if (n > 0) + g_string_append (str, ", "); + + if (G_LIKELY (g_str_has_prefix (enum_value->value_name, "GIMP_"))) + name = gimp_canonicalize_identifier (enum_value->value_name + 5); + else + name = gimp_canonicalize_identifier (enum_value->value_name); + + g_string_append (str, name); + g_free (name); + + g_string_append_printf (str, " (%d)", enum_value->value); + + n++; + } + + g_string_append (str, " }"); + + return g_string_free (str, FALSE); +} + +/** + * gimp_param_spec_get_desc: + * @pspec: a #GParamSpec + * + * This function creates a description of the passed @pspec, which is + * suitable for use in the PDB. Actually, it currently only deals with + * parameter types used in the PDB and should not be used for anything + * else. + * + * Return value: A newly allocated string describing the parameter. + */ +gchar * +gimp_param_spec_get_desc (GParamSpec *pspec) +{ + g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); + + if (GIMP_IS_PARAM_SPEC_UNIT (pspec)) + { + } + else if (GIMP_IS_PARAM_SPEC_INT32 (pspec)) + { + return gimp_param_spec_int32_desc (pspec); + } + else + { + switch (G_TYPE_FUNDAMENTAL (pspec->value_type)) + { + case G_TYPE_BOOLEAN: + return gimp_param_spec_boolean_desc (pspec); + + case G_TYPE_DOUBLE: + return gimp_param_spec_double_desc (pspec); + + case G_TYPE_ENUM: + return gimp_param_spec_enum_desc (pspec); + } + } + + return g_strdup (g_param_spec_get_blurb (pspec)); +} diff --git a/app/core/gimpparamspecs-desc.h b/app/core/gimpparamspecs-desc.h new file mode 100644 index 0000000..1e6ea2d --- /dev/null +++ b/app/core/gimpparamspecs-desc.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 . + */ + +#ifndef __GIMP_PARAM_SPECS_DESC_H__ +#define __GIMP_PARAM_SPECS_DESC_H__ + + +gchar * gimp_param_spec_get_desc (GParamSpec *pspec); + + +#endif /* __GIMP_PARAM_SPECS_DESC_H__ */ diff --git a/app/core/gimpparamspecs-duplicate.c b/app/core/gimpparamspecs-duplicate.c new file mode 100644 index 0000000..ff0587b --- /dev/null +++ b/app/core/gimpparamspecs-duplicate.c @@ -0,0 +1,269 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpparamspecs-duplicate.c + * Copyright (C) 2008-2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "gimpparamspecs.h" +#include "gimpparamspecs-duplicate.h" + + +/* FIXME: this code is not yet general as it should be (gegl tool only atm) */ + +GParamSpec * +gimp_param_spec_duplicate (GParamSpec *pspec) +{ + GParamSpec *copy = NULL; + GParamFlags flags; + + g_return_val_if_fail (pspec != NULL, NULL); + + flags = pspec->flags; + + if (! gimp_gegl_param_spec_has_key (pspec, "role", "output-extent")) + flags |= GIMP_CONFIG_PARAM_SERIALIZE; + + if (G_IS_PARAM_SPEC_STRING (pspec)) + { + GParamSpecString *spec = G_PARAM_SPEC_STRING (pspec); + + if (GEGL_IS_PARAM_SPEC_FILE_PATH (pspec)) + { + copy = gimp_param_spec_config_path (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + GIMP_CONFIG_PATH_FILE, + spec->default_value, + flags); + } + else + { + copy = g_param_spec_string (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->default_value, + flags); + } + } + else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) + { + GParamSpecBoolean *spec = G_PARAM_SPEC_BOOLEAN (pspec); + + copy = g_param_spec_boolean (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->default_value, + flags); + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) + { + GParamSpecEnum *spec = G_PARAM_SPEC_ENUM (pspec); + + copy = g_param_spec_enum (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + G_TYPE_FROM_CLASS (spec->enum_class), + spec->default_value, + flags); + } + else if (GEGL_IS_PARAM_SPEC_DOUBLE (pspec)) + { + GeglParamSpecDouble *gspec = GEGL_PARAM_SPEC_DOUBLE (pspec); + GParamSpecDouble *spec = G_PARAM_SPEC_DOUBLE (pspec); + + copy = gegl_param_spec_double (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + gspec->ui_minimum, + gspec->ui_maximum, + gspec->ui_gamma, + flags); + gegl_param_spec_double_set_steps (GEGL_PARAM_SPEC_DOUBLE (copy), + gspec->ui_step_small, + gspec->ui_step_big); + gegl_param_spec_double_set_digits (GEGL_PARAM_SPEC_DOUBLE (copy), + gspec->ui_digits); + } + else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) + { + GParamSpecDouble *spec = G_PARAM_SPEC_DOUBLE (pspec); + + copy = g_param_spec_double (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + flags); + } + else if (G_IS_PARAM_SPEC_FLOAT (pspec)) + { + GParamSpecFloat *spec = G_PARAM_SPEC_FLOAT (pspec); + + copy = g_param_spec_float (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + flags); + } + else if (GEGL_IS_PARAM_SPEC_INT (pspec)) + { + GeglParamSpecInt *gspec = GEGL_PARAM_SPEC_INT (pspec); + GParamSpecInt *spec = G_PARAM_SPEC_INT (pspec); + + copy = gegl_param_spec_int (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + gspec->ui_minimum, + gspec->ui_maximum, + gspec->ui_gamma, + flags); + gegl_param_spec_int_set_steps (GEGL_PARAM_SPEC_INT (copy), + gspec->ui_step_small, + gspec->ui_step_big); + } + else if (GEGL_IS_PARAM_SPEC_SEED (pspec)) + { + GParamSpecUInt *spec = G_PARAM_SPEC_UINT (pspec); + GeglParamSpecSeed *gspec = GEGL_PARAM_SPEC_SEED (pspec); + + copy = gegl_param_spec_seed (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + pspec->flags | + GIMP_CONFIG_PARAM_SERIALIZE); + + G_PARAM_SPEC_UINT (copy)->minimum = spec->minimum; + G_PARAM_SPEC_UINT (copy)->maximum = spec->maximum; + + GEGL_PARAM_SPEC_SEED (copy)->ui_minimum = gspec->ui_minimum; + GEGL_PARAM_SPEC_SEED (copy)->ui_maximum = gspec->ui_maximum; + } + else if (G_IS_PARAM_SPEC_INT (pspec)) + { + GParamSpecInt *spec = G_PARAM_SPEC_INT (pspec); + + copy = g_param_spec_int (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + flags); + } + else if (G_IS_PARAM_SPEC_UINT (pspec)) + { + GParamSpecUInt *spec = G_PARAM_SPEC_UINT (pspec); + + copy = g_param_spec_uint (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + spec->minimum, + spec->maximum, + spec->default_value, + flags); + } + else if (GIMP_IS_PARAM_SPEC_RGB (pspec)) + { + GValue value = G_VALUE_INIT; + GimpRGB color; + + g_value_init (&value, GIMP_TYPE_RGB); + g_param_value_set_default (pspec, &value); + gimp_value_get_rgb (&value, &color); + g_value_unset (&value); + + copy = gimp_param_spec_rgb (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + gimp_param_spec_rgb_has_alpha (pspec), + &color, + flags); + } + else if (GEGL_IS_PARAM_SPEC_COLOR (pspec)) + { + GeglColor *gegl_color; + GimpRGB gimp_color; + gdouble r = 0.0; + gdouble g = 0.0; + gdouble b = 0.0; + gdouble a = 1.0; + GValue value = G_VALUE_INIT; + + g_value_init (&value, GEGL_TYPE_COLOR); + g_param_value_set_default (pspec, &value); + + gegl_color = g_value_get_object (&value); + if (gegl_color) + gegl_color_get_rgba (gegl_color, &r, &g, &b, &a); + + gimp_rgba_set (&gimp_color, r, g, b, a); + + g_value_unset (&value); + + copy = gimp_param_spec_rgb (pspec->name, + g_param_spec_get_nick (pspec), + g_param_spec_get_blurb (pspec), + TRUE, + &gimp_color, + flags); + } + else if (G_IS_PARAM_SPEC_OBJECT (pspec) || + G_IS_PARAM_SPEC_POINTER (pspec)) + { + /* silently ignore object properties */ + } + else + { + g_warning ("%s: not supported: %s (%s)\n", G_STRFUNC, + g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name); + } + + if (copy) + { + GQuark quark = g_quark_from_static_string ("gegl-property-keys"); + GHashTable *keys = g_param_spec_get_qdata (pspec, quark); + + if (keys) + g_param_spec_set_qdata_full (copy, quark, g_hash_table_ref (keys), + (GDestroyNotify) g_hash_table_unref); + } + + return copy; +} diff --git a/app/core/gimpparamspecs-duplicate.h b/app/core/gimpparamspecs-duplicate.h new file mode 100644 index 0000000..2bb1432 --- /dev/null +++ b/app/core/gimpparamspecs-duplicate.h @@ -0,0 +1,28 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpparamspecs-duplicate.h + * Copyright (C) 2008-2009 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PARAM_SPECS_DUPLICATE_H__ +#define __GIMP_PARAM_SPECS_DUPLICATE_H__ + + +GParamSpec * gimp_param_spec_duplicate (GParamSpec *pspec); + + +#endif /* __GIMP_PARAM_SPECS_DUPLICATE_H__ */ diff --git a/app/core/gimpparamspecs.c b/app/core/gimpparamspecs.c new file mode 100644 index 0000000..17ba6a5 --- /dev/null +++ b/app/core/gimpparamspecs.c @@ -0,0 +1,2925 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpimage.h" +#include "gimplayer.h" +#include "gimplayermask.h" +#include "gimpparamspecs.h" +#include "gimpselection.h" + +#include "vectors/gimpvectors.h" + + +/* + * GIMP_TYPE_INT32 + */ + +GType +gimp_int32_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpInt32", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT32 + */ + +static void gimp_param_int32_class_init (GParamSpecClass *klass); +static void gimp_param_int32_init (GParamSpec *pspec); + +GType +gimp_param_int32_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int32_class_init, + NULL, NULL, + sizeof (GimpParamSpecInt32), + 0, + (GInstanceInitFunc) gimp_param_int32_init + }; + + type = g_type_register_static (G_TYPE_PARAM_INT, + "GimpParamInt32", &info, 0); + } + + return type; +} + +static void +gimp_param_int32_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT32; +} + +static void +gimp_param_int32_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int32 (const gchar *name, + const gchar *nick, + const gchar *blurb, + gint minimum, + gint maximum, + gint default_value, + GParamFlags flags) +{ + GParamSpecInt *ispec; + + g_return_val_if_fail (minimum >= G_MININT32, NULL); + g_return_val_if_fail (maximum <= G_MAXINT32, NULL); + g_return_val_if_fail (default_value >= minimum && + default_value <= maximum, NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_INT32, + name, nick, blurb, flags); + + ispec->minimum = minimum; + ispec->maximum = maximum; + ispec->default_value = default_value; + + return G_PARAM_SPEC (ispec); +} + + +/* + * GIMP_TYPE_INT16 + */ + +GType +gimp_int16_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpInt16", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT16 + */ + +static void gimp_param_int16_class_init (GParamSpecClass *klass); +static void gimp_param_int16_init (GParamSpec *pspec); + +GType +gimp_param_int16_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int16_class_init, + NULL, NULL, + sizeof (GimpParamSpecInt16), + 0, + (GInstanceInitFunc) gimp_param_int16_init + }; + + type = g_type_register_static (G_TYPE_PARAM_INT, + "GimpParamInt16", &info, 0); + } + + return type; +} + +static void +gimp_param_int16_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT16; +} + +static void +gimp_param_int16_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int16 (const gchar *name, + const gchar *nick, + const gchar *blurb, + gint minimum, + gint maximum, + gint default_value, + GParamFlags flags) +{ + GParamSpecInt *ispec; + + g_return_val_if_fail (minimum >= G_MININT16, NULL); + g_return_val_if_fail (maximum <= G_MAXINT16, NULL); + g_return_val_if_fail (default_value >= minimum && + default_value <= maximum, NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_INT16, + name, nick, blurb, flags); + + ispec->minimum = minimum; + ispec->maximum = maximum; + ispec->default_value = default_value; + + return G_PARAM_SPEC (ispec); +} + + +/* + * GIMP_TYPE_INT8 + */ + +GType +gimp_int8_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_UINT, "GimpInt8", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT8 + */ + +static void gimp_param_int8_class_init (GParamSpecClass *klass); +static void gimp_param_int8_init (GParamSpec *pspec); + +GType +gimp_param_int8_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int8_class_init, + NULL, NULL, + sizeof (GimpParamSpecInt8), + 0, + (GInstanceInitFunc) gimp_param_int8_init + }; + + type = g_type_register_static (G_TYPE_PARAM_UINT, + "GimpParamInt8", &info, 0); + } + + return type; +} + +static void +gimp_param_int8_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT8; +} + +static void +gimp_param_int8_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int8 (const gchar *name, + const gchar *nick, + const gchar *blurb, + guint minimum, + guint maximum, + guint default_value, + GParamFlags flags) +{ + GParamSpecInt *ispec; + + g_return_val_if_fail (maximum <= G_MAXUINT8, NULL); + g_return_val_if_fail (default_value >= minimum && + default_value <= maximum, NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_INT8, + name, nick, blurb, flags); + + ispec->minimum = minimum; + ispec->maximum = maximum; + ispec->default_value = default_value; + + return G_PARAM_SPEC (ispec); +} + + +/* + * GIMP_TYPE_PARAM_STRING + */ + +static void gimp_param_string_class_init (GParamSpecClass *klass); +static void gimp_param_string_init (GParamSpec *pspec); +static gboolean gimp_param_string_validate (GParamSpec *pspec, + GValue *value); + +static GParamSpecClass * gimp_param_string_parent_class = NULL; + +GType +gimp_param_string_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_string_class_init, + NULL, NULL, + sizeof (GimpParamSpecString), + 0, + (GInstanceInitFunc) gimp_param_string_init + }; + + type = g_type_register_static (G_TYPE_PARAM_STRING, + "GimpParamString", &info, 0); + } + + return type; +} + +static void +gimp_param_string_class_init (GParamSpecClass *klass) +{ + gimp_param_string_parent_class = g_type_class_peek_parent (klass); + + klass->value_type = G_TYPE_STRING; + klass->value_validate = gimp_param_string_validate; +} + +static void +gimp_param_string_init (GParamSpec *pspec) +{ + GimpParamSpecString *sspec = GIMP_PARAM_SPEC_STRING (pspec); + + G_PARAM_SPEC_STRING (pspec)->ensure_non_null = TRUE; + + sspec->allow_non_utf8 = FALSE; + sspec->non_empty = FALSE; +} + +static gboolean +gimp_param_string_validate (GParamSpec *pspec, + GValue *value) +{ + GimpParamSpecString *sspec = GIMP_PARAM_SPEC_STRING (pspec); + gchar *string = value->data[0].v_pointer; + + if (gimp_param_string_parent_class->value_validate (pspec, value)) + return TRUE; + + if (string) + { + gchar *s; + + if (sspec->non_empty && ! string[0]) + { + if (!(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS)) + g_free (string); + else + value->data[1].v_uint &= ~G_VALUE_NOCOPY_CONTENTS; + + value->data[0].v_pointer = g_strdup ("none"); + return TRUE; + } + + if (! sspec->allow_non_utf8 && + ! g_utf8_validate (string, -1, (const gchar **) &s)) + { + if (value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) + { + value->data[0].v_pointer = g_strdup (string); + value->data[1].v_uint &= ~G_VALUE_NOCOPY_CONTENTS; + string = value->data[0].v_pointer; + } + + for (s = string; *s; s++) + if (*s < ' ') + *s = '?'; + + return TRUE; + } + } + else if (sspec->non_empty) + { + value->data[1].v_uint &= ~G_VALUE_NOCOPY_CONTENTS; + value->data[0].v_pointer = g_strdup ("none"); + return TRUE; + } + + return FALSE; +} + +GParamSpec * +gimp_param_spec_string (const gchar *name, + const gchar *nick, + const gchar *blurb, + gboolean allow_non_utf8, + gboolean null_ok, + gboolean non_empty, + const gchar *default_value, + GParamFlags flags) +{ + GimpParamSpecString *sspec; + + g_return_val_if_fail (! (null_ok && non_empty), NULL); + + sspec = g_param_spec_internal (GIMP_TYPE_PARAM_STRING, + name, nick, blurb, flags); + + if (sspec) + { + g_free (G_PARAM_SPEC_STRING (sspec)->default_value); + G_PARAM_SPEC_STRING (sspec)->default_value = g_strdup (default_value); + + G_PARAM_SPEC_STRING (sspec)->ensure_non_null = null_ok ? FALSE : TRUE; + + sspec->allow_non_utf8 = allow_non_utf8 ? TRUE : FALSE; + sspec->non_empty = non_empty ? TRUE : FALSE; + } + + return G_PARAM_SPEC (sspec); +} + + +/* + * GIMP_TYPE_PARAM_ENUM + */ + +static void gimp_param_enum_class_init (GParamSpecClass *klass); +static void gimp_param_enum_init (GParamSpec *pspec); +static void gimp_param_enum_finalize (GParamSpec *pspec); +static gboolean gimp_param_enum_validate (GParamSpec *pspec, + GValue *value); + +GType +gimp_param_enum_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_enum_class_init, + NULL, NULL, + sizeof (GimpParamSpecEnum), + 0, + (GInstanceInitFunc) gimp_param_enum_init + }; + + type = g_type_register_static (G_TYPE_PARAM_ENUM, + "GimpParamEnum", &info, 0); + } + + return type; +} + +static void +gimp_param_enum_class_init (GParamSpecClass *klass) +{ + klass->value_type = G_TYPE_ENUM; + klass->finalize = gimp_param_enum_finalize; + klass->value_validate = gimp_param_enum_validate; +} + +static void +gimp_param_enum_init (GParamSpec *pspec) +{ + GimpParamSpecEnum *espec = GIMP_PARAM_SPEC_ENUM (pspec); + + espec->excluded_values = NULL; +} + +static void +gimp_param_enum_finalize (GParamSpec *pspec) +{ + GimpParamSpecEnum *espec = GIMP_PARAM_SPEC_ENUM (pspec); + GParamSpecClass *parent_class; + + parent_class = g_type_class_peek (g_type_parent (GIMP_TYPE_PARAM_ENUM)); + + g_slist_free (espec->excluded_values); + + parent_class->finalize (pspec); +} + +static gboolean +gimp_param_enum_validate (GParamSpec *pspec, + GValue *value) +{ + GimpParamSpecEnum *espec = GIMP_PARAM_SPEC_ENUM (pspec); + GParamSpecClass *parent_class; + GSList *list; + + parent_class = g_type_class_peek (g_type_parent (GIMP_TYPE_PARAM_ENUM)); + + if (parent_class->value_validate (pspec, value)) + return TRUE; + + for (list = espec->excluded_values; list; list = g_slist_next (list)) + { + if (GPOINTER_TO_INT (list->data) == value->data[0].v_long) + { + value->data[0].v_long = G_PARAM_SPEC_ENUM (pspec)->default_value; + return TRUE; + } + } + + return FALSE; +} + +GParamSpec * +gimp_param_spec_enum (const gchar *name, + const gchar *nick, + const gchar *blurb, + GType enum_type, + gint default_value, + GParamFlags flags) +{ + GimpParamSpecEnum *espec; + GEnumClass *enum_class; + + g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL); + + enum_class = g_type_class_ref (enum_type); + + g_return_val_if_fail (g_enum_get_value (enum_class, default_value) != NULL, + NULL); + + espec = g_param_spec_internal (GIMP_TYPE_PARAM_ENUM, + name, nick, blurb, flags); + + G_PARAM_SPEC_ENUM (espec)->enum_class = enum_class; + G_PARAM_SPEC_ENUM (espec)->default_value = default_value; + G_PARAM_SPEC (espec)->value_type = enum_type; + + return G_PARAM_SPEC (espec); +} + +void +gimp_param_spec_enum_exclude_value (GimpParamSpecEnum *espec, + gint value) +{ + g_return_if_fail (GIMP_IS_PARAM_SPEC_ENUM (espec)); + g_return_if_fail (g_enum_get_value (G_PARAM_SPEC_ENUM (espec)->enum_class, + value) != NULL); + + espec->excluded_values = g_slist_prepend (espec->excluded_values, + GINT_TO_POINTER (value)); +} + + +/* + * GIMP_TYPE_IMAGE_ID + */ + +GType +gimp_image_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpImageID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_IMAGE_ID + */ + +static void gimp_param_image_id_class_init (GParamSpecClass *klass); +static void gimp_param_image_id_init (GParamSpec *pspec); +static void gimp_param_image_id_set_default (GParamSpec *pspec, + GValue *value); +static gboolean gimp_param_image_id_validate (GParamSpec *pspec, + GValue *value); +static gint gimp_param_image_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2); + +GType +gimp_param_image_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_image_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecImageID), + 0, + (GInstanceInitFunc) gimp_param_image_id_init + }; + + type = g_type_register_static (G_TYPE_PARAM_INT, + "GimpParamImageID", &info, 0); + } + + return type; +} + +static void +gimp_param_image_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_IMAGE_ID; + klass->value_set_default = gimp_param_image_id_set_default; + klass->value_validate = gimp_param_image_id_validate; + klass->values_cmp = gimp_param_image_id_values_cmp; +} + +static void +gimp_param_image_id_init (GParamSpec *pspec) +{ + GimpParamSpecImageID *ispec = GIMP_PARAM_SPEC_IMAGE_ID (pspec); + + ispec->gimp = NULL; + ispec->none_ok = FALSE; +} + +static void +gimp_param_image_id_set_default (GParamSpec *pspec, + GValue *value) +{ + value->data[0].v_int = -1; +} + +static gboolean +gimp_param_image_id_validate (GParamSpec *pspec, + GValue *value) +{ + GimpParamSpecImageID *ispec = GIMP_PARAM_SPEC_IMAGE_ID (pspec); + gint image_id = value->data[0].v_int; + GimpImage *image; + + if (ispec->none_ok && (image_id == 0 || image_id == -1)) + return FALSE; + + image = gimp_image_get_by_ID (ispec->gimp, image_id); + + if (! GIMP_IS_IMAGE (image)) + { + value->data[0].v_int = -1; + return TRUE; + } + + return FALSE; +} + +static gint +gimp_param_image_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + gint image_id1 = value1->data[0].v_int; + gint image_id2 = value2->data[0].v_int; + + /* try to return at least *something*, it's useless anyway... */ + + if (image_id1 < image_id2) + return -1; + else if (image_id1 > image_id2) + return 1; + else + return 0; +} + +GParamSpec * +gimp_param_spec_image_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecImageID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_IMAGE_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpImage * +gimp_value_get_image (const GValue *value, + Gimp *gimp) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_IMAGE_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp_image_get_by_ID (gimp, value->data[0].v_int); +} + +void +gimp_value_set_image (GValue *value, + GimpImage *image) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_IMAGE_ID (value)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + + value->data[0].v_int = image ? gimp_image_get_ID (image) : -1; +} + + +/* + * GIMP_TYPE_ITEM_ID + */ + +GType +gimp_item_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpItemID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_ITEM_ID + */ + +static void gimp_param_item_id_class_init (GParamSpecClass *klass); +static void gimp_param_item_id_init (GParamSpec *pspec); +static void gimp_param_item_id_set_default (GParamSpec *pspec, + GValue *value); +static gboolean gimp_param_item_id_validate (GParamSpec *pspec, + GValue *value); +static gint gimp_param_item_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2); + +GType +gimp_param_item_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_item_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecItemID), + 0, + (GInstanceInitFunc) gimp_param_item_id_init + }; + + type = g_type_register_static (G_TYPE_PARAM_INT, + "GimpParamItemID", &info, 0); + } + + return type; +} + +static void +gimp_param_item_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_ITEM_ID; + klass->value_set_default = gimp_param_item_id_set_default; + klass->value_validate = gimp_param_item_id_validate; + klass->values_cmp = gimp_param_item_id_values_cmp; +} + +static void +gimp_param_item_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->gimp = NULL; + ispec->item_type = GIMP_TYPE_ITEM; + ispec->none_ok = FALSE; +} + +static void +gimp_param_item_id_set_default (GParamSpec *pspec, + GValue *value) +{ + value->data[0].v_int = -1; +} + +static gboolean +gimp_param_item_id_validate (GParamSpec *pspec, + GValue *value) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + gint item_id = value->data[0].v_int; + GimpItem *item; + + if (ispec->none_ok && (item_id == 0 || item_id == -1)) + return FALSE; + + item = gimp_item_get_by_ID (ispec->gimp, item_id); + + if (! item || ! g_type_is_a (G_TYPE_FROM_INSTANCE (item), ispec->item_type)) + { + value->data[0].v_int = -1; + return TRUE; + } + else if (gimp_item_is_removed (item)) + { + value->data[0].v_int = -1; + return TRUE; + } + + return FALSE; +} + +static gint +gimp_param_item_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + gint item_id1 = value1->data[0].v_int; + gint item_id2 = value2->data[0].v_int; + + /* try to return at least *something*, it's useless anyway... */ + + if (item_id1 < item_id2) + return -1; + else if (item_id1 > item_id2) + return 1; + else + return 0; +} + +GParamSpec * +gimp_param_spec_item_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_ITEM_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok; + + return G_PARAM_SPEC (ispec); +} + +GimpItem * +gimp_value_get_item (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_ITEM_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_ITEM (item)) + return NULL; + + return item; +} + +void +gimp_value_set_item (GValue *value, + GimpItem *item) +{ + g_return_if_fail (item == NULL || GIMP_IS_ITEM (item)); + + /* FIXME remove hack as soon as bug #375864 is fixed */ + + if (GIMP_VALUE_HOLDS_ITEM_ID (value)) + { + value->data[0].v_int = item ? gimp_item_get_ID (item) : -1; + } + else if (GIMP_VALUE_HOLDS_DRAWABLE_ID (value) && + (item == NULL || GIMP_IS_DRAWABLE (item))) + { + gimp_value_set_drawable (value, GIMP_DRAWABLE (item)); + } + else if (GIMP_VALUE_HOLDS_LAYER_ID (value) && + (item == NULL || GIMP_IS_LAYER (item))) + { + gimp_value_set_layer (value, GIMP_LAYER (item)); + } + else if (GIMP_VALUE_HOLDS_CHANNEL_ID (value) && + (item == NULL || GIMP_IS_CHANNEL (item))) + { + gimp_value_set_channel (value, GIMP_CHANNEL (item)); + } + else if (GIMP_VALUE_HOLDS_LAYER_MASK_ID (value) && + (item == NULL || GIMP_IS_LAYER_MASK (item))) + { + gimp_value_set_layer_mask (value, GIMP_LAYER_MASK (item)); + } + else if (GIMP_VALUE_HOLDS_SELECTION_ID (value) && + (item == NULL || GIMP_IS_SELECTION (item))) + { + gimp_value_set_selection (value, GIMP_SELECTION (item)); + } + else if (GIMP_VALUE_HOLDS_VECTORS_ID (value) && + (item == NULL || GIMP_IS_VECTORS (item))) + { + gimp_value_set_vectors (value, GIMP_VECTORS (item)); + } + else + { + g_return_if_reached (); + } +} + + +/* + * GIMP_TYPE_DRAWABLE_ID + */ + +GType +gimp_drawable_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpDrawableID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_DRAWABLE_ID + */ + +static void gimp_param_drawable_id_class_init (GParamSpecClass *klass); +static void gimp_param_drawable_id_init (GParamSpec *pspec); + +GType +gimp_param_drawable_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_drawable_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecDrawableID), + 0, + (GInstanceInitFunc) gimp_param_drawable_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ITEM_ID, + "GimpParamDrawableID", &info, 0); + } + + return type; +} + +static void +gimp_param_drawable_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_DRAWABLE_ID; +} + +static void +gimp_param_drawable_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_DRAWABLE; +} + +GParamSpec * +gimp_param_spec_drawable_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_DRAWABLE_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpDrawable * +gimp_value_get_drawable (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_DRAWABLE_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_DRAWABLE (item)) + return NULL; + + return GIMP_DRAWABLE (item); +} + +void +gimp_value_set_drawable (GValue *value, + GimpDrawable *drawable) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_DRAWABLE_ID (value)); + g_return_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable)); + + value->data[0].v_int = drawable ? gimp_item_get_ID (GIMP_ITEM (drawable)) : -1; +} + + +/* + * GIMP_TYPE_LAYER_ID + */ + +GType +gimp_layer_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpLayerID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_LAYER_ID + */ + +static void gimp_param_layer_id_class_init (GParamSpecClass *klass); +static void gimp_param_layer_id_init (GParamSpec *pspec); + +GType +gimp_param_layer_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_layer_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecLayerID), + 0, + (GInstanceInitFunc) gimp_param_layer_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_DRAWABLE_ID, + "GimpParamLayerID", &info, 0); + } + + return type; +} + +static void +gimp_param_layer_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_LAYER_ID; +} + +static void +gimp_param_layer_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_LAYER; +} + +GParamSpec * +gimp_param_spec_layer_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_LAYER_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpLayer * +gimp_value_get_layer (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_LAYER_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_LAYER (item)) + return NULL; + + return GIMP_LAYER (item); +} + +void +gimp_value_set_layer (GValue *value, + GimpLayer *layer) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_LAYER_ID (value)); + g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer)); + + value->data[0].v_int = layer ? gimp_item_get_ID (GIMP_ITEM (layer)) : -1; +} + + +/* + * GIMP_TYPE_CHANNEL_ID + */ + +GType +gimp_channel_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpChannelID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_CHANNEL_ID + */ + +static void gimp_param_channel_id_class_init (GParamSpecClass *klass); +static void gimp_param_channel_id_init (GParamSpec *pspec); + +GType +gimp_param_channel_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_channel_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecChannelID), + 0, + (GInstanceInitFunc) gimp_param_channel_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_DRAWABLE_ID, + "GimpParamChannelID", &info, 0); + } + + return type; +} + +static void +gimp_param_channel_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_CHANNEL_ID; +} + +static void +gimp_param_channel_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_CHANNEL; +} + +GParamSpec * +gimp_param_spec_channel_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_CHANNEL_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpChannel * +gimp_value_get_channel (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_CHANNEL_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_CHANNEL (item)) + return NULL; + + return GIMP_CHANNEL (item); +} + +void +gimp_value_set_channel (GValue *value, + GimpChannel *channel) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_CHANNEL_ID (value)); + g_return_if_fail (channel == NULL || GIMP_IS_CHANNEL (channel)); + + value->data[0].v_int = channel ? gimp_item_get_ID (GIMP_ITEM (channel)) : -1; +} + + +/* + * GIMP_TYPE_LAYER_MASK_ID + */ + +GType +gimp_layer_mask_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpLayerMaskID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_LAYER_MASK_ID + */ + +static void gimp_param_layer_mask_id_class_init (GParamSpecClass *klass); +static void gimp_param_layer_mask_id_init (GParamSpec *pspec); + +GType +gimp_param_layer_mask_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_layer_mask_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecLayerMaskID), + 0, + (GInstanceInitFunc) gimp_param_layer_mask_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_CHANNEL_ID, + "GimpParamLayerMaskID", &info, 0); + } + + return type; +} + +static void +gimp_param_layer_mask_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_LAYER_MASK_ID; +} + +static void +gimp_param_layer_mask_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_LAYER_MASK; +} + +GParamSpec * +gimp_param_spec_layer_mask_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_LAYER_MASK_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpLayerMask * +gimp_value_get_layer_mask (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_LAYER_MASK_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_LAYER_MASK (item)) + return NULL; + + return GIMP_LAYER_MASK (item); +} + +void +gimp_value_set_layer_mask (GValue *value, + GimpLayerMask *layer_mask) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_LAYER_MASK_ID (value)); + g_return_if_fail (layer_mask == NULL || GIMP_IS_LAYER_MASK (layer_mask)); + + value->data[0].v_int = layer_mask ? gimp_item_get_ID (GIMP_ITEM (layer_mask)) : -1; +} + + +/* + * GIMP_TYPE_SELECTION_ID + */ + +GType +gimp_selection_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpSelectionID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_SELECTION_ID + */ + +static void gimp_param_selection_id_class_init (GParamSpecClass *klass); +static void gimp_param_selection_id_init (GParamSpec *pspec); + +GType +gimp_param_selection_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_selection_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecSelectionID), + 0, + (GInstanceInitFunc) gimp_param_selection_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_CHANNEL_ID, + "GimpParamSelectionID", &info, 0); + } + + return type; +} + +static void +gimp_param_selection_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_SELECTION_ID; +} + +static void +gimp_param_selection_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_SELECTION; +} + +GParamSpec * +gimp_param_spec_selection_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_SELECTION_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpSelection * +gimp_value_get_selection (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_SELECTION_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_SELECTION (item)) + return NULL; + + return GIMP_SELECTION (item); +} + +void +gimp_value_set_selection (GValue *value, + GimpSelection *selection) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_SELECTION_ID (value)); + g_return_if_fail (selection == NULL || GIMP_IS_SELECTION (selection)); + + value->data[0].v_int = selection ? gimp_item_get_ID (GIMP_ITEM (selection)) : -1; +} + + +/* + * GIMP_TYPE_VECTORS_ID + */ + +GType +gimp_vectors_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpVectorsID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_VECTORS_ID + */ + +static void gimp_param_vectors_id_class_init (GParamSpecClass *klass); +static void gimp_param_vectors_id_init (GParamSpec *pspec); + +GType +gimp_param_vectors_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_vectors_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecVectorsID), + 0, + (GInstanceInitFunc) gimp_param_vectors_id_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ITEM_ID, + "GimpParamVectorsID", &info, 0); + } + + return type; +} + +static void +gimp_param_vectors_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_VECTORS_ID; +} + +static void +gimp_param_vectors_id_init (GParamSpec *pspec) +{ + GimpParamSpecItemID *ispec = GIMP_PARAM_SPEC_ITEM_ID (pspec); + + ispec->item_type = GIMP_TYPE_VECTORS; +} + +GParamSpec * +gimp_param_spec_vectors_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecItemID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_VECTORS_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpVectors * +gimp_value_get_vectors (const GValue *value, + Gimp *gimp) +{ + GimpItem *item; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_VECTORS_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + item = gimp_item_get_by_ID (gimp, value->data[0].v_int); + + if (item && ! GIMP_IS_VECTORS (item)) + return NULL; + + return GIMP_VECTORS (item); +} + +void +gimp_value_set_vectors (GValue *value, + GimpVectors *vectors) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_VECTORS_ID (value)); + g_return_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors)); + + value->data[0].v_int = vectors ? gimp_item_get_ID (GIMP_ITEM (vectors)) : -1; +} + + +/* + * GIMP_TYPE_DISPLAY_ID + */ + +GType +gimp_display_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = { 0, }; + + type = g_type_register_static (G_TYPE_INT, "GimpDisplayID", &info, 0); + } + + return type; +} + + +/* + * GIMP_TYPE_PARAM_DISPLAY_ID + */ + +static void gimp_param_display_id_class_init (GParamSpecClass *klass); +static void gimp_param_display_id_init (GParamSpec *pspec); +static void gimp_param_display_id_set_default (GParamSpec *pspec, + GValue *value); +static gboolean gimp_param_display_id_validate (GParamSpec *pspec, + GValue *value); +static gint gimp_param_display_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2); + +GType +gimp_param_display_id_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_display_id_class_init, + NULL, NULL, + sizeof (GimpParamSpecDisplayID), + 0, + (GInstanceInitFunc) gimp_param_display_id_init + }; + + type = g_type_register_static (G_TYPE_PARAM_INT, + "GimpParamDisplayID", &info, 0); + } + + return type; +} + +static void +gimp_param_display_id_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_DISPLAY_ID; + klass->value_set_default = gimp_param_display_id_set_default; + klass->value_validate = gimp_param_display_id_validate; + klass->values_cmp = gimp_param_display_id_values_cmp; +} + +static void +gimp_param_display_id_init (GParamSpec *pspec) +{ + GimpParamSpecDisplayID *ispec = GIMP_PARAM_SPEC_DISPLAY_ID (pspec); + + ispec->gimp = NULL; + ispec->none_ok = FALSE; +} + +static void +gimp_param_display_id_set_default (GParamSpec *pspec, + GValue *value) +{ + value->data[0].v_int = -1; +} + +static gboolean +gimp_param_display_id_validate (GParamSpec *pspec, + GValue *value) +{ + GimpParamSpecDisplayID *ispec = GIMP_PARAM_SPEC_DISPLAY_ID (pspec); + gint display_id = value->data[0].v_int; + GimpObject *display; + + if (ispec->none_ok && (display_id == 0 || display_id == -1)) + return FALSE; + + display = gimp_get_display_by_ID (ispec->gimp, display_id); + + if (! GIMP_IS_OBJECT (display)) + { + value->data[0].v_int = -1; + return TRUE; + } + + return FALSE; +} + +static gint +gimp_param_display_id_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + gint display_id1 = value1->data[0].v_int; + gint display_id2 = value2->data[0].v_int; + + /* try to return at least *something*, it's useless anyway... */ + + if (display_id1 < display_id2) + return -1; + else if (display_id1 > display_id2) + return 1; + else + return 0; +} + +GParamSpec * +gimp_param_spec_display_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecDisplayID *ispec; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + ispec = g_param_spec_internal (GIMP_TYPE_PARAM_DISPLAY_ID, + name, nick, blurb, flags); + + ispec->gimp = gimp; + ispec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (ispec); +} + +GimpObject * +gimp_value_get_display (const GValue *value, + Gimp *gimp) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_DISPLAY_ID (value), NULL); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp_get_display_by_ID (gimp, value->data[0].v_int); +} + +void +gimp_value_set_display (GValue *value, + GimpObject *display) +{ + gint id = -1; + + g_return_if_fail (GIMP_VALUE_HOLDS_DISPLAY_ID (value)); + g_return_if_fail (display == NULL || GIMP_IS_OBJECT (display)); + + if (display) + g_object_get (display, "id", &id, NULL); + + value->data[0].v_int = id; +} + + +/* + * GIMP_TYPE_ARRAY + */ + +GimpArray * +gimp_array_new (const guint8 *data, + gsize length, + gboolean static_data) +{ + GimpArray *array; + + g_return_val_if_fail ((data == NULL && length == 0) || + (data != NULL && length > 0), NULL); + + array = g_slice_new0 (GimpArray); + + array->data = static_data ? (guint8 *) data : g_memdup (data, length); + array->length = length; + array->static_data = static_data; + + return array; +} + +GimpArray * +gimp_array_copy (const GimpArray *array) +{ + if (array) + return gimp_array_new (array->data, array->length, FALSE); + + return NULL; +} + +void +gimp_array_free (GimpArray *array) +{ + if (array) + { + if (! array->static_data) + g_free (array->data); + + g_slice_free (GimpArray, array); + } +} + +GType +gimp_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpArray", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_ARRAY + */ + +static void gimp_param_array_class_init (GParamSpecClass *klass); +static void gimp_param_array_init (GParamSpec *pspec); +static gboolean gimp_param_array_validate (GParamSpec *pspec, + GValue *value); +static gint gimp_param_array_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2); + +GType +gimp_param_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_array_init + }; + + type = g_type_register_static (G_TYPE_PARAM_BOXED, + "GimpParamArray", &info, 0); + } + + return type; +} + +static void +gimp_param_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_ARRAY; + klass->value_validate = gimp_param_array_validate; + klass->values_cmp = gimp_param_array_values_cmp; +} + +static void +gimp_param_array_init (GParamSpec *pspec) +{ +} + +static gboolean +gimp_param_array_validate (GParamSpec *pspec, + GValue *value) +{ + GimpArray *array = value->data[0].v_pointer; + + if (array) + { + if ((array->data == NULL && array->length != 0) || + (array->data != NULL && array->length == 0)) + { + g_value_set_boxed (value, NULL); + return TRUE; + } + } + + return FALSE; +} + +static gint +gimp_param_array_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + GimpArray *array1 = value1->data[0].v_pointer; + GimpArray *array2 = value2->data[0].v_pointer; + + /* try to return at least *something*, it's useless anyway... */ + + if (! array1) + return array2 != NULL ? -1 : 0; + else if (! array2) + return array1 != NULL ? 1 : 0; + else if (array1->length < array2->length) + return -1; + else if (array1->length > array2->length) + return 1; + + return 0; +} + +GParamSpec * +gimp_param_spec_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +static const guint8 * +gimp_value_get_array (const GValue *value) +{ + GimpArray *array = value->data[0].v_pointer; + + if (array) + return array->data; + + return NULL; +} + +static guint8 * +gimp_value_dup_array (const GValue *value) +{ + GimpArray *array = value->data[0].v_pointer; + + if (array) + return g_memdup (array->data, array->length); + + return NULL; +} + +static void +gimp_value_set_array (GValue *value, + const guint8 *data, + gsize length) +{ + GimpArray *array = gimp_array_new (data, length, FALSE); + + g_value_take_boxed (value, array); +} + +static void +gimp_value_set_static_array (GValue *value, + const guint8 *data, + gsize length) +{ + GimpArray *array = gimp_array_new (data, length, TRUE); + + g_value_take_boxed (value, array); +} + +static void +gimp_value_take_array (GValue *value, + guint8 *data, + gsize length) +{ + GimpArray *array = gimp_array_new (data, length, TRUE); + + array->static_data = FALSE; + + g_value_take_boxed (value, array); +} + + +/* + * GIMP_TYPE_INT8_ARRAY + */ + +GType +gimp_int8_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpInt8Array", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT8_ARRAY + */ + +static void gimp_param_int8_array_class_init (GParamSpecClass *klass); +static void gimp_param_int8_array_init (GParamSpec *pspec); + +GType +gimp_param_int8_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int8_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_int8_array_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ARRAY, + "GimpParamInt8Array", &info, 0); + } + + return type; +} + +static void +gimp_param_int8_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT8_ARRAY; +} + +static void +gimp_param_int8_array_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int8_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_INT8_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const guint8 * +gimp_value_get_int8array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT8_ARRAY (value), NULL); + + return gimp_value_get_array (value); +} + +guint8 * +gimp_value_dup_int8array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT8_ARRAY (value), NULL); + + return gimp_value_dup_array (value); +} + +void +gimp_value_set_int8array (GValue *value, + const guint8 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT8_ARRAY (value)); + + gimp_value_set_array (value, data, length); +} + +void +gimp_value_set_static_int8array (GValue *value, + const guint8 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT8_ARRAY (value)); + + gimp_value_set_static_array (value, data, length); +} + +void +gimp_value_take_int8array (GValue *value, + guint8 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT8_ARRAY (value)); + + gimp_value_take_array (value, data, length); +} + + +/* + * GIMP_TYPE_INT16_ARRAY + */ + +GType +gimp_int16_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpInt16Array", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT16_ARRAY + */ + +static void gimp_param_int16_array_class_init (GParamSpecClass *klass); +static void gimp_param_int16_array_init (GParamSpec *pspec); + +GType +gimp_param_int16_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int16_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_int16_array_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ARRAY, + "GimpParamInt16Array", &info, 0); + } + + return type; +} + +static void +gimp_param_int16_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT16_ARRAY; +} + +static void +gimp_param_int16_array_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int16_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_INT16_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const gint16 * +gimp_value_get_int16array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT16_ARRAY (value), NULL); + + return (const gint16 *) gimp_value_get_array (value); +} + +gint16 * +gimp_value_dup_int16array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT16_ARRAY (value), NULL); + + return (gint16 *) gimp_value_dup_array (value); +} + +void +gimp_value_set_int16array (GValue *value, + const gint16 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT16_ARRAY (value)); + + gimp_value_set_array (value, (const guint8 *) data, + length * sizeof (gint16)); +} + +void +gimp_value_set_static_int16array (GValue *value, + const gint16 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT16_ARRAY (value)); + + gimp_value_set_static_array (value, (const guint8 *) data, + length * sizeof (gint16)); +} + +void +gimp_value_take_int16array (GValue *value, + gint16 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT16_ARRAY (value)); + + gimp_value_take_array (value, (guint8 *) data, + length * sizeof (gint16)); +} + + +/* + * GIMP_TYPE_INT32_ARRAY + */ + +GType +gimp_int32_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpInt32Array", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_INT32_ARRAY + */ + +static void gimp_param_int32_array_class_init (GParamSpecClass *klass); +static void gimp_param_int32_array_init (GParamSpec *pspec); + +GType +gimp_param_int32_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_int32_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_int32_array_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ARRAY, + "GimpParamInt32Array", &info, 0); + } + + return type; +} + +static void +gimp_param_int32_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_INT32_ARRAY; +} + +static void +gimp_param_int32_array_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_int32_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_INT32_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const gint32 * +gimp_value_get_int32array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT32_ARRAY (value), NULL); + + return (const gint32 *) gimp_value_get_array (value); +} + +gint32 * +gimp_value_dup_int32array (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_INT32_ARRAY (value), NULL); + + return (gint32 *) gimp_value_dup_array (value); +} + +void +gimp_value_set_int32array (GValue *value, + const gint32 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT32_ARRAY (value)); + + gimp_value_set_array (value, (const guint8 *) data, + length * sizeof (gint32)); +} + +void +gimp_value_set_static_int32array (GValue *value, + const gint32 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT32_ARRAY (value)); + + gimp_value_set_static_array (value, (const guint8 *) data, + length * sizeof (gint32)); +} + +void +gimp_value_take_int32array (GValue *value, + gint32 *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_INT32_ARRAY (value)); + + gimp_value_take_array (value, (guint8 *) data, + length * sizeof (gint32)); +} + + +/* + * GIMP_TYPE_FLOAT_ARRAY + */ + +GType +gimp_float_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpFloatArray", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_FLOAT_ARRAY + */ + +static void gimp_param_float_array_class_init (GParamSpecClass *klass); +static void gimp_param_float_array_init (GParamSpec *pspec); + +GType +gimp_param_float_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_float_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_float_array_init + }; + + type = g_type_register_static (GIMP_TYPE_PARAM_ARRAY, + "GimpParamFloatArray", &info, 0); + } + + return type; +} + +static void +gimp_param_float_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_FLOAT_ARRAY; +} + +static void +gimp_param_float_array_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_float_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_FLOAT_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const gdouble * +gimp_value_get_floatarray (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value), NULL); + + return (const gdouble *) gimp_value_get_array (value); +} + +gdouble * +gimp_value_dup_floatarray (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value), NULL); + + return (gdouble *) gimp_value_dup_array (value); +} + +void +gimp_value_set_floatarray (GValue *value, + const gdouble *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value)); + + gimp_value_set_array (value, (const guint8 *) data, + length * sizeof (gdouble)); +} + +void +gimp_value_set_static_floatarray (GValue *value, + const gdouble *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value)); + + gimp_value_set_static_array (value, (const guint8 *) data, + length * sizeof (gdouble)); +} + +void +gimp_value_take_floatarray (GValue *value, + gdouble *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value)); + + gimp_value_take_array (value, (guint8 *) data, + length * sizeof (gdouble)); +} + + +/* + * GIMP_TYPE_STRING_ARRAY + */ + +GimpArray * +gimp_string_array_new (const gchar **data, + gsize length, + gboolean static_data) +{ + GimpArray *array; + + g_return_val_if_fail ((data == NULL && length == 0) || + (data != NULL && length > 0), NULL); + + array = g_slice_new0 (GimpArray); + + if (! static_data) + { + gchar **tmp = g_new (gchar *, length); + gint i; + + for (i = 0; i < length; i++) + tmp[i] = g_strdup (data[i]); + + array->data = (guint8 *) tmp; + } + else + { + array->data = (guint8 *) data; + } + + array->length = length; + array->static_data = static_data; + + return array; +} + +GimpArray * +gimp_string_array_copy (const GimpArray *array) +{ + if (array) + return gimp_string_array_new ((const gchar **) array->data, + array->length, FALSE); + + return NULL; +} + +void +gimp_string_array_free (GimpArray *array) +{ + if (array) + { + if (! array->static_data) + { + gchar **tmp = (gchar **) array->data; + gint i; + + for (i = 0; i < array->length; i++) + g_free (tmp[i]); + + g_free (array->data); + } + + g_slice_free (GimpArray, array); + } +} + +GType +gimp_string_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpStringArray", + (GBoxedCopyFunc) gimp_string_array_copy, + (GBoxedFreeFunc) gimp_string_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_STRING_ARRAY + */ + +static void gimp_param_string_array_class_init (GParamSpecClass *klass); +static void gimp_param_string_array_init (GParamSpec *pspec); +static gboolean gimp_param_string_array_validate (GParamSpec *pspec, + GValue *value); +static gint gimp_param_string_array_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2); + +GType +gimp_param_string_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_string_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_string_array_init + }; + + type = g_type_register_static (G_TYPE_PARAM_BOXED, + "GimpParamStringArray", &info, 0); + } + + return type; +} + +static void +gimp_param_string_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_STRING_ARRAY; + klass->value_validate = gimp_param_string_array_validate; + klass->values_cmp = gimp_param_string_array_values_cmp; +} + +static void +gimp_param_string_array_init (GParamSpec *pspec) +{ +} + +static gboolean +gimp_param_string_array_validate (GParamSpec *pspec, + GValue *value) +{ + GimpArray *array = value->data[0].v_pointer; + + if (array) + { + if ((array->data == NULL && array->length != 0) || + (array->data != NULL && array->length == 0)) + { + g_value_set_boxed (value, NULL); + return TRUE; + } + } + + return FALSE; +} + +static gint +gimp_param_string_array_values_cmp (GParamSpec *pspec, + const GValue *value1, + const GValue *value2) +{ + GimpArray *array1 = value1->data[0].v_pointer; + GimpArray *array2 = value2->data[0].v_pointer; + + /* try to return at least *something*, it's useless anyway... */ + + if (! array1) + return array2 != NULL ? -1 : 0; + else if (! array2) + return array1 != NULL ? 1 : 0; + else if (array1->length < array2->length) + return -1; + else if (array1->length > array2->length) + return 1; + + return 0; +} + +GParamSpec * +gimp_param_spec_string_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecStringArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_STRING_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const gchar ** +gimp_value_get_stringarray (const GValue *value) +{ + GimpArray *array; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_STRING_ARRAY (value), NULL); + + array = value->data[0].v_pointer; + + if (array) + return (const gchar **) array->data; + + return NULL; +} + +gchar ** +gimp_value_dup_stringarray (const GValue *value) +{ + GimpArray *array; + + g_return_val_if_fail (GIMP_VALUE_HOLDS_STRING_ARRAY (value), NULL); + + array = value->data[0].v_pointer; + + if (array) + { + gchar **ret = g_memdup (array->data, array->length * sizeof (gchar *)); + gint i; + + for (i = 0; i < array->length; i++) + ret[i] = g_strdup (ret[i]); + + return ret; + } + + return NULL; +} + +void +gimp_value_set_stringarray (GValue *value, + const gchar **data, + gsize length) +{ + GimpArray *array; + + g_return_if_fail (GIMP_VALUE_HOLDS_STRING_ARRAY (value)); + + array = gimp_string_array_new (data, length, FALSE); + + g_value_take_boxed (value, array); +} + +void +gimp_value_set_static_stringarray (GValue *value, + const gchar **data, + gsize length) +{ + GimpArray *array; + + g_return_if_fail (GIMP_VALUE_HOLDS_STRING_ARRAY (value)); + + array = gimp_string_array_new (data, length, TRUE); + + g_value_take_boxed (value, array); +} + +void +gimp_value_take_stringarray (GValue *value, + gchar **data, + gsize length) +{ + GimpArray *array; + + g_return_if_fail (GIMP_VALUE_HOLDS_STRING_ARRAY (value)); + + array = gimp_string_array_new ((const gchar **) data, length, TRUE); + array->static_data = FALSE; + + g_value_take_boxed (value, array); +} + + +/* + * GIMP_TYPE_COLOR_ARRAY + */ + +GType +gimp_color_array_get_type (void) +{ + static GType type = 0; + + if (! type) + type = g_boxed_type_register_static ("GimpColorArray", + (GBoxedCopyFunc) gimp_array_copy, + (GBoxedFreeFunc) gimp_array_free); + + return type; +} + + +/* + * GIMP_TYPE_PARAM_COLOR_ARRAY + */ + +static void gimp_param_color_array_class_init (GParamSpecClass *klass); +static void gimp_param_color_array_init (GParamSpec *pspec); + +GType +gimp_param_color_array_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_color_array_class_init, + NULL, NULL, + sizeof (GimpParamSpecArray), + 0, + (GInstanceInitFunc) gimp_param_color_array_init + }; + + type = g_type_register_static (G_TYPE_PARAM_BOXED, + "GimpParamColorArray", &info, 0); + } + + return type; +} + +static void +gimp_param_color_array_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_COLOR_ARRAY; +} + +static void +gimp_param_color_array_init (GParamSpec *pspec) +{ +} + +GParamSpec * +gimp_param_spec_color_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags) +{ + GimpParamSpecColorArray *array_spec; + + array_spec = g_param_spec_internal (GIMP_TYPE_PARAM_COLOR_ARRAY, + name, nick, blurb, flags); + + return G_PARAM_SPEC (array_spec); +} + +const GimpRGB * +gimp_value_get_colorarray (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value), NULL); + + return (const GimpRGB *) gimp_value_get_array (value); +} + +GimpRGB * +gimp_value_dup_colorarray (const GValue *value) +{ + g_return_val_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value), NULL); + + return (GimpRGB *) gimp_value_dup_array (value); +} + +void +gimp_value_set_colorarray (GValue *value, + const GimpRGB *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value)); + + gimp_value_set_array (value, (const guint8 *) data, + length * sizeof (GimpRGB)); +} + +void +gimp_value_set_static_colorarray (GValue *value, + const GimpRGB *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value)); + + gimp_value_set_static_array (value, (const guint8 *) data, + length * sizeof (GimpRGB)); +} + +void +gimp_value_take_colorarray (GValue *value, + GimpRGB *data, + gsize length) +{ + g_return_if_fail (GIMP_VALUE_HOLDS_COLOR_ARRAY (value)); + + gimp_value_take_array (value, (guint8 *) data, + length * sizeof (GimpRGB)); +} diff --git a/app/core/gimpparamspecs.h b/app/core/gimpparamspecs.h new file mode 100644 index 0000000..abfb56f --- /dev/null +++ b/app/core/gimpparamspecs.h @@ -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 . + */ + +#ifndef __GIMP_PARAM_SPECS_H__ +#define __GIMP_PARAM_SPECS_H__ + + +/* + * Keep in sync with libgimpconfig/gimpconfig-params.h + */ +#define GIMP_PARAM_NO_VALIDATE (1 << (6 + G_PARAM_USER_SHIFT)) + + +/* + * GIMP_TYPE_INT32 + */ + +#define GIMP_TYPE_INT32 (gimp_int32_get_type ()) +#define GIMP_VALUE_HOLDS_INT32(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_INT32)) + +GType gimp_int32_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT32 + */ + +#define GIMP_TYPE_PARAM_INT32 (gimp_param_int32_get_type ()) +#define GIMP_PARAM_SPEC_INT32(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT32, GimpParamSpecInt32)) +#define GIMP_IS_PARAM_SPEC_INT32(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT32)) + +typedef struct _GimpParamSpecInt32 GimpParamSpecInt32; + +struct _GimpParamSpecInt32 +{ + GParamSpecInt parent_instance; +}; + +GType gimp_param_int32_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int32 (const gchar *name, + const gchar *nick, + const gchar *blurb, + gint minimum, + gint maximum, + gint default_value, + GParamFlags flags); + + +/* + * GIMP_TYPE_INT16 + */ + +#define GIMP_TYPE_INT16 (gimp_int16_get_type ()) +#define GIMP_VALUE_HOLDS_INT16(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_INT16)) + +GType gimp_int16_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT16 + */ + +#define GIMP_TYPE_PARAM_INT16 (gimp_param_int16_get_type ()) +#define GIMP_PARAM_SPEC_INT16(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT16, GimpParamSpecInt16)) +#define GIMP_IS_PARAM_SPEC_INT16(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT16)) + +typedef struct _GimpParamSpecInt16 GimpParamSpecInt16; + +struct _GimpParamSpecInt16 +{ + GParamSpecInt parent_instance; +}; + +GType gimp_param_int16_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int16 (const gchar *name, + const gchar *nick, + const gchar *blurb, + gint minimum, + gint maximum, + gint default_value, + GParamFlags flags); + + +/* + * GIMP_TYPE_INT8 + */ + +#define GIMP_TYPE_INT8 (gimp_int8_get_type ()) +#define GIMP_VALUE_HOLDS_INT8(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_INT8)) + +GType gimp_int8_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT8 + */ + +#define GIMP_TYPE_PARAM_INT8 (gimp_param_int8_get_type ()) +#define GIMP_PARAM_SPEC_INT8(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT8, GimpParamSpecInt8)) +#define GIMP_IS_PARAM_SPEC_INT8(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT8)) + +typedef struct _GimpParamSpecInt8 GimpParamSpecInt8; + +struct _GimpParamSpecInt8 +{ + GParamSpecUInt parent_instance; +}; + +GType gimp_param_int8_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int8 (const gchar *name, + const gchar *nick, + const gchar *blurb, + guint minimum, + guint maximum, + guint default_value, + GParamFlags flags); + + +/* + * GIMP_TYPE_PARAM_STRING + */ + +#define GIMP_TYPE_PARAM_STRING (gimp_param_string_get_type ()) +#define GIMP_PARAM_SPEC_STRING(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_STRING, GimpParamSpecString)) +#define GIMP_IS_PARAM_SPEC_STRING(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_STRING)) + +typedef struct _GimpParamSpecString GimpParamSpecString; + +struct _GimpParamSpecString +{ + GParamSpecString parent_instance; + + guint allow_non_utf8 : 1; + guint non_empty : 1; +}; + +GType gimp_param_string_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_string (const gchar *name, + const gchar *nick, + const gchar *blurb, + gboolean allow_non_utf8, + gboolean null_ok, + gboolean non_empty, + const gchar *default_value, + GParamFlags flags); + + +/* + * GIMP_TYPE_PARAM_ENUM + */ + +#define GIMP_TYPE_PARAM_ENUM (gimp_param_enum_get_type ()) +#define GIMP_PARAM_SPEC_ENUM(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_ENUM, GimpParamSpecEnum)) + +#define GIMP_IS_PARAM_SPEC_ENUM(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_ENUM)) + +typedef struct _GimpParamSpecEnum GimpParamSpecEnum; + +struct _GimpParamSpecEnum +{ + GParamSpecEnum parent_instance; + + GSList *excluded_values; +}; + +GType gimp_param_enum_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_enum (const gchar *name, + const gchar *nick, + const gchar *blurb, + GType enum_type, + gint default_value, + GParamFlags flags); + +void gimp_param_spec_enum_exclude_value (GimpParamSpecEnum *espec, + gint value); + + +/* + * GIMP_TYPE_IMAGE_ID + */ + +#define GIMP_TYPE_IMAGE_ID (gimp_image_id_get_type ()) +#define GIMP_VALUE_HOLDS_IMAGE_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_IMAGE_ID)) + +GType gimp_image_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_IMAGE_ID + */ + +#define GIMP_TYPE_PARAM_IMAGE_ID (gimp_param_image_id_get_type ()) +#define GIMP_PARAM_SPEC_IMAGE_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_IMAGE_ID, GimpParamSpecImageID)) +#define GIMP_IS_PARAM_SPEC_IMAGE_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_IMAGE_ID)) + +typedef struct _GimpParamSpecImageID GimpParamSpecImageID; + +struct _GimpParamSpecImageID +{ + GParamSpecInt parent_instance; + + Gimp *gimp; + gboolean none_ok; +}; + +GType gimp_param_image_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_image_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpImage * gimp_value_get_image (const GValue *value, + Gimp *gimp); +void gimp_value_set_image (GValue *value, + GimpImage *image); + + + +/* + * GIMP_TYPE_ITEM_ID + */ + +#define GIMP_TYPE_ITEM_ID (gimp_item_id_get_type ()) +#define GIMP_VALUE_HOLDS_ITEM_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_ITEM_ID)) + +GType gimp_item_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_ITEM_ID + */ + +#define GIMP_TYPE_PARAM_ITEM_ID (gimp_param_item_id_get_type ()) +#define GIMP_PARAM_SPEC_ITEM_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_ITEM_ID, GimpParamSpecItemID)) +#define GIMP_IS_PARAM_SPEC_ITEM_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_ITEM_ID)) + +typedef struct _GimpParamSpecItemID GimpParamSpecItemID; + +struct _GimpParamSpecItemID +{ + GParamSpecInt parent_instance; + + Gimp *gimp; + GType item_type; + gboolean none_ok; +}; + +GType gimp_param_item_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_item_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpItem * gimp_value_get_item (const GValue *value, + Gimp *gimp); +void gimp_value_set_item (GValue *value, + GimpItem *item); + + +/* + * GIMP_TYPE_DRAWABLE_ID + */ + +#define GIMP_TYPE_DRAWABLE_ID (gimp_drawable_id_get_type ()) +#define GIMP_VALUE_HOLDS_DRAWABLE_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_DRAWABLE_ID)) + +GType gimp_drawable_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_DRAWABLE_ID + */ + +#define GIMP_TYPE_PARAM_DRAWABLE_ID (gimp_param_drawable_id_get_type ()) +#define GIMP_PARAM_SPEC_DRAWABLE_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_DRAWABLE_ID, GimpParamSpecDrawableID)) +#define GIMP_IS_PARAM_SPEC_DRAWABLE_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_DRAWABLE_ID)) + +typedef struct _GimpParamSpecDrawableID GimpParamSpecDrawableID; + +struct _GimpParamSpecDrawableID +{ + GimpParamSpecItemID parent_instance; +}; + +GType gimp_param_drawable_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_drawable_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpDrawable * gimp_value_get_drawable (const GValue *value, + Gimp *gimp); +void gimp_value_set_drawable (GValue *value, + GimpDrawable *drawable); + + +/* + * GIMP_TYPE_LAYER_ID + */ + +#define GIMP_TYPE_LAYER_ID (gimp_layer_id_get_type ()) +#define GIMP_VALUE_HOLDS_LAYER_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_LAYER_ID)) + +GType gimp_layer_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_LAYER_ID + */ + +#define GIMP_TYPE_PARAM_LAYER_ID (gimp_param_layer_id_get_type ()) +#define GIMP_PARAM_SPEC_LAYER_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_LAYER_ID, GimpParamSpecLayerID)) +#define GIMP_IS_PARAM_SPEC_LAYER_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_LAYER_ID)) + +typedef struct _GimpParamSpecLayerID GimpParamSpecLayerID; + +struct _GimpParamSpecLayerID +{ + GimpParamSpecDrawableID parent_instance; +}; + +GType gimp_param_layer_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_layer_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpLayer * gimp_value_get_layer (const GValue *value, + Gimp *gimp); +void gimp_value_set_layer (GValue *value, + GimpLayer *layer); + + +/* + * GIMP_TYPE_CHANNEL_ID + */ + +#define GIMP_TYPE_CHANNEL_ID (gimp_channel_id_get_type ()) +#define GIMP_VALUE_HOLDS_CHANNEL_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_CHANNEL_ID)) + +GType gimp_channel_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_CHANNEL_ID + */ + +#define GIMP_TYPE_PARAM_CHANNEL_ID (gimp_param_channel_id_get_type ()) +#define GIMP_PARAM_SPEC_CHANNEL_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CHANNEL_ID, GimpParamSpecChannelID)) +#define GIMP_IS_PARAM_SPEC_CHANNEL_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_CHANNEL_ID)) + +typedef struct _GimpParamSpecChannelID GimpParamSpecChannelID; + +struct _GimpParamSpecChannelID +{ + GimpParamSpecDrawableID parent_instance; +}; + +GType gimp_param_channel_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_channel_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpChannel * gimp_value_get_channel (const GValue *value, + Gimp *gimp); +void gimp_value_set_channel (GValue *value, + GimpChannel *channel); + + +/* + * GIMP_TYPE_LAYER_MASK_ID + */ + +#define GIMP_TYPE_LAYER_MASK_ID (gimp_layer_mask_id_get_type ()) +#define GIMP_VALUE_HOLDS_LAYER_MASK_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_LAYER_MASK_ID)) + +GType gimp_layer_mask_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_LAYER_MASK_ID + */ + +#define GIMP_TYPE_PARAM_LAYER_MASK_ID (gimp_param_layer_mask_id_get_type ()) +#define GIMP_PARAM_SPEC_LAYER_MASK_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_LAYER_MASK_ID, GimpParamSpecLayerMaskID)) +#define GIMP_IS_PARAM_SPEC_LAYER_MASK_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_LAYER_MASK_ID)) + +typedef struct _GimpParamSpecLayerMaskID GimpParamSpecLayerMaskID; + +struct _GimpParamSpecLayerMaskID +{ + GimpParamSpecChannelID parent_instance; +}; + +GType gimp_param_layer_mask_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_layer_mask_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpLayerMask * gimp_value_get_layer_mask (const GValue *value, + Gimp *gimp); +void gimp_value_set_layer_mask (GValue *value, + GimpLayerMask *layer_mask); + + +/* + * GIMP_TYPE_SELECTION_ID + */ + +#define GIMP_TYPE_SELECTION_ID (gimp_selection_id_get_type ()) +#define GIMP_VALUE_HOLDS_SELECTION_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_SELECTION_ID)) + +GType gimp_selection_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_SELECTION_ID + */ + +#define GIMP_TYPE_PARAM_SELECTION_ID (gimp_param_selection_id_get_type ()) +#define GIMP_PARAM_SPEC_SELECTION_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_SELECTION_ID, GimpParamSpecSelectionID)) +#define GIMP_IS_PARAM_SPEC_SELECTION_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_SELECTION_ID)) + +typedef struct _GimpParamSpecSelectionID GimpParamSpecSelectionID; + +struct _GimpParamSpecSelectionID +{ + GimpParamSpecChannelID parent_instance; +}; + +GType gimp_param_selection_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_selection_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpSelection * gimp_value_get_selection (const GValue *value, + Gimp *gimp); +void gimp_value_set_selection (GValue *value, + GimpSelection *selection); + + +/* + * GIMP_TYPE_VECTORS_ID + */ + +#define GIMP_TYPE_VECTORS_ID (gimp_vectors_id_get_type ()) +#define GIMP_VALUE_HOLDS_VECTORS_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_VECTORS_ID)) + +GType gimp_vectors_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_VECTORS_ID + */ + +#define GIMP_TYPE_PARAM_VECTORS_ID (gimp_param_vectors_id_get_type ()) +#define GIMP_PARAM_SPEC_VECTORS_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_VECTORS_ID, GimpParamSpecVectorsID)) +#define GIMP_IS_PARAM_SPEC_VECTORS_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_VECTORS_ID)) + +typedef struct _GimpParamSpecVectorsID GimpParamSpecVectorsID; + +struct _GimpParamSpecVectorsID +{ + GimpParamSpecItemID parent_instance; +}; + +GType gimp_param_vectors_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_vectors_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpVectors * gimp_value_get_vectors (const GValue *value, + Gimp *gimp); +void gimp_value_set_vectors (GValue *value, + GimpVectors *vectors); + + +/* + * GIMP_TYPE_DISPLAY_ID + */ + +#define GIMP_TYPE_DISPLAY_ID (gimp_display_id_get_type ()) +#define GIMP_VALUE_HOLDS_DISPLAY_ID(value) (G_TYPE_CHECK_VALUE_TYPE ((value),\ + GIMP_TYPE_DISPLAY_ID)) + +GType gimp_display_id_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_DISPLAY_ID + */ + +#define GIMP_TYPE_PARAM_DISPLAY_ID (gimp_param_display_id_get_type ()) +#define GIMP_PARAM_SPEC_DISPLAY_ID(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_DISPLAY_ID, GimpParamSpecDisplayID)) +#define GIMP_IS_PARAM_SPEC_DISPLAY_ID(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_DISPLAY_ID)) + +typedef struct _GimpParamSpecDisplayID GimpParamSpecDisplayID; + +struct _GimpParamSpecDisplayID +{ + GParamSpecInt parent_instance; + + Gimp *gimp; + gboolean none_ok; +}; + +GType gimp_param_display_id_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_display_id (const gchar *name, + const gchar *nick, + const gchar *blurb, + Gimp *gimp, + gboolean none_ok, + GParamFlags flags); + +GimpObject * gimp_value_get_display (const GValue *value, + Gimp *gimp); +void gimp_value_set_display (GValue *value, + GimpObject *display); + + +/* + * GIMP_TYPE_ARRAY + */ + +typedef struct _GimpArray GimpArray; + +struct _GimpArray +{ + guint8 *data; + gsize length; + gboolean static_data; +}; + +GimpArray * gimp_array_new (const guint8 *data, + gsize length, + gboolean static_data); +GimpArray * gimp_array_copy (const GimpArray *array); +void gimp_array_free (GimpArray *array); + +#define GIMP_TYPE_ARRAY (gimp_array_get_type ()) +#define GIMP_VALUE_HOLDS_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_ARRAY)) + +GType gimp_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_ARRAY + */ + +#define GIMP_TYPE_PARAM_ARRAY (gimp_param_array_get_type ()) +#define GIMP_PARAM_SPEC_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_ARRAY, GimpParamSpecArray)) +#define GIMP_IS_PARAM_SPEC_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_ARRAY)) + +typedef struct _GimpParamSpecArray GimpParamSpecArray; + +struct _GimpParamSpecArray +{ + GParamSpecBoxed parent_instance; +}; + +GType gimp_param_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + + +/* + * GIMP_TYPE_INT8_ARRAY + */ + +#define GIMP_TYPE_INT8_ARRAY (gimp_int8_array_get_type ()) +#define GIMP_VALUE_HOLDS_INT8_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_INT8_ARRAY)) + +GType gimp_int8_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT8_ARRAY + */ + +#define GIMP_TYPE_PARAM_INT8_ARRAY (gimp_param_int8_array_get_type ()) +#define GIMP_PARAM_SPEC_INT8_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT8_ARRAY, GimpParamSpecInt8Array)) +#define GIMP_IS_PARAM_SPEC_INT8_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT8_ARRAY)) + +typedef struct _GimpParamSpecInt8Array GimpParamSpecInt8Array; + +struct _GimpParamSpecInt8Array +{ + GimpParamSpecArray parent_instance; +}; + +GType gimp_param_int8_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int8_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const guint8 * gimp_value_get_int8array (const GValue *value); +guint8 * gimp_value_dup_int8array (const GValue *value); +void gimp_value_set_int8array (GValue *value, + const guint8 *array, + gsize length); +void gimp_value_set_static_int8array (GValue *value, + const guint8 *array, + gsize length); +void gimp_value_take_int8array (GValue *value, + guint8 *array, + gsize length); + + +/* + * GIMP_TYPE_INT16_ARRAY + */ + +#define GIMP_TYPE_INT16_ARRAY (gimp_int16_array_get_type ()) +#define GIMP_VALUE_HOLDS_INT16_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_INT16_ARRAY)) + +GType gimp_int16_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT16_ARRAY + */ + +#define GIMP_TYPE_PARAM_INT16_ARRAY (gimp_param_int16_array_get_type ()) +#define GIMP_PARAM_SPEC_INT16_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT16_ARRAY, GimpParamSpecInt16Array)) +#define GIMP_IS_PARAM_SPEC_INT16_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT16_ARRAY)) + +typedef struct _GimpParamSpecInt16Array GimpParamSpecInt16Array; + +struct _GimpParamSpecInt16Array +{ + GimpParamSpecArray parent_instance; +}; + +GType gimp_param_int16_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int16_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const gint16 * gimp_value_get_int16array (const GValue *value); +gint16 * gimp_value_dup_int16array (const GValue *value); +void gimp_value_set_int16array (GValue *value, + const gint16 *array, + gsize length); +void gimp_value_set_static_int16array (GValue *value, + const gint16 *array, + gsize length); +void gimp_value_take_int16array (GValue *value, + gint16 *array, + gsize length); + + +/* + * GIMP_TYPE_INT32_ARRAY + */ + +#define GIMP_TYPE_INT32_ARRAY (gimp_int32_array_get_type ()) +#define GIMP_VALUE_HOLDS_INT32_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_INT32_ARRAY)) + +GType gimp_int32_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_INT32_ARRAY + */ + +#define GIMP_TYPE_PARAM_INT32_ARRAY (gimp_param_int32_array_get_type ()) +#define GIMP_PARAM_SPEC_INT32_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_INT32_ARRAY, GimpParamSpecInt32Array)) +#define GIMP_IS_PARAM_SPEC_INT32_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_INT32_ARRAY)) + +typedef struct _GimpParamSpecInt32Array GimpParamSpecInt32Array; + +struct _GimpParamSpecInt32Array +{ + GimpParamSpecArray parent_instance; +}; + +GType gimp_param_int32_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_int32_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const gint32 * gimp_value_get_int32array (const GValue *value); +gint32 * gimp_value_dup_int32array (const GValue *value); +void gimp_value_set_int32array (GValue *value, + const gint32 *array, + gsize length); +void gimp_value_set_static_int32array (GValue *value, + const gint32 *array, + gsize length); +void gimp_value_take_int32array (GValue *value, + gint32 *array, + gsize length); + + +/* + * GIMP_TYPE_FLOAT_ARRAY + */ + +#define GIMP_TYPE_FLOAT_ARRAY (gimp_float_array_get_type ()) +#define GIMP_VALUE_HOLDS_FLOAT_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_FLOAT_ARRAY)) + +GType gimp_float_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_FLOAT_ARRAY + */ + +#define GIMP_TYPE_PARAM_FLOAT_ARRAY (gimp_param_float_array_get_type ()) +#define GIMP_PARAM_SPEC_FLOAT_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_FLOAT_ARRAY, GimpParamSpecFloatArray)) +#define GIMP_IS_PARAM_SPEC_FLOAT_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_FLOAT_ARRAY)) + +typedef struct _GimpParamSpecFloatArray GimpParamSpecFloatArray; + +struct _GimpParamSpecFloatArray +{ + GimpParamSpecArray parent_instance; +}; + +GType gimp_param_float_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_float_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const gdouble * gimp_value_get_floatarray (const GValue *value); +gdouble * gimp_value_dup_floatarray (const GValue *value); +void gimp_value_set_floatarray (GValue *value, + const gdouble *array, + gsize length); +void gimp_value_set_static_floatarray (GValue *value, + const gdouble *array, + gsize length); +void gimp_value_take_floatarray (GValue *value, + gdouble *array, + gsize length); + + +/* + * GIMP_TYPE_STRING_ARRAY + */ + +GimpArray * gimp_string_array_new (const gchar **data, + gsize length, + gboolean static_data); +GimpArray * gimp_string_array_copy (const GimpArray *array); +void gimp_string_array_free (GimpArray *array); + +#define GIMP_TYPE_STRING_ARRAY (gimp_string_array_get_type ()) +#define GIMP_VALUE_HOLDS_STRING_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_STRING_ARRAY)) + +GType gimp_string_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_STRING_ARRAY + */ + +#define GIMP_TYPE_PARAM_STRING_ARRAY (gimp_param_string_array_get_type ()) +#define GIMP_PARAM_SPEC_STRING_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_STRING_ARRAY, GimpParamSpecStringArray)) +#define GIMP_IS_PARAM_SPEC_STRING_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_STRING_ARRAY)) + +typedef struct _GimpParamSpecStringArray GimpParamSpecStringArray; + +struct _GimpParamSpecStringArray +{ + GParamSpecBoxed parent_instance; +}; + +GType gimp_param_string_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_string_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const gchar ** gimp_value_get_stringarray (const GValue *value); +gchar ** gimp_value_dup_stringarray (const GValue *value); +void gimp_value_set_stringarray (GValue *value, + const gchar **array, + gsize length); +void gimp_value_set_static_stringarray (GValue *value, + const gchar **array, + gsize length); +void gimp_value_take_stringarray (GValue *value, + gchar **array, + gsize length); + + +/* + * GIMP_TYPE_COLOR_ARRAY + */ + +#define GIMP_TYPE_COLOR_ARRAY (gimp_color_array_get_type ()) +#define GIMP_VALUE_HOLDS_COLOR_ARRAY(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_COLOR_ARRAY)) + +GType gimp_color_array_get_type (void) G_GNUC_CONST; + + +/* + * GIMP_TYPE_PARAM_COLOR_ARRAY + */ + +#define GIMP_TYPE_PARAM_COLOR_ARRAY (gimp_param_color_array_get_type ()) +#define GIMP_PARAM_SPEC_COLOR_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_COLOR_ARRAY, GimpParamSpecColorArray)) +#define GIMP_IS_PARAM_SPEC_COLOR_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_COLOR_ARRAY)) + +typedef struct _GimpParamSpecColorArray GimpParamSpecColorArray; + +struct _GimpParamSpecColorArray +{ + GParamSpecBoxed parent_instance; +}; + +GType gimp_param_color_array_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_color_array (const gchar *name, + const gchar *nick, + const gchar *blurb, + GParamFlags flags); + +const GimpRGB * gimp_value_get_colorarray (const GValue *value); +GimpRGB * gimp_value_dup_colorarray (const GValue *value); +void gimp_value_set_colorarray (GValue *value, + const GimpRGB *array, + gsize length); +void gimp_value_set_static_colorarray (GValue *value, + const GimpRGB *array, + gsize length); +void gimp_value_take_colorarray (GValue *value, + GimpRGB *array, + gsize length); + + +#endif /* __GIMP_PARAM_SPECS_H__ */ diff --git a/app/core/gimpparasitelist.c b/app/core/gimpparasitelist.c new file mode 100644 index 0000000..70a81fa --- /dev/null +++ b/app/core/gimpparasitelist.c @@ -0,0 +1,453 @@ +/* parasitelist.c: Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpmarshal.h" +#include "gimpparasitelist.h" + + +enum +{ + ADD, + REMOVE, + LAST_SIGNAL +}; + + +static void gimp_parasite_list_finalize (GObject *object); +static gint64 gimp_parasite_list_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_parasite_list_config_iface_init (gpointer iface, + gpointer iface_data); +static gboolean gimp_parasite_list_serialize (GimpConfig *list, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_parasite_list_deserialize (GimpConfig *list, + GScanner *scanner, + gint nest_level, + gpointer data); + +static void parasite_serialize (const gchar *key, + GimpParasite *parasite, + GimpConfigWriter *writer); +static void parasite_copy (const gchar *key, + GimpParasite *parasite, + GimpParasiteList *list); +static gboolean parasite_free (const gchar *key, + GimpParasite *parasite, + gpointer unused); +static void parasite_count_if_persistent (const gchar *key, + GimpParasite *parasite, + gint *count); + + +G_DEFINE_TYPE_WITH_CODE (GimpParasiteList, gimp_parasite_list, GIMP_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_parasite_list_config_iface_init)) + +#define parent_class gimp_parasite_list_parent_class + +static guint parasite_list_signals[LAST_SIGNAL] = { 0 }; +static const gchar parasite_symbol[] = "parasite"; + + +static void +gimp_parasite_list_class_init (GimpParasiteListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + parasite_list_signals[ADD] = + g_signal_new ("add", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpParasiteListClass, add), + NULL, NULL, + gimp_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + parasite_list_signals[REMOVE] = + g_signal_new ("remove", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpParasiteListClass, remove), + NULL, NULL, + gimp_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + object_class->finalize = gimp_parasite_list_finalize; + + gimp_object_class->get_memsize = gimp_parasite_list_get_memsize; + + klass->add = NULL; + klass->remove = NULL; +} + +static void +gimp_parasite_list_config_iface_init (gpointer iface, + gpointer iface_data) +{ + GimpConfigInterface *config_iface = (GimpConfigInterface *) iface; + + config_iface->serialize = gimp_parasite_list_serialize; + config_iface->deserialize = gimp_parasite_list_deserialize; +} + +static void +gimp_parasite_list_init (GimpParasiteList *list) +{ + list->table = NULL; +} + +static void +gimp_parasite_list_finalize (GObject *object) +{ + GimpParasiteList *list = GIMP_PARASITE_LIST (object); + + if (list->table) + { + g_hash_table_foreach_remove (list->table, (GHRFunc) parasite_free, NULL); + g_hash_table_destroy (list->table); + list->table = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_parasite_list_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpParasiteList *list = GIMP_PARASITE_LIST (object); + gint64 memsize = 0; + + memsize += gimp_g_hash_table_get_memsize_foreach (list->table, + (GimpMemsizeFunc) + gimp_parasite_get_memsize, + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_parasite_list_serialize (GimpConfig *list, + GimpConfigWriter *writer, + gpointer data) +{ + if (GIMP_PARASITE_LIST (list)->table) + g_hash_table_foreach (GIMP_PARASITE_LIST (list)->table, + (GHFunc) parasite_serialize, + writer); + + return TRUE; +} + +static gboolean +gimp_parasite_list_deserialize (GimpConfig *list, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GTokenType token; + + g_scanner_scope_add_symbol (scanner, 0, + parasite_symbol, (gpointer) parasite_symbol); + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + if (scanner->value.v_symbol == parasite_symbol) + { + gchar *parasite_name = NULL; + gint parasite_flags = 0; + guint8 *parasite_data = NULL; + gint parasite_data_size = 0; + GimpParasite *parasite; + + token = G_TOKEN_STRING; + + if (g_scanner_peek_next_token (scanner) != token) + break; + + if (! gimp_scanner_parse_string (scanner, ¶site_name)) + break; + + token = G_TOKEN_INT; + + if (g_scanner_peek_next_token (scanner) != token) + goto cleanup; + + if (! gimp_scanner_parse_int (scanner, ¶site_flags)) + goto cleanup; + + token = G_TOKEN_INT; + + if (g_scanner_peek_next_token (scanner) != token) + { + /* old format -- plain string */ + + gchar *str; + + if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING) + goto cleanup; + + if (! gimp_scanner_parse_string (scanner, &str)) + goto cleanup; + + parasite_data_size = strlen (str); + parasite_data = (guint8 *) str; + } + else + { + /* new format -- properly encoded binary data */ + + if (! gimp_scanner_parse_int (scanner, ¶site_data_size)) + goto cleanup; + + token = G_TOKEN_STRING; + + if (g_scanner_peek_next_token (scanner) != token) + goto cleanup; + + if (! gimp_scanner_parse_data (scanner, parasite_data_size, + ¶site_data)) + goto cleanup; + } + + parasite = gimp_parasite_new (parasite_name, + parasite_flags, + parasite_data_size, + parasite_data); + gimp_parasite_list_add (GIMP_PARASITE_LIST (list), + parasite); /* adds a copy */ + gimp_parasite_free (parasite); + + token = G_TOKEN_RIGHT_PAREN; + + g_free (parasite_data); + cleanup: + g_free (parasite_name); + } + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + return gimp_config_deserialize_return (scanner, token, nest_level); +} + +GimpParasiteList * +gimp_parasite_list_new (void) +{ + GimpParasiteList *list; + + list = g_object_new (GIMP_TYPE_PARASITE_LIST, NULL); + + return list; +} + +GimpParasiteList * +gimp_parasite_list_copy (GimpParasiteList *list) +{ + GimpParasiteList *newlist; + + g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), NULL); + + newlist = gimp_parasite_list_new (); + + if (list->table) + g_hash_table_foreach (list->table, (GHFunc) parasite_copy, newlist); + + return newlist; +} + +void +gimp_parasite_list_add (GimpParasiteList *list, + const GimpParasite *parasite) +{ + GimpParasite *copy; + + g_return_if_fail (GIMP_IS_PARASITE_LIST (list)); + g_return_if_fail (parasite != NULL); + g_return_if_fail (parasite->name != NULL); + + if (list->table == NULL) + list->table = g_hash_table_new (g_str_hash, g_str_equal); + + gimp_parasite_list_remove (list, parasite->name); + copy = gimp_parasite_copy (parasite); + g_hash_table_insert (list->table, copy->name, copy); + + g_signal_emit (list, parasite_list_signals[ADD], 0, copy); +} + +void +gimp_parasite_list_remove (GimpParasiteList *list, + const gchar *name) +{ + g_return_if_fail (GIMP_IS_PARASITE_LIST (list)); + + if (list->table) + { + GimpParasite *parasite; + + parasite = (GimpParasite *) gimp_parasite_list_find (list, name); + + if (parasite) + { + g_hash_table_remove (list->table, name); + + g_signal_emit (list, parasite_list_signals[REMOVE], 0, parasite); + + gimp_parasite_free (parasite); + } + } +} + +gint +gimp_parasite_list_length (GimpParasiteList *list) +{ + g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), 0); + + if (! list->table) + return 0; + + return g_hash_table_size (list->table); +} + +gint +gimp_parasite_list_persistent_length (GimpParasiteList *list) +{ + gint len = 0; + + g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), 0); + + if (! list->table) + return 0; + + gimp_parasite_list_foreach (list, + (GHFunc) parasite_count_if_persistent, &len); + + return len; +} + +void +gimp_parasite_list_foreach (GimpParasiteList *list, + GHFunc function, + gpointer user_data) +{ + g_return_if_fail (GIMP_IS_PARASITE_LIST (list)); + + if (! list->table) + return; + + g_hash_table_foreach (list->table, function, user_data); +} + +const GimpParasite * +gimp_parasite_list_find (GimpParasiteList *list, + const gchar *name) +{ + g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), NULL); + + if (list->table) + return (GimpParasite *) g_hash_table_lookup (list->table, name); + + return NULL; +} + + +static void +parasite_serialize (const gchar *key, + GimpParasite *parasite, + GimpConfigWriter *writer) +{ + if (! gimp_parasite_is_persistent (parasite)) + return; + + gimp_config_writer_open (writer, parasite_symbol); + + gimp_config_writer_printf (writer, "\"%s\" %lu %lu", + gimp_parasite_name (parasite), + gimp_parasite_flags (parasite), + gimp_parasite_data_size (parasite)); + + gimp_config_writer_data (writer, + gimp_parasite_data_size (parasite), + gimp_parasite_data (parasite)); + + gimp_config_writer_close (writer); + gimp_config_writer_linefeed (writer); +} + +static void +parasite_copy (const gchar *key, + GimpParasite *parasite, + GimpParasiteList *list) +{ + gimp_parasite_list_add (list, parasite); +} + +static gboolean +parasite_free (const gchar *key, + GimpParasite *parasite, + gpointer unused) +{ + gimp_parasite_free (parasite); + + return TRUE; +} + +static void +parasite_count_if_persistent (const gchar *key, + GimpParasite *parasite, + gint *count) +{ + if (gimp_parasite_is_persistent (parasite)) + *count = *count + 1; +} diff --git a/app/core/gimpparasitelist.h b/app/core/gimpparasitelist.h new file mode 100644 index 0000000..1762753 --- /dev/null +++ b/app/core/gimpparasitelist.h @@ -0,0 +1,69 @@ +/* parasitelist.h: Copyright 1998 Jay Cox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PARASITE_LIST_H__ +#define __GIMP_PARASITE_LIST_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_PARASITE_LIST (gimp_parasite_list_get_type ()) +#define GIMP_PARASITE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PARASITE_LIST, GimpParasiteList)) +#define GIMP_PARASITE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PARASITE_LIST, GimpParasiteListClass)) +#define GIMP_IS_PARASITE_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PARASITE_LIST)) +#define GIMP_IS_PARASITE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PARASITE_LIST)) +#define GIMP_PARASITE_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PARASITE_LIST, GimpParasiteListClass)) + + +typedef struct _GimpParasiteListClass GimpParasiteListClass; + +struct _GimpParasiteList +{ + GimpObject object; + + GHashTable *table; +}; + +struct _GimpParasiteListClass +{ + GimpObjectClass parent_class; + + void (* add) (GimpParasiteList *list, + GimpParasite *parasite); + void (* remove) (GimpParasiteList *list, + GimpParasite *parasite); +}; + + +GType gimp_parasite_list_get_type (void) G_GNUC_CONST; + +GimpParasiteList * gimp_parasite_list_new (void); +GimpParasiteList * gimp_parasite_list_copy (GimpParasiteList *list); +void gimp_parasite_list_add (GimpParasiteList *list, + const GimpParasite *parasite); +void gimp_parasite_list_remove (GimpParasiteList *list, + const gchar *name); +gint gimp_parasite_list_length (GimpParasiteList *list); +gint gimp_parasite_list_persistent_length (GimpParasiteList *list); +void gimp_parasite_list_foreach (GimpParasiteList *list, + GHFunc function, + gpointer user_data); +const GimpParasite * gimp_parasite_list_find (GimpParasiteList *list, + const gchar *name); + + +#endif /* __GIMP_PARASITE_LIST_H__ */ diff --git a/app/core/gimppattern-header.h b/app/core/gimppattern-header.h new file mode 100644 index 0000000..e11b3c9 --- /dev/null +++ b/app/core/gimppattern-header.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GIMP_PATTERN_HEADER_H__ +#define __GIMP_PATTERN_HEADER_H__ + + +#define GIMP_PATTERN_MAGIC (('G' << 24) + ('P' << 16) + \ + ('A' << 8) + ('T' << 0)) +#define GIMP_PATTERN_MAX_SIZE 10000 /* Max size in either dimension in px */ +#define GIMP_PATTERN_MAX_NAME 256 /* Max length of the pattern's name */ + + +/* All field entries are MSB */ + +typedef struct _GimpPatternHeader GimpPatternHeader; + +struct _GimpPatternHeader +{ + guint32 header_size; /* = sizeof (GimpPatternHeader) + pattern name */ + guint32 version; /* pattern file version # */ + guint32 width; /* width of pattern */ + guint32 height; /* height of pattern */ + guint32 bytes; /* depth of pattern in bytes */ + guint32 magic_number; /* GIMP pattern magic number */ +}; + +/* In a pattern file, next comes the pattern name, null-terminated. + * After that comes the pattern data -- width * height * bytes bytes + * of it... + */ + + +#endif /* __GIMP_PATTERN_HEADER_H__ */ diff --git a/app/core/gimppattern-load.c b/app/core/gimppattern-load.c new file mode 100644 index 0000000..27f057f --- /dev/null +++ b/app/core/gimppattern-load.c @@ -0,0 +1,220 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimppattern.h" +#include "gimppattern-header.h" +#include "gimppattern-load.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +GList * +gimp_pattern_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPattern *pattern = NULL; + const Babl *format = NULL; + GimpPatternHeader header; + gsize size; + gsize bytes_read; + gsize bn_size; + gchar *name = NULL; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* read the size */ + if (! g_input_stream_read_all (input, &header, sizeof (header), + &bytes_read, NULL, error) || + bytes_read != sizeof (header)) + { + g_prefix_error (error, _("File appears truncated: ")); + goto error; + } + + /* rearrange the bytes in each unsigned int */ + header.header_size = g_ntohl (header.header_size); + header.version = g_ntohl (header.version); + header.width = g_ntohl (header.width); + header.height = g_ntohl (header.height); + header.bytes = g_ntohl (header.bytes); + header.magic_number = g_ntohl (header.magic_number); + + /* Check for correct file format */ + if (header.magic_number != GIMP_PATTERN_MAGIC || + header.version != 1 || + header.header_size <= sizeof (header)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unknown pattern format version %d."), + header.version); + goto error; + } + + /* Check for supported bit depths */ + if (header.bytes < 1 || header.bytes > 4) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unsupported pattern depth %d.\n" + "GIMP Patterns must be GRAY or RGB."), + header.bytes); + goto error; + } + + /* Validate dimensions */ + if ((header.width == 0) || (header.width > GIMP_PATTERN_MAX_SIZE) || + (header.height == 0) || (header.height > GIMP_PATTERN_MAX_SIZE) || + (G_MAXSIZE / header.width / header.height / header.bytes < 1)) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid header data in '%s': width=%lu (maximum %lu), " + "height=%lu (maximum %lu), bytes=%lu"), + gimp_file_get_utf8_name (file), + (gulong) header.width, (gulong) GIMP_PATTERN_MAX_SIZE, + (gulong) header.height, (gulong) GIMP_PATTERN_MAX_SIZE, + (gulong) header.bytes); + goto error; + } + + /* Read in the pattern name */ + if ((bn_size = (header.header_size - sizeof (header)))) + { + gchar *utf8; + + if (bn_size > GIMP_PATTERN_MAX_NAME) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Invalid header data in '%s': " + "Pattern name is too long: %lu"), + gimp_file_get_utf8_name (file), + (gulong) bn_size); + goto error; + } + + name = g_new0 (gchar, bn_size + 1); + + if (! g_input_stream_read_all (input, name, bn_size, + &bytes_read, NULL, error) || + bytes_read != bn_size) + { + g_prefix_error (error, _("File appears truncated.")); + g_free (name); + goto error; + } + + utf8 = gimp_any_to_utf8 (name, bn_size - 1, + _("Invalid UTF-8 string in pattern file '%s'."), + gimp_file_get_utf8_name (file)); + g_free (name); + name = utf8; + } + + if (! name) + name = g_strdup (_("Unnamed")); + + pattern = g_object_new (GIMP_TYPE_PATTERN, + "name", name, + "mime-type", "image/x-gimp-pat", + NULL); + + g_free (name); + + switch (header.bytes) + { + case 1: format = babl_format ("Y' u8"); break; + case 2: format = babl_format ("Y'A u8"); break; + case 3: format = babl_format ("R'G'B' u8"); break; + case 4: format = babl_format ("R'G'B'A u8"); break; + } + + pattern->mask = gimp_temp_buf_new (header.width, header.height, format); + size = (gsize) header.width * header.height * header.bytes; + + if (! g_input_stream_read_all (input, + gimp_temp_buf_get_data (pattern->mask), size, + &bytes_read, NULL, error) || + bytes_read != size) + { + g_prefix_error (error, _("File appears truncated.")); + goto error; + } + + return g_list_prepend (NULL, pattern); + + error: + + if (pattern) + g_object_unref (pattern); + + g_prefix_error (error, _("Fatal parse error in pattern file: ")); + + return NULL; +} + +GList * +gimp_pattern_load_pixbuf (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpPattern *pattern; + GdkPixbuf *pixbuf; + gchar *name; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + pixbuf = gdk_pixbuf_new_from_stream (input, NULL, error); + if (! pixbuf) + return NULL; + + name = g_strdup (gdk_pixbuf_get_option (pixbuf, "tEXt::Title")); + + if (! name) + name = g_strdup (gdk_pixbuf_get_option (pixbuf, "tEXt::Comment")); + + if (! name) + name = g_path_get_basename (gimp_file_get_utf8_name (file)); + + pattern = g_object_new (GIMP_TYPE_PATTERN, + "name", name, + "mime-type", NULL, /* FIXME!! */ + NULL); + g_free (name); + + pattern->mask = gimp_temp_buf_new_from_pixbuf (pixbuf, NULL); + + g_object_unref (pixbuf); + + return g_list_prepend (NULL, pattern); +} diff --git a/app/core/gimppattern-load.h b/app/core/gimppattern-load.h new file mode 100644 index 0000000..8e7fc8a --- /dev/null +++ b/app/core/gimppattern-load.h @@ -0,0 +1,35 @@ +/* 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 . + */ + +#ifndef __GIMP_PATTERN_LOAD_H__ +#define __GIMP_PATTERN_LOAD_H__ + + +#define GIMP_PATTERN_FILE_EXTENSION ".pat" + + +GList * gimp_pattern_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); +GList * gimp_pattern_load_pixbuf (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_PATTERN_LOAD_H__ */ diff --git a/app/core/gimppattern-save.c b/app/core/gimppattern-save.c new file mode 100644 index 0000000..b9ded3e --- /dev/null +++ b/app/core/gimppattern-save.c @@ -0,0 +1,87 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimppattern.h" +#include "gimppattern-header.h" +#include "gimppattern-save.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +gboolean +gimp_pattern_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + GimpPattern *pattern = GIMP_PATTERN (data); + GimpTempBuf *mask = gimp_pattern_get_mask (pattern); + const Babl *format = gimp_temp_buf_get_format (mask); + GimpPatternHeader header; + const gchar *name; + gint width; + gint height; + + name = gimp_object_get_name (pattern); + width = gimp_temp_buf_get_width (mask); + height = gimp_temp_buf_get_height (mask); + + if (width > GIMP_PATTERN_MAX_SIZE || height > GIMP_PATTERN_MAX_SIZE) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unsupported pattern dimensions %d x %d.\n" + "GIMP Patterns have a maximum size of %d x %d."), + width, height, + GIMP_PATTERN_MAX_SIZE, GIMP_PATTERN_MAX_SIZE); + return FALSE; + } + header.header_size = g_htonl (sizeof (GimpPatternHeader) + + strlen (name) + 1); + header.version = g_htonl (1); + header.width = g_htonl (width); + header.height = g_htonl (height); + header.bytes = g_htonl (babl_format_get_bytes_per_pixel (format)); + header.magic_number = g_htonl (GIMP_PATTERN_MAGIC); + + if (! g_output_stream_write_all (output, &header, sizeof (header), + NULL, NULL, error)) + { + return FALSE; + } + + if (! g_output_stream_write_all (output, name, strlen (name) + 1, + NULL, NULL, error)) + { + return FALSE; + } + + if (! g_output_stream_write_all (output, + gimp_temp_buf_get_data (mask), + gimp_temp_buf_get_data_size (mask), + NULL, NULL, error)) + { + return FALSE; + } + + return TRUE; +} diff --git a/app/core/gimppattern-save.h b/app/core/gimppattern-save.h new file mode 100644 index 0000000..d3c657c --- /dev/null +++ b/app/core/gimppattern-save.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 . + */ + +#ifndef __GIMP_PATTERN_SAVE_H__ +#define __GIMP_PATTERN_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_pattern_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_PATTERN_SAVE_H__ */ diff --git a/app/core/gimppattern.c b/app/core/gimppattern.c new file mode 100644 index 0000000..22d2d78 --- /dev/null +++ b/app/core/gimppattern.c @@ -0,0 +1,319 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "gimppattern.h" +#include "gimppattern-load.h" +#include "gimppattern-save.h" +#include "gimptagged.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +static void gimp_pattern_tagged_iface_init (GimpTaggedInterface *iface); +static void gimp_pattern_finalize (GObject *object); + +static gint64 gimp_pattern_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_pattern_get_size (GimpViewable *viewable, + gint *width, + gint *height); +static GimpTempBuf * gimp_pattern_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_pattern_get_description (GimpViewable *viewable, + gchar **tooltip); + +static const gchar * gimp_pattern_get_extension (GimpData *data); +static void gimp_pattern_copy (GimpData *data, + GimpData *src_data); + +static gchar * gimp_pattern_get_checksum (GimpTagged *tagged); + + +G_DEFINE_TYPE_WITH_CODE (GimpPattern, gimp_pattern, GIMP_TYPE_DATA, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED, + gimp_pattern_tagged_iface_init)) + +#define parent_class gimp_pattern_parent_class + + +static void +gimp_pattern_class_init (GimpPatternClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->finalize = gimp_pattern_finalize; + + gimp_object_class->get_memsize = gimp_pattern_get_memsize; + + viewable_class->default_icon_name = "gimp-tool-bucket-fill"; + viewable_class->get_size = gimp_pattern_get_size; + viewable_class->get_new_preview = gimp_pattern_get_new_preview; + viewable_class->get_description = gimp_pattern_get_description; + + data_class->save = gimp_pattern_save; + data_class->get_extension = gimp_pattern_get_extension; + data_class->copy = gimp_pattern_copy; +} + +static void +gimp_pattern_tagged_iface_init (GimpTaggedInterface *iface) +{ + iface->get_checksum = gimp_pattern_get_checksum; +} + +static void +gimp_pattern_init (GimpPattern *pattern) +{ + pattern->mask = NULL; +} + +static void +gimp_pattern_finalize (GObject *object) +{ + GimpPattern *pattern = GIMP_PATTERN (object); + + g_clear_pointer (&pattern->mask, gimp_temp_buf_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_pattern_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpPattern *pattern = GIMP_PATTERN (object); + gint64 memsize = 0; + + memsize += gimp_temp_buf_get_memsize (pattern->mask); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_pattern_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpPattern *pattern = GIMP_PATTERN (viewable); + + *width = gimp_temp_buf_get_width (pattern->mask); + *height = gimp_temp_buf_get_height (pattern->mask); + + return TRUE; +} + +static GimpTempBuf * +gimp_pattern_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpPattern *pattern = GIMP_PATTERN (viewable); + GimpTempBuf *temp_buf; + GeglBuffer *src_buffer; + gint true_width; + gint true_height; + gint copy_width; + gint copy_height; + + true_width = gimp_temp_buf_get_width (pattern->mask); + true_height = gimp_temp_buf_get_height (pattern->mask); + copy_width = MIN (width, true_width); + copy_height = MIN (height, true_height); + + src_buffer = gimp_temp_buf_create_buffer (pattern->mask); + + if (true_width > width || true_height > height) + { + gdouble ratio_x = (gdouble) width / (gdouble) true_width; + gdouble ratio_y = (gdouble) height / (gdouble) true_height; + gdouble scale = MIN (ratio_x, ratio_y); + gdouble aspect = (gdouble) true_width / (gdouble) true_height; + + /* Adjusting dimensions for non-square patterns */ + if (true_width > true_height) + copy_height = copy_width / aspect; + else if (true_width < true_height) + copy_width = copy_height * aspect; + + temp_buf = gimp_temp_buf_new (copy_width, copy_height, + gimp_temp_buf_get_format (pattern->mask)); + + gegl_buffer_get (src_buffer, + GEGL_RECTANGLE (0, 0, copy_width, copy_height), + scale, gimp_temp_buf_get_format (temp_buf), + gimp_temp_buf_get_data (temp_buf), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); + } + else + { + GeglBuffer *dest_buffer; + + temp_buf = gimp_temp_buf_new (copy_width, copy_height, + gimp_temp_buf_get_format (pattern->mask)); + + dest_buffer = gimp_temp_buf_create_buffer (temp_buf); + + gimp_gegl_buffer_copy (src_buffer, + GEGL_RECTANGLE (0, 0, copy_width, copy_height), + GEGL_ABYSS_NONE, dest_buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + + g_object_unref (dest_buffer); + } + + g_object_unref (src_buffer); + + return temp_buf; +} + +static gchar * +gimp_pattern_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpPattern *pattern = GIMP_PATTERN (viewable); + + return g_strdup_printf ("%s (%d × %d)", + gimp_object_get_name (pattern), + gimp_temp_buf_get_width (pattern->mask), + gimp_temp_buf_get_height (pattern->mask)); +} + +static const gchar * +gimp_pattern_get_extension (GimpData *data) +{ + return GIMP_PATTERN_FILE_EXTENSION; +} + +static void +gimp_pattern_copy (GimpData *data, + GimpData *src_data) +{ + GimpPattern *pattern = GIMP_PATTERN (data); + GimpPattern *src_pattern = GIMP_PATTERN (src_data); + + g_clear_pointer (&pattern->mask, gimp_temp_buf_unref); + pattern->mask = gimp_temp_buf_copy (src_pattern->mask); + + gimp_data_dirty (data); +} + +static gchar * +gimp_pattern_get_checksum (GimpTagged *tagged) +{ + GimpPattern *pattern = GIMP_PATTERN (tagged); + gchar *checksum_string = NULL; + + if (pattern->mask) + { + GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5); + + g_checksum_update (checksum, gimp_temp_buf_get_data (pattern->mask), + gimp_temp_buf_get_data_size (pattern->mask)); + + checksum_string = g_strdup (g_checksum_get_string (checksum)); + + g_checksum_free (checksum); + } + + return checksum_string; +} + +GimpData * +gimp_pattern_new (GimpContext *context, + const gchar *name) +{ + GimpPattern *pattern; + guchar *data; + gint row, col; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (name[0] != '\n', NULL); + + pattern = g_object_new (GIMP_TYPE_PATTERN, + "name", name, + NULL); + + pattern->mask = gimp_temp_buf_new (32, 32, babl_format ("R'G'B' u8")); + + data = gimp_temp_buf_get_data (pattern->mask); + + for (row = 0; row < gimp_temp_buf_get_height (pattern->mask); row++) + for (col = 0; col < gimp_temp_buf_get_width (pattern->mask); col++) + { + memset (data, (col % 2) && (row % 2) ? 255 : 0, 3); + data += 3; + } + + return GIMP_DATA (pattern); +} + +GimpData * +gimp_pattern_get_standard (GimpContext *context) +{ + static GimpData *standard_pattern = NULL; + + if (! standard_pattern) + { + standard_pattern = gimp_pattern_new (context, "Standard"); + + gimp_data_clean (standard_pattern); + gimp_data_make_internal (standard_pattern, "gimp-pattern-standard"); + + g_object_add_weak_pointer (G_OBJECT (standard_pattern), + (gpointer *) &standard_pattern); + } + + return standard_pattern; +} + +GimpTempBuf * +gimp_pattern_get_mask (GimpPattern *pattern) +{ + g_return_val_if_fail (GIMP_IS_PATTERN (pattern), NULL); + + return pattern->mask; +} + +GeglBuffer * +gimp_pattern_create_buffer (GimpPattern *pattern) +{ + g_return_val_if_fail (GIMP_IS_PATTERN (pattern), NULL); + + return gimp_temp_buf_create_buffer (pattern->mask); +} diff --git a/app/core/gimppattern.h b/app/core/gimppattern.h new file mode 100644 index 0000000..14cc099 --- /dev/null +++ b/app/core/gimppattern.h @@ -0,0 +1,58 @@ +/* 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 . + */ + +#ifndef __GIMP_PATTERN_H__ +#define __GIMP_PATTERN_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_PATTERN (gimp_pattern_get_type ()) +#define GIMP_PATTERN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PATTERN, GimpPattern)) +#define GIMP_PATTERN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PATTERN, GimpPatternClass)) +#define GIMP_IS_PATTERN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PATTERN)) +#define GIMP_IS_PATTERN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PATTERN)) +#define GIMP_PATTERN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PATTERN, GimpPatternClass)) + + +typedef struct _GimpPatternClass GimpPatternClass; + +struct _GimpPattern +{ + GimpData parent_instance; + + GimpTempBuf *mask; +}; + +struct _GimpPatternClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_pattern_get_type (void) G_GNUC_CONST; + +GimpData * gimp_pattern_new (GimpContext *context, + const gchar *name); +GimpData * gimp_pattern_get_standard (GimpContext *context); + +GimpTempBuf * gimp_pattern_get_mask (GimpPattern *pattern); +GeglBuffer * gimp_pattern_create_buffer (GimpPattern *pattern); + + +#endif /* __GIMP_PATTERN_H__ */ diff --git a/app/core/gimppatternclipboard.c b/app/core/gimppatternclipboard.c new file mode 100644 index 0000000..9dd6a1f --- /dev/null +++ b/app/core/gimppatternclipboard.c @@ -0,0 +1,213 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppatternclipboard.c + * Copyright (C) 2006 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpbuffer.h" +#include "gimppatternclipboard.h" +#include "gimpimage.h" +#include "gimppickable.h" +#include "gimptempbuf.h" + +#include "gimp-intl.h" + + +#define PATTERN_MAX_SIZE 1024 + +enum +{ + PROP_0, + PROP_GIMP +}; + + +/* local function prototypes */ + +static void gimp_pattern_clipboard_constructed (GObject *object); +static void gimp_pattern_clipboard_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_pattern_clipboard_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static GimpData * gimp_pattern_clipboard_duplicate (GimpData *data); + +static void gimp_pattern_clipboard_changed (Gimp *gimp, + GimpPattern *pattern); + + +G_DEFINE_TYPE (GimpPatternClipboard, gimp_pattern_clipboard, GIMP_TYPE_PATTERN) + +#define parent_class gimp_pattern_clipboard_parent_class + + +static void +gimp_pattern_clipboard_class_init (GimpPatternClipboardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->constructed = gimp_pattern_clipboard_constructed; + object_class->set_property = gimp_pattern_clipboard_set_property; + object_class->get_property = gimp_pattern_clipboard_get_property; + + data_class->duplicate = gimp_pattern_clipboard_duplicate; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_pattern_clipboard_init (GimpPatternClipboard *pattern) +{ +} + +static void +gimp_pattern_clipboard_constructed (GObject *object) +{ + GimpPatternClipboard *pattern = GIMP_PATTERN_CLIPBOARD (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (pattern->gimp)); + + g_signal_connect_object (pattern->gimp, "clipboard-changed", + G_CALLBACK (gimp_pattern_clipboard_changed), + pattern, 0); + + gimp_pattern_clipboard_changed (pattern->gimp, GIMP_PATTERN (pattern)); +} + +static void +gimp_pattern_clipboard_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpPatternClipboard *pattern = GIMP_PATTERN_CLIPBOARD (object); + + switch (property_id) + { + case PROP_GIMP: + pattern->gimp = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_pattern_clipboard_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpPatternClipboard *pattern = GIMP_PATTERN_CLIPBOARD (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, pattern->gimp); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GimpData * +gimp_pattern_clipboard_duplicate (GimpData *data) +{ + GimpData *new = g_object_new (GIMP_TYPE_PATTERN, NULL); + + gimp_data_copy (new, data); + + return new; +} + +GimpData * +gimp_pattern_clipboard_new (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return g_object_new (GIMP_TYPE_PATTERN_CLIPBOARD, + "name", _("Clipboard Image"), + "gimp", gimp, + NULL); +} + + +/* private functions */ + +static void +gimp_pattern_clipboard_changed (Gimp *gimp, + GimpPattern *pattern) +{ + GimpObject *paste; + GeglBuffer *buffer = NULL; + + g_clear_pointer (&pattern->mask, gimp_temp_buf_unref); + + paste = gimp_get_clipboard_object (gimp); + + if (GIMP_IS_IMAGE (paste)) + { + gimp_pickable_flush (GIMP_PICKABLE (paste)); + buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (paste)); + } + else if (GIMP_IS_BUFFER (paste)) + { + buffer = gimp_buffer_get_buffer (GIMP_BUFFER (paste)); + } + + if (buffer) + { + gint width = MIN (gegl_buffer_get_width (buffer), PATTERN_MAX_SIZE); + gint height = MIN (gegl_buffer_get_height (buffer), PATTERN_MAX_SIZE); + + pattern->mask = gimp_temp_buf_new (width, height, + gegl_buffer_get_format (buffer)); + + gegl_buffer_get (buffer, + GEGL_RECTANGLE (0, 0, width, height), 1.0, + NULL, + gimp_temp_buf_get_data (pattern->mask), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + else + { + pattern->mask = gimp_temp_buf_new (16, 16, babl_format ("R'G'B' u8")); + memset (gimp_temp_buf_get_data (pattern->mask), 255, 16 * 16 * 3); + } + + gimp_data_dirty (GIMP_DATA (pattern)); +} diff --git a/app/core/gimppatternclipboard.h b/app/core/gimppatternclipboard.h new file mode 100644 index 0000000..2707291 --- /dev/null +++ b/app/core/gimppatternclipboard.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppatternclipboard.h + * Copyright (C) 2006 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PATTERN_CLIPBOARD_H__ +#define __GIMP_PATTERN_CLIPBOARD_H__ + + +#include "gimppattern.h" + + +#define GIMP_TYPE_PATTERN_CLIPBOARD (gimp_pattern_clipboard_get_type ()) +#define GIMP_PATTERN_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PATTERN_CLIPBOARD, GimpPatternClipboard)) +#define GIMP_PATTERN_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PATTERN_CLIPBOARD, GimpPatternClipboardClass)) +#define GIMP_IS_PATTERN_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PATTERN_CLIPBOARD)) +#define GIMP_IS_PATTERN_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PATTERN_CLIPBOARD)) +#define GIMP_PATTERN_CLIPBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PATTERN_CLIPBOARD, GimpPatternClipboardClass)) + + +typedef struct _GimpPatternClipboardClass GimpPatternClipboardClass; + +struct _GimpPatternClipboard +{ + GimpPattern parent_instance; + + Gimp *gimp; +}; + +struct _GimpPatternClipboardClass +{ + GimpPatternClass parent_class; +}; + + +GType gimp_pattern_clipboard_get_type (void) G_GNUC_CONST; + +GimpData * gimp_pattern_clipboard_new (Gimp *gimp); + + +#endif /* __GIMP_PATTERN_CLIPBOARD_H__ */ diff --git a/app/core/gimppdbprogress.c b/app/core/gimppdbprogress.c new file mode 100644 index 0000000..85147e8 --- /dev/null +++ b/app/core/gimppdbprogress.c @@ -0,0 +1,408 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppdbprogress.c + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "core/gimpcontext.h" + +#include "pdb/gimppdb.h" + +#include "gimp.h" +#include "gimpparamspecs.h" +#include "gimppdbprogress.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_PDB, + PROP_CONTEXT, + PROP_CALLBACK_NAME +}; + + +static void gimp_pdb_progress_class_init (GimpPdbProgressClass *klass); +static void gimp_pdb_progress_init (GimpPdbProgress *progress, + GimpPdbProgressClass *klass); +static void gimp_pdb_progress_progress_iface_init (GimpProgressInterface *iface); + +static void gimp_pdb_progress_constructed (GObject *object); +static void gimp_pdb_progress_dispose (GObject *object); +static void gimp_pdb_progress_finalize (GObject *object); +static void gimp_pdb_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static GimpProgress * gimp_pdb_progress_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message); +static void gimp_pdb_progress_progress_end (GimpProgress *progress); +static gboolean gimp_pdb_progress_progress_is_active (GimpProgress *progress); +static void gimp_pdb_progress_progress_set_text (GimpProgress *progress, + const gchar *message); +static void gimp_pdb_progress_progress_set_value (GimpProgress *progress, + gdouble percentage); +static gdouble gimp_pdb_progress_progress_get_value (GimpProgress *progress); +static void gimp_pdb_progress_progress_pulse (GimpProgress *progress); +static guint32 gimp_pdb_progress_progress_get_window_id (GimpProgress *progress); + + +static GObjectClass *parent_class = NULL; + + +GType +gimp_pdb_progress_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GimpPdbProgressClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gimp_pdb_progress_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GimpPdbProgress), + 0, /* n_preallocs */ + (GInstanceInitFunc) gimp_pdb_progress_init, + }; + + const GInterfaceInfo progress_iface_info = + { + (GInterfaceInitFunc) gimp_pdb_progress_progress_iface_init, + NULL, /* iface_finalize */ + NULL /* iface_data */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "GimpPdbProgress", + &info, 0); + + g_type_add_interface_static (type, GIMP_TYPE_PROGRESS, + &progress_iface_info); + } + + return type; +} + +static void +gimp_pdb_progress_class_init (GimpPdbProgressClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->constructed = gimp_pdb_progress_constructed; + object_class->dispose = gimp_pdb_progress_dispose; + object_class->finalize = gimp_pdb_progress_finalize; + object_class->set_property = gimp_pdb_progress_set_property; + + g_object_class_install_property (object_class, PROP_PDB, + g_param_spec_object ("pdb", NULL, NULL, + GIMP_TYPE_PDB, + GIMP_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_CONTEXT, + g_param_spec_object ("context", NULL, NULL, + GIMP_TYPE_CONTEXT, + GIMP_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_CALLBACK_NAME, + g_param_spec_string ("callback-name", + NULL, NULL, + NULL, + GIMP_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_pdb_progress_init (GimpPdbProgress *progress, + GimpPdbProgressClass *klass) +{ + klass->progresses = g_list_prepend (klass->progresses, progress); +} + +static void +gimp_pdb_progress_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_pdb_progress_progress_start; + iface->end = gimp_pdb_progress_progress_end; + iface->is_active = gimp_pdb_progress_progress_is_active; + iface->set_text = gimp_pdb_progress_progress_set_text; + iface->set_value = gimp_pdb_progress_progress_set_value; + iface->get_value = gimp_pdb_progress_progress_get_value; + iface->pulse = gimp_pdb_progress_progress_pulse; + iface->get_window_id = gimp_pdb_progress_progress_get_window_id; +} + +static void +gimp_pdb_progress_constructed (GObject *object) +{ + GimpPdbProgress *progress = GIMP_PDB_PROGRESS (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_PDB (progress->pdb)); + gimp_assert (GIMP_IS_CONTEXT (progress->context)); +} + +static void +gimp_pdb_progress_dispose (GObject *object) +{ + GimpPdbProgressClass *klass = GIMP_PDB_PROGRESS_GET_CLASS (object); + + klass->progresses = g_list_remove (klass->progresses, object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_pdb_progress_finalize (GObject *object) +{ + GimpPdbProgress *progress = GIMP_PDB_PROGRESS (object); + + g_clear_object (&progress->pdb); + g_clear_object (&progress->context); + g_clear_pointer (&progress->callback_name, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_pdb_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpPdbProgress *progress = GIMP_PDB_PROGRESS (object); + + switch (property_id) + { + case PROP_PDB: + if (progress->pdb) + g_object_unref (progress->pdb); + progress->pdb = g_value_dup_object (value); + break; + + case PROP_CONTEXT: + if (progress->context) + g_object_unref (progress->context); + progress->context = g_value_dup_object (value); + break; + + case PROP_CALLBACK_NAME: + if (progress->callback_name) + g_free (progress->callback_name); + progress->callback_name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gdouble +gimp_pdb_progress_run_callback (GimpPdbProgress *progress, + GimpProgressCommand command, + const gchar *text, + gdouble value) +{ + gdouble retval = 0; + + if (progress->callback_name && ! progress->callback_busy) + { + GimpValueArray *return_vals; + + progress->callback_busy = TRUE; + + return_vals = + gimp_pdb_execute_procedure_by_name (progress->pdb, + progress->context, + NULL, NULL, + progress->callback_name, + GIMP_TYPE_INT32, command, + G_TYPE_STRING, text, + G_TYPE_DOUBLE, value, + G_TYPE_NONE); + + if (g_value_get_enum (gimp_value_array_index (return_vals, 0)) != + GIMP_PDB_SUCCESS) + { + gimp_message (progress->context->gimp, NULL, GIMP_MESSAGE_ERROR, + _("Unable to run %s callback. " + "The corresponding plug-in may have crashed."), + g_type_name (G_TYPE_FROM_INSTANCE (progress))); + } + else if (gimp_value_array_length (return_vals) >= 2 && + G_VALUE_HOLDS_DOUBLE (gimp_value_array_index (return_vals, 1))) + { + retval = g_value_get_double (gimp_value_array_index (return_vals, 1)); + } + + gimp_value_array_unref (return_vals); + + progress->callback_busy = FALSE; + } + + return retval; +} + +static GimpProgress * +gimp_pdb_progress_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + if (! pdb_progress->active) + { + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_START, + message, 0.0); + + pdb_progress->active = TRUE; + pdb_progress->value = 0.0; + + return progress; + } + + return NULL; +} + +static void +gimp_pdb_progress_progress_end (GimpProgress *progress) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + if (pdb_progress->active) + { + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_END, + NULL, 0.0); + + pdb_progress->active = FALSE; + pdb_progress->value = 0.0; + } +} + +static gboolean +gimp_pdb_progress_progress_is_active (GimpProgress *progress) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + return pdb_progress->active; +} + +static void +gimp_pdb_progress_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + if (pdb_progress->active) + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_SET_TEXT, + message, 0.0); +} + +static void +gimp_pdb_progress_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + if (pdb_progress->active) + { + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_SET_VALUE, + NULL, percentage); + pdb_progress->value = percentage; + } +} + +static gdouble +gimp_pdb_progress_progress_get_value (GimpProgress *progress) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + return pdb_progress->value; + +} + +static void +gimp_pdb_progress_progress_pulse (GimpProgress *progress) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + if (pdb_progress->active) + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_PULSE, + NULL, 0.0); +} + +static guint32 +gimp_pdb_progress_progress_get_window_id (GimpProgress *progress) +{ + GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + + return (guint32) + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_GET_WINDOW, + NULL, 0.0); +} + +GimpPdbProgress * +gimp_pdb_progress_get_by_callback (GimpPdbProgressClass *klass, + const gchar *callback_name) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_PDB_PROGRESS_CLASS (klass), NULL); + g_return_val_if_fail (callback_name != NULL, NULL); + + for (list = klass->progresses; list; list = g_list_next (list)) + { + GimpPdbProgress *progress = list->data; + + if (! g_strcmp0 (callback_name, progress->callback_name)) + return progress; + } + + return NULL; +} diff --git a/app/core/gimppdbprogress.h b/app/core/gimppdbprogress.h new file mode 100644 index 0000000..4057332 --- /dev/null +++ b/app/core/gimppdbprogress.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppdbprogress.h + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PDB_PROGRESS_H__ +#define __GIMP_PDB_PROGRESS_H__ + +G_BEGIN_DECLS + + +#define GIMP_TYPE_PDB_PROGRESS (gimp_pdb_progress_get_type ()) +#define GIMP_PDB_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PDB_PROGRESS, GimpPdbProgress)) +#define GIMP_PDB_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PDB_PROGRESS, GimpPdbProgressClass)) +#define GIMP_IS_PDB_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PDB_PROGRESS)) +#define GIMP_IS_PDB_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PDB_PROGRESS)) +#define GIMP_PDB_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PDB_PROGRESS, GimpPdbProgressClass)) + + +typedef struct _GimpPdbProgressClass GimpPdbProgressClass; + +struct _GimpPdbProgress +{ + GObject object; + + gboolean active; + gdouble value; + + GimpPDB *pdb; + GimpContext *context; + gchar *callback_name; + gboolean callback_busy; +}; + +struct _GimpPdbProgressClass +{ + GObjectClass parent_class; + + GList *progresses; +}; + + +GType gimp_pdb_progress_get_type (void) G_GNUC_CONST; + +GimpPdbProgress * gimp_pdb_progress_get_by_callback (GimpPdbProgressClass *klass, + const gchar *callback_name); + + +G_END_DECLS + +#endif /* __GIMP_PDB_PROGRESS_H__ */ diff --git a/app/core/gimppickable-auto-shrink.c b/app/core/gimppickable-auto-shrink.c new file mode 100644 index 0000000..7ddbee2 --- /dev/null +++ b/app/core/gimppickable-auto-shrink.c @@ -0,0 +1,312 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpimage.h" +#include "gimppickable.h" +#include "gimppickable-auto-shrink.h" + + +typedef enum +{ + AUTO_SHRINK_NOTHING = 0, + AUTO_SHRINK_ALPHA = 1, + AUTO_SHRINK_COLOR = 2 +} AutoShrinkType; + + +typedef gboolean (* ColorsEqualFunc) (guchar *col1, + guchar *col2); + + +/* local function prototypes */ + +static AutoShrinkType gimp_pickable_guess_bgcolor (GimpPickable *pickable, + guchar *color, + gint x1, + gint x2, + gint y1, + gint y2); +static gboolean gimp_pickable_colors_equal (guchar *col1, + guchar *col2); +static gboolean gimp_pickable_colors_alpha (guchar *col1, + guchar *col2); + + +/* public functions */ + +GimpAutoShrink +gimp_pickable_auto_shrink (GimpPickable *pickable, + gint start_x, + gint start_y, + gint start_width, + gint start_height, + gint *shrunk_x, + gint *shrunk_y, + gint *shrunk_width, + gint *shrunk_height) +{ + GeglBuffer *buffer; + GeglRectangle rect; + ColorsEqualFunc colors_equal_func; + guchar bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 }; + guchar *buf = NULL; + gint x1, y1, x2, y2; + gint width, height; + const Babl *format; + gint x, y, abort; + GimpAutoShrink retval = GIMP_AUTO_SHRINK_UNSHRINKABLE; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE); + g_return_val_if_fail (shrunk_x != NULL, FALSE); + g_return_val_if_fail (shrunk_y != NULL, FALSE); + g_return_val_if_fail (shrunk_width != NULL, FALSE); + g_return_val_if_fail (shrunk_height != NULL, FALSE); + + gimp_set_busy (gimp_pickable_get_image (pickable)->gimp); + + /* You should always keep in mind that x2 and y2 are the NOT the + * coordinates of the bottomright corner of the area to be + * cropped. They point at the pixel located one to the right and one + * to the bottom. + */ + + gimp_pickable_flush (pickable); + + buffer = gimp_pickable_get_buffer (pickable); + + x1 = MAX (start_x, 0); + y1 = MAX (start_y, 0); + x2 = MIN (start_x + start_width, gegl_buffer_get_width (buffer)); + y2 = MIN (start_y + start_height, gegl_buffer_get_height (buffer)); + + /* By default, return the start values */ + *shrunk_x = x1; + *shrunk_y = y1; + *shrunk_width = x2 - x1; + *shrunk_height = y2 - y1; + + format = babl_format ("R'G'B'A u8"); + + switch (gimp_pickable_guess_bgcolor (pickable, bgcolor, + x1, x2 - 1, y1, y2 - 1)) + { + case AUTO_SHRINK_ALPHA: + colors_equal_func = gimp_pickable_colors_alpha; + break; + case AUTO_SHRINK_COLOR: + colors_equal_func = gimp_pickable_colors_equal; + break; + default: + goto FINISH; + break; + } + + width = x2 - x1; + height = y2 - y1; + + /* The following could be optimized further by processing + * the smaller side first instead of defaulting to width --Sven + */ + + buf = g_malloc (MAX (width, height) * 4); + + /* Check how many of the top lines are uniform/transparent. */ + rect.x = x1; + rect.y = 0; + rect.width = width; + rect.height = 1; + + abort = FALSE; + for (y = y1; y < y2 && !abort; y++) + { + rect.y = y; + gegl_buffer_get (buffer, &rect, 1.0, format, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + for (x = 0; x < width && !abort; x++) + abort = ! colors_equal_func (bgcolor, buf + x * 4); + } + if (y == y2 && !abort) + { + retval = GIMP_AUTO_SHRINK_EMPTY; + goto FINISH; + } + y1 = y - 1; + + /* Check how many of the bottom lines are uniform/transparent. */ + rect.x = x1; + rect.y = 0; + rect.width = width; + rect.height = 1; + + abort = FALSE; + for (y = y2; y > y1 && !abort; y--) + { + rect.y = y - 1; + gegl_buffer_get (buffer, &rect, 1.0, format, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + for (x = 0; x < width && !abort; x++) + abort = ! colors_equal_func (bgcolor, buf + x * 4); + } + y2 = y + 1; + + /* compute a new height for the next operations */ + height = y2 - y1; + + /* Check how many of the left lines are uniform/transparent. */ + rect.x = 0; + rect.y = y1; + rect.width = 1; + rect.height = height; + + abort = FALSE; + for (x = x1; x < x2 && !abort; x++) + { + rect.x = x; + gegl_buffer_get (buffer, &rect, 1.0, format, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + for (y = 0; y < height && !abort; y++) + abort = ! colors_equal_func (bgcolor, buf + y * 4); + } + x1 = x - 1; + + /* Check how many of the right lines are uniform/transparent. */ + rect.x = 0; + rect.y = y1; + rect.width = 1; + rect.height = height; + + abort = FALSE; + for (x = x2; x > x1 && !abort; x--) + { + rect.x = x - 1; + gegl_buffer_get (buffer, &rect, 1.0, format, buf, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + for (y = 0; y < height && !abort; y++) + abort = ! colors_equal_func (bgcolor, buf + y * 4); + } + x2 = x + 1; + + if (x1 != start_x || + y1 != start_y || + x2 - x1 != start_width || + y2 - y1 != start_height) + { + *shrunk_x = x1; + *shrunk_y = y1; + *shrunk_width = x2 - x1; + *shrunk_height = y2 - y1; + + retval = GIMP_AUTO_SHRINK_SHRINK; + } + + FINISH: + + g_free (buf); + gimp_unset_busy (gimp_pickable_get_image (pickable)->gimp); + + return retval; +} + + +/* private functions */ + +static AutoShrinkType +gimp_pickable_guess_bgcolor (GimpPickable *pickable, + guchar *color, + gint x1, + gint x2, + gint y1, + gint y2) +{ + const Babl *format = babl_format ("R'G'B'A u8"); + guchar tl[4]; + guchar tr[4]; + guchar bl[4]; + guchar br[4]; + gint i; + + for (i = 0; i < 4; i++) + color[i] = 0; + + /* First check if there's transparency to crop. If not, guess the + * background-color to see if at least 2 corners are equal. + */ + + if (! gimp_pickable_get_pixel_at (pickable, x1, y1, format, tl) || + ! gimp_pickable_get_pixel_at (pickable, x1, y2, format, tr) || + ! gimp_pickable_get_pixel_at (pickable, x2, y1, format, bl) || + ! gimp_pickable_get_pixel_at (pickable, x2, y2, format, br)) + { + return AUTO_SHRINK_NOTHING; + } + + if ((tl[ALPHA] == 0 && tr[ALPHA] == 0) || + (tl[ALPHA] == 0 && bl[ALPHA] == 0) || + (tr[ALPHA] == 0 && br[ALPHA] == 0) || + (bl[ALPHA] == 0 && br[ALPHA] == 0)) + { + return AUTO_SHRINK_ALPHA; + } + + if (gimp_pickable_colors_equal (tl, tr) || + gimp_pickable_colors_equal (tl, bl)) + { + memcpy (color, tl, 4); + return AUTO_SHRINK_COLOR; + } + + if (gimp_pickable_colors_equal (br, bl) || + gimp_pickable_colors_equal (br, tr)) + { + memcpy (color, br, 4); + return AUTO_SHRINK_COLOR; + } + + return AUTO_SHRINK_NOTHING; +} + +static gboolean +gimp_pickable_colors_equal (guchar *col1, + guchar *col2) +{ + gint b; + + for (b = 0; b < 4; b++) + { + if (col1[b] != col2[b]) + return FALSE; + } + + return TRUE; +} + +static gboolean +gimp_pickable_colors_alpha (guchar *dummy, + guchar *col) +{ + return (col[ALPHA] == 0); +} diff --git a/app/core/gimppickable-auto-shrink.h b/app/core/gimppickable-auto-shrink.h new file mode 100644 index 0000000..2f2eaa0 --- /dev/null +++ b/app/core/gimppickable-auto-shrink.h @@ -0,0 +1,41 @@ +/* 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 . + */ + +#ifndef __GIMP_PICKABLE_AUTO_SHRINK_H__ +#define __GIMP_PICKABLE_AUTO_SHRINK_H__ + + +typedef enum +{ + GIMP_AUTO_SHRINK_SHRINK, + GIMP_AUTO_SHRINK_EMPTY, + GIMP_AUTO_SHRINK_UNSHRINKABLE +} GimpAutoShrink; + + +GimpAutoShrink gimp_pickable_auto_shrink (GimpPickable *pickable, + gint x, + gint y, + gint width, + gint height, + gint *shrunk_x, + gint *shrunk_y, + gint *shrunk_width, + gint *shrunk_height); + + +#endif /* __GIMP_PICKABLE_AUTO_SHRINK_H__ */ diff --git a/app/core/gimppickable-contiguous-region.cc b/app/core/gimppickable-contiguous-region.cc new file mode 100644 index 0000000..ea30d0c --- /dev/null +++ b/app/core/gimppickable-contiguous-region.cc @@ -0,0 +1,1123 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +extern "C" +{ + +#include "core-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimp-parallel.h" +#include "gimp-utils.h" /* GIMP_TIMER */ +#include "gimpasync.h" +#include "gimplineart.h" +#include "gimppickable.h" +#include "gimppickable-contiguous-region.h" + + +#define EPSILON 1e-6 + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + + +typedef struct +{ + gint x; + gint y; + gint level; +} BorderPixel; + + +/* local function prototypes */ + +static const Babl * choose_format (GeglBuffer *buffer, + GimpSelectCriterion select_criterion, + gint *n_components, + gboolean *has_alpha); +static gfloat pixel_difference (const gfloat *col1, + const gfloat *col2, + gboolean antialias, + gfloat threshold, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion); +static void push_segment (GQueue *segment_queue, + gint y, + gint old_y, + gint start, + gint end, + gint new_y, + gint new_start, + gint new_end); +static void pop_segment (GQueue *segment_queue, + gint *y, + gint *old_y, + gint *start, + gint *end); +static gboolean find_contiguous_segment (const gfloat *col, + GeglBuffer *src_buffer, + GeglSampler *src_sampler, + const GeglRectangle *src_extent, + GeglBuffer *mask_buffer, + const Babl *src_format, + const Babl *mask_format, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean antialias, + gfloat threshold, + gint initial_x, + gint initial_y, + gint *start, + gint *end, + gfloat *row); +static void find_contiguous_region (GeglBuffer *src_buffer, + GeglBuffer *mask_buffer, + const Babl *format, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean antialias, + gfloat threshold, + gboolean diagonal_neighbors, + gint x, + gint y, + const gfloat *col); + +static void line_art_queue_pixel (GQueue *queue, + gint x, + gint y, + gint level); + + +/* public functions */ + +GeglBuffer * +gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable, + gboolean antialias, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean diagonal_neighbors, + gint x, + gint y) +{ + GeglBuffer *src_buffer; + GeglBuffer *mask_buffer; + const Babl *format; + GeglRectangle extent; + gint n_components; + gboolean has_alpha; + gfloat start_col[MAX_CHANNELS]; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + + gimp_pickable_flush (pickable); + src_buffer = gimp_pickable_get_buffer (pickable); + + format = choose_format (src_buffer, select_criterion, + &n_components, &has_alpha); + gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + if (has_alpha) + { + if (select_transparent) + { + /* don't select transparent regions if the start pixel isn't + * fully transparent + */ + if (start_col[n_components - 1] > 0) + select_transparent = FALSE; + } + } + else + { + select_transparent = FALSE; + } + + extent = *gegl_buffer_get_extent (src_buffer); + + mask_buffer = gegl_buffer_new (&extent, babl_format ("Y float")); + + if (x >= extent.x && x < (extent.x + extent.width) && + y >= extent.y && y < (extent.y + extent.height)) + { + GIMP_TIMER_START(); + + find_contiguous_region (src_buffer, mask_buffer, + format, n_components, has_alpha, + select_transparent, select_criterion, + antialias, threshold, diagonal_neighbors, + x, y, start_col); + + GIMP_TIMER_END("foo"); + } + + return mask_buffer; +} + +GeglBuffer * +gimp_pickable_contiguous_region_by_color (GimpPickable *pickable, + gboolean antialias, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + const GimpRGB *color) +{ + /* Scan over the pickable's active layer, finding pixels within the + * specified threshold from the given R, G, & B values. If + * antialiasing is on, use the same antialiasing scheme as in + * fuzzy_select. Modify the pickable's mask to reflect the + * additional selection + */ + GeglBuffer *src_buffer; + GeglBuffer *mask_buffer; + const Babl *format; + gint n_components; + gboolean has_alpha; + gfloat start_col[MAX_CHANNELS]; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + g_return_val_if_fail (color != NULL, NULL); + + /* increase the threshold by EPSILON, to allow for conversion errors, + * especially when threshold == 0 (see issue #1554.) we need to do this + * here, but not in the other functions, since the input color gets converted + * to the format in which we perform the comparison through a different path + * than the pickable's pixels, which can introduce error. + */ + threshold += EPSILON; + + gimp_pickable_flush (pickable); + + src_buffer = gimp_pickable_get_buffer (pickable); + + format = choose_format (src_buffer, select_criterion, + &n_components, &has_alpha); + + gimp_rgba_get_pixel (color, format, start_col); + + if (has_alpha) + { + if (select_transparent) + { + /* don't select transparency if "color" isn't fully transparent + */ + if (start_col[n_components - 1] > 0.0) + select_transparent = FALSE; + } + } + else + { + select_transparent = FALSE; + } + + mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), + babl_format ("Y float")); + + gegl_parallel_distribute_area ( + gegl_buffer_get_extent (src_buffer), PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + GeglBufferIterator *iter; + + iter = gegl_buffer_iterator_new (src_buffer, + area, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, mask_buffer, + area, 0, babl_format ("Y float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *src = (const gfloat *) iter->items[0].data; + gfloat *dest = ( gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + /* Find how closely the colors match */ + *dest = pixel_difference (start_col, src, + antialias, + threshold, + n_components, + has_alpha, + select_transparent, + select_criterion); + + src += n_components; + dest += 1; + } + } + }); + + return mask_buffer; +} + +GeglBuffer * +gimp_pickable_contiguous_region_by_line_art (GimpPickable *pickable, + GimpLineArt *line_art, + gint x, + gint y) +{ + GeglBuffer *src_buffer; + GeglBuffer *mask_buffer; + const Babl *format = babl_format ("Y float"); + gfloat *distmap = NULL; + GeglRectangle extent; + gboolean free_line_art = FALSE; + gboolean filled = FALSE; + guchar start_col; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable) || GIMP_IS_LINE_ART (line_art), NULL); + + if (! line_art) + { + /* It is much better experience to pre-compute the line art, + * but it may not be always possible (for instance when + * selecting/filling through a PDB call). + */ + line_art = gimp_line_art_new (); + gimp_line_art_set_input (line_art, pickable); + free_line_art = TRUE; + } + + src_buffer = gimp_line_art_get (line_art, &distmap); + g_return_val_if_fail (src_buffer && distmap, NULL); + + gegl_buffer_sample (src_buffer, x, y, NULL, &start_col, NULL, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + extent = *gegl_buffer_get_extent (src_buffer); + + mask_buffer = gegl_buffer_new (&extent, format); + + if (start_col) + { + if (start_col == 1) + { + /* As a special exception, if you fill over a line art pixel, only + * fill the pixel and exit + */ + gfloat col = 1.0; + + gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (x, y, 1, 1), + 0, format, &col, GEGL_AUTO_ROWSTRIDE); + } + else /* start_col == 2 */ + { + /* If you fill over a closure pixel, let's fill on all sides + * of the start point. Otherwise we get a very weird result + * with only a single pixel filled in the middle of an empty + * region (since closure pixels are invisible by nature). + */ + gfloat col = 0.0; + + if (x - 1 >= extent.x && x - 1 < extent.x + extent.width && + y - 1 >= extent.y && y - 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x - 1, y - 1, &col); + if (x - 1 >= extent.x && x - 1 < extent.x + extent.width && + y >= extent.y && y < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x - 1, y, &col); + if (x - 1 >= extent.x && x - 1 < extent.x + extent.width && + y + 1 >= extent.y && y + 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x - 1, y + 1, &col); + if (x >= extent.x && x < extent.x + extent.width && + y - 1 >= extent.y && y - 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x, y - 1, &col); + if (x >= extent.x && x < extent.x + extent.width && + y + 1 >= extent.y && y + 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x, y + 1, &col); + if (x + 1 >= extent.x && x + 1 < extent.x + extent.width && + y - 1 >= extent.y && y - 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x + 1, y - 1, &col); + if (x + 1 >= extent.x && x + 1 < extent.x + extent.width && + y >= extent.y && y < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x + 1, y, &col); + if (x + 1 >= extent.x && x + 1 < extent.x + extent.width && + y + 1 >= extent.y && y + 1 < (extent.y + extent.height)) + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x + 1, y + 1, &col); + filled = TRUE; + } + } + else if (x >= extent.x && x < (extent.x + extent.width) && + y >= extent.y && y < (extent.y + extent.height)) + { + gfloat col = 0.0; + + find_contiguous_region (src_buffer, mask_buffer, + format, 1, FALSE, + FALSE, GIMP_SELECT_CRITERION_COMPOSITE, + FALSE, 0.0, FALSE, + x, y, &col); + filled = TRUE; + } + + if (filled) + { + GQueue *queue = g_queue_new (); + gfloat *mask; + gint width = gegl_buffer_get_width (src_buffer); + gint height = gegl_buffer_get_height (src_buffer); + gint line_art_max_grow; + gint nx, ny; + + GIMP_TIMER_START(); + /* The last step of the line art algorithm is to make sure that + * selections does not leave "holes" between its borders and the + * line arts, while not stepping over as well. + * I used to run the "gegl:watershed-transform" operation to flood + * the stroke pixels, but for such simple need, this simple code + * is so much faster while producing better results. + */ + mask = g_new (gfloat, width * height); + gegl_buffer_get (mask_buffer, NULL, 1.0, NULL, + mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + if (distmap[x + y * width] == 1.0) + { + if (x > 0) + { + nx = x - 1; + if (y > 0) + { + ny = y - 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + ny = y; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + if (y < height - 1) + { + ny = y + 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + } + if (x < width - 1) + { + nx = x + 1; + if (y > 0) + { + ny = y - 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + ny = y; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + if (y < height - 1) + { + ny = y + 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + } + nx = x; + if (y > 0) + { + ny = y - 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + if (y < height - 1) + { + ny = y + 1; + if (mask[nx + ny * width] != 0.0) + { + line_art_queue_pixel (queue, x, y, 1); + continue; + } + } + } + } + + g_object_get (line_art, + "max-grow", &line_art_max_grow, + NULL); + while (! g_queue_is_empty (queue)) + { + BorderPixel *c = (BorderPixel *) g_queue_pop_head (queue); + + if (mask[c->x + c->y * width] != 1.0) + { + mask[c->x + c->y * width] = 1.0; + if (c->level >= line_art_max_grow) + /* Do not overflood under line arts. */ + continue; + if (c->x > 0) + { + nx = c->x - 1; + if (c->y > 0) + { + ny = c->y - 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + ny = c->y; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + if (c->y < height - 1) + { + ny = c->y + 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + } + if (c->x < width - 1) + { + nx = c->x + 1; + if (c->y > 0) + { + ny = c->y - 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + ny = c->y; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + if (c->y < height - 1) + { + ny = c->y + 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + } + nx = c->x; + if (c->y > 0) + { + ny = c->y - 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + if (c->y < height - 1) + { + ny = c->y + 1; + if (mask[nx + ny * width] == 0.0 && + distmap[nx + ny * width] > distmap[c->x + c->y * width]) + line_art_queue_pixel (queue, nx, ny, c->level + 1); + } + } + g_free (c); + } + g_queue_free (queue); + gegl_buffer_set (mask_buffer, gegl_buffer_get_extent (mask_buffer), + 0, NULL, mask, GEGL_AUTO_ROWSTRIDE); + g_free (mask); + + GIMP_TIMER_END("watershed line art"); + } + if (free_line_art) + g_clear_object (&line_art); + + return mask_buffer; +} + +/* private functions */ + +static const Babl * +choose_format (GeglBuffer *buffer, + GimpSelectCriterion select_criterion, + gint *n_components, + gboolean *has_alpha) +{ + const Babl *format = gegl_buffer_get_format (buffer); + + *has_alpha = babl_format_has_alpha (format); + + switch (select_criterion) + { + case GIMP_SELECT_CRITERION_COMPOSITE: + if (babl_format_is_palette (format)) + format = babl_format ("R'G'B'A float"); + else + format = gimp_babl_format (gimp_babl_format_get_base_type (format), + GIMP_PRECISION_FLOAT_GAMMA, + *has_alpha); + break; + + case GIMP_SELECT_CRITERION_R: + case GIMP_SELECT_CRITERION_G: + case GIMP_SELECT_CRITERION_B: + case GIMP_SELECT_CRITERION_A: + format = babl_format ("R'G'B'A float"); + break; + + case GIMP_SELECT_CRITERION_H: + case GIMP_SELECT_CRITERION_S: + case GIMP_SELECT_CRITERION_V: + format = babl_format ("HSVA float"); + break; + + case GIMP_SELECT_CRITERION_LCH_L: + format = babl_format ("CIE L alpha float"); + break; + + case GIMP_SELECT_CRITERION_LCH_C: + case GIMP_SELECT_CRITERION_LCH_H: + format = babl_format ("CIE LCH(ab) alpha float"); + break; + + default: + g_return_val_if_reached (NULL); + break; + } + + *n_components = babl_format_get_n_components (format); + + return format; +} + +static gfloat +pixel_difference (const gfloat *col1, + const gfloat *col2, + gboolean antialias, + gfloat threshold, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion) +{ + gfloat max = 0.0; + + /* if there is an alpha channel, never select transparent regions */ + if (! select_transparent && has_alpha && col2[n_components - 1] == 0.0) + return 0.0; + + if (select_transparent && has_alpha) + { + max = fabs (col1[n_components - 1] - col2[n_components - 1]); + } + else + { + gfloat diff; + gint b; + + if (has_alpha) + n_components--; + + switch (select_criterion) + { + case GIMP_SELECT_CRITERION_COMPOSITE: + for (b = 0; b < n_components; b++) + { + diff = fabs (col1[b] - col2[b]); + if (diff > max) + max = diff; + } + break; + + case GIMP_SELECT_CRITERION_R: + max = fabs (col1[0] - col2[0]); + break; + + case GIMP_SELECT_CRITERION_G: + max = fabs (col1[1] - col2[1]); + break; + + case GIMP_SELECT_CRITERION_B: + max = fabs (col1[2] - col2[2]); + break; + + case GIMP_SELECT_CRITERION_A: + max = fabs (col1[3] - col2[3]); + break; + + case GIMP_SELECT_CRITERION_H: + if (col1[1] > EPSILON) + { + if (col2[1] > EPSILON) + { + max = fabs (col1[0] - col2[0]); + max = MIN (max, 1.0 - max); + } + else + { + /* "infinite" difference. anything >> 1 will do. */ + max = 10.0; + } + } + else + { + if (col2[1] > EPSILON) + { + /* "infinite" difference. anything >> 1 will do. */ + max = 10.0; + } + else + { + max = 0.0; + } + } + break; + + case GIMP_SELECT_CRITERION_S: + max = fabs (col1[1] - col2[1]); + break; + + case GIMP_SELECT_CRITERION_V: + max = fabs (col1[2] - col2[2]); + break; + + case GIMP_SELECT_CRITERION_LCH_L: + max = fabs (col1[0] - col2[0]) / 100.0; + break; + + case GIMP_SELECT_CRITERION_LCH_C: + max = fabs (col1[1] - col2[1]) / 100.0; + break; + + case GIMP_SELECT_CRITERION_LCH_H: + if (col1[1] > 100.0 * EPSILON) + { + if (col2[1] > 100.0 * EPSILON) + { + max = fabs (col1[2] - col2[2]) / 360.0; + max = MIN (max, 1.0 - max); + } + else + { + /* "infinite" difference. anything >> 1 will do. */ + max = 10.0; + } + } + else + { + if (col2[1] > 100.0 * EPSILON) + { + /* "infinite" difference. anything >> 1 will do. */ + max = 10.0; + } + else + { + max = 0.0; + } + } + break; + } + } + + if (antialias && threshold > 0.0) + { + gfloat aa = 1.5 - (max / threshold); + + if (aa <= 0.0) + return 0.0; + else if (aa < 0.5) + return aa * 2.0; + else + return 1.0; + } + else + { + if (max > threshold) + return 0.0; + else + return 1.0; + } +} + +static void +push_segment (GQueue *segment_queue, + gint y, + gint old_y, + gint start, + gint end, + gint new_y, + gint new_start, + gint new_end) +{ + /* To avoid excessive memory allocation (y, old_y, start, end) tuples are + * stored in interleaved format: + * + * [y1] [old_y1] [start1] [end1] [y2] [old_y2] [start2] [end2] + */ + + if (new_y != old_y) + { + /* If the new segment's y-coordinate is different than the old (source) + * segment's y-coordinate, push the entire segment. + */ + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_start)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_end)); + } + else + { + /* Otherwise, only push the set-difference between the new segment and + * the source segment (since we've already scanned the source segment.) + * Note that the `+ 1` and `- 1` terms of the end/start coordinates below + * are only necessary when `diagonal_neighbors` is on (and otherwise make + * the segments slightly larger than necessary), but, meh... + */ + if (new_start < start) + { + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_start)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (start + 1)); + } + + if (new_end > end) + { + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (y)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (end - 1)); + g_queue_push_tail (segment_queue, GINT_TO_POINTER (new_end)); + } + } +} + +static void +pop_segment (GQueue *segment_queue, + gint *y, + gint *old_y, + gint *start, + gint *end) +{ + *y = GPOINTER_TO_INT (g_queue_pop_head (segment_queue)); + *old_y = GPOINTER_TO_INT (g_queue_pop_head (segment_queue)); + *start = GPOINTER_TO_INT (g_queue_pop_head (segment_queue)); + *end = GPOINTER_TO_INT (g_queue_pop_head (segment_queue)); +} + +/* #define FETCH_ROW 1 */ + +static gboolean +find_contiguous_segment (const gfloat *col, + GeglBuffer *src_buffer, + GeglSampler *src_sampler, + const GeglRectangle *src_extent, + GeglBuffer *mask_buffer, + const Babl *src_format, + const Babl *mask_format, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean antialias, + gfloat threshold, + gint initial_x, + gint initial_y, + gint *start, + gint *end, + gfloat *row) +{ + gfloat *s; + gfloat mask_row_buf[src_extent->width]; + gfloat *mask_row = mask_row_buf - src_extent->x; + gfloat diff; + +#ifdef FETCH_ROW + gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, initial_y, width, 1), 1.0, + src_format, + row, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + s = row + initial_x * n_components; +#else + s = (gfloat *) g_alloca (n_components * sizeof (gfloat)); + + gegl_sampler_get (src_sampler, + initial_x, initial_y, NULL, s, GEGL_ABYSS_NONE); +#endif + + diff = pixel_difference (col, s, antialias, threshold, + n_components, has_alpha, select_transparent, + select_criterion); + + /* check the starting pixel */ + if (! diff) + return FALSE; + + mask_row[initial_x] = diff; + + *start = initial_x - 1; +#ifdef FETCH_ROW + s = row + *start * n_components; +#endif + + while (*start >= src_extent->x) + { +#ifndef FETCH_ROW + gegl_sampler_get (src_sampler, + *start, initial_y, NULL, s, GEGL_ABYSS_NONE); +#endif + + diff = pixel_difference (col, s, antialias, threshold, + n_components, has_alpha, select_transparent, + select_criterion); + if (diff == 0.0) + break; + + mask_row[*start] = diff; + + (*start)--; +#ifdef FETCH_ROW + s -= n_components; +#endif + } + + *end = initial_x + 1; +#ifdef FETCH_ROW + s = row + *end * n_components; +#endif + + while (*end < src_extent->x + src_extent->width) + { +#ifndef FETCH_ROW + gegl_sampler_get (src_sampler, + *end, initial_y, NULL, s, GEGL_ABYSS_NONE); +#endif + + diff = pixel_difference (col, s, antialias, threshold, + n_components, has_alpha, select_transparent, + select_criterion); + if (diff == 0.0) + break; + + mask_row[*end] = diff; + + (*end)++; +#ifdef FETCH_ROW + s += n_components; +#endif + } + + gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start + 1, initial_y, + *end - *start - 1, 1), + 0, mask_format, &mask_row[*start + 1], + GEGL_AUTO_ROWSTRIDE); + + return TRUE; +} + +static void +find_contiguous_region (GeglBuffer *src_buffer, + GeglBuffer *mask_buffer, + const Babl *format, + gint n_components, + gboolean has_alpha, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean antialias, + gfloat threshold, + gboolean diagonal_neighbors, + gint x, + gint y, + const gfloat *col) +{ + const Babl *mask_format = babl_format ("Y float"); + GeglSampler *src_sampler; + const GeglRectangle *src_extent; + gint old_y; + gint start, end; + gint new_start, new_end; + GQueue *segment_queue; + gfloat *row = NULL; + + src_extent = gegl_buffer_get_extent (src_buffer); + +#ifdef FETCH_ROW + row = g_new (gfloat, src_extent->width * n_components); +#endif + + src_sampler = gegl_buffer_sampler_new (src_buffer, + format, GEGL_SAMPLER_NEAREST); + + segment_queue = g_queue_new (); + + push_segment (segment_queue, + y, /* dummy values: */ -1, 0, 0, + y, x - 1, x + 1); + + do + { + pop_segment (segment_queue, + &y, &old_y, &start, &end); + + for (x = start + 1; x < end; x++) + { + gfloat val; + + gegl_buffer_get (mask_buffer, GEGL_RECTANGLE (x, y, 1, 1), 1.0, + mask_format, &val, GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + + if (val != 0.0) + { + /* If the current pixel is selected, then we've already visited + * the next pixel. (Note that we assume that the maximal image + * width is sufficiently low that `x` won't overflow.) + */ + x++; + continue; + } + + if (! find_contiguous_segment (col, + src_buffer, src_sampler, src_extent, + mask_buffer, + format, mask_format, + n_components, + has_alpha, + select_transparent, select_criterion, + antialias, threshold, x, y, + &new_start, &new_end, + row)) + continue; + + /* We can skip directly to `new_end + 1` on the next iteration, since + * we've just selected all pixels in the range `[x, new_end)`, and + * the pixel at `new_end` is above threshold. (Note that we assume + * that the maximal image width is sufficiently low that `x` won't + * overflow.) + */ + x = new_end; + + if (diagonal_neighbors) + { + if (new_start >= src_extent->x) + new_start--; + + if (new_end < src_extent->x + src_extent->width) + new_end++; + } + + if (y + 1 < src_extent->y + src_extent->height) + { + push_segment (segment_queue, + y, old_y, start, end, + y + 1, new_start, new_end); + } + + if (y - 1 >= src_extent->y) + { + push_segment (segment_queue, + y, old_y, start, end, + y - 1, new_start, new_end); + } + + } + } + while (! g_queue_is_empty (segment_queue)); + + g_queue_free (segment_queue); + + g_object_unref (src_sampler); + +#ifdef FETCH_ROW + g_free (row); +#endif +} + +static void +line_art_queue_pixel (GQueue *queue, + gint x, + gint y, + gint level) +{ + BorderPixel *p = g_new (BorderPixel, 1); + + p->x = x; + p->y = y; + p->level = level; + + g_queue_push_head (queue, p); +} + +} /* extern "C" */ diff --git a/app/core/gimppickable-contiguous-region.h b/app/core/gimppickable-contiguous-region.h new file mode 100644 index 0000000..26cbe74 --- /dev/null +++ b/app/core/gimppickable-contiguous-region.h @@ -0,0 +1,43 @@ +/* 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 . + */ + +#ifndef __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ +#define __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ + + +GeglBuffer * gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable, + gboolean antialias, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + gboolean diagonal_neighbors, + gint x, + gint y); + +GeglBuffer * gimp_pickable_contiguous_region_by_color (GimpPickable *pickable, + gboolean antialias, + gfloat threshold, + gboolean select_transparent, + GimpSelectCriterion select_criterion, + const GimpRGB *color); + +GeglBuffer * gimp_pickable_contiguous_region_by_line_art (GimpPickable *pickable, + GimpLineArt *line_art, + gint x, + gint y); + +#endif /* __GIMP_PICKABLE_CONTIGUOUS_REGION_H__ */ diff --git a/app/core/gimppickable.c b/app/core/gimppickable.c new file mode 100644 index 0000000..2949def --- /dev/null +++ b/app/core/gimppickable.c @@ -0,0 +1,378 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimppickable.c + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains an interface for pixel objects that their color at + * a given position can be picked. Also included is a utility for + * sampling an average area (which uses the implemented picking + * functions). + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpobject.h" +#include "gimpimage.h" +#include "gimppickable.h" + + +/* local function prototypes */ + +static void gimp_pickable_real_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); + + +G_DEFINE_INTERFACE (GimpPickable, gimp_pickable, GIMP_TYPE_OBJECT) + + +/* private functions */ + + +static void +gimp_pickable_default_init (GimpPickableInterface *iface) +{ + iface->get_pixel_average = gimp_pickable_real_get_pixel_average; + + g_object_interface_install_property (iface, + g_param_spec_object ("buffer", + NULL, NULL, + GEGL_TYPE_BUFFER, + GIMP_PARAM_READABLE)); +} + +static void +gimp_pickable_real_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + const Babl *average_format = babl_format ("RaGaBaA double"); + gdouble average[4] = {}; + gint n = 0; + gint x; + gint y; + gint c; + + for (y = rect->y; y < rect->y + rect->height; y++) + { + for (x = rect->x; x < rect->x + rect->width; x++) + { + gdouble sample[4]; + + if (gimp_pickable_get_pixel_at (pickable, + x, y, average_format, sample)) + { + for (c = 0; c < 4; c++) + average[c] += sample[c]; + + n++; + } + } + } + + if (n > 0) + { + for (c = 0; c < 4; c++) + average[c] /= n; + } + + babl_process (babl_fish (average_format, format), average, pixel, 1); +} + + +/* public functions */ + + +void +gimp_pickable_flush (GimpPickable *pickable) +{ + GimpPickableInterface *pickable_iface; + + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->flush) + pickable_iface->flush (pickable); +} + +GimpImage * +gimp_pickable_get_image (GimpPickable *pickable) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_image) + return pickable_iface->get_image (pickable); + + return NULL; +} + +const Babl * +gimp_pickable_get_format (GimpPickable *pickable) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_format) + return pickable_iface->get_format (pickable); + + return NULL; +} + +const Babl * +gimp_pickable_get_format_with_alpha (GimpPickable *pickable) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_format_with_alpha) + return pickable_iface->get_format_with_alpha (pickable); + + return NULL; +} + +GeglBuffer * +gimp_pickable_get_buffer (GimpPickable *pickable) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_buffer) + return pickable_iface->get_buffer (pickable); + + return NULL; +} + +gboolean +gimp_pickable_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE); + g_return_val_if_fail (pixel != NULL, FALSE); + + if (! format) + format = gimp_pickable_get_format (pickable); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_pixel_at) + return pickable_iface->get_pixel_at (pickable, x, y, format, pixel); + + return FALSE; +} + +void +gimp_pickable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GimpPickableInterface *pickable_iface; + + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + g_return_if_fail (rect != NULL); + g_return_if_fail (pixel != NULL); + + if (! format) + format = gimp_pickable_get_format (pickable); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_pixel_average) + pickable_iface->get_pixel_average (pickable, rect, format, pixel); + else + memset (pixel, 0, babl_format_get_bytes_per_pixel (format)); +} + +gboolean +gimp_pickable_get_color_at (GimpPickable *pickable, + gint x, + gint y, + GimpRGB *color) +{ + gdouble pixel[4]; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + if (! gimp_pickable_get_pixel_at (pickable, x, y, NULL, pixel)) + return FALSE; + + gimp_pickable_pixel_to_srgb (pickable, NULL, pixel, color); + + return TRUE; +} + +gdouble +gimp_pickable_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + GimpPickableInterface *pickable_iface; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), GIMP_OPACITY_TRANSPARENT); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_opacity_at) + return pickable_iface->get_opacity_at (pickable, x, y); + + return GIMP_OPACITY_TRANSPARENT; +} + +void +gimp_pickable_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color) +{ + GimpPickableInterface *pickable_iface; + + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + g_return_if_fail (pixel != NULL); + g_return_if_fail (color != NULL); + + if (! format) + format = gimp_pickable_get_format (pickable); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->pixel_to_srgb) + { + pickable_iface->pixel_to_srgb (pickable, format, pixel, color); + } + else + { + gimp_rgba_set_pixel (color, format, pixel); + } +} + +void +gimp_pickable_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel) +{ + GimpPickableInterface *pickable_iface; + + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + g_return_if_fail (color != NULL); + g_return_if_fail (pixel != NULL); + + if (! format) + format = gimp_pickable_get_format (pickable); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->srgb_to_pixel) + { + pickable_iface->srgb_to_pixel (pickable, color, format, pixel); + } + else + { + gimp_rgba_get_pixel (color, format, pixel); + } +} + +void +gimp_pickable_srgb_to_image_color (GimpPickable *pickable, + const GimpRGB *color, + GimpRGB *image_color) +{ + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + g_return_if_fail (color != NULL); + g_return_if_fail (image_color != NULL); + + gimp_pickable_srgb_to_pixel (pickable, + color, + babl_format ("R'G'B'A double"), + image_color); +} + +gboolean +gimp_pickable_pick_color (GimpPickable *pickable, + gint x, + gint y, + gboolean sample_average, + gdouble average_radius, + gpointer pixel, + GimpRGB *color) +{ + const Babl *format; + gdouble sample[4]; + + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + format = gimp_pickable_get_format (pickable); + + if (! gimp_pickable_get_pixel_at (pickable, x, y, format, sample)) + return FALSE; + + if (pixel) + memcpy (pixel, sample, babl_format_get_bytes_per_pixel (format)); + + if (sample_average) + { + gint radius = floor (average_radius); + + format = babl_format ("RaGaBaA double"); + + gimp_pickable_get_pixel_average (pickable, + GEGL_RECTANGLE (x - radius, + y - radius, + 2 * radius + 1, + 2 * radius + 1), + format, sample); + } + + gimp_pickable_pixel_to_srgb (pickable, format, sample, color); + + return TRUE; +} diff --git a/app/core/gimppickable.h b/app/core/gimppickable.h new file mode 100644 index 0000000..c8c77c9 --- /dev/null +++ b/app/core/gimppickable.h @@ -0,0 +1,110 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimppickable.h + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PICKABLE_H__ +#define __GIMP_PICKABLE_H__ + + +#define GIMP_TYPE_PICKABLE (gimp_pickable_get_type ()) +#define GIMP_IS_PICKABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PICKABLE)) +#define GIMP_PICKABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PICKABLE, GimpPickable)) +#define GIMP_PICKABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_PICKABLE, GimpPickableInterface)) + + +typedef struct _GimpPickableInterface GimpPickableInterface; + +struct _GimpPickableInterface +{ + GTypeInterface base_iface; + + /* virtual functions */ + void (* flush) (GimpPickable *pickable); + GimpImage * (* get_image) (GimpPickable *pickable); + const Babl * (* get_format) (GimpPickable *pickable); + const Babl * (* get_format_with_alpha) (GimpPickable *pickable); + GeglBuffer * (* get_buffer) (GimpPickable *pickable); + gboolean (* get_pixel_at) (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); + gdouble (* get_opacity_at) (GimpPickable *pickable, + gint x, + gint y); + void (* get_pixel_average) (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); + void (* pixel_to_srgb) (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); + void (* srgb_to_pixel) (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); +}; + + +GType gimp_pickable_get_type (void) G_GNUC_CONST; + +void gimp_pickable_flush (GimpPickable *pickable); +GimpImage * gimp_pickable_get_image (GimpPickable *pickable); +const Babl * gimp_pickable_get_format (GimpPickable *pickable); +const Babl * gimp_pickable_get_format_with_alpha (GimpPickable *pickable); +GeglBuffer * gimp_pickable_get_buffer (GimpPickable *pickable); +gboolean gimp_pickable_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +gboolean gimp_pickable_get_color_at (GimpPickable *pickable, + gint x, + gint y, + GimpRGB *color); +gdouble gimp_pickable_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +void gimp_pickable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); +void gimp_pickable_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +void gimp_pickable_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); +void gimp_pickable_srgb_to_image_color (GimpPickable *pickable, + const GimpRGB *color, + GimpRGB *image_color); + +gboolean gimp_pickable_pick_color (GimpPickable *pickable, + gint x, + gint y, + gboolean sample_average, + gdouble average_radius, + gpointer pixel, + GimpRGB *color); + + +#endif /* __GIMP_PICKABLE_H__ */ diff --git a/app/core/gimpprogress.c b/app/core/gimpprogress.c new file mode 100644 index 0000000..6b8bf46 --- /dev/null +++ b/app/core/gimpprogress.c @@ -0,0 +1,266 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpprogress.c + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpmarshal.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +enum +{ + CANCEL, + LAST_SIGNAL +}; + + +G_DEFINE_INTERFACE (GimpProgress, gimp_progress, G_TYPE_OBJECT) + + +static guint progress_signals[LAST_SIGNAL] = { 0 }; + + +/* private functions */ + + +static void +gimp_progress_default_init (GimpProgressInterface *progress_iface) +{ + progress_signals[CANCEL] = + g_signal_new ("cancel", + G_TYPE_FROM_INTERFACE (progress_iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProgressInterface, cancel), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + +/* public functions */ + + +GimpProgress * +gimp_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *format, + ...) +{ + GimpProgressInterface *progress_iface; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (format != NULL, NULL); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->start) + { + GimpProgress *ret; + va_list args; + gchar *text; + + va_start (args, format); + text = g_strdup_vprintf (format, args); + va_end (args); + + ret = progress_iface->start (progress, cancellable, text); + + g_free (text); + + return ret; + } + + return NULL; +} + +void +gimp_progress_end (GimpProgress *progress) +{ + GimpProgressInterface *progress_iface; + + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->end) + progress_iface->end (progress); +} + +gboolean +gimp_progress_is_active (GimpProgress *progress) +{ + GimpProgressInterface *progress_iface; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), FALSE); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->is_active) + return progress_iface->is_active (progress); + + return FALSE; +} + +void +gimp_progress_set_text (GimpProgress *progress, + const gchar *format, + ...) +{ + va_list args; + gchar *message; + + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + g_return_if_fail (format != NULL); + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + gimp_progress_set_text_literal (progress, message); + + g_free (message); +} + +void +gimp_progress_set_text_literal (GimpProgress *progress, + const gchar *message) +{ + GimpProgressInterface *progress_iface; + + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + g_return_if_fail (message != NULL); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->set_text) + progress_iface->set_text (progress, message); +} + +void +gimp_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpProgressInterface *progress_iface; + + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + + percentage = CLAMP (percentage, 0.0, 1.0); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->set_value) + progress_iface->set_value (progress, percentage); +} + +gdouble +gimp_progress_get_value (GimpProgress *progress) +{ + GimpProgressInterface *progress_iface; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), 0.0); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->get_value) + return progress_iface->get_value (progress); + + return 0.0; +} + +void +gimp_progress_pulse (GimpProgress *progress) +{ + GimpProgressInterface *progress_iface; + + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->pulse) + progress_iface->pulse (progress); +} + +guint32 +gimp_progress_get_window_id (GimpProgress *progress) +{ + GimpProgressInterface *progress_iface; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), 0); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->get_window_id) + return progress_iface->get_window_id (progress); + + return 0; +} + +gboolean +gimp_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GimpProgressInterface *progress_iface; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (domain != NULL, FALSE); + g_return_val_if_fail (message != NULL, FALSE); + + progress_iface = GIMP_PROGRESS_GET_INTERFACE (progress); + + if (progress_iface->message) + return progress_iface->message (progress, gimp, severity, domain, message); + + return FALSE; +} + +void +gimp_progress_cancel (GimpProgress *progress) +{ + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + + g_signal_emit (progress, progress_signals[CANCEL], 0); +} + +void +gimp_progress_update_and_flush (gint min, + gint max, + gint current, + gpointer data) +{ + gimp_progress_set_value (GIMP_PROGRESS (data), + (gdouble) (current - min) / (gdouble) (max - min)); + + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, TRUE); +} diff --git a/app/core/gimpprogress.h b/app/core/gimpprogress.h new file mode 100644 index 0000000..488f050 --- /dev/null +++ b/app/core/gimpprogress.h @@ -0,0 +1,99 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpprogress.h + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PROGRESS_H__ +#define __GIMP_PROGRESS_H__ + + +#define GIMP_TYPE_PROGRESS (gimp_progress_get_type ()) +#define GIMP_IS_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PROGRESS)) +#define GIMP_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PROGRESS, GimpProgress)) +#define GIMP_PROGRESS_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_PROGRESS, GimpProgressInterface)) + + +typedef struct _GimpProgressInterface GimpProgressInterface; + +struct _GimpProgressInterface +{ + GTypeInterface base_iface; + + /* virtual functions */ + GimpProgress * (* start) (GimpProgress *progress, + gboolean cancellable, + const gchar *message); + void (* end) (GimpProgress *progress); + gboolean (* is_active) (GimpProgress *progress); + + void (* set_text) (GimpProgress *progress, + const gchar *message); + void (* set_value) (GimpProgress *progress, + gdouble percentage); + gdouble (* get_value) (GimpProgress *progress); + void (* pulse) (GimpProgress *progress); + + guint32 (* get_window_id) (GimpProgress *progress); + + gboolean (* message) (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + + /* signals */ + void (* cancel) (GimpProgress *progress); +}; + + +GType gimp_progress_get_type (void) G_GNUC_CONST; + +GimpProgress * gimp_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *format, + ...) G_GNUC_PRINTF (3, 4); +void gimp_progress_end (GimpProgress *progress); +gboolean gimp_progress_is_active (GimpProgress *progress); + +void gimp_progress_set_text (GimpProgress *progress, + const gchar *format, + ...) G_GNUC_PRINTF (2, 3); +void gimp_progress_set_text_literal (GimpProgress *progress, + const gchar *message); +void gimp_progress_set_value (GimpProgress *progress, + gdouble percentage); +gdouble gimp_progress_get_value (GimpProgress *progress); +void gimp_progress_pulse (GimpProgress *progress); + +guint32 gimp_progress_get_window_id (GimpProgress *progress); + +gboolean gimp_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + +void gimp_progress_cancel (GimpProgress *progress); + +void gimp_progress_update_and_flush (gint min, + gint max, + gint current, + gpointer data); + + +#endif /* __GIMP_PROGRESS_H__ */ diff --git a/app/core/gimpprojectable.c b/app/core/gimpprojectable.c new file mode 100644 index 0000000..8814c98 --- /dev/null +++ b/app/core/gimpprojectable.c @@ -0,0 +1,262 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpprojectable.c + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpmarshal.h" +#include "gimpprojectable.h" +#include "gimpviewable.h" + + +enum +{ + INVALIDATE, + FLUSH, + STRUCTURE_CHANGED, + BOUNDS_CHANGED, + LAST_SIGNAL +}; + + +G_DEFINE_INTERFACE (GimpProjectable, gimp_projectable, GIMP_TYPE_VIEWABLE) + + +static guint projectable_signals[LAST_SIGNAL] = { 0 }; + + +/* private functions */ + + +static void +gimp_projectable_default_init (GimpProjectableInterface *iface) +{ + projectable_signals[INVALIDATE] = + g_signal_new ("invalidate", + G_TYPE_FROM_CLASS (iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProjectableInterface, invalidate), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + projectable_signals[FLUSH] = + g_signal_new ("flush", + G_TYPE_FROM_CLASS (iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProjectableInterface, flush), + NULL, NULL, + gimp_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + projectable_signals[STRUCTURE_CHANGED] = + g_signal_new ("structure-changed", + G_TYPE_FROM_CLASS (iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProjectableInterface, structure_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + projectable_signals[BOUNDS_CHANGED] = + g_signal_new ("bounds-changed", + G_TYPE_FROM_CLASS (iface), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProjectableInterface, bounds_changed), + NULL, NULL, + gimp_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); +} + + +/* public functions */ + +void +gimp_projectable_invalidate (GimpProjectable *projectable, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + g_signal_emit (projectable, projectable_signals[INVALIDATE], 0, + x, y, width, height); +} + +void +gimp_projectable_flush (GimpProjectable *projectable, + gboolean preview_invalidated) +{ + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + g_signal_emit (projectable, projectable_signals[FLUSH], 0, + preview_invalidated); +} + +void +gimp_projectable_structure_changed (GimpProjectable *projectable) +{ + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + g_signal_emit (projectable, projectable_signals[STRUCTURE_CHANGED], 0); +} + +void +gimp_projectable_bounds_changed (GimpProjectable *projectable, + gint old_x, + gint old_y) +{ + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + g_signal_emit (projectable, projectable_signals[BOUNDS_CHANGED], 0, + old_x, old_y); +} + +GimpImage * +gimp_projectable_get_image (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), NULL); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->get_image) + return iface->get_image (projectable); + + return NULL; +} + +const Babl * +gimp_projectable_get_format (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), NULL); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->get_format) + return iface->get_format (projectable); + + return 0; +} + +void +gimp_projectable_get_offset (GimpProjectable *projectable, + gint *x, + gint *y) +{ + GimpProjectableInterface *iface; + + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + g_return_if_fail (x != NULL); + g_return_if_fail (y != NULL); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + *x = 0; + *y = 0; + + if (iface->get_offset) + iface->get_offset (projectable, x, y); +} + +GeglRectangle +gimp_projectable_get_bounding_box (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + GeglRectangle result = {}; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), result); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->get_bounding_box) + result = iface->get_bounding_box (projectable); + + return result; +} + +GeglNode * +gimp_projectable_get_graph (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), NULL); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->get_graph) + return iface->get_graph (projectable); + + return NULL; +} + +void +gimp_projectable_begin_render (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->begin_render) + iface->begin_render (projectable); +} + +void +gimp_projectable_end_render (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->end_render) + iface->end_render (projectable); +} + +void +gimp_projectable_invalidate_preview (GimpProjectable *projectable) +{ + GimpProjectableInterface *iface; + + g_return_if_fail (GIMP_IS_PROJECTABLE (projectable)); + + iface = GIMP_PROJECTABLE_GET_INTERFACE (projectable); + + if (iface->invalidate_preview) + iface->invalidate_preview (projectable); +} diff --git a/app/core/gimpprojectable.h b/app/core/gimpprojectable.h new file mode 100644 index 0000000..955f3a4 --- /dev/null +++ b/app/core/gimpprojectable.h @@ -0,0 +1,90 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpprojectable.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PROJECTABLE_H__ +#define __GIMP_PROJECTABLE_H__ + + +#define GIMP_TYPE_PROJECTABLE (gimp_projectable_get_type ()) +#define GIMP_IS_PROJECTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PROJECTABLE)) +#define GIMP_PROJECTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PROJECTABLE, GimpProjectable)) +#define GIMP_PROJECTABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_PROJECTABLE, GimpProjectableInterface)) + + +typedef struct _GimpProjectableInterface GimpProjectableInterface; + +struct _GimpProjectableInterface +{ + GTypeInterface base_iface; + + /* signals */ + void (* invalidate) (GimpProjectable *projectable, + gint x, + gint y, + gint width, + gint height); + void (* flush) (GimpProjectable *projectable, + gboolean invalidate_preview); + void (* structure_changed) (GimpProjectable *projectable); + void (* bounds_changed) (GimpProjectable *projectable, + gint old_x, + gint old_y); + + /* virtual functions */ + GimpImage * (* get_image) (GimpProjectable *projectable); + const Babl * (* get_format) (GimpProjectable *projectable); + void (* get_offset) (GimpProjectable *projectable, + gint *x, + gint *y); + GeglRectangle (* get_bounding_box) (GimpProjectable *projectable); + GeglNode * (* get_graph) (GimpProjectable *projectable); + void (* begin_render) (GimpProjectable *projectable); + void (* end_render) (GimpProjectable *projectable); + void (* invalidate_preview) (GimpProjectable *projectable); +}; + + +GType gimp_projectable_get_type (void) G_GNUC_CONST; + +void gimp_projectable_invalidate (GimpProjectable *projectable, + gint x, + gint y, + gint width, + gint height); +void gimp_projectable_flush (GimpProjectable *projectable, + gboolean preview_invalidated); +void gimp_projectable_structure_changed (GimpProjectable *projectable); +void gimp_projectable_bounds_changed (GimpProjectable *projectable, + gint old_x, + gint old_y); + +GimpImage * gimp_projectable_get_image (GimpProjectable *projectable); +const Babl * gimp_projectable_get_format (GimpProjectable *projectable); +void gimp_projectable_get_offset (GimpProjectable *projectable, + gint *x, + gint *y); +GeglRectangle gimp_projectable_get_bounding_box (GimpProjectable *projectable); +GeglNode * gimp_projectable_get_graph (GimpProjectable *projectable); +void gimp_projectable_begin_render (GimpProjectable *projectable); +void gimp_projectable_end_render (GimpProjectable *projectable); +void gimp_projectable_invalidate_preview (GimpProjectable *projectable); + + +#endif /* __GIMP_PROJECTABLE_H__ */ diff --git a/app/core/gimpprojection.c b/app/core/gimpprojection.c new file mode 100644 index 0000000..c249b55 --- /dev/null +++ b/app/core/gimpprojection.c @@ -0,0 +1,1132 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "gimp.h" +#include "gimp-memsize.h" +#include "gimpchunkiterator.h" +#include "gimpimage.h" +#include "gimpmarshal.h" +#include "gimppickable.h" +#include "gimpprojectable.h" +#include "gimpprojection.h" +#include "gimptilehandlerprojectable.h" + +#include "gimp-log.h" +#include "gimp-priorities.h" + + +/* chunk size for area updates */ +#define GIMP_PROJECTION_UPDATE_CHUNK_WIDTH 32 +#define GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT 32 + + +enum +{ + UPDATE, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_BUFFER +}; + + +struct _GimpProjectionPrivate +{ + GimpProjectable *projectable; + + GeglBuffer *buffer; + GimpTileHandlerValidate *validate_handler; + + gint priority; + + cairo_region_t *update_region; + GeglRectangle priority_rect; + GimpChunkIterator *iter; + guint idle_id; + + gboolean invalidate_preview; +}; + + +/* local function prototypes */ + +static void gimp_projection_pickable_iface_init (GimpPickableInterface *iface); + +static void gimp_projection_finalize (GObject *object); +static void gimp_projection_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_projection_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_projection_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_projection_pickable_flush (GimpPickable *pickable); +static GimpImage * gimp_projection_get_image (GimpPickable *pickable); +static const Babl * gimp_projection_get_format (GimpPickable *pickable); +static GeglBuffer * gimp_projection_get_buffer (GimpPickable *pickable); +static gboolean gimp_projection_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +static gdouble gimp_projection_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +static void gimp_projection_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); +static void gimp_projection_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +static void gimp_projection_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); + +static void gimp_projection_allocate_buffer (GimpProjection *proj); +static void gimp_projection_free_buffer (GimpProjection *proj); +static void gimp_projection_add_update_area (GimpProjection *proj, + gint x, + gint y, + gint w, + gint h); +static void gimp_projection_flush_whenever (GimpProjection *proj, + gboolean now, + gboolean direct); +static void gimp_projection_update_priority_rect (GimpProjection *proj); +static void gimp_projection_chunk_render_start (GimpProjection *proj); +static void gimp_projection_chunk_render_stop (GimpProjection *proj, + gboolean merge); +static gboolean gimp_projection_chunk_render_callback (GimpProjection *proj); +static gboolean gimp_projection_chunk_render_iteration(GimpProjection *proj); +static void gimp_projection_paint_area (GimpProjection *proj, + gboolean now, + gint x, + gint y, + gint w, + gint h); + +static void gimp_projection_projectable_invalidate(GimpProjectable *projectable, + gint x, + gint y, + gint w, + gint h, + GimpProjection *proj); +static void gimp_projection_projectable_flush (GimpProjectable *projectable, + gboolean invalidate_preview, + GimpProjection *proj); +static void + gimp_projection_projectable_structure_changed (GimpProjectable *projectable, + GimpProjection *proj); +static void gimp_projection_projectable_bounds_changed (GimpProjectable *projectable, + gint old_x, + gint old_y, + GimpProjection *proj); + + +G_DEFINE_TYPE_WITH_CODE (GimpProjection, gimp_projection, GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpProjection) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, + gimp_projection_pickable_iface_init)) + +#define parent_class gimp_projection_parent_class + +static guint projection_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_projection_class_init (GimpProjectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + projection_signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpProjectionClass, update), + NULL, NULL, + gimp_marshal_VOID__BOOLEAN_INT_INT_INT_INT, + G_TYPE_NONE, 5, + G_TYPE_BOOLEAN, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + object_class->finalize = gimp_projection_finalize; + object_class->set_property = gimp_projection_set_property; + object_class->get_property = gimp_projection_get_property; + + gimp_object_class->get_memsize = gimp_projection_get_memsize; + + g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); +} + +static void +gimp_projection_init (GimpProjection *proj) +{ + proj->priv = gimp_projection_get_instance_private (proj); +} + +static void +gimp_projection_pickable_iface_init (GimpPickableInterface *iface) +{ + iface->flush = gimp_projection_pickable_flush; + iface->get_image = gimp_projection_get_image; + iface->get_format = gimp_projection_get_format; + iface->get_format_with_alpha = gimp_projection_get_format; /* sic */ + iface->get_buffer = gimp_projection_get_buffer; + iface->get_pixel_at = gimp_projection_get_pixel_at; + iface->get_opacity_at = gimp_projection_get_opacity_at; + iface->get_pixel_average = gimp_projection_get_pixel_average; + iface->pixel_to_srgb = gimp_projection_pixel_to_srgb; + iface->srgb_to_pixel = gimp_projection_srgb_to_pixel; +} + +static void +gimp_projection_finalize (GObject *object) +{ + GimpProjection *proj = GIMP_PROJECTION (object); + + gimp_projection_free_buffer (proj); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_projection_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_BUFFER: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_projection_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpProjection *projection = GIMP_PROJECTION (object); + + switch (property_id) + { + case PROP_BUFFER: + g_value_set_object (value, projection->priv->buffer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_projection_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpProjection *projection = GIMP_PROJECTION (object); + gint64 memsize = 0; + + memsize += gimp_gegl_pyramid_get_memsize (projection->priv->buffer); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +/** + * gimp_projection_estimate_memsize: + * @type: the projectable's base type + * @component_type: the projectable's component type + * @width: projection width + * @height: projection height + * + * Calculates a rough estimate of the memory that is required for the + * projection of an image with the given @width and @height. + * + * Return value: a rough estimate of the memory requirements. + **/ +gint64 +gimp_projection_estimate_memsize (GimpImageBaseType type, + GimpComponentType component_type, + gint width, + gint height) +{ + const Babl *format; + gint64 bytes; + + if (type == GIMP_INDEXED) + type = GIMP_RGB; + + format = gimp_babl_format (type, + gimp_babl_precision (component_type, FALSE), + TRUE); + bytes = babl_format_get_bytes_per_pixel (format); + + /* The pyramid levels constitute a geometric sum with a ratio of 1/4. */ + return bytes * (gint64) width * (gint64) height * 1.33; +} + + +static void +gimp_projection_pickable_flush (GimpPickable *pickable) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + + /* create the buffer if it doesn't exist */ + gimp_projection_get_buffer (pickable); + + gimp_projection_finish_draw (proj); + gimp_projection_flush_now (proj, FALSE); + + if (proj->priv->invalidate_preview) + { + /* invalidate the preview here since it is constructed from + * the projection + */ + proj->priv->invalidate_preview = FALSE; + + gimp_projectable_invalidate_preview (proj->priv->projectable); + } +} + +static GimpImage * +gimp_projection_get_image (GimpPickable *pickable) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + + return gimp_projectable_get_image (proj->priv->projectable); +} + +static const Babl * +gimp_projection_get_format (GimpPickable *pickable) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + + return gimp_projectable_get_format (proj->priv->projectable); +} + +static GeglBuffer * +gimp_projection_get_buffer (GimpPickable *pickable) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + + if (! proj->priv->buffer) + { + GeglRectangle bounding_box; + + bounding_box = + gimp_projectable_get_bounding_box (proj->priv->projectable); + + gimp_projection_allocate_buffer (proj); + + /* This used to call gimp_tile_handler_validate_invalidate() + * which forced the entire projection to be constructed in one + * go for new images, causing a potentially huge delay. Now we + * initially validate stuff the normal way, which makes the + * image appear incrementally, but it keeps everything + * responsive. + */ + gimp_projection_add_update_area (proj, + bounding_box.x, bounding_box.y, + bounding_box.width, bounding_box.height); + proj->priv->invalidate_preview = TRUE; + gimp_projection_flush (proj); + } + + return proj->priv->buffer; +} + +static gboolean +gimp_projection_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + GeglBuffer *buffer = gimp_projection_get_buffer (pickable); + GeglRectangle bounding_box; + + bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable); + + if (x < bounding_box.x || + y < bounding_box.y || + x >= bounding_box.x + bounding_box.width || + y >= bounding_box.y + bounding_box.height) + { + return FALSE; + } + + gegl_buffer_sample (buffer, x, y, NULL, pixel, format, + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + return TRUE; +} + +static gdouble +gimp_projection_get_opacity_at (GimpPickable *pickable, + gint x, + gint y) +{ + return GIMP_OPACITY_OPAQUE; +} + +static void +gimp_projection_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GeglBuffer *buffer = gimp_projection_get_buffer (pickable); + + return gimp_gegl_average_color (buffer, rect, TRUE, GEGL_ABYSS_NONE, format, + pixel); +} + +static void +gimp_projection_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + GimpImage *image = gimp_projectable_get_image (proj->priv->projectable); + + gimp_pickable_pixel_to_srgb (GIMP_PICKABLE (image), format, pixel, color); +} + +static void +gimp_projection_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel) +{ + GimpProjection *proj = GIMP_PROJECTION (pickable); + GimpImage *image = gimp_projectable_get_image (proj->priv->projectable); + + gimp_pickable_srgb_to_pixel (GIMP_PICKABLE (image), color, format, pixel); +} + + +/* public functions */ + +GimpProjection * +gimp_projection_new (GimpProjectable *projectable) +{ + GimpProjection *proj; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), NULL); + + proj = g_object_new (GIMP_TYPE_PROJECTION, NULL); + + proj->priv->projectable = projectable; + + g_signal_connect_object (projectable, "invalidate", + G_CALLBACK (gimp_projection_projectable_invalidate), + proj, 0); + g_signal_connect_object (projectable, "flush", + G_CALLBACK (gimp_projection_projectable_flush), + proj, 0); + g_signal_connect_object (projectable, "structure-changed", + G_CALLBACK (gimp_projection_projectable_structure_changed), + proj, 0); + g_signal_connect_object (projectable, "bounds-changed", + G_CALLBACK (gimp_projection_projectable_bounds_changed), + proj, 0); + + return proj; +} + +void +gimp_projection_set_priority (GimpProjection *proj, + gint priority) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + proj->priv->priority = priority; +} + +gint +gimp_projection_get_priority (GimpProjection *proj) +{ + g_return_val_if_fail (GIMP_IS_PROJECTION (proj), 0); + + return proj->priv->priority; +} + +void +gimp_projection_set_priority_rect (GimpProjection *proj, + gint x, + gint y, + gint w, + gint h) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + proj->priv->priority_rect = *GEGL_RECTANGLE (x, y, w, h); + + gimp_projection_update_priority_rect (proj); +} + +void +gimp_projection_stop_rendering (GimpProjection *proj) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + gimp_projection_chunk_render_stop (proj, TRUE); +} + +void +gimp_projection_flush (GimpProjection *proj) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + /* Construct in chunks */ + gimp_projection_flush_whenever (proj, FALSE, FALSE); +} + +void +gimp_projection_flush_now (GimpProjection *proj, + gboolean direct) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + /* Construct NOW */ + gimp_projection_flush_whenever (proj, TRUE, direct); +} + +void +gimp_projection_finish_draw (GimpProjection *proj) +{ + g_return_if_fail (GIMP_IS_PROJECTION (proj)); + + if (proj->priv->iter) + { + gimp_chunk_iterator_set_priority_rect (proj->priv->iter, NULL); + + gimp_tile_handler_validate_begin_validate (proj->priv->validate_handler); + + while (gimp_projection_chunk_render_iteration (proj)); + + gimp_tile_handler_validate_end_validate (proj->priv->validate_handler); + + gimp_projection_chunk_render_stop (proj, FALSE); + } +} + + +/* private functions */ + +static void +gimp_projection_allocate_buffer (GimpProjection *proj) +{ + const Babl *format; + GeglRectangle bounding_box; + + if (proj->priv->buffer) + return; + + format = gimp_projection_get_format (GIMP_PICKABLE (proj)); + bounding_box = + gimp_projectable_get_bounding_box (proj->priv->projectable); + + proj->priv->buffer = gegl_buffer_new (&bounding_box, format); + + proj->priv->validate_handler = + GIMP_TILE_HANDLER_VALIDATE ( + gimp_tile_handler_projectable_new (proj->priv->projectable)); + + gimp_tile_handler_validate_assign (proj->priv->validate_handler, + proj->priv->buffer); + + g_object_notify (G_OBJECT (proj), "buffer"); +} + +static void +gimp_projection_free_buffer (GimpProjection *proj) +{ + gimp_projection_chunk_render_stop (proj, FALSE); + + g_clear_pointer (&proj->priv->update_region, cairo_region_destroy); + + if (proj->priv->buffer) + { + gimp_tile_handler_validate_unassign (proj->priv->validate_handler, + proj->priv->buffer); + + g_clear_object (&proj->priv->buffer); + g_clear_object (&proj->priv->validate_handler); + + g_object_notify (G_OBJECT (proj), "buffer"); + } +} + +static void +gimp_projection_add_update_area (GimpProjection *proj, + gint x, + gint y, + gint w, + gint h) +{ + cairo_rectangle_int_t rect; + GeglRectangle bounding_box; + + bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable); + + /* align the rectangle to the UPDATE_CHUNK_WIDTH x UPDATE_CHUNK_HEIGHT grid, + * to decrease the complexity of the update area. + */ + w = ceil ((gdouble) (x + w) / GIMP_PROJECTION_UPDATE_CHUNK_WIDTH ) * GIMP_PROJECTION_UPDATE_CHUNK_WIDTH; + h = ceil ((gdouble) (y + h) / GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT) * GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT; + x = floor ((gdouble) x / GIMP_PROJECTION_UPDATE_CHUNK_WIDTH ) * GIMP_PROJECTION_UPDATE_CHUNK_WIDTH; + y = floor ((gdouble) y / GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT) * GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT; + + w -= x; + h -= y; + + if (gegl_rectangle_intersect ((GeglRectangle *) &rect, + GEGL_RECTANGLE (x, y, w, h), &bounding_box)) + { + if (proj->priv->update_region) + cairo_region_union_rectangle (proj->priv->update_region, &rect); + else + proj->priv->update_region = cairo_region_create_rectangle (&rect); + } +} + +static void +gimp_projection_flush_whenever (GimpProjection *proj, + gboolean now, + gboolean direct) +{ + if (proj->priv->update_region) + { + /* Make sure we have a buffer */ + gimp_projection_allocate_buffer (proj); + + if (now) /* Synchronous */ + { + gint n_rects = cairo_region_num_rectangles (proj->priv->update_region); + gint i; + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (proj->priv->update_region, + i, &rect); + + gimp_projection_paint_area (proj, + direct, + rect.x, + rect.y, + rect.width, + rect.height); + } + + /* Free the update region */ + g_clear_pointer (&proj->priv->update_region, cairo_region_destroy); + } + else /* Asynchronous */ + { + /* Consumes the update region */ + gimp_projection_chunk_render_start (proj); + } + } + else if (! now && ! proj->priv->iter && proj->priv->invalidate_preview) + { + /* invalidate the preview here since it is constructed from + * the projection + */ + proj->priv->invalidate_preview = FALSE; + + gimp_projectable_invalidate_preview (proj->priv->projectable); + } +} + +static void +gimp_projection_update_priority_rect (GimpProjection *proj) +{ + if (proj->priv->iter) + { + GeglRectangle rect; + GeglRectangle bounding_box; + gint off_x, off_y; + + rect = proj->priv->priority_rect; + + gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y); + bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable); + + /* subtract the projectable's offsets because the list of update + * areas is in tile-pyramid coordinates, but our external API is + * always in terms of image coordinates. + */ + rect.x -= off_x; + rect.y -= off_y; + + gegl_rectangle_intersect (&rect, &rect, &bounding_box); + + gimp_chunk_iterator_set_priority_rect (proj->priv->iter, &rect); + } +} + +static void +gimp_projection_chunk_render_start (GimpProjection *proj) +{ + cairo_region_t *region = proj->priv->update_region; + gboolean invalidate_preview = FALSE; + + if (proj->priv->iter) + { + region = gimp_chunk_iterator_stop (proj->priv->iter, FALSE); + + proj->priv->iter = NULL; + + if (cairo_region_is_empty (region)) + invalidate_preview = proj->priv->invalidate_preview; + + if (proj->priv->update_region) + { + cairo_region_union (region, proj->priv->update_region); + + cairo_region_destroy (proj->priv->update_region); + } + } + + proj->priv->update_region = NULL; + + if (region && ! cairo_region_is_empty (region)) + { + proj->priv->iter = gimp_chunk_iterator_new (region); + + gimp_projection_update_priority_rect (proj); + + if (! proj->priv->idle_id) + { + proj->priv->idle_id = g_idle_add_full ( + GIMP_PRIORITY_PROJECTION_IDLE + proj->priv->priority, + (GSourceFunc) gimp_projection_chunk_render_callback, + proj, NULL); + } + } + else + { + if (region) + cairo_region_destroy (region); + + if (proj->priv->idle_id) + { + g_source_remove (proj->priv->idle_id); + proj->priv->idle_id = 0; + } + + if (invalidate_preview) + { + /* invalidate the preview here since it is constructed from + * the projection + */ + proj->priv->invalidate_preview = FALSE; + + gimp_projectable_invalidate_preview (proj->priv->projectable); + } + } +} + +static void +gimp_projection_chunk_render_stop (GimpProjection *proj, + gboolean merge) +{ + if (proj->priv->idle_id) + { + g_source_remove (proj->priv->idle_id); + proj->priv->idle_id = 0; + } + + if (proj->priv->iter) + { + if (merge) + { + cairo_region_t *region; + + region = gimp_chunk_iterator_stop (proj->priv->iter, FALSE); + + if (proj->priv->update_region) + { + cairo_region_union (proj->priv->update_region, region); + + cairo_region_destroy (region); + } + else + { + proj->priv->update_region = region; + } + } + else + { + gimp_chunk_iterator_stop (proj->priv->iter, TRUE); + } + + proj->priv->iter = NULL; + } +} + +static gboolean +gimp_projection_chunk_render_callback (GimpProjection *proj) +{ + if (gimp_projection_chunk_render_iteration (proj)) + { + return G_SOURCE_CONTINUE; + } + else + { + proj->priv->idle_id = 0; + + return G_SOURCE_REMOVE; + } +} + +static gboolean +gimp_projection_chunk_render_iteration (GimpProjection *proj) +{ + if (gimp_chunk_iterator_next (proj->priv->iter)) + { + GeglRectangle rect; + + gimp_tile_handler_validate_begin_validate (proj->priv->validate_handler); + + while (gimp_chunk_iterator_get_rect (proj->priv->iter, &rect)) + { + gimp_projection_paint_area (proj, TRUE, + rect.x, rect.y, rect.width, rect.height); + } + + gimp_tile_handler_validate_end_validate (proj->priv->validate_handler); + + /* Still work to do. */ + return TRUE; + } + else + { + proj->priv->iter = NULL; + + if (proj->priv->invalidate_preview) + { + /* invalidate the preview here since it is constructed from + * the projection + */ + proj->priv->invalidate_preview = FALSE; + + gimp_projectable_invalidate_preview (proj->priv->projectable); + } + + /* FINISHED */ + return FALSE; + } +} + +static void +gimp_projection_paint_area (GimpProjection *proj, + gboolean now, + gint x, + gint y, + gint w, + gint h) +{ + gint off_x, off_y; + GeglRectangle bounding_box; + GeglRectangle rect; + + gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y); + bounding_box = gimp_projectable_get_bounding_box (proj->priv->projectable); + + if (gegl_rectangle_intersect (&rect, + GEGL_RECTANGLE (x, y, w, h), &bounding_box)) + { + if (now) + { + gimp_tile_handler_validate_validate ( + proj->priv->validate_handler, + proj->priv->buffer, + &rect, + FALSE, FALSE); + } + else + { + gimp_tile_handler_validate_invalidate ( + proj->priv->validate_handler, + &rect); + } + + /* add the projectable's offsets because the list of update areas + * is in tile-pyramid coordinates, but our external API is always + * in terms of image coordinates. + */ + g_signal_emit (proj, projection_signals[UPDATE], 0, + now, + rect.x + off_x, + rect.y + off_y, + rect.width, + rect.height); + } +} + + +/* image callbacks */ + +static void +gimp_projection_projectable_invalidate (GimpProjectable *projectable, + gint x, + gint y, + gint w, + gint h, + GimpProjection *proj) +{ + gint off_x, off_y; + + gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y); + + /* subtract the projectable's offsets because the list of update + * areas is in tile-pyramid coordinates, but our external API is + * always in terms of image coordinates. + */ + x -= off_x; + y -= off_y; + + gimp_projection_add_update_area (proj, x, y, w, h); +} + +static void +gimp_projection_projectable_flush (GimpProjectable *projectable, + gboolean invalidate_preview, + GimpProjection *proj) +{ + if (invalidate_preview) + proj->priv->invalidate_preview = TRUE; + + gimp_projection_flush (proj); +} + +static void +gimp_projection_projectable_structure_changed (GimpProjectable *projectable, + GimpProjection *proj) +{ + GeglRectangle bounding_box; + + gimp_projection_free_buffer (proj); + + bounding_box = gimp_projectable_get_bounding_box (projectable); + + gimp_projection_add_update_area (proj, + bounding_box.x, bounding_box.y, + bounding_box.width, bounding_box.height); +} + +static void +gimp_projection_projectable_bounds_changed (GimpProjectable *projectable, + gint old_x, + gint old_y, + GimpProjection *proj) +{ + GeglBuffer *old_buffer = proj->priv->buffer; + GimpTileHandlerValidate *old_validate_handler; + GeglRectangle old_bounding_box; + GeglRectangle bounding_box; + GeglRectangle old_bounds; + GeglRectangle bounds; + GeglRectangle int_bounds; + gint x, y; + gint dx, dy; + + if (! old_buffer) + { + gimp_projection_projectable_structure_changed (projectable, proj); + + return; + } + + old_bounding_box = *gegl_buffer_get_extent (old_buffer); + + gimp_projectable_get_offset (projectable, &x, &y); + bounding_box = gimp_projectable_get_bounding_box (projectable); + + if (x == old_x && y == old_y && + gegl_rectangle_equal (&bounding_box, &old_bounding_box)) + { + return; + } + + old_bounds = old_bounding_box; + old_bounds.x += old_x; + old_bounds.y += old_y; + + bounds = bounding_box; + bounds.x += x; + bounds.y += y; + + if (! gegl_rectangle_intersect (&int_bounds, &bounds, &old_bounds)) + { + gimp_projection_projectable_structure_changed (projectable, proj); + + return; + } + + dx = x - old_x; + dy = y - old_y; + +#if 1 + /* FIXME: when there's an offset between the new bounds and the old bounds, + * use gimp_projection_projectable_structure_changed(), instead of copying a + * shifted version of the old buffer, since the synchronous copy can take a + * notable amount of time for big buffers, when the offset is such that tiles + * are not COW-ed. while gimp_projection_projectable_structure_changed() + * causes the projection to be re-rendered, which is overall slower, it's + * done asynchronously. + * + * this needs to be improved. + */ + if (dx || dy) + { + gimp_projection_projectable_structure_changed (projectable, proj); + + return; + } +#endif + + /* reallocate the buffer, and copy the old buffer to the corresponding + * region of the new buffer. + */ + + gimp_projection_chunk_render_stop (proj, TRUE); + + if (dx == 0 && dy == 0) + { + gimp_tile_handler_validate_buffer_set_extent (old_buffer, &bounding_box); + } + else + { + old_validate_handler = proj->priv->validate_handler; + + proj->priv->buffer = NULL; + proj->priv->validate_handler = NULL; + + gimp_projection_allocate_buffer (proj); + + gimp_tile_handler_validate_buffer_copy ( + old_buffer, + GEGL_RECTANGLE (int_bounds.x - old_x, + int_bounds.y - old_y, + int_bounds.width, + int_bounds.height), + proj->priv->buffer, + GEGL_RECTANGLE (int_bounds.x - x, + int_bounds.y - y, + int_bounds.width, + int_bounds.height)); + + gimp_tile_handler_validate_unassign (old_validate_handler, + old_buffer); + + g_object_unref (old_validate_handler); + g_object_unref (old_buffer); + } + + if (proj->priv->update_region) + { + cairo_region_translate (proj->priv->update_region, dx, dy); + cairo_region_intersect_rectangle ( + proj->priv->update_region, + (const cairo_rectangle_int_t *) &bounding_box); + } + + int_bounds.x -= x; + int_bounds.y -= y; + + if (int_bounds.x > bounding_box.x) + { + gimp_projection_add_update_area (proj, + bounding_box.x, + bounding_box.y, + int_bounds.x - bounding_box.x, + bounding_box.height); + } + if (int_bounds.y > bounding_box.y) + { + gimp_projection_add_update_area (proj, + bounding_box.x, + bounding_box.y, + bounding_box.width, + int_bounds.y - bounding_box.y); + } + if (int_bounds.x + int_bounds.width < bounding_box.x + bounding_box.width) + { + gimp_projection_add_update_area (proj, + int_bounds.x + int_bounds.width, + bounding_box.y, + bounding_box.x + bounding_box.width - + (int_bounds.x + int_bounds.width), + bounding_box.height); + } + if (int_bounds.y + int_bounds.height < bounding_box.y + bounding_box.height) + { + gimp_projection_add_update_area (proj, + bounding_box.x, + int_bounds.y + int_bounds.height, + bounding_box.width, + bounding_box.y + bounding_box.height - + (int_bounds.y + int_bounds.height)); + } + + proj->priv->invalidate_preview = TRUE; +} diff --git a/app/core/gimpprojection.h b/app/core/gimpprojection.h new file mode 100644 index 0000000..8c9baf6 --- /dev/null +++ b/app/core/gimpprojection.h @@ -0,0 +1,83 @@ +/* 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 . + */ + +#ifndef __GIMP_PROJECTION_H__ +#define __GIMP_PROJECTION_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_PROJECTION (gimp_projection_get_type ()) +#define GIMP_PROJECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PROJECTION, GimpProjection)) +#define GIMP_PROJECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PROJECTION, GimpProjectionClass)) +#define GIMP_IS_PROJECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PROJECTION)) +#define GIMP_IS_PROJECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PROJECTION)) +#define GIMP_PROJECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PROJECTION, GimpProjectionClass)) + + +typedef struct _GimpProjectionPrivate GimpProjectionPrivate; +typedef struct _GimpProjectionClass GimpProjectionClass; + +struct _GimpProjection +{ + GimpObject parent_instance; + + GimpProjectionPrivate *priv; +}; + +struct _GimpProjectionClass +{ + GimpObjectClass parent_class; + + void (* update) (GimpProjection *proj, + gboolean now, + gint x, + gint y, + gint width, + gint height); +}; + + +GType gimp_projection_get_type (void) G_GNUC_CONST; + +GimpProjection * gimp_projection_new (GimpProjectable *projectable); + +void gimp_projection_set_priority (GimpProjection *projection, + gint priority); +gint gimp_projection_get_priority (GimpProjection *projection); + +void gimp_projection_set_priority_rect (GimpProjection *proj, + gint x, + gint y, + gint width, + gint height); + +void gimp_projection_stop_rendering (GimpProjection *proj); + +void gimp_projection_flush (GimpProjection *proj); +void gimp_projection_flush_now (GimpProjection *proj, + gboolean direct); +void gimp_projection_finish_draw (GimpProjection *proj); + +gint64 gimp_projection_estimate_memsize (GimpImageBaseType type, + GimpComponentType component_type, + gint width, + gint height); + + +#endif /* __GIMP_PROJECTION_H__ */ diff --git a/app/core/gimpsamplepoint.c b/app/core/gimpsamplepoint.c new file mode 100644 index 0000000..f0fe377 --- /dev/null +++ b/app/core/gimpsamplepoint.c @@ -0,0 +1,215 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpsamplepoint.h" + + +enum +{ + PROP_0, + PROP_POSITION_X, + PROP_POSITION_Y, + PROP_PICK_MODE +}; + + +struct _GimpSamplePointPrivate +{ + gint position_x; + gint position_y; + GimpColorPickMode pick_mode; +}; + + +static void gimp_sample_point_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_sample_point_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpSamplePoint, gimp_sample_point, + GIMP_TYPE_AUX_ITEM) + + +static void +gimp_sample_point_class_init (GimpSamplePointClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gimp_sample_point_get_property; + object_class->set_property = gimp_sample_point_set_property; + + GIMP_CONFIG_PROP_INT (object_class, PROP_POSITION_X, + "position-x", + NULL, NULL, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED, + GIMP_MAX_IMAGE_SIZE, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED, + 0); + + GIMP_CONFIG_PROP_INT (object_class, PROP_POSITION_Y, + "position-y", + NULL, NULL, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED, + GIMP_MAX_IMAGE_SIZE, + GIMP_SAMPLE_POINT_POSITION_UNDEFINED, + 0); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_PICK_MODE, + "pick-mode", + NULL, NULL, + GIMP_TYPE_COLOR_PICK_MODE, + GIMP_COLOR_PICK_MODE_PIXEL, + 0); +} + +static void +gimp_sample_point_init (GimpSamplePoint *sample_point) +{ + sample_point->priv = gimp_sample_point_get_instance_private (sample_point); +} + +static void +gimp_sample_point_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpSamplePoint *sample_point = GIMP_SAMPLE_POINT (object); + + switch (property_id) + { + case PROP_POSITION_X: + g_value_set_int (value, sample_point->priv->position_x); + break; + case PROP_POSITION_Y: + g_value_set_int (value, sample_point->priv->position_y); + break; + case PROP_PICK_MODE: + g_value_set_enum (value, sample_point->priv->pick_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_sample_point_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpSamplePoint *sample_point = GIMP_SAMPLE_POINT (object); + + switch (property_id) + { + case PROP_POSITION_X: + sample_point->priv->position_x = g_value_get_int (value); + break; + case PROP_POSITION_Y: + sample_point->priv->position_y = g_value_get_int (value); + break; + case PROP_PICK_MODE: + sample_point->priv->pick_mode = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +GimpSamplePoint * +gimp_sample_point_new (guint32 sample_point_ID) +{ + return g_object_new (GIMP_TYPE_SAMPLE_POINT, + "id", sample_point_ID, + NULL); +} + +void +gimp_sample_point_get_position (GimpSamplePoint *sample_point, + gint *position_x, + gint *position_y) +{ + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + g_return_if_fail (position_x != NULL); + g_return_if_fail (position_y != NULL); + + *position_x = sample_point->priv->position_x; + *position_y = sample_point->priv->position_y; +} + +void +gimp_sample_point_set_position (GimpSamplePoint *sample_point, + gint position_x, + gint position_y) +{ + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + if (sample_point->priv->position_x != position_x || + sample_point->priv->position_y != position_y) + { + sample_point->priv->position_x = position_x; + sample_point->priv->position_y = position_y; + + g_object_freeze_notify (G_OBJECT (sample_point)); + + g_object_notify (G_OBJECT (sample_point), "position-x"); + g_object_notify (G_OBJECT (sample_point), "position-y"); + + g_object_thaw_notify (G_OBJECT (sample_point)); + } +} + +GimpColorPickMode +gimp_sample_point_get_pick_mode (GimpSamplePoint *sample_point) +{ + g_return_val_if_fail (GIMP_IS_SAMPLE_POINT (sample_point), + GIMP_COLOR_PICK_MODE_PIXEL); + + return sample_point->priv->pick_mode; +} + +void +gimp_sample_point_set_pick_mode (GimpSamplePoint *sample_point, + GimpColorPickMode pick_mode) +{ + g_return_if_fail (GIMP_IS_SAMPLE_POINT (sample_point)); + + if (sample_point->priv->pick_mode != pick_mode) + { + sample_point->priv->pick_mode = pick_mode; + + g_object_notify (G_OBJECT (sample_point), "pick-mode"); + } +} diff --git a/app/core/gimpsamplepoint.h b/app/core/gimpsamplepoint.h new file mode 100644 index 0000000..dddd328 --- /dev/null +++ b/app/core/gimpsamplepoint.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 . + */ + +#ifndef __GIMP_SAMPLE_POINT_H__ +#define __GIMP_SAMPLE_POINT_H__ + + +#include "gimpauxitem.h" + + +#define GIMP_SAMPLE_POINT_POSITION_UNDEFINED G_MININT + + +#define GIMP_TYPE_SAMPLE_POINT (gimp_sample_point_get_type ()) +#define GIMP_SAMPLE_POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SAMPLE_POINT, GimpSamplePoint)) +#define GIMP_SAMPLE_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SAMPLE_POINT, GimpSamplePointClass)) +#define GIMP_IS_SAMPLE_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SAMPLE_POINT)) +#define GIMP_IS_SAMPLE_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SAMPLE_POINT)) +#define GIMP_SAMPLE_POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SAMPLE_POINT, GimpSamplePointClass)) + + +typedef struct _GimpSamplePointPrivate GimpSamplePointPrivate; +typedef struct _GimpSamplePointClass GimpSamplePointClass; + +struct _GimpSamplePoint +{ + GimpAuxItem parent_instance; + + GimpSamplePointPrivate *priv; +}; + +struct _GimpSamplePointClass +{ + GimpAuxItemClass parent_class; +}; + + +GType gimp_sample_point_get_type (void) G_GNUC_CONST; + +GimpSamplePoint * gimp_sample_point_new (guint32 sample_point_ID); + +void gimp_sample_point_get_position (GimpSamplePoint *sample_point, + gint *position_x, + gint *position_y); +void gimp_sample_point_set_position (GimpSamplePoint *sample_point, + gint position_x, + gint position_y); + +GimpColorPickMode gimp_sample_point_get_pick_mode (GimpSamplePoint *sample_point); +void gimp_sample_point_set_pick_mode (GimpSamplePoint *sample_point, + GimpColorPickMode pick_mode); + + +#endif /* __GIMP_SAMPLE_POINT_H__ */ diff --git a/app/core/gimpsamplepointundo.c b/app/core/gimpsamplepointundo.c new file mode 100644 index 0000000..89cd8ae --- /dev/null +++ b/app/core/gimpsamplepointundo.c @@ -0,0 +1,121 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimpimage-sample-points.h" +#include "gimpsamplepoint.h" +#include "gimpsamplepointundo.h" + + +static void gimp_sample_point_undo_constructed (GObject *object); + +static void gimp_sample_point_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + + +G_DEFINE_TYPE (GimpSamplePointUndo, gimp_sample_point_undo, + GIMP_TYPE_AUX_ITEM_UNDO) + +#define parent_class gimp_sample_point_undo_parent_class + + +static void +gimp_sample_point_undo_class_init (GimpSamplePointUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_sample_point_undo_constructed; + + undo_class->pop = gimp_sample_point_undo_pop; +} + +static void +gimp_sample_point_undo_init (GimpSamplePointUndo *undo) +{ +} + +static void +gimp_sample_point_undo_constructed (GObject *object) +{ + GimpSamplePointUndo *sample_point_undo = GIMP_SAMPLE_POINT_UNDO (object); + GimpSamplePoint *sample_point; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + sample_point = GIMP_SAMPLE_POINT (GIMP_AUX_ITEM_UNDO (object)->aux_item); + + gimp_assert (GIMP_IS_SAMPLE_POINT (sample_point)); + + gimp_sample_point_get_position (sample_point, + &sample_point_undo->x, + &sample_point_undo->y); + sample_point_undo->pick_mode = gimp_sample_point_get_pick_mode (sample_point); +} + +static void +gimp_sample_point_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpSamplePointUndo *sample_point_undo = GIMP_SAMPLE_POINT_UNDO (undo); + GimpSamplePoint *sample_point; + gint x; + gint y; + GimpColorPickMode pick_mode; + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + sample_point = GIMP_SAMPLE_POINT (GIMP_AUX_ITEM_UNDO (undo)->aux_item); + + gimp_sample_point_get_position (sample_point, &x, &y); + pick_mode = gimp_sample_point_get_pick_mode (sample_point); + + if (x == GIMP_SAMPLE_POINT_POSITION_UNDEFINED) + { + gimp_image_add_sample_point (undo->image, + sample_point, + sample_point_undo->x, + sample_point_undo->y); + } + else if (sample_point_undo->x == GIMP_SAMPLE_POINT_POSITION_UNDEFINED) + { + gimp_image_remove_sample_point (undo->image, sample_point, FALSE); + } + else + { + gimp_sample_point_set_position (sample_point, + sample_point_undo->x, + sample_point_undo->y); + gimp_sample_point_set_pick_mode (sample_point, + sample_point_undo->pick_mode); + + gimp_image_sample_point_moved (undo->image, sample_point); + } + + sample_point_undo->x = x; + sample_point_undo->y = y; + sample_point_undo->pick_mode = pick_mode; +} diff --git a/app/core/gimpsamplepointundo.h b/app/core/gimpsamplepointundo.h new file mode 100644 index 0000000..10a3e90 --- /dev/null +++ b/app/core/gimpsamplepointundo.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 . + */ + +#ifndef __GIMP_SAMPLE_POINT_UNDO_H__ +#define __GIMP_SAMPLE_POINT_UNDO_H__ + + +#include "gimpauxitemundo.h" + + +#define GIMP_TYPE_SAMPLE_POINT_UNDO (gimp_sample_point_undo_get_type ()) +#define GIMP_SAMPLE_POINT_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SAMPLE_POINT_UNDO, GimpSamplePointUndo)) +#define GIMP_SAMPLE_POINT_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SAMPLE_POINT_UNDO, GimpSamplePointUndoClass)) +#define GIMP_IS_SAMPLE_POINT_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SAMPLE_POINT_UNDO)) +#define GIMP_IS_SAMPLE_POINT_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SAMPLE_POINT_UNDO)) +#define GIMP_SAMPLE_POINT_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SAMPLE_POINT_UNDO, GimpSamplePointUndoClass)) + + +typedef struct _GimpSamplePointUndo GimpSamplePointUndo; +typedef struct _GimpSamplePointUndoClass GimpSamplePointUndoClass; + +struct _GimpSamplePointUndo +{ + GimpAuxItemUndo parent_instance; + + gint x; + gint y; + GimpColorPickMode pick_mode; +}; + +struct _GimpSamplePointUndoClass +{ + GimpAuxItemUndoClass parent_class; +}; + + +GType gimp_sample_point_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_SAMPLE_POINT_UNDO_H__ */ diff --git a/app/core/gimpscanconvert.c b/app/core/gimpscanconvert.c new file mode 100644 index 0000000..ed5859c --- /dev/null +++ b/app/core/gimpscanconvert.c @@ -0,0 +1,647 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimpboundary.h" +#include "gimpbezierdesc.h" +#include "gimpscanconvert.h" + + +struct _GimpScanConvert +{ + gdouble ratio_xy; + + gboolean clip; + gint clip_x; + gint clip_y; + gint clip_w; + gint clip_h; + + /* stroking options */ + gboolean do_stroke; + gdouble width; + GimpJoinStyle join; + GimpCapStyle cap; + gdouble miter; + gdouble dash_offset; + GArray *dash_info; + + GArray *path_data; +}; + + +/* public functions */ + +/** + * gimp_scan_convert_new: + * + * Create a new scan conversion context. + * + * Return value: a newly allocated #GimpScanConvert context. + */ +GimpScanConvert * +gimp_scan_convert_new (void) +{ + GimpScanConvert *sc = g_slice_new0 (GimpScanConvert); + + sc->path_data = g_array_new (FALSE, FALSE, sizeof (cairo_path_data_t)); + sc->ratio_xy = 1.0; + + return sc; +} + +GimpScanConvert * +gimp_scan_convert_new_from_boundary (const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y) +{ + g_return_val_if_fail (bound_segs == NULL || n_bound_segs != 0, NULL); + + if (bound_segs) + { + GimpBoundSeg *stroke_segs; + gint n_stroke_segs; + + stroke_segs = gimp_boundary_sort (bound_segs, n_bound_segs, + &n_stroke_segs); + + if (stroke_segs) + { + GimpBezierDesc *bezier; + + bezier = gimp_bezier_desc_new_from_bound_segs (stroke_segs, + n_bound_segs, + n_stroke_segs); + + g_free (stroke_segs); + + if (bezier) + { + GimpScanConvert *scan_convert; + + scan_convert = gimp_scan_convert_new (); + + gimp_bezier_desc_translate (bezier, offset_x, offset_y); + gimp_scan_convert_add_bezier (scan_convert, bezier); + + gimp_bezier_desc_free (bezier); + + return scan_convert; + } + } + } + + return NULL; +} + +/** + * gimp_scan_convert_free: + * @sc: a #GimpScanConvert context + * + * Frees the resources allocated for @sc. + */ +void +gimp_scan_convert_free (GimpScanConvert *sc) +{ + g_return_if_fail (sc != NULL); + + if (sc->path_data) + g_array_free (sc->path_data, TRUE); + + if (sc->dash_info) + g_array_free (sc->dash_info, TRUE); + + g_slice_free (GimpScanConvert, sc); +} + +/** + * gimp_scan_convert_set_pixel_ratio: + * @sc: a #GimpScanConvert context + * @ratio_xy: the aspect ratio of the major coordinate axes + * + * Sets the pixel aspect ratio. + */ +void +gimp_scan_convert_set_pixel_ratio (GimpScanConvert *sc, + gdouble ratio_xy) +{ + g_return_if_fail (sc != NULL); + + /* we only need the relative resolution */ + sc->ratio_xy = ratio_xy; +} + +/** + * gimp_scan_convert_set_clip_rectangle + * @sc: a #GimpScanConvert context + * @x: horizontal offset of clip rectangle + * @y: vertical offset of clip rectangle + * @width: width of clip rectangle + * @height: height of clip rectangle + * + * Sets a clip rectangle on @sc. Subsequent render operations will be + * restricted to this area. + */ +void +gimp_scan_convert_set_clip_rectangle (GimpScanConvert *sc, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (sc != NULL); + + sc->clip = TRUE; + sc->clip_x = x; + sc->clip_y = y; + sc->clip_w = width; + sc->clip_h = height; +} + +/** + * gimp_scan_convert_add_polyline: + * @sc: a #GimpScanConvert context + * @n_points: number of points to add + * @points: array of points to add + * @closed: whether to close the polyline and make it a polygon + * + * Add a polyline with @n_points @points that may be open or closed. + * + * Please note that you should use gimp_scan_convert_stroke() if you + * specify open polygons. + */ +void +gimp_scan_convert_add_polyline (GimpScanConvert *sc, + guint n_points, + const GimpVector2 *points, + gboolean closed) +{ + GimpVector2 prev = { 0.0, 0.0, }; + cairo_path_data_t pd; + gint i; + + g_return_if_fail (sc != NULL); + g_return_if_fail (points != NULL); + g_return_if_fail (n_points > 0); + + for (i = 0; i < n_points; i++) + { + /* compress multiple identical coordinates */ + if (i == 0 || + prev.x != points[i].x || + prev.y != points[i].y) + { + pd.header.type = (i == 0) ? CAIRO_PATH_MOVE_TO : CAIRO_PATH_LINE_TO; + pd.header.length = 2; + sc->path_data = g_array_append_val (sc->path_data, pd); + + pd.point.x = points[i].x; + pd.point.y = points[i].y; + sc->path_data = g_array_append_val (sc->path_data, pd); + prev = points[i]; + } + } + + /* close the polyline when needed */ + if (closed) + { + pd.header.type = CAIRO_PATH_CLOSE_PATH; + pd.header.length = 1; + sc->path_data = g_array_append_val (sc->path_data, pd); + } +} + +/** + * gimp_scan_convert_add_polyline: + * @sc: a #GimpScanConvert context + * @bezier: a #GimpBezierDesc + * + * Adds a @bezier path to @sc. + * + * Please note that you should use gimp_scan_convert_stroke() if you + * specify open paths. + **/ +void +gimp_scan_convert_add_bezier (GimpScanConvert *sc, + const GimpBezierDesc *bezier) +{ + g_return_if_fail (sc != NULL); + g_return_if_fail (bezier != NULL); + + sc->path_data = g_array_append_vals (sc->path_data, + bezier->data, bezier->num_data); +} + +/** + * gimp_scan_convert_stroke: + * @sc: a #GimpScanConvert context + * @width: line width in pixels + * @join: how lines should be joined + * @cap: how to render the end of lines + * @miter: convert a mitered join to a bevelled join if the miter would + * extend to a distance of more than @miter times @width from + * the actual join point + * @dash_offset: offset to apply on the dash pattern + * @dash_info: dash pattern or %NULL for a solid line + * + * Stroke the content of a GimpScanConvert. The next + * gimp_scan_convert_render() will result in the outline of the + * polygon defined with the commands above. + * + * You cannot add additional polygons after this command. + * + * Note that if you have nonstandard resolution, "width" gives the + * width (in pixels) for a vertical stroke, i.e. use the X resolution + * to calculate the width of a stroke when operating with real world + * units. + */ +void +gimp_scan_convert_stroke (GimpScanConvert *sc, + gdouble width, + GimpJoinStyle join, + GimpCapStyle cap, + gdouble miter, + gdouble dash_offset, + GArray *dash_info) +{ + sc->do_stroke = TRUE; + sc->width = width; + sc->join = join; + sc->cap = cap; + sc->miter = miter; + + if (sc->dash_info) + { + g_array_free (sc->dash_info, TRUE); + sc->dash_info = NULL; + } + + if (dash_info && dash_info->len >= 2) + { + gint n_dashes; + gdouble *dashes; + gint i; + + dash_offset = dash_offset * MAX (width, 1.0); + + n_dashes = dash_info->len; + dashes = g_new (gdouble, dash_info->len); + + for (i = 0; i < dash_info->len ; i++) + dashes[i] = MAX (width, 1.0) * g_array_index (dash_info, gdouble, i); + + /* correct 0.0 in the first element (starts with a gap) */ + + if (dashes[0] == 0.0) + { + gdouble first; + + first = dashes[1]; + + /* shift the pattern to really starts with a dash and + * use the offset to skip into it. + */ + for (i = 0; i < dash_info->len - 2; i++) + { + dashes[i] = dashes[i+2]; + dash_offset += dashes[i]; + } + + if (dash_info->len % 2 == 1) + { + dashes[dash_info->len - 2] = first; + n_dashes --; + } + else if (dash_info->len > 2) + { + dashes [dash_info->len - 3] += first; + n_dashes -= 2; + } + } + + /* correct odd number of dash specifiers */ + + if (n_dashes % 2 == 1) + { + gdouble last; + + last = dashes[n_dashes - 1]; + dashes[0] += last; + dash_offset += last; + n_dashes --; + } + + if (n_dashes >= 2) + { + sc->dash_info = g_array_sized_new (FALSE, FALSE, + sizeof (gdouble), n_dashes); + sc->dash_info = g_array_append_vals (sc->dash_info, dashes, n_dashes); + sc->dash_offset = dash_offset; + } + + g_free (dashes); + } +} + + +/** + * gimp_scan_convert_render: + * @sc: a #GimpScanConvert context + * @buffer: the #GeglBuffer to render to + * @off_x: horizontal offset into the @buffer + * @off_y: vertical offset into the @buffer + * @antialias: whether to apply antialiasiing + * + * This is a wrapper around gimp_scan_convert_render_full() that replaces the + * content of the @buffer with a rendered form of the path passed in. + * + * You cannot add additional polygons after this command. + */ +void +gimp_scan_convert_render (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gboolean antialias) +{ + gimp_scan_convert_render_full (sc, buffer, off_x, off_y, + TRUE, antialias, 1.0); +} + +/** + * gimp_scan_convert_render_value: + * @sc: a #GimpScanConvert context + * @buffer: the #GeglBuffer to render to + * @off_x: horizontal offset into the @buffer + * @off_y: vertical offset into the @buffer + * @value: value to use for covered pixels + * + * This is a wrapper around gimp_scan_convert_render_full() that + * doesn't do antialiasing but gives control over the value that + * should be used for pixels covered by the scan conversion. Uncovered + * pixels are set to zero. + * + * You cannot add additional polygons after this command. + */ +void +gimp_scan_convert_render_value (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gdouble value) +{ + gimp_scan_convert_render_full (sc, buffer, off_x, off_y, + TRUE, FALSE, value); +} + +/** + * gimp_scan_convert_compose: + * @sc: a #GimpScanConvert context + * @buffer: the #GeglBuffer to render to + * @off_x: horizontal offset into the @buffer + * @off_y: vertical offset into the @buffer + * + * This is a wrapper around of gimp_scan_convert_render_full() that composes + * the (aliased) scan conversion on top of the content of the @buffer. + * + * You cannot add additional polygons after this command. + */ +void +gimp_scan_convert_compose (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y) +{ + gimp_scan_convert_render_full (sc, buffer, off_x, off_y, + FALSE, FALSE, 1.0); +} + +/** + * gimp_scan_convert_compose_value: + * @sc: a #GimpScanConvert context + * @buffer: the #GeglBuffer to render to + * @off_x: horizontal offset into the @buffer + * @off_y: vertical offset into the @buffer + * @value: value to use for covered pixels + * + * This is a wrapper around gimp_scan_convert_render_full() that + * composes the (aliased) scan conversion with value @value on top of the + * content of the @buffer. + * + * You cannot add additional polygons after this command. + */ +void +gimp_scan_convert_compose_value (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gdouble value) +{ + gimp_scan_convert_render_full (sc, buffer, off_x, off_y, + FALSE, FALSE, value); +} + +/** + * gimp_scan_convert_render_full: + * @sc: a #GimpScanConvert context + * @buffer: the #GeglBuffer to render to + * @off_x: horizontal offset into the @buffer + * @off_y: vertical offset into the @buffer + * @replace: if true the original content of the @buffer gets estroyed + * @antialias: if true the rendering happens antialiased + * @value: value to use for covered pixels + * + * This function renders the area described by the path to the + * @buffer, taking the offset @off_x and @off_y in the buffer into + * account. The rendering can happen antialiased and be rendered on + * top of existing content or replacing it completely. The @value + * specifies the opacity value to be used for the objects in the @sc. + * + * You cannot add additional polygons after this command. + */ +void +gimp_scan_convert_render_full (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gboolean replace, + gboolean antialias, + gdouble value) +{ + const Babl *format; + guchar *shared_buf = NULL; + gsize shared_buf_size = 0; + GeglBufferIterator *iter; + GeglRectangle *roi; + cairo_t *cr; + cairo_surface_t *surface; + cairo_path_t path; + gint bpp; + gint x, y; + gint width, height; + + g_return_if_fail (sc != NULL); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + x = gegl_buffer_get_x (buffer); + y = gegl_buffer_get_y (buffer); + width = gegl_buffer_get_width (buffer); + height = gegl_buffer_get_height (buffer); + + if (sc->clip && ! gimp_rectangle_intersect (x, y, width, height, + sc->clip_x, sc->clip_y, + sc->clip_w, sc->clip_h, + &x, &y, &width, &height)) + return; + + path.status = CAIRO_STATUS_SUCCESS; + path.data = (cairo_path_data_t *) sc->path_data->data; + path.num_data = sc->path_data->len; + + format = babl_format ("Y u8"); + bpp = babl_format_get_bytes_per_pixel (format); + + iter = gegl_buffer_iterator_new (buffer, NULL, 0, format, + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1); + roi = &iter->items[0].roi; + + while (gegl_buffer_iterator_next (iter)) + { + guchar *data = iter->items[0].data; + guchar *tmp_buf = NULL; + const gint stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, + roi->width); + + /* cairo rowstrides are always multiples of 4, whereas + * maskPR.rowstride can be anything, so to be able to create an + * image surface, we maybe have to create our own temporary + * buffer + */ + if (roi->width * bpp != stride) + { + if (shared_buf_size < stride * roi->height) + { + shared_buf_size = stride * roi->height; + g_free (shared_buf); + shared_buf = g_malloc (shared_buf_size); + } + tmp_buf = shared_buf; + + if (! replace) + { + const guchar *src = data; + guchar *dest = tmp_buf; + gint i; + + for (i = 0; i < roi->height; i++) + { + memcpy (dest, src, roi->width * bpp); + + src += roi->width * bpp; + dest += stride; + } + } + } + + surface = cairo_image_surface_create_for_data (tmp_buf ? + tmp_buf : data, + CAIRO_FORMAT_A8, + roi->width, roi->height, + stride); + + cairo_surface_set_device_offset (surface, + -off_x - roi->x, + -off_y - roi->y); + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + + if (replace) + { + cairo_set_source_rgba (cr, 0, 0, 0, 0); + cairo_paint (cr); + } + + cairo_set_source_rgba (cr, 0, 0, 0, value); + cairo_append_path (cr, &path); + + cairo_set_antialias (cr, antialias ? + CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE); + cairo_set_miter_limit (cr, sc->miter); + + if (sc->do_stroke) + { + cairo_set_line_cap (cr, + sc->cap == GIMP_CAP_BUTT ? CAIRO_LINE_CAP_BUTT : + sc->cap == GIMP_CAP_ROUND ? CAIRO_LINE_CAP_ROUND : + CAIRO_LINE_CAP_SQUARE); + cairo_set_line_join (cr, + sc->join == GIMP_JOIN_MITER ? CAIRO_LINE_JOIN_MITER : + sc->join == GIMP_JOIN_ROUND ? CAIRO_LINE_JOIN_ROUND : + CAIRO_LINE_JOIN_BEVEL); + + cairo_set_line_width (cr, sc->width); + + if (sc->dash_info) + cairo_set_dash (cr, + (double *) sc->dash_info->data, + sc->dash_info->len, + sc->dash_offset); + + cairo_scale (cr, 1.0, sc->ratio_xy); + cairo_stroke (cr); + } + else + { + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_fill (cr); + } + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + if (tmp_buf) + { + const guchar *src = tmp_buf; + guchar *dest = data; + gint i; + + for (i = 0; i < roi->height; i++) + { + memcpy (dest, src, roi->width * bpp); + + src += stride; + dest += roi->width * bpp; + } + } + } + + g_free (shared_buf); +} diff --git a/app/core/gimpscanconvert.h b/app/core/gimpscanconvert.h new file mode 100644 index 0000000..9df4da1 --- /dev/null +++ b/app/core/gimpscanconvert.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_SCAN_CONVERT_H__ +#define __GIMP_SCAN_CONVERT_H__ + + +GimpScanConvert * + gimp_scan_convert_new (void); + +GimpScanConvert * + gimp_scan_convert_new_from_boundary (const GimpBoundSeg *bound_segs, + gint n_bound_segs, + gint offset_x, + gint offset_y); + +void gimp_scan_convert_free (GimpScanConvert *sc); +void gimp_scan_convert_set_pixel_ratio (GimpScanConvert *sc, + gdouble ratio_xy); +void gimp_scan_convert_set_clip_rectangle (GimpScanConvert *sc, + gint x, + gint y, + gint width, + gint height); +void gimp_scan_convert_add_polyline (GimpScanConvert *sc, + guint n_points, + const GimpVector2 *points, + gboolean closed); +void gimp_scan_convert_add_bezier (GimpScanConvert *sc, + const GimpBezierDesc *bezier); +void gimp_scan_convert_stroke (GimpScanConvert *sc, + gdouble width, + GimpJoinStyle join, + GimpCapStyle cap, + gdouble miter, + gdouble dash_offset, + GArray *dash_info); +void gimp_scan_convert_render_full (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gboolean replace, + gboolean antialias, + gdouble value); + +void gimp_scan_convert_render (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gboolean antialias); +void gimp_scan_convert_render_value (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gdouble value); +void gimp_scan_convert_compose (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y); +void gimp_scan_convert_compose_value (GimpScanConvert *sc, + GeglBuffer *buffer, + gint off_x, + gint off_y, + gdouble value); + + +#endif /* __GIMP_SCAN_CONVERT_H__ */ diff --git a/app/core/gimpselection.c b/app/core/gimpselection.c new file mode 100644 index 0000000..5f66b98 --- /dev/null +++ b/app/core/gimpselection.c @@ -0,0 +1,863 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpdrawable-edit.h" +#include "gimpdrawable-private.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimplayer-new.h" +#include "gimplayermask.h" +#include "gimplayer-floating-selection.h" +#include "gimppickable.h" +#include "gimpselection.h" + +#include "gimp-intl.h" + + +static gboolean gimp_selection_is_attached (GimpItem *item); +static GimpItemTree * gimp_selection_get_tree (GimpItem *item); +static void gimp_selection_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo); +static void gimp_selection_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress); +static void gimp_selection_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y); +static void gimp_selection_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result); +static void gimp_selection_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotation_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result); +static gboolean gimp_selection_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); +static gboolean gimp_selection_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpStrokeOptions *stroke_options, + gboolean push_undo, + GimpProgress *progress, + GError **error); +static void gimp_selection_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress); +static void gimp_selection_invalidate_boundary (GimpDrawable *drawable); + +static gboolean gimp_selection_boundary (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +static gboolean gimp_selection_is_empty (GimpChannel *channel); +static void gimp_selection_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo); +static void gimp_selection_sharpen (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); +static void gimp_selection_all (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_invert (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo); +static void gimp_selection_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_selection_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); +static void gimp_selection_flood (GimpChannel *channel, + gboolean push_undo); + + +G_DEFINE_TYPE (GimpSelection, gimp_selection, GIMP_TYPE_CHANNEL) + +#define parent_class gimp_selection_parent_class + + +static void +gimp_selection_class_init (GimpSelectionClass *klass) +{ + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + GimpChannelClass *channel_class = GIMP_CHANNEL_CLASS (klass); + + viewable_class->default_icon_name = "gimp-selection"; + + item_class->is_attached = gimp_selection_is_attached; + item_class->get_tree = gimp_selection_get_tree; + item_class->translate = gimp_selection_translate; + item_class->scale = gimp_selection_scale; + item_class->resize = gimp_selection_resize; + item_class->flip = gimp_selection_flip; + item_class->rotate = gimp_selection_rotate; + item_class->fill = gimp_selection_fill; + item_class->stroke = gimp_selection_stroke; + item_class->default_name = _("Selection Mask"); + item_class->translate_desc = C_("undo-type", "Move Selection"); + item_class->fill_desc = C_("undo-type", "Fill Selection"); + item_class->stroke_desc = C_("undo-type", "Stroke Selection"); + + drawable_class->convert_type = gimp_selection_convert_type; + drawable_class->invalidate_boundary = gimp_selection_invalidate_boundary; + + channel_class->boundary = gimp_selection_boundary; + channel_class->is_empty = gimp_selection_is_empty; + channel_class->feather = gimp_selection_feather; + channel_class->sharpen = gimp_selection_sharpen; + channel_class->clear = gimp_selection_clear; + channel_class->all = gimp_selection_all; + channel_class->invert = gimp_selection_invert; + channel_class->border = gimp_selection_border; + channel_class->grow = gimp_selection_grow; + channel_class->shrink = gimp_selection_shrink; + channel_class->flood = gimp_selection_flood; + + channel_class->feather_desc = C_("undo-type", "Feather Selection"); + channel_class->sharpen_desc = C_("undo-type", "Sharpen Selection"); + channel_class->clear_desc = C_("undo-type", "Select None"); + channel_class->all_desc = C_("undo-type", "Select All"); + channel_class->invert_desc = C_("undo-type", "Invert Selection"); + channel_class->border_desc = C_("undo-type", "Border Selection"); + channel_class->grow_desc = C_("undo-type", "Grow Selection"); + channel_class->shrink_desc = C_("undo-type", "Shrink Selection"); + channel_class->flood_desc = C_("undo-type", "Remove Holes"); +} + +static void +gimp_selection_init (GimpSelection *selection) +{ +} + +static gboolean +gimp_selection_is_attached (GimpItem *item) +{ + return (GIMP_IS_IMAGE (gimp_item_get_image (item)) && + gimp_image_get_mask (gimp_item_get_image (item)) == + GIMP_CHANNEL (item)); +} + +static GimpItemTree * +gimp_selection_get_tree (GimpItem *item) +{ + return NULL; +} + +static void +gimp_selection_translate (GimpItem *item, + gdouble offset_x, + gdouble offset_y, + gboolean push_undo) +{ + GIMP_ITEM_CLASS (parent_class)->translate (item, offset_x, offset_y, + push_undo); +} + +static void +gimp_selection_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type, + GimpProgress *progress) +{ + GIMP_ITEM_CLASS (parent_class)->scale (item, new_width, new_height, + new_offset_x, new_offset_y, + interp_type, progress); + + gimp_item_set_offset (item, 0, 0); +} + +static void +gimp_selection_resize (GimpItem *item, + GimpContext *context, + GimpFillType fill_type, + gint new_width, + gint new_height, + gint offset_x, + gint offset_y) +{ + GIMP_ITEM_CLASS (parent_class)->resize (item, context, GIMP_FILL_TRANSPARENT, + new_width, new_height, + offset_x, offset_y); + + gimp_item_set_offset (item, 0, 0); +} + +static void +gimp_selection_flip (GimpItem *item, + GimpContext *context, + GimpOrientationType flip_type, + gdouble axis, + gboolean clip_result) +{ + GIMP_ITEM_CLASS (parent_class)->flip (item, context, flip_type, axis, TRUE); +} + +static void +gimp_selection_rotate (GimpItem *item, + GimpContext *context, + GimpRotationType rotation_type, + gdouble center_x, + gdouble center_y, + gboolean clip_result) +{ + GIMP_ITEM_CLASS (parent_class)->rotate (item, context, rotation_type, + center_x, center_y, + clip_result); +} + +static gboolean +gimp_selection_fill (GimpItem *item, + GimpDrawable *drawable, + GimpFillOptions *fill_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpSelection *selection = GIMP_SELECTION (item); + const GimpBoundSeg *dummy_in; + const GimpBoundSeg *dummy_out; + gint num_dummy_in; + gint num_dummy_out; + gboolean retval; + + if (! gimp_channel_boundary (GIMP_CHANNEL (selection), + &dummy_in, &dummy_out, + &num_dummy_in, &num_dummy_out, + 0, 0, 0, 0)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("There is no selection to fill.")); + return FALSE; + } + + gimp_selection_suspend (selection); + + retval = GIMP_ITEM_CLASS (parent_class)->fill (item, drawable, + fill_options, + push_undo, progress, error); + + gimp_selection_resume (selection); + + return retval; +} + +static gboolean +gimp_selection_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpStrokeOptions *stroke_options, + gboolean push_undo, + GimpProgress *progress, + GError **error) +{ + GimpSelection *selection = GIMP_SELECTION (item); + const GimpBoundSeg *dummy_in; + const GimpBoundSeg *dummy_out; + gint num_dummy_in; + gint num_dummy_out; + gboolean retval; + + if (! gimp_channel_boundary (GIMP_CHANNEL (selection), + &dummy_in, &dummy_out, + &num_dummy_in, &num_dummy_out, + 0, 0, 0, 0)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("There is no selection to stroke.")); + return FALSE; + } + + gimp_selection_suspend (selection); + + retval = GIMP_ITEM_CLASS (parent_class)->stroke (item, drawable, + stroke_options, + push_undo, progress, error); + + gimp_selection_resume (selection); + + return retval; +} + +static void +gimp_selection_convert_type (GimpDrawable *drawable, + GimpImage *dest_image, + const Babl *new_format, + GimpColorProfile *dest_profile, + GeglDitherMethod layer_dither_type, + GeglDitherMethod mask_dither_type, + gboolean push_undo, + GimpProgress *progress) +{ + new_format = + gimp_babl_mask_format (gimp_babl_format_get_precision (new_format)); + + GIMP_DRAWABLE_CLASS (parent_class)->convert_type (drawable, dest_image, + new_format, + dest_profile, + layer_dither_type, + mask_dither_type, + push_undo, + progress); +} + +static void +gimp_selection_invalidate_boundary (GimpDrawable *drawable) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpLayer *layer; + + /* Turn the current selection off */ + gimp_image_selection_invalidate (image); + + GIMP_DRAWABLE_CLASS (parent_class)->invalidate_boundary (drawable); + + /* If there is a floating selection, update it's area... + * we need to do this since this selection mask can act as an additional + * mask in the composition of the floating selection + */ + layer = gimp_image_get_active_layer (image); + + if (layer && gimp_layer_is_floating_sel (layer)) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), 0, 0, -1, -1); + } + +#if 0 + /* invalidate the preview */ + drawable->private->preview_valid = FALSE; +#endif +} + +static gboolean +gimp_selection_boundary (GimpChannel *channel, + const GimpBoundSeg **segs_in, + const GimpBoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint unused1, + gint unused2, + gint unused3, + gint unused4) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (channel)); + GimpDrawable *drawable; + GimpLayer *layer; + + if ((layer = gimp_image_get_floating_selection (image))) + { + /* If there is a floating selection, then + * we need to do some slightly different boundaries. + * Instead of inside and outside boundaries being defined + * by the extents of the layer, the inside boundary (the one + * that actually marches and is black/white) is the boundary of + * the floating selection. The outside boundary (doesn't move, + * is black/gray) is defined as the normal selection mask + */ + + /* Find the selection mask boundary */ + GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, num_segs_out, + 0, 0, 0, 0); + + /* Find the floating selection boundary */ + *segs_in = floating_sel_boundary (layer, num_segs_in); + + return TRUE; + } + else if ((drawable = gimp_image_get_active_drawable (image)) && + GIMP_IS_CHANNEL (drawable)) + { + /* Otherwise, return the boundary...if a channel is active */ + + return GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, + num_segs_out, + 0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image)); + } + else if ((layer = gimp_image_get_active_layer (image))) + { + /* If a layer is active, we return multiple boundaries based + * on the extents + */ + + gint x1, y1; + gint x2, y2; + gint offset_x; + gint offset_y; + + gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); + + x1 = CLAMP (offset_x, 0, gimp_image_get_width (image)); + y1 = CLAMP (offset_y, 0, gimp_image_get_height (image)); + x2 = CLAMP (offset_x + gimp_item_get_width (GIMP_ITEM (layer)), + 0, gimp_image_get_width (image)); + y2 = CLAMP (offset_y + gimp_item_get_height (GIMP_ITEM (layer)), + 0, gimp_image_get_height (image)); + + return GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, + num_segs_out, + x1, y1, x2, y2); + } + + *segs_in = NULL; + *segs_out = NULL; + *num_segs_in = 0; + *num_segs_out = 0; + + return FALSE; +} + +static gboolean +gimp_selection_is_empty (GimpChannel *channel) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + /* in order to allow stroking of selections, we need to pretend here + * that the selection mask is empty so that it doesn't mask the paint + * during the stroke operation. + */ + if (selection->suspend_count > 0) + return TRUE; + + return GIMP_CHANNEL_CLASS (parent_class)->is_empty (channel); +} + +static void +gimp_selection_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->feather (channel, radius_x, radius_y, + edge_lock, push_undo); +} + +static void +gimp_selection_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->sharpen (channel, push_undo); +} + +static void +gimp_selection_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->clear (channel, undo_desc, push_undo); +} + +static void +gimp_selection_all (GimpChannel *channel, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->all (channel, push_undo); +} + +static void +gimp_selection_invert (GimpChannel *channel, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->invert (channel, push_undo); +} + +static void +gimp_selection_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->border (channel, + radius_x, radius_y, + style, edge_lock, + push_undo); +} + +static void +gimp_selection_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->grow (channel, + radius_x, radius_y, + push_undo); +} + +static void +gimp_selection_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->shrink (channel, + radius_x, radius_y, edge_lock, + push_undo); +} + +static void +gimp_selection_flood (GimpChannel *channel, + gboolean push_undo) +{ + GIMP_CHANNEL_CLASS (parent_class)->flood (channel, push_undo); +} + + +/* public functions */ + +GimpChannel * +gimp_selection_new (GimpImage *image, + gint width, + gint height) +{ + GimpRGB black = { 0.0, 0.0, 0.0, 0.5 }; + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + channel = GIMP_CHANNEL (gimp_drawable_new (GIMP_TYPE_SELECTION, + image, NULL, + 0, 0, width, height, + gimp_image_get_mask_format (image))); + + gimp_channel_set_color (channel, &black, FALSE); + gimp_channel_set_show_masked (channel, TRUE); + + channel->x2 = width; + channel->y2 = height; + + return channel; +} + +gint +gimp_selection_suspend (GimpSelection *selection) +{ + g_return_val_if_fail (GIMP_IS_SELECTION (selection), 0); + + selection->suspend_count++; + + return selection->suspend_count; +} + +gint +gimp_selection_resume (GimpSelection *selection) +{ + g_return_val_if_fail (GIMP_IS_SELECTION (selection), 0); + g_return_val_if_fail (selection->suspend_count > 0, 0); + + selection->suspend_count--; + + return selection->suspend_count; +} + +GeglBuffer * +gimp_selection_extract (GimpSelection *selection, + GimpPickable *pickable, + GimpContext *context, + gboolean cut_image, + gboolean keep_indexed, + gboolean add_alpha, + gint *offset_x, + gint *offset_y, + GError **error) +{ + GimpImage *image; + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + const Babl *src_format; + const Babl *dest_format; + gint x1, y1, x2, y2; + gboolean non_empty; + gint off_x, off_y; + + g_return_val_if_fail (GIMP_IS_SELECTION (selection), NULL); + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + if (GIMP_IS_ITEM (pickable)) + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (pickable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + image = gimp_pickable_get_image (pickable); + + /* If there are no bounds, then just extract the entire image + * This may not be the correct behavior, but after getting rid + * of floating selections, it's still tempting to "cut" or "copy" + * a small layer and expect it to work, even though there is no + * actual selection mask + */ + if (GIMP_IS_DRAWABLE (pickable)) + { + non_empty = gimp_item_mask_bounds (GIMP_ITEM (pickable), + &x1, &y1, &x2, &y2); + + gimp_item_get_offset (GIMP_ITEM (pickable), &off_x, &off_y); + } + else + { + non_empty = gimp_item_bounds (GIMP_ITEM (selection), + &x1, &y1, &x2, &y2); + x2 += x1; + y2 += y1; + + off_x = 0; + off_y = 0; + + /* can't cut from non-drawables, fall back to copy */ + cut_image = FALSE; + } + + if (non_empty && ((x1 == x2) || (y1 == y2))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Unable to cut or copy because the " + "selected region is empty.")); + return NULL; + } + + /* If there is a selection, we must add alpha because the selection + * could have any shape. + */ + if (non_empty) + add_alpha = TRUE; + + src_format = gimp_pickable_get_format (pickable); + + /* How many bytes in the temp buffer? */ + if (babl_format_is_palette (src_format) && ! keep_indexed) + { + dest_format = gimp_image_get_format (image, GIMP_RGB, + gimp_image_get_precision (image), + add_alpha || + babl_format_has_alpha (src_format)); + } + else + { + if (add_alpha) + dest_format = gimp_pickable_get_format_with_alpha (pickable); + else + dest_format = src_format; + } + + gimp_pickable_flush (pickable); + + src_buffer = gimp_pickable_get_buffer (pickable); + + /* Allocate the temp buffer */ + dest_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), + dest_format); + + /* First, copy the pixels, possibly doing INDEXED->RGB and adding alpha */ + gimp_gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), + GEGL_ABYSS_NONE, + dest_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); + + if (non_empty) + { + /* If there is a selection, mask the dest_buffer with it */ + + GeglBuffer *mask_buffer; + + mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (selection)); + + gimp_gegl_apply_opacity (dest_buffer, NULL, NULL, dest_buffer, + mask_buffer, + - (off_x + x1), + - (off_y + y1), + 1.0); + + if (cut_image) + { + gimp_drawable_edit_clear (GIMP_DRAWABLE (pickable), context); + } + } + else if (cut_image) + { + /* If we're cutting without selection, remove either the layer + * (or floating selection), the layer mask, or the channel + */ + if (GIMP_IS_LAYER (pickable)) + { + gimp_image_remove_layer (image, GIMP_LAYER (pickable), + TRUE, NULL); + } + else if (GIMP_IS_LAYER_MASK (pickable)) + { + gimp_layer_apply_mask (gimp_layer_mask_get_layer (GIMP_LAYER_MASK (pickable)), + GIMP_MASK_DISCARD, TRUE); + } + else if (GIMP_IS_CHANNEL (pickable)) + { + gimp_image_remove_channel (image, GIMP_CHANNEL (pickable), + TRUE, NULL); + } + } + + *offset_x = x1 + off_x; + *offset_y = y1 + off_y; + + return dest_buffer; +} + +GimpLayer * +gimp_selection_float (GimpSelection *selection, + GimpDrawable *drawable, + GimpContext *context, + gboolean cut_image, + gint off_x, + gint off_y, + GError **error) +{ + GimpImage *image; + GimpLayer *layer; + GeglBuffer *buffer; + GimpColorProfile *profile; + gint x1, y1; + gint x2, y2; + + g_return_val_if_fail (GIMP_IS_SELECTION (selection), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + image = gimp_item_get_image (GIMP_ITEM (selection)); + + /* Make sure there is a region to float... */ + if (! gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2) || + (x1 == x2 || y1 == y2)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot float selection because the selected " + "region is empty.")); + return NULL; + } + + /* Start an undo group */ + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_FS_FLOAT, + C_("undo-type", "Float Selection")); + + /* Cut or copy the selected region */ + buffer = gimp_selection_extract (selection, GIMP_PICKABLE (drawable), context, + cut_image, FALSE, TRUE, + &x1, &y1, NULL); + + profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); + + /* Clear the selection */ + gimp_channel_clear (GIMP_CHANNEL (selection), NULL, TRUE); + + /* Create a new layer from the buffer, using the drawable's type + * because it may be different from the image's type if we cut from + * a channel or layer mask + */ + layer = gimp_layer_new_from_gegl_buffer (buffer, image, + gimp_drawable_get_format_with_alpha (drawable), + _("Floated Layer"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image), + profile); + + /* Set the offsets */ + gimp_item_set_offset (GIMP_ITEM (layer), x1 + off_x, y1 + off_y); + + /* Free the temp buffer */ + g_object_unref (buffer); + + /* Add the floating layer to the image */ + floating_sel_attach (layer, drawable); + + /* End an undo group */ + gimp_image_undo_group_end (image); + + /* invalidate the image's boundary variables */ + GIMP_CHANNEL (selection)->boundary_known = FALSE; + + return layer; +} diff --git a/app/core/gimpselection.h b/app/core/gimpselection.h new file mode 100644 index 0000000..64a2962 --- /dev/null +++ b/app/core/gimpselection.h @@ -0,0 +1,76 @@ +/* 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 . + */ + +#ifndef __GIMP_SELECTION_H__ +#define __GIMP_SELECTION_H__ + + +#include "gimpchannel.h" + + +#define GIMP_TYPE_SELECTION (gimp_selection_get_type ()) +#define GIMP_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SELECTION, GimpSelection)) +#define GIMP_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SELECTION, GimpSelectionClass)) +#define GIMP_IS_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SELECTION)) +#define GIMP_IS_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SELECTION)) +#define GIMP_SELECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SELECTION, GimpSelectionClass)) + + +typedef struct _GimpSelectionClass GimpSelectionClass; + +struct _GimpSelection +{ + GimpChannel parent_instance; + + gint suspend_count; +}; + +struct _GimpSelectionClass +{ + GimpChannelClass parent_class; +}; + + +GType gimp_selection_get_type (void) G_GNUC_CONST; + +GimpChannel * gimp_selection_new (GimpImage *image, + gint width, + gint height); + +gint gimp_selection_suspend (GimpSelection *selection); +gint gimp_selection_resume (GimpSelection *selection); + +GeglBuffer * gimp_selection_extract (GimpSelection *selection, + GimpPickable *pickable, + GimpContext *context, + gboolean cut_image, + gboolean keep_indexed, + gboolean add_alpha, + gint *offset_x, + gint *offset_y, + GError **error); + +GimpLayer * gimp_selection_float (GimpSelection *selection, + GimpDrawable *drawable, + GimpContext *context, + gboolean cut_image, + gint off_x, + gint off_y, + GError **error); + + +#endif /* __GIMP_SELECTION_H__ */ diff --git a/app/core/gimpsettings.c b/app/core/gimpsettings.c new file mode 100644 index 0000000..ee21a36 --- /dev/null +++ b/app/core/gimpsettings.c @@ -0,0 +1,195 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsettings.c + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpsettings.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_TIME +}; + + +static void gimp_settings_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_settings_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gchar * gimp_settings_get_description (GimpViewable *viewable, + gchar **tooltip); + + +G_DEFINE_TYPE (GimpSettings, gimp_settings, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_settings_parent_class + + +static void +gimp_settings_class_init (GimpSettingsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->set_property = gimp_settings_set_property; + object_class->get_property = gimp_settings_get_property; + + viewable_class->get_description = gimp_settings_get_description; + viewable_class->name_editable = TRUE; + + GIMP_CONFIG_PROP_INT64 (object_class, PROP_TIME, + "time", + "Time", + "Time of settings creation", + 0, G_MAXINT64, 0, 0); +} + +static void +gimp_settings_init (GimpSettings *settings) +{ +} + +static void +gimp_settings_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpSettings *settings = GIMP_SETTINGS (object); + + switch (property_id) + { + case PROP_TIME: + g_value_set_int64 (value, settings->time); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_settings_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpSettings *settings = GIMP_SETTINGS (object); + + switch (property_id) + { + case PROP_TIME: + settings->time = g_value_get_int64 (value); + + if (settings->time > 0) + { + GDateTime *utc = g_date_time_new_from_unix_utc (settings->time); + GDateTime *local = g_date_time_to_local (utc); + gchar *name; + + name = g_date_time_format (local, "%Y-%m-%d %H:%M:%S"); + gimp_object_take_name (GIMP_OBJECT (settings), name); + + g_date_time_unref (local); + g_date_time_unref (utc); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gchar * +gimp_settings_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpSettings *settings = GIMP_SETTINGS (viewable); + + if (settings->time > 0) + { + if (tooltip) + *tooltip = g_strdup ("You can rename automatic presets " + "to make them permanently saved"); + + return g_strdup_printf (_("Last used: %s"), + gimp_object_get_name (settings)); + } + + return GIMP_VIEWABLE_CLASS (parent_class)->get_description (viewable, + tooltip); +} + + +/* public functions */ + +gint +gimp_settings_compare (GimpSettings *a, + GimpSettings *b) +{ + const gchar *name_a = gimp_object_get_name (a); + const gchar *name_b = gimp_object_get_name (b); + + if (a->time > 0 && b->time > 0) + { + return - strcmp (name_a, name_b); + } + else if (a->time > 0) + { + return -1; + } + else if (b->time > 0) + { + return 1; + } + else if (name_a && name_b) + { + return strcmp (name_a, name_b); + } + else if (name_a) + { + return 1; + } + else if (name_b) + { + return -1; + } + + return 0; +} diff --git a/app/core/gimpsettings.h b/app/core/gimpsettings.h new file mode 100644 index 0000000..73aa96f --- /dev/null +++ b/app/core/gimpsettings.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsettings.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_SETTINGS_H__ +#define __GIMP_SETTINGS_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_SETTINGS (gimp_settings_get_type ()) +#define GIMP_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SETTINGS, GimpSettings)) +#define GIMP_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SETTINGS, GimpSettingsClass)) +#define GIMP_IS_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SETTINGS)) +#define GIMP_IS_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SETTINGS)) +#define GIMP_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SETTINGS, GimpSettingsClass)) + + +typedef struct _GimpSettingsClass GimpSettingsClass; + +struct _GimpSettings +{ + GimpViewable parent_instance; + + gint64 time; +}; + +struct _GimpSettingsClass +{ + GimpViewableClass parent_class; +}; + + +GType gimp_settings_get_type (void) G_GNUC_CONST; + +gint gimp_settings_compare (GimpSettings *a, + GimpSettings *b); + + +#endif /* __GIMP_SETTINGS_H__ */ diff --git a/app/core/gimpstrokeoptions.c b/app/core/gimpstrokeoptions.c new file mode 100644 index 0000000..3bb5c92 --- /dev/null +++ b/app/core/gimpstrokeoptions.c @@ -0,0 +1,638 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpstrokeoptions.c + * Copyright (C) 2003 Simon Budig + * Copyright (C) 2004 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpdashpattern.h" +#include "gimpmarshal.h" +#include "gimppaintinfo.h" +#include "gimpparamspecs.h" +#include "gimpstrokeoptions.h" + +#include "paint/gimppaintoptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + + PROP_METHOD, + + PROP_STYLE, + PROP_WIDTH, + PROP_UNIT, + PROP_CAP_STYLE, + PROP_JOIN_STYLE, + PROP_MITER_LIMIT, + PROP_ANTIALIAS, + PROP_DASH_UNIT, + PROP_DASH_OFFSET, + PROP_DASH_INFO, + + PROP_PAINT_OPTIONS, + PROP_EMULATE_DYNAMICS +}; + +enum +{ + DASH_INFO_CHANGED, + LAST_SIGNAL +}; + + +typedef struct _GimpStrokeOptionsPrivate GimpStrokeOptionsPrivate; + +struct _GimpStrokeOptionsPrivate +{ + GimpStrokeMethod method; + + /* options for method == LIBART */ + gdouble width; + GimpUnit unit; + + GimpCapStyle cap_style; + GimpJoinStyle join_style; + + gdouble miter_limit; + + gdouble dash_offset; + GArray *dash_info; + + /* options for method == PAINT_TOOL */ + GimpPaintOptions *paint_options; + gboolean emulate_dynamics; +}; + +#define GET_PRIVATE(options) \ + ((GimpStrokeOptionsPrivate *) gimp_stroke_options_get_instance_private ((GimpStrokeOptions *) (options))) + + +static void gimp_stroke_options_config_iface_init (gpointer iface, + gpointer iface_data); + +static void gimp_stroke_options_finalize (GObject *object); +static void gimp_stroke_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_stroke_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static GimpConfig * gimp_stroke_options_duplicate (GimpConfig *config); + + +G_DEFINE_TYPE_WITH_CODE (GimpStrokeOptions, gimp_stroke_options, + GIMP_TYPE_FILL_OPTIONS, + G_ADD_PRIVATE (GimpStrokeOptions) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_stroke_options_config_iface_init)) + +#define parent_class gimp_stroke_options_parent_class + +static GimpConfigInterface *parent_config_iface = NULL; + +static guint stroke_options_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_stroke_options_class_init (GimpStrokeOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *array_spec; + + object_class->finalize = gimp_stroke_options_finalize; + object_class->set_property = gimp_stroke_options_set_property; + object_class->get_property = gimp_stroke_options_get_property; + + klass->dash_info_changed = NULL; + + stroke_options_signals[DASH_INFO_CHANGED] = + g_signal_new ("dash-info-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpStrokeOptionsClass, dash_info_changed), + NULL, NULL, + gimp_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GIMP_TYPE_DASH_PRESET); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_METHOD, + "method", + _("Method"), + NULL, + GIMP_TYPE_STROKE_METHOD, + GIMP_STROKE_LINE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_WIDTH, + "width", + _("Line width"), + NULL, + 0.0, 2000.0, 6.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_UNIT (object_class, PROP_UNIT, + "unit", + _("Unit"), + NULL, + TRUE, FALSE, GIMP_UNIT_PIXEL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CAP_STYLE, + "cap-style", + _("Cap style"), + NULL, + GIMP_TYPE_CAP_STYLE, GIMP_CAP_BUTT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_JOIN_STYLE, + "join-style", + _("Join style"), + NULL, + GIMP_TYPE_JOIN_STYLE, GIMP_JOIN_MITER, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_MITER_LIMIT, + "miter-limit", + _("Miter limit"), + _("Convert a mitered join to a bevelled " + "join if the miter would extend to a " + "distance of more than miter-limit * " + "line-width from the actual join point."), + 0.0, 100.0, 10.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_DASH_OFFSET, + "dash-offset", + _("Dash offset"), + NULL, + 0.0, 2000.0, 0.0, + GIMP_PARAM_STATIC_STRINGS); + + array_spec = g_param_spec_double ("dash-length", NULL, NULL, + 0.0, 2000.0, 1.0, GIMP_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_DASH_INFO, + gimp_param_spec_value_array ("dash-info", + NULL, NULL, + array_spec, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_FLAGS)); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_PAINT_OPTIONS, + "paint-options", + NULL, NULL, + GIMP_TYPE_PAINT_OPTIONS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EMULATE_DYNAMICS, + "emulate-brush-dynamics", + _("Emulate brush dynamics"), + NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_stroke_options_config_iface_init (gpointer iface, + gpointer iface_data) +{ + GimpConfigInterface *config_iface = (GimpConfigInterface *) iface; + + parent_config_iface = g_type_interface_peek_parent (config_iface); + + if (! parent_config_iface) + parent_config_iface = g_type_default_interface_peek (GIMP_TYPE_CONFIG); + + config_iface->duplicate = gimp_stroke_options_duplicate; +} + +static void +gimp_stroke_options_init (GimpStrokeOptions *options) +{ +} + +static void +gimp_stroke_options_finalize (GObject *object) +{ + GimpStrokeOptionsPrivate *private = GET_PRIVATE (object); + + if (private->dash_info) + { + gimp_dash_pattern_free (private->dash_info); + private->dash_info = NULL; + } + + g_clear_object (&private->paint_options); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_stroke_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpStrokeOptions *options = GIMP_STROKE_OPTIONS (object); + GimpStrokeOptionsPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_METHOD: + private->method = g_value_get_enum (value); + break; + + case PROP_WIDTH: + private->width = g_value_get_double (value); + break; + case PROP_UNIT: + private->unit = g_value_get_int (value); + break; + case PROP_CAP_STYLE: + private->cap_style = g_value_get_enum (value); + break; + case PROP_JOIN_STYLE: + private->join_style = g_value_get_enum (value); + break; + case PROP_MITER_LIMIT: + private->miter_limit = g_value_get_double (value); + break; + case PROP_DASH_OFFSET: + private->dash_offset = g_value_get_double (value); + break; + case PROP_DASH_INFO: + { + GimpValueArray *value_array = g_value_get_boxed (value); + GArray *pattern; + + pattern = gimp_dash_pattern_from_value_array (value_array); + gimp_stroke_options_take_dash_pattern (options, GIMP_DASH_CUSTOM, + pattern); + } + break; + + case PROP_PAINT_OPTIONS: + if (private->paint_options) + g_object_unref (private->paint_options); + private->paint_options = g_value_dup_object (value); + break; + case PROP_EMULATE_DYNAMICS: + private->emulate_dynamics = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_stroke_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpStrokeOptionsPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_METHOD: + g_value_set_enum (value, private->method); + break; + + case PROP_WIDTH: + g_value_set_double (value, private->width); + break; + case PROP_UNIT: + g_value_set_int (value, private->unit); + break; + case PROP_CAP_STYLE: + g_value_set_enum (value, private->cap_style); + break; + case PROP_JOIN_STYLE: + g_value_set_enum (value, private->join_style); + break; + case PROP_MITER_LIMIT: + g_value_set_double (value, private->miter_limit); + break; + case PROP_DASH_OFFSET: + g_value_set_double (value, private->dash_offset); + break; + case PROP_DASH_INFO: + { + GimpValueArray *value_array; + + value_array = gimp_dash_pattern_to_value_array (private->dash_info); + g_value_take_boxed (value, value_array); + } + break; + + case PROP_PAINT_OPTIONS: + g_value_set_object (value, private->paint_options); + break; + case PROP_EMULATE_DYNAMICS: + g_value_set_boolean (value, private->emulate_dynamics); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GimpConfig * +gimp_stroke_options_duplicate (GimpConfig *config) +{ + GimpStrokeOptions *options = GIMP_STROKE_OPTIONS (config); + GimpStrokeOptionsPrivate *private = GET_PRIVATE (options); + GimpStrokeOptions *new_options; + + new_options = GIMP_STROKE_OPTIONS (parent_config_iface->duplicate (config)); + + if (private->paint_options) + { + GObject *paint_options; + + paint_options = gimp_config_duplicate (GIMP_CONFIG (private->paint_options)); + g_object_set (new_options, "paint-options", paint_options, NULL); + g_object_unref (paint_options); + } + + return GIMP_CONFIG (new_options); +} + + +/* public functions */ + +GimpStrokeOptions * +gimp_stroke_options_new (Gimp *gimp, + GimpContext *context, + gboolean use_context_color) +{ + GimpPaintInfo *paint_info = NULL; + GimpStrokeOptions *options; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (use_context_color == FALSE || context != NULL, NULL); + + if (context) + paint_info = gimp_context_get_paint_info (context); + + if (! paint_info) + paint_info = gimp_paint_info_get_standard (gimp); + + options = g_object_new (GIMP_TYPE_STROKE_OPTIONS, + "gimp", gimp, + "paint-info", paint_info, + NULL); + + if (use_context_color) + { + gimp_context_define_properties (GIMP_CONTEXT (options), + GIMP_CONTEXT_PROP_MASK_FOREGROUND | + GIMP_CONTEXT_PROP_MASK_PATTERN, + FALSE); + + gimp_context_set_parent (GIMP_CONTEXT (options), context); + } + + return options; +} + +GimpStrokeMethod +gimp_stroke_options_get_method (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), + GIMP_STROKE_LINE); + + return GET_PRIVATE (options)->method; +} + +gdouble +gimp_stroke_options_get_width (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), 1.0); + + return GET_PRIVATE (options)->width; +} + +GimpUnit +gimp_stroke_options_get_unit (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), GIMP_UNIT_PIXEL); + + return GET_PRIVATE (options)->unit; +} + +GimpCapStyle +gimp_stroke_options_get_cap_style (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), GIMP_CAP_BUTT); + + return GET_PRIVATE (options)->cap_style; +} + +GimpJoinStyle +gimp_stroke_options_get_join_style (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), GIMP_JOIN_MITER); + + return GET_PRIVATE (options)->join_style; +} + +gdouble +gimp_stroke_options_get_miter_limit (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), 1.0); + + return GET_PRIVATE (options)->miter_limit; +} + +gdouble +gimp_stroke_options_get_dash_offset (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), 0.0); + + return GET_PRIVATE (options)->dash_offset; +} + +GArray * +gimp_stroke_options_get_dash_info (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), NULL); + + return GET_PRIVATE (options)->dash_info; +} + +GimpPaintOptions * +gimp_stroke_options_get_paint_options (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), NULL); + + return GET_PRIVATE (options)->paint_options; +} + +gboolean +gimp_stroke_options_get_emulate_dynamics (GimpStrokeOptions *options) +{ + g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (options), FALSE); + + return GET_PRIVATE (options)->emulate_dynamics; +} + +/** + * gimp_stroke_options_take_dash_pattern: + * @options: a #GimpStrokeOptions object + * @preset: a value out of the #GimpDashPreset enum + * @pattern: a #GArray or %NULL if @preset is not %GIMP_DASH_CUSTOM + * + * Sets the dash pattern. Either a @preset is passed and @pattern is + * %NULL or @preset is %GIMP_DASH_CUSTOM and @pattern is the #GArray + * to use as the dash pattern. Note that this function takes ownership + * of the passed pattern. + */ +void +gimp_stroke_options_take_dash_pattern (GimpStrokeOptions *options, + GimpDashPreset preset, + GArray *pattern) +{ + GimpStrokeOptionsPrivate *private; + + g_return_if_fail (GIMP_IS_STROKE_OPTIONS (options)); + g_return_if_fail (preset == GIMP_DASH_CUSTOM || pattern == NULL); + + private = GET_PRIVATE (options); + + if (preset != GIMP_DASH_CUSTOM) + pattern = gimp_dash_pattern_new_from_preset (preset); + + if (private->dash_info) + gimp_dash_pattern_free (private->dash_info); + + private->dash_info = pattern; + + g_object_notify (G_OBJECT (options), "dash-info"); + + g_signal_emit (options, stroke_options_signals [DASH_INFO_CHANGED], 0, + preset); +} + +void +gimp_stroke_options_prepare (GimpStrokeOptions *options, + GimpContext *context, + GimpPaintOptions *paint_options) +{ + GimpStrokeOptionsPrivate *private; + + g_return_if_fail (GIMP_IS_STROKE_OPTIONS (options)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (paint_options == NULL || + GIMP_IS_PAINT_OPTIONS (paint_options)); + + private = GET_PRIVATE (options); + + switch (private->method) + { + case GIMP_STROKE_LINE: + break; + + case GIMP_STROKE_PAINT_METHOD: + { + GimpPaintInfo *paint_info = GIMP_CONTEXT (options)->paint_info; + + if (paint_options) + { + g_return_if_fail (paint_info == paint_options->paint_info); + + /* undefine the paint-relevant context properties and get them + * from the passed context + */ + gimp_context_define_properties (GIMP_CONTEXT (paint_options), + GIMP_CONTEXT_PROP_MASK_PAINT, + FALSE); + gimp_context_set_parent (GIMP_CONTEXT (paint_options), context); + + g_object_ref (paint_options); + } + else + { + GimpCoreConfig *config = context->gimp->config; + GimpContextPropMask global_props = 0; + + paint_options = + gimp_config_duplicate (GIMP_CONFIG (paint_info->paint_options)); + + /* FG and BG are always shared between all tools */ + global_props |= GIMP_CONTEXT_PROP_MASK_FOREGROUND; + global_props |= GIMP_CONTEXT_PROP_MASK_BACKGROUND; + + if (config->global_brush) + global_props |= GIMP_CONTEXT_PROP_MASK_BRUSH; + if (config->global_dynamics) + global_props |= GIMP_CONTEXT_PROP_MASK_DYNAMICS; + if (config->global_pattern) + global_props |= GIMP_CONTEXT_PROP_MASK_PATTERN; + if (config->global_palette) + global_props |= GIMP_CONTEXT_PROP_MASK_PALETTE; + if (config->global_gradient) + global_props |= GIMP_CONTEXT_PROP_MASK_GRADIENT; + if (config->global_font) + global_props |= GIMP_CONTEXT_PROP_MASK_FONT; + + gimp_context_copy_properties (context, + GIMP_CONTEXT (paint_options), + global_props); + } + + g_object_set (options, "paint-options", paint_options, NULL); + g_object_unref (paint_options); + } + break; + + default: + g_return_if_reached (); + } +} + +void +gimp_stroke_options_finish (GimpStrokeOptions *options) +{ + g_return_if_fail (GIMP_IS_STROKE_OPTIONS (options)); + + g_object_set (options, "paint-options", NULL, NULL); +} diff --git a/app/core/gimpstrokeoptions.h b/app/core/gimpstrokeoptions.h new file mode 100644 index 0000000..f5210e8 --- /dev/null +++ b/app/core/gimpstrokeoptions.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpstrokeoptions.h + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_STROKE_OPTIONS_H__ +#define __GIMP_STROKE_OPTIONS_H__ + + +#include "gimpfilloptions.h" + + +#define GIMP_TYPE_STROKE_OPTIONS (gimp_stroke_options_get_type ()) +#define GIMP_STROKE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_STROKE_OPTIONS, GimpStrokeOptions)) +#define GIMP_STROKE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_STROKE_OPTIONS, GimpStrokeOptionsClass)) +#define GIMP_IS_STROKE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_STROKE_OPTIONS)) +#define GIMP_IS_STROKE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_STROKE_OPTIONS)) +#define GIMP_STROKE_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_STROKE_OPTIONS, GimpStrokeOptionsClass)) + + +typedef struct _GimpStrokeOptionsClass GimpStrokeOptionsClass; + +struct _GimpStrokeOptions +{ + GimpFillOptions parent_instance; +}; + +struct _GimpStrokeOptionsClass +{ + GimpFillOptionsClass parent_class; + + void (* dash_info_changed) (GimpStrokeOptions *stroke_options, + GimpDashPreset preset); +}; + + +GType gimp_stroke_options_get_type (void) G_GNUC_CONST; + +GimpStrokeOptions * gimp_stroke_options_new (Gimp *gimp, + GimpContext *context, + gboolean use_context_color); + +GimpStrokeMethod gimp_stroke_options_get_method (GimpStrokeOptions *options); + +gdouble gimp_stroke_options_get_width (GimpStrokeOptions *options); +GimpUnit gimp_stroke_options_get_unit (GimpStrokeOptions *options); +GimpCapStyle gimp_stroke_options_get_cap_style (GimpStrokeOptions *options); +GimpJoinStyle gimp_stroke_options_get_join_style (GimpStrokeOptions *options); +gdouble gimp_stroke_options_get_miter_limit (GimpStrokeOptions *options); +gdouble gimp_stroke_options_get_dash_offset (GimpStrokeOptions *options); +GArray * gimp_stroke_options_get_dash_info (GimpStrokeOptions *options); + +GimpPaintOptions * gimp_stroke_options_get_paint_options (GimpStrokeOptions *options); +gboolean gimp_stroke_options_get_emulate_dynamics (GimpStrokeOptions *options); + +void gimp_stroke_options_take_dash_pattern (GimpStrokeOptions *options, + GimpDashPreset preset, + GArray *pattern); + +void gimp_stroke_options_prepare (GimpStrokeOptions *options, + GimpContext *context, + GimpPaintOptions *paint_options); +void gimp_stroke_options_finish (GimpStrokeOptions *options); + + +#endif /* __GIMP_STROKE_OPTIONS_H__ */ diff --git a/app/core/gimpsubprogress.c b/app/core/gimpsubprogress.c new file mode 100644 index 0000000..3183f61 --- /dev/null +++ b/app/core/gimpsubprogress.c @@ -0,0 +1,317 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "core/gimpsubprogress.h" +#include "core/gimpprogress.h" + + +enum +{ + PROP_0, + PROP_PROGRESS +}; + + +static void gimp_sub_progress_iface_init (GimpProgressInterface *iface); + +static void gimp_sub_progress_finalize (GObject *object); +static void gimp_sub_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_sub_progress_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static GimpProgress * gimp_sub_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message); +static void gimp_sub_progress_end (GimpProgress *progress); +static gboolean gimp_sub_progress_is_active (GimpProgress *progress); +static void gimp_sub_progress_set_text (GimpProgress *progress, + const gchar *message); +static void gimp_sub_progress_set_value (GimpProgress *progress, + gdouble percentage); +static gdouble gimp_sub_progress_get_value (GimpProgress *progress); +static void gimp_sub_progress_pulse (GimpProgress *progress); +static guint32 gimp_sub_progress_get_window_id (GimpProgress *progress); +static gboolean gimp_sub_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + + +G_DEFINE_TYPE_WITH_CODE (GimpSubProgress, gimp_sub_progress, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS, + gimp_sub_progress_iface_init)) + +#define parent_class gimp_sub_progress_parent_class + + +static void +gimp_sub_progress_class_init (GimpSubProgressClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_sub_progress_finalize; + object_class->set_property = gimp_sub_progress_set_property; + object_class->get_property = gimp_sub_progress_get_property; + + g_object_class_install_property (object_class, PROP_PROGRESS, + g_param_spec_object ("progress", + NULL, NULL, + GIMP_TYPE_PROGRESS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_sub_progress_init (GimpSubProgress *sub) +{ + sub->progress = NULL; + sub->start = 0.0; + sub->end = 1.0; +} + +static void +gimp_sub_progress_finalize (GObject *object) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (object); + + g_clear_object (&sub->progress); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_sub_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_sub_progress_start; + iface->end = gimp_sub_progress_end; + iface->is_active = gimp_sub_progress_is_active; + iface->set_text = gimp_sub_progress_set_text; + iface->set_value = gimp_sub_progress_set_value; + iface->get_value = gimp_sub_progress_get_value; + iface->pulse = gimp_sub_progress_pulse; + iface->get_window_id = gimp_sub_progress_get_window_id; + iface->message = gimp_sub_progress_message; +} + +static void +gimp_sub_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (object); + + switch (property_id) + { + case PROP_PROGRESS: + g_return_if_fail (sub->progress == NULL); + sub->progress = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_sub_progress_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (object); + + switch (property_id) + { + case PROP_PROGRESS: + g_value_set_object (value, sub->progress); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GimpProgress * +gimp_sub_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + /* does nothing */ + return NULL; +} + +static void +gimp_sub_progress_end (GimpProgress *progress) +{ + /* does nothing */ +} + +static gboolean +gimp_sub_progress_is_active (GimpProgress *progress) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + return gimp_progress_is_active (sub->progress); + + return FALSE; +} + +static void +gimp_sub_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + /* does nothing */ +} + +static void +gimp_sub_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + gimp_progress_set_value (sub->progress, + sub->start + percentage * (sub->end - sub->start)); +} + +static gdouble +gimp_sub_progress_get_value (GimpProgress *progress) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + return gimp_progress_get_value (sub->progress); + + return 0.0; +} + +static void +gimp_sub_progress_pulse (GimpProgress *progress) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + gimp_progress_pulse (sub->progress); +} + +static guint32 +gimp_sub_progress_get_window_id (GimpProgress *progress) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + return gimp_progress_get_window_id (sub->progress); + + return 0; +} + +static gboolean +gimp_sub_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); + + if (sub->progress) + return gimp_progress_message (sub->progress, + gimp, severity, domain, message); + + return FALSE; +} + +/** + * gimp_sub_progress_new: + * @progress: parent progress or %NULL + * + * GimpSubProgress implements the GimpProgress interface and can be + * used wherever a GimpProgress is needed. It maps progress + * information to a sub-range of its parent @progress. This is useful + * when an action breaks down into multiple sub-actions that itself + * need a #GimpProgress pointer. See gimp_image_scale() for an example. + * + * Return value: a new #GimpProgress object + */ +GimpProgress * +gimp_sub_progress_new (GimpProgress *progress) +{ + GimpSubProgress *sub; + + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + + sub = g_object_new (GIMP_TYPE_SUB_PROGRESS, + "progress", progress, + NULL); + + return GIMP_PROGRESS (sub); +} + +/** + * gimp_sub_progress_set_range: + * @start: start value of range on the parent process + * @end: end value of range on the parent process + * + * Sets a range on the parent progress that this @progress should be + * mapped to. + */ +void +gimp_sub_progress_set_range (GimpSubProgress *progress, + gdouble start, + gdouble end) +{ + g_return_if_fail (GIMP_IS_SUB_PROGRESS (progress)); + g_return_if_fail (start < end); + + progress->start = start; + progress->end = end; +} + +/** + * gimp_sub_progress_set_step: + * @index: step index + * @num_steps: number of steps + * + * A more convenient form of gimp_sub_progress_set_range(). + */ +void +gimp_sub_progress_set_step (GimpSubProgress *progress, + gint index, + gint num_steps) +{ + g_return_if_fail (GIMP_IS_SUB_PROGRESS (progress)); + g_return_if_fail (index < num_steps && num_steps > 0); + + progress->start = (gdouble) index / num_steps; + progress->end = (gdouble) (index + 1) / num_steps; +} diff --git a/app/core/gimpsubprogress.h b/app/core/gimpsubprogress.h new file mode 100644 index 0000000..c031182 --- /dev/null +++ b/app/core/gimpsubprogress.h @@ -0,0 +1,59 @@ +/* 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 . + */ + +#ifndef __GIMP_SUB_PROGRESS_H__ +#define __GIMP_SUB_PROGRESS_H__ + + +#define GIMP_TYPE_SUB_PROGRESS (gimp_sub_progress_get_type ()) +#define GIMP_SUB_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SUB_PROGRESS, GimpSubProgress)) +#define GIMP_SUB_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SUB_PROGRESS, GimpSubProgressClass)) +#define GIMP_IS_SUB_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SUB_PROGRESS)) +#define GIMP_IS_SUB_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SUB_PROGRESS)) +#define GIMP_SUB_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SUB_PROGRESS, GimpSubProgressClass)) + + +typedef struct _GimpSubProgressClass GimpSubProgressClass; + +struct _GimpSubProgress +{ + GObject parent_instance; + + GimpProgress *progress; + gdouble start; + gdouble end; +}; + +struct _GimpSubProgressClass +{ + GObjectClass parent_class; +}; + + +GType gimp_sub_progress_get_type (void) G_GNUC_CONST; + +GimpProgress * gimp_sub_progress_new (GimpProgress *progress); +void gimp_sub_progress_set_range (GimpSubProgress *progress, + gdouble start, + gdouble end); +void gimp_sub_progress_set_step (GimpSubProgress *progress, + gint index, + gint num_steps); + + + +#endif /* __GIMP_SUB_PROGRESS_H__ */ diff --git a/app/core/gimpsymmetry-mandala.c b/app/core/gimpsymmetry-mandala.c new file mode 100644 index 0000000..668819a --- /dev/null +++ b/app/core/gimpsymmetry-mandala.c @@ -0,0 +1,574 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-mandala.c + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-cairo.h" +#include "gimpdrawable.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-symmetry.h" +#include "gimpitem.h" +#include "gimpsymmetry-mandala.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CENTER_X, + PROP_CENTER_Y, + PROP_SIZE, + PROP_DISABLE_TRANSFORMATION, + PROP_ENABLE_REFLECTION, +}; + + +/* Local function prototypes */ + +static void gimp_mandala_constructed (GObject *object); +static void gimp_mandala_finalize (GObject *object); +static void gimp_mandala_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mandala_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_mandala_active_changed (GimpSymmetry *sym); + +static void gimp_mandala_add_guide (GimpMandala *mandala, + GimpOrientationType orientation); +static void gimp_mandala_remove_guide (GimpMandala *mandala, + GimpOrientationType orientation); +static void gimp_mandala_guide_removed_cb (GObject *object, + GimpMandala *mandala); +static void gimp_mandala_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMandala *mandala); + +static void gimp_mandala_update_strokes (GimpSymmetry *mandala, + GimpDrawable *drawable, + GimpCoords *origin); +static void gimp_mandala_get_transform (GimpSymmetry *mandala, + gint stroke, + gdouble *angle, + gboolean *reflect); +static void gimp_mandala_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym); + + +G_DEFINE_TYPE (GimpMandala, gimp_mandala, GIMP_TYPE_SYMMETRY) + +#define parent_class gimp_mandala_parent_class + + +static void +gimp_mandala_class_init (GimpMandalaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpSymmetryClass *symmetry_class = GIMP_SYMMETRY_CLASS (klass); + GParamSpec *pspec; + + object_class->constructed = gimp_mandala_constructed; + object_class->finalize = gimp_mandala_finalize; + object_class->set_property = gimp_mandala_set_property; + object_class->get_property = gimp_mandala_get_property; + + symmetry_class->label = _("Mandala"); + symmetry_class->update_strokes = gimp_mandala_update_strokes; + symmetry_class->get_transform = gimp_mandala_get_transform; + symmetry_class->active_changed = gimp_mandala_active_changed; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CENTER_X, + "center-x", + _("Center abscissa"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "center-x"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "x"); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CENTER_Y, + "center-y", + _("Center ordinate"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "center-y"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "y"); + + GIMP_CONFIG_PROP_INT (object_class, PROP_SIZE, + "size", + _("Number of points"), + NULL, + 1, 100, 6.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISABLE_TRANSFORMATION, + "disable-transformation", + _("Disable brush transform"), + _("Disable brush rotation"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ENABLE_REFLECTION, + "enable-reflection", + _("Kaleidoscope"), + _("Reflect consecutive strokes"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); +} + +static void +gimp_mandala_init (GimpMandala *mandala) +{ +} + +static void +gimp_mandala_constructed (GObject *object) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + g_signal_connect_object (sym->image, "size-changed-detailed", + G_CALLBACK (gimp_mandala_image_size_changed_cb), + sym, 0); +} + +static void +gimp_mandala_finalize (GObject *object) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + + g_clear_object (&mandala->horizontal_guide); + g_clear_object (&mandala->vertical_guide); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_mandala_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + GimpImage *image = GIMP_SYMMETRY (mandala)->image; + + switch (property_id) + { + case PROP_CENTER_X: + if (g_value_get_double (value) > 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_width (image)) + { + mandala->center_x = g_value_get_double (value); + + if (mandala->vertical_guide) + { + g_signal_handlers_block_by_func (mandala->vertical_guide, + gimp_mandala_guide_position_cb, + mandala); + gimp_image_move_guide (image, mandala->vertical_guide, + mandala->center_x, + FALSE); + g_signal_handlers_unblock_by_func (mandala->vertical_guide, + gimp_mandala_guide_position_cb, + mandala); + } + } + break; + + case PROP_CENTER_Y: + if (g_value_get_double (value) > 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_height (image)) + { + mandala->center_y = g_value_get_double (value); + + if (mandala->horizontal_guide) + { + g_signal_handlers_block_by_func (mandala->horizontal_guide, + gimp_mandala_guide_position_cb, + mandala); + gimp_image_move_guide (image, mandala->horizontal_guide, + mandala->center_y, + FALSE); + g_signal_handlers_unblock_by_func (mandala->horizontal_guide, + gimp_mandala_guide_position_cb, + mandala); + } + } + break; + + case PROP_SIZE: + mandala->size = g_value_get_int (value); + break; + + case PROP_DISABLE_TRANSFORMATION: + mandala->disable_transformation = g_value_get_boolean (value); + break; + + case PROP_ENABLE_REFLECTION: + mandala->enable_reflection = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mandala_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpMandala *mandala = GIMP_MANDALA (object); + + switch (property_id) + { + case PROP_CENTER_X: + g_value_set_double (value, mandala->center_x); + break; + case PROP_CENTER_Y: + g_value_set_double (value, mandala->center_y); + break; + case PROP_SIZE: + g_value_set_int (value, mandala->size); + break; + case PROP_DISABLE_TRANSFORMATION: + g_value_set_boolean (value, mandala->disable_transformation); + break; + case PROP_ENABLE_REFLECTION: + g_value_set_boolean (value, mandala->enable_reflection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mandala_active_changed (GimpSymmetry *sym) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + + if (sym->active) + { + if (! mandala->horizontal_guide) + gimp_mandala_add_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + + if (! mandala->vertical_guide) + gimp_mandala_add_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } + else + { + if (mandala->horizontal_guide) + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + + if (mandala->vertical_guide) + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } +} + +static void +gimp_mandala_add_guide (GimpMandala *mandala, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + GimpImage *image; + Gimp *gimp; + GimpGuide *guide; + gint position; + + image = sym->image; + gimp = image->gimp; + + guide = gimp_guide_custom_new (orientation, + gimp->next_guide_ID++, + GIMP_GUIDE_STYLE_MANDALA); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + { + mandala->horizontal_guide = guide; + + /* Mandala guide position at first activation is at canvas middle. */ + if (mandala->center_y < 1.0) + mandala->center_y = (gdouble) gimp_image_get_height (image) / 2.0; + + position = (gint) mandala->center_y; + } + else + { + mandala->vertical_guide = guide; + + /* Mandala guide position at first activation is at canvas middle. */ + if (mandala->center_x < 1.0) + mandala->center_x = (gdouble) gimp_image_get_width (image) / 2.0; + + position = (gint) mandala->center_x; + } + + g_signal_connect (guide, "removed", + G_CALLBACK (gimp_mandala_guide_removed_cb), + mandala); + + gimp_image_add_guide (image, guide, position); + + g_signal_connect (guide, "notify::position", + G_CALLBACK (gimp_mandala_guide_position_cb), + mandala); +} + +static void +gimp_mandala_remove_guide (GimpMandala *mandala, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + GimpImage *image; + GimpGuide *guide; + + image = sym->image; + guide = (orientation == GIMP_ORIENTATION_HORIZONTAL) ? + mandala->horizontal_guide : mandala->vertical_guide; + + g_signal_handlers_disconnect_by_func (guide, + gimp_mandala_guide_removed_cb, + mandala); + g_signal_handlers_disconnect_by_func (guide, + gimp_mandala_guide_position_cb, + mandala); + + gimp_image_remove_guide (image, guide, FALSE); + g_object_unref (guide); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + mandala->horizontal_guide = NULL; + else + mandala->vertical_guide = NULL; +} + +static void +gimp_mandala_guide_removed_cb (GObject *object, + GimpMandala *mandala) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mandala); + + g_signal_handlers_disconnect_by_func (object, + gimp_mandala_guide_removed_cb, + mandala); + g_signal_handlers_disconnect_by_func (object, + gimp_mandala_guide_position_cb, + mandala); + + if (GIMP_GUIDE (object) == mandala->horizontal_guide) + { + g_object_unref (mandala->horizontal_guide); + + mandala->horizontal_guide = NULL; + mandala->center_y = 0.0; + + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_VERTICAL); + } + else if (GIMP_GUIDE (object) == mandala->vertical_guide) + { + g_object_unref (mandala->vertical_guide); + mandala->vertical_guide = NULL; + mandala->center_x = 0.0; + + gimp_mandala_remove_guide (mandala, GIMP_ORIENTATION_HORIZONTAL); + } + + gimp_image_symmetry_remove (sym->image, sym); +} + +static void +gimp_mandala_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMandala *mandala) +{ + GimpGuide *guide = GIMP_GUIDE (object); + + if (guide == mandala->horizontal_guide) + { + g_object_set (G_OBJECT (mandala), + "center-y", (gdouble) gimp_guide_get_position (guide), + NULL); + } + else if (guide == mandala->vertical_guide) + { + g_object_set (G_OBJECT (mandala), + "center-x", (gdouble) gimp_guide_get_position (guide), + NULL); + } +} + +static void +gimp_mandala_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + GimpCoords *coords; + GimpMatrix3 matrix; + gdouble slice_angle; + gdouble mid_slice_angle = 0.0; + gdouble center_x, center_y; + gint offset_x, offset_y; + gint i; + + gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); + + center_x = mandala->center_x - offset_x; + center_y = mandala->center_y - offset_y; + + g_list_free_full (sym->strokes, g_free); + sym->strokes = NULL; + + coords = g_memdup (sym->origin, sizeof (GimpCoords)); + sym->strokes = g_list_prepend (sym->strokes, coords); + + /* The angle of each slice, in radians. */ + slice_angle = 2.0 * G_PI / mandala->size; + + if (mandala->enable_reflection) + { + /* Find out in which slice the user is currently drawing. */ + gdouble angle = atan2 (sym->origin->y - center_y, + sym->origin->x - center_x); + gint slice_no = (int) floor(angle/slice_angle); + + /* Angle where the middle of that slice is. */ + mid_slice_angle = slice_no * slice_angle + slice_angle / 2.0; + } + + for (i = 1; i < mandala->size; i++) + { + gdouble new_x, new_y; + + coords = g_memdup (sym->origin, sizeof (GimpCoords)); + gimp_matrix3_identity (&matrix); + gimp_matrix3_translate (&matrix, + -center_x, + -center_y); + if (mandala->enable_reflection && i % 2 == 1) + { + /* Reflecting over the mid_slice_angle axis, reflects slice without changing position. */ + gimp_matrix3_rotate(&matrix, -mid_slice_angle); + gimp_matrix3_scale (&matrix, 1, -1); + gimp_matrix3_rotate(&matrix, mid_slice_angle - i * slice_angle); + } + else + { + gimp_matrix3_rotate (&matrix, - i * slice_angle); + } + gimp_matrix3_translate (&matrix, + +center_x, + +center_y); + gimp_matrix3_transform_point (&matrix, + coords->x, + coords->y, + &new_x, + &new_y); + coords->x = new_x; + coords->y = new_y; + sym->strokes = g_list_prepend (sym->strokes, coords); + } + + sym->strokes = g_list_reverse (sym->strokes); + + g_signal_emit_by_name (sym, "strokes-updated", sym->image); +} + +static void +gimp_mandala_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) +{ + GimpMandala *mandala = GIMP_MANDALA (sym); + gdouble slice_angle; + + if (mandala->disable_transformation) + return; + + slice_angle = 360.0 / mandala->size; + + if (mandala->enable_reflection && stroke % 2 == 1) + { + /* Find out in which slice the user is currently drawing. */ + gdouble origin_angle = gimp_rad_to_deg (atan2 (sym->origin->y - mandala->center_y, + sym->origin->x - mandala->center_x)); + gint slice_no = (int) floor(origin_angle/slice_angle); + + /* Angle where the middle of that slice is. */ + gdouble mid_slice_angle = slice_no * slice_angle + slice_angle / 2.0; + + *angle = 180.0 - (-2 * mid_slice_angle + stroke * slice_angle) ; + *reflect = TRUE; + } + else + { + *angle = stroke * slice_angle; + } +} + +static void +gimp_mandala_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym) +{ + if (previous_width != gimp_image_get_width (image) || + previous_height != gimp_image_get_height (image)) + { + g_signal_emit_by_name (sym, "gui-param-changed", sym->image); + } +} diff --git a/app/core/gimpsymmetry-mandala.h b/app/core/gimpsymmetry-mandala.h new file mode 100644 index 0000000..0978a40 --- /dev/null +++ b/app/core/gimpsymmetry-mandala.h @@ -0,0 +1,61 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-mandala.h + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_MANDALA_H__ +#define __GIMP_MANDALA_H__ + + +#include "gimpsymmetry.h" + + +#define GIMP_TYPE_MANDALA (gimp_mandala_get_type ()) +#define GIMP_MANDALA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MANDALA, GimpMandala)) +#define GIMP_MANDALA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MANDALA, GimpMandalaClass)) +#define GIMP_IS_MANDALA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MANDALA)) +#define GIMP_IS_MANDALA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MANDALA)) +#define GIMP_MANDALA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MANDALA, GimpMandalaClass)) + + +typedef struct _GimpMandalaClass GimpMandalaClass; + +struct _GimpMandala +{ + GimpSymmetry parent_instance; + + gdouble center_x; + gdouble center_y; + gint size; + gboolean disable_transformation; + gboolean enable_reflection; + + GimpGuide *horizontal_guide; + GimpGuide *vertical_guide; +}; + +struct _GimpMandalaClass +{ + GimpSymmetryClass parent_class; +}; + + +GType gimp_mandala_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_MANDALA_H__ */ diff --git a/app/core/gimpsymmetry-mirror.c b/app/core/gimpsymmetry-mirror.c new file mode 100644 index 0000000..66a0eaa --- /dev/null +++ b/app/core/gimpsymmetry-mirror.c @@ -0,0 +1,738 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-mirror.c + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimp-cairo.h" +#include "gimpbrush.h" +#include "gimpguide.h" +#include "gimpimage.h" +#include "gimpimage-guides.h" +#include "gimpimage-symmetry.h" +#include "gimpitem.h" +#include "gimpsymmetry-mirror.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_HORIZONTAL_SYMMETRY, + PROP_VERTICAL_SYMMETRY, + PROP_POINT_SYMMETRY, + PROP_DISABLE_TRANSFORMATION, + PROP_MIRROR_POSITION_X, + PROP_MIRROR_POSITION_Y +}; + + +/* Local function prototypes */ + +static void gimp_mirror_constructed (GObject *object); +static void gimp_mirror_finalize (GObject *object); +static void gimp_mirror_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mirror_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_mirror_update_strokes (GimpSymmetry *mirror, + GimpDrawable *drawable, + GimpCoords *origin); +static void gimp_mirror_get_transform (GimpSymmetry *mirror, + gint stroke, + gdouble *angle, + gboolean *reflect); +static void gimp_mirror_reset (GimpMirror *mirror); +static void gimp_mirror_add_guide (GimpMirror *mirror, + GimpOrientationType orientation); +static void gimp_mirror_remove_guide (GimpMirror *mirror, + GimpOrientationType orientation); +static void gimp_mirror_guide_removed_cb (GObject *object, + GimpMirror *mirror); +static void gimp_mirror_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMirror *mirror); +static void gimp_mirror_active_changed (GimpSymmetry *sym); +static void gimp_mirror_set_horizontal_symmetry (GimpMirror *mirror, + gboolean active); +static void gimp_mirror_set_vertical_symmetry (GimpMirror *mirror, + gboolean active); +static void gimp_mirror_set_point_symmetry (GimpMirror *mirror, + gboolean active); + +static void gimp_mirror_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym); + +G_DEFINE_TYPE (GimpMirror, gimp_mirror, GIMP_TYPE_SYMMETRY) + +#define parent_class gimp_mirror_parent_class + + +static void +gimp_mirror_class_init (GimpMirrorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpSymmetryClass *symmetry_class = GIMP_SYMMETRY_CLASS (klass); + GParamSpec *pspec; + + object_class->constructed = gimp_mirror_constructed; + object_class->finalize = gimp_mirror_finalize; + object_class->set_property = gimp_mirror_set_property; + object_class->get_property = gimp_mirror_get_property; + + symmetry_class->label = _("Mirror"); + symmetry_class->update_strokes = gimp_mirror_update_strokes; + symmetry_class->get_transform = gimp_mirror_get_transform; + symmetry_class->active_changed = gimp_mirror_active_changed; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_HORIZONTAL_SYMMETRY, + "horizontal-symmetry", + _("Horizontal Symmetry"), + _("Reflect the initial stroke across a horizontal axis"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_VERTICAL_SYMMETRY, + "vertical-symmetry", + _("Vertical Symmetry"), + _("Reflect the initial stroke across a vertical axis"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_POINT_SYMMETRY, + "point-symmetry", + _("Central Symmetry"), + _("Invert the initial stroke through a point"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISABLE_TRANSFORMATION, + "disable-transformation", + _("Disable brush transform"), + _("Disable brush reflection"), + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_MIRROR_POSITION_X, + "mirror-position-x", + _("Vertical axis position"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "mirror-position-x"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "x"); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_MIRROR_POSITION_Y, + "mirror-position-y", + _("Horizontal axis position"), + NULL, + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "mirror-position-y"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-coordinate"); + gegl_param_spec_set_property_key (pspec, "axis", "y"); +} + +static void +gimp_mirror_init (GimpMirror *mirror) +{ +} + +static void +gimp_mirror_constructed (GObject *object) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + g_signal_connect_object (sym->image, "size-changed-detailed", + G_CALLBACK (gimp_mirror_image_size_changed_cb), + sym, 0); +} + +static void +gimp_mirror_finalize (GObject *object) +{ + GimpMirror *mirror = GIMP_MIRROR (object); + + g_clear_object (&mirror->horizontal_guide); + g_clear_object (&mirror->vertical_guide); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_mirror_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpMirror *mirror = GIMP_MIRROR (object); + GimpImage *image = GIMP_SYMMETRY (mirror)->image; + + switch (property_id) + { + case PROP_HORIZONTAL_SYMMETRY: + gimp_mirror_set_horizontal_symmetry (mirror, + g_value_get_boolean (value)); + break; + + case PROP_VERTICAL_SYMMETRY: + gimp_mirror_set_vertical_symmetry (mirror, + g_value_get_boolean (value)); + break; + + case PROP_POINT_SYMMETRY: + gimp_mirror_set_point_symmetry (mirror, + g_value_get_boolean (value)); + break; + + case PROP_DISABLE_TRANSFORMATION: + mirror->disable_transformation = g_value_get_boolean (value); + break; + + case PROP_MIRROR_POSITION_X: + if (g_value_get_double (value) >= 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_width (image)) + { + mirror->mirror_position_x = g_value_get_double (value); + + if (mirror->vertical_guide) + { + g_signal_handlers_block_by_func (mirror->vertical_guide, + gimp_mirror_guide_position_cb, + mirror); + gimp_image_move_guide (image, mirror->vertical_guide, + mirror->mirror_position_x, + FALSE); + g_signal_handlers_unblock_by_func (mirror->vertical_guide, + gimp_mirror_guide_position_cb, + mirror); + } + } + break; + + case PROP_MIRROR_POSITION_Y: + if (g_value_get_double (value) >= 0.0 && + g_value_get_double (value) < (gdouble) gimp_image_get_height (image)) + { + mirror->mirror_position_y = g_value_get_double (value); + + if (mirror->horizontal_guide) + { + g_signal_handlers_block_by_func (mirror->horizontal_guide, + gimp_mirror_guide_position_cb, + mirror); + gimp_image_move_guide (image, mirror->horizontal_guide, + mirror->mirror_position_y, + FALSE); + g_signal_handlers_unblock_by_func (mirror->horizontal_guide, + gimp_mirror_guide_position_cb, + mirror); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mirror_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpMirror *mirror = GIMP_MIRROR (object); + + switch (property_id) + { + case PROP_HORIZONTAL_SYMMETRY: + g_value_set_boolean (value, mirror->horizontal_mirror); + break; + case PROP_VERTICAL_SYMMETRY: + g_value_set_boolean (value, mirror->vertical_mirror); + break; + case PROP_POINT_SYMMETRY: + g_value_set_boolean (value, mirror->point_symmetry); + break; + case PROP_DISABLE_TRANSFORMATION: + g_value_set_boolean (value, mirror->disable_transformation); + break; + case PROP_MIRROR_POSITION_X: + g_value_set_double (value, mirror->mirror_position_x); + break; + case PROP_MIRROR_POSITION_Y: + g_value_set_double (value, mirror->mirror_position_y); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mirror_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + GimpMirror *mirror = GIMP_MIRROR (sym); + GList *strokes = NULL; + GimpCoords *coords; + gdouble mirror_position_x, mirror_position_y; + gint offset_x, offset_y; + + gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); + + mirror_position_x = mirror->mirror_position_x - offset_x; + mirror_position_y = mirror->mirror_position_y - offset_y; + + g_list_free_full (sym->strokes, g_free); + strokes = g_list_prepend (strokes, + g_memdup (origin, sizeof (GimpCoords))); + + if (mirror->horizontal_mirror) + { + coords = g_memdup (origin, sizeof (GimpCoords)); + coords->y = 2.0 * mirror_position_y - origin->y; + strokes = g_list_prepend (strokes, coords); + } + + if (mirror->vertical_mirror) + { + coords = g_memdup (origin, sizeof (GimpCoords)); + coords->x = 2.0 * mirror_position_x - origin->x; + strokes = g_list_prepend (strokes, coords); + } + + if (mirror->point_symmetry) + { + coords = g_memdup (origin, sizeof (GimpCoords)); + coords->x = 2.0 * mirror_position_x - origin->x; + coords->y = 2.0 * mirror_position_y - origin->y; + strokes = g_list_prepend (strokes, coords); + } + + sym->strokes = g_list_reverse (strokes); + + g_signal_emit_by_name (sym, "strokes-updated", sym->image); +} + +static void +gimp_mirror_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) +{ + GimpMirror *mirror = GIMP_MIRROR (sym); + + if (mirror->disable_transformation) + return; + + if (! mirror->horizontal_mirror && stroke >= 1) + stroke++; + + if (! mirror->vertical_mirror && stroke >= 2) + stroke++; + + switch (stroke) + { + /* original */ + case 0: + break; + + /* horizontal */ + case 1: + *angle = 180.0; + *reflect = TRUE; + break; + + /* vertical */ + case 2: + *reflect = TRUE; + break; + + /* central */ + case 3: + *angle = 180.0; + break; + + default: + g_return_if_reached (); + } +} + +static void +gimp_mirror_reset (GimpMirror *mirror) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mirror); + + if (sym->origin) + { + gimp_symmetry_set_origin (sym, sym->drawable, + sym->origin); + } +} + +static void +gimp_mirror_add_guide (GimpMirror *mirror, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mirror); + GimpImage *image; + Gimp *gimp; + GimpGuide *guide; + gdouble position; + + image = sym->image; + gimp = image->gimp; + + guide = gimp_guide_custom_new (orientation, + gimp->next_guide_ID++, + GIMP_GUIDE_STYLE_MIRROR); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + { + /* Mirror guide position at first activation is at canvas middle. */ + if (mirror->mirror_position_y < 1.0) + position = gimp_image_get_height (image) / 2.0; + else + position = mirror->mirror_position_y; + + g_object_set (mirror, + "mirror-position-y", position, + NULL); + + mirror->horizontal_guide = guide; + } + else + { + /* Mirror guide position at first activation is at canvas middle. */ + if (mirror->mirror_position_x < 1.0) + position = gimp_image_get_width (image) / 2.0; + else + position = mirror->mirror_position_x; + + g_object_set (mirror, + "mirror-position-x", position, + NULL); + + mirror->vertical_guide = guide; + } + + g_signal_connect (guide, "removed", + G_CALLBACK (gimp_mirror_guide_removed_cb), + mirror); + + gimp_image_add_guide (image, guide, (gint) position); + + g_signal_connect (guide, "notify::position", + G_CALLBACK (gimp_mirror_guide_position_cb), + mirror); +} + +static void +gimp_mirror_remove_guide (GimpMirror *mirror, + GimpOrientationType orientation) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (mirror); + GimpImage *image; + GimpGuide *guide; + + image = sym->image; + guide = (orientation == GIMP_ORIENTATION_HORIZONTAL) ? + mirror->horizontal_guide : mirror->vertical_guide; + + /* The guide may have already been removed, for instance from GUI. */ + if (guide) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (guide), + gimp_mirror_guide_removed_cb, + mirror); + g_signal_handlers_disconnect_by_func (G_OBJECT (guide), + gimp_mirror_guide_position_cb, + mirror); + + gimp_image_remove_guide (image, guide, FALSE); + g_object_unref (guide); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + mirror->horizontal_guide = NULL; + else + mirror->vertical_guide = NULL; + } +} + +static void +gimp_mirror_guide_removed_cb (GObject *object, + GimpMirror *mirror) +{ + GimpSymmetry *symmetry = GIMP_SYMMETRY (mirror); + + g_signal_handlers_disconnect_by_func (object, + gimp_mirror_guide_removed_cb, + mirror); + g_signal_handlers_disconnect_by_func (object, + gimp_mirror_guide_position_cb, + mirror); + + if (GIMP_GUIDE (object) == mirror->horizontal_guide) + { + g_object_unref (mirror->horizontal_guide); + mirror->horizontal_guide = NULL; + + g_object_set (mirror, + "horizontal-symmetry", FALSE, + NULL); + g_object_set (mirror, + "point-symmetry", FALSE, + NULL); + g_object_set (mirror, + "mirror-position-y", 0.0, + NULL); + + if (mirror->vertical_guide && + ! mirror->vertical_mirror) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->vertical_guide), + gimp_mirror_guide_removed_cb, + mirror); + g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->vertical_guide), + gimp_mirror_guide_position_cb, + mirror); + + gimp_image_remove_guide (symmetry->image, + mirror->vertical_guide, + FALSE); + g_clear_object (&mirror->vertical_guide); + } + } + else if (GIMP_GUIDE (object) == mirror->vertical_guide) + { + g_object_unref (mirror->vertical_guide); + mirror->vertical_guide = NULL; + + g_object_set (mirror, + "vertical-symmetry", FALSE, + NULL); + g_object_set (mirror, + "point-symmetry", FALSE, + NULL); + g_object_set (mirror, + "mirror-position-x", 0.0, + NULL); + + if (mirror->horizontal_guide && + ! mirror->horizontal_mirror) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->horizontal_guide), + gimp_mirror_guide_removed_cb, + mirror); + g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->horizontal_guide), + gimp_mirror_guide_position_cb, + mirror); + + gimp_image_remove_guide (symmetry->image, + mirror->horizontal_guide, + FALSE); + g_clear_object (&mirror->horizontal_guide); + } + } + + if (mirror->horizontal_guide == NULL && + mirror->vertical_guide == NULL) + { + gimp_image_symmetry_remove (symmetry->image, + GIMP_SYMMETRY (mirror)); + } + else + { + gimp_mirror_reset (mirror); + g_signal_emit_by_name (mirror, "gui-param-changed", + GIMP_SYMMETRY (mirror)->image); + } +} + +static void +gimp_mirror_guide_position_cb (GObject *object, + GParamSpec *pspec, + GimpMirror *mirror) +{ + GimpGuide *guide = GIMP_GUIDE (object); + + if (guide == mirror->horizontal_guide) + { + g_object_set (mirror, + "mirror-position-y", (gdouble) gimp_guide_get_position (guide), + NULL); + } + else if (guide == mirror->vertical_guide) + { + g_object_set (mirror, + "mirror-position-x", (gdouble) gimp_guide_get_position (guide), + NULL); + } +} + +static void +gimp_mirror_active_changed (GimpSymmetry *sym) +{ + GimpMirror *mirror = GIMP_MIRROR (sym); + + if (sym->active) + { + if ((mirror->horizontal_mirror || mirror->point_symmetry) && + ! mirror->horizontal_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + + if ((mirror->vertical_mirror || mirror->point_symmetry) && + ! mirror->vertical_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } + else + { + if (mirror->horizontal_guide) + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + + if (mirror->vertical_guide) + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } +} + +static void +gimp_mirror_set_horizontal_symmetry (GimpMirror *mirror, + gboolean active) +{ + if (active == mirror->horizontal_mirror) + return; + + mirror->horizontal_mirror = active; + + if (active) + { + if (! mirror->horizontal_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + } + else if (! mirror->point_symmetry) + { + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + } + + gimp_mirror_reset (mirror); +} + +static void +gimp_mirror_set_vertical_symmetry (GimpMirror *mirror, + gboolean active) +{ + if (active == mirror->vertical_mirror) + return; + + mirror->vertical_mirror = active; + + if (active) + { + if (! mirror->vertical_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } + else if (! mirror->point_symmetry) + { + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } + + gimp_mirror_reset (mirror); +} + +static void +gimp_mirror_set_point_symmetry (GimpMirror *mirror, + gboolean active) +{ + if (active == mirror->point_symmetry) + return; + + mirror->point_symmetry = active; + + if (active) + { + /* Show the horizontal guide unless already shown */ + if (! mirror->horizontal_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + + /* Show the vertical guide unless already shown */ + if (! mirror->vertical_guide) + gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } + else + { + /* Remove the horizontal guide unless needed by horizontal mirror */ + if (! mirror->horizontal_mirror) + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL); + + /* Remove the vertical guide unless needed by vertical mirror */ + if (! mirror->vertical_mirror) + gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL); + } + + gimp_mirror_reset (mirror); +} + +static void +gimp_mirror_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym) +{ + if (previous_width != gimp_image_get_width (image) || + previous_height != gimp_image_get_height (image)) + { + g_signal_emit_by_name (sym, "gui-param-changed", sym->image); + } +} diff --git a/app/core/gimpsymmetry-mirror.h b/app/core/gimpsymmetry-mirror.h new file mode 100644 index 0000000..f9ac26a --- /dev/null +++ b/app/core/gimpsymmetry-mirror.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-mirror.h + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_MIRROR_H__ +#define __GIMP_MIRROR_H__ + + +#include "gimpsymmetry.h" + + +#define GIMP_TYPE_MIRROR (gimp_mirror_get_type ()) +#define GIMP_MIRROR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MIRROR, GimpMirror)) +#define GIMP_MIRROR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MIRROR, GimpMirrorClass)) +#define GIMP_IS_MIRROR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MIRROR)) +#define GIMP_IS_MIRROR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MIRROR)) +#define GIMP_MIRROR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MIRROR, GimpMirrorClass)) + + +typedef struct _GimpMirrorClass GimpMirrorClass; + +struct _GimpMirror +{ + GimpSymmetry parent_instance; + + gboolean horizontal_mirror; + gboolean vertical_mirror; + gboolean point_symmetry; + gboolean disable_transformation; + + gdouble mirror_position_y; + gdouble mirror_position_x; + GimpGuide *horizontal_guide; + GimpGuide *vertical_guide; +}; + +struct _GimpMirrorClass +{ + GimpSymmetryClass parent_class; +}; + + +GType gimp_mirror_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_MIRROR_H__ */ diff --git a/app/core/gimpsymmetry-tiling.c b/app/core/gimpsymmetry-tiling.c new file mode 100644 index 0000000..555f584 --- /dev/null +++ b/app/core/gimpsymmetry-tiling.c @@ -0,0 +1,442 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-tiling.c + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpdrawable.h" +#include "gimpimage.h" +#include "gimpitem.h" +#include "gimpsymmetry-tiling.h" + +#include "gimp-intl.h" + + +/* Using same epsilon as in GLIB. */ +#define G_DOUBLE_EPSILON (1e-90) + +enum +{ + PROP_0, + + PROP_INTERVAL_X, + PROP_INTERVAL_Y, + PROP_SHIFT, + PROP_MAX_X, + PROP_MAX_Y +}; + + +/* Local function prototypes */ + +static void gimp_tiling_constructed (GObject *object); +static void gimp_tiling_finalize (GObject *object); +static void gimp_tiling_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tiling_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tiling_update_strokes (GimpSymmetry *tiling, + GimpDrawable *drawable, + GimpCoords *origin); +static void gimp_tiling_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym); + + +G_DEFINE_TYPE (GimpTiling, gimp_tiling, GIMP_TYPE_SYMMETRY) + +#define parent_class gimp_tiling_parent_class + + +static void +gimp_tiling_class_init (GimpTilingClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpSymmetryClass *symmetry_class = GIMP_SYMMETRY_CLASS (klass); + GParamSpec *pspec; + + object_class->constructed = gimp_tiling_constructed; + object_class->finalize = gimp_tiling_finalize; + object_class->set_property = gimp_tiling_set_property; + object_class->get_property = gimp_tiling_get_property; + + symmetry_class->label = _("Tiling"); + symmetry_class->update_strokes = gimp_tiling_update_strokes; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_INTERVAL_X, + "interval-x", + _("Interval X"), + _("Interval on the X axis (pixels)"), + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "interval-x"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-distance"); + gegl_param_spec_set_property_key (pspec, "axis", "x"); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_INTERVAL_Y, + "interval-y", + _("Interval Y"), + _("Interval on the Y axis (pixels)"), + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "interval-y"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-distance"); + gegl_param_spec_set_property_key (pspec, "axis", "y"); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SHIFT, + "shift", + _("Shift"), + _("X-shift between lines (pixels)"), + 0.0, G_MAXDOUBLE, 0.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + pspec = g_object_class_find_property (object_class, "shift"); + gegl_param_spec_set_property_key (pspec, "unit", "pixel-distance"); + gegl_param_spec_set_property_key (pspec, "axis", "x"); + + GIMP_CONFIG_PROP_INT (object_class, PROP_MAX_X, + "max-x", + _("Max strokes X"), + _("Maximum number of strokes on the X axis"), + 0, 100, 0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); + + GIMP_CONFIG_PROP_INT (object_class, PROP_MAX_Y, + "max-y", + _("Max strokes Y"), + _("Maximum number of strokes on the Y axis"), + 0, 100, 0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_SYMMETRY_PARAM_GUI); +} + +static void +gimp_tiling_init (GimpTiling *tiling) +{ +} + +static void +gimp_tiling_constructed (GObject *object) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + GimpTiling *tiling = GIMP_TILING (object); + + g_signal_connect_object (sym->image, "size-changed-detailed", + G_CALLBACK (gimp_tiling_image_size_changed_cb), + sym, 0); + + /* Set reasonable defaults. */ + tiling->interval_x = gimp_image_get_width (sym->image) / 2; + tiling->interval_y = gimp_image_get_height (sym->image) / 2; +} + +static void +gimp_tiling_finalize (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tiling_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpTiling *tiling = GIMP_TILING (object); + GimpSymmetry *sym = GIMP_SYMMETRY (tiling); + + switch (property_id) + { + case PROP_INTERVAL_X: + if (sym->image) + { + gdouble new_x = g_value_get_double (value); + + if (new_x < gimp_image_get_width (sym->image)) + { + tiling->interval_x = new_x; + + if (tiling->interval_x <= tiling->shift + G_DOUBLE_EPSILON) + { + GValue val = G_VALUE_INIT; + + g_value_init (&val, G_TYPE_DOUBLE); + g_value_set_double (&val, 0.0); + g_object_set_property (G_OBJECT (object), "shift", &val); + } + if (sym->drawable) + gimp_tiling_update_strokes (sym, sym->drawable, sym->origin); + } + } + break; + + case PROP_INTERVAL_Y: + { + gdouble new_y = g_value_get_double (value); + + if (new_y < gimp_image_get_height (sym->image)) + { + tiling->interval_y = new_y; + + if (tiling->interval_y <= G_DOUBLE_EPSILON) + { + GValue val = G_VALUE_INIT; + + g_value_init (&val, G_TYPE_DOUBLE); + g_value_set_double (&val, 0.0); + g_object_set_property (G_OBJECT (object), "shift", &val); + } + if (sym->drawable) + gimp_tiling_update_strokes (sym, sym->drawable, sym->origin); + } + } + break; + + case PROP_SHIFT: + { + gdouble new_shift = g_value_get_double (value); + + if (new_shift == 0.0 || + (tiling->interval_y != 0.0 && new_shift < tiling->interval_x)) + { + tiling->shift = new_shift; + if (sym->drawable) + gimp_tiling_update_strokes (sym, sym->drawable, sym->origin); + } + } + break; + + case PROP_MAX_X: + tiling->max_x = g_value_get_int (value); + if (sym->drawable) + gimp_tiling_update_strokes (sym, sym->drawable, sym->origin); + break; + + case PROP_MAX_Y: + tiling->max_y = g_value_get_int (value); + if (sym->drawable) + gimp_tiling_update_strokes (sym, sym->drawable, sym->origin); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tiling_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpTiling *tiling = GIMP_TILING (object); + + switch (property_id) + { + case PROP_INTERVAL_X: + g_value_set_double (value, tiling->interval_x); + break; + case PROP_INTERVAL_Y: + g_value_set_double (value, tiling->interval_y); + break; + case PROP_SHIFT: + g_value_set_double (value, tiling->shift); + break; + case PROP_MAX_X: + g_value_set_int (value, tiling->max_x); + break; + case PROP_MAX_Y: + g_value_set_int (value, tiling->max_y); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tiling_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + GimpTiling *tiling = GIMP_TILING (sym); + GList *strokes = NULL; + GimpCoords *coords; + gdouble width; + gdouble height; + gdouble startx = origin->x; + gdouble starty = origin->y; + gdouble x; + gdouble y; + gint x_count; + gint y_count; + + g_list_free_full (sym->strokes, g_free); + sym->strokes = NULL; + + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + + if (sym->stateful) + { + /* While I can compute exactly the right number of strokes to + * paint on-canvas for stateless tools, stateful tools need to + * always have the same number and order of strokes. For this + * reason, I compute strokes to fill 2 times the width and height. + * This makes the symmetry less efficient with stateful tools, but + * also weird behavior may happen if you decide to paint out of + * canvas and expect tiling to work in-canvas since it won't + * actually be infinite (as no new strokes can be added while + * painting since we are stateful). + */ + gint i, j; + + if (tiling->interval_x < 1.0) + { + x_count = 1; + } + else if (tiling->max_x == 0) + { + x_count = (gint) ceil (width / tiling->interval_x); + startx -= tiling->interval_x * (gdouble) x_count; + x_count = 2 * x_count + 1; + } + else + { + x_count = tiling->max_x; + } + + if (tiling->interval_y < 1.0) + { + y_count = 1; + } + else if (tiling->max_y == 0) + { + y_count = (gint) ceil (height / tiling->interval_y); + starty -= tiling->interval_y * (gdouble) y_count; + y_count = 2 * y_count + 1; + } + else + { + y_count = tiling->max_y; + } + + for (i = 0, x = startx; i < x_count; i++) + { + for (j = 0, y = starty; j < y_count; j++) + { + coords = g_memdup (origin, sizeof (GimpCoords)); + coords->x = x; + coords->y = y; + strokes = g_list_prepend (strokes, coords); + + y += tiling->interval_y; + } + x += tiling->interval_x; + } + } + else + { + if (origin->x > 0 && tiling->max_x == 0 && tiling->interval_x >= 1.0) + startx = fmod (origin->x, tiling->interval_x) - tiling->interval_x; + + if (origin->y > 0 && tiling->max_y == 0 && tiling->interval_y >= 1.0) + { + starty = fmod (origin->y, tiling->interval_y) - tiling->interval_y; + + if (tiling->shift > 0.0) + startx -= tiling->shift * floor (origin->y / tiling->interval_y + 1); + } + + for (y_count = 0, y = starty; y < height + tiling->interval_y; + y_count++, y += tiling->interval_y) + { + if (tiling->max_y && y_count >= tiling->max_y) + break; + + for (x_count = 0, x = startx; x < width + tiling->interval_x; + x_count++, x += tiling->interval_x) + { + if (tiling->max_x && x_count >= tiling->max_x) + break; + + coords = g_memdup (origin, sizeof (GimpCoords)); + coords->x = x; + coords->y = y; + strokes = g_list_prepend (strokes, coords); + + if (tiling->interval_x < 1.0) + break; + } + + if (tiling->max_x || startx + tiling->shift <= 0.0) + startx = startx + tiling->shift; + else + startx = startx - tiling->interval_x + tiling->shift; + + if (tiling->interval_y < 1.0) + break; + } + } + + sym->strokes = strokes; + + g_signal_emit_by_name (sym, "strokes-updated", sym->image); +} + +static void +gimp_tiling_image_size_changed_cb (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpSymmetry *sym) +{ + if (previous_width != gimp_image_get_width (image) || + previous_height != gimp_image_get_height (image)) + { + g_signal_emit_by_name (sym, "gui-param-changed", sym->image); + } +} diff --git a/app/core/gimpsymmetry-tiling.h b/app/core/gimpsymmetry-tiling.h new file mode 100644 index 0000000..7fbff71 --- /dev/null +++ b/app/core/gimpsymmetry-tiling.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry-tiling.h + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TILING_H__ +#define __GIMP_TILING_H__ + + +#include "gimpsymmetry.h" + + +#define GIMP_TYPE_TILING (gimp_tiling_get_type ()) +#define GIMP_TILING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TILING, GimpTiling)) +#define GIMP_TILING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TILING, GimpTilingClass)) +#define GIMP_IS_TILING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TILING)) +#define GIMP_IS_TILING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TILING)) +#define GIMP_TILING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TILING, GimpTilingClass)) + + +typedef struct _GimpTilingClass GimpTilingClass; + +struct _GimpTiling +{ + GimpSymmetry parent_instance; + + gdouble interval_x; + gdouble interval_y; + gdouble shift; + gint max_x; + gint max_y; +}; + +struct _GimpTilingClass +{ + GimpSymmetryClass parent_class; +}; + + +GType gimp_tiling_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_TILING_H__ */ diff --git a/app/core/gimpsymmetry.c b/app/core/gimpsymmetry.c new file mode 100644 index 0000000..e0bc23b --- /dev/null +++ b/app/core/gimpsymmetry.c @@ -0,0 +1,591 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry.c + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "gegl/gimp-gegl-nodes.h" + +#include "gimpdrawable.h" +#include "gimpimage.h" +#include "gimpimage-symmetry.h" +#include "gimpitem.h" +#include "gimpsymmetry.h" + +#include "gimp-intl.h" + + +enum +{ + STROKES_UPDATED, + GUI_PARAM_CHANGED, + ACTIVE_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_IMAGE, + PROP_ACTIVE, + PROP_VERSION, +}; + + +/* Local function prototypes */ + +static void gimp_symmetry_finalize (GObject *object); +static void gimp_symmetry_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_symmetry_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_symmetry_real_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin); +static void gimp_symmetry_real_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect); +static gboolean gimp_symmetry_real_update_version (GimpSymmetry *sym); + + +G_DEFINE_TYPE_WITH_CODE (GimpSymmetry, gimp_symmetry, GIMP_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +#define parent_class gimp_symmetry_parent_class + +static guint gimp_symmetry_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_symmetry_class_init (GimpSymmetryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + /* This signal should likely be emitted at the end of + * update_strokes() if stroke coordinates were changed. + */ + gimp_symmetry_signals[STROKES_UPDATED] = + g_signal_new ("strokes-updated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, + 1, GIMP_TYPE_IMAGE); + + /* This signal should be emitted when you request a change in the + * settings UI. For instance adding some settings (therefore having + * a dynamic UI), or changing scale min/max extremes, etc. + */ + gimp_symmetry_signals[GUI_PARAM_CHANGED] = + g_signal_new ("gui-param-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, + 1, GIMP_TYPE_IMAGE); + + gimp_symmetry_signals[ACTIVE_CHANGED] = + g_signal_new ("active-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpSymmetryClass, active_changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_symmetry_finalize; + object_class->set_property = gimp_symmetry_set_property; + object_class->get_property = gimp_symmetry_get_property; + + klass->label = _("None"); + klass->update_strokes = gimp_symmetry_real_update_strokes; + klass->get_transform = gimp_symmetry_real_get_transform; + klass->active_changed = NULL; + klass->update_version = gimp_symmetry_real_update_version; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", + NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ACTIVE, + "active", + _("Active"), + _("Activate symmetry painting"), + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_VERSION, + "version", + "Symmetry version", + "Version of the symmetry object", + -1, G_MAXINT, 0, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_symmetry_init (GimpSymmetry *sym) +{ +} + +static void +gimp_symmetry_finalize (GObject *object) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + gimp_symmetry_clear_origin (sym); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_symmetry_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + switch (property_id) + { + case PROP_IMAGE: + sym->image = g_value_get_object (value); + break; + case PROP_ACTIVE: + sym->active = g_value_get_boolean (value); + g_signal_emit (sym, gimp_symmetry_signals[ACTIVE_CHANGED], 0, + sym->active); + break; + case PROP_VERSION: + sym->version = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_symmetry_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpSymmetry *sym = GIMP_SYMMETRY (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, sym->image); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, sym->active); + break; + case PROP_VERSION: + g_value_set_int (value, sym->version); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_symmetry_real_update_strokes (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + /* The basic symmetry just uses the origin as is. */ + sym->strokes = g_list_prepend (sym->strokes, + g_memdup (origin, sizeof (GimpCoords))); +} + +static void +gimp_symmetry_real_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) +{ + /* The basic symmetry does nothing, since no transformation of the + * brush painting happen. */ +} + +static gboolean +gimp_symmetry_real_update_version (GimpSymmetry *symmetry) +{ + /* Currently all symmetries are at version 0. So all this check has to + * do is verify that we are at version 0. + * If one of the child symmetry bumps its version, it will have to + * override the update_version() virtual function and do any necessary + * update there (for instance new properties, modified properties, or + * whatnot). + */ + gint version; + + g_object_get (symmetry, + "version", &version, + NULL); + + return (version == 0); +} + +/***** Public Functions *****/ + +/** + * gimp_symmetry_set_stateful: + * @sym: the #GimpSymmetry + * @stateful: whether the symmetry should be stateful or stateless. + * + * By default, symmetry is made stateless, which means in particular + * that the size of points can change from one stroke to the next, and + * in particular you cannot map the coordinates from a stroke to the + * next. I.e. stroke N at time T+1 is not necessarily the continuation + * of stroke N at time T. + * To obtain corresponding strokes, stateful tools, such as MyPaint + * brushes or the ink tool, need to run this function. They should reset + * to stateless behavior once finished painting. + * + * One of the first consequence of being stateful is that the number of + * strokes cannot be changed, so more strokes than possible on canvas + * may be computed, and oppositely it will be possible to end up in + * cases with missing strokes (e.g. a tiling, theoretically infinite, + * won't be for the ink tool if one draws too far out of canvas). + **/ +void +gimp_symmetry_set_stateful (GimpSymmetry *symmetry, + gboolean stateful) +{ + symmetry->stateful = stateful; +} + +/** + * gimp_symmetry_set_origin: + * @sym: the #GimpSymmetry + * @drawable: the #GimpDrawable where painting will happen + * @origin: new base coordinates. + * + * Set the symmetry to new origin coordinates and drawable. + **/ +void +gimp_symmetry_set_origin (GimpSymmetry *sym, + GimpDrawable *drawable, + GimpCoords *origin) +{ + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (gimp_item_get_image (GIMP_ITEM (drawable)) == sym->image); + + if (drawable != sym->drawable) + { + if (sym->drawable) + g_object_unref (sym->drawable); + sym->drawable = g_object_ref (drawable); + } + + if (origin != sym->origin) + { + g_free (sym->origin); + sym->origin = g_memdup (origin, sizeof (GimpCoords)); + } + + g_list_free_full (sym->strokes, g_free); + sym->strokes = NULL; + + GIMP_SYMMETRY_GET_CLASS (sym)->update_strokes (sym, drawable, origin); +} + +/** + * gimp_symmetry_clear_origin: + * @sym: the #GimpSymmetry + * + * Clear the symmetry's origin coordinates and drawable. + **/ +void +gimp_symmetry_clear_origin (GimpSymmetry *sym) +{ + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + + g_clear_object (&sym->drawable); + + g_clear_pointer (&sym->origin, g_free); + + g_list_free_full (sym->strokes, g_free); + sym->strokes = NULL; +} + +/** + * gimp_symmetry_get_origin: + * @sym: the #GimpSymmetry + * + * Returns: the origin stroke coordinates. + * The returned value is owned by the #GimpSymmetry and must not be freed. + **/ +GimpCoords * +gimp_symmetry_get_origin (GimpSymmetry *sym) +{ + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL); + + return sym->origin; +} + +/** + * gimp_symmetry_get_size: + * @sym: the #GimpSymmetry + * + * Returns: the total number of strokes. + **/ +gint +gimp_symmetry_get_size (GimpSymmetry *sym) +{ + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), 0); + + return g_list_length (sym->strokes); +} + +/** + * gimp_symmetry_get_coords: + * @sym: the #GimpSymmetry + * @stroke: the stroke number + * + * Returns: the coordinates of the stroke number @stroke. + * The returned value is owned by the #GimpSymmetry and must not be freed. + **/ +GimpCoords * +gimp_symmetry_get_coords (GimpSymmetry *sym, + gint stroke) +{ + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL); + + return g_list_nth_data (sym->strokes, stroke); +} + +/** + * gimp_symmetry_get_transform: + * @sym: the #GimpSymmetry + * @stroke: the stroke number + * @angle: output pointer to the transformation rotation angle, + * in degrees (ccw) + * @reflect: output pointer to the transformation reflection flag + * + * Returns: the transformation to apply to the paint content for stroke + * number @stroke. The transformation is comprised of rotation, possibly + * followed by horizontal reflection, around the stroke coordinates. + **/ +void +gimp_symmetry_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) +{ + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + g_return_if_fail (angle != NULL); + g_return_if_fail (reflect != NULL); + + *angle = 0.0; + *reflect = FALSE; + + GIMP_SYMMETRY_GET_CLASS (sym)->get_transform (sym, + stroke, + angle, + reflect); +} + +/** + * gimp_symmetry_get_matrix: + * @sym: the #GimpSymmetry + * @stroke: the stroke number + * @matrix: output pointer to the transformation matrix + * + * Returns: the transformation matrix to apply to the paint content for stroke + * number @stroke. + **/ +void +gimp_symmetry_get_matrix (GimpSymmetry *sym, + gint stroke, + GimpMatrix3 *matrix) +{ + gdouble angle; + gboolean reflect; + + g_return_if_fail (GIMP_IS_SYMMETRY (sym)); + g_return_if_fail (matrix != NULL); + + gimp_symmetry_get_transform (sym, stroke, &angle, &reflect); + + gimp_matrix3_identity (matrix); + gimp_matrix3_rotate (matrix, -gimp_deg_to_rad (angle)); + if (reflect) + gimp_matrix3_scale (matrix, -1.0, 1.0); +} + +/** + * gimp_symmetry_get_operation: + * @sym: the #GimpSymmetry + * @stroke: the stroke number + * + * Returns: the transformation operation to apply to the paint content for + * stroke number @stroke, or NULL for the identity transformation. + * + * The returned #GeglNode should be freed by the caller. + **/ +GeglNode * +gimp_symmetry_get_operation (GimpSymmetry *sym, + gint stroke) +{ + GimpMatrix3 matrix; + + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL); + + gimp_symmetry_get_matrix (sym, stroke, &matrix); + + if (gimp_matrix3_is_identity (&matrix)) + return NULL; + + return gimp_gegl_create_transform_node (&matrix); +} + +/* + * gimp_symmetry_parasite_name: + * @type: the #GimpSymmetry's #GType + * + * Returns: a newly allocated string. + */ +gchar * +gimp_symmetry_parasite_name (GType type) +{ + return g_strconcat ("gimp-image-symmetry:", g_type_name (type), NULL); +} + +GimpParasite * +gimp_symmetry_to_parasite (const GimpSymmetry *sym) +{ + GimpParasite *parasite; + gchar *parasite_name; + gchar *str; + + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL); + + str = gimp_config_serialize_to_string (GIMP_CONFIG (sym), NULL); + g_return_val_if_fail (str != NULL, NULL); + + parasite_name = gimp_symmetry_parasite_name (G_TYPE_FROM_INSTANCE (sym)); + parasite = gimp_parasite_new (parasite_name, + GIMP_PARASITE_PERSISTENT, + strlen (str) + 1, str); + g_free (parasite_name); + g_free (str); + + return parasite; +} + +GimpSymmetry * +gimp_symmetry_from_parasite (const GimpParasite *parasite, + GimpImage *image, + GType type) +{ + GimpSymmetry *symmetry; + gchar *parasite_name; + const gchar *str; + GError *error = NULL; + + parasite_name = gimp_symmetry_parasite_name (type); + + g_return_val_if_fail (parasite != NULL, NULL); + g_return_val_if_fail (strcmp (gimp_parasite_name (parasite), + parasite_name) == 0, + NULL); + + str = gimp_parasite_data (parasite); + + if (! str) + { + g_warning ("Empty symmetry parasite \"%s\"", parasite_name); + + return NULL; + } + + symmetry = gimp_image_symmetry_new (image, type); + + g_object_set (symmetry, + "version", -1, + NULL); + + if (! gimp_config_deserialize_string (GIMP_CONFIG (symmetry), + str, + gimp_parasite_data_size (parasite), + NULL, + &error)) + { + g_printerr ("Failed to deserialize symmetry parasite: %s\n" + "\t- parasite name: %s\n\t- parasite data: %s\n", + error->message, parasite_name, str); + g_error_free (error); + + g_object_unref (symmetry); + symmetry = NULL; + } + g_free (parasite_name); + + if (symmetry) + { + gint version; + + g_object_get (symmetry, + "version", &version, + NULL); + if (version == -1) + { + /* If version has not been updated, let's assume this parasite was + * not representing symmetry settings. + */ + g_object_unref (symmetry); + symmetry = NULL; + } + else if (GIMP_SYMMETRY_GET_CLASS (symmetry)->update_version (symmetry) && + ! GIMP_SYMMETRY_GET_CLASS (symmetry)->update_version (symmetry)) + { + g_object_unref (symmetry); + symmetry = NULL; + } + } + + return symmetry; +} diff --git a/app/core/gimpsymmetry.h b/app/core/gimpsymmetry.h new file mode 100644 index 0000000..9474662 --- /dev/null +++ b/app/core/gimpsymmetry.h @@ -0,0 +1,106 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsymmetry.h + * Copyright (C) 2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_SYMMETRY_H__ +#define __GIMP_SYMMETRY_H__ + + +#include "gimpobject.h" + + +/* shift one more than GIMP_CONFIG_PARAM_IGNORE */ +#define GIMP_SYMMETRY_PARAM_GUI (1 << (6 + G_PARAM_USER_SHIFT)) + + +#define GIMP_TYPE_SYMMETRY (gimp_symmetry_get_type ()) +#define GIMP_SYMMETRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SYMMETRY, GimpSymmetry)) +#define GIMP_SYMMETRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SYMMETRY, GimpSymmetryClass)) +#define GIMP_IS_SYMMETRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SYMMETRY)) +#define GIMP_IS_SYMMETRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SYMMETRY)) +#define GIMP_SYMMETRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SYMMETRY, GimpSymmetryClass)) + + +typedef struct _GimpSymmetryClass GimpSymmetryClass; + +struct _GimpSymmetry +{ + GimpObject parent_instance; + + GimpImage *image; + GimpDrawable *drawable; + GimpCoords *origin; + gboolean active; + gint version; + + GList *strokes; + gboolean stateful; +}; + +struct _GimpSymmetryClass +{ + GimpObjectClass parent_class; + + const gchar * label; + + /* Virtual functions */ + void (* update_strokes) (GimpSymmetry *symmetry, + GimpDrawable *drawable, + GimpCoords *origin); + void (* get_transform) (GimpSymmetry *symmetry, + gint stroke, + gdouble *angle, + gboolean *reflect); + void (* active_changed) (GimpSymmetry *symmetry); + + gboolean (* update_version) (GimpSymmetry *symmetry); +}; + + +GType gimp_symmetry_get_type (void) G_GNUC_CONST; + +void gimp_symmetry_set_stateful (GimpSymmetry *symmetry, + gboolean stateful); +void gimp_symmetry_set_origin (GimpSymmetry *symmetry, + GimpDrawable *drawable, + GimpCoords *origin); +void gimp_symmetry_clear_origin (GimpSymmetry *symmetry); + +GimpCoords * gimp_symmetry_get_origin (GimpSymmetry *symmetry); +gint gimp_symmetry_get_size (GimpSymmetry *symmetry); +GimpCoords * gimp_symmetry_get_coords (GimpSymmetry *symmetry, + gint stroke); +void gimp_symmetry_get_transform (GimpSymmetry *symmetry, + gint stroke, + gdouble *angle, + gboolean *reflect); +void gimp_symmetry_get_matrix (GimpSymmetry *symmetry, + gint stroke, + GimpMatrix3 *matrix); +GeglNode * gimp_symmetry_get_operation (GimpSymmetry *symmetry, + gint stroke); + +gchar * gimp_symmetry_parasite_name (GType type); +GimpParasite * gimp_symmetry_to_parasite (const GimpSymmetry *symmetry); +GimpSymmetry * gimp_symmetry_from_parasite (const GimpParasite *parasite, + GimpImage *image, + GType type); + + +#endif /* __GIMP_SYMMETRY_H__ */ diff --git a/app/core/gimptag.c b/app/core/gimptag.c new file mode 100644 index 0000000..95c4954 --- /dev/null +++ b/app/core/gimptag.c @@ -0,0 +1,442 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptag.c + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimptag.h" + + +#define GIMP_TAG_INTERNAL_PREFIX "gimp:" + + +G_DEFINE_TYPE (GimpTag, gimp_tag, G_TYPE_OBJECT) + +#define parent_class gimp_tag_parent_class + + +static void +gimp_tag_class_init (GimpTagClass *klass) +{ +} + +static void +gimp_tag_init (GimpTag *tag) +{ + tag->tag = 0; + tag->collate_key = 0; + tag->internal = FALSE; +} + +/** + * gimp_tag_new: + * @tag_string: a tag name. + * + * If given tag name is not valid, an attempt will be made to fix it. + * + * Return value: a new #GimpTag object, or NULL if tag string is invalid and + * cannot be fixed. + **/ +GimpTag * +gimp_tag_new (const char *tag_string) +{ + GimpTag *tag; + gchar *tag_name; + gchar *case_folded; + gchar *collate_key; + + g_return_val_if_fail (tag_string != NULL, NULL); + + tag_name = gimp_tag_string_make_valid (tag_string); + if (! tag_name) + return NULL; + + tag = g_object_new (GIMP_TYPE_TAG, NULL); + + tag->tag = g_quark_from_string (tag_name); + + case_folded = g_utf8_casefold (tag_name, -1); + collate_key = g_utf8_collate_key (case_folded, -1); + tag->collate_key = g_quark_from_string (collate_key); + g_free (collate_key); + g_free (case_folded); + g_free (tag_name); + + return tag; +} + +/** + * gimp_tag_try_new: + * @tag_string: a tag name. + * + * Similar to gimp_tag_new(), but returns NULL if tag is surely not equal + * to any of currently created tags. It is useful for tag querying to avoid + * unneeded comparisons. If tag is created, however, it does not mean that + * it would necessarily match with some other tag. + * + * Return value: new #GimpTag object, or NULL if tag will not match with any + * other #GimpTag. + **/ +GimpTag * +gimp_tag_try_new (const char *tag_string) +{ + GimpTag *tag; + gchar *tag_name; + gchar *case_folded; + gchar *collate_key; + GQuark tag_quark; + GQuark collate_key_quark; + + tag_name = gimp_tag_string_make_valid (tag_string); + if (! tag_name) + return NULL; + + case_folded = g_utf8_casefold (tag_name, -1); + collate_key = g_utf8_collate_key (case_folded, -1); + collate_key_quark = g_quark_try_string (collate_key); + g_free (collate_key); + g_free (case_folded); + + if (! collate_key_quark) + { + g_free (tag_name); + return NULL; + } + + tag_quark = g_quark_from_string (tag_name); + g_free (tag_name); + if (! tag_quark) + return NULL; + + tag = g_object_new (GIMP_TYPE_TAG, NULL); + tag->tag = tag_quark; + tag->collate_key = collate_key_quark; + + return tag; +} + +/** + * gimp_tag_get_internal: + * @tag: a gimp tag. + * + * Retrieve internal status of the tag. + * + * Return value: internal status of tag. Internal tags are not saved. + **/ +gboolean +gimp_tag_get_internal (GimpTag *tag) +{ + g_return_val_if_fail (GIMP_IS_TAG (tag), FALSE); + + return tag->internal; +} + +/** + * gimp_tag_set_internal: + * @tag: a gimp tag. + * @internal: desired tag internal status + * + * Set internal status of the tag. Internal tags are usually automatically + * generated and will not be saved into users tag cache. + * + **/ +void +gimp_tag_set_internal (GimpTag *tag, gboolean internal) +{ + g_return_if_fail (GIMP_IS_TAG (tag)); + + tag->internal = internal; +} + + +/** + * gimp_tag_get_name: + * @tag: a gimp tag. + * + * Retrieve name of the tag. + * + * Return value: name of tag. + **/ +const gchar * +gimp_tag_get_name (GimpTag *tag) +{ + g_return_val_if_fail (GIMP_IS_TAG (tag), NULL); + + return g_quark_to_string (tag->tag); +} + +/** + * gimp_tag_get_hash: + * @tag: a gimp tag. + * + * Hashing function which is useful, for example, to store #GimpTag in + * a #GHashTable. + * + * Return value: hash value for tag. + **/ +guint +gimp_tag_get_hash (GimpTag *tag) +{ + g_return_val_if_fail (GIMP_IS_TAG (tag), -1); + + return tag->collate_key; +} + +/** + * gimp_tag_equals: + * @tag: a gimp tag. + * @other: another gimp tag to compare with. + * + * Compares tags for equality according to tag comparison rules. + * + * Return value: TRUE if tags are equal, FALSE otherwise. + **/ +gboolean +gimp_tag_equals (GimpTag *tag, + GimpTag *other) +{ + g_return_val_if_fail (GIMP_IS_TAG (tag), FALSE); + g_return_val_if_fail (GIMP_IS_TAG (other), FALSE); + + return tag->collate_key == other->collate_key; +} + +/** + * gimp_tag_compare_func: + * @p1: pointer to left-hand #GimpTag object. + * @p2: pointer to right-hand #GimpTag object. + * + * Compares tags according to tag comparison rules. Useful for sorting + * functions. + * + * Return value: meaning of return value is the same as in strcmp(). + **/ +int +gimp_tag_compare_func (const void *p1, + const void *p2) +{ + GimpTag *t1 = GIMP_TAG (p1); + GimpTag *t2 = GIMP_TAG (p2); + + return g_strcmp0 (g_quark_to_string (t1->collate_key), + g_quark_to_string (t2->collate_key)); +} + +/** + * gimp_tag_compare_with_string: + * @tag: a #GimpTag object. + * @tag_string: the string to compare to. + * + * Compares tag and a string according to tag comparison rules. Similar to + * gimp_tag_compare_func(), but can be used without creating temporary tag + * object. + * + * Return value: meaning of return value is the same as in strcmp(). + **/ +gint +gimp_tag_compare_with_string (GimpTag *tag, + const gchar *tag_string) +{ + gchar *case_folded; + const gchar *collate_key; + gchar *collate_key2; + gint result; + + g_return_val_if_fail (GIMP_IS_TAG (tag), 0); + g_return_val_if_fail (tag_string != NULL, 0); + + collate_key = g_quark_to_string (tag->collate_key); + case_folded = g_utf8_casefold (tag_string, -1); + collate_key2 = g_utf8_collate_key (case_folded, -1); + result = g_strcmp0 (collate_key, collate_key2); + g_free (collate_key2); + g_free (case_folded); + + return result; +} + +/** + * gimp_tag_has_prefix: + * @tag: a #GimpTag object. + * @prefix_string: the prefix to compare to. + * + * Compares tag and a prefix according to tag comparison rules. Similar to + * gimp_tag_compare_with_string(), but does not work on the collate key + * because that can't be matched partially. + * + * Return value: wheher #tag starts with @prefix_string. + **/ +gboolean +gimp_tag_has_prefix (GimpTag *tag, + const gchar *prefix_string) +{ + gchar *case_folded1; + gchar *case_folded2; + gboolean has_prefix; + + g_return_val_if_fail (GIMP_IS_TAG (tag), FALSE); + g_return_val_if_fail (prefix_string != NULL, FALSE); + + case_folded1 = g_utf8_casefold (g_quark_to_string (tag->tag), -1); + case_folded2 = g_utf8_casefold (prefix_string, -1); + + has_prefix = g_str_has_prefix (case_folded1, case_folded2); + + g_free (case_folded1); + g_free (case_folded2); + + g_printerr ("'%s' has prefix '%s': %d\n", + g_quark_to_string (tag->tag), prefix_string, has_prefix); + + return has_prefix; +} + +/** + * gimp_tag_string_make_valid: + * @tag_string: a text string. + * + * Tries to create a valid tag string from given @tag_string. + * + * Return value: a newly allocated tag string in case given @tag_string was + * valid or could be fixed, otherwise NULL. Allocated value should be freed + * using g_free(). + **/ +gchar * +gimp_tag_string_make_valid (const gchar *tag_string) +{ + gchar *tag; + GString *buffer; + gchar *tag_cursor; + gunichar c; + + g_return_val_if_fail (tag_string, NULL); + + tag = g_utf8_normalize (tag_string, -1, G_NORMALIZE_ALL); + if (! tag) + return NULL; + + tag = g_strstrip (tag); + if (! *tag) + { + g_free (tag); + return NULL; + } + + buffer = g_string_new (""); + tag_cursor = tag; + if (g_str_has_prefix (tag_cursor, GIMP_TAG_INTERNAL_PREFIX)) + { + tag_cursor += strlen (GIMP_TAG_INTERNAL_PREFIX); + } + do + { + c = g_utf8_get_char (tag_cursor); + tag_cursor = g_utf8_next_char (tag_cursor); + if (g_unichar_isprint (c) + && ! gimp_tag_is_tag_separator (c)) + { + g_string_append_unichar (buffer, c); + } + } while (c); + + g_free (tag); + tag = g_string_free (buffer, FALSE); + tag = g_strstrip (tag); + + if (! *tag) + { + g_free (tag); + return NULL; + } + + return tag; +} + +/** + * gimp_tag_is_tag_separator: + * @c: Unicode character. + * + * Defines a set of characters that are considered tag separators. The + * tag separators are hand-picked from the set of characters with the + * Terminal_Punctuation property as specified in the version 5.1.0 of + * the Unicode Standard. + * + * Return value: %TRUE if the character is a tag separator. + */ +gboolean +gimp_tag_is_tag_separator (gunichar c) +{ + switch (c) + { + case 0x002C: /* COMMA */ + case 0x060C: /* ARABIC COMMA */ + case 0x07F8: /* NKO COMMA */ + case 0x1363: /* ETHIOPIC COMMA */ + case 0x1802: /* MONGOLIAN COMMA */ + case 0x1808: /* MONGOLIAN MANCHU COMMA */ + case 0x3001: /* IDEOGRAPHIC COMMA */ + case 0xA60D: /* VAI COMMA */ + case 0xFE50: /* SMALL COMMA */ + case 0xFF0C: /* FULLWIDTH COMMA */ + case 0xFF64: /* HALFWIDTH IDEOGRAPHIC COMMA */ + return TRUE; + + default: + return FALSE; + } +} + +/** + * gimp_tag_or_null_ref: + * @tag: a #GimpTag + * + * A simple wrapper around g_object_ref() that silently accepts #NULL. + **/ +void +gimp_tag_or_null_ref (GimpTag *tag_or_null) +{ + if (tag_or_null) + { + g_return_if_fail (GIMP_IS_TAG (tag_or_null)); + + g_object_ref (tag_or_null); + } +} + +/** + * gimp_tag_or_null_unref: + * @tag: a #GimpTag + * + * A simple wrapper around g_object_unref() that silently accepts #NULL. + **/ +void +gimp_tag_or_null_unref (GimpTag *tag_or_null) +{ + if (tag_or_null) + { + g_return_if_fail (GIMP_IS_TAG (tag_or_null)); + + g_object_unref (tag_or_null); + } +} diff --git a/app/core/gimptag.h b/app/core/gimptag.h new file mode 100644 index 0000000..0cd6c32 --- /dev/null +++ b/app/core/gimptag.h @@ -0,0 +1,80 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptag.h + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TAG_H__ +#define __GIMP_TAG_H__ + + +#include + + +#define GIMP_TYPE_TAG (gimp_tag_get_type ()) +#define GIMP_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TAG, GimpTag)) +#define GIMP_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TAG, GimpTagClass)) +#define GIMP_IS_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TAG)) +#define GIMP_IS_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TAG)) +#define GIMP_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TAG, GimpTagClass)) + + +typedef struct _GimpTagClass GimpTagClass; + +struct _GimpTag +{ + GObject parent_instance; + + GQuark tag; + GQuark collate_key; + + gboolean internal; /* Tags that are not serialized to disk */ +}; + +struct _GimpTagClass +{ + GObjectClass parent_class; +}; + +GType gimp_tag_get_type (void) G_GNUC_CONST; + +GimpTag * gimp_tag_new (const gchar *tag_string); +GimpTag * gimp_tag_try_new (const gchar *tag_string); + +const gchar * gimp_tag_get_name (GimpTag *tag); +guint gimp_tag_get_hash (GimpTag *tag); + +gboolean gimp_tag_get_internal (GimpTag *tag); +void gimp_tag_set_internal (GimpTag *tag, + gboolean internal); + +gboolean gimp_tag_equals (GimpTag *tag, + GimpTag *other); +gint gimp_tag_compare_func (const void *p1, + const void *p2); +gint gimp_tag_compare_with_string (GimpTag *tag, + const gchar *tag_string); +gboolean gimp_tag_has_prefix (GimpTag *tag, + const gchar *prefix_string); +gchar * gimp_tag_string_make_valid (const gchar *tag_string); +gboolean gimp_tag_is_tag_separator (gunichar c); + +void gimp_tag_or_null_ref (GimpTag *tag_or_null); +void gimp_tag_or_null_unref (GimpTag *tag_or_null); + + +#endif /* __GIMP_TAG_H__ */ diff --git a/app/core/gimptagcache.c b/app/core/gimptagcache.c new file mode 100644 index 0000000..a0e4534 --- /dev/null +++ b/app/core/gimptagcache.c @@ -0,0 +1,646 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptagcache.c + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "config/gimpxmlparser.h" + +#include "gimp-memsize.h" +#include "gimpcontext.h" +#include "gimpdata.h" +#include "gimplist.h" +#include "gimptag.h" +#include "gimptagcache.h" +#include "gimptagged.h" + +#include "gimp-intl.h" + + +#define GIMP_TAG_CACHE_FILE "tags.xml" + +/* #define DEBUG_GIMP_TAG_CACHE 1 */ + + +enum +{ + PROP_0, + PROP_GIMP +}; + + +typedef struct +{ + GQuark identifier; + GQuark checksum; + GList *tags; + guint referenced : 1; +} GimpTagCacheRecord; + +typedef struct +{ + GArray *records; + GimpTagCacheRecord current_record; +} GimpTagCacheParseData; + +struct _GimpTagCachePrivate +{ + GArray *records; + GList *containers; +}; + + +static void gimp_tag_cache_finalize (GObject *object); + +static gint64 gimp_tag_cache_get_memsize (GimpObject *object, + gint64 *gui_size); +static void gimp_tag_cache_object_initialize (GimpTagged *tagged, + GimpTagCache *cache); +static void gimp_tag_cache_add_object (GimpTagCache *cache, + GimpTagged *tagged); + +static void gimp_tag_cache_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void gimp_tag_cache_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void gimp_tag_cache_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); +static void gimp_tag_cache_load_error (GMarkupParseContext *context, + GError *error, + gpointer user_data); +static const gchar * gimp_tag_cache_attribute_name_to_value + (const gchar **attribute_names, + const gchar **attribute_values, + const gchar *name); + +static GQuark gimp_tag_cache_get_error_domain (void); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpTagCache, gimp_tag_cache, GIMP_TYPE_OBJECT) + +#define parent_class gimp_tag_cache_parent_class + + +static void +gimp_tag_cache_class_init (GimpTagCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + object_class->finalize = gimp_tag_cache_finalize; + + gimp_object_class->get_memsize = gimp_tag_cache_get_memsize; +} + +static void +gimp_tag_cache_init (GimpTagCache *cache) +{ + cache->priv = gimp_tag_cache_get_instance_private (cache); + + cache->priv->records = g_array_new (FALSE, FALSE, + sizeof (GimpTagCacheRecord)); + cache->priv->containers = NULL; +} + +static void +gimp_tag_cache_finalize (GObject *object) +{ + GimpTagCache *cache = GIMP_TAG_CACHE (object); + + if (cache->priv->records) + { + gint i; + + for (i = 0; i < cache->priv->records->len; i++) + { + GimpTagCacheRecord *rec = &g_array_index (cache->priv->records, + GimpTagCacheRecord, i); + + g_list_free_full (rec->tags, (GDestroyNotify) g_object_unref); + } + + g_array_free (cache->priv->records, TRUE); + cache->priv->records = NULL; + } + + if (cache->priv->containers) + { + g_list_free (cache->priv->containers); + cache->priv->containers = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_tag_cache_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpTagCache *cache = GIMP_TAG_CACHE (object); + gint64 memsize = 0; + + memsize += gimp_g_list_get_memsize (cache->priv->containers, 0); + memsize += cache->priv->records->len * sizeof (GimpTagCacheRecord); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +/** + * gimp_tag_cache_new: + * + * Return value: creates new GimpTagCache object. + **/ +GimpTagCache * +gimp_tag_cache_new (void) +{ + return g_object_new (GIMP_TYPE_TAG_CACHE, NULL); +} + +static void +gimp_tag_cache_container_add_callback (GimpTagCache *cache, + GimpTagged *tagged, + GimpContainer *not_used) +{ + gimp_tag_cache_add_object (cache, tagged); +} + +/** + * gimp_tag_cache_add_container: + * @cache: a GimpTagCache object. + * @container: container containing GimpTagged objects. + * + * Adds container of GimpTagged objects to tag cache. Before calling this + * function tag cache must be loaded using gimp_tag_cache_load(). When tag + * cache is saved to file, tags are collected from objects in priv->containers. + **/ +void +gimp_tag_cache_add_container (GimpTagCache *cache, + GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_TAG_CACHE (cache)); + g_return_if_fail (GIMP_IS_CONTAINER (container)); + + cache->priv->containers = g_list_append (cache->priv->containers, container); + gimp_container_foreach (container, (GFunc) gimp_tag_cache_object_initialize, + cache); + + g_signal_connect_swapped (container, "add", + G_CALLBACK (gimp_tag_cache_container_add_callback), + cache); +} + +static void +gimp_tag_cache_add_object (GimpTagCache *cache, + GimpTagged *tagged) +{ + gchar *identifier; + GQuark identifier_quark = 0; + gchar *checksum; + GQuark checksum_quark = 0; + GList *list; + gint i; + + identifier = gimp_tagged_get_identifier (tagged); + + if (identifier) + { + identifier_quark = g_quark_try_string (identifier); + g_free (identifier); + } + + if (identifier_quark) + { + for (i = 0; i < cache->priv->records->len; i++) + { + GimpTagCacheRecord *rec = &g_array_index (cache->priv->records, + GimpTagCacheRecord, i); + + if (rec->identifier == identifier_quark) + { + for (list = rec->tags; list; list = g_list_next (list)) + { + gimp_tagged_add_tag (tagged, GIMP_TAG (list->data)); + } + + rec->referenced = TRUE; + return; + } + } + } + + checksum = gimp_tagged_get_checksum (tagged); + + if (checksum) + { + checksum_quark = g_quark_try_string (checksum); + g_free (checksum); + } + + if (checksum_quark) + { + for (i = 0; i < cache->priv->records->len; i++) + { + GimpTagCacheRecord *rec = &g_array_index (cache->priv->records, + GimpTagCacheRecord, i); + + if (rec->checksum == checksum_quark) + { +#if DEBUG_GIMP_TAG_CACHE + g_printerr ("remapping identifier: %s ==> %s\n", + rec->identifier ? g_quark_to_string (rec->identifier) : "(NULL)", + identifier_quark ? g_quark_to_string (identifier_quark) : "(NULL)"); +#endif + + rec->identifier = identifier_quark; + + for (list = rec->tags; list; list = g_list_next (list)) + { + gimp_tagged_add_tag (tagged, GIMP_TAG (list->data)); + } + + rec->referenced = TRUE; + return; + } + } + } + +} + +static void +gimp_tag_cache_object_initialize (GimpTagged *tagged, + GimpTagCache *cache) +{ + gimp_tag_cache_add_object (cache, tagged); +} + +static void +gimp_tag_cache_tagged_to_cache_record_foreach (GimpTagged *tagged, + GList **cache_records) +{ + gchar *identifier = gimp_tagged_get_identifier (tagged); + + if (identifier) + { + GimpTagCacheRecord *cache_rec = g_new (GimpTagCacheRecord, 1); + gchar *checksum; + + checksum = gimp_tagged_get_checksum (tagged); + + cache_rec->identifier = g_quark_from_string (identifier); + cache_rec->checksum = g_quark_from_string (checksum); + cache_rec->tags = g_list_copy (gimp_tagged_get_tags (tagged)); + + g_free (checksum); + + *cache_records = g_list_prepend (*cache_records, cache_rec); + } + + g_free (identifier); +} + +/** + * gimp_tag_cache_save: + * @cache: a GimpTagCache object. + * + * Saves tag cache to cache file. + **/ +void +gimp_tag_cache_save (GimpTagCache *cache) +{ + GString *buf; + GList *saved_records; + GList *iterator; + GFile *file; + GOutputStream *output; + GError *error = NULL; + gint i; + + g_return_if_fail (GIMP_IS_TAG_CACHE (cache)); + + saved_records = NULL; + for (i = 0; i < cache->priv->records->len; i++) + { + GimpTagCacheRecord *current_record = &g_array_index (cache->priv->records, + GimpTagCacheRecord, i); + + if (! current_record->referenced && current_record->tags) + { + /* keep tagged objects which have tags assigned + * but were not loaded. + */ + GimpTagCacheRecord *record_copy = g_new (GimpTagCacheRecord, 1); + + record_copy->identifier = current_record->identifier; + record_copy->checksum = current_record->checksum; + record_copy->tags = g_list_copy (current_record->tags); + + saved_records = g_list_prepend (saved_records, record_copy); + } + } + + for (iterator = cache->priv->containers; + iterator; + iterator = g_list_next (iterator)) + { + gimp_container_foreach (GIMP_CONTAINER (iterator->data), + (GFunc) gimp_tag_cache_tagged_to_cache_record_foreach, + &saved_records); + } + + saved_records = g_list_reverse (saved_records); + + buf = g_string_new (""); + g_string_append (buf, "\n"); + g_string_append (buf, "\n"); + + for (iterator = saved_records; iterator; iterator = g_list_next (iterator)) + { + GimpTagCacheRecord *cache_rec = iterator->data; + GList *tag_iterator; + gchar *identifier_string; + gchar *tag_string; + + identifier_string = g_markup_escape_text (g_quark_to_string (cache_rec->identifier), -1); + g_string_append_printf (buf, "\n \n", + identifier_string, + g_quark_to_string (cache_rec->checksum)); + g_free (identifier_string); + + for (tag_iterator = cache_rec->tags; + tag_iterator; + tag_iterator = g_list_next (tag_iterator)) + { + GimpTag *tag = GIMP_TAG (tag_iterator->data); + + if (! gimp_tag_get_internal (tag)) + { + tag_string = g_markup_escape_text (gimp_tag_get_name (tag), -1); + g_string_append_printf (buf, " %s\n", tag_string); + g_free (tag_string); + } + } + + g_string_append (buf, " \n"); + } + + g_string_append (buf, "\n"); + + file = gimp_directory_file (GIMP_TAG_CACHE_FILE, NULL); + + output = G_OUTPUT_STREAM (g_file_replace (file, + NULL, FALSE, G_FILE_CREATE_NONE, + NULL, &error)); + if (! output) + { + g_printerr ("%s\n", error->message); + } + else if (! g_output_stream_write_all (output, buf->str, buf->len, + NULL, NULL, &error)) + { + GCancellable *cancellable = g_cancellable_new (); + + g_printerr (_("Error writing '%s': %s\n"), + gimp_file_get_utf8_name (file), error->message); + + /* Cancel the overwrite initiated by g_file_replace(). */ + g_cancellable_cancel (cancellable); + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + } + else if (! g_output_stream_close (output, NULL, &error)) + { + g_printerr (_("Error closing '%s': %s\n"), + gimp_file_get_utf8_name (file), error->message); + } + + if (output) + g_object_unref (output); + + g_clear_error (&error); + g_object_unref (file); + g_string_free (buf, TRUE); + + for (iterator = saved_records; + iterator; + iterator = g_list_next (iterator)) + { + GimpTagCacheRecord *cache_rec = iterator->data; + + g_list_free (cache_rec->tags); + g_free (cache_rec); + } + + g_list_free (saved_records); +} + +/** + * gimp_tag_cache_load: + * @cache: a GimpTagCache object. + * + * Loads tag cache from file. + **/ +void +gimp_tag_cache_load (GimpTagCache *cache) +{ + GFile *file; + GMarkupParser markup_parser; + GimpXmlParser *xml_parser; + GimpTagCacheParseData parse_data; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_TAG_CACHE (cache)); + + /* clear any previous priv->records */ + cache->priv->records = g_array_set_size (cache->priv->records, 0); + + parse_data.records = g_array_new (FALSE, FALSE, sizeof (GimpTagCacheRecord)); + memset (&parse_data.current_record, 0, sizeof (GimpTagCacheRecord)); + + markup_parser.start_element = gimp_tag_cache_load_start_element; + markup_parser.end_element = gimp_tag_cache_load_end_element; + markup_parser.text = gimp_tag_cache_load_text; + markup_parser.passthrough = NULL; + markup_parser.error = gimp_tag_cache_load_error; + + xml_parser = gimp_xml_parser_new (&markup_parser, &parse_data); + + file = gimp_directory_file (GIMP_TAG_CACHE_FILE, NULL); + + if (gimp_xml_parser_parse_gfile (xml_parser, file, &error)) + { + cache->priv->records = g_array_append_vals (cache->priv->records, + parse_data.records->data, + parse_data.records->len); + } + else + { + g_printerr ("Failed to parse tag cache: %s\n", + error ? error->message : "WTF unknown error"); + } + + g_object_unref (file); + gimp_xml_parser_free (xml_parser); + g_array_free (parse_data.records, TRUE); +} + +static void +gimp_tag_cache_load_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + GimpTagCacheParseData *parse_data = user_data; + + if (! strcmp (element_name, "resource")) + { + const gchar *identifier; + const gchar *checksum; + + identifier = gimp_tag_cache_attribute_name_to_value (attribute_names, + attribute_values, + "identifier"); + checksum = gimp_tag_cache_attribute_name_to_value (attribute_names, + attribute_values, + "checksum"); + + if (! identifier) + { + g_set_error (error, + gimp_tag_cache_get_error_domain (), + 1001, + "Resource tag does not contain required attribute identifier."); + return; + } + + memset (&parse_data->current_record, 0, sizeof (GimpTagCacheRecord)); + + parse_data->current_record.identifier = g_quark_from_string (identifier); + parse_data->current_record.checksum = g_quark_from_string (checksum); + } +} + +static void +gimp_tag_cache_load_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + GimpTagCacheParseData *parse_data = user_data; + + if (strcmp (element_name, "resource") == 0) + { + parse_data->records = g_array_append_val (parse_data->records, + parse_data->current_record); + memset (&parse_data->current_record, 0, sizeof (GimpTagCacheRecord)); + } +} + +static void +gimp_tag_cache_load_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + GimpTagCacheParseData *parse_data = user_data; + const gchar *current_element; + gchar buffer[2048]; + GimpTag *tag; + + current_element = g_markup_parse_context_get_element (context); + + if (g_strcmp0 (current_element, "tag") == 0) + { + if (text_len >= sizeof (buffer)) + { + g_set_error (error, gimp_tag_cache_get_error_domain (), 1002, + "Tag value is too long."); + return; + } + + memcpy (buffer, text, text_len); + buffer[text_len] = '\0'; + + tag = gimp_tag_new (buffer); + if (tag) + { + parse_data->current_record.tags = g_list_append (parse_data->current_record.tags, + tag); + } + else + { + g_warning ("dropping invalid tag '%s' from '%s'\n", buffer, + g_quark_to_string (parse_data->current_record.identifier)); + } + } +} + +static void +gimp_tag_cache_load_error (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + g_printerr ("Tag cache parse error: %s\n", error->message); +} + +static const gchar* +gimp_tag_cache_attribute_name_to_value (const gchar **attribute_names, + const gchar **attribute_values, + const gchar *name) +{ + while (*attribute_names) + { + if (! strcmp (*attribute_names, name)) + { + return *attribute_values; + } + + attribute_names++; + attribute_values++; + } + + return NULL; +} + +static GQuark +gimp_tag_cache_get_error_domain (void) +{ + return g_quark_from_static_string ("gimp-tag-cache-error-quark"); +} diff --git a/app/core/gimptagcache.h b/app/core/gimptagcache.h new file mode 100644 index 0000000..4c3a465 --- /dev/null +++ b/app/core/gimptagcache.h @@ -0,0 +1,63 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptagcache.h + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TAG_CACHE_H__ +#define __GIMP_TAG_CACHE_H__ + + +#include "gimpobject.h" + + +#define GIMP_TYPE_TAG_CACHE (gimp_tag_cache_get_type ()) +#define GIMP_TAG_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TAG_CACHE, GimpTagCache)) +#define GIMP_TAG_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TAG_CACHE, GimpTagCacheClass)) +#define GIMP_IS_TAG_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TAG_CACHE)) +#define GIMP_IS_TAG_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TAG_CACHE)) +#define GIMP_TAG_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TAG_CACHE, GimpTagCacheClass)) + + +typedef struct _GimpTagCacheClass GimpTagCacheClass; +typedef struct _GimpTagCachePrivate GimpTagCachePrivate; + +struct _GimpTagCache +{ + GimpObject parent_instance; + + GimpTagCachePrivate *priv; +}; + +struct _GimpTagCacheClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_tag_cache_get_type (void) G_GNUC_CONST; + +GimpTagCache * gimp_tag_cache_new (void); + +void gimp_tag_cache_save (GimpTagCache *cache); +void gimp_tag_cache_load (GimpTagCache *cache); + +void gimp_tag_cache_add_container (GimpTagCache *cache, + GimpContainer *container); + + +#endif /* __GIMP_TAG_CACHE_H__ */ diff --git a/app/core/gimptagged.c b/app/core/gimptagged.c new file mode 100644 index 0000000..7875f9a --- /dev/null +++ b/app/core/gimptagged.c @@ -0,0 +1,260 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptagged.c + * Copyright (C) 2008 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 . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimpmarshal.h" +#include "gimptag.h" +#include "gimptagged.h" + + +enum +{ + TAG_ADDED, + TAG_REMOVED, + LAST_SIGNAL +}; + + +G_DEFINE_INTERFACE (GimpTagged, gimp_tagged, G_TYPE_OBJECT) + + +static guint gimp_tagged_signals[LAST_SIGNAL] = { 0, }; + + +/* private functions */ + + +static void +gimp_tagged_default_init (GimpTaggedInterface *iface) +{ + gimp_tagged_signals[TAG_ADDED] = + g_signal_new ("tag-added", + GIMP_TYPE_TAGGED, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpTaggedInterface, tag_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_TAG); + + gimp_tagged_signals[TAG_REMOVED] = + g_signal_new ("tag-removed", + GIMP_TYPE_TAGGED, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpTaggedInterface, tag_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_TAG); +} + + +/* public functions */ + + +/** + * gimp_tagged_add_tag: + * @tagged: an object that implements the %GimpTagged interface + * @tag: a %GimpTag + * + * Adds @tag to the @tagged object. The GimpTagged::tag-added signal + * is emitted if and only if the @tag was not already assigned to this + * object. + **/ +void +gimp_tagged_add_tag (GimpTagged *tagged, + GimpTag *tag) +{ + g_return_if_fail (GIMP_IS_TAGGED (tagged)); + g_return_if_fail (GIMP_IS_TAG (tag)); + + if (GIMP_TAGGED_GET_INTERFACE (tagged)->add_tag (tagged, tag)) + { + g_signal_emit (tagged, gimp_tagged_signals[TAG_ADDED], 0, tag); + } +} + +/** + * gimp_tagged_remove_tag: + * @tagged: an object that implements the %GimpTagged interface + * @tag: a %GimpTag + * + * Removes @tag from the @tagged object. The GimpTagged::tag-removed + * signal is emitted if and only if the @tag was actually assigned to + * this object. + **/ +void +gimp_tagged_remove_tag (GimpTagged *tagged, + GimpTag *tag) +{ + GList *tag_iter; + + g_return_if_fail (GIMP_IS_TAGGED (tagged)); + g_return_if_fail (GIMP_IS_TAG (tag)); + + for (tag_iter = gimp_tagged_get_tags (tagged); + tag_iter; + tag_iter = g_list_next (tag_iter)) + { + GimpTag *tag_ref = tag_iter->data; + + if (gimp_tag_equals (tag_ref, tag)) + { + g_object_ref (tag_ref); + + if (GIMP_TAGGED_GET_INTERFACE (tagged)->remove_tag (tagged, tag_ref)) + { + g_signal_emit (tagged, gimp_tagged_signals[TAG_REMOVED], 0, + tag_ref); + } + + g_object_unref (tag_ref); + + return; + } + } +} + +/** + * gimp_tagged_set_tags: + * @tagged: an object that implements the %GimpTagged interface + * @tags: a list of tags + * + * Sets the list of tags assigned to this object. The passed list of + * tags is copied and should be freed by the caller. + **/ +void +gimp_tagged_set_tags (GimpTagged *tagged, + GList *tags) +{ + GList *old_tags; + GList *list; + + g_return_if_fail (GIMP_IS_TAGGED (tagged)); + + old_tags = g_list_copy (gimp_tagged_get_tags (tagged)); + + for (list = old_tags; list; list = g_list_next (list)) + { + gimp_tagged_remove_tag (tagged, list->data); + } + + g_list_free (old_tags); + + for (list = tags; list; list = g_list_next (list)) + { + g_return_if_fail (GIMP_IS_TAG (list->data)); + + gimp_tagged_add_tag (tagged, list->data); + } +} + +/** + * gimp_tagged_get_tags: + * @tagged: an object that implements the %GimpTagged interface + * + * Returns the list of tags assigned to this object. The returned %GList + * is owned by the @tagged object and must not be modified or destroyed. + * + * Return value: a list of tags + **/ +GList * +gimp_tagged_get_tags (GimpTagged *tagged) +{ + g_return_val_if_fail (GIMP_IS_TAGGED (tagged), NULL); + + return GIMP_TAGGED_GET_INTERFACE (tagged)->get_tags (tagged); +} + +/** + * gimp_tagged_get_identifier: + * @tagged: an object that implements the %GimpTagged interface + * + * Returns an identifier string which uniquely identifies the tagged + * object. Two different objects must have unique identifiers but may + * have the same checksum (which will be the case if one object is a + * copy of the other). The identifier must be the same across + * sessions, so for example an instance pointer cannot be used as an + * identifier. + * + * Return value: a newly allocated string containing unique identifier + * of the object. It must be freed using #g_free. + **/ +gchar * +gimp_tagged_get_identifier (GimpTagged *tagged) +{ + g_return_val_if_fail (GIMP_IS_TAGGED (tagged), NULL); + + return GIMP_TAGGED_GET_INTERFACE (tagged)->get_identifier (tagged); +} + +/** + * gimp_tagged_get_checksum: + * @tagged: an object that implements the %GimpTagged interface + * + * Returns the checksum of the @tagged object. It is used to remap the + * tags for an object for which the identifier has changed, for + * example if the user has renamed a data file since the last session. + * + * If the object does not want to support such remapping (objects not + * stored in file for example) it can return #NULL. + * + * Return value: checksum string if object needs identifier remapping, + * #NULL otherwise. Returned string must be freed with #g_free(). + **/ +gchar * +gimp_tagged_get_checksum (GimpTagged *tagged) +{ + g_return_val_if_fail (GIMP_IS_TAGGED (tagged), FALSE); + + return GIMP_TAGGED_GET_INTERFACE (tagged)->get_checksum (tagged); +} + +/** + * gimp_tagged_has_tag: + * @tagged: an object that implements the %GimpTagged interface + * @tag: a %GimpTag + * + * Return value: #TRUE if the object has @tag, #FALSE otherwise. + **/ +gboolean +gimp_tagged_has_tag (GimpTagged *tagged, + GimpTag *tag) +{ + GList *tag_iter; + + g_return_val_if_fail (GIMP_IS_TAGGED (tagged), FALSE); + g_return_val_if_fail (GIMP_IS_TAG (tag), FALSE); + + for (tag_iter = gimp_tagged_get_tags (tagged); + tag_iter; + tag_iter = g_list_next (tag_iter)) + { + if (gimp_tag_equals (tag_iter->data, tag)) + return TRUE; + } + + return FALSE; +} diff --git a/app/core/gimptagged.h b/app/core/gimptagged.h new file mode 100644 index 0000000..32251e9 --- /dev/null +++ b/app/core/gimptagged.h @@ -0,0 +1,72 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimptagged.h + * Copyright (C) 2008 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 . + */ + +#ifndef __GIMP_TAGGED_H__ +#define __GIMP_TAGGED_H__ + + +#define GIMP_TYPE_TAGGED (gimp_tagged_get_type ()) +#define GIMP_IS_TAGGED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TAGGED)) +#define GIMP_TAGGED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TAGGED, GimpTagged)) +#define GIMP_TAGGED_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_TAGGED, GimpTaggedInterface)) + + +typedef struct _GimpTaggedInterface GimpTaggedInterface; + +struct _GimpTaggedInterface +{ + GTypeInterface base_iface; + + /* signals */ + void (* tag_added) (GimpTagged *tagged, + GimpTag *tag); + void (* tag_removed) (GimpTagged *tagged, + GimpTag *tag); + + /* virtual functions */ + gboolean (* add_tag) (GimpTagged *tagged, + GimpTag *tag); + gboolean (* remove_tag) (GimpTagged *tagged, + GimpTag *tag); + GList * (* get_tags) (GimpTagged *tagged); + gchar * (* get_identifier) (GimpTagged *tagged); + gchar * (* get_checksum) (GimpTagged *tagged); +}; + + +GType gimp_tagged_get_type (void) G_GNUC_CONST; + +void gimp_tagged_add_tag (GimpTagged *tagged, + GimpTag *tag); +void gimp_tagged_remove_tag (GimpTagged *tagged, + GimpTag *tag); + +void gimp_tagged_set_tags (GimpTagged *tagged, + GList *tags); +GList * gimp_tagged_get_tags (GimpTagged *tagged); + +gchar * gimp_tagged_get_identifier (GimpTagged *tagged); +gchar * gimp_tagged_get_checksum (GimpTagged *tagged); + +gboolean gimp_tagged_has_tag (GimpTagged *tagged, + GimpTag *tag); + + +#endif /* __GIMP_TAGGED_H__ */ diff --git a/app/core/gimptaggedcontainer.c b/app/core/gimptaggedcontainer.c new file mode 100644 index 0000000..93ea58b --- /dev/null +++ b/app/core/gimptaggedcontainer.c @@ -0,0 +1,483 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimptaggedcontainer.c + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "core-types.h" + +#include "gimp.h" +#include "gimpmarshal.h" +#include "gimptag.h" +#include "gimptagged.h" +#include "gimptaggedcontainer.h" + + +enum +{ + TAG_COUNT_CHANGED, + LAST_SIGNAL +}; + + +static void gimp_tagged_container_dispose (GObject *object); +static gint64 gimp_tagged_container_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_tagged_container_clear (GimpContainer *container); + +static void gimp_tagged_container_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_tagged_container_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_tagged_container_src_freeze (GimpFilteredContainer *filtered_container); +static void gimp_tagged_container_src_thaw (GimpFilteredContainer *filtered_container); + +static gboolean gimp_tagged_container_object_matches (GimpTaggedContainer *tagged_container, + GimpObject *object); + +static void gimp_tagged_container_tag_added (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container); +static void gimp_tagged_container_tag_removed (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container); +static void gimp_tagged_container_ref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag); +static void gimp_tagged_container_unref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag); +static void gimp_tagged_container_tag_count_changed (GimpTaggedContainer *tagged_container, + gint tag_count); + + +G_DEFINE_TYPE (GimpTaggedContainer, gimp_tagged_container, + GIMP_TYPE_FILTERED_CONTAINER) + +#define parent_class gimp_tagged_container_parent_class + +static guint gimp_tagged_container_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_tagged_container_class_init (GimpTaggedContainerClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + GimpFilteredContainerClass *filtered_class = GIMP_FILTERED_CONTAINER_CLASS (klass); + + g_object_class->dispose = gimp_tagged_container_dispose; + + gimp_object_class->get_memsize = gimp_tagged_container_get_memsize; + + container_class->clear = gimp_tagged_container_clear; + + filtered_class->src_add = gimp_tagged_container_src_add; + filtered_class->src_remove = gimp_tagged_container_src_remove; + filtered_class->src_freeze = gimp_tagged_container_src_freeze; + filtered_class->src_thaw = gimp_tagged_container_src_thaw; + + klass->tag_count_changed = gimp_tagged_container_tag_count_changed; + + gimp_tagged_container_signals[TAG_COUNT_CHANGED] = + g_signal_new ("tag-count-changed", + GIMP_TYPE_TAGGED_CONTAINER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpTaggedContainerClass, tag_count_changed), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); +} + +static void +gimp_tagged_container_init (GimpTaggedContainer *tagged_container) +{ + tagged_container->tag_ref_counts = + g_hash_table_new_full ((GHashFunc) gimp_tag_get_hash, + (GEqualFunc) gimp_tag_equals, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) NULL); +} + +static void +gimp_tagged_container_dispose (GObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (object); + + if (tagged_container->filter) + { + g_list_free_full (tagged_container->filter, + (GDestroyNotify) gimp_tag_or_null_unref); + tagged_container->filter = NULL; + } + + g_clear_pointer (&tagged_container->tag_ref_counts, g_hash_table_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gint64 +gimp_tagged_container_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + gint64 memsize = 0; + + /* FIXME take members into account */ + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_tagged_container_clear (GimpContainer *container) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (container); + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (container); + GList *list; + + for (list = GIMP_LIST (filtered_container->src_container)->queue->head; + list; + list = g_list_next (list)) + { + g_signal_handlers_disconnect_by_func (list->data, + gimp_tagged_container_tag_added, + tagged_container); + g_signal_handlers_disconnect_by_func (list->data, + gimp_tagged_container_tag_removed, + tagged_container); + } + + if (tagged_container->tag_ref_counts) + { + g_hash_table_remove_all (tagged_container->tag_ref_counts); + tagged_container->tag_count = 0; + } + + GIMP_CONTAINER_CLASS (parent_class)->clear (container); +} + +static void +gimp_tagged_container_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container); + GList *list; + + for (list = gimp_tagged_get_tags (GIMP_TAGGED (object)); + list; + list = g_list_next (list)) + { + gimp_tagged_container_ref_tag (tagged_container, list->data); + } + + g_signal_connect (object, "tag-added", + G_CALLBACK (gimp_tagged_container_tag_added), + tagged_container); + g_signal_connect (object, "tag-removed", + G_CALLBACK (gimp_tagged_container_tag_removed), + tagged_container); + + if (gimp_tagged_container_object_matches (tagged_container, object)) + { + gimp_container_add (GIMP_CONTAINER (tagged_container), object); + } +} + +static void +gimp_tagged_container_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container); + GList *list; + + g_signal_handlers_disconnect_by_func (object, + gimp_tagged_container_tag_added, + tagged_container); + g_signal_handlers_disconnect_by_func (object, + gimp_tagged_container_tag_removed, + tagged_container); + + for (list = gimp_tagged_get_tags (GIMP_TAGGED (object)); + list; + list = g_list_next (list)) + { + gimp_tagged_container_unref_tag (tagged_container, list->data); + } + + if (gimp_tagged_container_object_matches (tagged_container, object)) + { + gimp_container_remove (GIMP_CONTAINER (tagged_container), object); + } +} + +static void +gimp_tagged_container_src_freeze (GimpFilteredContainer *filtered_container) +{ + gimp_container_clear (GIMP_CONTAINER (filtered_container)); +} + +static void +gimp_tagged_container_src_thaw (GimpFilteredContainer *filtered_container) +{ + GList *list; + + for (list = GIMP_LIST (filtered_container->src_container)->queue->head; + list; + list = g_list_next (list)) + { + gimp_tagged_container_src_add (filtered_container, list->data); + } +} + +/** + * gimp_tagged_container_new: + * @src_container: container to be filtered. + * + * Creates a new #GimpTaggedContainer object which creates filtered + * data view of #GimpTagged objects. It filters @src_container for + * objects containing all of the filtering tags. Synchronization with + * @src_container data is performed automatically. + * + * Return value: a new #GimpTaggedContainer object. + **/ +GimpContainer * +gimp_tagged_container_new (GimpContainer *src_container) +{ + GimpTaggedContainer *tagged_container; + GType children_type; + GCompareFunc sort_func; + + g_return_val_if_fail (GIMP_IS_LIST (src_container), NULL); + + children_type = gimp_container_get_children_type (src_container); + sort_func = GIMP_LIST (src_container)->sort_func; + + tagged_container = g_object_new (GIMP_TYPE_TAGGED_CONTAINER, + "sort-func", sort_func, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "unique-names", FALSE, + "src-container", src_container, + NULL); + + return GIMP_CONTAINER (tagged_container); +} + +/** + * gimp_tagged_container_set_filter: + * @tagged_container: a #GimpTaggedContainer object. + * @tags: list of #GimpTag objects. + * + * Sets list of tags to be used for filtering. Only objects which have + * all of the tags assigned match filtering criteria. + **/ +void +gimp_tagged_container_set_filter (GimpTaggedContainer *tagged_container, + GList *tags) +{ + GList *new_filter; + + g_return_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container)); + + if (tags) + { + GList *list; + + for (list = tags; list; list = g_list_next (list)) + g_return_if_fail (list->data == NULL || GIMP_IS_TAG (list->data)); + } + + if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container)) + { + gimp_tagged_container_src_freeze (GIMP_FILTERED_CONTAINER (tagged_container)); + } + + /* ref new tags first, they could be the same as the old ones */ + new_filter = g_list_copy (tags); + g_list_foreach (new_filter, (GFunc) gimp_tag_or_null_ref, NULL); + + g_list_free_full (tagged_container->filter, + (GDestroyNotify) gimp_tag_or_null_unref); + tagged_container->filter = new_filter; + + if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container)) + { + gimp_tagged_container_src_thaw (GIMP_FILTERED_CONTAINER (tagged_container)); + } +} + +/** + * gimp_tagged_container_get_filter: + * @tagged_container: a #GimpTaggedContainer object. + * + * Returns current tag filter. Tag filter is a list of GimpTag objects, which + * must be contained by each object matching filter criteria. + * + * Return value: a list of GimpTag objects used as filter. This value should + * not be modified or freed. + **/ +const GList * +gimp_tagged_container_get_filter (GimpTaggedContainer *tagged_container) +{ + g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container), NULL); + + return tagged_container->filter; +} + +static gboolean +gimp_tagged_container_object_matches (GimpTaggedContainer *tagged_container, + GimpObject *object) +{ + GList *filter_tags; + + for (filter_tags = tagged_container->filter; + filter_tags; + filter_tags = g_list_next (filter_tags)) + { + if (! filter_tags->data) + { + /* invalid tag - does not match */ + return FALSE; + } + + if (! gimp_tagged_has_tag (GIMP_TAGGED (object), + filter_tags->data)) + { + /* match for the tag was not found. + * since query is of type AND, it whole fails. + */ + return FALSE; + } + } + + return TRUE; +} + +static void +gimp_tagged_container_tag_added (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container) +{ + gimp_tagged_container_ref_tag (tagged_container, tag); + + if (gimp_tagged_container_object_matches (tagged_container, + GIMP_OBJECT (tagged)) && + ! gimp_container_have (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged))) + { + gimp_container_add (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged)); + } +} + +static void +gimp_tagged_container_tag_removed (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container) +{ + gimp_tagged_container_unref_tag (tagged_container, tag); + + if (! gimp_tagged_container_object_matches (tagged_container, + GIMP_OBJECT (tagged)) && + gimp_container_have (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged))) + { + gimp_container_remove (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged)); + } +} + +static void +gimp_tagged_container_ref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag) +{ + gint ref_count; + + ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts, + tag)); + ref_count++; + g_hash_table_insert (tagged_container->tag_ref_counts, + g_object_ref (tag), + GINT_TO_POINTER (ref_count)); + + if (ref_count == 1) + { + tagged_container->tag_count++; + g_signal_emit (tagged_container, + gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0, + tagged_container->tag_count); + } +} + +static void +gimp_tagged_container_unref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag) +{ + gint ref_count; + + ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts, + tag)); + ref_count--; + + if (ref_count > 0) + { + g_hash_table_insert (tagged_container->tag_ref_counts, + g_object_ref (tag), + GINT_TO_POINTER (ref_count)); + } + else + { + if (g_hash_table_remove (tagged_container->tag_ref_counts, tag)) + { + tagged_container->tag_count--; + g_signal_emit (tagged_container, + gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0, + tagged_container->tag_count); + } + } +} + +static void +gimp_tagged_container_tag_count_changed (GimpTaggedContainer *container, + gint tag_count) +{ +} + +/** + * gimp_tagged_container_get_tag_count: + * @container: a #GimpTaggedContainer object. + * + * Get number of distinct tags that are currently assigned to all + * objects in the container. The count is independent of currently + * used filter, it is provided for all available objects (ie. empty + * filter). + * + * Return value: number of distinct tags assigned to all objects in the + * container. + **/ +gint +gimp_tagged_container_get_tag_count (GimpTaggedContainer *container) +{ + g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (container), 0); + + return container->tag_count; +} diff --git a/app/core/gimptaggedcontainer.h b/app/core/gimptaggedcontainer.h new file mode 100644 index 0000000..02beeeb --- /dev/null +++ b/app/core/gimptaggedcontainer.h @@ -0,0 +1,67 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimptaggedcontainer.h + * Copyright (C) 2008 Aurimas Juška + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TAGGED_CONTAINER_H__ +#define __GIMP_TAGGED_CONTAINER_H__ + + +#include "gimpfilteredcontainer.h" + + +#define GIMP_TYPE_TAGGED_CONTAINER (gimp_tagged_container_get_type ()) +#define GIMP_TAGGED_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TAGGED_CONTAINER, GimpTaggedContainer)) +#define GIMP_TAGGED_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TAGGED_CONTAINER, GimpTaggedContainerClass)) +#define GIMP_IS_TAGGED_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TAGGED_CONTAINER)) +#define GIMP_IS_TAGGED_CONTAINER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), GIMP_TYPE_TAGGED_CONTAINER)) +#define GIMP_TAGGED_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TAGGED_CONTAINER, GimpTaggedContainerClass)) + + +typedef struct _GimpTaggedContainerClass GimpTaggedContainerClass; + +struct _GimpTaggedContainer +{ + GimpFilteredContainer parent_instance; + + GList *filter; + GHashTable *tag_ref_counts; + gint tag_count; +}; + +struct _GimpTaggedContainerClass +{ + GimpFilteredContainerClass parent_class; + + void (* tag_count_changed) (GimpTaggedContainer *container, + gint count); +}; + + +GType gimp_tagged_container_get_type (void) G_GNUC_CONST; + +GimpContainer * gimp_tagged_container_new (GimpContainer *src_container); + +void gimp_tagged_container_set_filter (GimpTaggedContainer *tagged_container, + GList *tags); +const GList * gimp_tagged_container_get_filter (GimpTaggedContainer *tagged_container); + +gint gimp_tagged_container_get_tag_count (GimpTaggedContainer *container); + + +#endif /* __GIMP_TAGGED_CONTAINER_H__ */ diff --git a/app/core/gimptempbuf.c b/app/core/gimptempbuf.c new file mode 100644 index 0000000..2879be9 --- /dev/null +++ b/app/core/gimptempbuf.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 . + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include + +#include "core-types.h" + +#include "libgimpcolor/gimpcolor.h" + +#include "gimptempbuf.h" + + +#define LOCK_DATA_ALIGNMENT 16 + + +struct _GimpTempBuf +{ + gint ref_count; + gint width; + gint height; + const Babl *format; + guchar *data; +}; + +typedef struct +{ + const Babl *format; + GeglAccessMode access_mode; +} LockData; + +G_STATIC_ASSERT (sizeof (LockData) <= LOCK_DATA_ALIGNMENT); + + +/* local variables */ + +static guintptr gimp_temp_buf_total_memsize = 0; + + +/* public functions */ + +GimpTempBuf * +gimp_temp_buf_new (gint width, + gint height, + const Babl *format) +{ + GimpTempBuf *temp; + gint bpp; + + g_return_val_if_fail (format != NULL, NULL); + + bpp = babl_format_get_bytes_per_pixel (format); + + g_return_val_if_fail (width > 0 && height > 0 && bpp > 0, NULL); + g_return_val_if_fail (G_MAXSIZE / width / height / bpp > 0, NULL); + + temp = g_slice_new (GimpTempBuf); + + temp->ref_count = 1; + temp->width = width; + temp->height = height; + temp->format = format; + temp->data = gegl_malloc ((gsize) width * height * bpp); + + g_atomic_pointer_add (&gimp_temp_buf_total_memsize, + +gimp_temp_buf_get_memsize (temp)); + + return temp; +} + +GimpTempBuf * +gimp_temp_buf_new_from_pixbuf (GdkPixbuf *pixbuf, + const Babl *f_or_null) +{ + const Babl *format = f_or_null; + const Babl *fish = NULL; + GimpTempBuf *temp_buf; + const guchar *pixels; + gint width; + gint height; + gint rowstride; + gint bpp; + guchar *data; + gint i; + + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); + + if (! format) + format = gimp_pixbuf_get_format (pixbuf); + + pixels = gdk_pixbuf_get_pixels (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + temp_buf = gimp_temp_buf_new (width, height, format); + data = gimp_temp_buf_get_data (temp_buf); + + bpp = babl_format_get_bytes_per_pixel (format); + + if (gimp_pixbuf_get_format (pixbuf) != format) + fish = babl_fish (gimp_pixbuf_get_format (pixbuf), format); + + for (i = 0; i < height; i++) + { + if (fish) + babl_process (fish, pixels, data, width); + else + memcpy (data, pixels, width * bpp); + + data += width * bpp; + pixels += rowstride; + } + + return temp_buf; +} + +GimpTempBuf * +gimp_temp_buf_copy (const GimpTempBuf *src) +{ + GimpTempBuf *dest; + + g_return_val_if_fail (src != NULL, NULL); + + dest = gimp_temp_buf_new (src->width, src->height, src->format); + + memcpy (gimp_temp_buf_get_data (dest), + gimp_temp_buf_get_data (src), + gimp_temp_buf_get_data_size (src)); + + return dest; +} + +GimpTempBuf * +gimp_temp_buf_ref (const GimpTempBuf *buf) +{ + g_return_val_if_fail (buf != NULL, NULL); + + g_atomic_int_inc ((gint *) &buf->ref_count); + + return (GimpTempBuf *) buf; +} + +void +gimp_temp_buf_unref (const GimpTempBuf *buf) +{ + g_return_if_fail (buf != NULL); + g_return_if_fail (buf->ref_count > 0); + + if (g_atomic_int_dec_and_test ((gint *) &buf->ref_count)) + { + g_atomic_pointer_add (&gimp_temp_buf_total_memsize, + -gimp_temp_buf_get_memsize (buf)); + + + if (buf->data) + gegl_free (buf->data); + + g_slice_free (GimpTempBuf, (GimpTempBuf *) buf); + } +} + +GimpTempBuf * +gimp_temp_buf_scale (const GimpTempBuf *src, + gint new_width, + gint new_height) +{ + GimpTempBuf *dest; + const guchar *src_data; + guchar *dest_data; + gdouble x_ratio; + gdouble y_ratio; + gint bytes; + gint loop1; + gint loop2; + + g_return_val_if_fail (src != NULL, NULL); + g_return_val_if_fail (new_width > 0 && new_height > 0, NULL); + + if (new_width == src->width && new_height == src->height) + return gimp_temp_buf_copy (src); + + dest = gimp_temp_buf_new (new_width, + new_height, + src->format); + + src_data = gimp_temp_buf_get_data (src); + dest_data = gimp_temp_buf_get_data (dest); + + x_ratio = (gdouble) src->width / (gdouble) new_width; + y_ratio = (gdouble) src->height / (gdouble) new_height; + + bytes = babl_format_get_bytes_per_pixel (src->format); + + for (loop1 = 0 ; loop1 < new_height ; loop1++) + { + for (loop2 = 0 ; loop2 < new_width ; loop2++) + { + const guchar *src_pixel; + guchar *dest_pixel; + gint i; + + src_pixel = src_data + + (gint) (loop2 * x_ratio) * bytes + + (gint) (loop1 * y_ratio) * bytes * src->width; + + dest_pixel = dest_data + + (loop2 + loop1 * new_width) * bytes; + + for (i = 0 ; i < bytes; i++) + *dest_pixel++ = *src_pixel++; + } + } + + return dest; +} + +gint +gimp_temp_buf_get_width (const GimpTempBuf *buf) +{ + return buf->width; +} + +gint +gimp_temp_buf_get_height (const GimpTempBuf *buf) +{ + return buf->height; +} + +const Babl * +gimp_temp_buf_get_format (const GimpTempBuf *buf) +{ + return buf->format; +} + +void +gimp_temp_buf_set_format (GimpTempBuf *buf, + const Babl *format) +{ + g_return_if_fail (babl_format_get_bytes_per_pixel (buf->format) == + babl_format_get_bytes_per_pixel (format)); + + buf->format = format; +} + +guchar * +gimp_temp_buf_get_data (const GimpTempBuf *buf) +{ + return buf->data; +} + +gsize +gimp_temp_buf_get_data_size (const GimpTempBuf *buf) +{ + return (gsize) babl_format_get_bytes_per_pixel (buf->format) * + buf->width * buf->height; +} + +guchar * +gimp_temp_buf_data_clear (GimpTempBuf *buf) +{ + memset (buf->data, 0, gimp_temp_buf_get_data_size (buf)); + + return buf->data; +} + +gpointer +gimp_temp_buf_lock (const GimpTempBuf *buf, + const Babl *format, + GeglAccessMode access_mode) +{ + guchar *data; + LockData *lock_data; + gint n_pixels; + gint bpp; + + g_return_val_if_fail (buf != NULL, NULL); + + if (! format || format == buf->format) + return gimp_temp_buf_get_data (buf); + + n_pixels = buf->width * buf->height; + bpp = babl_format_get_bytes_per_pixel (format); + + data = gegl_scratch_alloc (LOCK_DATA_ALIGNMENT + n_pixels * bpp); + + if ((guintptr) data % LOCK_DATA_ALIGNMENT) + { + g_free (data); + + g_return_val_if_reached (NULL); + } + + lock_data = (LockData *) data; + lock_data->format = format; + lock_data->access_mode = access_mode; + + data += LOCK_DATA_ALIGNMENT; + + if (access_mode & GEGL_ACCESS_READ) + { + babl_process (babl_fish (buf->format, format), + gimp_temp_buf_get_data (buf), + data, + n_pixels); + } + + return data; +} + +void +gimp_temp_buf_unlock (const GimpTempBuf *buf, + gconstpointer data) +{ + LockData *lock_data; + + g_return_if_fail (buf != NULL); + g_return_if_fail (data != NULL); + + if (data == buf->data) + return; + + lock_data = (LockData *) ((const guint8 *) data - LOCK_DATA_ALIGNMENT); + + if (lock_data->access_mode & GEGL_ACCESS_WRITE) + { + babl_process (babl_fish (lock_data->format, buf->format), + data, + gimp_temp_buf_get_data (buf), + buf->width * buf->height); + } + + gegl_scratch_free (lock_data); +} + +gsize +gimp_temp_buf_get_memsize (const GimpTempBuf *buf) +{ + if (buf) + return (sizeof (GimpTempBuf) + gimp_temp_buf_get_data_size (buf)); + + return 0; +} + +GeglBuffer * +gimp_temp_buf_create_buffer (const GimpTempBuf *temp_buf) +{ + GeglBuffer *buffer; + + g_return_val_if_fail (temp_buf != NULL, NULL); + + buffer = + gegl_buffer_linear_new_from_data (gimp_temp_buf_get_data (temp_buf), + temp_buf->format, + GEGL_RECTANGLE (0, 0, + temp_buf->width, + temp_buf->height), + GEGL_AUTO_ROWSTRIDE, + (GDestroyNotify) gimp_temp_buf_unref, + gimp_temp_buf_ref (temp_buf)); + + g_object_set_data (G_OBJECT (buffer), + "gimp-temp-buf", (GimpTempBuf *) temp_buf); + + return buffer; +} + +GdkPixbuf * +gimp_temp_buf_create_pixbuf (const GimpTempBuf *temp_buf) +{ + GdkPixbuf *pixbuf; + const Babl *format; + const Babl *fish = NULL; + const guchar *data; + gint width; + gint height; + gint bpp; + guchar *pixels; + gint rowstride; + gint i; + + g_return_val_if_fail (temp_buf != NULL, NULL); + + data = gimp_temp_buf_get_data (temp_buf); + format = gimp_temp_buf_get_format (temp_buf); + width = gimp_temp_buf_get_width (temp_buf); + height = gimp_temp_buf_get_height (temp_buf); + bpp = babl_format_get_bytes_per_pixel (format); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + babl_format_has_alpha (format), + 8, width, height); + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + if (format != gimp_pixbuf_get_format (pixbuf)) + fish = babl_fish (format, gimp_pixbuf_get_format (pixbuf)); + + for (i = 0; i < height; i++) + { + if (fish) + babl_process (fish, data, pixels, width); + else + memcpy (pixels, data, width * bpp); + + data += width * bpp; + pixels += rowstride; + } + + return pixbuf; +} + +GimpTempBuf * +gimp_gegl_buffer_get_temp_buf (GeglBuffer *buffer) +{ + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + return g_object_get_data (G_OBJECT (buffer), "gimp-temp-buf"); +} + + +/* public functions (stats) */ + +guint64 +gimp_temp_buf_get_total_memsize (void) +{ + return gimp_temp_buf_total_memsize; +} diff --git a/app/core/gimptempbuf.h b/app/core/gimptempbuf.h new file mode 100644 index 0000000..d5228f5 --- /dev/null +++ b/app/core/gimptempbuf.h @@ -0,0 +1,67 @@ +/* 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 . + */ + +#ifndef __GIMP_TEMP_BUF_H__ +#define __GIMP_TEMP_BUF_H__ + + +GimpTempBuf * gimp_temp_buf_new (gint width, + gint height, + const Babl *format) G_GNUC_WARN_UNUSED_RESULT; +GimpTempBuf * gimp_temp_buf_new_from_pixbuf (GdkPixbuf *pixbuf, + const Babl *f_or_null) G_GNUC_WARN_UNUSED_RESULT; +GimpTempBuf * gimp_temp_buf_copy (const GimpTempBuf *src) G_GNUC_WARN_UNUSED_RESULT; + +GimpTempBuf * gimp_temp_buf_ref (const GimpTempBuf *buf); +void gimp_temp_buf_unref (const GimpTempBuf *buf); + +GimpTempBuf * gimp_temp_buf_scale (const GimpTempBuf *buf, + gint width, + gint height) G_GNUC_WARN_UNUSED_RESULT; + +gint gimp_temp_buf_get_width (const GimpTempBuf *buf); +gint gimp_temp_buf_get_height (const GimpTempBuf *buf); + +const Babl * gimp_temp_buf_get_format (const GimpTempBuf *buf); +void gimp_temp_buf_set_format (GimpTempBuf *buf, + const Babl *format); + +guchar * gimp_temp_buf_get_data (const GimpTempBuf *buf); +gsize gimp_temp_buf_get_data_size (const GimpTempBuf *buf); + +guchar * gimp_temp_buf_data_clear (GimpTempBuf *buf); + +gpointer gimp_temp_buf_lock (const GimpTempBuf *buf, + const Babl *format, + GeglAccessMode access_mode) G_GNUC_WARN_UNUSED_RESULT; +void gimp_temp_buf_unlock (const GimpTempBuf *buf, + gconstpointer data); + +gsize gimp_temp_buf_get_memsize (const GimpTempBuf *buf); + +GeglBuffer * gimp_temp_buf_create_buffer (const GimpTempBuf *temp_buf) G_GNUC_WARN_UNUSED_RESULT; +GdkPixbuf * gimp_temp_buf_create_pixbuf (const GimpTempBuf *temp_buf) G_GNUC_WARN_UNUSED_RESULT; + +GimpTempBuf * gimp_gegl_buffer_get_temp_buf (GeglBuffer *buffer); + + +/* stats */ + +guint64 gimp_temp_buf_get_total_memsize (void); + + +#endif /* __GIMP_TEMP_BUF_H__ */ diff --git a/app/core/gimptemplate.c b/app/core/gimptemplate.c new file mode 100644 index 0000000..1459493 --- /dev/null +++ b/app/core/gimptemplate.c @@ -0,0 +1,595 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimptemplate.c + * Copyright (C) 2003 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains the definition of the image template objects. + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimpimage.h" +#include "gimpprojection.h" +#include "gimptemplate.h" + +#include "gimp-intl.h" + + +#define DEFAULT_RESOLUTION 300.0 + +enum +{ + PROP_0, + PROP_WIDTH, + PROP_HEIGHT, + PROP_UNIT, + PROP_XRESOLUTION, + PROP_YRESOLUTION, + PROP_RESOLUTION_UNIT, + PROP_BASE_TYPE, + PROP_PRECISION, + PROP_COMPONENT_TYPE, + PROP_LINEAR, + PROP_COLOR_MANAGED, + PROP_COLOR_PROFILE, + PROP_FILL_TYPE, + PROP_COMMENT, + PROP_FILENAME +}; + + +typedef struct _GimpTemplatePrivate GimpTemplatePrivate; + +struct _GimpTemplatePrivate +{ + gint width; + gint height; + GimpUnit unit; + + gdouble xresolution; + gdouble yresolution; + GimpUnit resolution_unit; + + GimpImageBaseType base_type; + GimpPrecision precision; + + gboolean color_managed; + GFile *color_profile; + + GimpFillType fill_type; + + gchar *comment; + gchar *filename; + + guint64 initial_size; +}; + +#define GET_PRIVATE(template) ((GimpTemplatePrivate *) gimp_template_get_instance_private ((GimpTemplate *) (template))) + + +static void gimp_template_finalize (GObject *object); +static void gimp_template_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_template_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_template_notify (GObject *object, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_CODE (GimpTemplate, gimp_template, GIMP_TYPE_VIEWABLE, + G_ADD_PRIVATE (GimpTemplate) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)) + +#define parent_class gimp_template_parent_class + + +static void +gimp_template_class_init (GimpTemplateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->finalize = gimp_template_finalize; + + object_class->set_property = gimp_template_set_property; + object_class->get_property = gimp_template_get_property; + object_class->notify = gimp_template_notify; + + viewable_class->default_icon_name = "gimp-template"; + viewable_class->name_editable = TRUE; + + GIMP_CONFIG_PROP_INT (object_class, PROP_WIDTH, + "width", + _("Width"), + NULL, + GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, + GIMP_DEFAULT_IMAGE_WIDTH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_INT (object_class, PROP_HEIGHT, + "height", + _("Height"), + NULL, + GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, + GIMP_DEFAULT_IMAGE_HEIGHT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_UNIT (object_class, PROP_UNIT, + "unit", + _("Unit"), + _("The unit used for coordinate display " + "when not in dot-for-dot mode."), + TRUE, FALSE, GIMP_UNIT_PIXEL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_RESOLUTION (object_class, PROP_XRESOLUTION, + "xresolution", + _("Resolution X"), + _("The horizontal image resolution."), + DEFAULT_RESOLUTION, + GIMP_PARAM_STATIC_STRINGS | + GIMP_TEMPLATE_PARAM_COPY_FIRST); + + GIMP_CONFIG_PROP_RESOLUTION (object_class, PROP_YRESOLUTION, + "yresolution", + _("Resolution X"), + _("The vertical image resolution."), + DEFAULT_RESOLUTION, + GIMP_PARAM_STATIC_STRINGS | + GIMP_TEMPLATE_PARAM_COPY_FIRST); + + GIMP_CONFIG_PROP_UNIT (object_class, PROP_RESOLUTION_UNIT, + "resolution-unit", + _("Resolution unit"), + NULL, + FALSE, FALSE, GIMP_UNIT_INCH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_BASE_TYPE, + "image-type", /* serialized name */ + _("Image type"), + NULL, + GIMP_TYPE_IMAGE_BASE_TYPE, GIMP_RGB, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_PRECISION, + "precision", + _("Precision"), + NULL, + GIMP_TYPE_PRECISION, GIMP_PRECISION_U8_GAMMA, + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, PROP_COMPONENT_TYPE, + g_param_spec_enum ("component-type", + _("Precision"), + NULL, + GIMP_TYPE_COMPONENT_TYPE, + GIMP_COMPONENT_TYPE_U8, + G_PARAM_READWRITE | + GIMP_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_LINEAR, + g_param_spec_boolean ("linear", + _("Gamma"), + NULL, + FALSE, + G_PARAM_READWRITE | + GIMP_PARAM_STATIC_STRINGS)); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COLOR_MANAGED, + "color-managed", + _("Color managed"), + _("Whether the image is color managed. " + "Disabling color management is equivalent to " + "choosing a built-in sRGB profile. Better " + "leave color management enabled."), + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_COLOR_PROFILE, + "color-profile", + _("Color profile"), + NULL, + G_TYPE_FILE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_FILL_TYPE, + "fill-type", + _("Fill type"), + NULL, + GIMP_TYPE_FILL_TYPE, GIMP_FILL_BACKGROUND, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_COMMENT, + "comment", + _("Comment"), + NULL, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_FILENAME, + "filename", + _("Filename"), + NULL, + NULL, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_template_init (GimpTemplate *template) +{ +} + +static void +gimp_template_finalize (GObject *object) +{ + GimpTemplatePrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->color_profile); + g_clear_pointer (&private->comment, g_free); + g_clear_pointer (&private->filename, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_template_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpTemplatePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_WIDTH: + private->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_int (value); + break; + case PROP_UNIT: + private->unit = g_value_get_int (value); + break; + case PROP_XRESOLUTION: + private->xresolution = g_value_get_double (value); + break; + case PROP_YRESOLUTION: + private->yresolution = g_value_get_double (value); + break; + case PROP_RESOLUTION_UNIT: + private->resolution_unit = g_value_get_int (value); + break; + case PROP_BASE_TYPE: + private->base_type = g_value_get_enum (value); + break; + case PROP_PRECISION: + private->precision = g_value_get_enum (value); + g_object_notify (object, "component-type"); + g_object_notify (object, "linear"); + break; + case PROP_COMPONENT_TYPE: + private->precision = + gimp_babl_precision (g_value_get_enum (value), + gimp_babl_linear (private->precision)); + g_object_notify (object, "precision"); + break; + case PROP_LINEAR: + private->precision = + gimp_babl_precision (gimp_babl_component_type (private->precision), + g_value_get_boolean (value)); + g_object_notify (object, "precision"); + break; + case PROP_COLOR_MANAGED: + private->color_managed = g_value_get_boolean (value); + break; + case PROP_COLOR_PROFILE: + if (private->color_profile) + g_object_unref (private->color_profile); + private->color_profile = g_value_dup_object (value); + break; + case PROP_FILL_TYPE: + private->fill_type = g_value_get_enum (value); + break; + case PROP_COMMENT: + if (private->comment) + g_free (private->comment); + private->comment = g_value_dup_string (value); + break; + case PROP_FILENAME: + if (private->filename) + g_free (private->filename); + private->filename = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_template_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpTemplatePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, private->height); + break; + case PROP_UNIT: + g_value_set_int (value, private->unit); + break; + case PROP_XRESOLUTION: + g_value_set_double (value, private->xresolution); + break; + case PROP_YRESOLUTION: + g_value_set_double (value, private->yresolution); + break; + case PROP_RESOLUTION_UNIT: + g_value_set_int (value, private->resolution_unit); + break; + case PROP_BASE_TYPE: + g_value_set_enum (value, private->base_type); + break; + case PROP_PRECISION: + g_value_set_enum (value, private->precision); + break; + case PROP_COMPONENT_TYPE: + g_value_set_enum (value, gimp_babl_component_type (private->precision)); + break; + case PROP_LINEAR: + g_value_set_boolean (value, gimp_babl_linear (private->precision)); + break; + case PROP_COLOR_MANAGED: + g_value_set_boolean (value, private->color_managed); + break; + case PROP_COLOR_PROFILE: + g_value_set_object (value, private->color_profile); + break; + case PROP_FILL_TYPE: + g_value_set_enum (value, private->fill_type); + break; + case PROP_COMMENT: + g_value_set_string (value, private->comment); + break; + case PROP_FILENAME: + g_value_set_string (value, private->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_template_notify (GObject *object, + GParamSpec *pspec) +{ + GimpTemplatePrivate *private = GET_PRIVATE (object); + const Babl *format; + gint bytes; + + if (G_OBJECT_CLASS (parent_class)->notify) + G_OBJECT_CLASS (parent_class)->notify (object, pspec); + + /* the initial layer */ + format = gimp_babl_format (private->base_type, + private->precision, + private->fill_type == GIMP_FILL_TRANSPARENT); + bytes = babl_format_get_bytes_per_pixel (format); + + /* the selection */ + format = gimp_babl_mask_format (private->precision); + bytes += babl_format_get_bytes_per_pixel (format); + + private->initial_size = ((guint64) bytes * + (guint64) private->width * + (guint64) private->height); + + private->initial_size += + gimp_projection_estimate_memsize (private->base_type, + gimp_babl_component_type (private->precision), + private->width, private->height); +} + + +/* public functions */ + +GimpTemplate * +gimp_template_new (const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (GIMP_TYPE_TEMPLATE, + "name", name, + NULL); +} + +void +gimp_template_set_from_image (GimpTemplate *template, + GimpImage *image) +{ + gdouble xresolution; + gdouble yresolution; + GimpImageBaseType base_type; + const GimpParasite *parasite; + gchar *comment = NULL; + + g_return_if_fail (GIMP_IS_TEMPLATE (template)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + gimp_image_get_resolution (image, &xresolution, &yresolution); + + base_type = gimp_image_get_base_type (image); + + if (base_type == GIMP_INDEXED) + base_type = GIMP_RGB; + + parasite = gimp_image_parasite_find (image, "gimp-comment"); + if (parasite) + comment = g_strndup (gimp_parasite_data (parasite), + gimp_parasite_data_size (parasite)); + + g_object_set (template, + "width", gimp_image_get_width (image), + "height", gimp_image_get_height (image), + "xresolution", xresolution, + "yresolution", yresolution, + "resolution-unit", gimp_image_get_unit (image), + "image-type", base_type, + "precision", gimp_image_get_precision (image), + "comment", comment, + NULL); + + if (comment) + g_free (comment); +} + +gint +gimp_template_get_width (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), 0); + + return GET_PRIVATE (template)->width; +} + +gint +gimp_template_get_height (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), 0); + + return GET_PRIVATE (template)->height; +} + +GimpUnit +gimp_template_get_unit (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), GIMP_UNIT_INCH); + + return GET_PRIVATE (template)->unit; +} + +gdouble +gimp_template_get_resolution_x (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), 1.0); + + return GET_PRIVATE (template)->xresolution; +} + +gdouble +gimp_template_get_resolution_y (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), 1.0); + + return GET_PRIVATE (template)->yresolution; +} + +GimpUnit +gimp_template_get_resolution_unit (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), GIMP_UNIT_INCH); + + return GET_PRIVATE (template)->resolution_unit; +} + +GimpImageBaseType +gimp_template_get_base_type (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), GIMP_RGB); + + return GET_PRIVATE (template)->base_type; +} + +GimpPrecision +gimp_template_get_precision (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), GIMP_PRECISION_U8_GAMMA); + + return GET_PRIVATE (template)->precision; +} + +gboolean +gimp_template_get_color_managed (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), FALSE); + + return GET_PRIVATE (template)->color_managed; +} + +GimpColorProfile * +gimp_template_get_color_profile (GimpTemplate *template) +{ + GimpTemplatePrivate *private; + + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), FALSE); + + private = GET_PRIVATE (template); + + if (private->color_profile) + return gimp_color_profile_new_from_file (private->color_profile, NULL); + + return NULL; +} + +GimpFillType +gimp_template_get_fill_type (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), GIMP_FILL_BACKGROUND); + + return GET_PRIVATE (template)->fill_type; +} + +const gchar * +gimp_template_get_comment (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), NULL); + + return GET_PRIVATE (template)->comment; +} + +guint64 +gimp_template_get_initial_size (GimpTemplate *template) +{ + g_return_val_if_fail (GIMP_IS_TEMPLATE (template), 0); + + return GET_PRIVATE (template)->initial_size; +} diff --git a/app/core/gimptemplate.h b/app/core/gimptemplate.h new file mode 100644 index 0000000..671a247 --- /dev/null +++ b/app/core/gimptemplate.h @@ -0,0 +1,97 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimptemplate.h + * Copyright (C) 2003 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TEMPLATE_H__ +#define __GIMP_TEMPLATE_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TEMPLATE_PARAM_COPY_FIRST (1 << (8 + G_PARAM_USER_SHIFT)) + +#ifdef GIMP_UNSTABLE +/* Uncommon ratio, with at least one odd value, to encourage testing + * GIMP with unusual numbers. + * It also has to be a higher resolution, to push GIMP a little further + * in tests. */ +#define GIMP_DEFAULT_IMAGE_WIDTH 2001 +#define GIMP_DEFAULT_IMAGE_HEIGHT 1984 +#else +/* 1366x768 is the most common screen resolution in 2016. + * 1920x1080 is the second most common. + * Since GIMP targets advanced graphics artists, let's go for the + * highest common dimension. + */ +#define GIMP_DEFAULT_IMAGE_WIDTH 1920 +#define GIMP_DEFAULT_IMAGE_HEIGHT 1080 +#endif + + +#define GIMP_TYPE_TEMPLATE (gimp_template_get_type ()) +#define GIMP_TEMPLATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEMPLATE, GimpTemplate)) +#define GIMP_TEMPLATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TEMPLATE, GimpTemplateClass)) +#define GIMP_IS_TEMPLATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEMPLATE)) +#define GIMP_IS_TEMPLATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TEMPLATE)) +#define GIMP_TEMPLATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TEMPLATE, GimpTemplateClass)) + + +typedef struct _GimpTemplateClass GimpTemplateClass; + +struct _GimpTemplate +{ + GimpViewable parent_instance; +}; + +struct _GimpTemplateClass +{ + GimpViewableClass parent_instance; +}; + + +GType gimp_template_get_type (void) G_GNUC_CONST; + +GimpTemplate * gimp_template_new (const gchar *name); + +void gimp_template_set_from_image (GimpTemplate *template, + GimpImage *image); + +gint gimp_template_get_width (GimpTemplate *template); +gint gimp_template_get_height (GimpTemplate *template); +GimpUnit gimp_template_get_unit (GimpTemplate *template); + +gdouble gimp_template_get_resolution_x (GimpTemplate *template); +gdouble gimp_template_get_resolution_y (GimpTemplate *template); +GimpUnit gimp_template_get_resolution_unit (GimpTemplate *template); + +GimpImageBaseType gimp_template_get_base_type (GimpTemplate *template); +GimpPrecision gimp_template_get_precision (GimpTemplate *template); + +gboolean gimp_template_get_color_managed (GimpTemplate *template); +GimpColorProfile * gimp_template_get_color_profile (GimpTemplate *template); + +GimpFillType gimp_template_get_fill_type (GimpTemplate *template); + +const gchar * gimp_template_get_comment (GimpTemplate *template); + +guint64 gimp_template_get_initial_size (GimpTemplate *template); + + +#endif /* __GIMP_TEMPLATE__ */ diff --git a/app/core/gimptilehandlerprojectable.c b/app/core/gimptilehandlerprojectable.c new file mode 100644 index 0000000..4b9e86b --- /dev/null +++ b/app/core/gimptilehandlerprojectable.c @@ -0,0 +1,91 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpprojectable.h" + +#include "gimptilehandlerprojectable.h" + + +static void gimp_tile_handler_projectable_begin_validate (GimpTileHandlerValidate *validate); +static void gimp_tile_handler_projectable_end_validate (GimpTileHandlerValidate *validate); + + +G_DEFINE_TYPE (GimpTileHandlerProjectable, gimp_tile_handler_projectable, + GIMP_TYPE_TILE_HANDLER_VALIDATE) + +#define parent_class gimp_tile_handler_projectable_parent_class + + +static void +gimp_tile_handler_projectable_class_init (GimpTileHandlerProjectableClass *klass) +{ + GimpTileHandlerValidateClass *validate_class; + + validate_class = GIMP_TILE_HANDLER_VALIDATE_CLASS (klass); + + validate_class->begin_validate = gimp_tile_handler_projectable_begin_validate; + validate_class->end_validate = gimp_tile_handler_projectable_end_validate; +} + +static void +gimp_tile_handler_projectable_init (GimpTileHandlerProjectable *projectable) +{ +} + +static void +gimp_tile_handler_projectable_begin_validate (GimpTileHandlerValidate *validate) +{ + GimpTileHandlerProjectable *handler = GIMP_TILE_HANDLER_PROJECTABLE (validate); + + GIMP_TILE_HANDLER_VALIDATE_CLASS (parent_class)->begin_validate (validate); + + gimp_projectable_begin_render (handler->projectable); +} + +static void +gimp_tile_handler_projectable_end_validate (GimpTileHandlerValidate *validate) +{ + GimpTileHandlerProjectable *handler = GIMP_TILE_HANDLER_PROJECTABLE (validate); + + gimp_projectable_end_render (handler->projectable); + + GIMP_TILE_HANDLER_VALIDATE_CLASS (parent_class)->end_validate (validate); +} + +GeglTileHandler * +gimp_tile_handler_projectable_new (GimpProjectable *projectable) +{ + GimpTileHandlerProjectable *handler; + + g_return_val_if_fail (GIMP_IS_PROJECTABLE (projectable), NULL); + + handler = g_object_new (GIMP_TYPE_TILE_HANDLER_PROJECTABLE, NULL); + + GIMP_TILE_HANDLER_VALIDATE (handler)->graph = + g_object_ref (gimp_projectable_get_graph (projectable)); + + handler->projectable = projectable; + + return GEGL_TILE_HANDLER (handler); +} diff --git a/app/core/gimptilehandlerprojectable.h b/app/core/gimptilehandlerprojectable.h new file mode 100644 index 0000000..57af4b6 --- /dev/null +++ b/app/core/gimptilehandlerprojectable.h @@ -0,0 +1,64 @@ +/* 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 . + */ + +#ifndef __GIMP_TILE_HANDLER_PROJECTABLE_H__ +#define __GIMP_TILE_HANDLER_PROJECTABLE_H__ + + +#include "gegl/gimptilehandlervalidate.h" + + +/*** + * GimpTileHandlerProjectable is a GeglTileHandler that renders a + * projectable, calling the projectable's begin_render() and end_render() + * before/after the actual rendering. + * + * Note that the tile handler does not own a reference to the projectable. + * It's the user's responsibility to manage the handler's and projectable's + * lifetime. + */ + +#define GIMP_TYPE_TILE_HANDLER_PROJECTABLE (gimp_tile_handler_projectable_get_type ()) +#define GIMP_TILE_HANDLER_PROJECTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTABLE, GimpTileHandlerProjectable)) +#define GIMP_TILE_HANDLER_PROJECTABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TILE_HANDLER_PROJECTABLE, GimpTileHandlerProjectableClass)) +#define GIMP_IS_TILE_HANDLER_PROJECTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTABLE)) +#define GIMP_IS_TILE_HANDLER_PROJECTABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TILE_HANDLER_PROJECTABLE)) +#define GIMP_TILE_HANDLER_PROJECTABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TILE_HANDLER_PROJECTABLE, GimpTileHandlerProjectableClass)) + + +typedef struct _GimpTileHandlerProjectable GimpTileHandlerProjectable; +typedef struct _GimpTileHandlerProjectableClass GimpTileHandlerProjectableClass; + +struct _GimpTileHandlerProjectable +{ + GimpTileHandlerValidate parent_instance; + + GimpProjectable *projectable; +}; + +struct _GimpTileHandlerProjectableClass +{ + GimpTileHandlerValidateClass parent_class; +}; + + +GType gimp_tile_handler_projectable_get_type (void) G_GNUC_CONST; + +GeglTileHandler * gimp_tile_handler_projectable_new (GimpProjectable *projectable); + + +#endif /* __GIMP_TILE_HANDLER_PROJECTABLE_H__ */ diff --git a/app/core/gimptoolgroup.c b/app/core/gimptoolgroup.c new file mode 100644 index 0000000..aa8e3bc --- /dev/null +++ b/app/core/gimptoolgroup.c @@ -0,0 +1,413 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgroup.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimplist.h" +#include "gimpmarshal.h" +#include "gimptoolgroup.h" +#include "gimptoolinfo.h" + +#include "gimp-intl.h" + + +enum +{ + ACTIVE_TOOL_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_ACTIVE_TOOL, + PROP_CHILDREN +}; + + +struct _GimpToolGroupPrivate +{ + gchar *active_tool; + GimpContainer *children; +}; + + +/* local function prototypes */ + + +static void gimp_tool_group_finalize (GObject *object); +static void gimp_tool_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_tool_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gint64 gimp_tool_group_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gchar * gimp_tool_group_get_description (GimpViewable *viewable, + gchar **tooltip); +static GimpContainer * gimp_tool_group_get_children (GimpViewable *viewable); +static void gimp_tool_group_set_expanded (GimpViewable *viewable, + gboolean expand); +static gboolean gimp_tool_group_get_expanded (GimpViewable *viewable); + +static void gimp_tool_group_child_add (GimpContainer *container, + GimpToolInfo *tool_info, + GimpToolGroup *tool_group); +static void gimp_tool_group_child_remove (GimpContainer *container, + GimpToolInfo *tool_info, + GimpToolGroup *tool_group); + +static void gimp_tool_group_shown_changed (GimpToolItem *tool_item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolGroup, gimp_tool_group, GIMP_TYPE_TOOL_ITEM) + +#define parent_class gimp_tool_group_parent_class + +static guint gimp_tool_group_signals[LAST_SIGNAL] = { 0 }; + + +/* private functions */ + +static void +gimp_tool_group_class_init (GimpToolGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + GimpToolItemClass *tool_item_class = GIMP_TOOL_ITEM_CLASS (klass); + + gimp_tool_group_signals[ACTIVE_TOOL_CHANGED] = + g_signal_new ("active-tool-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolGroupClass, active_tool_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_tool_group_finalize; + object_class->get_property = gimp_tool_group_get_property; + object_class->set_property = gimp_tool_group_set_property; + + gimp_object_class->get_memsize = gimp_tool_group_get_memsize; + + viewable_class->default_icon_name = "folder"; + viewable_class->get_description = gimp_tool_group_get_description; + viewable_class->get_children = gimp_tool_group_get_children; + viewable_class->get_expanded = gimp_tool_group_get_expanded; + viewable_class->set_expanded = gimp_tool_group_set_expanded; + + tool_item_class->shown_changed = gimp_tool_group_shown_changed; + + GIMP_CONFIG_PROP_STRING (object_class, PROP_ACTIVE_TOOL, + "active-tool", NULL, NULL, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_CHILDREN, + "children", NULL, NULL, + GIMP_TYPE_CONTAINER, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_AGGREGATE); +} + +static void +gimp_tool_group_init (GimpToolGroup *tool_group) +{ + tool_group->priv = gimp_tool_group_get_instance_private (tool_group); + + tool_group->priv->children = g_object_new ( + GIMP_TYPE_LIST, + "children-type", GIMP_TYPE_TOOL_INFO, + "append", TRUE, + NULL); + + g_signal_connect (tool_group->priv->children, "add", + G_CALLBACK (gimp_tool_group_child_add), + tool_group); + g_signal_connect (tool_group->priv->children, "remove", + G_CALLBACK (gimp_tool_group_child_remove), + tool_group); +} + +static void +gimp_tool_group_finalize (GObject *object) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object); + + g_clear_pointer (&tool_group->priv->active_tool, g_free); + + g_clear_object (&tool_group->priv->children); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object); + + switch (property_id) + { + case PROP_ACTIVE_TOOL: + g_value_set_string (value, tool_group->priv->active_tool); + break; + + case PROP_CHILDREN: + g_value_set_object (value, tool_group->priv->children); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_group_set_property_add_tool (GimpToolInfo *tool_info, + GimpToolGroup *tool_group) +{ + gimp_container_add (tool_group->priv->children, GIMP_OBJECT (tool_info)); +} + +static void +gimp_tool_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object); + + switch (property_id) + { + case PROP_ACTIVE_TOOL: + g_free (tool_group->priv->active_tool); + + tool_group->priv->active_tool = g_value_dup_string (value); + break; + + case PROP_CHILDREN: + { + GimpContainer *container = g_value_get_object (value); + + gimp_container_clear (tool_group->priv->children); + + if (! container) + break; + + gimp_container_foreach (container, + (GFunc) gimp_tool_group_set_property_add_tool, + tool_group); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_tool_group_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (tool_group->priv->children), + gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gchar * +gimp_tool_group_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + /* Translators: this is a noun */ + return g_strdup (C_("tool-item", "Group")); +} + +static GimpContainer * +gimp_tool_group_get_children (GimpViewable *viewable) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (viewable); + + return tool_group->priv->children; +} + +static void +gimp_tool_group_set_expanded (GimpViewable *viewable, + gboolean expand) +{ + if (! expand) + gimp_viewable_expanded_changed (viewable); +} + +static gboolean +gimp_tool_group_get_expanded (GimpViewable *viewable) +{ + return TRUE; +} + +static void +gimp_tool_group_child_add (GimpContainer *container, + GimpToolInfo *tool_info, + GimpToolGroup *tool_group) +{ + g_return_if_fail ( + gimp_viewable_get_parent (GIMP_VIEWABLE (tool_info)) == NULL); + + gimp_viewable_set_parent (GIMP_VIEWABLE (tool_info), + GIMP_VIEWABLE (tool_group)); + + if (! tool_group->priv->active_tool) + gimp_tool_group_set_active_tool_info (tool_group, tool_info); +} + +static void +gimp_tool_group_child_remove (GimpContainer *container, + GimpToolInfo *tool_info, + GimpToolGroup *tool_group) +{ + gimp_viewable_set_parent (GIMP_VIEWABLE (tool_info), NULL); + + if (! g_strcmp0 (tool_group->priv->active_tool, + gimp_object_get_name (GIMP_OBJECT (tool_info)))) + { + GimpToolInfo *active_tool_info = NULL; + + if (! gimp_container_is_empty (tool_group->priv->children)) + { + active_tool_info = GIMP_TOOL_INFO ( + gimp_container_get_first_child (tool_group->priv->children)); + } + + gimp_tool_group_set_active_tool_info (tool_group, active_tool_info); + } +} + +static void +gimp_tool_group_shown_changed (GimpToolItem *tool_item) +{ + GimpToolGroup *tool_group = GIMP_TOOL_GROUP (tool_item); + GList *iter; + + if (GIMP_TOOL_ITEM_CLASS (parent_class)->shown_changed) + GIMP_TOOL_ITEM_CLASS (parent_class)->shown_changed (tool_item); + + for (iter = GIMP_LIST (tool_group->priv->children)->queue->head; + iter; + iter = g_list_next (iter)) + { + GimpToolItem *tool_item = iter->data; + + if (gimp_tool_item_get_visible (tool_item)) + gimp_tool_item_shown_changed (tool_item); + } +} + + +/* public functions */ + +GimpToolGroup * +gimp_tool_group_new (void) +{ + GimpToolGroup *tool_group; + + tool_group = g_object_new (GIMP_TYPE_TOOL_GROUP, NULL); + + gimp_object_set_static_name (GIMP_OBJECT (tool_group), "tool group"); + + return tool_group; +} + +void +gimp_tool_group_set_active_tool (GimpToolGroup *tool_group, + const gchar *tool_name) +{ + g_return_if_fail (GIMP_IS_TOOL_GROUP (tool_group)); + + if (g_strcmp0 (tool_group->priv->active_tool, tool_name)) + { + g_return_if_fail (tool_name == NULL || + gimp_container_get_child_by_name ( + tool_group->priv->children, tool_name) != NULL); + + g_free (tool_group->priv->active_tool); + + tool_group->priv->active_tool = g_strdup (tool_name);; + + g_signal_emit (tool_group, + gimp_tool_group_signals[ACTIVE_TOOL_CHANGED], 0); + + g_object_notify (G_OBJECT (tool_group), "active-tool"); + } +} + +const gchar * +gimp_tool_group_get_active_tool (GimpToolGroup *tool_group) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GROUP (tool_group), NULL); + + return tool_group->priv->active_tool; +} + +void +gimp_tool_group_set_active_tool_info (GimpToolGroup *tool_group, + GimpToolInfo *tool_info) +{ + g_return_if_fail (GIMP_IS_TOOL_GROUP (tool_group)); + g_return_if_fail (tool_info == NULL || GIMP_IS_TOOL_INFO (tool_info)); + + gimp_tool_group_set_active_tool ( + tool_group, + tool_info ? gimp_object_get_name (GIMP_OBJECT (tool_info)) : NULL); +} + +GimpToolInfo * +gimp_tool_group_get_active_tool_info (GimpToolGroup *tool_group) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GROUP (tool_group), NULL); + + return GIMP_TOOL_INFO ( + gimp_container_get_child_by_name (tool_group->priv->children, + tool_group->priv->active_tool)); +} diff --git a/app/core/gimptoolgroup.h b/app/core/gimptoolgroup.h new file mode 100644 index 0000000..ef4d979 --- /dev/null +++ b/app/core/gimptoolgroup.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgroup.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_TOOL_GROUP_H__ +#define __GIMP_TOOL_GROUP_H__ + + +#include "gimptoolitem.h" + + +#define GIMP_TYPE_TOOL_GROUP (gimp_tool_group_get_type ()) +#define GIMP_TOOL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_GROUP, GimpToolGroup)) +#define GIMP_TOOL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_GROUP, GimpToolGroupClass)) +#define GIMP_IS_TOOL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_GROUP)) +#define GIMP_IS_TOOL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_GROUP)) +#define GIMP_TOOL_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_GROUP, GimpToolGroupClass)) + + +typedef struct _GimpToolGroupPrivate GimpToolGroupPrivate; +typedef struct _GimpToolGroupClass GimpToolGroupClass; + +struct _GimpToolGroup +{ + GimpToolItem parent_instance; + + GimpToolGroupPrivate *priv; +}; + +struct _GimpToolGroupClass +{ + GimpToolItemClass parent_class; + + /* signals */ + void (* active_tool_changed) (GimpToolGroup *tool_group); +}; + + +GType gimp_tool_group_get_type (void) G_GNUC_CONST; + +GimpToolGroup * gimp_tool_group_new (void); + +void gimp_tool_group_set_active_tool (GimpToolGroup *tool_group, + const gchar *tool_name); +const gchar * gimp_tool_group_get_active_tool (GimpToolGroup *tool_group); + +void gimp_tool_group_set_active_tool_info (GimpToolGroup *tool_group, + GimpToolInfo *tool_info); +GimpToolInfo * gimp_tool_group_get_active_tool_info (GimpToolGroup *tool_group); + + +#endif /* __GIMP_TOOL_GROUP_H__ */ diff --git a/app/core/gimptoolinfo.c b/app/core/gimptoolinfo.c new file mode 100644 index 0000000..34d93c3 --- /dev/null +++ b/app/core/gimptoolinfo.c @@ -0,0 +1,263 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpdatafactory.h" +#include "gimpfilteredcontainer.h" +#include "gimppaintinfo.h" +#include "gimptoolinfo.h" +#include "gimptooloptions.h" +#include "gimptoolpreset.h" + + +static void gimp_tool_info_dispose (GObject *object); +static void gimp_tool_info_finalize (GObject *object); + +static gchar * gimp_tool_info_get_description (GimpViewable *viewable, + gchar **tooltip); + + +G_DEFINE_TYPE (GimpToolInfo, gimp_tool_info, GIMP_TYPE_TOOL_ITEM) + +#define parent_class gimp_tool_info_parent_class + + +static void +gimp_tool_info_class_init (GimpToolInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->dispose = gimp_tool_info_dispose; + object_class->finalize = gimp_tool_info_finalize; + + viewable_class->get_description = gimp_tool_info_get_description; +} + +static void +gimp_tool_info_init (GimpToolInfo *tool_info) +{ +} + +static void +gimp_tool_info_dispose (GObject *object) +{ + GimpToolInfo *tool_info = GIMP_TOOL_INFO (object); + + if (tool_info->tool_options) + { + g_object_run_dispose (G_OBJECT (tool_info->tool_options)); + g_clear_object (&tool_info->tool_options); + } + + g_clear_object (&tool_info->presets); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_tool_info_finalize (GObject *object) +{ + GimpToolInfo *tool_info = GIMP_TOOL_INFO (object); + + g_clear_pointer (&tool_info->label, g_free); + g_clear_pointer (&tool_info->tooltip, g_free); + g_clear_pointer (&tool_info->menu_label, g_free); + g_clear_pointer (&tool_info->menu_accel, g_free); + g_clear_pointer (&tool_info->help_domain, g_free); + g_clear_pointer (&tool_info->help_id, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gchar * +gimp_tool_info_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + GimpToolInfo *tool_info = GIMP_TOOL_INFO (viewable); + + if (tooltip) + *tooltip = g_strdup (tool_info->tooltip); + + return g_strdup (tool_info->label); +} + +static gboolean +gimp_tool_info_filter_preset (GimpObject *object, + gpointer user_data) +{ + GimpToolPreset *preset = GIMP_TOOL_PRESET (object); + GimpToolInfo *tool_info = user_data; + + return preset->tool_options->tool_info == tool_info; +} + +GimpToolInfo * +gimp_tool_info_new (Gimp *gimp, + GType tool_type, + GType tool_options_type, + GimpContextPropMask context_props, + const gchar *identifier, + const gchar *label, + const gchar *tooltip, + const gchar *menu_label, + const gchar *menu_accel, + const gchar *help_domain, + const gchar *help_id, + const gchar *paint_core_name, + const gchar *icon_name) +{ + GimpPaintInfo *paint_info; + GimpToolInfo *tool_info; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (identifier != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + g_return_val_if_fail (tooltip != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (paint_core_name != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + + paint_info = (GimpPaintInfo *) + gimp_container_get_child_by_name (gimp->paint_info_list, paint_core_name); + + g_return_val_if_fail (GIMP_IS_PAINT_INFO (paint_info), NULL); + + tool_info = g_object_new (GIMP_TYPE_TOOL_INFO, + "name", identifier, + "icon-name", icon_name, + NULL); + + tool_info->gimp = gimp; + tool_info->tool_type = tool_type; + tool_info->tool_options_type = tool_options_type; + tool_info->context_props = context_props; + + tool_info->label = g_strdup (label); + tool_info->tooltip = g_strdup (tooltip); + + tool_info->menu_label = g_strdup (menu_label); + tool_info->menu_accel = g_strdup (menu_accel); + + tool_info->help_domain = g_strdup (help_domain); + tool_info->help_id = g_strdup (help_id); + + tool_info->paint_info = paint_info; + + if (tool_info->tool_options_type == paint_info->paint_options_type) + { + tool_info->tool_options = g_object_ref (GIMP_TOOL_OPTIONS (paint_info->paint_options)); + } + else + { + tool_info->tool_options = g_object_new (tool_info->tool_options_type, + "gimp", gimp, + "name", identifier, + NULL); + } + + g_object_set (tool_info->tool_options, + "tool", tool_info, + "tool-info", tool_info, NULL); + + gimp_tool_options_set_gui_mode (tool_info->tool_options, TRUE); + + if (tool_info->tool_options_type != GIMP_TYPE_TOOL_OPTIONS) + { + GimpContainer *presets; + + presets = gimp_data_factory_get_container (gimp->tool_preset_factory); + + tool_info->presets = + gimp_filtered_container_new (presets, + gimp_tool_info_filter_preset, + tool_info); + } + + return tool_info; +} + +void +gimp_tool_info_set_standard (Gimp *gimp, + GimpToolInfo *tool_info) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (! tool_info || GIMP_IS_TOOL_INFO (tool_info)); + + g_set_object (&gimp->standard_tool_info, tool_info); +} + +GimpToolInfo * +gimp_tool_info_get_standard (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + return gimp->standard_tool_info; +} + +gchar * +gimp_tool_info_get_action_name (GimpToolInfo *tool_info) +{ + const gchar *identifier; + gchar *tmp; + gchar *name; + + g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); + + identifier = gimp_object_get_name (GIMP_OBJECT (tool_info)); + + g_return_val_if_fail (g_str_has_prefix (identifier, "gimp-"), NULL); + g_return_val_if_fail (g_str_has_suffix (identifier, "-tool"), NULL); + + tmp = g_strndup (identifier + strlen ("gimp-"), + strlen (identifier) - strlen ("gimp--tool")); + + name = g_strdup_printf ("tools-%s", tmp); + + g_free (tmp); + + return name; +} + +GFile * +gimp_tool_info_get_options_file (GimpToolInfo *tool_info, + const gchar *suffix) +{ + gchar *basename; + GFile *file; + + g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); + + /* also works for a NULL suffix */ + basename = g_strconcat (gimp_object_get_name (tool_info), suffix, NULL); + + file = gimp_directory_file ("tool-options", basename, NULL); + g_free (basename); + + return file; +} diff --git a/app/core/gimptoolinfo.h b/app/core/gimptoolinfo.h new file mode 100644 index 0000000..fc028b7 --- /dev/null +++ b/app/core/gimptoolinfo.h @@ -0,0 +1,95 @@ +/* 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 . + */ + +#ifndef __GIMP_TOOL_INFO_H__ +#define __GIMP_TOOL_INFO_H__ + + +#include "gimptoolitem.h" + + +#define GIMP_TYPE_TOOL_INFO (gimp_tool_info_get_type ()) +#define GIMP_TOOL_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_INFO, GimpToolInfo)) +#define GIMP_TOOL_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_INFO, GimpToolInfoClass)) +#define GIMP_IS_TOOL_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_INFO)) +#define GIMP_IS_TOOL_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_INFO)) +#define GIMP_TOOL_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_INFO, GimpToolInfoClass)) + + +typedef struct _GimpToolInfoClass GimpToolInfoClass; + +struct _GimpToolInfo +{ + GimpToolItem parent_instance; + + Gimp *gimp; + + GType tool_type; + GType tool_options_type; + GimpContextPropMask context_props; + + gchar *label; + gchar *tooltip; + + gchar *menu_label; + gchar *menu_accel; + + gchar *help_domain; + gchar *help_id; + + gboolean hidden; + gboolean experimental; + + GimpToolOptions *tool_options; + GimpPaintInfo *paint_info; + + GimpContainer *presets; +}; + +struct _GimpToolInfoClass +{ + GimpToolItemClass parent_class; +}; + + +GType gimp_tool_info_get_type (void) G_GNUC_CONST; + +GimpToolInfo * gimp_tool_info_new (Gimp *gimp, + GType tool_type, + GType tool_options_type, + GimpContextPropMask context_props, + const gchar *identifier, + const gchar *label, + const gchar *tooltip, + const gchar *menu_label, + const gchar *menu_accel, + const gchar *help_domain, + const gchar *help_id, + const gchar *paint_core_name, + const gchar *icon_name); + +void gimp_tool_info_set_standard (Gimp *gimp, + GimpToolInfo *tool_info); +GimpToolInfo * gimp_tool_info_get_standard (Gimp *gimp); + +gchar * gimp_tool_info_get_action_name (GimpToolInfo *tool_info); + +GFile * gimp_tool_info_get_options_file (GimpToolInfo *tool_info, + const gchar *suffix); + + +#endif /* __GIMP_TOOL_INFO_H__ */ diff --git a/app/core/gimptoolitem.c b/app/core/gimptoolitem.c new file mode 100644 index 0000000..984597b --- /dev/null +++ b/app/core/gimptoolitem.c @@ -0,0 +1,227 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolitem.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "core/gimpmarshal.h" + +#include "gimptoolitem.h" + + +enum +{ + VISIBLE_CHANGED, + SHOWN_CHANGED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_VISIBLE, + PROP_SHOWN +}; + + +struct _GimpToolItemPrivate +{ + gboolean visible; +}; + + +/* local function prototypes */ + +static void gimp_tool_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_tool_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolItem, gimp_tool_item, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_tool_item_parent_class + +static guint gimp_tool_item_signals[LAST_SIGNAL] = { 0 }; + + +/* private functions */ + +static void +gimp_tool_item_class_init (GimpToolItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gimp_tool_item_signals[VISIBLE_CHANGED] = + g_signal_new ("visible-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolItemClass, visible_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gimp_tool_item_signals[SHOWN_CHANGED] = + g_signal_new ("shown-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolItemClass, shown_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->get_property = gimp_tool_item_get_property; + object_class->set_property = gimp_tool_item_set_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_VISIBLE, + "visible", NULL, NULL, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, PROP_SHOWN, + g_param_spec_boolean ("shown", NULL, NULL, + TRUE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_tool_item_init (GimpToolItem *tool_item) +{ + tool_item->priv = gimp_tool_item_get_instance_private (tool_item); +} + +static void +gimp_tool_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolItem *tool_item = GIMP_TOOL_ITEM (object); + + switch (property_id) + { + case PROP_VISIBLE: + g_value_set_boolean (value, tool_item->priv->visible); + break; + case PROP_SHOWN: + g_value_set_boolean (value, gimp_tool_item_get_shown (tool_item)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolItem *tool_item = GIMP_TOOL_ITEM (object); + + switch (property_id) + { + case PROP_VISIBLE: + gimp_tool_item_set_visible (tool_item, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +void +gimp_tool_item_set_visible (GimpToolItem *tool_item, + gboolean visible) +{ + g_return_if_fail (GIMP_IS_TOOL_ITEM (tool_item)); + + if (visible != tool_item->priv->visible) + { + gboolean old_shown; + + g_object_freeze_notify (G_OBJECT (tool_item)); + + old_shown = gimp_tool_item_get_shown (tool_item); + + tool_item->priv->visible = visible; + + g_signal_emit (tool_item, gimp_tool_item_signals[VISIBLE_CHANGED], 0); + + if (gimp_tool_item_get_shown (tool_item) != old_shown) + gimp_tool_item_shown_changed (tool_item); + + g_object_notify (G_OBJECT (tool_item), "visible"); + + g_object_thaw_notify (G_OBJECT (tool_item)); + } +} + +gboolean +gimp_tool_item_get_visible (GimpToolItem *tool_item) +{ + g_return_val_if_fail (GIMP_IS_TOOL_ITEM (tool_item), FALSE); + + return tool_item->priv->visible; +} + +gboolean +gimp_tool_item_get_shown (GimpToolItem *tool_item) +{ + GimpToolItem *parent; + + g_return_val_if_fail (GIMP_IS_TOOL_ITEM (tool_item), FALSE); + + parent = GIMP_TOOL_ITEM ( + gimp_viewable_get_parent (GIMP_VIEWABLE (tool_item))); + + return tool_item->priv->visible && + (! parent || gimp_tool_item_get_shown (parent)); +} + + +/* protected functions */ + +void +gimp_tool_item_shown_changed (GimpToolItem *tool_item) +{ + g_signal_emit (tool_item, gimp_tool_item_signals[SHOWN_CHANGED], 0); + + g_object_notify (G_OBJECT (tool_item), "shown"); +} diff --git a/app/core/gimptoolitem.h b/app/core/gimptoolitem.h new file mode 100644 index 0000000..3f19f49 --- /dev/null +++ b/app/core/gimptoolitem.h @@ -0,0 +1,70 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolitem.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_TOOL_ITEM_H__ +#define __GIMP_TOOL_ITEM_H__ + + +#include "gimpviewable.h" + + +#define GIMP_TYPE_TOOL_ITEM (gimp_tool_item_get_type ()) +#define GIMP_TOOL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_ITEM, GimpToolItem)) +#define GIMP_TOOL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_ITEM, GimpToolItemClass)) +#define GIMP_IS_TOOL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_ITEM)) +#define GIMP_IS_TOOL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_ITEM)) +#define GIMP_TOOL_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_ITEM, GimpToolItemClass)) + + +typedef struct _GimpToolItemPrivate GimpToolItemPrivate; +typedef struct _GimpToolItemClass GimpToolItemClass; + +struct _GimpToolItem +{ + GimpViewable parent_instance; + + GimpToolItemPrivate *priv; +}; + +struct _GimpToolItemClass +{ + GimpViewableClass parent_class; + + /* signals */ + void (* visible_changed) (GimpToolItem *tool_item); + void (* shown_changed) (GimpToolItem *tool_item); +}; + + +GType gimp_tool_item_get_type (void) G_GNUC_CONST; + +void gimp_tool_item_set_visible (GimpToolItem *tool_item, + gboolean visible); +gboolean gimp_tool_item_get_visible (GimpToolItem *tool_item); + +gboolean gimp_tool_item_get_shown (GimpToolItem *tool_item); + + +/* protected */ + +void gimp_tool_item_shown_changed (GimpToolItem *tool_item); + + +#endif /* __GIMP_TOOL_ITEM_H__ */ diff --git a/app/core/gimptooloptions.c b/app/core/gimptooloptions.c new file mode 100644 index 0000000..8a19d6e --- /dev/null +++ b/app/core/gimptooloptions.c @@ -0,0 +1,378 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimperror.h" +#include "gimptoolinfo.h" +#include "gimptooloptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_TOOL, + PROP_TOOL_INFO +}; + + +static void gimp_tool_options_config_iface_init (GimpConfigInterface *iface); + +static void gimp_tool_options_dispose (GObject *object); +static void gimp_tool_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_options_config_reset (GimpConfig *config); + +static void gimp_tool_options_tool_notify (GimpToolOptions *options, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_CODE (GimpToolOptions, gimp_tool_options, GIMP_TYPE_CONTEXT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_tool_options_config_iface_init)) + +#define parent_class gimp_tool_options_parent_class + + +static void +gimp_tool_options_class_init (GimpToolOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_tool_options_dispose; + object_class->set_property = gimp_tool_options_set_property; + object_class->get_property = gimp_tool_options_get_property; + + g_object_class_override_property (object_class, PROP_TOOL, "tool"); + + g_object_class_install_property (object_class, PROP_TOOL_INFO, + g_param_spec_object ("tool-info", + NULL, NULL, + GIMP_TYPE_TOOL_INFO, + GIMP_PARAM_READWRITE)); + +} + +static void +gimp_tool_options_init (GimpToolOptions *options) +{ + options->tool_info = NULL; + + g_signal_connect (options, "notify::tool", + G_CALLBACK (gimp_tool_options_tool_notify), + NULL); +} + +static void +gimp_tool_options_config_iface_init (GimpConfigInterface *iface) +{ + iface->reset = gimp_tool_options_config_reset; +} + +static void +gimp_tool_options_dispose (GObject *object) +{ + GimpToolOptions *options = GIMP_TOOL_OPTIONS (object); + + g_clear_object (&options->tool_info); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/* This is such a horrible hack, but necessary because we + * a) load an option's tool-info from disk in many cases + * b) screwed up in the past and saved the wrong tool-info in some cases + */ +static GimpToolInfo * +gimp_tool_options_check_tool_info (GimpToolOptions *options, + GimpToolInfo *tool_info, + gboolean warn) +{ + if (tool_info && G_OBJECT_TYPE (options) == tool_info->tool_options_type) + { + return tool_info; + } + else + { + GList *list; + + for (list = gimp_get_tool_info_iter (GIMP_CONTEXT (options)->gimp); + list; + list = g_list_next (list)) + { + GimpToolInfo *new_info = list->data; + + if (G_OBJECT_TYPE (options) == new_info->tool_options_type) + { + if (warn) + g_printerr ("%s: correcting bogus deserialized tool " + "type '%s' with right type '%s'\n", + g_type_name (G_OBJECT_TYPE (options)), + tool_info ? gimp_object_get_name (tool_info) : "NULL", + gimp_object_get_name (new_info)); + + return new_info; + } + } + + g_return_val_if_reached (NULL); + } +} + +static void +gimp_tool_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolOptions *options = GIMP_TOOL_OPTIONS (object); + + switch (property_id) + { + case PROP_TOOL: + { + GimpToolInfo *tool_info = g_value_get_object (value); + GimpToolInfo *context_tool; + + context_tool = gimp_context_get_tool (GIMP_CONTEXT (options)); + + g_return_if_fail (context_tool == NULL || + context_tool == tool_info); + + tool_info = gimp_tool_options_check_tool_info (options, tool_info, TRUE); + + if (! context_tool) + gimp_context_set_tool (GIMP_CONTEXT (options), tool_info); + } + break; + + case PROP_TOOL_INFO: + { + GimpToolInfo *tool_info = g_value_get_object (value); + + g_return_if_fail (options->tool_info == NULL || + options->tool_info == tool_info); + + tool_info = gimp_tool_options_check_tool_info (options, tool_info, TRUE); + + if (! options->tool_info) + { + options->tool_info = g_object_ref (tool_info); + + gimp_context_set_serialize_properties (GIMP_CONTEXT (options), + tool_info->context_props); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolOptions *options = GIMP_TOOL_OPTIONS (object); + + switch (property_id) + { + case PROP_TOOL: + g_value_set_object (value, gimp_context_get_tool (GIMP_CONTEXT (options))); + break; + + case PROP_TOOL_INFO: + g_value_set_object (value, options->tool_info); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_options_config_reset (GimpConfig *config) +{ + gchar *name = g_strdup (gimp_object_get_name (config)); + + gimp_config_reset_properties (G_OBJECT (config)); + + gimp_object_take_name (GIMP_OBJECT (config), name); +} + +static void +gimp_tool_options_tool_notify (GimpToolOptions *options, + GParamSpec *pspec) +{ + GimpToolInfo *tool_info = gimp_context_get_tool (GIMP_CONTEXT (options)); + GimpToolInfo *new_info; + + new_info = gimp_tool_options_check_tool_info (options, tool_info, FALSE); + + if (tool_info && new_info != tool_info) + g_warning ("%s: 'tool' property on %s was set to bogus value " + "'%s', it MUST be '%s'.", + G_STRFUNC, + g_type_name (G_OBJECT_TYPE (options)), + gimp_object_get_name (tool_info), + gimp_object_get_name (new_info)); +} + + +/* public functions */ + +void +gimp_tool_options_set_gui_mode (GimpToolOptions *tool_options, + gboolean gui_mode) +{ + g_return_if_fail (GIMP_IS_TOOL_OPTIONS (tool_options)); + + tool_options->gui_mode = gui_mode ? TRUE : FALSE; +} + +gboolean +gimp_tool_options_get_gui_mode (GimpToolOptions *tool_options) +{ + g_return_val_if_fail (GIMP_IS_TOOL_OPTIONS (tool_options), FALSE); + + return tool_options->gui_mode; +} + +gboolean +gimp_tool_options_serialize (GimpToolOptions *tool_options, + GError **error) +{ + GFile *file; + gchar *header; + gchar *footer; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_TOOL_OPTIONS (tool_options), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_tool_info_get_options_file (tool_options->tool_info, NULL); + + if (tool_options->tool_info->gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + header = g_strdup_printf ("GIMP %s options", + gimp_object_get_name (tool_options->tool_info)); + footer = g_strdup_printf ("end of %s options", + gimp_object_get_name (tool_options->tool_info)); + + retval = gimp_config_serialize_to_gfile (GIMP_CONFIG (tool_options), + file, + header, footer, + NULL, + error); + + g_free (header); + g_free (footer); + + g_object_unref (file); + + return retval; +} + +gboolean +gimp_tool_options_deserialize (GimpToolOptions *tool_options, + GError **error) +{ + GFile *file; + gboolean retval; + + g_return_val_if_fail (GIMP_IS_TOOL_OPTIONS (tool_options), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_tool_info_get_options_file (tool_options->tool_info, NULL); + + if (tool_options->tool_info->gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + retval = gimp_config_deserialize_gfile (GIMP_CONFIG (tool_options), + file, + NULL, + error); + + g_object_unref (file); + + return retval; +} + +gboolean +gimp_tool_options_delete (GimpToolOptions *tool_options, + GError **error) +{ + GFile *file; + GError *my_error = NULL; + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_TOOL_OPTIONS (tool_options), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_tool_info_get_options_file (tool_options->tool_info, NULL); + + if (tool_options->tool_info->gimp->be_verbose) + g_print ("Deleting '%s'\n", gimp_file_get_utf8_name (file)); + + if (! g_file_delete (file, NULL, &my_error) && + my_error->code != G_IO_ERROR_NOT_FOUND) + { + success = FALSE; + + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("Deleting \"%s\" failed: %s"), + gimp_file_get_utf8_name (file), my_error->message); + } + + g_clear_error (&my_error); + g_object_unref (file); + + return success; +} + +void +gimp_tool_options_create_folder (void) +{ + GFile *file = gimp_directory_file ("tool-options", NULL); + + g_file_make_directory (file, NULL, NULL); + g_object_unref (file); +} diff --git a/app/core/gimptooloptions.h b/app/core/gimptooloptions.h new file mode 100644 index 0000000..f59aa52 --- /dev/null +++ b/app/core/gimptooloptions.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_TOOL_OPTIONS_H__ +#define __GIMP_TOOL_OPTIONS_H__ + + +#include "gimpcontext.h" + + +#define GIMP_TYPE_TOOL_OPTIONS (gimp_tool_options_get_type ()) +#define GIMP_TOOL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_OPTIONS, GimpToolOptions)) +#define GIMP_TOOL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_OPTIONS, GimpToolOptionsClass)) +#define GIMP_IS_TOOL_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_OPTIONS)) +#define GIMP_IS_TOOL_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_OPTIONS)) +#define GIMP_TOOL_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_OPTIONS, GimpToolOptionsClass)) + + +typedef struct _GimpToolOptionsClass GimpToolOptionsClass; + +struct _GimpToolOptions +{ + GimpContext parent_instance; + + GimpToolInfo *tool_info; + + /* if TRUE this instance is the main tool options object used for + * the GUI, this is not exactly clean, but there are some things + * (like linking brush properties to the active brush, or properly + * maintaining global brush, pattern etc.) that can only be done + * right in the object, and not by signal connections from the GUI, + * or upon switching tools, all of which was much more horrible. + */ + gboolean gui_mode; +}; + +struct _GimpToolOptionsClass +{ + GimpContextClass parent_class; +}; + + +GType gimp_tool_options_get_type (void) G_GNUC_CONST; + +void gimp_tool_options_set_gui_mode (GimpToolOptions *tool_options, + gboolean gui_mode); +gboolean gimp_tool_options_get_gui_mode (GimpToolOptions *tool_options); + +gboolean gimp_tool_options_serialize (GimpToolOptions *tool_options, + GError **error); +gboolean gimp_tool_options_deserialize (GimpToolOptions *tool_options, + GError **error); + +gboolean gimp_tool_options_delete (GimpToolOptions *tool_options, + GError **error); +void gimp_tool_options_create_folder (void); + + +#endif /* __GIMP_TOOL_OPTIONS_H__ */ diff --git a/app/core/gimptoolpreset-load.c b/app/core/gimptoolpreset-load.c new file mode 100644 index 0000000..386ac09 --- /dev/null +++ b/app/core/gimptoolpreset-load.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimpcontext.h" +#include "gimptoolpreset.h" +#include "gimptoolpreset-load.h" + +#include "gimp-intl.h" + + +GList * +gimp_tool_preset_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error) +{ + GimpToolPreset *tool_preset; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tool_preset = g_object_new (GIMP_TYPE_TOOL_PRESET, + "gimp", context->gimp, + NULL); + + if (gimp_config_deserialize_stream (GIMP_CONFIG (tool_preset), + input, + NULL, error)) + { + if (GIMP_IS_CONTEXT (tool_preset->tool_options)) + { + return g_list_prepend (NULL, tool_preset); + } + else + { + g_set_error_literal (error, + GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("Tool preset file is corrupt.")); + } + } + + g_object_unref (tool_preset); + + return NULL; +} diff --git a/app/core/gimptoolpreset-load.h b/app/core/gimptoolpreset-load.h new file mode 100644 index 0000000..9a016df --- /dev/null +++ b/app/core/gimptoolpreset-load.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GIMP_TOOL_PRESET_LOAD_H__ +#define __GIMP_TOOL_PRESET_LOAD_H__ + + +#define GIMP_TOOL_PRESET_FILE_EXTENSION ".gtp" + + +GList * gimp_tool_preset_load (GimpContext *context, + GFile *file, + GInputStream *input, + GError **error); + + +#endif /* __GIMP_TOOL_PRESET_LOAD_H__ */ diff --git a/app/core/gimptoolpreset-save.c b/app/core/gimptoolpreset-save.c new file mode 100644 index 0000000..0afd024 --- /dev/null +++ b/app/core/gimptoolpreset-save.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimptoolpreset.h" +#include "gimptoolpreset-save.h" + + +gboolean +gimp_tool_preset_save (GimpData *data, + GOutputStream *output, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_TOOL_PRESET (data), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return gimp_config_serialize_to_stream (GIMP_CONFIG (data), + output, + "GIMP tool preset file", + "end of GIMP tool preset file", + NULL, error); +} diff --git a/app/core/gimptoolpreset-save.h b/app/core/gimptoolpreset-save.h new file mode 100644 index 0000000..d78ef64 --- /dev/null +++ b/app/core/gimptoolpreset-save.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 . + */ + +#ifndef __GIMP_TOOL_PRESET_SAVE_H__ +#define __GIMP_TOOL_PRESET_SAVE_H__ + + +/* don't call this function directly, use gimp_data_save() instead */ +gboolean gimp_tool_preset_save (GimpData *data, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_TOOL_PRESET_SAVE_H__ */ diff --git a/app/core/gimptoolpreset.c b/app/core/gimptoolpreset.c new file mode 100644 index 0000000..6ab2eb7 --- /dev/null +++ b/app/core/gimptoolpreset.c @@ -0,0 +1,705 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimptoolinfo.h" +#include "gimptooloptions.h" +#include "gimptoolpreset.h" +#include "gimptoolpreset-load.h" +#include "gimptoolpreset-save.h" + +#include "gimp-intl.h" + + +/* The defaults are "everything except color", which is problematic + * with gradients, which is why we special case the gradient tool in + * gimp_tool_preset_set_options(). + */ +#define DEFAULT_USE_FG_BG FALSE +#define DEFAULT_USE_OPACITY_PAINT_MODE TRUE +#define DEFAULT_USE_BRUSH TRUE +#define DEFAULT_USE_DYNAMICS TRUE +#define DEFAULT_USE_MYBRUSH TRUE +#define DEFAULT_USE_GRADIENT FALSE +#define DEFAULT_USE_PATTERN TRUE +#define DEFAULT_USE_PALETTE FALSE +#define DEFAULT_USE_FONT TRUE + +enum +{ + PROP_0, + PROP_NAME, + PROP_GIMP, + PROP_TOOL_OPTIONS, + PROP_USE_FG_BG, + PROP_USE_OPACITY_PAINT_MODE, + PROP_USE_BRUSH, + PROP_USE_DYNAMICS, + PROP_USE_MYBRUSH, + PROP_USE_GRADIENT, + PROP_USE_PATTERN, + PROP_USE_PALETTE, + PROP_USE_FONT +}; + + +static void gimp_tool_preset_config_iface_init (GimpConfigInterface *iface); + +static void gimp_tool_preset_constructed (GObject *object); +static void gimp_tool_preset_finalize (GObject *object); +static void gimp_tool_preset_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_preset_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void + gimp_tool_preset_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs); + +static const gchar * gimp_tool_preset_get_extension (GimpData *data); + +static gboolean gimp_tool_preset_deserialize_property (GimpConfig *config, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected); + +static void gimp_tool_preset_set_options (GimpToolPreset *preset, + GimpToolOptions *options); +static void gimp_tool_preset_options_notify (GObject *tool_options, + const GParamSpec *pspec, + GimpToolPreset *preset); +static void gimp_tool_preset_options_prop_name_changed (GimpContext *tool_options, + GimpContextPropType prop, + GimpToolPreset *preset); + + +G_DEFINE_TYPE_WITH_CODE (GimpToolPreset, gimp_tool_preset, GIMP_TYPE_DATA, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_tool_preset_config_iface_init)) + +#define parent_class gimp_tool_preset_parent_class + + +static void +gimp_tool_preset_class_init (GimpToolPresetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpDataClass *data_class = GIMP_DATA_CLASS (klass); + + object_class->constructed = gimp_tool_preset_constructed; + object_class->finalize = gimp_tool_preset_finalize; + object_class->set_property = gimp_tool_preset_set_property; + object_class->get_property = gimp_tool_preset_get_property; + object_class->dispatch_properties_changed = gimp_tool_preset_dispatch_properties_changed; + + data_class->save = gimp_tool_preset_save; + data_class->get_extension = gimp_tool_preset_get_extension; + + GIMP_CONFIG_PROP_STRING (object_class, PROP_NAME, + "name", + NULL, NULL, + "Unnamed", + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", + NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_TOOL_OPTIONS, + "tool-options", + NULL, NULL, + GIMP_TYPE_TOOL_OPTIONS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_FG_BG, + "use-fg-bg", + _("Apply stored FG/BG"), + NULL, + DEFAULT_USE_FG_BG, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_OPACITY_PAINT_MODE, + "use-opacity-paint-mode", + _("Apply stored opacity/paint mode"), + NULL, + DEFAULT_USE_OPACITY_PAINT_MODE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_BRUSH, + "use-brush", + _("Apply stored brush"), + NULL, + DEFAULT_USE_BRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_DYNAMICS, + "use-dynamics", + _("Apply stored dynamics"), + NULL, + DEFAULT_USE_DYNAMICS, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_MYBRUSH, + "use-mypaint-brush", + _("Apply stored MyPaint brush"), + NULL, + DEFAULT_USE_MYBRUSH, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_PATTERN, + "use-pattern", + _("Apply stored pattern"), + NULL, + DEFAULT_USE_PATTERN, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_PALETTE, + "use-palette", + _("Apply stored palette"), + NULL, + DEFAULT_USE_PALETTE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_GRADIENT, + "use-gradient", + _("Apply stored gradient"), + NULL, + DEFAULT_USE_GRADIENT, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_FONT, + "use-font", + _("Apply stored font"), + NULL, + DEFAULT_USE_FONT, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_tool_preset_config_iface_init (GimpConfigInterface *iface) +{ + iface->deserialize_property = gimp_tool_preset_deserialize_property; +} + +static void +gimp_tool_preset_init (GimpToolPreset *tool_preset) +{ +} + +static void +gimp_tool_preset_constructed (GObject *object) +{ + GimpToolPreset *preset = GIMP_TOOL_PRESET (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + g_return_if_fail (GIMP_IS_GIMP (preset->gimp)); +} + +static void +gimp_tool_preset_finalize (GObject *object) +{ + GimpToolPreset *tool_preset = GIMP_TOOL_PRESET (object); + + gimp_tool_preset_set_options (tool_preset, NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_preset_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolPreset *tool_preset = GIMP_TOOL_PRESET (object); + + switch (property_id) + { + case PROP_NAME: + gimp_object_set_name (GIMP_OBJECT (tool_preset), + g_value_get_string (value)); + break; + + case PROP_GIMP: + tool_preset->gimp = g_value_get_object (value); /* don't ref */ + break; + + case PROP_TOOL_OPTIONS: + gimp_tool_preset_set_options (tool_preset, + GIMP_TOOL_OPTIONS (g_value_get_object (value))); + break; + + case PROP_USE_FG_BG: + tool_preset->use_fg_bg = g_value_get_boolean (value); + break; + case PROP_USE_OPACITY_PAINT_MODE: + tool_preset->use_opacity_paint_mode = g_value_get_boolean (value); + break; + case PROP_USE_BRUSH: + tool_preset->use_brush = g_value_get_boolean (value); + break; + case PROP_USE_DYNAMICS: + tool_preset->use_dynamics = g_value_get_boolean (value); + break; + case PROP_USE_MYBRUSH: + tool_preset->use_mybrush = g_value_get_boolean (value); + break; + case PROP_USE_PATTERN: + tool_preset->use_pattern = g_value_get_boolean (value); + break; + case PROP_USE_PALETTE: + tool_preset->use_palette = g_value_get_boolean (value); + break; + case PROP_USE_GRADIENT: + tool_preset->use_gradient = g_value_get_boolean (value); + break; + case PROP_USE_FONT: + tool_preset->use_font = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_preset_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolPreset *tool_preset = GIMP_TOOL_PRESET (object); + + switch (property_id) + { + case PROP_NAME: + g_value_set_string (value, gimp_object_get_name (tool_preset)); + break; + + case PROP_GIMP: + g_value_set_object (value, tool_preset->gimp); + break; + + case PROP_TOOL_OPTIONS: + g_value_set_object (value, tool_preset->tool_options); + break; + + case PROP_USE_FG_BG: + g_value_set_boolean (value, tool_preset->use_fg_bg); + break; + case PROP_USE_OPACITY_PAINT_MODE: + g_value_set_boolean (value, tool_preset->use_opacity_paint_mode); + break; + case PROP_USE_BRUSH: + g_value_set_boolean (value, tool_preset->use_brush); + break; + case PROP_USE_MYBRUSH: + g_value_set_boolean (value, tool_preset->use_mybrush); + break; + case PROP_USE_DYNAMICS: + g_value_set_boolean (value, tool_preset->use_dynamics); + break; + case PROP_USE_PATTERN: + g_value_set_boolean (value, tool_preset->use_pattern); + break; + case PROP_USE_PALETTE: + g_value_set_boolean (value, tool_preset->use_palette); + break; + case PROP_USE_GRADIENT: + g_value_set_boolean (value, tool_preset->use_gradient); + break; + case PROP_USE_FONT: + g_value_set_boolean (value, tool_preset->use_font); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_preset_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + gint i; + + G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, + n_pspecs, pspecs); + + for (i = 0; i < n_pspecs; i++) + { + if (pspecs[i]->flags & GIMP_CONFIG_PARAM_SERIALIZE) + { + gimp_data_dirty (GIMP_DATA (object)); + break; + } + } +} + +static const gchar * +gimp_tool_preset_get_extension (GimpData *data) +{ + return GIMP_TOOL_PRESET_FILE_EXTENSION; +} + +static gboolean +gimp_tool_preset_deserialize_property (GimpConfig *config, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected) +{ + GimpToolPreset *tool_preset = GIMP_TOOL_PRESET (config); + + switch (property_id) + { + case PROP_TOOL_OPTIONS: + { + GObject *options; + gchar *type_name; + GType type; + GimpContextPropMask serialize_props; + + if (! gimp_scanner_parse_string (scanner, &type_name)) + { + *expected = G_TOKEN_STRING; + break; + } + + if (! (type_name && *type_name)) + { + g_scanner_error (scanner, "GimpToolOptions type name is empty"); + *expected = G_TOKEN_NONE; + g_free (type_name); + break; + } + + if (! strcmp (type_name, "GimpTransformOptions")) + { + g_printerr ("Correcting tool options type GimpTransformOptions " + "to GimpTransformGridOptions\n"); + g_free (type_name); + type_name = g_strdup ("GimpTransformGridOptions"); + } + + type = g_type_from_name (type_name); + + if (! type) + { + g_scanner_error (scanner, + "unable to determine type of '%s'", + type_name); + *expected = G_TOKEN_NONE; + g_free (type_name); + break; + } + + if (! g_type_is_a (type, GIMP_TYPE_TOOL_OPTIONS)) + { + g_scanner_error (scanner, + "'%s' is not a subclass of GimpToolOptions", + type_name); + *expected = G_TOKEN_NONE; + g_free (type_name); + break; + } + + g_free (type_name); + + options = g_object_new (type, + "gimp", tool_preset->gimp, + NULL); + + /* Initialize all GimpContext object properties that can be + * used by presets with some non-NULL object, so loading a + * broken preset won't leave us with NULL objects that have + * bad effects. See bug #742159. + */ + gimp_context_copy_properties (gimp_get_user_context (tool_preset->gimp), + GIMP_CONTEXT (options), + GIMP_CONTEXT_PROP_MASK_BRUSH | + GIMP_CONTEXT_PROP_MASK_DYNAMICS | + GIMP_CONTEXT_PROP_MASK_MYBRUSH | + GIMP_CONTEXT_PROP_MASK_PATTERN | + GIMP_CONTEXT_PROP_MASK_GRADIENT | + GIMP_CONTEXT_PROP_MASK_PALETTE | + GIMP_CONTEXT_PROP_MASK_FONT); + + if (! GIMP_CONFIG_GET_INTERFACE (options)->deserialize (GIMP_CONFIG (options), + scanner, 1, + NULL)) + { + *expected = G_TOKEN_NONE; + g_object_unref (options); + break; + } + + /* we need both tool and tool-info on the options */ + if (gimp_context_get_tool (GIMP_CONTEXT (options))) + { + g_object_set (options, + "tool-info", + gimp_context_get_tool (GIMP_CONTEXT (options)), + NULL); + } + else if (GIMP_TOOL_OPTIONS (options)->tool_info) + { + g_object_set (options, + "tool", GIMP_TOOL_OPTIONS (options)->tool_info, + NULL); + } + else + { + /* if we have none, the options set_property() logic will + * replace the NULL with its best guess + */ + g_object_set (options, + "tool", NULL, + "tool-info", NULL, + NULL); + } + + serialize_props = + gimp_context_get_serialize_properties (GIMP_CONTEXT (options)); + + gimp_context_set_serialize_properties (GIMP_CONTEXT (options), + serialize_props | + GIMP_CONTEXT_PROP_MASK_TOOL); + + g_value_take_object (value, options); + } + break; + + default: + return FALSE; + } + + return TRUE; +} + +static void +gimp_tool_preset_set_options (GimpToolPreset *preset, + GimpToolOptions *options) +{ + if (preset->tool_options) + { + g_signal_handlers_disconnect_by_func (preset->tool_options, + gimp_tool_preset_options_notify, + preset); + + g_signal_handlers_disconnect_by_func (preset->tool_options, + gimp_tool_preset_options_prop_name_changed, + preset); + + g_clear_object (&preset->tool_options); + } + + if (options) + { + GimpContextPropMask serialize_props; + + preset->tool_options = + GIMP_TOOL_OPTIONS (gimp_config_duplicate (GIMP_CONFIG (options))); + + serialize_props = + gimp_context_get_serialize_properties (GIMP_CONTEXT (preset->tool_options)); + + gimp_context_set_serialize_properties (GIMP_CONTEXT (preset->tool_options), + serialize_props | + GIMP_CONTEXT_PROP_MASK_TOOL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_FOREGROUND) && + ! (serialize_props & GIMP_CONTEXT_PROP_MASK_BACKGROUND)) + g_object_set (preset, "use-fg-bg", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_OPACITY) && + ! (serialize_props & GIMP_CONTEXT_PROP_MASK_PAINT_MODE)) + g_object_set (preset, "use-opacity-paint-mode", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_BRUSH)) + g_object_set (preset, "use-brush", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_DYNAMICS)) + g_object_set (preset, "use-dynamics", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_MYBRUSH)) + g_object_set (preset, "use-mypaint-brush", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_GRADIENT)) + g_object_set (preset, "use-gradient", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_PATTERN)) + g_object_set (preset, "use-pattern", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_PALETTE)) + g_object_set (preset, "use-palette", FALSE, NULL); + + if (! (serialize_props & GIMP_CONTEXT_PROP_MASK_FONT)) + g_object_set (preset, "use-font", FALSE, NULL); + + /* see comment above the DEFAULT defines at the top of the file */ + if (! g_strcmp0 ("gimp-gradient-tool", + gimp_object_get_name (preset->tool_options->tool_info))) + g_object_set (preset, "use-gradient", TRUE, NULL); + + g_signal_connect (preset->tool_options, "notify", + G_CALLBACK (gimp_tool_preset_options_notify), + preset); + + g_signal_connect (preset->tool_options, "prop-name-changed", + G_CALLBACK (gimp_tool_preset_options_prop_name_changed), + preset); + } + + g_object_notify (G_OBJECT (preset), "tool-options"); +} + +static void +gimp_tool_preset_options_notify (GObject *tool_options, + const GParamSpec *pspec, + GimpToolPreset *preset) +{ + if (pspec->owner_type == GIMP_TYPE_CONTEXT) + { + GimpContextPropMask serialize_props; + + serialize_props = + gimp_context_get_serialize_properties (GIMP_CONTEXT (tool_options)); + + if ((1 << pspec->param_id) & serialize_props) + { + g_object_notify (G_OBJECT (preset), "tool-options"); + } + } + else if (pspec->flags & GIMP_CONFIG_PARAM_SERIALIZE) + { + g_object_notify (G_OBJECT (preset), "tool-options"); + } +} + +static void +gimp_tool_preset_options_prop_name_changed (GimpContext *tool_options, + GimpContextPropType prop, + GimpToolPreset *preset) +{ + GimpContextPropMask serialize_props; + + serialize_props = + gimp_context_get_serialize_properties (GIMP_CONTEXT (preset->tool_options)); + + if ((1 << prop) & serialize_props) + { + g_object_notify (G_OBJECT (preset), "tool-options"); + } +} + + +/* public functions */ + +GimpData * +gimp_tool_preset_new (GimpContext *context, + const gchar *unused) +{ + GimpToolInfo *tool_info; + const gchar *icon_name; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + tool_info = gimp_context_get_tool (context); + + g_return_val_if_fail (tool_info != NULL, NULL); + + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + + return g_object_new (GIMP_TYPE_TOOL_PRESET, + "name", tool_info->label, + "icon-name", icon_name, + "gimp", context->gimp, + "tool-options", tool_info->tool_options, + NULL); +} + +GimpContextPropMask +gimp_tool_preset_get_prop_mask (GimpToolPreset *preset) +{ + GimpContextPropMask serialize_props; + GimpContextPropMask use_props = 0; + + g_return_val_if_fail (GIMP_IS_TOOL_PRESET (preset), 0); + + serialize_props = + gimp_context_get_serialize_properties (GIMP_CONTEXT (preset->tool_options)); + + if (preset->use_fg_bg) + { + use_props |= (GIMP_CONTEXT_PROP_MASK_FOREGROUND & serialize_props); + use_props |= (GIMP_CONTEXT_PROP_MASK_BACKGROUND & serialize_props); + } + + if (preset->use_opacity_paint_mode) + { + use_props |= (GIMP_CONTEXT_PROP_MASK_OPACITY & serialize_props); + use_props |= (GIMP_CONTEXT_PROP_MASK_PAINT_MODE & serialize_props); + } + + if (preset->use_brush) + use_props |= (GIMP_CONTEXT_PROP_MASK_BRUSH & serialize_props); + + if (preset->use_dynamics) + use_props |= (GIMP_CONTEXT_PROP_MASK_DYNAMICS & serialize_props); + + if (preset->use_mybrush) + use_props |= (GIMP_CONTEXT_PROP_MASK_MYBRUSH & serialize_props); + + if (preset->use_pattern) + use_props |= (GIMP_CONTEXT_PROP_MASK_PATTERN & serialize_props); + + if (preset->use_palette) + use_props |= (GIMP_CONTEXT_PROP_MASK_PALETTE & serialize_props); + + if (preset->use_gradient) + use_props |= (GIMP_CONTEXT_PROP_MASK_GRADIENT & serialize_props); + + if (preset->use_font) + use_props |= (GIMP_CONTEXT_PROP_MASK_FONT & serialize_props); + + return use_props; +} diff --git a/app/core/gimptoolpreset.h b/app/core/gimptoolpreset.h new file mode 100644 index 0000000..bf2cceb --- /dev/null +++ b/app/core/gimptoolpreset.h @@ -0,0 +1,67 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __GIMP_TOOL_PRESET_H__ +#define __GIMP_TOOL_PRESET_H__ + + +#include "gimpdata.h" + + +#define GIMP_TYPE_TOOL_PRESET (gimp_tool_preset_get_type ()) +#define GIMP_TOOL_PRESET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_PRESET, GimpToolPreset)) +#define GIMP_TOOL_PRESET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_PRESET, GimpToolPresetClass)) +#define GIMP_IS_TOOL_PRESET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_PRESET)) +#define GIMP_IS_TOOL_PRESET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_PRESET)) +#define GIMP_TOOL_PRESET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_PRESET, GimpToolPresetClass)) + + +typedef struct _GimpToolPresetClass GimpToolPresetClass; + +struct _GimpToolPreset +{ + GimpData parent_instance; + + Gimp *gimp; + GimpToolOptions *tool_options; + + gboolean use_fg_bg; + gboolean use_opacity_paint_mode; + gboolean use_brush; + gboolean use_dynamics; + gboolean use_mybrush; + gboolean use_gradient; + gboolean use_pattern; + gboolean use_palette; + gboolean use_font; +}; + +struct _GimpToolPresetClass +{ + GimpDataClass parent_class; +}; + + +GType gimp_tool_preset_get_type (void) G_GNUC_CONST; + +GimpData * gimp_tool_preset_new (GimpContext *context, + const gchar *unused); + +GimpContextPropMask gimp_tool_preset_get_prop_mask (GimpToolPreset *preset); + + +#endif /* __GIMP_TOOL_PRESET_H__ */ diff --git a/app/core/gimptreehandler.c b/app/core/gimptreehandler.c new file mode 100644 index 0000000..b26768e --- /dev/null +++ b/app/core/gimptreehandler.c @@ -0,0 +1,238 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpTreeHandler + * Copyright (C) 2009 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpcontainer.h" +#include "gimptreehandler.h" +#include "gimpviewable.h" + + +static void gimp_tree_handler_dispose (GObject *object); + +static void gimp_tree_handler_freeze (GimpTreeHandler *handler, + GimpContainer *container); +static void gimp_tree_handler_thaw (GimpTreeHandler *handler, + GimpContainer *container); + +static void gimp_tree_handler_add_container (GimpTreeHandler *handler, + GimpContainer *container); +static void gimp_tree_handler_add_foreach (GimpViewable *viewable, + GimpTreeHandler *handler); +static void gimp_tree_handler_add (GimpTreeHandler *handler, + GimpViewable *viewable, + GimpContainer *container); + +static void gimp_tree_handler_remove_container (GimpTreeHandler *handler, + GimpContainer *container); +static void gimp_tree_handler_remove_foreach (GimpViewable *viewable, + GimpTreeHandler *handler); +static void gimp_tree_handler_remove (GimpTreeHandler *handler, + GimpViewable *viewable, + GimpContainer *container); + + +G_DEFINE_TYPE (GimpTreeHandler, gimp_tree_handler, GIMP_TYPE_OBJECT) + +#define parent_class gimp_tree_handler_parent_class + + +static void +gimp_tree_handler_class_init (GimpTreeHandlerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_tree_handler_dispose; +} + +static void +gimp_tree_handler_init (GimpTreeHandler *handler) +{ +} + +static void +gimp_tree_handler_dispose (GObject *object) +{ + GimpTreeHandler *handler = GIMP_TREE_HANDLER (object); + + if (handler->container) + { + g_signal_handlers_disconnect_by_func (handler->container, + gimp_tree_handler_freeze, + handler); + g_signal_handlers_disconnect_by_func (handler->container, + gimp_tree_handler_thaw, + handler); + + if (! gimp_container_frozen (handler->container)) + gimp_tree_handler_remove_container (handler, handler->container); + + g_clear_object (&handler->container); + g_clear_pointer (&handler->signal_name, g_free); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + + +/* public functions */ + +GimpTreeHandler * +gimp_tree_handler_connect (GimpContainer *container, + const gchar *signal_name, + GCallback callback, + gpointer user_data) +{ + GimpTreeHandler *handler; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + g_return_val_if_fail (signal_name != NULL, NULL); + + handler = g_object_new (GIMP_TYPE_TREE_HANDLER, NULL); + + handler->container = g_object_ref (container); + handler->signal_name = g_strdup (signal_name); + handler->callback = callback; + handler->user_data = user_data; + + if (! gimp_container_frozen (container)) + gimp_tree_handler_add_container (handler, container); + + g_signal_connect_object (container, "freeze", + G_CALLBACK (gimp_tree_handler_freeze), + handler, + G_CONNECT_SWAPPED); + g_signal_connect_object (container, "thaw", + G_CALLBACK (gimp_tree_handler_thaw), + handler, + G_CONNECT_SWAPPED); + + return handler; +} + +void +gimp_tree_handler_disconnect (GimpTreeHandler *handler) +{ + g_return_if_fail (GIMP_IS_TREE_HANDLER (handler)); + + g_object_run_dispose (G_OBJECT (handler)); + g_object_unref (handler); +} + + +/* private functions */ + +static void +gimp_tree_handler_freeze (GimpTreeHandler *handler, + GimpContainer *container) +{ + gimp_tree_handler_remove_container (handler, container); +} + +static void +gimp_tree_handler_thaw (GimpTreeHandler *handler, + GimpContainer *container) +{ + gimp_tree_handler_add_container (handler, container); +} + +static void +gimp_tree_handler_add_container (GimpTreeHandler *handler, + GimpContainer *container) +{ + gimp_container_foreach (container, + (GFunc) gimp_tree_handler_add_foreach, + handler); + + g_signal_connect_object (container, "add", + G_CALLBACK (gimp_tree_handler_add), + handler, + G_CONNECT_SWAPPED); + g_signal_connect_object (container, "remove", + G_CALLBACK (gimp_tree_handler_remove), + handler, + G_CONNECT_SWAPPED); +} + +static void +gimp_tree_handler_add_foreach (GimpViewable *viewable, + GimpTreeHandler *handler) +{ + gimp_tree_handler_add (handler, viewable, NULL); +} + +static void +gimp_tree_handler_add (GimpTreeHandler *handler, + GimpViewable *viewable, + GimpContainer *unused) +{ + GimpContainer *children = gimp_viewable_get_children (viewable); + + g_signal_connect (viewable, + handler->signal_name, + handler->callback, + handler->user_data); + + if (children) + gimp_tree_handler_add_container (handler, children); +} + +static void +gimp_tree_handler_remove_container (GimpTreeHandler *handler, + GimpContainer *container) +{ + g_signal_handlers_disconnect_by_func (container, + gimp_tree_handler_add, + handler); + g_signal_handlers_disconnect_by_func (container, + gimp_tree_handler_remove, + handler); + + gimp_container_foreach (container, + (GFunc) gimp_tree_handler_remove_foreach, + handler); +} + +static void +gimp_tree_handler_remove_foreach (GimpViewable *viewable, + GimpTreeHandler *handler) +{ + gimp_tree_handler_remove (handler, viewable, NULL); +} + +static void +gimp_tree_handler_remove (GimpTreeHandler *handler, + GimpViewable *viewable, + GimpContainer *unused) +{ + GimpContainer *children = gimp_viewable_get_children (viewable); + + if (children) + gimp_tree_handler_remove_container (handler, children); + + g_signal_handlers_disconnect_by_func (viewable, + handler->callback, + handler->user_data); +} diff --git a/app/core/gimptreehandler.h b/app/core/gimptreehandler.h new file mode 100644 index 0000000..27497c4 --- /dev/null +++ b/app/core/gimptreehandler.h @@ -0,0 +1,64 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpTreeHandler + * Copyright (C) 2009 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TREE_HANDLER_H__ +#define __GIMP_TREE_HANDLER_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_TREE_HANDLER (gimp_tree_handler_get_type ()) +#define GIMP_TREE_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TREE_HANDLER, GimpTreeHandler)) +#define GIMP_TREE_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TREE_HANDLER, GimpTreeHandlerClass)) +#define GIMP_IS_TREE_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TREE_HANDLER)) +#define GIMP_IS_TREE_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TREE_HANDLER)) +#define GIMP_TREE_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TREE_HANDLER, GimpTreeHandlerClass)) + + +typedef struct _GimpTreeHandlerClass GimpTreeHandlerClass; + +struct _GimpTreeHandler +{ + GimpObject parent_instance; + + GimpContainer *container; + + gchar *signal_name; + GCallback callback; + gpointer user_data; +}; + +struct _GimpTreeHandlerClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_tree_handler_get_type (void) G_GNUC_CONST; + +GimpTreeHandler * gimp_tree_handler_connect (GimpContainer *container, + const gchar *signal_name, + GCallback callback, + gpointer user_data); +void gimp_tree_handler_disconnect (GimpTreeHandler *handler); + + +#endif /* __GIMP_TREE_HANDLER_H__ */ diff --git a/app/core/gimptreeproxy.c b/app/core/gimptreeproxy.c new file mode 100644 index 0000000..76fad91 --- /dev/null +++ b/app/core/gimptreeproxy.c @@ -0,0 +1,634 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptreeproxy.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimpviewable.h" +#include "gimptreeproxy.h" + + +enum +{ + PROP_0, + PROP_CONTAINER, + PROP_FLAT +}; + + +struct _GimpTreeProxyPrivate +{ + GimpContainer *container; + gboolean flat; +}; + + +/* local function prototypes */ + +static void gimp_tree_proxy_dispose (GObject *object); +static void gimp_tree_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tree_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tree_proxy_container_add (GimpContainer *container, + GimpObject *object, + GimpTreeProxy *tree_proxy); +static void gimp_tree_proxy_container_remove (GimpContainer *container, + GimpObject *object, + GimpTreeProxy *tree_proxy); +static void gimp_tree_proxy_container_reorder (GimpContainer *container, + GimpObject *object, + gint new_index, + GimpTreeProxy *tree_proxy); +static void gimp_tree_proxy_container_freeze (GimpContainer *container, + GimpTreeProxy *tree_proxy); +static void gimp_tree_proxy_container_thaw (GimpContainer *container, + GimpTreeProxy *tree_proxy); + +static gint gimp_tree_proxy_add_container (GimpTreeProxy *tree_proxy, + GimpContainer *container, + gint index); +static void gimp_tree_proxy_remove_container (GimpTreeProxy *tree_proxy, + GimpContainer *container); + +static gint gimp_tree_proxy_add_object (GimpTreeProxy *tree_proxy, + GimpObject *object, + gint index); +static void gimp_tree_proxy_remove_object (GimpTreeProxy *tree_proxy, + GimpObject *object); + +static gint gimp_tree_proxy_find_container (GimpTreeProxy *tree_proxy, + GimpContainer *container); +static gint gimp_tree_proxy_find_object (GimpContainer *container, + GimpObject *object); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpTreeProxy, gimp_tree_proxy, GIMP_TYPE_LIST) + +#define parent_class gimp_tree_proxy_parent_class + + +/* private functions */ + +static void +gimp_tree_proxy_class_init (GimpTreeProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_tree_proxy_dispose; + object_class->set_property = gimp_tree_proxy_set_property; + object_class->get_property = gimp_tree_proxy_get_property; + + g_object_class_install_property (object_class, PROP_CONTAINER, + g_param_spec_object ("container", NULL, NULL, + GIMP_TYPE_CONTAINER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FLAT, + g_param_spec_boolean ("flat", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_tree_proxy_init (GimpTreeProxy *tree_proxy) +{ + tree_proxy->priv = gimp_tree_proxy_get_instance_private (tree_proxy); +} + +static void +gimp_tree_proxy_dispose (GObject *object) +{ + GimpTreeProxy *tree_proxy = GIMP_TREE_PROXY (object); + + gimp_tree_proxy_set_container (tree_proxy, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object);; +} + +static void +gimp_tree_proxy_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpTreeProxy *tree_proxy = GIMP_TREE_PROXY (object); + + switch (property_id) + { + case PROP_CONTAINER: + gimp_tree_proxy_set_container (tree_proxy, g_value_get_object (value)); + break; + + case PROP_FLAT: + gimp_tree_proxy_set_flat (tree_proxy, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tree_proxy_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpTreeProxy *tree_proxy = GIMP_TREE_PROXY (object); + + switch (property_id) + { + case PROP_CONTAINER: + g_value_set_object (value, tree_proxy->priv->container); + break; + + case PROP_FLAT: + g_value_set_boolean (value, tree_proxy->priv->flat); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tree_proxy_container_add (GimpContainer *container, + GimpObject *object, + GimpTreeProxy *tree_proxy) +{ + gint index; + + if (tree_proxy->priv->flat) + { + index = gimp_tree_proxy_find_container (tree_proxy, container) + + gimp_tree_proxy_find_object (container, object); + } + else + { + index = gimp_container_get_child_index (container, object); + } + + gimp_tree_proxy_add_object (tree_proxy, object, index); +} + +static void +gimp_tree_proxy_container_remove (GimpContainer *container, + GimpObject *object, + GimpTreeProxy *tree_proxy) +{ + gimp_tree_proxy_remove_object (tree_proxy, object); +} + +static void +gimp_tree_proxy_container_reorder (GimpContainer *container, + GimpObject *object, + gint new_index, + GimpTreeProxy *tree_proxy) +{ + gint index; + + if (tree_proxy->priv->flat) + { + index = gimp_tree_proxy_find_container (tree_proxy, container) + + gimp_tree_proxy_find_object (container, object); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (object))) + { + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); + + gimp_tree_proxy_remove_object (tree_proxy, object); + gimp_tree_proxy_add_object (tree_proxy, object, index); + + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); + + return; + } + } + else + { + index = new_index; + } + + gimp_container_reorder (GIMP_CONTAINER (tree_proxy), object, index); +} + +static void +gimp_tree_proxy_container_freeze (GimpContainer *container, + GimpTreeProxy *tree_proxy) +{ + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); +} + +static void +gimp_tree_proxy_container_thaw (GimpContainer *container, + GimpTreeProxy *tree_proxy) +{ + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); +} + +typedef struct +{ + GimpTreeProxy *tree_proxy; + gint index; +} AddContainerData; + +static void +gimp_tree_proxy_add_container_func (GimpObject *object, + AddContainerData *data) +{ + data->index = gimp_tree_proxy_add_object (data->tree_proxy, + object, data->index); +} + +static gint +gimp_tree_proxy_add_container (GimpTreeProxy *tree_proxy, + GimpContainer *container, + gint index) +{ + AddContainerData data; + + g_signal_connect (container, "add", + G_CALLBACK (gimp_tree_proxy_container_add), + tree_proxy); + g_signal_connect (container, "remove", + G_CALLBACK (gimp_tree_proxy_container_remove), + tree_proxy); + g_signal_connect (container, "reorder", + G_CALLBACK (gimp_tree_proxy_container_reorder), + tree_proxy); + g_signal_connect (container, "freeze", + G_CALLBACK (gimp_tree_proxy_container_freeze), + tree_proxy); + g_signal_connect (container, "thaw", + G_CALLBACK (gimp_tree_proxy_container_thaw), + tree_proxy); + + data.tree_proxy = tree_proxy; + data.index = index; + + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); + + gimp_container_foreach (container, + (GFunc) gimp_tree_proxy_add_container_func, + &data); + + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); + + return data.index; +} + +static void +gimp_tree_proxy_remove_container_func (GimpObject *object, + GimpTreeProxy *tree_proxy) +{ + gimp_tree_proxy_remove_object (tree_proxy, object); +} + +static void +gimp_tree_proxy_remove_container (GimpTreeProxy *tree_proxy, + GimpContainer *container) +{ + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); + + gimp_container_foreach (container, + (GFunc) gimp_tree_proxy_remove_container_func, + tree_proxy); + + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); + + g_signal_handlers_disconnect_by_func ( + container, + gimp_tree_proxy_container_add, + tree_proxy); + g_signal_handlers_disconnect_by_func ( + container, + gimp_tree_proxy_container_remove, + tree_proxy); + g_signal_handlers_disconnect_by_func ( + container, + gimp_tree_proxy_container_reorder, + tree_proxy); + g_signal_handlers_disconnect_by_func ( + container, + gimp_tree_proxy_container_freeze, + tree_proxy); + g_signal_handlers_disconnect_by_func ( + container, + gimp_tree_proxy_container_thaw, + tree_proxy); +} + +static gint +gimp_tree_proxy_add_object (GimpTreeProxy *tree_proxy, + GimpObject *object, + gint index) +{ + if (index == gimp_container_get_n_children (GIMP_CONTAINER (tree_proxy))) + index = -1; + + if (tree_proxy->priv->flat) + { + GimpContainer *children; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (object)); + + if (children) + return gimp_tree_proxy_add_container (tree_proxy, children, index); + } + + if (index >= 0) + { + gimp_container_insert (GIMP_CONTAINER (tree_proxy), object, index); + + return index + 1; + } + else + { + gimp_container_add (GIMP_CONTAINER (tree_proxy), object); + + return index; + } +} + +static void +gimp_tree_proxy_remove_object (GimpTreeProxy *tree_proxy, + GimpObject *object) +{ + if (tree_proxy->priv->flat) + { + GimpContainer *children; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (object)); + + if (children) + return gimp_tree_proxy_remove_container (tree_proxy, children); + } + + gimp_container_remove (GIMP_CONTAINER (tree_proxy), object); +} + +typedef struct +{ + GimpContainer *container; + gint index; +} FindContainerData; + +static gboolean +gimp_tree_proxy_find_container_search_func (GimpObject *object, + FindContainerData *data) +{ + GimpContainer *children; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (object)); + + if (children) + { + if (children == data->container) + return TRUE; + + return gimp_container_search ( + children, + (GimpContainerSearchFunc) gimp_tree_proxy_find_container_search_func, + data) != NULL; + } + + data->index++; + + return FALSE; +} + +static gint +gimp_tree_proxy_find_container (GimpTreeProxy *tree_proxy, + GimpContainer *container) +{ + FindContainerData data; + + if (container == tree_proxy->priv->container) + return 0; + + data.container = container; + data.index = 0; + + if (gimp_container_search ( + tree_proxy->priv->container, + (GimpContainerSearchFunc) gimp_tree_proxy_find_container_search_func, + &data)) + { + return data.index; + } + + g_return_val_if_reached (0); +} + +typedef struct +{ + GimpObject *object; + gint index; +} FindObjectData; + +static gboolean +gimp_tree_proxy_find_object_search_func (GimpObject *object, + FindObjectData *data) +{ + GimpContainer *children; + + if (object == data->object) + return TRUE; + + children = gimp_viewable_get_children (GIMP_VIEWABLE (object)); + + if (children) + { + return gimp_container_search ( + children, + (GimpContainerSearchFunc) gimp_tree_proxy_find_object_search_func, + data) != NULL; + } + + data->index++; + + return FALSE; +} + +static gint +gimp_tree_proxy_find_object (GimpContainer *container, + GimpObject *object) +{ + FindObjectData data; + + data.object = object; + data.index = 0; + + if (gimp_container_search ( + container, + (GimpContainerSearchFunc) gimp_tree_proxy_find_object_search_func, + &data)) + { + return data.index; + } + + g_return_val_if_reached (0); +} + + +/* public functions */ + +GimpContainer * +gimp_tree_proxy_new (GType children_type) +{ + GTypeClass *children_class; + + children_class = g_type_class_ref (children_type); + + g_return_val_if_fail (G_TYPE_CHECK_CLASS_TYPE (children_class, + GIMP_TYPE_VIEWABLE), + NULL); + + g_type_class_unref (children_class); + + return g_object_new (GIMP_TYPE_TREE_PROXY, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "append", TRUE, + NULL); +} + +GimpContainer * +gimp_tree_proxy_new_for_container (GimpContainer *container) +{ + GimpTreeProxy *tree_proxy; + + g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); + + tree_proxy = GIMP_TREE_PROXY ( + gimp_tree_proxy_new (gimp_container_get_children_type (container))); + + gimp_tree_proxy_set_container (tree_proxy, container); + + return GIMP_CONTAINER (tree_proxy); +} + +void +gimp_tree_proxy_set_container (GimpTreeProxy *tree_proxy, + GimpContainer *container) +{ + g_return_if_fail (GIMP_IS_TREE_PROXY (tree_proxy)); + g_return_if_fail (container == NULL || GIMP_IS_CONTAINER (container)); + + if (container) + { + GTypeClass *children_class; + + children_class = g_type_class_ref ( + gimp_container_get_children_type (container)); + + g_return_if_fail ( + G_TYPE_CHECK_CLASS_TYPE ( + children_class, + gimp_container_get_children_type (GIMP_CONTAINER (tree_proxy)))); + + g_type_class_unref (children_class); + } + + if (container != tree_proxy->priv->container) + { + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); + + if (tree_proxy->priv->container) + { + gimp_tree_proxy_remove_container (tree_proxy, + tree_proxy->priv->container); + } + + g_set_object (&tree_proxy->priv->container, container); + + if (tree_proxy->priv->container) + { + gimp_tree_proxy_add_container (tree_proxy, + tree_proxy->priv->container, + -1); + } + + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); + + g_object_notify (G_OBJECT (tree_proxy), "container"); + } +} + +GimpContainer * +gimp_tree_proxy_get_container (GimpTreeProxy *tree_proxy) +{ + g_return_val_if_fail (GIMP_IS_TREE_PROXY (tree_proxy), NULL); + + return tree_proxy->priv->container; +} + +void +gimp_tree_proxy_set_flat (GimpTreeProxy *tree_proxy, + gboolean flat) +{ + g_return_if_fail (GIMP_IS_TREE_PROXY (tree_proxy)); + + if (flat != tree_proxy->priv->flat) + { + gimp_container_freeze (GIMP_CONTAINER (tree_proxy)); + + if (tree_proxy->priv->container) + { + gimp_tree_proxy_remove_container (tree_proxy, + tree_proxy->priv->container); + } + + tree_proxy->priv->flat = flat; + + if (tree_proxy->priv->container) + { + gimp_tree_proxy_add_container (tree_proxy, + tree_proxy->priv->container, + -1); + } + + gimp_container_thaw (GIMP_CONTAINER (tree_proxy)); + + g_object_notify (G_OBJECT (tree_proxy), "flat"); + } +} + +gboolean +gimp_tree_proxy_get_flat (GimpTreeProxy *tree_proxy) +{ + g_return_val_if_fail (GIMP_IS_TREE_PROXY (tree_proxy), FALSE); + + return tree_proxy->priv->flat; +} diff --git a/app/core/gimptreeproxy.h b/app/core/gimptreeproxy.h new file mode 100644 index 0000000..8ae39f8 --- /dev/null +++ b/app/core/gimptreeproxy.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptreeproxy.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_TREE_PROXY_H__ +#define __GIMP_TREE_PROXY_H__ + + +#include "gimplist.h" + + +#define GIMP_TYPE_TREE_PROXY (gimp_tree_proxy_get_type ()) +#define GIMP_TREE_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TREE_PROXY, GimpTreeProxy)) +#define GIMP_TREE_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TREE_PROXY, GimpTreeProxyClass)) +#define GIMP_IS_TREE_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TREE_PROXY)) +#define GIMP_IS_TREE_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TREE_PROXY)) +#define GIMP_TREE_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TREE_PROXY, GimpTreeProxyClass)) + + +typedef struct _GimpTreeProxyPrivate GimpTreeProxyPrivate; +typedef struct _GimpTreeProxyClass GimpTreeProxyClass; + +struct _GimpTreeProxy +{ + GimpList parent_instance; + + GimpTreeProxyPrivate *priv; +}; + +struct _GimpTreeProxyClass +{ + GimpListClass parent_class; +}; + + +GType gimp_tree_proxy_get_type (void) G_GNUC_CONST; + +GimpContainer * gimp_tree_proxy_new (GType children_type); +GimpContainer * gimp_tree_proxy_new_for_container (GimpContainer *container); + +void gimp_tree_proxy_set_container (GimpTreeProxy *tree_proxy, + GimpContainer *container); +GimpContainer * gimp_tree_proxy_get_container (GimpTreeProxy *tree_proxy); + +void gimp_tree_proxy_set_flat (GimpTreeProxy *tree_proxy, + gboolean flat); +gboolean gimp_tree_proxy_get_flat (GimpTreeProxy *tree_proxy); + + +#endif /* __GIMP_TREE_PROXY_H__ */ diff --git a/app/core/gimptriviallycancelablewaitable.c b/app/core/gimptriviallycancelablewaitable.c new file mode 100644 index 0000000..d90a9cb --- /dev/null +++ b/app/core/gimptriviallycancelablewaitable.c @@ -0,0 +1,92 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptriviallycancelablewaitable.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpcancelable.h" +#include "gimptriviallycancelablewaitable.h" +#include "gimpwaitable.h" + + +/* local function prototypes */ + +static void gimp_trivially_cancelable_waitable_cancelable_iface_init (GimpCancelableInterface *iface); + +static void gimp_trivially_cancelable_waitable_cancel (GimpCancelable *cancelable); + + +G_DEFINE_TYPE_WITH_CODE (GimpTriviallyCancelableWaitable, gimp_trivially_cancelable_waitable, GIMP_TYPE_UNCANCELABLE_WAITABLE, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CANCELABLE, + gimp_trivially_cancelable_waitable_cancelable_iface_init)) + +#define parent_class gimp_trivially_cancelable_waitable_parent_class + + +/* private functions */ + + +static void +gimp_trivially_cancelable_waitable_class_init (GimpTriviallyCancelableWaitableClass *klass) +{ +} + +static void +gimp_trivially_cancelable_waitable_cancelable_iface_init (GimpCancelableInterface *iface) +{ + iface->cancel = gimp_trivially_cancelable_waitable_cancel; +} + +static void +gimp_trivially_cancelable_waitable_init (GimpTriviallyCancelableWaitable *trivially_cancelable_waitable) +{ +} + +static void +gimp_trivially_cancelable_waitable_cancel (GimpCancelable *cancelable) +{ + GimpUncancelableWaitable *uncancelable_waitable = + GIMP_UNCANCELABLE_WAITABLE (cancelable); + + g_clear_object (&uncancelable_waitable->waitable); +} + + +/* public functions */ + + +GimpWaitable * +gimp_trivially_cancelable_waitable_new (GimpWaitable *waitable) +{ + GimpUncancelableWaitable *uncancelable_waitable; + + g_return_val_if_fail (GIMP_IS_WAITABLE (waitable), NULL); + + uncancelable_waitable = g_object_new (GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE, + NULL); + + uncancelable_waitable->waitable = g_object_ref (waitable); + + return GIMP_WAITABLE (uncancelable_waitable); +} diff --git a/app/core/gimptriviallycancelablewaitable.h b/app/core/gimptriviallycancelablewaitable.h new file mode 100644 index 0000000..bb09a52 --- /dev/null +++ b/app/core/gimptriviallycancelablewaitable.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptriviallycancelablewaitable.h + * 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 . + */ + +#ifndef __GIMP_TRIVIALLY_CANCELABLE_WAITABLE_H__ +#define __GIMP_TRIVIALLY_CANCELABLE_WAITABLE_H__ + + +#include "gimpuncancelablewaitable.h" + + +#define GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE (gimp_trivially_cancelable_waitable_get_type ()) +#define GIMP_TRIVIALLY_CANCELABLE_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE, GimpTriviallyCancelableWaitable)) +#define GIMP_TRIVIALLY_CANCELABLE_WAITABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE, GimpTriviallyCancelableWaitableClass)) +#define GIMP_IS_TRIVIALLY_CANCELABLE_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE)) +#define GIMP_IS_TRIVIALLY_CANCELABLE_WAITABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE)) +#define GIMP_TRIVIALLY_CANCELABLE_WAITABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TRIVIALLY_CANCELABLE_WAITABLE, GimpTriviallyCancelableWaitableClass)) + + +typedef struct _GimpTriviallyCancelableWaitablePrivate GimpTriviallyCancelableWaitablePrivate; +typedef struct _GimpTriviallyCancelableWaitableClass GimpTriviallyCancelableWaitableClass; + +struct _GimpTriviallyCancelableWaitable +{ + GimpUncancelableWaitable parent_instance; + + GimpTriviallyCancelableWaitablePrivate *priv; +}; + +struct _GimpTriviallyCancelableWaitableClass +{ + GimpUncancelableWaitableClass parent_class; +}; + + +GType gimp_trivially_cancelable_waitable_get_type (void) G_GNUC_CONST; + +GimpWaitable * gimp_trivially_cancelable_waitable_new (GimpWaitable *waitable); + + +#endif /* __GIMP_TRIVIALLY_CANCELABLE_WAITABLE_H__ */ diff --git a/app/core/gimpuncancelablewaitable.c b/app/core/gimpuncancelablewaitable.c new file mode 100644 index 0000000..596efed --- /dev/null +++ b/app/core/gimpuncancelablewaitable.c @@ -0,0 +1,137 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpuncancelablewaitable.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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpuncancelablewaitable.h" +#include "gimpwaitable.h" + + +/* local function prototypes */ + +static void gimp_uncancelable_waitable_waitable_iface_init (GimpWaitableInterface *iface); + +static void gimp_uncancelable_waitable_finalize (GObject *object); + +static void gimp_uncancelable_waitable_wait (GimpWaitable *waitable); +static gboolean gimp_uncancelable_waitable_try_wait (GimpWaitable *waitable); +static gboolean gimp_uncancelable_waitable_wait_until (GimpWaitable *waitable, + gint64 end_time); + + +G_DEFINE_TYPE_WITH_CODE (GimpUncancelableWaitable, gimp_uncancelable_waitable, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_WAITABLE, + gimp_uncancelable_waitable_waitable_iface_init)) + +#define parent_class gimp_uncancelable_waitable_parent_class + + +/* private functions */ + + +static void +gimp_uncancelable_waitable_class_init (GimpUncancelableWaitableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_uncancelable_waitable_finalize; +} + +static void +gimp_uncancelable_waitable_waitable_iface_init (GimpWaitableInterface *iface) +{ + iface->wait = gimp_uncancelable_waitable_wait; + iface->try_wait = gimp_uncancelable_waitable_try_wait; + iface->wait_until = gimp_uncancelable_waitable_wait_until; +} + +static void +gimp_uncancelable_waitable_init (GimpUncancelableWaitable *uncancelable_waitable) +{ +} + +static void +gimp_uncancelable_waitable_finalize (GObject *object) +{ + GimpUncancelableWaitable *uncancelable_waitable = + GIMP_UNCANCELABLE_WAITABLE (object); + + g_clear_object (&uncancelable_waitable->waitable); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_uncancelable_waitable_wait (GimpWaitable *waitable) +{ + GimpUncancelableWaitable *uncancelable_waitable = + GIMP_UNCANCELABLE_WAITABLE (waitable); + + if (uncancelable_waitable->waitable) + gimp_waitable_wait (uncancelable_waitable->waitable); +} + +static gboolean +gimp_uncancelable_waitable_try_wait (GimpWaitable *waitable) +{ + GimpUncancelableWaitable *uncancelable_waitable = + GIMP_UNCANCELABLE_WAITABLE (waitable); + + if (uncancelable_waitable->waitable) + return gimp_waitable_try_wait (uncancelable_waitable->waitable); + else + return TRUE; +} + +static gboolean +gimp_uncancelable_waitable_wait_until (GimpWaitable *waitable, + gint64 end_time) +{ + GimpUncancelableWaitable *uncancelable_waitable = + GIMP_UNCANCELABLE_WAITABLE (waitable); + + if (uncancelable_waitable->waitable) + return gimp_waitable_wait_until (uncancelable_waitable->waitable, end_time); + else + return TRUE; +} + + +/* public functions */ + + +GimpWaitable * +gimp_uncancelable_waitable_new (GimpWaitable *waitable) +{ + GimpUncancelableWaitable *uncancelable_waitable; + + g_return_val_if_fail (GIMP_IS_WAITABLE (waitable), NULL); + + uncancelable_waitable = g_object_new (GIMP_TYPE_UNCANCELABLE_WAITABLE, NULL); + + uncancelable_waitable->waitable = g_object_ref (waitable); + + return GIMP_WAITABLE (uncancelable_waitable); +} diff --git a/app/core/gimpuncancelablewaitable.h b/app/core/gimpuncancelablewaitable.h new file mode 100644 index 0000000..90b26ff --- /dev/null +++ b/app/core/gimpuncancelablewaitable.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpuncancelablewaitable.h + * 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 . + */ + +#ifndef __GIMP_UNCANCELABLE_WAITABLE_H__ +#define __GIMP_UNCANCELABLE_WAITABLE_H__ + + +#define GIMP_TYPE_UNCANCELABLE_WAITABLE (gimp_uncancelable_waitable_get_type ()) +#define GIMP_UNCANCELABLE_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNCANCELABLE_WAITABLE, GimpUncancelableWaitable)) +#define GIMP_UNCANCELABLE_WAITABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNCANCELABLE_WAITABLE, GimpUncancelableWaitableClass)) +#define GIMP_IS_UNCANCELABLE_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNCANCELABLE_WAITABLE)) +#define GIMP_IS_UNCANCELABLE_WAITABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNCANCELABLE_WAITABLE)) +#define GIMP_UNCANCELABLE_WAITABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNCANCELABLE_WAITABLE, GimpUncancelableWaitableClass)) + + +typedef struct _GimpUncancelableWaitablePrivate GimpUncancelableWaitablePrivate; +typedef struct _GimpUncancelableWaitableClass GimpUncancelableWaitableClass; + +struct _GimpUncancelableWaitable +{ + GObject parent_instance; + + GimpWaitable *waitable; +}; + +struct _GimpUncancelableWaitableClass +{ + GObjectClass parent_class; +}; + + +GType gimp_uncancelable_waitable_get_type (void) G_GNUC_CONST; + +GimpWaitable * gimp_uncancelable_waitable_new (GimpWaitable *waitable); + + +#endif /* __GIMP_UNCANCELABLE_WAITABLE_H__ */ diff --git a/app/core/gimpundo.c b/app/core/gimpundo.c new file mode 100644 index 0000000..68bbae0 --- /dev/null +++ b/app/core/gimpundo.c @@ -0,0 +1,585 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpmarshal.h" +#include "gimptempbuf.h" +#include "gimpundo.h" +#include "gimpundostack.h" + +#include "gimp-priorities.h" + +#include "gimp-intl.h" + + +enum +{ + POP, + FREE, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_IMAGE, + PROP_TIME, + PROP_UNDO_TYPE, + PROP_DIRTY_MASK +}; + + +static void gimp_undo_constructed (GObject *object); +static void gimp_undo_finalize (GObject *object); +static void gimp_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_undo_get_memsize (GimpObject *object, + gint64 *gui_size); + +static gboolean gimp_undo_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_undo_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + +static void gimp_undo_real_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_undo_real_free (GimpUndo *undo, + GimpUndoMode undo_mode); + +static gboolean gimp_undo_create_preview_idle (gpointer data); +static void gimp_undo_create_preview_private (GimpUndo *undo, + GimpContext *context); + + +G_DEFINE_TYPE (GimpUndo, gimp_undo, GIMP_TYPE_VIEWABLE) + +#define parent_class gimp_undo_parent_class + +static guint undo_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_undo_class_init (GimpUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + undo_signals[POP] = + g_signal_new ("pop", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpUndoClass, pop), + NULL, NULL, + gimp_marshal_VOID__ENUM_POINTER, + G_TYPE_NONE, 2, + GIMP_TYPE_UNDO_MODE, + G_TYPE_POINTER); + + undo_signals[FREE] = + g_signal_new ("free", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpUndoClass, free), + NULL, NULL, + gimp_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GIMP_TYPE_UNDO_MODE); + + object_class->constructed = gimp_undo_constructed; + object_class->finalize = gimp_undo_finalize; + object_class->set_property = gimp_undo_set_property; + object_class->get_property = gimp_undo_get_property; + + gimp_object_class->get_memsize = gimp_undo_get_memsize; + + viewable_class->default_icon_name = "edit-undo"; + viewable_class->get_popup_size = gimp_undo_get_popup_size; + viewable_class->get_new_preview = gimp_undo_get_new_preview; + + klass->pop = gimp_undo_real_pop; + klass->free = gimp_undo_real_free; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_TIME, + g_param_spec_uint ("time", NULL, NULL, + 0, G_MAXUINT, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_UNDO_TYPE, + g_param_spec_enum ("undo-type", NULL, NULL, + GIMP_TYPE_UNDO_TYPE, + GIMP_UNDO_GROUP_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_DIRTY_MASK, + g_param_spec_flags ("dirty-mask", + NULL, NULL, + GIMP_TYPE_DIRTY_MASK, + GIMP_DIRTY_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_undo_init (GimpUndo *undo) +{ + undo->time = time (NULL); +} + +static void +gimp_undo_constructed (GObject *object) +{ + GimpUndo *undo = GIMP_UNDO (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_IMAGE (undo->image)); +} + +static void +gimp_undo_finalize (GObject *object) +{ + GimpUndo *undo = GIMP_UNDO (object); + + if (undo->preview_idle_id) + { + g_source_remove (undo->preview_idle_id); + undo->preview_idle_id = 0; + } + + g_clear_pointer (&undo->preview, gimp_temp_buf_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_undo_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpUndo *undo = GIMP_UNDO (object); + + switch (property_id) + { + case PROP_IMAGE: + /* don't ref */ + undo->image = g_value_get_object (value); + break; + case PROP_TIME: + undo->time = g_value_get_uint (value); + break; + case PROP_UNDO_TYPE: + undo->undo_type = g_value_get_enum (value); + break; + case PROP_DIRTY_MASK: + undo->dirty_mask = g_value_get_flags (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_undo_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpUndo *undo = GIMP_UNDO (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, undo->image); + break; + case PROP_TIME: + g_value_set_uint (value, undo->time); + break; + case PROP_UNDO_TYPE: + g_value_set_enum (value, undo->undo_type); + break; + case PROP_DIRTY_MASK: + g_value_set_flags (value, undo->dirty_mask); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_undo_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpUndo *undo = GIMP_UNDO (object); + gint64 memsize = 0; + + *gui_size += gimp_temp_buf_get_memsize (undo->preview); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gboolean +gimp_undo_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + GimpUndo *undo = GIMP_UNDO (viewable); + + if (undo->preview && + (gimp_temp_buf_get_width (undo->preview) > width || + gimp_temp_buf_get_height (undo->preview) > height)) + { + *popup_width = gimp_temp_buf_get_width (undo->preview); + *popup_height = gimp_temp_buf_get_height (undo->preview); + + return TRUE; + } + + return FALSE; +} + +static GimpTempBuf * +gimp_undo_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpUndo *undo = GIMP_UNDO (viewable); + + if (undo->preview) + { + gint preview_width; + gint preview_height; + + gimp_viewable_calc_preview_size (gimp_temp_buf_get_width (undo->preview), + gimp_temp_buf_get_height (undo->preview), + width, + height, + TRUE, 1.0, 1.0, + &preview_width, + &preview_height, + NULL); + + if (preview_width < gimp_temp_buf_get_width (undo->preview) && + preview_height < gimp_temp_buf_get_height (undo->preview)) + { + return gimp_temp_buf_scale (undo->preview, + preview_width, preview_height); + } + + return gimp_temp_buf_copy (undo->preview); + } + + return NULL; +} + +static void +gimp_undo_real_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ +} + +static void +gimp_undo_real_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ +} + +void +gimp_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + g_return_if_fail (GIMP_IS_UNDO (undo)); + g_return_if_fail (accum != NULL); + + if (undo->dirty_mask != GIMP_DIRTY_NONE) + { + switch (undo_mode) + { + case GIMP_UNDO_MODE_UNDO: + gimp_image_clean (undo->image, undo->dirty_mask); + break; + + case GIMP_UNDO_MODE_REDO: + gimp_image_dirty (undo->image, undo->dirty_mask); + break; + } + } + + g_signal_emit (undo, undo_signals[POP], 0, undo_mode, accum); +} + +void +gimp_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + g_return_if_fail (GIMP_IS_UNDO (undo)); + + g_signal_emit (undo, undo_signals[FREE], 0, undo_mode); +} + +typedef struct _GimpUndoIdle GimpUndoIdle; + +struct _GimpUndoIdle +{ + GimpUndo *undo; + GimpContext *context; +}; + +static void +gimp_undo_idle_free (GimpUndoIdle *idle) +{ + if (idle->context) + g_object_unref (idle->context); + + g_slice_free (GimpUndoIdle, idle); +} + +void +gimp_undo_create_preview (GimpUndo *undo, + GimpContext *context, + gboolean create_now) +{ + g_return_if_fail (GIMP_IS_UNDO (undo)); + g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context)); + + if (undo->preview || undo->preview_idle_id) + return; + + if (create_now) + { + gimp_undo_create_preview_private (undo, context); + } + else + { + GimpUndoIdle *idle = g_slice_new0 (GimpUndoIdle); + + idle->undo = undo; + + if (context) + idle->context = g_object_ref (context); + + undo->preview_idle_id = + g_idle_add_full (GIMP_PRIORITY_VIEWABLE_IDLE, + gimp_undo_create_preview_idle, idle, + (GDestroyNotify) gimp_undo_idle_free); + } +} + +static gboolean +gimp_undo_create_preview_idle (gpointer data) +{ + GimpUndoIdle *idle = data; + GimpUndoStack *stack = gimp_image_get_undo_stack (idle->undo->image); + + if (idle->undo == gimp_undo_stack_peek (stack)) + { + gimp_undo_create_preview_private (idle->undo, idle->context); + } + + idle->undo->preview_idle_id = 0; + + return FALSE; +} + +static void +gimp_undo_create_preview_private (GimpUndo *undo, + GimpContext *context) +{ + GimpImage *image = undo->image; + GimpViewable *preview_viewable; + GimpViewSize preview_size; + gint width; + gint height; + + switch (undo->undo_type) + { + case GIMP_UNDO_GROUP_IMAGE_QUICK_MASK: + case GIMP_UNDO_GROUP_MASK: + case GIMP_UNDO_MASK: + preview_viewable = GIMP_VIEWABLE (gimp_image_get_mask (image)); + break; + + default: + preview_viewable = GIMP_VIEWABLE (image); + break; + } + + preview_size = image->gimp->config->undo_preview_size; + + if (gimp_image_get_width (image) <= preview_size && + gimp_image_get_height (image) <= preview_size) + { + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + } + else + { + if (gimp_image_get_width (image) > gimp_image_get_height (image)) + { + width = preview_size; + height = MAX (1, (gimp_image_get_height (image) * preview_size / + gimp_image_get_width (image))); + } + else + { + height = preview_size; + width = MAX (1, (gimp_image_get_width (image) * preview_size / + gimp_image_get_height (image))); + } + } + + undo->preview = gimp_viewable_get_new_preview (preview_viewable, context, + width, height); + + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (undo)); +} + +void +gimp_undo_refresh_preview (GimpUndo *undo, + GimpContext *context) +{ + g_return_if_fail (GIMP_IS_UNDO (undo)); + g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context)); + + if (undo->preview_idle_id) + return; + + if (undo->preview) + { + g_clear_pointer (&undo->preview, gimp_temp_buf_unref); + gimp_undo_create_preview (undo, context, FALSE); + } +} + +const gchar * +gimp_undo_type_to_name (GimpUndoType type) +{ + const gchar *desc; + + if (gimp_enum_get_value (GIMP_TYPE_UNDO_TYPE, type, NULL, NULL, &desc, NULL)) + return desc; + else + return ""; +} + +gboolean +gimp_undo_is_weak (GimpUndo *undo) +{ + if (! undo) + return FALSE; + + switch (undo->undo_type) + { + case GIMP_UNDO_GROUP_ITEM_VISIBILITY: + case GIMP_UNDO_GROUP_ITEM_PROPERTIES: + case GIMP_UNDO_GROUP_LAYER_APPLY_MASK: + case GIMP_UNDO_ITEM_VISIBILITY: + case GIMP_UNDO_LAYER_MODE: + case GIMP_UNDO_LAYER_OPACITY: + case GIMP_UNDO_LAYER_MASK_APPLY: + case GIMP_UNDO_LAYER_MASK_SHOW: + return TRUE; + break; + + default: + break; + } + + return FALSE; +} + +/** + * gimp_undo_get_age: + * @undo: + * + * Return value: the time in seconds since this undo item was created + */ +gint +gimp_undo_get_age (GimpUndo *undo) +{ + guint now = time (NULL); + + g_return_val_if_fail (GIMP_IS_UNDO (undo), 0); + g_return_val_if_fail (now >= undo->time, 0); + + return now - undo->time; +} + +/** + * gimp_undo_reset_age: + * @undo: + * + * Changes the creation time of this undo item to the current time. + */ +void +gimp_undo_reset_age (GimpUndo *undo) +{ + g_return_if_fail (GIMP_IS_UNDO (undo)); + + undo->time = time (NULL); + + g_object_notify (G_OBJECT (undo), "time"); +} diff --git a/app/core/gimpundo.h b/app/core/gimpundo.h new file mode 100644 index 0000000..03d03c2 --- /dev/null +++ b/app/core/gimpundo.h @@ -0,0 +1,99 @@ +/* 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 . + */ + +#ifndef __GIMP_UNDO_H__ +#define __GIMP_UNDO_H__ + + +#include "gimpviewable.h" + + +struct _GimpUndoAccumulator +{ + gboolean mode_changed; + gboolean precision_changed; + + gboolean size_changed; + gint previous_origin_x; + gint previous_origin_y; + gint previous_width; + gint previous_height; + + gboolean resolution_changed; + + gboolean unit_changed; +}; + + +#define GIMP_TYPE_UNDO (gimp_undo_get_type ()) +#define GIMP_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNDO, GimpUndo)) +#define GIMP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNDO, GimpUndoClass)) +#define GIMP_IS_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNDO)) +#define GIMP_IS_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNDO)) +#define GIMP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNDO, GimpUndoClass)) + + +typedef struct _GimpUndoClass GimpUndoClass; + +struct _GimpUndo +{ + GimpViewable parent_instance; + + GimpImage *image; /* the image this undo is part of */ + guint time; /* time of undo step construction */ + + GimpUndoType undo_type; /* undo type */ + GimpDirtyMask dirty_mask; /* affected parts of the image */ + + GimpTempBuf *preview; + guint preview_idle_id; +}; + +struct _GimpUndoClass +{ + GimpViewableClass parent_class; + + void (* pop) (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + void (* free) (GimpUndo *undo, + GimpUndoMode undo_mode); +}; + + +GType gimp_undo_get_type (void) G_GNUC_CONST; + +void gimp_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +void gimp_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + +void gimp_undo_create_preview (GimpUndo *undo, + GimpContext *context, + gboolean create_now); +void gimp_undo_refresh_preview (GimpUndo *undo, + GimpContext *context); + +const gchar * gimp_undo_type_to_name (GimpUndoType type); + +gboolean gimp_undo_is_weak (GimpUndo *undo); +gint gimp_undo_get_age (GimpUndo *undo); +void gimp_undo_reset_age (GimpUndo *undo); + + +#endif /* __GIMP_UNDO_H__ */ diff --git a/app/core/gimpundostack.c b/app/core/gimpundostack.c new file mode 100644 index 0000000..666ed9b --- /dev/null +++ b/app/core/gimpundostack.c @@ -0,0 +1,208 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpimage.h" +#include "gimplist.h" +#include "gimpundo.h" +#include "gimpundostack.h" + + +static void gimp_undo_stack_finalize (GObject *object); + +static gint64 gimp_undo_stack_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_undo_stack_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_undo_stack_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpUndoStack, gimp_undo_stack, GIMP_TYPE_UNDO) + +#define parent_class gimp_undo_stack_parent_class + + +static void +gimp_undo_stack_class_init (GimpUndoStackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->finalize = gimp_undo_stack_finalize; + + gimp_object_class->get_memsize = gimp_undo_stack_get_memsize; + + undo_class->pop = gimp_undo_stack_pop; + undo_class->free = gimp_undo_stack_free; +} + +static void +gimp_undo_stack_init (GimpUndoStack *stack) +{ + stack->undos = gimp_list_new (GIMP_TYPE_UNDO, FALSE); +} + +static void +gimp_undo_stack_finalize (GObject *object) +{ + GimpUndoStack *stack = GIMP_UNDO_STACK (object); + + g_clear_object (&stack->undos); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint64 +gimp_undo_stack_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpUndoStack *stack = GIMP_UNDO_STACK (object); + gint64 memsize = 0; + + memsize += gimp_object_get_memsize (GIMP_OBJECT (stack->undos), gui_size); + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_undo_stack_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpUndoStack *stack = GIMP_UNDO_STACK (undo); + GList *list; + + for (list = GIMP_LIST (stack->undos)->queue->head; + list; + list = g_list_next (list)) + { + GimpUndo *child = list->data; + + gimp_undo_pop (child, undo_mode, accum); + } +} + +static void +gimp_undo_stack_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpUndoStack *stack = GIMP_UNDO_STACK (undo); + GList *list; + + for (list = GIMP_LIST (stack->undos)->queue->head; + list; + list = g_list_next (list)) + { + GimpUndo *child = list->data; + + gimp_undo_free (child, undo_mode); + g_object_unref (child); + } + + gimp_container_clear (stack->undos); +} + +GimpUndoStack * +gimp_undo_stack_new (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return g_object_new (GIMP_TYPE_UNDO_STACK, + "image", image, + NULL); +} + +void +gimp_undo_stack_push_undo (GimpUndoStack *stack, + GimpUndo *undo) +{ + g_return_if_fail (GIMP_IS_UNDO_STACK (stack)); + g_return_if_fail (GIMP_IS_UNDO (undo)); + + gimp_container_add (stack->undos, GIMP_OBJECT (undo)); +} + +GimpUndo * +gimp_undo_stack_pop_undo (GimpUndoStack *stack, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), NULL); + g_return_val_if_fail (accum != NULL, NULL); + + undo = GIMP_UNDO (gimp_container_get_first_child (stack->undos)); + + if (undo) + { + gimp_container_remove (stack->undos, GIMP_OBJECT (undo)); + gimp_undo_pop (undo, undo_mode, accum); + + return undo; + } + + return NULL; +} + +GimpUndo * +gimp_undo_stack_free_bottom (GimpUndoStack *stack, + GimpUndoMode undo_mode) +{ + GimpUndo *undo; + + g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), NULL); + + undo = GIMP_UNDO (gimp_container_get_last_child (stack->undos)); + + if (undo) + { + gimp_container_remove (stack->undos, GIMP_OBJECT (undo)); + gimp_undo_free (undo, undo_mode); + + return undo; + } + + return NULL; +} + +GimpUndo * +gimp_undo_stack_peek (GimpUndoStack *stack) +{ + g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), NULL); + + return GIMP_UNDO (gimp_container_get_first_child (stack->undos)); +} + +gint +gimp_undo_stack_get_depth (GimpUndoStack *stack) +{ + g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), 0); + + return gimp_container_get_n_children (stack->undos); +} diff --git a/app/core/gimpundostack.h b/app/core/gimpundostack.h new file mode 100644 index 0000000..8432df0 --- /dev/null +++ b/app/core/gimpundostack.h @@ -0,0 +1,64 @@ +/* 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 . + */ + +#ifndef __GIMP_UNDO_STACK_H__ +#define __GIMP_UNDO_STACK_H__ + + +#include "gimpundo.h" + + +#define GIMP_TYPE_UNDO_STACK (gimp_undo_stack_get_type ()) +#define GIMP_UNDO_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNDO_STACK, GimpUndoStack)) +#define GIMP_UNDO_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNDO_STACK, GimpUndoStackClass)) +#define GIMP_IS_UNDO_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNDO_STACK)) +#define GIMP_IS_UNDO_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNDO_STACK)) +#define GIMP_UNDO_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNDO_STACK, GimpUndoStackClass)) + + +typedef struct _GimpUndoStackClass GimpUndoStackClass; + +struct _GimpUndoStack +{ + GimpUndo parent_instance; + + GimpContainer *undos; +}; + +struct _GimpUndoStackClass +{ + GimpUndoClass parent_class; +}; + + +GType gimp_undo_stack_get_type (void) G_GNUC_CONST; + +GimpUndoStack * gimp_undo_stack_new (GimpImage *image); + +void gimp_undo_stack_push_undo (GimpUndoStack *stack, + GimpUndo *undo); +GimpUndo * gimp_undo_stack_pop_undo (GimpUndoStack *stack, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); + +GimpUndo * gimp_undo_stack_free_bottom (GimpUndoStack *stack, + GimpUndoMode undo_mode); +GimpUndo * gimp_undo_stack_peek (GimpUndoStack *stack); +gint gimp_undo_stack_get_depth (GimpUndoStack *stack); + + +#endif /* __GIMP_UNDO_STACK_H__ */ diff --git a/app/core/gimpunit.c b/app/core/gimpunit.c new file mode 100644 index 0000000..3fad305 --- /dev/null +++ b/app/core/gimpunit.c @@ -0,0 +1,305 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpunit.c + * Copyright (C) 1999-2000 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains the definition of the size unit objects. The + * factor of the units is relative to inches (which have a factor of 1). + */ + +#include "config.h" + +#include + +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "gimp.h" +#include "gimpunit.h" + +#include "gimp-intl.h" + + +/* internal structures */ + +typedef struct +{ + gboolean delete_on_exit; + gdouble factor; + gint digits; + gchar *identifier; + gchar *symbol; + gchar *abbreviation; + gchar *singular; + gchar *plural; +} GimpUnitDef; + + +/* these are the built-in units + */ +static const GimpUnitDef gimp_unit_defs[GIMP_UNIT_END] = +{ + /* pseudo unit */ + { FALSE, 0.0, 0, "pixels", "px", "px", + NC_("unit-singular", "pixel"), NC_("unit-plural", "pixels") }, + + /* standard units */ + { FALSE, 1.0, 2, "inches", "''", "in", + NC_("unit-singular", "inch"), NC_("unit-plural", "inches") }, + + { FALSE, 25.4, 1, "millimeters", "mm", "mm", + NC_("unit-singular", "millimeter"), NC_("unit-plural", "millimeters") }, + + /* professional units */ + { FALSE, 72.0, 0, "points", "pt", "pt", + NC_("unit-singular", "point"), NC_("unit-plural", "points") }, + + { FALSE, 6.0, 1, "picas", "pc", "pc", + NC_("unit-singular", "pica"), NC_("unit-plural", "picas") } +}; + +/* not a unit at all but kept here to have the strings in one place + */ +static const GimpUnitDef gimp_unit_percent = +{ + FALSE, 0.0, 0, "percent", "%", "%", + NC_("singular", "percent"), NC_("plural", "percent") +}; + + +/* private functions */ + +static GimpUnitDef * +_gimp_unit_get_user_unit (Gimp *gimp, + GimpUnit unit) +{ + return g_list_nth_data (gimp->user_units, unit - GIMP_UNIT_END); +} + + +/* public functions */ + +gint +_gimp_unit_get_number_of_units (Gimp *gimp) +{ + return GIMP_UNIT_END + gimp->n_user_units; +} + +gint +_gimp_unit_get_number_of_built_in_units (Gimp *gimp) +{ + return GIMP_UNIT_END; +} + +GimpUnit +_gimp_unit_new (Gimp *gimp, + const gchar *identifier, + gdouble factor, + gint digits, + const gchar *symbol, + const gchar *abbreviation, + const gchar *singular, + const gchar *plural) +{ + GimpUnitDef *user_unit = g_slice_new0 (GimpUnitDef); + + user_unit->delete_on_exit = TRUE; + user_unit->factor = factor; + user_unit->digits = digits; + user_unit->identifier = g_strdup (identifier); + user_unit->symbol = g_strdup (symbol); + user_unit->abbreviation = g_strdup (abbreviation); + user_unit->singular = g_strdup (singular); + user_unit->plural = g_strdup (plural); + + gimp->user_units = g_list_append (gimp->user_units, user_unit); + gimp->n_user_units++; + + return GIMP_UNIT_END + gimp->n_user_units - 1; +} + +gboolean +_gimp_unit_get_deletion_flag (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail (unit < (GIMP_UNIT_END + gimp->n_user_units), FALSE); + + if (unit < GIMP_UNIT_END) + return FALSE; + + return _gimp_unit_get_user_unit (gimp, unit)->delete_on_exit; +} + +void +_gimp_unit_set_deletion_flag (Gimp *gimp, + GimpUnit unit, + gboolean deletion_flag) +{ + g_return_if_fail ((unit >= GIMP_UNIT_END) && + (unit < (GIMP_UNIT_END + gimp->n_user_units))); + + _gimp_unit_get_user_unit (gimp, unit)->delete_on_exit = + deletion_flag ? TRUE : FALSE; +} + +gdouble +_gimp_unit_get_factor (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail (unit < (GIMP_UNIT_END + gimp->n_user_units) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].factor); + + if (unit < GIMP_UNIT_END) + return gimp_unit_defs[unit].factor; + + if (unit == GIMP_UNIT_PERCENT) + return gimp_unit_percent.factor; + + return _gimp_unit_get_user_unit (gimp, unit)->factor; +} + +gint +_gimp_unit_get_digits (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail (unit < (GIMP_UNIT_END + gimp->n_user_units) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].digits); + + if (unit < GIMP_UNIT_END) + return gimp_unit_defs[unit].digits; + + if (unit == GIMP_UNIT_PERCENT) + return gimp_unit_percent.digits; + + return _gimp_unit_get_user_unit (gimp, unit)->digits; +} + +const gchar * +_gimp_unit_get_identifier (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail ((unit < (GIMP_UNIT_END + gimp->n_user_units)) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].identifier); + + if (unit < GIMP_UNIT_END) + return gimp_unit_defs[unit].identifier; + + if (unit == GIMP_UNIT_PERCENT) + return gimp_unit_percent.identifier; + + return _gimp_unit_get_user_unit (gimp, unit)->identifier; +} + +const gchar * +_gimp_unit_get_symbol (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail ((unit < (GIMP_UNIT_END + gimp->n_user_units)) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].symbol); + + if (unit < GIMP_UNIT_END) + return gimp_unit_defs[unit].symbol; + + if (unit == GIMP_UNIT_PERCENT) + return gimp_unit_percent.symbol; + + return _gimp_unit_get_user_unit (gimp, unit)->symbol; +} + +const gchar * +_gimp_unit_get_abbreviation (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail ((unit < (GIMP_UNIT_END + gimp->n_user_units)) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].abbreviation); + + if (unit < GIMP_UNIT_END) + return gimp_unit_defs[unit].abbreviation; + + if (unit == GIMP_UNIT_PERCENT) + return gimp_unit_percent.abbreviation; + + return _gimp_unit_get_user_unit (gimp, unit)->abbreviation; +} + +const gchar * +_gimp_unit_get_singular (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail ((unit < (GIMP_UNIT_END + gimp->n_user_units)) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].singular); + + if (unit < GIMP_UNIT_END) + return g_dpgettext2 (NULL, "unit-singular", gimp_unit_defs[unit].singular); + + if (unit == GIMP_UNIT_PERCENT) + return g_dpgettext2 (NULL, "unit-singular", gimp_unit_percent.singular); + + return _gimp_unit_get_user_unit (gimp, unit)->singular; +} + +const gchar * +_gimp_unit_get_plural (Gimp *gimp, + GimpUnit unit) +{ + g_return_val_if_fail ((unit < (GIMP_UNIT_END + gimp->n_user_units)) || + (unit == GIMP_UNIT_PERCENT), + gimp_unit_defs[GIMP_UNIT_INCH].plural); + + if (unit < GIMP_UNIT_END) + return g_dpgettext2 (NULL, "unit-plural", gimp_unit_defs[unit].plural); + + if (unit == GIMP_UNIT_PERCENT) + return g_dpgettext2 (NULL, "unit-plural", gimp_unit_percent.plural); + + return _gimp_unit_get_user_unit (gimp, unit)->plural; +} + + +/* The sole purpose of this function is to release the allocated + * memory. It must only be used from gimp_units_exit(). + */ +void +gimp_user_units_free (Gimp *gimp) +{ + GList *list; + + for (list = gimp->user_units; list; list = g_list_next (list)) + { + GimpUnitDef *user_unit = list->data; + + g_free (user_unit->identifier); + g_free (user_unit->symbol); + g_free (user_unit->abbreviation); + g_free (user_unit->singular); + g_free (user_unit->plural); + + g_slice_free (GimpUnitDef, user_unit); + } + + g_list_free (gimp->user_units); + gimp->user_units = NULL; + gimp->n_user_units = 0; +} diff --git a/app/core/gimpunit.h b/app/core/gimpunit.h new file mode 100644 index 0000000..60dbc75 --- /dev/null +++ b/app/core/gimpunit.h @@ -0,0 +1,61 @@ +/* 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 . + */ + +#ifndef __APP_GIMP_UNIT_H__ +#define __APP_GIMP_UNIT_H__ + + +gint _gimp_unit_get_number_of_units (Gimp *gimp); +gint _gimp_unit_get_number_of_built_in_units (Gimp *gimp) G_GNUC_CONST; + +GimpUnit _gimp_unit_new (Gimp *gimp, + const gchar *identifier, + gdouble factor, + gint digits, + const gchar *symbol, + const gchar *abbreviation, + const gchar *singular, + const gchar *plural); + +gboolean _gimp_unit_get_deletion_flag (Gimp *gimp, + GimpUnit unit); +void _gimp_unit_set_deletion_flag (Gimp *gimp, + GimpUnit unit, + gboolean deletion_flag); + +gdouble _gimp_unit_get_factor (Gimp *gimp, + GimpUnit unit); + +gint _gimp_unit_get_digits (Gimp *gimp, + GimpUnit unit); + +const gchar * _gimp_unit_get_identifier (Gimp *gimp, + GimpUnit unit); + +const gchar * _gimp_unit_get_symbol (Gimp *gimp, + GimpUnit unit); +const gchar * _gimp_unit_get_abbreviation (Gimp *gimp, + GimpUnit unit); +const gchar * _gimp_unit_get_singular (Gimp *gimp, + GimpUnit unit); +const gchar * _gimp_unit_get_plural (Gimp *gimp, + GimpUnit unit); + +void gimp_user_units_free (Gimp *gimp); + + +#endif /* __APP_GIMP_UNIT_H__ */ diff --git a/app/core/gimpviewable.c b/app/core/gimpviewable.c new file mode 100644 index 0000000..5b547a2 --- /dev/null +++ b/app/core/gimpviewable.c @@ -0,0 +1,1430 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpviewable.c + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpmarshal.h" +#include "gimptempbuf.h" +#include "gimpviewable.h" + +#include "icons/Color/gimp-core-pixbufs.c" + + +enum +{ + PROP_0, + PROP_STOCK_ID, /* compat */ + PROP_ICON_NAME, + PROP_ICON_PIXBUF, + PROP_FROZEN +}; + +enum +{ + INVALIDATE_PREVIEW, + SIZE_CHANGED, + EXPANDED_CHANGED, + ANCESTRY_CHANGED, + LAST_SIGNAL +}; + + +typedef struct _GimpViewablePrivate GimpViewablePrivate; + +struct _GimpViewablePrivate +{ + gchar *icon_name; + GdkPixbuf *icon_pixbuf; + gint freeze_count; + gboolean invalidate_pending; + gboolean size_changed_prending; + GimpViewable *parent; + gint depth; + + GimpTempBuf *preview_temp_buf; + GdkPixbuf *preview_pixbuf; +}; + +#define GET_PRIVATE(viewable) ((GimpViewablePrivate *) gimp_viewable_get_instance_private ((GimpViewable *) (viewable))) + + +static void gimp_viewable_config_iface_init (GimpConfigInterface *iface); + +static void gimp_viewable_finalize (GObject *object); +static void gimp_viewable_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_viewable_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_viewable_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_viewable_real_invalidate_preview (GimpViewable *viewable); +static void gimp_viewable_real_ancestry_changed (GimpViewable *viewable); + +static GdkPixbuf * gimp_viewable_real_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static void gimp_viewable_real_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_viewable_real_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static gchar * gimp_viewable_real_get_description (GimpViewable *viewable, + gchar **tooltip); +static gboolean gimp_viewable_real_is_name_editable (GimpViewable *viewable); +static GimpContainer * gimp_viewable_real_get_children (GimpViewable *viewable); + +static gboolean gimp_viewable_serialize_property (GimpConfig *config, + guint property_id, + const GValue *value, + GParamSpec *pspec, + GimpConfigWriter *writer); +static gboolean gimp_viewable_deserialize_property (GimpConfig *config, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected); + + +G_DEFINE_TYPE_WITH_CODE (GimpViewable, gimp_viewable, GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpViewable) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_viewable_config_iface_init)) + +#define parent_class gimp_viewable_parent_class + +static guint viewable_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_viewable_class_init (GimpViewableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + + viewable_signals[INVALIDATE_PREVIEW] = + g_signal_new ("invalidate-preview", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpViewableClass, invalidate_preview), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + viewable_signals[SIZE_CHANGED] = + g_signal_new ("size-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpViewableClass, size_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + viewable_signals[EXPANDED_CHANGED] = + g_signal_new ("expanded-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpViewableClass, expanded_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + viewable_signals[ANCESTRY_CHANGED] = + g_signal_new ("ancestry-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpViewableClass, ancestry_changed), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->finalize = gimp_viewable_finalize; + object_class->get_property = gimp_viewable_get_property; + object_class->set_property = gimp_viewable_set_property; + + gimp_object_class->get_memsize = gimp_viewable_get_memsize; + + klass->default_icon_name = "gimp-question"; + klass->name_changed_signal = "name-changed"; + klass->name_editable = FALSE; + + klass->invalidate_preview = gimp_viewable_real_invalidate_preview; + klass->size_changed = NULL; + klass->expanded_changed = NULL; + klass->ancestry_changed = gimp_viewable_real_ancestry_changed; + + klass->get_size = NULL; + klass->get_preview_size = gimp_viewable_real_get_preview_size; + klass->get_popup_size = gimp_viewable_real_get_popup_size; + klass->get_preview = NULL; + klass->get_new_preview = NULL; + klass->get_pixbuf = NULL; + klass->get_new_pixbuf = gimp_viewable_real_get_new_pixbuf; + klass->get_description = gimp_viewable_real_get_description; + klass->is_name_editable = gimp_viewable_real_is_name_editable; + klass->preview_freeze = NULL; + klass->preview_thaw = NULL; + klass->get_children = gimp_viewable_real_get_children; + klass->set_expanded = NULL; + klass->get_expanded = NULL; + + /* compat property */ + GIMP_CONFIG_PROP_STRING (object_class, PROP_STOCK_ID, "stock-id", + NULL, NULL, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_STRING (object_class, PROP_ICON_NAME, "icon-name", + NULL, NULL, + NULL, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_ICON_PIXBUF, + "icon-pixbuf", + NULL, NULL, + GDK_TYPE_PIXBUF, + G_PARAM_CONSTRUCT | + GIMP_PARAM_STATIC_STRINGS); + + g_object_class_install_property (object_class, PROP_FROZEN, + g_param_spec_boolean ("frozen", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_viewable_init (GimpViewable *viewable) +{ +} + +static void +gimp_viewable_config_iface_init (GimpConfigInterface *iface) +{ + iface->deserialize_property = gimp_viewable_deserialize_property; + iface->serialize_property = gimp_viewable_serialize_property; +} + +static void +gimp_viewable_finalize (GObject *object) +{ + GimpViewablePrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->icon_name, g_free); + g_clear_object (&private->icon_pixbuf); + g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref); + g_clear_object (&private->preview_pixbuf); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_viewable_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpViewable *viewable = GIMP_VIEWABLE (object); + GimpViewablePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_STOCK_ID: + if (! g_value_get_string (value)) + break; + case PROP_ICON_NAME: + gimp_viewable_set_icon_name (viewable, g_value_get_string (value)); + break; + case PROP_ICON_PIXBUF: + if (private->icon_pixbuf) + g_object_unref (private->icon_pixbuf); + private->icon_pixbuf = g_value_dup_object (value); + gimp_viewable_invalidate_preview (viewable); + break; + case PROP_FROZEN: + /* read-only, fall through */ + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_viewable_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpViewable *viewable = GIMP_VIEWABLE (object); + GimpViewablePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_STOCK_ID: + case PROP_ICON_NAME: + g_value_set_string (value, gimp_viewable_get_icon_name (viewable)); + break; + case PROP_ICON_PIXBUF: + g_value_set_object (value, private->icon_pixbuf); + break; + case PROP_FROZEN: + g_value_set_boolean (value, gimp_viewable_preview_is_frozen (viewable)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_viewable_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpViewablePrivate *private = GET_PRIVATE (object); + + *gui_size += gimp_temp_buf_get_memsize (private->preview_temp_buf); + + if (private->preview_pixbuf) + { + *gui_size += + (gimp_g_object_get_memsize (G_OBJECT (private->preview_pixbuf)) + + (gsize) gdk_pixbuf_get_height (private->preview_pixbuf) * + gdk_pixbuf_get_rowstride (private->preview_pixbuf)); + } + + return GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); +} + +static void +gimp_viewable_real_invalidate_preview (GimpViewable *viewable) +{ + GimpViewablePrivate *private = GET_PRIVATE (viewable); + + g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref); + g_clear_object (&private->preview_pixbuf); +} + +static void +gimp_viewable_real_ancestry_changed_propagate (GimpViewable *viewable, + GimpViewable *parent) +{ + GimpViewablePrivate *private = GET_PRIVATE (viewable); + + private->depth = gimp_viewable_get_depth (parent) + 1; + + g_signal_emit (viewable, viewable_signals[ANCESTRY_CHANGED], 0); +} + +static void +gimp_viewable_real_ancestry_changed (GimpViewable *viewable) +{ + GimpContainer *children; + + children = gimp_viewable_get_children (viewable); + + if (children) + { + gimp_container_foreach (children, + (GFunc) gimp_viewable_real_ancestry_changed_propagate, + viewable); + } +} + +static void +gimp_viewable_real_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + *width = size; + *height = size; +} + +static gboolean +gimp_viewable_real_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + gint w, h; + + if (gimp_viewable_get_size (viewable, &w, &h)) + { + if (w > width || h > height) + { + *popup_width = w; + *popup_height = h; + + return TRUE; + } + } + + return FALSE; +} + +static GdkPixbuf * +gimp_viewable_real_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpViewablePrivate *private = GET_PRIVATE (viewable); + GdkPixbuf *pixbuf = NULL; + GimpTempBuf *temp_buf; + + temp_buf = gimp_viewable_get_preview (viewable, context, width, height); + + if (temp_buf) + { + pixbuf = gimp_temp_buf_create_pixbuf (temp_buf); + } + else if (private->icon_pixbuf) + { + pixbuf = gdk_pixbuf_scale_simple (private->icon_pixbuf, + width, + height, + GDK_INTERP_BILINEAR); + } + + return pixbuf; +} + +static gchar * +gimp_viewable_real_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + return g_strdup (gimp_object_get_name (viewable)); +} + +static gboolean +gimp_viewable_real_is_name_editable (GimpViewable *viewable) +{ + return GIMP_VIEWABLE_GET_CLASS (viewable)->name_editable; +} + +static GimpContainer * +gimp_viewable_real_get_children (GimpViewable *viewable) +{ + return NULL; +} + +static gboolean +gimp_viewable_serialize_property (GimpConfig *config, + guint property_id, + const GValue *value, + GParamSpec *pspec, + GimpConfigWriter *writer) +{ + GimpViewablePrivate *private = GET_PRIVATE (config); + + switch (property_id) + { + case PROP_STOCK_ID: + return TRUE; + + case PROP_ICON_NAME: + if (private->icon_name) + { + gimp_config_writer_open (writer, pspec->name); + gimp_config_writer_string (writer, private->icon_name); + gimp_config_writer_close (writer); + } + return TRUE; + + case PROP_ICON_PIXBUF: + { + GdkPixbuf *icon_pixbuf = g_value_get_object (value); + + if (icon_pixbuf) + { + gchar *pixbuffer; + gsize pixbuffer_size; + GError *error = NULL; + + if (gdk_pixbuf_save_to_buffer (icon_pixbuf, + &pixbuffer, + &pixbuffer_size, + "png", &error, NULL)) + { + gchar *pixbuffer_enc; + + pixbuffer_enc = g_base64_encode ((guchar *)pixbuffer, + pixbuffer_size); + gimp_config_writer_open (writer, "icon-pixbuf"); + gimp_config_writer_string (writer, pixbuffer_enc); + gimp_config_writer_close (writer); + + g_free (pixbuffer_enc); + g_free (pixbuffer); + } + } + } + return TRUE; + + default: + break; + } + + return FALSE; +} + +static gboolean +gimp_viewable_deserialize_property (GimpConfig *config, + guint property_id, + GValue *value, + GParamSpec *pspec, + GScanner *scanner, + GTokenType *expected) +{ + switch (property_id) + { + case PROP_ICON_PIXBUF: + { + GdkPixbuf *icon_pixbuf = NULL; + gchar *encoded_image; + + if (! gimp_scanner_parse_string (scanner, &encoded_image)) + { + *expected = G_TOKEN_STRING; + return TRUE; + } + + if (encoded_image && strlen (encoded_image) > 0) + { + gsize out_len; + guchar *decoded_image = g_base64_decode (encoded_image, &out_len); + + if (decoded_image) + { + GInputStream *stream; + + stream = g_memory_input_stream_new_from_data (decoded_image, + out_len, NULL); + icon_pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); + g_object_unref (stream); + + g_free (decoded_image); + } + } + + g_free (encoded_image); + + g_value_take_object (value, icon_pixbuf); + } + return TRUE; + + default: + break; + } + + return FALSE; +} + +/** + * gimp_viewable_invalidate_preview: + * @viewable: a viewable object + * + * Causes any cached preview to be marked as invalid, so that a new + * preview will be generated at the next attempt to display one. + **/ +void +gimp_viewable_invalidate_preview (GimpViewable *viewable) +{ + GimpViewablePrivate *private; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (viewable); + + if (private->freeze_count == 0) + g_signal_emit (viewable, viewable_signals[INVALIDATE_PREVIEW], 0); + else + private->invalidate_pending = TRUE; +} + +/** + * gimp_viewable_size_changed: + * @viewable: a viewable object + * + * This function sends a signal that is handled at a lower level in the + * object hierarchy, and provides a mechanism by which objects derived + * from #GimpViewable can respond to size changes. + **/ +void +gimp_viewable_size_changed (GimpViewable *viewable) +{ + GimpViewablePrivate *private; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (viewable); + + if (private->freeze_count == 0) + g_signal_emit (viewable, viewable_signals[SIZE_CHANGED], 0); + else + private->size_changed_prending = TRUE; +} + +/** + * gimp_viewable_expanded_changed: + * @viewable: a viewable object + * + * This function sends a signal that is handled at a lower level in the + * object hierarchy, and provides a mechanism by which objects derived + * from #GimpViewable can respond to expanded state changes. + **/ +void +gimp_viewable_expanded_changed (GimpViewable *viewable) +{ + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + g_signal_emit (viewable, viewable_signals[EXPANDED_CHANGED], 0); +} + +/** + * gimp_viewable_calc_preview_size: + * @aspect_width: unscaled width of the preview for an item. + * @aspect_height: unscaled height of the preview for an item. + * @width: maximum available width for scaled preview. + * @height: maximum available height for scaled preview. + * @dot_for_dot: if #TRUE, ignore any differences in axis resolution. + * @xresolution: resolution in the horizontal direction. + * @yresolution: resolution in the vertical direction. + * @return_width: place to return the calculated preview width. + * @return_height: place to return the calculated preview height. + * @scaling_up: returns #TRUE here if the calculated preview size + * is larger than the viewable itself. + * + * A utility function, for calculating the dimensions of a preview + * based on the information specified in the arguments. The arguments + * @aspect_width and @aspect_height are the dimensions of the unscaled + * preview. The arguments @width and @height represent the maximum + * width and height that the scaled preview must fit into. The + * preview is scaled to be as large as possible without exceeding + * these constraints. + * + * If @dot_for_dot is #TRUE, and @xresolution and @yresolution are + * different, then these results are corrected for the difference in + * resolution on the two axes, so that the requested aspect ratio + * applies to the appearance of the display rather than to pixel + * counts. + **/ +void +gimp_viewable_calc_preview_size (gint aspect_width, + gint aspect_height, + gint width, + gint height, + gboolean dot_for_dot, + gdouble xresolution, + gdouble yresolution, + gint *return_width, + gint *return_height, + gboolean *scaling_up) +{ + gdouble xratio; + gdouble yratio; + + if (aspect_width > aspect_height) + { + xratio = yratio = (gdouble) width / (gdouble) aspect_width; + } + else + { + xratio = yratio = (gdouble) height / (gdouble) aspect_height; + } + + if (! dot_for_dot && xresolution != yresolution) + { + yratio *= xresolution / yresolution; + } + + width = RINT (xratio * (gdouble) aspect_width); + height = RINT (yratio * (gdouble) aspect_height); + + if (width < 1) width = 1; + if (height < 1) height = 1; + + if (return_width) *return_width = width; + if (return_height) *return_height = height; + if (scaling_up) *scaling_up = (xratio > 1.0) || (yratio > 1.0); +} + +gboolean +gimp_viewable_get_size (GimpViewable *viewable, + gint *width, + gint *height) +{ + GimpViewableClass *viewable_class; + gboolean retval = FALSE; + gint w = 0; + gint h = 0; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (viewable_class->get_size) + retval = viewable_class->get_size (viewable, &w, &h); + + if (width) *width = w; + if (height) *height = h; + + return retval; +} + +/** + * gimp_viewable_get_preview_size: + * @viewable: the object for which to calculate the preview size. + * @size: requested size for preview. + * @popup: %TRUE if the preview is intended for a popup window. + * @dot_for_dot: If #TRUE, ignore any differences in X and Y resolution. + * @width: return location for the the calculated width. + * @height: return location for the calculated height. + * + * Retrieve the size of a viewable's preview. By default, this + * simply returns the value of the @size argument for both the @width + * and @height, but this can be overridden in objects derived from + * #GimpViewable. If either the width or height exceeds + * #GIMP_VIEWABLE_MAX_PREVIEW_SIZE, they are silently truncated. + **/ +void +gimp_viewable_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height) +{ + gint w, h; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + g_return_if_fail (size > 0); + + GIMP_VIEWABLE_GET_CLASS (viewable)->get_preview_size (viewable, size, + popup, dot_for_dot, + &w, &h); + + w = MIN (w, GIMP_VIEWABLE_MAX_PREVIEW_SIZE); + h = MIN (h, GIMP_VIEWABLE_MAX_PREVIEW_SIZE); + + if (width) *width = w; + if (height) *height = h; + +} + +/** + * gimp_viewable_get_popup_size: + * @viewable: the object for which to calculate the popup size. + * @width: the width of the preview from which the popup will be shown. + * @height: the height of the preview from which the popup will be shown. + * @dot_for_dot: If #TRUE, ignore any differences in X and Y resolution. + * @popup_width: return location for the calculated popup width. + * @popup_height: return location for the calculated popup height. + * + * Calculate the size of a viewable's preview, for use in making a + * popup. The arguments @width and @height specify the size of the + * preview from which the popup will be shown. + * + * Returns: Whether the viewable wants a popup to be shown. Usually + * %TRUE if the passed preview size is smaller than the viewable + * size, and %FALSE if the viewable completely fits into the + * original preview. + **/ +gboolean +gimp_viewable_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height) +{ + gint w, h; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE); + + if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_popup_size (viewable, + width, height, + dot_for_dot, + &w, &h)) + { + if (w < 1) w = 1; + if (h < 1) h = 1; + + /* limit the popup to 2 * GIMP_VIEWABLE_MAX_POPUP_SIZE + * on each axis. + */ + if ((w > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE)) || + (h > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE))) + { + gimp_viewable_calc_preview_size (w, h, + 2 * GIMP_VIEWABLE_MAX_POPUP_SIZE, + 2 * GIMP_VIEWABLE_MAX_POPUP_SIZE, + dot_for_dot, 1.0, 1.0, + &w, &h, NULL); + } + + /* limit the number of pixels to + * GIMP_VIEWABLE_MAX_POPUP_SIZE ^ 2 + */ + if ((w * h) > SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE)) + { + gdouble factor; + + factor = sqrt (((gdouble) (w * h) / + (gdouble) SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE))); + + w = RINT ((gdouble) w / factor); + h = RINT ((gdouble) h / factor); + } + + if (w < 1) w = 1; + if (h < 1) h = 1; + + if (popup_width) *popup_width = w; + if (popup_height) *popup_height = h; + + return TRUE; + } + + return FALSE; +} + +/** + * gimp_viewable_get_preview: + * @viewable: The viewable object to get a preview for. + * @context: The context to render the preview for. + * @width: desired width for the preview + * @height: desired height for the preview + * + * Gets a preview for a viewable object, by running through a variety + * of methods until it finds one that works. First, if an + * implementation exists of a "get_preview" method, it is tried, and + * the result is returned if it is not #NULL. Second, the function + * checks to see whether there is a cached preview with the correct + * dimensions; if so, it is returned. If neither of these works, then + * the function looks for an implementation of the "get_new_preview" + * method, and executes it, caching the result. If everything fails, + * #NULL is returned. + * + * Returns: A #GimpTempBuf containing the preview image, or #NULL if + * none can be found or created. + **/ +GimpTempBuf * +gimp_viewable_get_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpViewablePrivate *private; + GimpViewableClass *viewable_class; + GimpTempBuf *temp_buf = NULL; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + private = GET_PRIVATE (viewable); + + if (G_UNLIKELY (context == NULL)) + g_warning ("%s: context is NULL", G_STRFUNC); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (viewable_class->get_preview) + temp_buf = viewable_class->get_preview (viewable, context, width, height); + + if (temp_buf) + return temp_buf; + + if (private->preview_temp_buf) + { + if (gimp_temp_buf_get_width (private->preview_temp_buf) == width && + gimp_temp_buf_get_height (private->preview_temp_buf) == height) + { + return private->preview_temp_buf; + } + + g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref); + } + + if (viewable_class->get_new_preview) + temp_buf = viewable_class->get_new_preview (viewable, context, + width, height); + + private->preview_temp_buf = temp_buf; + + return temp_buf; +} + +/** + * gimp_viewable_get_new_preview: + * @viewable: The viewable object to get a preview for. + * @width: desired width for the preview + * @height: desired height for the preview + * + * Gets a new preview for a viewable object. Similar to + * gimp_viewable_get_preview(), except that it tries things in a + * different order, first looking for a "get_new_preview" method, and + * then if that fails for a "get_preview" method. This function does + * not look for a cached preview. + * + * Returns: A #GimpTempBuf containing the preview image, or #NULL if + * none can be found or created. + **/ +GimpTempBuf * +gimp_viewable_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpViewableClass *viewable_class; + GimpTempBuf *temp_buf = NULL; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + if (G_UNLIKELY (context == NULL)) + g_warning ("%s: context is NULL", G_STRFUNC); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (viewable_class->get_new_preview) + temp_buf = viewable_class->get_new_preview (viewable, context, + width, height); + + if (temp_buf) + return temp_buf; + + if (viewable_class->get_preview) + temp_buf = viewable_class->get_preview (viewable, context, + width, height); + + if (temp_buf) + return gimp_temp_buf_copy (temp_buf); + + return NULL; +} + +/** + * gimp_viewable_get_dummy_preview: + * @viewable: viewable object for which to get a dummy preview. + * @width: width of the preview. + * @height: height of the preview. + * @bpp: bytes per pixel for the preview, must be 3 or 4. + * + * Creates a dummy preview the fits into the specified dimensions, + * containing a default "question" symbol. This function is used to + * generate a preview in situations where layer previews have been + * disabled in the current Gimp configuration. + * + * Returns: a #GimpTempBuf containing the preview image. + **/ +GimpTempBuf * +gimp_viewable_get_dummy_preview (GimpViewable *viewable, + gint width, + gint height, + const Babl *format) +{ + GdkPixbuf *pixbuf; + GimpTempBuf *buf; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + g_return_val_if_fail (format != NULL, NULL); + + pixbuf = gimp_viewable_get_dummy_pixbuf (viewable, width, height, + babl_format_has_alpha (format)); + + buf = gimp_temp_buf_new_from_pixbuf (pixbuf, format); + + g_object_unref (pixbuf); + + return buf; +} + +/** + * gimp_viewable_get_pixbuf: + * @viewable: The viewable object to get a pixbuf preview for. + * @context: The context to render the preview for. + * @width: desired width for the preview + * @height: desired height for the preview + * + * Gets a preview for a viewable object, by running through a variety + * of methods until it finds one that works. First, if an + * implementation exists of a "get_pixbuf" method, it is tried, and + * the result is returned if it is not #NULL. Second, the function + * checks to see whether there is a cached preview with the correct + * dimensions; if so, it is returned. If neither of these works, then + * the function looks for an implementation of the "get_new_pixbuf" + * method, and executes it, caching the result. If everything fails, + * #NULL is returned. + * + * Returns: A #GdkPixbuf containing the preview pixbuf, or #NULL if none can + * be found or created. + **/ +GdkPixbuf * +gimp_viewable_get_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpViewablePrivate *private; + GimpViewableClass *viewable_class; + GdkPixbuf *pixbuf = NULL; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + private = GET_PRIVATE (viewable); + + if (G_UNLIKELY (context == NULL)) + g_warning ("%s: context is NULL", G_STRFUNC); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (viewable_class->get_pixbuf) + pixbuf = viewable_class->get_pixbuf (viewable, context, width, height); + + if (pixbuf) + return pixbuf; + + if (private->preview_pixbuf) + { + if (gdk_pixbuf_get_width (private->preview_pixbuf) == width && + gdk_pixbuf_get_height (private->preview_pixbuf) == height) + { + return private->preview_pixbuf; + } + + g_clear_object (&private->preview_pixbuf); + } + + if (viewable_class->get_new_pixbuf) + pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height); + + private->preview_pixbuf = pixbuf; + + return pixbuf; +} + +/** + * gimp_viewable_get_new_pixbuf: + * @viewable: The viewable object to get a new pixbuf preview for. + * @context: The context to render the preview for. + * @width: desired width for the pixbuf + * @height: desired height for the pixbuf + * + * Gets a new preview for a viewable object. Similar to + * gimp_viewable_get_pixbuf(), except that it tries things in a + * different order, first looking for a "get_new_pixbuf" method, and + * then if that fails for a "get_pixbuf" method. This function does + * not look for a cached pixbuf. + * + * Returns: A #GdkPixbuf containing the preview, or #NULL if none can + * be created. + **/ +GdkPixbuf * +gimp_viewable_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height) +{ + GimpViewableClass *viewable_class; + GdkPixbuf *pixbuf = NULL; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + if (G_UNLIKELY (context == NULL)) + g_warning ("%s: context is NULL", G_STRFUNC); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (viewable_class->get_new_pixbuf) + pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height); + + if (pixbuf) + return pixbuf; + + if (viewable_class->get_pixbuf) + pixbuf = viewable_class->get_pixbuf (viewable, context, width, height); + + if (pixbuf) + return gdk_pixbuf_copy (pixbuf); + + return NULL; +} + +/** + * gimp_viewable_get_dummy_pixbuf: + * @viewable: the viewable object for which to create a dummy representation. + * @width: maximum permitted width for the pixbuf. + * @height: maximum permitted height for the pixbuf. + * @bpp: bytes per pixel for the pixbuf, must equal 3 or 4. + * + * Creates a pixbuf containing a default "question" symbol, sized to + * fit into the specified dimensions. The depth of the pixbuf must be + * 3 or 4 because #GdkPixbuf does not support grayscale. This + * function is used to generate a preview in situations where + * previewing has been disabled in the current Gimp configuration. + * [Note: this function is currently unused except internally to + * #GimpViewable -- consider making it static?] + * + * Returns: the created #GdkPixbuf. + **/ +GdkPixbuf * +gimp_viewable_get_dummy_pixbuf (GimpViewable *viewable, + gint width, + gint height, + gboolean with_alpha) +{ + GdkPixbuf *icon; + GdkPixbuf *pixbuf; + GError *error = NULL; + gdouble ratio; + gint w, h; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + icon = gdk_pixbuf_new_from_resource ("/org/gimp/icons/64/gimp-question.png", + &error); + if (! icon) + { + g_critical ("Failed to create icon image: %s", error->message); + g_clear_error (&error); + return NULL; + } + + w = gdk_pixbuf_get_width (icon); + h = gdk_pixbuf_get_height (icon); + + ratio = (gdouble) MIN (width, height) / (gdouble) MAX (w, h); + ratio = MIN (ratio, 1.0); + + w = RINT (ratio * (gdouble) w); + h = RINT (ratio * (gdouble) h); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, with_alpha, 8, width, height); + gdk_pixbuf_fill (pixbuf, 0xffffffff); + + if (w && h) + gdk_pixbuf_composite (icon, pixbuf, + (width - w) / 2, (height - h) / 2, w, h, + (width - w) / 2, (height - h) / 2, ratio, ratio, + GDK_INTERP_BILINEAR, 0xFF); + + g_object_unref (icon); + + return pixbuf; +} + +/** + * gimp_viewable_get_description: + * @viewable: viewable object for which to retrieve a description. + * @tooltip: return location for an optional tooltip string. + * + * Retrieves a string containing a description of the viewable object, + * By default, it simply returns the name of the object, but this can + * be overridden by object types that inherit from #GimpViewable. + * + * Returns: a copy of the description string. This should be freed + * when it is no longer needed. + **/ +gchar * +gimp_viewable_get_description (GimpViewable *viewable, + gchar **tooltip) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + + if (tooltip) + *tooltip = NULL; + + return GIMP_VIEWABLE_GET_CLASS (viewable)->get_description (viewable, + tooltip); +} + +/** + * gimp_viewable_is_name_editable: + * @viewable: viewable object for which to retrieve a description. + * + * Returns: whether the viewable's name is editable by the user. + **/ +gboolean +gimp_viewable_is_name_editable (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE); + + return GIMP_VIEWABLE_GET_CLASS (viewable)->is_name_editable (viewable); +} + +/** + * gimp_viewable_get_icon_name: + * @viewable: viewable object for which to retrieve a icon name. + * + * Gets the current value of the object's icon name, for use in + * constructing an iconic representation of the object. + * + * Returns: a pointer to the string containing the icon name. The + * contents must not be altered or freed. + **/ +const gchar * +gimp_viewable_get_icon_name (GimpViewable *viewable) +{ + GimpViewablePrivate *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + + private = GET_PRIVATE (viewable); + + if (private->icon_name) + return (const gchar *) private->icon_name; + + return GIMP_VIEWABLE_GET_CLASS (viewable)->default_icon_name; +} + +/** + * gimp_viewable_set_icon_name: + * @viewable: viewable object to assign the specified icon name. + * @icon_name: string containing an icon name identifier. + * + * Seta the object's icon name, for use in constructing iconic smbols + * of the object. The contents of @icon_name are copied, so you can + * free it when you are done with it. + **/ +void +gimp_viewable_set_icon_name (GimpViewable *viewable, + const gchar *icon_name) +{ + GimpViewablePrivate *private; + GimpViewableClass *viewable_class; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (viewable); + + g_clear_pointer (&private->icon_name, g_free); + + viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); + + if (icon_name) + { + if (viewable_class->default_icon_name == NULL || + strcmp (icon_name, viewable_class->default_icon_name)) + private->icon_name = g_strdup (icon_name); + } + + gimp_viewable_invalidate_preview (viewable); + + g_object_notify (G_OBJECT (viewable), "icon-name"); +} + +void +gimp_viewable_preview_freeze (GimpViewable *viewable) +{ + GimpViewablePrivate *private; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (viewable); + + private->freeze_count++; + + if (private->freeze_count == 1) + { + if (GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze) + GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze (viewable); + + g_object_notify (G_OBJECT (viewable), "frozen"); + } +} + +void +gimp_viewable_preview_thaw (GimpViewable *viewable) +{ + GimpViewablePrivate *private; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (viewable); + + g_return_if_fail (private->freeze_count > 0); + + private->freeze_count--; + + if (private->freeze_count == 0) + { + if (private->size_changed_prending) + { + private->size_changed_prending = FALSE; + + gimp_viewable_size_changed (viewable); + } + + if (private->invalidate_pending) + { + private->invalidate_pending = FALSE; + + gimp_viewable_invalidate_preview (viewable); + } + + g_object_notify (G_OBJECT (viewable), "frozen"); + + if (GIMP_VIEWABLE_GET_CLASS (viewable)->preview_thaw) + GIMP_VIEWABLE_GET_CLASS (viewable)->preview_thaw (viewable); + } +} + +gboolean +gimp_viewable_preview_is_frozen (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE); + + return GET_PRIVATE (viewable)->freeze_count != 0; +} + +GimpViewable * +gimp_viewable_get_parent (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + + return GET_PRIVATE (viewable)->parent; +} + +void +gimp_viewable_set_parent (GimpViewable *viewable, + GimpViewable *parent) +{ + GimpViewablePrivate *private; + + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + g_return_if_fail (parent == NULL || GIMP_IS_VIEWABLE (parent)); + + private = GET_PRIVATE (viewable); + + if (parent != private->parent) + { + private->parent = parent; + private->depth = parent ? gimp_viewable_get_depth (parent) + 1 : 0; + + g_signal_emit (viewable, viewable_signals[ANCESTRY_CHANGED], 0); + } +} + +gint +gimp_viewable_get_depth (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), 0); + + return GET_PRIVATE (viewable)->depth; +} + +GimpContainer * +gimp_viewable_get_children (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + + return GIMP_VIEWABLE_GET_CLASS (viewable)->get_children (viewable); +} + +gboolean +gimp_viewable_get_expanded (GimpViewable *viewable) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE); + + if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_expanded) + return GIMP_VIEWABLE_GET_CLASS (viewable)->get_expanded (viewable); + + return FALSE; +} + +void +gimp_viewable_set_expanded (GimpViewable *viewable, + gboolean expanded) +{ + g_return_if_fail (GIMP_IS_VIEWABLE (viewable)); + + if (GIMP_VIEWABLE_GET_CLASS (viewable)->set_expanded) + GIMP_VIEWABLE_GET_CLASS (viewable)->set_expanded (viewable, expanded); +} + +gboolean +gimp_viewable_is_ancestor (GimpViewable *ancestor, + GimpViewable *descendant) +{ + g_return_val_if_fail (GIMP_IS_VIEWABLE (ancestor), FALSE); + g_return_val_if_fail (GIMP_IS_VIEWABLE (descendant), FALSE); + + while (descendant) + { + GimpViewable *parent = gimp_viewable_get_parent (descendant); + + if (parent == ancestor) + return TRUE; + + descendant = parent; + } + + return FALSE; +} diff --git a/app/core/gimpviewable.h b/app/core/gimpviewable.h new file mode 100644 index 0000000..249750e --- /dev/null +++ b/app/core/gimpviewable.h @@ -0,0 +1,201 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpviewable.h + * Copyright (C) 2001 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_VIEWABLE_H__ +#define __GIMP_VIEWABLE_H__ + + +#include "gimpobject.h" + + +#define GIMP_VIEWABLE_MAX_PREVIEW_SIZE 2048 +#define GIMP_VIEWABLE_MAX_POPUP_SIZE 256 +#define GIMP_VIEWABLE_MAX_BUTTON_SIZE 64 +#define GIMP_VIEWABLE_MAX_MENU_SIZE 48 + + +#define GIMP_TYPE_VIEWABLE (gimp_viewable_get_type ()) +#define GIMP_VIEWABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_VIEWABLE, GimpViewable)) +#define GIMP_VIEWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_VIEWABLE, GimpViewableClass)) +#define GIMP_IS_VIEWABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_VIEWABLE)) +#define GIMP_IS_VIEWABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_VIEWABLE)) +#define GIMP_VIEWABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_VIEWABLE, GimpViewableClass)) + + +typedef struct _GimpViewableClass GimpViewableClass; + +struct _GimpViewable +{ + GimpObject parent_instance; +}; + +struct _GimpViewableClass +{ + GimpObjectClass parent_class; + + const gchar *default_icon_name; + const gchar *name_changed_signal; + gboolean name_editable; + + /* signals */ + void (* invalidate_preview) (GimpViewable *viewable); + void (* size_changed) (GimpViewable *viewable); + void (* expanded_changed) (GimpViewable *viewable); + void (* ancestry_changed) (GimpViewable *viewable); + + /* virtual functions */ + gboolean (* get_size) (GimpViewable *viewable, + gint *width, + gint *height); + void (* get_preview_size) (GimpViewable *viewable, + gint size, + gboolean is_popup, + gboolean dot_for_dot, + gint *width, + gint *height); + gboolean (* get_popup_size) (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); + GimpTempBuf * (* get_preview) (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + GimpTempBuf * (* get_new_preview) (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + GdkPixbuf * (* get_pixbuf) (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + GdkPixbuf * (* get_new_pixbuf) (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + gchar * (* get_description) (GimpViewable *viewable, + gchar **tooltip); + + gboolean (* is_name_editable) (GimpViewable *viewable); + + void (* preview_freeze) (GimpViewable *viewable); + void (* preview_thaw) (GimpViewable *viewable); + + GimpContainer * (* get_children) (GimpViewable *viewable); + + void (* set_expanded) (GimpViewable *viewable, + gboolean expand); + gboolean (* get_expanded) (GimpViewable *viewable); +}; + + +GType gimp_viewable_get_type (void) G_GNUC_CONST; + +void gimp_viewable_invalidate_preview (GimpViewable *viewable); +void gimp_viewable_size_changed (GimpViewable *viewable); +void gimp_viewable_expanded_changed (GimpViewable *viewable); + +void gimp_viewable_calc_preview_size (gint aspect_width, + gint aspect_height, + gint width, + gint height, + gboolean dot_for_dot, + gdouble xresolution, + gdouble yresolution, + gint *return_width, + gint *return_height, + gboolean *scaling_up); + +gboolean gimp_viewable_get_size (GimpViewable *viewable, + gint *width, + gint *height); +void gimp_viewable_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +gboolean gimp_viewable_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); + +GimpTempBuf * gimp_viewable_get_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +GimpTempBuf * gimp_viewable_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + +GimpTempBuf * gimp_viewable_get_dummy_preview (GimpViewable *viewable, + gint width, + gint height, + const Babl *format); + +GdkPixbuf * gimp_viewable_get_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +GdkPixbuf * gimp_viewable_get_new_pixbuf (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); + +GdkPixbuf * gimp_viewable_get_dummy_pixbuf (GimpViewable *viewable, + gint width, + gint height, + gboolean with_alpha); + +gchar * gimp_viewable_get_description (GimpViewable *viewable, + gchar **tooltip); + +gboolean gimp_viewable_is_name_editable (GimpViewable *viewable); + +const gchar * gimp_viewable_get_icon_name (GimpViewable *viewable); +void gimp_viewable_set_icon_name (GimpViewable *viewable, + const gchar *icon_name); + +void gimp_viewable_preview_freeze (GimpViewable *viewable); +void gimp_viewable_preview_thaw (GimpViewable *viewable); +gboolean gimp_viewable_preview_is_frozen (GimpViewable *viewable); + +GimpViewable * gimp_viewable_get_parent (GimpViewable *viewable); +void gimp_viewable_set_parent (GimpViewable *viewable, + GimpViewable *parent); + +gint gimp_viewable_get_depth (GimpViewable *viewable); + +GimpContainer * gimp_viewable_get_children (GimpViewable *viewable); + +gboolean gimp_viewable_get_expanded (GimpViewable *viewable); +void gimp_viewable_set_expanded (GimpViewable *viewable, + gboolean expanded); + +gboolean gimp_viewable_is_ancestor (GimpViewable *ancestor, + GimpViewable *descendant); + + +#endif /* __GIMP_VIEWABLE_H__ */ diff --git a/app/core/gimpwaitable.c b/app/core/gimpwaitable.c new file mode 100644 index 0000000..4607f74 --- /dev/null +++ b/app/core/gimpwaitable.c @@ -0,0 +1,118 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpwaitable.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 . + */ + + +#include "config.h" + +#include +#include + +#include "core-types.h" + +#include "gimpwaitable.h" + + +G_DEFINE_INTERFACE (GimpWaitable, gimp_waitable, G_TYPE_OBJECT) + + +/* private functions */ + + +static void +gimp_waitable_default_init (GimpWaitableInterface *iface) +{ +} + + +/* public functions */ + + +void +gimp_waitable_wait (GimpWaitable *waitable) +{ + GimpWaitableInterface *iface; + + g_return_if_fail (GIMP_IS_WAITABLE (waitable)); + + iface = GIMP_WAITABLE_GET_INTERFACE (waitable); + + if (iface->wait) + iface->wait (waitable); +} + +gboolean +gimp_waitable_try_wait (GimpWaitable *waitable) +{ + GimpWaitableInterface *iface; + + g_return_val_if_fail (GIMP_IS_WAITABLE (waitable), FALSE); + + iface = GIMP_WAITABLE_GET_INTERFACE (waitable); + + if (iface->try_wait) + { + return iface->try_wait (waitable); + } + else + { + gimp_waitable_wait (waitable); + + return TRUE; + } +} + +gboolean +gimp_waitable_wait_until (GimpWaitable *waitable, + gint64 end_time) +{ + GimpWaitableInterface *iface; + + g_return_val_if_fail (GIMP_IS_WAITABLE (waitable), FALSE); + + iface = GIMP_WAITABLE_GET_INTERFACE (waitable); + + if (iface->wait_until) + { + return iface->wait_until (waitable, end_time); + } + else + { + gimp_waitable_wait (waitable); + + return TRUE; + } +} + +gboolean +gimp_waitable_wait_for (GimpWaitable *waitable, + gint64 wait_duration) +{ + g_return_val_if_fail (GIMP_IS_WAITABLE (waitable), FALSE); + + if (wait_duration <= 0) + { + return gimp_waitable_try_wait (waitable); + } + else + { + return gimp_waitable_wait_until (waitable, + g_get_monotonic_time () + wait_duration); + } +} diff --git a/app/core/gimpwaitable.h b/app/core/gimpwaitable.h new file mode 100644 index 0000000..b85bece --- /dev/null +++ b/app/core/gimpwaitable.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpwaitable.h + * 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 . + */ + +#ifndef __GIMP_WAITABLE_H__ +#define __GIMP_WAITABLE_H__ + + +#define GIMP_TYPE_WAITABLE (gimp_waitable_get_type ()) +#define GIMP_IS_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_WAITABLE)) +#define GIMP_WAITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_WAITABLE, GimpWaitable)) +#define GIMP_WAITABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_WAITABLE, GimpWaitableInterface)) + + +typedef struct _GimpWaitableInterface GimpWaitableInterface; + +struct _GimpWaitableInterface +{ + GTypeInterface base_iface; + + /* virtual functions */ + void (* wait) (GimpWaitable *waitable); + gboolean (* try_wait) (GimpWaitable *waitable); + gboolean (* wait_until) (GimpWaitable *waitable, + gint64 end_time); +}; + + +GType gimp_waitable_get_type (void) G_GNUC_CONST; + +void gimp_waitable_wait (GimpWaitable *waitable); +gboolean gimp_waitable_try_wait (GimpWaitable *waitable); +gboolean gimp_waitable_wait_until (GimpWaitable *waitable, + gint64 end_time); +gboolean gimp_waitable_wait_for (GimpWaitable *waitable, + gint64 wait_duration); + + +#endif /* __GIMP_WAITABLE_H__ */ diff --git a/app/dialogs/Makefile.am b/app/dialogs/Makefile.am new file mode 100644 index 0000000..a8d0768 --- /dev/null +++ b/app/dialogs/Makefile.am @@ -0,0 +1,120 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Dialogs\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappdialogs.a + +libappdialogs_a_sources = \ + dialogs-types.h \ + dialogs.c \ + dialogs.h \ + dialogs-constructors.c \ + dialogs-constructors.h \ + \ + about-dialog.c \ + about-dialog.h \ + action-search-dialog.c \ + action-search-dialog.h \ + channel-options-dialog.c \ + channel-options-dialog.h \ + color-profile-dialog.c \ + color-profile-dialog.h \ + color-profile-import-dialog.c \ + color-profile-import-dialog.h \ + convert-indexed-dialog.c \ + convert-indexed-dialog.h \ + convert-precision-dialog.c \ + convert-precision-dialog.h \ + data-delete-dialog.c \ + data-delete-dialog.h \ + file-open-dialog.c \ + file-open-dialog.h \ + file-open-location-dialog.c \ + file-open-location-dialog.h \ + file-save-dialog.c \ + file-save-dialog.h \ + fill-dialog.c \ + fill-dialog.h \ + grid-dialog.h \ + grid-dialog.c \ + image-merge-layers-dialog.c \ + image-merge-layers-dialog.h \ + image-new-dialog.c \ + image-new-dialog.h \ + image-properties-dialog.c \ + image-properties-dialog.h \ + image-scale-dialog.c \ + image-scale-dialog.h \ + input-devices-dialog.c \ + input-devices-dialog.h \ + item-options-dialog.c \ + item-options-dialog.h \ + keyboard-shortcuts-dialog.c \ + keyboard-shortcuts-dialog.h \ + layer-add-mask-dialog.c \ + layer-add-mask-dialog.h \ + layer-options-dialog.c \ + layer-options-dialog.h \ + lebl-dialog.c \ + lebl-dialog.h \ + module-dialog.c \ + module-dialog.h \ + palette-import-dialog.c \ + palette-import-dialog.h \ + preferences-dialog.c \ + preferences-dialog.h \ + preferences-dialog-utils.c \ + preferences-dialog-utils.h \ + print-size-dialog.c \ + print-size-dialog.h \ + quit-dialog.c \ + quit-dialog.h \ + resize-dialog.c \ + resize-dialog.h \ + resolution-calibrate-dialog.c \ + resolution-calibrate-dialog.h \ + scale-dialog.c \ + scale-dialog.h \ + stroke-dialog.c \ + stroke-dialog.h \ + template-options-dialog.c \ + template-options-dialog.h \ + tips-dialog.c \ + tips-dialog.h \ + tips-parser.c \ + tips-parser.h \ + user-install-dialog.c \ + user-install-dialog.h \ + vectors-export-dialog.c \ + vectors-export-dialog.h \ + vectors-import-dialog.c \ + vectors-import-dialog.h \ + vectors-options-dialog.c \ + vectors-options-dialog.h + +libappdialogs_a_built_sources = \ + authors.h + +libappdialogs_a_SOURCES = \ + $(libappdialogs_a_built_sources) $(libappdialogs_a_sources) + +EXTRA_DIST = \ + authors.xsl + + +$(srcdir)/about-dialog.c: authors.h + +authors.h: $(top_srcdir)/authors.xml $(srcdir)/authors.xsl +if HAVE_XSLTPROC + $(XSLTPROC) $(srcdir)/authors.xsl $< > $(@) || rm -f $(@) +else + @echo "*** xsltproc is required to regenerate $(@) ***"; exit 1; +endif diff --git a/app/dialogs/Makefile.in b/app/dialogs/Makefile.in new file mode 100644 index 0000000..e3cad86 --- /dev/null +++ b/app/dialogs/Makefile.in @@ -0,0 +1,1197 @@ +# 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 = app/dialogs +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 = +libappdialogs_a_AR = $(AR) $(ARFLAGS) +libappdialogs_a_LIBADD = +am__objects_1 = +am__objects_2 = dialogs.$(OBJEXT) dialogs-constructors.$(OBJEXT) \ + about-dialog.$(OBJEXT) action-search-dialog.$(OBJEXT) \ + channel-options-dialog.$(OBJEXT) \ + color-profile-dialog.$(OBJEXT) \ + color-profile-import-dialog.$(OBJEXT) \ + convert-indexed-dialog.$(OBJEXT) \ + convert-precision-dialog.$(OBJEXT) \ + data-delete-dialog.$(OBJEXT) file-open-dialog.$(OBJEXT) \ + file-open-location-dialog.$(OBJEXT) file-save-dialog.$(OBJEXT) \ + fill-dialog.$(OBJEXT) grid-dialog.$(OBJEXT) \ + image-merge-layers-dialog.$(OBJEXT) image-new-dialog.$(OBJEXT) \ + image-properties-dialog.$(OBJEXT) image-scale-dialog.$(OBJEXT) \ + input-devices-dialog.$(OBJEXT) item-options-dialog.$(OBJEXT) \ + keyboard-shortcuts-dialog.$(OBJEXT) \ + layer-add-mask-dialog.$(OBJEXT) layer-options-dialog.$(OBJEXT) \ + lebl-dialog.$(OBJEXT) module-dialog.$(OBJEXT) \ + palette-import-dialog.$(OBJEXT) preferences-dialog.$(OBJEXT) \ + preferences-dialog-utils.$(OBJEXT) print-size-dialog.$(OBJEXT) \ + quit-dialog.$(OBJEXT) resize-dialog.$(OBJEXT) \ + resolution-calibrate-dialog.$(OBJEXT) scale-dialog.$(OBJEXT) \ + stroke-dialog.$(OBJEXT) template-options-dialog.$(OBJEXT) \ + tips-dialog.$(OBJEXT) tips-parser.$(OBJEXT) \ + user-install-dialog.$(OBJEXT) vectors-export-dialog.$(OBJEXT) \ + vectors-import-dialog.$(OBJEXT) \ + vectors-options-dialog.$(OBJEXT) +am_libappdialogs_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappdialogs_a_OBJECTS = $(am_libappdialogs_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)/about-dialog.Po \ + ./$(DEPDIR)/action-search-dialog.Po \ + ./$(DEPDIR)/channel-options-dialog.Po \ + ./$(DEPDIR)/color-profile-dialog.Po \ + ./$(DEPDIR)/color-profile-import-dialog.Po \ + ./$(DEPDIR)/convert-indexed-dialog.Po \ + ./$(DEPDIR)/convert-precision-dialog.Po \ + ./$(DEPDIR)/data-delete-dialog.Po \ + ./$(DEPDIR)/dialogs-constructors.Po ./$(DEPDIR)/dialogs.Po \ + ./$(DEPDIR)/file-open-dialog.Po \ + ./$(DEPDIR)/file-open-location-dialog.Po \ + ./$(DEPDIR)/file-save-dialog.Po ./$(DEPDIR)/fill-dialog.Po \ + ./$(DEPDIR)/grid-dialog.Po \ + ./$(DEPDIR)/image-merge-layers-dialog.Po \ + ./$(DEPDIR)/image-new-dialog.Po \ + ./$(DEPDIR)/image-properties-dialog.Po \ + ./$(DEPDIR)/image-scale-dialog.Po \ + ./$(DEPDIR)/input-devices-dialog.Po \ + ./$(DEPDIR)/item-options-dialog.Po \ + ./$(DEPDIR)/keyboard-shortcuts-dialog.Po \ + ./$(DEPDIR)/layer-add-mask-dialog.Po \ + ./$(DEPDIR)/layer-options-dialog.Po ./$(DEPDIR)/lebl-dialog.Po \ + ./$(DEPDIR)/module-dialog.Po \ + ./$(DEPDIR)/palette-import-dialog.Po \ + ./$(DEPDIR)/preferences-dialog-utils.Po \ + ./$(DEPDIR)/preferences-dialog.Po \ + ./$(DEPDIR)/print-size-dialog.Po ./$(DEPDIR)/quit-dialog.Po \ + ./$(DEPDIR)/resize-dialog.Po \ + ./$(DEPDIR)/resolution-calibrate-dialog.Po \ + ./$(DEPDIR)/scale-dialog.Po ./$(DEPDIR)/stroke-dialog.Po \ + ./$(DEPDIR)/template-options-dialog.Po \ + ./$(DEPDIR)/tips-dialog.Po ./$(DEPDIR)/tips-parser.Po \ + ./$(DEPDIR)/user-install-dialog.Po \ + ./$(DEPDIR)/vectors-export-dialog.Po \ + ./$(DEPDIR)/vectors-import-dialog.Po \ + ./$(DEPDIR)/vectors-options-dialog.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 = $(libappdialogs_a_SOURCES) +DIST_SOURCES = $(libappdialogs_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Dialogs\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappdialogs.a +libappdialogs_a_sources = \ + dialogs-types.h \ + dialogs.c \ + dialogs.h \ + dialogs-constructors.c \ + dialogs-constructors.h \ + \ + about-dialog.c \ + about-dialog.h \ + action-search-dialog.c \ + action-search-dialog.h \ + channel-options-dialog.c \ + channel-options-dialog.h \ + color-profile-dialog.c \ + color-profile-dialog.h \ + color-profile-import-dialog.c \ + color-profile-import-dialog.h \ + convert-indexed-dialog.c \ + convert-indexed-dialog.h \ + convert-precision-dialog.c \ + convert-precision-dialog.h \ + data-delete-dialog.c \ + data-delete-dialog.h \ + file-open-dialog.c \ + file-open-dialog.h \ + file-open-location-dialog.c \ + file-open-location-dialog.h \ + file-save-dialog.c \ + file-save-dialog.h \ + fill-dialog.c \ + fill-dialog.h \ + grid-dialog.h \ + grid-dialog.c \ + image-merge-layers-dialog.c \ + image-merge-layers-dialog.h \ + image-new-dialog.c \ + image-new-dialog.h \ + image-properties-dialog.c \ + image-properties-dialog.h \ + image-scale-dialog.c \ + image-scale-dialog.h \ + input-devices-dialog.c \ + input-devices-dialog.h \ + item-options-dialog.c \ + item-options-dialog.h \ + keyboard-shortcuts-dialog.c \ + keyboard-shortcuts-dialog.h \ + layer-add-mask-dialog.c \ + layer-add-mask-dialog.h \ + layer-options-dialog.c \ + layer-options-dialog.h \ + lebl-dialog.c \ + lebl-dialog.h \ + module-dialog.c \ + module-dialog.h \ + palette-import-dialog.c \ + palette-import-dialog.h \ + preferences-dialog.c \ + preferences-dialog.h \ + preferences-dialog-utils.c \ + preferences-dialog-utils.h \ + print-size-dialog.c \ + print-size-dialog.h \ + quit-dialog.c \ + quit-dialog.h \ + resize-dialog.c \ + resize-dialog.h \ + resolution-calibrate-dialog.c \ + resolution-calibrate-dialog.h \ + scale-dialog.c \ + scale-dialog.h \ + stroke-dialog.c \ + stroke-dialog.h \ + template-options-dialog.c \ + template-options-dialog.h \ + tips-dialog.c \ + tips-dialog.h \ + tips-parser.c \ + tips-parser.h \ + user-install-dialog.c \ + user-install-dialog.h \ + vectors-export-dialog.c \ + vectors-export-dialog.h \ + vectors-import-dialog.c \ + vectors-import-dialog.h \ + vectors-options-dialog.c \ + vectors-options-dialog.h + +libappdialogs_a_built_sources = \ + authors.h + +libappdialogs_a_SOURCES = \ + $(libappdialogs_a_built_sources) $(libappdialogs_a_sources) + +EXTRA_DIST = \ + authors.xsl + +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 app/dialogs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/dialogs/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) + +libappdialogs.a: $(libappdialogs_a_OBJECTS) $(libappdialogs_a_DEPENDENCIES) $(EXTRA_libappdialogs_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappdialogs.a + $(AM_V_AR)$(libappdialogs_a_AR) libappdialogs.a $(libappdialogs_a_OBJECTS) $(libappdialogs_a_LIBADD) + $(AM_V_at)$(RANLIB) libappdialogs.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/about-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/action-search-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel-options-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-profile-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-profile-import-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/convert-indexed-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/convert-precision-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-delete-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialogs-constructors.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialogs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-open-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-open-location-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-save-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fill-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-merge-layers-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-new-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-properties-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-scale-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-devices-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/item-options-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyboard-shortcuts-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer-add-mask-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer-options-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lebl-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/module-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palette-import-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preferences-dialog-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preferences-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-size-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quit-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resize-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolution-calibrate-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scale-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/template-options-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tips-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tips-parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user-install-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vectors-export-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vectors-import-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vectors-options-dialog.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)/about-dialog.Po + -rm -f ./$(DEPDIR)/action-search-dialog.Po + -rm -f ./$(DEPDIR)/channel-options-dialog.Po + -rm -f ./$(DEPDIR)/color-profile-dialog.Po + -rm -f ./$(DEPDIR)/color-profile-import-dialog.Po + -rm -f ./$(DEPDIR)/convert-indexed-dialog.Po + -rm -f ./$(DEPDIR)/convert-precision-dialog.Po + -rm -f ./$(DEPDIR)/data-delete-dialog.Po + -rm -f ./$(DEPDIR)/dialogs-constructors.Po + -rm -f ./$(DEPDIR)/dialogs.Po + -rm -f ./$(DEPDIR)/file-open-dialog.Po + -rm -f ./$(DEPDIR)/file-open-location-dialog.Po + -rm -f ./$(DEPDIR)/file-save-dialog.Po + -rm -f ./$(DEPDIR)/fill-dialog.Po + -rm -f ./$(DEPDIR)/grid-dialog.Po + -rm -f ./$(DEPDIR)/image-merge-layers-dialog.Po + -rm -f ./$(DEPDIR)/image-new-dialog.Po + -rm -f ./$(DEPDIR)/image-properties-dialog.Po + -rm -f ./$(DEPDIR)/image-scale-dialog.Po + -rm -f ./$(DEPDIR)/input-devices-dialog.Po + -rm -f ./$(DEPDIR)/item-options-dialog.Po + -rm -f ./$(DEPDIR)/keyboard-shortcuts-dialog.Po + -rm -f ./$(DEPDIR)/layer-add-mask-dialog.Po + -rm -f ./$(DEPDIR)/layer-options-dialog.Po + -rm -f ./$(DEPDIR)/lebl-dialog.Po + -rm -f ./$(DEPDIR)/module-dialog.Po + -rm -f ./$(DEPDIR)/palette-import-dialog.Po + -rm -f ./$(DEPDIR)/preferences-dialog-utils.Po + -rm -f ./$(DEPDIR)/preferences-dialog.Po + -rm -f ./$(DEPDIR)/print-size-dialog.Po + -rm -f ./$(DEPDIR)/quit-dialog.Po + -rm -f ./$(DEPDIR)/resize-dialog.Po + -rm -f ./$(DEPDIR)/resolution-calibrate-dialog.Po + -rm -f ./$(DEPDIR)/scale-dialog.Po + -rm -f ./$(DEPDIR)/stroke-dialog.Po + -rm -f ./$(DEPDIR)/template-options-dialog.Po + -rm -f ./$(DEPDIR)/tips-dialog.Po + -rm -f ./$(DEPDIR)/tips-parser.Po + -rm -f ./$(DEPDIR)/user-install-dialog.Po + -rm -f ./$(DEPDIR)/vectors-export-dialog.Po + -rm -f ./$(DEPDIR)/vectors-import-dialog.Po + -rm -f ./$(DEPDIR)/vectors-options-dialog.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)/about-dialog.Po + -rm -f ./$(DEPDIR)/action-search-dialog.Po + -rm -f ./$(DEPDIR)/channel-options-dialog.Po + -rm -f ./$(DEPDIR)/color-profile-dialog.Po + -rm -f ./$(DEPDIR)/color-profile-import-dialog.Po + -rm -f ./$(DEPDIR)/convert-indexed-dialog.Po + -rm -f ./$(DEPDIR)/convert-precision-dialog.Po + -rm -f ./$(DEPDIR)/data-delete-dialog.Po + -rm -f ./$(DEPDIR)/dialogs-constructors.Po + -rm -f ./$(DEPDIR)/dialogs.Po + -rm -f ./$(DEPDIR)/file-open-dialog.Po + -rm -f ./$(DEPDIR)/file-open-location-dialog.Po + -rm -f ./$(DEPDIR)/file-save-dialog.Po + -rm -f ./$(DEPDIR)/fill-dialog.Po + -rm -f ./$(DEPDIR)/grid-dialog.Po + -rm -f ./$(DEPDIR)/image-merge-layers-dialog.Po + -rm -f ./$(DEPDIR)/image-new-dialog.Po + -rm -f ./$(DEPDIR)/image-properties-dialog.Po + -rm -f ./$(DEPDIR)/image-scale-dialog.Po + -rm -f ./$(DEPDIR)/input-devices-dialog.Po + -rm -f ./$(DEPDIR)/item-options-dialog.Po + -rm -f ./$(DEPDIR)/keyboard-shortcuts-dialog.Po + -rm -f ./$(DEPDIR)/layer-add-mask-dialog.Po + -rm -f ./$(DEPDIR)/layer-options-dialog.Po + -rm -f ./$(DEPDIR)/lebl-dialog.Po + -rm -f ./$(DEPDIR)/module-dialog.Po + -rm -f ./$(DEPDIR)/palette-import-dialog.Po + -rm -f ./$(DEPDIR)/preferences-dialog-utils.Po + -rm -f ./$(DEPDIR)/preferences-dialog.Po + -rm -f ./$(DEPDIR)/print-size-dialog.Po + -rm -f ./$(DEPDIR)/quit-dialog.Po + -rm -f ./$(DEPDIR)/resize-dialog.Po + -rm -f ./$(DEPDIR)/resolution-calibrate-dialog.Po + -rm -f ./$(DEPDIR)/scale-dialog.Po + -rm -f ./$(DEPDIR)/stroke-dialog.Po + -rm -f ./$(DEPDIR)/template-options-dialog.Po + -rm -f ./$(DEPDIR)/tips-dialog.Po + -rm -f ./$(DEPDIR)/tips-parser.Po + -rm -f ./$(DEPDIR)/user-install-dialog.Po + -rm -f ./$(DEPDIR)/vectors-export-dialog.Po + -rm -f ./$(DEPDIR)/vectors-import-dialog.Po + -rm -f ./$(DEPDIR)/vectors-options-dialog.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 + + +$(srcdir)/about-dialog.c: authors.h + +authors.h: $(top_srcdir)/authors.xml $(srcdir)/authors.xsl +@HAVE_XSLTPROC_TRUE@ $(XSLTPROC) $(srcdir)/authors.xsl $< > $(@) || rm -f $(@) +@HAVE_XSLTPROC_FALSE@ @echo "*** xsltproc is required to regenerate $(@) ***"; exit 1; + +# 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/app/dialogs/about-dialog.c b/app/dialogs/about-dialog.c new file mode 100644 index 0000000..5db0fc4 --- /dev/null +++ b/app/dialogs/about-dialog.c @@ -0,0 +1,879 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "pdb/gimppdb.h" + +#include "about.h" +#include "git-version.h" + +#include "about-dialog.h" +#include "authors.h" +#include "gimp-update.h" +#include "gimp-version.h" + +#include "gimp-intl.h" + + +/* The first authors are the creators and maintainers, don't shuffle + * them + */ +#define START_INDEX (G_N_ELEMENTS (creators) - 1 /*NULL*/ + \ + G_N_ELEMENTS (maintainers) - 1 /*NULL*/) + + +typedef struct +{ + GtkWidget *dialog; + + GtkWidget *update_frame; + GimpCoreConfig *config; + + GtkWidget *anim_area; + PangoLayout *layout; + + gint n_authors; + gint shuffle[G_N_ELEMENTS (authors) - 1]; /* NULL terminated */ + + guint timer; + + gint index; + gint animstep; + gint textrange[2]; + gint state; + gboolean visible; +} GimpAboutDialog; + + +static void about_dialog_map (GtkWidget *widget, + GimpAboutDialog *dialog); +static void about_dialog_unmap (GtkWidget *widget, + GimpAboutDialog *dialog); +static GdkPixbuf * about_dialog_load_logo (void); +static void about_dialog_add_animation (GtkWidget *vbox, + GimpAboutDialog *dialog); +static gboolean about_dialog_anim_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpAboutDialog *dialog); +static void about_dialog_add_update (GimpAboutDialog *dialog, + GimpCoreConfig *config); +static void about_dialog_reshuffle (GimpAboutDialog *dialog); +static gboolean about_dialog_timer (gpointer data); + +#ifdef GIMP_UNSTABLE +static void about_dialog_add_unstable_message + (GtkWidget *vbox); +#endif /* GIMP_UNSTABLE */ + +static void about_dialog_last_release_changed + (GimpCoreConfig *config, + const GParamSpec *pspec, + GimpAboutDialog *dialog); +static void about_dialog_download_clicked + (GtkButton *button, + const gchar *link); + +GtkWidget * +about_dialog_create (GimpCoreConfig *config) +{ + static GimpAboutDialog dialog; + + g_return_val_if_fail (GIMP_IS_CORE_CONFIG (config), NULL); + + if (! dialog.dialog) + { + GtkWidget *widget; + GtkWidget *container; + GdkPixbuf *pixbuf; + GList *children; + gchar *copyright; + gchar *version; + + dialog.n_authors = G_N_ELEMENTS (authors) - 1; + dialog.config = config; + + pixbuf = about_dialog_load_logo (); + + copyright = g_strdup_printf (GIMP_COPYRIGHT, GIMP_GIT_LAST_COMMIT_YEAR); + if (gimp_version_get_revision () > 0) + /* Translators: the %s is GIMP version, the %d is the + * installer/package revision. + * For instance: "2.10.18 (revision 2)" + */ + version = g_strdup_printf (_("%s (revision %d)"), GIMP_VERSION, + gimp_version_get_revision ()); + else + version = g_strdup (GIMP_VERSION); + + widget = g_object_new (GTK_TYPE_ABOUT_DIALOG, + "role", "gimp-about", + "window-position", GTK_WIN_POS_CENTER, + "title", _("About GIMP"), + "program-name", GIMP_ACRONYM, + "version", version, + "copyright", copyright, + "comments", GIMP_NAME, + "license", GIMP_LICENSE, + "wrap-license", TRUE, + "logo", pixbuf, + "website", "https://www.gimp.org/", + "website-label", _("Visit the GIMP website"), + "authors", authors, + "artists", artists, + "documenters", documenters, + /* Translators: insert your names here, + separated by newline */ + "translator-credits", _("translator-credits"), + NULL); + + if (pixbuf) + g_object_unref (pixbuf); + + g_free (copyright); + g_free (version); + + dialog.dialog = widget; + + g_object_add_weak_pointer (G_OBJECT (widget), (gpointer) &dialog.dialog); + + g_signal_connect (widget, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + g_signal_connect (widget, "map", + G_CALLBACK (about_dialog_map), + &dialog); + g_signal_connect (widget, "unmap", + G_CALLBACK (about_dialog_unmap), + &dialog); + + /* kids, don't try this at home! */ + container = gtk_dialog_get_content_area (GTK_DIALOG (widget)); + children = gtk_container_get_children (GTK_CONTAINER (container)); + + if (GTK_IS_BOX (children->data)) + { + about_dialog_add_animation (children->data, &dialog); +#ifdef GIMP_UNSTABLE + about_dialog_add_unstable_message (children->data); +#endif /* GIMP_UNSTABLE */ + about_dialog_add_update (&dialog, config); + } + else + g_warning ("%s: ooops, no box in this container?", G_STRLOC); + + g_list_free (children); + } + + gtk_window_present (GTK_WINDOW (dialog.dialog)); + + return dialog.dialog; +} + +static void +about_dialog_map (GtkWidget *widget, + GimpAboutDialog *dialog) +{ + gimp_update_refresh (dialog->config); + + if (dialog->layout && dialog->timer == 0) + { + dialog->state = 0; + dialog->index = 0; + dialog->animstep = 0; + dialog->visible = FALSE; + + about_dialog_reshuffle (dialog); + + dialog->timer = g_timeout_add (800, about_dialog_timer, dialog); + } +} + +static void +about_dialog_unmap (GtkWidget *widget, + GimpAboutDialog *dialog) +{ + if (dialog->timer) + { + g_source_remove (dialog->timer); + dialog->timer = 0; + } +} + +static GdkPixbuf * +about_dialog_load_logo (void) +{ + GdkPixbuf *pixbuf = NULL; + GFile *file; + GInputStream *input; + + file = gimp_data_directory_file ("images", +#ifdef GIMP_UNSTABLE + "gimp-devel-logo.png", +#else + "gimp-logo.png", +#endif + NULL); + + input = G_INPUT_STREAM (g_file_read (file, NULL, NULL)); + g_object_unref (file); + + if (input) + { + pixbuf = gdk_pixbuf_new_from_stream (input, NULL, NULL); + g_object_unref (input); + } + + return pixbuf; +} + +static void +about_dialog_add_animation (GtkWidget *vbox, + GimpAboutDialog *dialog) +{ + gint height; + + dialog->anim_area = gtk_drawing_area_new (); + gtk_box_pack_start (GTK_BOX (vbox), dialog->anim_area, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (vbox), dialog->anim_area, 5); + gtk_widget_show (dialog->anim_area); + + dialog->layout = gtk_widget_create_pango_layout (dialog->anim_area, NULL); + g_object_weak_ref (G_OBJECT (dialog->anim_area), + (GWeakNotify) g_object_unref, dialog->layout); + + pango_layout_get_pixel_size (dialog->layout, NULL, &height); + + gtk_widget_set_size_request (dialog->anim_area, -1, 2 * height); + + g_signal_connect (dialog->anim_area, "expose-event", + G_CALLBACK (about_dialog_anim_expose), + dialog); +} + +static void +about_dialog_add_update (GimpAboutDialog *dialog, + GimpCoreConfig *config) +{ + GtkWidget *container; + GList *children; + GtkWidget *vbox; + + GtkWidget *frame; + GtkWidget *box; + GtkWidget *box2; + GtkWidget *label; + GtkWidget *button; + GtkWidget *button_image; + GtkWidget *button_label; + GDateTime *datetime; + gchar *date; + gchar *text; + + if (dialog->update_frame) + { + gtk_widget_destroy (dialog->update_frame); + dialog->update_frame = NULL; + } + + /* Get the dialog vbox. */ + container = gtk_dialog_get_content_area (GTK_DIALOG (dialog->dialog)); + children = gtk_container_get_children (GTK_CONTAINER (container)); + g_return_if_fail (GTK_IS_BOX (children->data)); + vbox = children->data; + g_list_free (children); + + /* The preferred localized date representation without the time. */ + datetime = g_date_time_new_from_unix_local (config->last_release_timestamp); + date = g_date_time_format (datetime, "%x"); + g_date_time_unref (datetime); + + /* The update frame. */ + frame = gtk_frame_new (NULL); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 2); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (frame), box); + + /* Button in the frame. */ + button = gtk_button_new (); + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add (GTK_CONTAINER (button), box2); + gtk_widget_show (box2); + + button_image = gtk_image_new_from_icon_name (NULL, GTK_ICON_SIZE_DIALOG); + gtk_box_pack_start (GTK_BOX (box2), button_image, FALSE, FALSE, 0); + gtk_widget_show (button_image); + + button_label = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX (box2), button_label, FALSE, FALSE, 0); + gtk_container_child_set (GTK_CONTAINER (box2), button_label, "expand", TRUE, NULL); + gtk_widget_show (button_label); + + if (config->last_known_release != NULL) + { + /* There is a newer version. */ + gchar *comment = NULL; + + /* We want the frame to stand out. */ + label = gtk_label_new (NULL); + text = g_strdup_printf ("%s", + _("Update available!")); + gtk_label_set_markup (GTK_LABEL (label), text); + g_free (text); + gtk_widget_show (label); + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_frame_set_label_align (GTK_FRAME (frame), 0.5, 0.5); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT); + gtk_box_reorder_child (GTK_BOX (vbox), frame, 3); + + /* Button is an update link. */ + gtk_image_set_from_icon_name (GTK_IMAGE (button_image), + "software-update-available", + GTK_ICON_SIZE_DIALOG); + g_signal_connect (button, "clicked", + (GCallback) about_dialog_download_clicked, + "https://www.gimp.org/downloads/"); + + if (config->last_revision > 0) + { + /* This is actually a new revision of current version. */ + text = g_strdup_printf (_("Download GIMP %s revision %d (released on %s)\n"), + config->last_known_release, + config->last_revision, + date); + + /* Finally an optional release comment. */ + if (config->last_release_comment) + { + /* Translators: <> tags are Pango markup. Please keep these + * markups in your translation. */ + comment = g_strdup_printf (_("Release comment: %s"), config->last_release_comment); + } + } + else + { + text = g_strdup_printf (_("Download GIMP %s (released on %s)\n"), + config->last_known_release, date); + } + gtk_label_set_text (GTK_LABEL (button_label), text); + g_free (text); + g_free (date); + + if (comment) + { + label = gtk_label_new (NULL); + gtk_label_set_max_width_chars (GTK_LABEL (label), 80); + gtk_label_set_markup (GTK_LABEL (label), comment); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + g_free (comment); + + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + } + } + else + { + /* Button is a "Check for updates" action. */ + gtk_image_set_from_icon_name (GTK_IMAGE (button_image), + "view-refresh", + GTK_ICON_SIZE_MENU); + gtk_label_set_text (GTK_LABEL (button_label), _("Check for updates")); + g_signal_connect_swapped (button, "clicked", + (GCallback) gimp_update_check, config); + + } + + gtk_box_reorder_child (GTK_BOX (vbox), frame, 4); + + /* Last check date box. */ + box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add (GTK_CONTAINER (box), box2); + gtk_widget_show (box2); + + /* Show a small "Check for updates" button only if the big one has + * been replaced by a download button. + */ + if (config->last_known_release != NULL) + { + button = gtk_button_new (); + button_image = gtk_image_new_from_icon_name ("view-refresh", GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (button), button_image); + gtk_widget_set_tooltip_text (button, _("Check for updates")); + gtk_box_pack_start (GTK_BOX (box2), button, FALSE, FALSE, 0); + g_signal_connect_swapped (button, "clicked", + (GCallback) gimp_update_check, config); + gtk_widget_show (button); + gtk_widget_show (button_image); + } + + if (config->check_update_timestamp > 0) + { + gchar *subtext; + gchar *time; + + datetime = g_date_time_new_from_unix_local (config->check_update_timestamp); + date = g_date_time_format (datetime, "%x"); + time = g_date_time_format (datetime, "%X"); + /* Translators: first string is the date in the locale's date + * representation (e.g., 12/31/99), second is the time in the + * locale's time representation (e.g., 23:13:48). + */ + subtext = g_strdup_printf (_("Last checked on %s at %s"), date, time); + g_date_time_unref (datetime); + g_free (date); + g_free (time); + + text = g_strdup_printf ("%s", subtext); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), text); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_container_child_set (GTK_CONTAINER (box2), label, "expand", TRUE, NULL); + gtk_widget_show (label); + g_free (text); + g_free (subtext); + } + + gtk_widget_show (box); + gtk_widget_show (frame); + + dialog->update_frame = frame; + g_object_add_weak_pointer (G_OBJECT (frame), (gpointer) &dialog->update_frame); + + /* Reconstruct the dialog when release info changes. */ + g_signal_connect (config, "notify::last-known-release", + (GCallback) about_dialog_last_release_changed, + dialog); +} + +static void +about_dialog_reshuffle (GimpAboutDialog *dialog) +{ + GRand *gr = g_rand_new (); + gint i; + + for (i = 0; i < dialog->n_authors; i++) + dialog->shuffle[i] = i; + + for (i = START_INDEX; i < dialog->n_authors; i++) + { + gint j = g_rand_int_range (gr, START_INDEX, dialog->n_authors); + + if (i != j) + { + gint t; + + t = dialog->shuffle[j]; + dialog->shuffle[j] = dialog->shuffle[i]; + dialog->shuffle[i] = t; + } + } + + g_rand_free (gr); +} + +static gboolean +about_dialog_anim_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpAboutDialog *dialog) +{ + GtkStyle *style = gtk_widget_get_style (widget); + cairo_t *cr; + GtkAllocation allocation; + gint x, y; + gint width, height; + + if (! dialog->visible) + return FALSE; + + cr = gdk_cairo_create (event->window); + + gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]); + + gtk_widget_get_allocation (widget, &allocation); + pango_layout_get_pixel_size (dialog->layout, &width, &height); + + x = (allocation.width - width) / 2; + y = (allocation.height - height) / 2; + + if (dialog->textrange[1] > 0) + { + GdkRegion *covered_region; + + covered_region = gdk_pango_layout_get_clip_region (dialog->layout, + x, y, + dialog->textrange, 1); + + gdk_region_intersect (covered_region, event->region); + + gdk_cairo_region (cr, covered_region); + cairo_clip (cr); + + gdk_region_destroy (covered_region); + } + + cairo_move_to (cr, x, y); + + pango_cairo_show_layout (cr, dialog->layout); + + cairo_destroy (cr); + + return FALSE; +} + +static gchar * +insert_spacers (const gchar *string) +{ + GString *str = g_string_new (NULL); + gchar *normalized; + gchar *ptr; + gunichar unichr; + + normalized = g_utf8_normalize (string, -1, G_NORMALIZE_DEFAULT_COMPOSE); + ptr = normalized; + + while ((unichr = g_utf8_get_char (ptr))) + { + g_string_append_unichar (str, unichr); + g_string_append_unichar (str, 0x200b); /* ZERO WIDTH SPACE */ + ptr = g_utf8_next_char (ptr); + } + + g_free (normalized); + + return g_string_free (str, FALSE); +} + +static inline void +mix_colors (const GdkColor *start, + const GdkColor *end, + GdkColor *target, + gdouble pos) +{ + target->red = start->red * (1.0 - pos) + end->red * pos; + target->green = start->green * (1.0 - pos) + end->green * pos; + target->blue = start->blue * (1.0 - pos) + end->blue * pos; +} + +static void +decorate_text (GimpAboutDialog *dialog, + gint anim_type, + gdouble time) +{ + GtkStyle *style = gtk_widget_get_style (dialog->anim_area); + const gchar *text; + const gchar *ptr; + gint letter_count = 0; + gint text_length = 0; + gint text_bytelen = 0; + gint cluster_start, cluster_end; + gunichar unichr; + PangoAttrList *attrlist = NULL; + PangoAttribute *attr; + PangoRectangle irect = {0, 0, 0, 0}; + PangoRectangle lrect = {0, 0, 0, 0}; + GdkColor mix; + + mix_colors (style->bg + GTK_STATE_NORMAL, + style->fg + GTK_STATE_NORMAL, &mix, time); + + text = pango_layout_get_text (dialog->layout); + g_return_if_fail (text != NULL); + + text_length = g_utf8_strlen (text, -1); + text_bytelen = strlen (text); + + attrlist = pango_attr_list_new (); + + dialog->textrange[0] = 0; + dialog->textrange[1] = text_bytelen; + + switch (anim_type) + { + case 0: /* Fade in */ + attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue); + attr->start_index = 0; + attr->end_index = text_bytelen; + pango_attr_list_insert (attrlist, attr); + break; + + case 1: /* Fade in, spread */ + attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue); + attr->start_index = 0; + attr->end_index = text_bytelen; + pango_attr_list_change (attrlist, attr); + + ptr = text; + + cluster_start = 0; + while ((unichr = g_utf8_get_char (ptr))) + { + ptr = g_utf8_next_char (ptr); + cluster_end = (ptr - text); + + if (unichr == 0x200b) + { + lrect.width = (1.0 - time) * 15.0 * PANGO_SCALE + 0.5; + attr = pango_attr_shape_new (&irect, &lrect); + attr->start_index = cluster_start; + attr->end_index = cluster_end; + pango_attr_list_change (attrlist, attr); + } + cluster_start = cluster_end; + } + break; + + case 2: /* Fade in, sinewave */ + attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue); + attr->start_index = 0; + attr->end_index = text_bytelen; + pango_attr_list_change (attrlist, attr); + + ptr = text; + + cluster_start = 0; + + while ((unichr = g_utf8_get_char (ptr))) + { + if (unichr == 0x200b) + { + cluster_end = ptr - text; + attr = pango_attr_rise_new ((1.0 -time) * 18000 * + sin (4.0 * time + + (float) letter_count * 0.7)); + attr->start_index = cluster_start; + attr->end_index = cluster_end; + pango_attr_list_change (attrlist, attr); + + letter_count++; + cluster_start = cluster_end; + } + + ptr = g_utf8_next_char (ptr); + } + break; + + case 3: /* letterwise Fade in */ + ptr = text; + + letter_count = 0; + cluster_start = 0; + + while ((unichr = g_utf8_get_char (ptr))) + { + gint border = (text_length + 15) * time - 15; + gdouble pos; + + if (letter_count < border) + pos = 0; + else if (letter_count > border + 15) + pos = 1; + else + pos = ((gdouble) (letter_count - border)) / 15; + + mix_colors (style->fg + GTK_STATE_NORMAL, + style->bg + GTK_STATE_NORMAL, + &mix, pos); + + ptr = g_utf8_next_char (ptr); + + cluster_end = ptr - text; + + attr = pango_attr_foreground_new (mix.red, mix.green, mix.blue); + attr->start_index = cluster_start; + attr->end_index = cluster_end; + pango_attr_list_change (attrlist, attr); + + if (pos < 1.0) + dialog->textrange[1] = cluster_end; + + letter_count++; + cluster_start = cluster_end; + } + + break; + + default: + g_printerr ("Unknown animation type %d\n", anim_type); + } + + pango_layout_set_attributes (dialog->layout, attrlist); + pango_attr_list_unref (attrlist); +} + +static gboolean +about_dialog_timer (gpointer data) +{ + GimpAboutDialog *dialog = data; + gint timeout = 0; + + if (dialog->animstep == 0) + { + gchar *text = NULL; + + dialog->visible = TRUE; + + switch (dialog->state) + { + case 0: + dialog->timer = g_timeout_add (30, about_dialog_timer, dialog); + dialog->state += 1; + return FALSE; + + case 1: + text = insert_spacers (_("GIMP is brought to you by")); + dialog->state += 1; + break; + + case 2: + if (! (dialog->index < dialog->n_authors)) + dialog->index = 0; + + text = insert_spacers (authors[dialog->shuffle[dialog->index]]); + dialog->index += 1; + break; + + default: + g_return_val_if_reached (TRUE); + break; + } + + g_return_val_if_fail (text != NULL, TRUE); + + pango_layout_set_text (dialog->layout, text, -1); + pango_layout_set_attributes (dialog->layout, NULL); + + g_free (text); + } + + if (dialog->animstep < 16) + { + decorate_text (dialog, 2, ((gfloat) dialog->animstep) / 15.0); + } + else if (dialog->animstep == 16) + { + timeout = 800; + } + else if (dialog->animstep == 17) + { + timeout = 30; + } + else if (dialog->animstep < 33) + { + decorate_text (dialog, 1, + 1.0 - ((gfloat) (dialog->animstep - 17)) / 15.0); + } + else if (dialog->animstep == 33) + { + dialog->visible = FALSE; + timeout = 300; + } + else + { + dialog->visible = FALSE; + dialog->animstep = -1; + timeout = 30; + } + + dialog->animstep++; + + gtk_widget_queue_draw (dialog->anim_area); + + if (timeout > 0) + { + dialog->timer = g_timeout_add (timeout, about_dialog_timer, dialog); + return FALSE; + } + + /* else keep the current timeout */ + return TRUE; +} + +#ifdef GIMP_UNSTABLE + +static void +about_dialog_add_unstable_message (GtkWidget *vbox) +{ + GtkWidget *label; + gchar *text; + + text = g_strdup_printf (_("This is an unstable development release\n" + "commit %s"), GIMP_GIT_VERSION_ABBREV); + label = gtk_label_new (text); + g_free (text); + + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC, + -1); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (vbox), label, 2); + gtk_widget_show (label); +} + +#endif /* GIMP_UNSTABLE */ + +static void +about_dialog_last_release_changed (GimpCoreConfig *config, + const GParamSpec *pspec, + GimpAboutDialog *dialog) +{ + g_signal_handlers_disconnect_by_func (config, + (GCallback) about_dialog_last_release_changed, + dialog); + if (! dialog->dialog) + return; + + about_dialog_add_update (dialog, config); +} + +static void +about_dialog_download_clicked (GtkButton *button, + const gchar *link) +{ + GtkWidget *window; + + window = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_WINDOW); + + if (window) + gtk_show_uri (gdk_screen_get_default (), + link, + gtk_get_current_event_time(), + NULL); +} diff --git a/app/dialogs/about-dialog.h b/app/dialogs/about-dialog.h new file mode 100644 index 0000000..516d7da --- /dev/null +++ b/app/dialogs/about-dialog.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 . + */ + +#ifndef __ABOUT_DIALOG_H__ +#define __ABOUT_DIALOG_H__ + + +GtkWidget * about_dialog_create (GimpCoreConfig *config); + + +#endif /* __ABOUT_DIALOG_H__ */ diff --git a/app/dialogs/action-search-dialog.c b/app/dialogs/action-search-dialog.c new file mode 100644 index 0000000..7b1d434 --- /dev/null +++ b/app/dialogs/action-search-dialog.c @@ -0,0 +1,358 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * action-search-dialog.c + * Copyright (C) 2012-2013 Srihari Sriraman + * Suhas V + * Vidyashree K + * Zeeshan Ali Ansari + * Copyright (C) 2013-2015 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "dialogs-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpaction-history.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpsearchpopup.h" +#include "widgets/gimpuimanager.h" + +#include "action-search-dialog.h" + +#include "gimp-intl.h" + + +static void action_search_history_and_actions (GimpSearchPopup *popup, + const gchar *keyword, + gpointer data); +static gboolean action_search_match_keyword (GimpAction *action, + const gchar* keyword, + gint *section, + Gimp *gimp); + + +/* Public Functions */ + +GtkWidget * +action_search_dialog_create (Gimp *gimp) +{ + GtkWidget *dialog; + + dialog = gimp_search_popup_new (gimp, + "gimp-action-search-dialog", + _("Search Actions"), + action_search_history_and_actions, + gimp); + return dialog; +} + +/* Private Functions */ + +static void +action_search_history_and_actions (GimpSearchPopup *popup, + const gchar *keyword, + gpointer data) +{ + GimpUIManager *manager; + GList *list; + GList *history_actions = NULL; + Gimp *gimp; + + g_return_if_fail (GIMP_IS_GIMP (data)); + + gimp = GIMP (data); + manager = gimp_ui_managers_from_name ("")->data; + + if (g_strcmp0 (keyword, "") == 0) + return; + + history_actions = gimp_action_history_search (gimp, + action_search_match_keyword, + keyword); + + /* First put on top of the list any matching action of user history. */ + for (list = history_actions; list; list = g_list_next (list)) + { + gimp_search_popup_add_result (popup, list->data, 0); + } + + /* Now check other actions. */ + for (list = gimp_ui_manager_get_action_groups (manager); + list; + list = g_list_next (list)) + { + GList *list2; + GimpActionGroup *group = list->data; + GList *actions = NULL; + + actions = gimp_action_group_list_actions (group); + actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare); + + for (list2 = actions; list2; list2 = g_list_next (list2)) + { + const gchar *name; + GimpAction *action = list2->data; + gboolean is_redundant = FALSE; + gint section; + + name = gimp_action_get_name (action); + + /* The action search dialog doesn't show any non-historized + * actions, with a few exceptions. See the difference between + * gimp_action_history_is_blacklisted_action() and + * gimp_action_history_is_excluded_action(). + */ + if (gimp_action_history_is_blacklisted_action (name)) + continue; + + if (! gimp_action_is_visible (action) || + (! gimp_action_is_sensitive (action) && + ! GIMP_GUI_CONFIG (gimp->config)->search_show_unavailable)) + continue; + + if (action_search_match_keyword (action, keyword, §ion, gimp)) + { + GList *list3; + + /* A matching action. Check if we have not already added + * it as an history action. + */ + for (list3 = history_actions; list3; list3 = g_list_next (list3)) + { + if (strcmp (gimp_action_get_name (list3->data), + name) == 0) + { + is_redundant = TRUE; + break; + } + } + + if (! is_redundant) + { + gimp_search_popup_add_result (popup, action, section); + } + } + } + + g_list_free (actions); + } + + g_list_free_full (history_actions, (GDestroyNotify) g_object_unref); +} + +static gboolean +action_search_match_keyword (GimpAction *action, + const gchar *keyword, + gint *section, + Gimp *gimp) +{ + gboolean matched = FALSE; + gchar **key_tokens; + gchar **label_tokens; + gchar **label_alternates = NULL; + gchar *tmp; + + if (keyword == NULL) + { + /* As a special exception, a NULL keyword means any action + * matches. + */ + if (section) + { + *section = 0; + } + return TRUE; + } + + key_tokens = g_str_tokenize_and_fold (keyword, gimp->config->language, NULL); + tmp = gimp_strip_uline (gimp_action_get_label (action)); + label_tokens = g_str_tokenize_and_fold (tmp, gimp->config->language, &label_alternates); + g_free (tmp); + + /* Try to match the keyword as an initialism of the action's label. + * For instance 'gb' will match 'Gaussian Blur...' + */ + if (g_strv_length (key_tokens) == 1) + { + gchar **search_tokens[] = {label_tokens, label_alternates}; + gint i; + + for (i = 0; i < G_N_ELEMENTS (search_tokens); i++) + { + const gchar *key_token; + gchar **label_tokens; + + for (key_token = key_tokens[0], label_tokens = search_tokens[i]; + *key_token && *label_tokens; + key_token = g_utf8_find_next_char (key_token, NULL), label_tokens++) + { + gunichar key_char = g_utf8_get_char (key_token); + gunichar label_char = g_utf8_get_char (*label_tokens); + + if (key_char != label_char) + break; + } + + if (! *key_token) + { + matched = TRUE; + + if (section) + { + /* full match is better than a partial match */ + *section = ! *label_tokens ? 1 : 4; + } + else + { + break; + } + } + } + } + + if (! matched && g_strv_length (label_tokens) > 0) + { + gint previous_matched = -1; + gboolean match_start; + gboolean match_ordered; + gint i; + + matched = TRUE; + match_start = TRUE; + match_ordered = TRUE; + for (i = 0; key_tokens[i] != NULL; i++) + { + gint j; + for (j = 0; label_tokens[j] != NULL; j++) + { + if (g_str_has_prefix (label_tokens[j], key_tokens[i])) + { + goto one_matched; + } + } + for (j = 0; label_alternates[j] != NULL; j++) + { + if (g_str_has_prefix (label_alternates[j], key_tokens[i])) + { + goto one_matched; + } + } + matched = FALSE; +one_matched: + if (previous_matched > j) + match_ordered = FALSE; + previous_matched = j; + + if (i != j) + match_start = FALSE; + + continue; + } + + if (matched && section) + { + /* If the key is the label start, this is a nicer match. + * Then if key tokens are found in the same order in the label. + * Finally we show at the end if the key tokens are found with a different order. */ + *section = match_ordered ? (match_start ? 1 : 2) : 3; + } + } + + if (! matched && key_tokens[0] && g_utf8_strlen (key_tokens[0], -1) > 2 && + gimp_action_get_tooltip (action) != NULL) + { + gchar **tooltip_tokens; + gchar **tooltip_alternates = NULL; + gboolean mixed_match; + gint i; + + tooltip_tokens = g_str_tokenize_and_fold (gimp_action_get_tooltip (action), + gimp->config->language, &tooltip_alternates); + + if (g_strv_length (tooltip_tokens) > 0) + { + matched = TRUE; + mixed_match = FALSE; + + for (i = 0; key_tokens[i] != NULL; i++) + { + gint j; + for (j = 0; tooltip_tokens[j] != NULL; j++) + { + if (g_str_has_prefix (tooltip_tokens[j], key_tokens[i])) + { + goto one_tooltip_matched; + } + } + for (j = 0; tooltip_alternates[j] != NULL; j++) + { + if (g_str_has_prefix (tooltip_alternates[j], key_tokens[i])) + { + goto one_tooltip_matched; + } + } + for (j = 0; label_tokens[j] != NULL; j++) + { + if (g_str_has_prefix (label_tokens[j], key_tokens[i])) + { + mixed_match = TRUE; + goto one_tooltip_matched; + } + } + for (j = 0; label_alternates[j] != NULL; j++) + { + if (g_str_has_prefix (label_alternates[j], key_tokens[i])) + { + mixed_match = TRUE; + goto one_tooltip_matched; + } + } + matched = FALSE; +one_tooltip_matched: + continue; + } + if (matched && section) + { + /* Matching the tooltip is section 4. We don't go looking + * for start of string or token order for tooltip match. + * But if the match is mixed on tooltip and label (there are + * no match for *only* label or *only* tooltip), this is + * section 5. */ + *section = mixed_match ? 6 : 5; + } + } + g_strfreev (tooltip_tokens); + g_strfreev (tooltip_alternates); + } + + g_strfreev (key_tokens); + g_strfreev (label_tokens); + g_strfreev (label_alternates); + + return matched; +} diff --git a/app/dialogs/action-search-dialog.h b/app/dialogs/action-search-dialog.h new file mode 100644 index 0000000..7fa3e06 --- /dev/null +++ b/app/dialogs/action-search-dialog.h @@ -0,0 +1,29 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * action-search-dialog.c + * Copyright (C) 2012-2013 Srihari Sriraman + * Suhas V + * Vidyashree K + * Zeeshan Ali Ansari + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ACTION_SEARCH_DIALOG_H__ +#define __ACTION_SEARCH_DIALOG_H__ + +GtkWidget * action_search_dialog_create (Gimp *gimp); + +#endif /* __ACTION_SEARCH_DIALOG_H__ */ diff --git a/app/dialogs/authors.h b/app/dialogs/authors.h new file mode 100644 index 0000000..2829c05 --- /dev/null +++ b/app/dialogs/authors.h @@ -0,0 +1,189 @@ + +/* NOTE: This file is auto-generated from authors.xml, do not edit it. */ + +static const gchar * const creators[] = +{ + "Spencer Kimball", + "Peter Mattis", + NULL +}; + +static const gchar * const maintainers[] = +{ + "Michael Natterer", + "Jehan", + NULL +}; + +static const gchar * const authors[] = +{ + "Spencer Kimball", + "Peter Mattis", + "Michael Natterer", + "Jehan", + "Fredrik Alströmer", + "Rob Antonishen", + "Timm Bäder", + "Jerry Baker", + "Daniel P. Berrange", + "Jacob Boerema", + "Hendrik Boom", + "Richard Bowers", + "Hans Breuer", + "Simon Budig", + "João S. O. Bueno", + "Seth Burgess", + "Marco Ciampa", + "Winston Chang", + "Lars-Peter Clausen", + "Sven Claussner", + "Kevin Cozens", + "Jeremiah Darais", + "Alexia Death", + "Nicholas Doyle", + "Marek Dvoroznak", + "Daniel Eddeland", + "Ulf-D. Ehlert", + "Gil Eliyahu", + "Tobias Ellinghaus", + "Ell", + "Dirk Farin", + "Richard Gitschlag", + "Saul Goode", + "David Gowers", + "Niels De Graef", + "Cameron Gregory", + "Stanislav Grinkov", + "Eric Grivel", + "Stephen Griffiths", + "Julien Hardelin", + "Tim Harder", + "Michael Henning", + "Lukasz Hladowski", + "Éric Hoffman", + "Patrick Horgan", + "Daniel Hornung", + "Christopher Howard", + "Alexander Hämmerle", + "HJ Imbens", + "Barak Itkin", + "Javier Jardón", + "Tim Jedlicka", + "Róman Joost", + "Alexander Jones", + "Aurimas Juška", + "Povilas Kanapickas", + "Malay Keshav", + "Øyvind Kolås", + "Lloyd Konneker", + "Kretynofil", + "Christian Krippendorf", + "Hartmut Kuhse", + "Eric Lamarque", + "Simone Karin Lehmann", + "Dave Lichterman", + "Adrian Likins", + "lillolollo", + "Tor Lillqvist", + "Nikc M.", + "Mikael Magnusson", + "Luidnel Maignan", + "Thomas Manni", + "Pascal Massimino", + "Johannes Matschke", + "Takeshi Matsuyama", + "Téo Mazars", + "Robert McHardy", + "Richard McLean", + "Jörn Meier", + "Mike Melancon", + "Christopher Montgomery", + "Simon Müller", + "Tobias Mueller", + "Michael Muré", + "Lionel N.", + "Sven Neumann", + "Andreas Neustifter", + "Jon Nordby", + "Martin Nordholts", + "Daniel Novomesky", + "David Odin", + "Nelson A. de Oliveira", + "Victor Oliveira", + "Yoshio Ono", + "Nathan Osman", + "Benjamin Otte", + "Petr Ovtchenkov", + "Juan Palacios", + "Ville Pätsi", + "Akkana Peck", + "Félix Piédallu", + "Nils Philippsen", + "Mircea Purdea", + "Liam Quin", + "John Ralls", + "Dennis Ranke", + "Debarshi Ray", + "Martin Renold", + "Kristian Rietveld", + "Gilles Rochefort", + "Marco Rossini", + "Karthikeyan S", + "Daniel Sabo", + "Oleksii Samorukov", + "Enrico Schröder", + "Michael Schumacher", + "Elad Shahar", + "shark0r", + "Peter Sikking", + "RyōTa SimaMoto", + "SHIRAKAWA Akira", + "Jernej Simončič", + "Manish Singh", + "Mukund Sivaraman", + "Ville Sokk", + "Jakub Steiner", + "Omari Stephens", + "Tobias Stoeckmann", + "Elle Stone", + "Bogdan Szczurek", + "Tal Trachtman", + "Mason Thomas", + "Benoit Touchette", + "Andreas Turtschan", + "Massimo Valentini", + "Thorsten Vollmer", + "Clayton Walker", + "Rupert Weber", + "Alexis Wilhelm", + "woob", + "Andrew Wyatt", + "Yoshinori Yamakawa", + "Mihail Zenkov", + "Zhenfeng Zhao", + "Simon Zilliken", + "Przemyslaw Zych", + NULL +}; + +static const gchar * const artists[] = +{ + "Alexia Death", + "Philipp Haegi", + "Aryeom Han", + "Ville Pätsi", + "Klaus Staedtler", + "Jakub Steiner", + NULL +}; + +static const gchar * const documenters[] = +{ + "Marco Ciampa", + "Sven Claussner", + "Ulf-D. Ehlert", + "Julien Hardelin", + "Róman Joost", + "Alexandre Prokoudine", + NULL +}; diff --git a/app/dialogs/authors.xsl b/app/dialogs/authors.xsl new file mode 100644 index 0000000..322eb8d --- /dev/null +++ b/app/dialogs/authors.xsl @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + +/* NOTE: This file is auto-generated from authors.xml, do not edit it. */ + +static const gchar * const creators[] = +{ + + + NULL +}; + + + +static const gchar * const maintainers[] = +{ + + + NULL +}; + + + +static const gchar * const authors[] = +{ + + + + + + + NULL +}; + + + +static const gchar * const artists[] = +{ + + + + + NULL +}; + + + +static const gchar * const documenters[] = +{ + + + + + NULL +}; + + + + "", + + "", + + "", + + + diff --git a/app/dialogs/channel-options-dialog.c b/app/dialogs/channel-options-dialog.c new file mode 100644 index 0000000..733bf3a --- /dev/null +++ b/app/dialogs/channel-options-dialog.c @@ -0,0 +1,247 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpchannel.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimpcolorpanel.h" +#include "widgets/gimpspinscale.h" +#include "widgets/gimpviewabledialog.h" + +#include "channel-options-dialog.h" +#include "item-options-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _ChannelOptionsDialog ChannelOptionsDialog; + +struct _ChannelOptionsDialog +{ + GimpChannelOptionsCallback callback; + gpointer user_data; + + GtkWidget *color_panel; + GtkWidget *save_sel_toggle; +}; + + +/* local function prototypes */ + +static void channel_options_dialog_free (ChannelOptionsDialog *private); +static void channel_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data); +static void channel_options_opacity_changed (GtkAdjustment *adjustment, + GimpColorButton *color_button); +static void channel_options_color_changed (GimpColorButton *color_button, + GtkAdjustment *adjustment); + + +/* public functions */ + +GtkWidget * +channel_options_dialog_new (GimpImage *image, + GimpChannel *channel, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *color_label, + const gchar *opacity_label, + gboolean show_from_sel, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + GimpChannelOptionsCallback callback, + gpointer user_data) +{ + ChannelOptionsDialog *private; + GtkWidget *dialog; + GtkAdjustment *opacity_adj; + GtkWidget *scale; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (channel == NULL || GIMP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (title != NULL, NULL); + g_return_val_if_fail (role != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (desc != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (channel_color != NULL, NULL); + g_return_val_if_fail (color_label != NULL, NULL); + g_return_val_if_fail (opacity_label != NULL, NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (ChannelOptionsDialog); + + private->callback = callback; + private->user_data = user_data; + + dialog = item_options_dialog_new (image, GIMP_ITEM (channel), context, + parent, title, role, + icon_name, desc, help_id, + channel_name ? _("Channel _name:") : NULL, + GIMP_ICON_TOOL_PAINTBRUSH, + _("Lock _pixels"), + _("Lock position and _size"), + channel_name, + channel_visible, + channel_linked, + channel_color_tag, + channel_lock_content, + channel_lock_position, + channel_options_dialog_callback, + private); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) channel_options_dialog_free, private); + + opacity_adj = (GtkAdjustment *) + gtk_adjustment_new (channel_color->a * 100.0, + 0.0, 100.0, 1.0, 10.0, 0); + scale = gimp_spin_scale_new (opacity_adj, NULL, 1); + gtk_widget_set_size_request (scale, 200, -1); + item_options_dialog_add_widget (dialog, + opacity_label, scale); + + private->color_panel = gimp_color_panel_new (color_label, + channel_color, + GIMP_COLOR_AREA_LARGE_CHECKS, + 24, 24); + gimp_color_panel_set_context (GIMP_COLOR_PANEL (private->color_panel), + context); + + g_signal_connect (opacity_adj, "value-changed", + G_CALLBACK (channel_options_opacity_changed), + private->color_panel); + + g_signal_connect (private->color_panel, "color-changed", + G_CALLBACK (channel_options_color_changed), + opacity_adj); + + item_options_dialog_add_widget (dialog, + NULL, private->color_panel); + + if (show_from_sel) + { + private->save_sel_toggle = + gtk_check_button_new_with_mnemonic (_("Initialize from _selection")); + + item_options_dialog_add_widget (dialog, + NULL, private->save_sel_toggle); + } + + return dialog; +} + + +/* private functions */ + +static void +channel_options_dialog_free (ChannelOptionsDialog *private) +{ + g_slice_free (ChannelOptionsDialog, private); +} + +static void +channel_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data) +{ + ChannelOptionsDialog *private = user_data; + GimpRGB color; + gboolean save_selection = FALSE; + + gimp_color_button_get_color (GIMP_COLOR_BUTTON (private->color_panel), + &color); + + if (private->save_sel_toggle) + save_selection = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->save_sel_toggle)); + + private->callback (dialog, + image, + GIMP_CHANNEL (item), + context, + item_name, + &color, + save_selection, + item_visible, + item_linked, + item_color_tag, + item_lock_content, + item_lock_position, + private->user_data); +} + +static void +channel_options_opacity_changed (GtkAdjustment *adjustment, + GimpColorButton *color_button) +{ + GimpRGB color; + + gimp_color_button_get_color (color_button, &color); + gimp_rgb_set_alpha (&color, gtk_adjustment_get_value (adjustment) / 100.0); + gimp_color_button_set_color (color_button, &color); +} + +static void +channel_options_color_changed (GimpColorButton *button, + GtkAdjustment *adjustment) +{ + GimpRGB color; + + gimp_color_button_get_color (button, &color); + gtk_adjustment_set_value (adjustment, color.a * 100.0); +} diff --git a/app/dialogs/channel-options-dialog.h b/app/dialogs/channel-options-dialog.h new file mode 100644 index 0000000..a1de490 --- /dev/null +++ b/app/dialogs/channel-options-dialog.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 . + */ + +#ifndef __CHANNEL_OPTIONS_DIALOG_H__ +#define __CHANNEL_OPTIONS_DIALOG_H__ + + +typedef void (* GimpChannelOptionsCallback) (GtkWidget *dialog, + GimpImage *image, + GimpChannel *channel, + GimpContext *context, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean save_selection, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + gpointer user_data); + + +GtkWidget * channel_options_dialog_new (GimpImage *image, + GimpChannel *channel, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *color_label, + const gchar *opacity_label, + gboolean show_from_sel, + const gchar *channel_name, + const GimpRGB *channel_color, + gboolean channel_visible, + gboolean channel_linked, + GimpColorTag channel_color_tag, + gboolean channel_lock_content, + gboolean channel_lock_position, + GimpChannelOptionsCallback callback, + gpointer user_data); + + +#endif /* __CHANNEL_OPTIONS_DIALOG_H__ */ diff --git a/app/dialogs/color-profile-dialog.c b/app/dialogs/color-profile-dialog.c new file mode 100644 index 0000000..225b811 --- /dev/null +++ b/app/dialogs/color-profile-dialog.c @@ -0,0 +1,494 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * color-profile-dialog.h + * Copyright (C) 2015 Michael Natterer + * + * Partly based on the lcms plug-in + * Copyright (C) 2006, 2007 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-constructors.h" +#include "widgets/gimpwidgets-utils.h" + +#include "color-profile-dialog.h" + +#include "gimp-intl.h" + + +typedef struct +{ + ColorProfileDialogType dialog_type; + GimpImage *image; + GimpColorProfile *current_profile; + GimpColorProfile *default_profile; + GimpColorRenderingIntent intent; + gboolean bpc; + GimpColorProfileCallback callback; + gpointer user_data; + + GimpColorConfig *config; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *combo; + GtkWidget *dest_view; + +} ProfileDialog; + + +static void color_profile_dialog_free (ProfileDialog *private); +static GtkWidget * color_profile_combo_box_new (ProfileDialog *private); +static void color_profile_dialog_response (GtkWidget *dialog, + gint response_id, + ProfileDialog *private); +static void color_profile_dest_changed (GtkWidget *combo, + ProfileDialog *private); + + +/* public functions */ + +GtkWidget * +color_profile_dialog_new (ColorProfileDialogType dialog_type, + GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpColorProfile *current_profile, + GimpColorProfile *default_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpColorProfileCallback callback, + gpointer user_data) +{ + ProfileDialog *private; + GtkWidget *dialog; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *expander; + GtkWidget *label; + const gchar *dest_label; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (current_profile == NULL || + GIMP_IS_COLOR_PROFILE (current_profile), NULL); + g_return_val_if_fail (default_profile == NULL || + GIMP_IS_COLOR_PROFILE (default_profile), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (ProfileDialog); + + private->dialog_type = dialog_type; + private->image = image; + private->current_profile = current_profile; + private->default_profile = default_profile; + private->intent = intent; + private->bpc = bpc; + private->callback = callback; + private->user_data = user_data; + private->config = image->gimp->config->color_management; + + switch (dialog_type) + { + case COLOR_PROFILE_DIALOG_ASSIGN_PROFILE: + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Assign ICC Color Profile"), + "gimp-image-color-profile-assign", + NULL, + _("Assign a color profile to the image"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_COLOR_PROFILE_ASSIGN, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Assign"), GTK_RESPONSE_OK, + + NULL); + dest_label = _("Assign"); + break; + + case COLOR_PROFILE_DIALOG_CONVERT_TO_PROFILE: + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Convert to ICC Color Profile"), + "gimp-image-color-profile-convert", + NULL, + _("Convert the image to a color profile"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_COLOR_PROFILE_CONVERT, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onvert"), GTK_RESPONSE_OK, + + NULL); + dest_label = _("Convert to"); + break; + + case COLOR_PROFILE_DIALOG_CONVERT_TO_RGB: + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("RGB Conversion"), + "gimp-image-convert-rgb", + GIMP_ICON_CONVERT_RGB, + _("Convert Image to RGB"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_CONVERT_RGB, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onvert"), GTK_RESPONSE_OK, + + NULL); + dest_label = _("Convert to"); + break; + + case COLOR_PROFILE_DIALOG_CONVERT_TO_GRAY: + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Grayscale Conversion"), + "gimp-image-convert-gray", + GIMP_ICON_CONVERT_GRAYSCALE, + _("Convert Image to Grayscale"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_CONVERT_GRAYSCALE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onvert"), GTK_RESPONSE_OK, + + NULL); + dest_label = _("Convert to"); + break; + + case COLOR_PROFILE_DIALOG_SELECT_SOFTPROOF_PROFILE: + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Soft-Proof Profile"), + "gimp-select-softproof-profile", + GIMP_ICON_DOCUMENT_PRINT, + _("Select Soft-Proof Profile"), + parent, + gimp_standard_help_func, + GIMP_HELP_VIEW_COLOR_MANAGEMENT, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Select"), GTK_RESPONSE_OK, + + NULL); + dest_label = _("New Color Profile"); + break; + + default: + g_return_val_if_reached (NULL); + } + + private->dialog = dialog; + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) color_profile_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (color_profile_dialog_response), + private); + + private->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (private->main_vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + private->main_vbox, TRUE, TRUE, 0); + gtk_widget_show (private->main_vbox); + + frame = gimp_frame_new (_("Current Color Profile")); + gtk_box_pack_start (GTK_BOX (private->main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + label = gimp_color_profile_label_new (private->current_profile); + gtk_container_add (GTK_CONTAINER (frame), label); + gtk_widget_show (label); + + frame = gimp_frame_new (dest_label); + gtk_box_pack_start (GTK_BOX (private->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); + + private->combo = color_profile_combo_box_new (private); + gtk_box_pack_start (GTK_BOX (vbox), private->combo, FALSE, FALSE, 0); + gtk_widget_show (private->combo); + + expander = gtk_expander_new_with_mnemonic (_("Profile _details")); + gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0); + gtk_widget_show (expander); + + private->dest_view = gimp_color_profile_view_new (); + gtk_container_add (GTK_CONTAINER (expander), private->dest_view); + gtk_widget_show (private->dest_view); + + g_signal_connect (private->combo, "changed", + G_CALLBACK (color_profile_dest_changed), + private); + + color_profile_dest_changed (private->combo, private); + + if (dialog_type == COLOR_PROFILE_DIALOG_CONVERT_TO_PROFILE) + { + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *combo; + GtkWidget *toggle; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (private->main_vbox), vbox, FALSE, FALSE, 0); + 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 (_("_Rendering Intent:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + combo = gimp_enum_combo_box_new (GIMP_TYPE_COLOR_RENDERING_INTENT); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->intent, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->intent); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + toggle = + gtk_check_button_new_with_mnemonic (_("_Black Point Compensation")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), private->bpc); + 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), + &private->bpc); + } + + return dialog; +} + + +/* private functions */ + +static void +color_profile_dialog_free (ProfileDialog *private) +{ + g_slice_free (ProfileDialog, private); +} + +static GtkWidget * +color_profile_combo_box_new (ProfileDialog *private) +{ + GtkListStore *store; + GtkWidget *combo; + GtkWidget *chooser; + gchar *history; + + history = gimp_personal_rc_file ("profilerc"); + store = gimp_color_profile_store_new (history); + g_free (history); + + if (private->default_profile) + { + GimpImageBaseType base_type; + GimpPrecision precision; + GError *error = NULL; + + switch (private->dialog_type) + { + case COLOR_PROFILE_DIALOG_ASSIGN_PROFILE: + case COLOR_PROFILE_DIALOG_CONVERT_TO_PROFILE: + base_type = gimp_image_get_base_type (private->image); + break; + + case COLOR_PROFILE_DIALOG_CONVERT_TO_RGB: + base_type = GIMP_RGB; + break; + + case COLOR_PROFILE_DIALOG_CONVERT_TO_GRAY: + base_type = GIMP_GRAY; + break; + + default: + g_return_val_if_reached (NULL); + } + + precision = gimp_image_get_precision (private->image); + + if (! gimp_color_profile_store_add_defaults (GIMP_COLOR_PROFILE_STORE (store), + private->config, + base_type, + precision, + &error)) + { + gimp_message (private->image->gimp, G_OBJECT (private->dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + } + } + else + { + gimp_color_profile_store_add_file (GIMP_COLOR_PROFILE_STORE (store), + NULL, NULL); + } + + chooser = + gimp_color_profile_chooser_dialog_new (_("Select Destination Profile"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN); + + gimp_color_profile_chooser_dialog_connect_path (chooser, + G_OBJECT (private->image->gimp->config), + "color-profile-path"); + + combo = gimp_color_profile_combo_box_new_with_model (chooser, + GTK_TREE_MODEL (store)); + g_object_unref (store); + + gimp_color_profile_combo_box_set_active_file (GIMP_COLOR_PROFILE_COMBO_BOX (combo), + NULL, NULL); + + return combo; +} + +static void +color_profile_dialog_response (GtkWidget *dialog, + gint response_id, + ProfileDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GimpColorProfile *profile = NULL; + GFile *file; + + file = gimp_color_profile_combo_box_get_active_file (GIMP_COLOR_PROFILE_COMBO_BOX (private->combo)); + + if (file) + { + GError *error = NULL; + + profile = gimp_color_profile_new_from_file (file, &error); + g_object_unref (file); + + if (! profile) + { + gimp_message (private->image->gimp, G_OBJECT (dialog), + GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + + return; + } + } + else if (private->default_profile) + { + profile = g_object_ref (private->default_profile); + } + + private->callback (dialog, + private->image, + profile, + file, + private->intent, + private->bpc, + private->user_data); + + if (profile) + g_object_unref (profile); + } + else + { + gtk_widget_destroy (dialog); + } +} + +static void +color_profile_dest_changed (GtkWidget *combo, + ProfileDialog *private) +{ + GimpColorProfile *dest_profile = NULL; + GFile *file; + + file = gimp_color_profile_combo_box_get_active_file (GIMP_COLOR_PROFILE_COMBO_BOX (combo)); + + if (file) + { + GError *error = NULL; + + dest_profile = gimp_color_profile_new_from_file (file, &error); + g_object_unref (file); + + if (! dest_profile) + { + gimp_color_profile_view_set_error (GIMP_COLOR_PROFILE_VIEW (private->dest_view), + error->message); + g_clear_error (&error); + } + } + else if (private->default_profile) + { + dest_profile = g_object_ref (private->default_profile); + } + else + { + gimp_color_profile_view_set_error (GIMP_COLOR_PROFILE_VIEW (private->dest_view), + C_("profile", "None")); + } + + if (dest_profile) + { + gimp_color_profile_view_set_profile (GIMP_COLOR_PROFILE_VIEW (private->dest_view), + dest_profile); + g_object_unref (dest_profile); + } +} diff --git a/app/dialogs/color-profile-dialog.h b/app/dialogs/color-profile-dialog.h new file mode 100644 index 0000000..5f3921d --- /dev/null +++ b/app/dialogs/color-profile-dialog.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * color-profile-dialog.h + * Copyright (C) 2015 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __COLOR_PROFILE_DIALOG_H__ +#define __COLOR_PROFILE_DIALOG_H__ + + +typedef enum +{ + COLOR_PROFILE_DIALOG_ASSIGN_PROFILE, + COLOR_PROFILE_DIALOG_CONVERT_TO_PROFILE, + COLOR_PROFILE_DIALOG_CONVERT_TO_RGB, + COLOR_PROFILE_DIALOG_CONVERT_TO_GRAY, + COLOR_PROFILE_DIALOG_SELECT_SOFTPROOF_PROFILE +} ColorProfileDialogType; + + +typedef void (* GimpColorProfileCallback) (GtkWidget *dialog, + GimpImage *image, + GimpColorProfile *new_profile, + GFile *new_file, + GimpColorRenderingIntent intent, + gboolean bpc, + gpointer user_data); + + +GtkWidget * color_profile_dialog_new (ColorProfileDialogType dialog_type, + GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpColorProfile *current_profile, + GimpColorProfile *default_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpColorProfileCallback callback, + gpointer user_data); + + +#endif /* __COLOR_PROFILE_DIALOG_H__ */ diff --git a/app/dialogs/color-profile-import-dialog.c b/app/dialogs/color-profile-import-dialog.c new file mode 100644 index 0000000..86f2be3 --- /dev/null +++ b/app/dialogs/color-profile-import-dialog.c @@ -0,0 +1,216 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * color-profile-import-dialog.h + * Copyright (C) 2015 Michael Natterer + * + * Partly based on the lcms plug-in + * Copyright (C) 2006, 2007 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-color-profile.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-constructors.h" + +#include "color-profile-import-dialog.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GimpColorProfilePolicy +color_profile_import_dialog_run (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask) +{ + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *label; + GtkWidget *intent_combo; + GtkWidget *bpc_toggle; + GtkWidget *dont_ask_toggle; + GimpColorProfile *src_profile; + GimpColorProfilePolicy policy; + const gchar *title; + const gchar *frame_title; + gchar *text; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), + GIMP_COLOR_PROFILE_POLICY_KEEP); + g_return_val_if_fail (dest_profile != NULL, GIMP_COLOR_PROFILE_POLICY_KEEP); + + src_profile = gimp_image_get_color_profile (image); + *dest_profile = gimp_image_get_builtin_color_profile (image); + + if (gimp_image_get_base_type (image) == GIMP_GRAY) + { + title = _("Convert to Grayscale Working Space?"); + frame_title = _("Convert the image to the built-in grayscale color profile?"); + } + else + { + title = _("Convert to RGB Working Space?"); + frame_title = _("Convert the image to the built-in sRGB color profile?"); + } + + dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + title, + "gimp-image-color-profile-import", + NULL, + _("Import the image from a color profile"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_COLOR_PROFILE_IMPORT, + + _("_Keep"), GTK_RESPONSE_CANCEL, + _("C_onvert"), 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); + + 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); + + text = g_strdup_printf (_("The image '%s' has an embedded color profile"), + gimp_image_get_display_name (image)); + frame = gimp_frame_new (text); + g_free (text); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + label = gimp_color_profile_label_new (src_profile); + gtk_container_add (GTK_CONTAINER (frame), label); + gtk_widget_show (label); + + frame = gimp_frame_new (frame_title); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + label = gimp_color_profile_label_new (*dest_profile); + gtk_container_add (GTK_CONTAINER (frame), label); + gtk_widget_show (label); + + if (intent && bpc) + { + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + } + else + { + vbox = main_vbox; + } + + if (intent) + { + GtkWidget *hbox; + + 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 (_("_Rendering Intent:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + intent_combo = gimp_enum_combo_box_new (GIMP_TYPE_COLOR_RENDERING_INTENT); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (intent_combo), + *intent); + gtk_box_pack_start (GTK_BOX (hbox), intent_combo, TRUE, TRUE, 0); + gtk_widget_show (intent_combo); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), intent_combo); + } + + if (bpc) + { + bpc_toggle = + gtk_check_button_new_with_mnemonic (_("_Black Point Compensation")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bpc_toggle), *bpc); + gtk_box_pack_start (GTK_BOX (vbox), bpc_toggle, FALSE, FALSE, 0); + gtk_widget_show (bpc_toggle); + } + + if (dont_ask) + { + dont_ask_toggle = + gtk_check_button_new_with_mnemonic (_("_Don't ask me again")); + gtk_box_pack_end (GTK_BOX (main_vbox), dont_ask_toggle, FALSE, FALSE, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dont_ask_toggle), FALSE); + gtk_widget_show (dont_ask_toggle); + } + + switch (gtk_dialog_run (GTK_DIALOG (dialog))) + { + case GTK_RESPONSE_OK: + policy = GIMP_COLOR_PROFILE_POLICY_CONVERT; + g_object_ref (*dest_profile); + break; + + default: + policy = GIMP_COLOR_PROFILE_POLICY_KEEP; + break; + } + + if (intent) + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (intent_combo), + (gint *) intent); + + if (bpc) + *bpc = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bpc_toggle)); + + if (dont_ask) + *dont_ask = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dont_ask_toggle)); + + gtk_widget_destroy (dialog); + + return policy; +} diff --git a/app/dialogs/color-profile-import-dialog.h b/app/dialogs/color-profile-import-dialog.h new file mode 100644 index 0000000..2b91d4c --- /dev/null +++ b/app/dialogs/color-profile-import-dialog.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * color-profile-import-dialog.h + * Copyright (C) 2015 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __COLOR_PROFILE_IMPORT_DIALOG_H__ +#define __COLOR_PROFILE_IMPORT_DIALOG_H__ + + +GimpColorProfilePolicy + color_profile_import_dialog_run (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask); + + +#endif /* __COLOR_PROFILE_IMPORT_DIALOG_H__ */ diff --git a/app/dialogs/convert-indexed-dialog.c b/app/dialogs/convert-indexed-dialog.c new file mode 100644 index 0000000..95b705d --- /dev/null +++ b/app/dialogs/convert-indexed-dialog.c @@ -0,0 +1,436 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer-filter.h" +#include "core/gimpcontext.h" +#include "core/gimpdatafactory.h" +#include "core/gimpimage.h" +#include "core/gimplist.h" +#include "core/gimppalette.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewablebox.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "convert-indexed-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _IndexedDialog IndexedDialog; + +struct _IndexedDialog +{ + GimpImage *image; + GimpConvertPaletteType palette_type; + gint max_colors; + gboolean remove_duplicates; + GimpConvertDitherType dither_type; + gboolean dither_alpha; + gboolean dither_text_layers; + GimpPalette *custom_palette; + GimpConvertIndexedCallback callback; + gpointer user_data; + + GtkWidget *dialog; + GimpContext *context; + GimpContainer *container; + GtkWidget *duplicates_toggle; +}; + + +static void convert_dialog_free (IndexedDialog *private); +static void convert_dialog_response (GtkWidget *widget, + gint response_id, + IndexedDialog *private); +static GtkWidget * convert_dialog_palette_box (IndexedDialog *private); +static gboolean convert_dialog_palette_filter (GimpObject *object, + gpointer user_data); +static void convert_dialog_palette_changed (GimpContext *context, + GimpPalette *palette, + IndexedDialog *private); +static void convert_dialog_type_update (GtkWidget *widget, + IndexedDialog *private); + + + +/* public functions */ + +GtkWidget * +convert_indexed_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + GimpConvertIndexedCallback callback, + gpointer user_data) +{ + IndexedDialog *private; + GtkWidget *dialog; + GtkWidget *button; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkAdjustment *adjustment; + GtkWidget *spinbutton; + GtkWidget *frame; + GtkWidget *toggle; + GtkWidget *palette_box; + GtkWidget *combo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (custom_palette == NULL || + GIMP_IS_PALETTE (custom_palette), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (IndexedDialog); + + private->image = image; + private->palette_type = palette_type; + private->max_colors = max_colors; + private->remove_duplicates = remove_duplicates; + private->dither_type = dither_type; + private->dither_alpha = dither_alpha; + private->dither_text_layers = dither_text_layers; + private->custom_palette = custom_palette; + private->callback = callback; + private->user_data = user_data; + + private->dialog = dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Indexed Color Conversion"), + "gimp-image-convert-indexed", + GIMP_ICON_CONVERT_INDEXED, + _("Convert Image to Indexed Colors"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_CONVERT_INDEXED, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onvert"), 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); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) convert_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (convert_dialog_response), + private); + + palette_box = convert_dialog_palette_box (private); + + 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); + + + /* palette */ + + frame = + gimp_enum_radio_frame_new_with_range (GIMP_TYPE_CONVERT_PALETTE_TYPE, + GIMP_CONVERT_PALETTE_GENERATE, + (palette_box ? + GIMP_CONVERT_PALETTE_CUSTOM : + GIMP_CONVERT_PALETTE_MONO), + gtk_label_new (_("Colormap")), + G_CALLBACK (convert_dialog_type_update), + private, + &button); + + gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), + private->palette_type); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* max n_colors */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gimp_enum_radio_frame_add (GTK_FRAME (frame), hbox, + GIMP_CONVERT_PALETTE_GENERATE, TRUE); + gtk_widget_show (hbox); + + label = gtk_label_new_with_mnemonic (_("_Maximum number of colors:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + if (private->max_colors == 256 && gimp_image_has_alpha (image)) + private->max_colors = 255; + + adjustment = (GtkAdjustment *) + gtk_adjustment_new (private->max_colors, 2, 256, 1, 8, 0); + spinbutton = gimp_spin_button_new (adjustment, 1.0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton); + gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0); + gtk_widget_show (spinbutton); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gimp_int_adjustment_update), + &private->max_colors); + + /* custom palette */ + if (palette_box) + { + gimp_enum_radio_frame_add (GTK_FRAME (frame), palette_box, + GIMP_CONVERT_PALETTE_CUSTOM, TRUE); + gtk_widget_show (palette_box); + } + + vbox = gtk_bin_get_child (GTK_BIN (frame)); + + private->duplicates_toggle = toggle = + gtk_check_button_new_with_mnemonic (_("_Remove unused and duplicate " + "colors from colormap")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + private->remove_duplicates); + gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 3); + gtk_widget_show (toggle); + + if (private->palette_type == GIMP_CONVERT_PALETTE_GENERATE || + private->palette_type == GIMP_CONVERT_PALETTE_MONO) + gtk_widget_set_sensitive (toggle, FALSE); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->remove_duplicates); + + /* dithering */ + + frame = gimp_frame_new (_("Dithering")); + 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); + + 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 _dithering:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + combo = gimp_enum_combo_box_new (GIMP_TYPE_CONVERT_DITHER_TYPE); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->dither_type, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->dither_type); + + toggle = + gtk_check_button_new_with_mnemonic (_("Enable dithering of _transparency")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + private->dither_alpha); + 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), + &private->dither_alpha); + + + toggle = + gtk_check_button_new_with_mnemonic (_("Enable dithering of text _layers")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + private->dither_text_layers); + 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), + &private->dither_text_layers); + + gimp_help_set_help_data (toggle, + _("Dithering text layers will make them uneditable"), + NULL); + + return dialog; +} + + +/* private functions */ + +static void +convert_dialog_free (IndexedDialog *private) +{ + if (private->container) + g_object_unref (private->container); + + if (private->context) + g_object_unref (private->context); + + g_slice_free (IndexedDialog, private); +} + +static void +convert_dialog_response (GtkWidget *dialog, + gint response_id, + IndexedDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + private->callback (dialog, + private->image, + private->palette_type, + private->max_colors, + private->remove_duplicates, + private->dither_type, + private->dither_alpha, + private->dither_text_layers, + private->custom_palette, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} + +static GtkWidget * +convert_dialog_palette_box (IndexedDialog *private) +{ + Gimp *gimp = private->image->gimp; + GList *list; + GimpPalette *web_palette = NULL; + gboolean custom_found = FALSE; + + /* We can't dither to > 256 colors */ + private->container = + gimp_container_filter (gimp_data_factory_get_container (gimp->palette_factory), + convert_dialog_palette_filter, + NULL); + + if (gimp_container_is_empty (private->container)) + { + g_object_unref (private->container); + private->container = NULL; + return NULL; + } + + private->context = gimp_context_new (gimp, "convert-dialog", NULL); + + for (list = GIMP_LIST (private->container)->queue->head; + list; + list = g_list_next (list)) + { + GimpPalette *palette = list->data; + + /* Preferentially, the initial default is 'Web' if available */ + if (web_palette == NULL && + g_ascii_strcasecmp (gimp_object_get_name (palette), "Web") == 0) + { + web_palette = palette; + } + + if (private->custom_palette == palette) + custom_found = TRUE; + } + + if (! custom_found) + { + if (web_palette) + private->custom_palette = web_palette; + else + private->custom_palette = GIMP_LIST (private->container)->queue->head->data; + } + + gimp_context_set_palette (private->context, private->custom_palette); + + g_signal_connect (private->context, "palette-changed", + G_CALLBACK (convert_dialog_palette_changed), + private); + + return gimp_palette_box_new (private->container, private->context, NULL, 4); +} + +static gboolean +convert_dialog_palette_filter (GimpObject *object, + gpointer user_data) +{ + GimpPalette *palette = GIMP_PALETTE (object); + + return (gimp_palette_get_n_colors (palette) > 0 && + gimp_palette_get_n_colors (palette) <= 256); +} + +static void +convert_dialog_palette_changed (GimpContext *context, + GimpPalette *palette, + IndexedDialog *private) +{ + if (! palette) + return; + + if (gimp_palette_get_n_colors (palette) > 256) + { + gimp_message (private->image->gimp, G_OBJECT (private->dialog), + GIMP_MESSAGE_WARNING, + _("Cannot convert to a palette " + "with more than 256 colors.")); + } + else + { + private->custom_palette = palette; + } +} + +static void +convert_dialog_type_update (GtkWidget *widget, + IndexedDialog *private) +{ + gimp_radio_button_update (widget, &private->palette_type); + + if (private->duplicates_toggle) + gtk_widget_set_sensitive (private->duplicates_toggle, + private->palette_type != + GIMP_CONVERT_PALETTE_GENERATE && + private->palette_type != + GIMP_CONVERT_PALETTE_MONO); +} diff --git a/app/dialogs/convert-indexed-dialog.h b/app/dialogs/convert-indexed-dialog.h new file mode 100644 index 0000000..df0ca50 --- /dev/null +++ b/app/dialogs/convert-indexed-dialog.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __CONVERT_INDEXED_DIALOG_H__ +#define __CONVERT_INDEXED_DIALOG_H__ + + +typedef void (* GimpConvertIndexedCallback) (GtkWidget *dialog, + GimpImage *image, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + gpointer user_data); + + +GtkWidget * convert_indexed_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpConvertPaletteType palette_type, + gint max_colors, + gboolean remove_duplicates, + GimpConvertDitherType dither_type, + gboolean dither_alpha, + gboolean dither_text_layers, + GimpPalette *custom_palette, + GimpConvertIndexedCallback callback, + gpointer user_data); + + +#endif /* __CONVERT_INDEXED_DIALOG_H__ */ diff --git a/app/dialogs/convert-precision-dialog.c b/app/dialogs/convert-precision-dialog.c new file mode 100644 index 0000000..180106b --- /dev/null +++ b/app/dialogs/convert-precision-dialog.c @@ -0,0 +1,342 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "convert-precision-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _ConvertDialog ConvertDialog; + +struct _ConvertDialog +{ + GimpImage *image; + GimpComponentType component_type; + gboolean linear; + GeglDitherMethod layer_dither_method; + GeglDitherMethod text_layer_dither_method; + GeglDitherMethod channel_dither_method; + GimpConvertPrecisionCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void convert_precision_dialog_free (ConvertDialog *private); +static void convert_precision_dialog_response (GtkWidget *widget, + gint response_id, + ConvertDialog *private); + + +/* public functions */ + +GtkWidget * +convert_precision_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpComponentType component_type, + GeglDitherMethod layer_dither_method, + GeglDitherMethod text_layer_dither_method, + GeglDitherMethod channel_dither_method, + GimpConvertPrecisionCallback callback, + gpointer user_data) + +{ + ConvertDialog *private; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *frame; + const gchar *enum_desc; + gchar *blurb; + const Babl *old_format; + const Babl *new_format; + gint old_bits; + gint new_bits; + gboolean dither; + gboolean linear; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + /* random formats with the right precision */ + old_format = gimp_image_get_layer_format (image, FALSE); + new_format = gimp_babl_format (GIMP_RGB, + gimp_babl_precision (component_type, FALSE), + FALSE); + + old_bits = (babl_format_get_bytes_per_pixel (old_format) * 8 / + babl_format_get_n_components (old_format)); + new_bits = (babl_format_get_bytes_per_pixel (new_format) * 8 / + babl_format_get_n_components (new_format)); + + /* don't dither if we are converting to a higher bit depth, + * or to more than MAX_DITHER_BITS. + */ + dither = (new_bits < old_bits && + new_bits <= CONVERT_PRECISION_DIALOG_MAX_DITHER_BITS); + + /* when changing this logic, also change the same switch() + * in gimptemplateeditor.h + */ + switch (component_type) + { + case GIMP_COMPONENT_TYPE_U8: + /* default to gamma when converting 8 bit */ + linear = FALSE; + break; + + case GIMP_COMPONENT_TYPE_U16: + case GIMP_COMPONENT_TYPE_U32: + default: + /* leave gamma alone by default when converting to 16/32 bit int */ + linear = gimp_babl_format_get_linear (old_format); + break; + + case GIMP_COMPONENT_TYPE_HALF: + case GIMP_COMPONENT_TYPE_FLOAT: + case GIMP_COMPONENT_TYPE_DOUBLE: + /* default to linear when converting to floating point */ + linear = TRUE; + break; + } + + private = g_slice_new0 (ConvertDialog); + + private->image = image; + private->component_type = component_type; + private->linear = linear; + private->layer_dither_method = layer_dither_method; + private->text_layer_dither_method = text_layer_dither_method; + private->channel_dither_method = channel_dither_method; + private->callback = callback; + private->user_data = user_data; + + gimp_enum_get_value (GIMP_TYPE_COMPONENT_TYPE, component_type, + NULL, NULL, &enum_desc, NULL); + + blurb = g_strdup_printf (_("Convert Image to %s"), enum_desc); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, +#if PENDING_TRANSLATION + _("Encoding Conversion"), +#endif + _("Precision Conversion"), + "gimp-image-convert-precision", + GIMP_ICON_CONVERT_PRECISION, + blurb, + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_CONVERT_PRECISION, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("C_onvert"), GTK_RESPONSE_OK, + + NULL); + + g_free (blurb); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) convert_precision_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (convert_precision_dialog_response), + private); + + 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); + + + /* gamma */ + + frame = gimp_frame_new (_("Gamma")); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + vbox = gimp_int_radio_group_new (FALSE, NULL, + G_CALLBACK (gimp_radio_button_update), + &private->linear, + linear, + + _("Perceptual gamma (sRGB)"), FALSE, NULL, + _("Linear light"), TRUE, NULL, + + NULL); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + + /* dithering */ + + if (dither) + { + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo; + GtkSizeGroup *size_group; + + frame = gimp_frame_new (_("Dithering")); + 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); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* layers */ + + 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 (_("_Layers:")); + 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 (size_group, label); + gtk_widget_show (label); + + combo = gimp_enum_combo_box_new (GEGL_TYPE_DITHER_METHOD); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->layer_dither_method, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->layer_dither_method); + + /* text layers */ + + 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 (_("_Text Layers:")); + 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 (size_group, label); + gtk_widget_show (label); + + combo = gimp_enum_combo_box_new (GEGL_TYPE_DITHER_METHOD); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->text_layer_dither_method, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->text_layer_dither_method); + + gimp_help_set_help_data (combo, + _("Dithering text layers will make them " + "uneditable"), + NULL); + + /* channels */ + + 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 (_("_Channels and Masks:")); + 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 (size_group, label); + gtk_widget_show (label); + + combo = gimp_enum_combo_box_new (GEGL_TYPE_DITHER_METHOD); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->channel_dither_method, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->channel_dither_method); + + g_object_unref (size_group); + } + + return dialog; +} + + +/* private functions */ + +static void +convert_precision_dialog_free (ConvertDialog *private) +{ + g_slice_free (ConvertDialog, private); +} + +static void +convert_precision_dialog_response (GtkWidget *dialog, + gint response_id, + ConvertDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GimpPrecision precision = gimp_babl_precision (private->component_type, + private->linear); + + private->callback (dialog, + private->image, + precision, + private->layer_dither_method, + private->text_layer_dither_method, + private->channel_dither_method, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/convert-precision-dialog.h b/app/dialogs/convert-precision-dialog.h new file mode 100644 index 0000000..7d4ebcd --- /dev/null +++ b/app/dialogs/convert-precision-dialog.h @@ -0,0 +1,50 @@ +/* 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 . + */ + +#ifndef __CONVERT_PRECISION_DIALOG_H__ +#define __CONVERT_PRECISION_DIALOG_H__ + + +/* Don't offer dithering when converting down to more than this + * number of bits per component. Note that gegl:dither would + * do 16 bit, so this is a limitation of the GUI to values that make + * sense. See bug #735895. + */ +#define CONVERT_PRECISION_DIALOG_MAX_DITHER_BITS 8 + + +typedef void (* GimpConvertPrecisionCallback) (GtkWidget *dialog, + GimpImage *image, + GimpPrecision precision, + GeglDitherMethod layer_dither_method, + GeglDitherMethod text_layer_dither_method, + GeglDitherMethod channel_dither_method, + gpointer user_data); + + +GtkWidget * convert_precision_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpComponentType component_type, + GeglDitherMethod layer_dither_method, + GeglDitherMethod text_layer_dither_method, + GeglDitherMethod channel_dither_method, + GimpConvertPrecisionCallback callback, + gpointer user_data); + + +#endif /* __CONVERT_PRECISION_DIALOG_H__ */ diff --git a/app/dialogs/data-delete-dialog.c b/app/dialogs/data-delete-dialog.c new file mode 100644 index 0000000..77b73a8 --- /dev/null +++ b/app/dialogs/data-delete-dialog.c @@ -0,0 +1,159 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpdata.h" +#include "core/gimpdatafactory.h" + +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" + +#include "data-delete-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _DataDeleteDialog DataDeleteDialog; + +struct _DataDeleteDialog +{ + GimpDataFactory *factory; + GimpData *data; + GimpContext *context; + GtkWidget *parent; +}; + + +/* local function prototypes */ + +static void data_delete_dialog_response (GtkWidget *dialog, + gint response_id, + DataDeleteDialog *private); + + +/* public functions */ + +GtkWidget * +data_delete_dialog_new (GimpDataFactory *factory, + GimpData *data, + GimpContext *context, + GtkWidget *parent) +{ + DataDeleteDialog *private; + GtkWidget *dialog; + + g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL); + g_return_val_if_fail (GIMP_IS_DATA (data), NULL); + g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + + private = g_slice_new0 (DataDeleteDialog); + + private->factory = factory; + private->data = data; + private->context = context; + private->parent = parent; + + dialog = gimp_message_dialog_new (_("Delete Object"), "edit-delete", + gtk_widget_get_toplevel (parent), 0, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Delete"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (data, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + g_signal_connect (dialog, "response", + G_CALLBACK (data_delete_dialog_response), + private); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Delete '%s'?"), + gimp_object_get_name (data)); + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Are you sure you want to remove '%s' " + "from the list and delete it on disk?"), + gimp_object_get_name (data)); + + return dialog; +} + + +/* private functions */ + +static void +data_delete_dialog_response (GtkWidget *dialog, + gint response_id, + DataDeleteDialog *private) +{ + gtk_widget_destroy (dialog); + + if (response_id == GTK_RESPONSE_OK) + { + GimpDataFactory *factory = private->factory; + GimpData *data = private->data; + GimpContainer *container; + GimpObject *new_active = NULL; + GError *error = NULL; + + container = gimp_data_factory_get_container (factory); + + if (private->context && + GIMP_OBJECT (data) == + gimp_context_get_by_type (private->context, + gimp_container_get_children_type (container))) + { + new_active = gimp_container_get_neighbor_of (container, + GIMP_OBJECT (data)); + } + + if (! gimp_data_factory_data_delete (factory, data, TRUE, &error)) + { + gimp_message (gimp_data_factory_get_gimp (factory), + G_OBJECT (private->parent), GIMP_MESSAGE_ERROR, + "%s", error->message); + g_clear_error (&error); + } + + if (new_active) + gimp_context_set_by_type (private->context, + gimp_container_get_children_type (container), + new_active); + } + + g_slice_free (DataDeleteDialog, private); +} diff --git a/app/dialogs/data-delete-dialog.h b/app/dialogs/data-delete-dialog.h new file mode 100644 index 0000000..1b41a96 --- /dev/null +++ b/app/dialogs/data-delete-dialog.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 . + */ + +#ifndef __DATA_DELETE_DIALOG_H__ +#define __DATA_DELETE_DIALOG_H__ + + +GtkWidget * data_delete_dialog_new (GimpDataFactory *factory, + GimpData *data, + GimpContext *context, + GtkWidget *parent); + + +#endif /* __DATA_DELETE_DIALOG_H__ */ diff --git a/app/dialogs/dialogs-constructors.c b/app/dialogs/dialogs-constructors.c new file mode 100644 index 0000000..f80a3e8 --- /dev/null +++ b/app/dialogs/dialogs-constructors.c @@ -0,0 +1,894 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "config/gimpguiconfig.h" + +#include "widgets/gimpbrusheditor.h" +#include "widgets/gimpbrushfactoryview.h" +#include "widgets/gimpbufferview.h" +#include "widgets/gimpchanneltreeview.h" +#include "widgets/gimpcoloreditor.h" +#include "widgets/gimpcolormapeditor.h" +#include "widgets/gimpcriticaldialog.h" +#include "widgets/gimpdashboard.h" +#include "widgets/gimpdevicestatus.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimpdocumentview.h" +#include "widgets/gimpdynamicseditor.h" +#include "widgets/gimpdynamicsfactoryview.h" +#include "widgets/gimperrorconsole.h" +#include "widgets/gimperrordialog.h" +#include "widgets/gimpfontfactoryview.h" +#include "widgets/gimpgradienteditor.h" +#include "widgets/gimphistogrameditor.h" +#include "widgets/gimpimageview.h" +#include "widgets/gimplayertreeview.h" +#include "widgets/gimpmenudock.h" +#include "widgets/gimppaletteeditor.h" +#include "widgets/gimppatternfactoryview.h" +#include "widgets/gimpsamplepointeditor.h" +#include "widgets/gimpselectioneditor.h" +#include "widgets/gimpsymmetryeditor.h" +#include "widgets/gimptemplateview.h" +#include "widgets/gimptoolbox.h" +#include "widgets/gimptooloptionseditor.h" +#include "widgets/gimptoolpresetfactoryview.h" +#include "widgets/gimptoolpreseteditor.h" +#include "widgets/gimpundoeditor.h" +#include "widgets/gimpvectorstreeview.h" + +#include "display/gimpcursorview.h" +#include "display/gimpnavigationeditor.h" + +#include "about-dialog.h" +#include "action-search-dialog.h" +#include "dialogs.h" +#include "dialogs-constructors.h" +#include "file-open-dialog.h" +#include "file-open-location-dialog.h" +#include "file-save-dialog.h" +#include "image-new-dialog.h" +#include "input-devices-dialog.h" +#include "keyboard-shortcuts-dialog.h" +#include "module-dialog.h" +#include "palette-import-dialog.h" +#include "preferences-dialog.h" +#include "quit-dialog.h" +#include "tips-dialog.h" + +#include "gimp-intl.h" + + +/**********************/ +/* toplevel dialogs */ +/**********************/ + +GtkWidget * +dialogs_image_new_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return image_new_dialog_new (context); +} + +GtkWidget * +dialogs_file_open_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return file_open_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_file_open_location_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return file_open_location_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_file_save_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return file_save_dialog_new (context->gimp, FALSE); +} + +GtkWidget * +dialogs_file_export_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return file_save_dialog_new (context->gimp, TRUE); +} + +GtkWidget * +dialogs_preferences_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return preferences_dialog_create (context->gimp); +} + +GtkWidget * +dialogs_keyboard_shortcuts_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return keyboard_shortcuts_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_input_devices_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return input_devices_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_module_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return module_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_palette_import_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return palette_import_dialog_new (context); +} + +GtkWidget * +dialogs_tips_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return tips_dialog_create (context->gimp); +} + +GtkWidget * +dialogs_about_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return about_dialog_create (context->gimp->edit_config); +} + +GtkWidget * +dialogs_action_search_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return action_search_dialog_create (context->gimp); +} + +GtkWidget * +dialogs_error_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_error_dialog_new (_("GIMP Message")); +} + +GtkWidget * +dialogs_critical_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_critical_dialog_new (_("GIMP Debug"), + context->gimp->config->last_known_release, + context->gimp->config->last_release_timestamp); +} + +GtkWidget * +dialogs_close_all_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return close_all_dialog_new (context->gimp); +} + +GtkWidget * +dialogs_quit_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return quit_dialog_new (context->gimp); +} + + +/***********/ +/* docks */ +/***********/ + +GtkWidget * +dialogs_toolbox_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_toolbox_new (factory, + context, + ui_manager); +} + +GtkWidget * +dialogs_toolbox_dock_window_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + static gint role_serial = 1; + GtkWidget *dock; + gchar *role; + + role = g_strdup_printf ("gimp-toolbox-%d", role_serial++); + dock = gimp_dock_window_new (role, + "", + TRUE, + factory, + context); + g_free (role); + + return dock; +} + +GtkWidget * +dialogs_dock_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_menu_dock_new (); +} + +GtkWidget * +dialogs_dock_window_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + static gint role_serial = 1; + GtkWidget *dock; + gchar *role; + + role = g_strdup_printf ("gimp-dock-%d", role_serial++); + dock = gimp_dock_window_new (role, + "", + FALSE, + factory, + context); + g_free (role); + + return dock; +} + + +/***************/ +/* dockables */ +/***************/ + +/***** singleton dialogs *****/ + +GtkWidget * +dialogs_tool_options_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_tool_options_editor_new (context->gimp, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_device_status_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_device_status_new (context->gimp); +} + +GtkWidget * +dialogs_error_console_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_error_console_new (context->gimp, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_cursor_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_cursor_view_new (gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_dashboard_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_dashboard_new (context->gimp, + gimp_dialog_factory_get_menu_factory (factory)); +} + + +/***** list views *****/ + +GtkWidget * +dialogs_image_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_image_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->images, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_brush_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_brush_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->brush_factory, + context, + TRUE, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_dynamics_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_dynamics_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->dynamics_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_mypaint_brush_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->mybrush_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/mypaint-brushes-popup", + "mypaint-brushes"); +} + +GtkWidget * +dialogs_pattern_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_pattern_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->pattern_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_gradient_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->gradient_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/gradients-popup", + "gradients"); +} + +GtkWidget * +dialogs_palette_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->palette_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/palettes-popup", + "palettes"); +} + +GtkWidget * +dialogs_font_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_font_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->font_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_buffer_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_buffer_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->named_buffers, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_tool_preset_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_tool_preset_factory_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->tool_preset_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_document_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_document_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->documents, + context, + view_size, 0, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_template_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_template_view_new (GIMP_VIEW_TYPE_LIST, + context->gimp->templates, + context, + view_size, 0, + gimp_dialog_factory_get_menu_factory (factory)); +} + + +/***** grid views *****/ + +GtkWidget * +dialogs_image_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_image_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->images, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_brush_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_brush_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->brush_factory, + context, + TRUE, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_dynamics_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_dynamics_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->dynamics_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_mypaint_brush_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->mybrush_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/mypaint-brushes-popup", + "mypaint-brushes"); +} + +GtkWidget * +dialogs_pattern_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_pattern_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->pattern_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_gradient_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->gradient_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/gradients-popup", + "gradients"); +} + +GtkWidget * +dialogs_palette_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_data_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->palette_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory), + "", + "/palettes-popup", + "palettes"); +} + +GtkWidget * +dialogs_font_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_font_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->font_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_buffer_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_buffer_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->named_buffers, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_tool_preset_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_tool_preset_factory_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->tool_preset_factory, + context, + view_size, 1, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_document_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_document_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->documents, + context, + view_size, 0, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_template_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_template_view_new (GIMP_VIEW_TYPE_GRID, + context->gimp->templates, + context, + view_size, 0, + gimp_dialog_factory_get_menu_factory (factory)); +} + + +/***** image related dialogs *****/ + +GtkWidget * +dialogs_layer_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + if (view_size < 1) + view_size = context->gimp->config->layer_preview_size; + + return gimp_item_tree_view_new (GIMP_TYPE_LAYER_TREE_VIEW, + view_size, 2, + gimp_context_get_image (context), + gimp_dialog_factory_get_menu_factory (factory), + "", + "/layers-popup"); +} + +GtkWidget * +dialogs_channel_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + if (view_size < 1) + view_size = context->gimp->config->layer_preview_size; + + return gimp_item_tree_view_new (GIMP_TYPE_CHANNEL_TREE_VIEW, + view_size, 1, + gimp_context_get_image (context), + gimp_dialog_factory_get_menu_factory (factory), + "", + "/channels-popup"); +} + +GtkWidget * +dialogs_vectors_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + if (view_size < 1) + view_size = context->gimp->config->layer_preview_size; + + return gimp_item_tree_view_new (GIMP_TYPE_VECTORS_TREE_VIEW, + view_size, 1, + gimp_context_get_image (context), + gimp_dialog_factory_get_menu_factory (factory), + "", + "/vectors-popup"); +} + +GtkWidget * +dialogs_colormap_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_colormap_editor_new (gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_histogram_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_histogram_editor_new (); +} + +GtkWidget * +dialogs_selection_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_selection_editor_new (gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_symmetry_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_symmetry_editor_new (gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_undo_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_undo_editor_new (context->gimp->config, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_sample_point_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_sample_point_editor_new (gimp_dialog_factory_get_menu_factory (factory)); +} + + +/***** display related dialogs *****/ + +GtkWidget * +dialogs_navigation_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_navigation_editor_new (gimp_dialog_factory_get_menu_factory (factory)); +} + + +/***** misc dockables *****/ + +GtkWidget * +dialogs_color_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_color_editor_new (context); +} + + +/*************/ +/* editors */ +/*************/ + +GtkWidget * +dialogs_brush_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_brush_editor_new (context, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_dynamics_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_dynamics_editor_new (context, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_gradient_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_gradient_editor_new (context, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_palette_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_palette_editor_new (context, + gimp_dialog_factory_get_menu_factory (factory)); +} + +GtkWidget * +dialogs_tool_preset_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size) +{ + return gimp_tool_preset_editor_new (context, + gimp_dialog_factory_get_menu_factory (factory)); +} diff --git a/app/dialogs/dialogs-constructors.h b/app/dialogs/dialogs-constructors.h new file mode 100644 index 0000000..bbe4159 --- /dev/null +++ b/app/dialogs/dialogs-constructors.h @@ -0,0 +1,308 @@ +/* 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 . + */ + +#ifndef __DIALOGS_CONSTRUCTORS_H__ +#define __DIALOGS_CONSTRUCTORS_H__ + + +/* toplevel dialogs */ + +GtkWidget * dialogs_image_new_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_file_open_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_file_open_location_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_file_save_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_file_export_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_preferences_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_input_devices_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_keyboard_shortcuts_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_module_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_palette_import_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_tips_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_about_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_action_search_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_error_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_critical_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_close_all_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_quit_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + + +/* docks */ + +GtkWidget * dialogs_toolbox_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_toolbox_dock_window_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dock_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dock_window_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + + +/* dockables */ + +GtkWidget * dialogs_tool_options_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_device_status_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_error_console_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_cursor_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dashboard_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_image_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_brush_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dynamics_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_mypaint_brush_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_pattern_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_gradient_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_palette_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_font_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_buffer_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_tool_preset_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_document_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_template_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_image_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_brush_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dynamics_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_mypaint_brush_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_pattern_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_gradient_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_palette_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_font_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_buffer_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_tool_preset_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_document_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_template_grid_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_layer_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_channel_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_vectors_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_path_list_view_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_colormap_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_histogram_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_selection_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_symmetry_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_undo_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_sample_point_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_navigation_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_color_editor_new (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + +GtkWidget * dialogs_brush_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_dynamics_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_gradient_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_palette_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); +GtkWidget * dialogs_tool_preset_editor_get (GimpDialogFactory *factory, + GimpContext *context, + GimpUIManager *ui_manager, + gint view_size); + + +#endif /* __DIALOGS_CONSTRUCTORS_H__ */ diff --git a/app/dialogs/dialogs-types.h b/app/dialogs/dialogs-types.h new file mode 100644 index 0000000..afdd025 --- /dev/null +++ b/app/dialogs/dialogs-types.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __DIALOGS_TYPES_H__ +#define __DIALOGS_TYPES_H__ + + +#include "display/display-types.h" + + +typedef void (* GimpScaleCallback) (GtkWidget *dialog, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data); + + +#endif /* __DIALOGS_TYPES_H__ */ diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c new file mode 100644 index 0000000..064f2c4 --- /dev/null +++ b/app/dialogs/dialogs.c @@ -0,0 +1,749 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * dialogs.c + * Copyright (C) 2010 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpguiconfig.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimplist.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpsessioninfo-aux.h" +#include "widgets/gimpsessionmanaged.h" +#include "widgets/gimptoolbox.h" + +#include "dialogs.h" +#include "dialogs-constructors.h" + +#include "gimp-log.h" + +#include "gimp-intl.h" + + +GimpContainer *global_recent_docks = NULL; + + +#define FOREIGN(id, singleton, remember_size) \ + { id /* identifier */, \ + NULL /* name */, \ + NULL /* blurb */, \ + NULL /* icon_name */, \ + NULL /* help_id */, \ + NULL /* new_func */, \ + dialogs_restore_dialog /* restore_func */, \ + 0 /* view_size */, \ + singleton /* singleton */, \ + TRUE /* session_managed */, \ + remember_size /* remember_size */, \ + FALSE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + FALSE /* dockable */} + +#define IMAGE_WINDOW(id, singleton, remember_size) \ + { id /* identifier */, \ + NULL /* name */, \ + NULL /* blurb */, \ + NULL /* icon_name */, \ + NULL /* help_id */, \ + NULL /* new_func */, \ + dialogs_restore_window /* restore_func */, \ + 0 /* view_size */, \ + singleton /* singleton */, \ + TRUE /* session_managed */, \ + remember_size /* remember_size */, \ + TRUE /* remember_if_open */, \ + FALSE /* hideable */, \ + TRUE /* image_window */, \ + FALSE /* dockable */} + +#define TOPLEVEL(id, new_func, singleton, session_managed, remember_size) \ + { id /* identifier */, \ + NULL /* name */, \ + NULL /* blurb */, \ + NULL /* icon_name */, \ + NULL /* help_id */, \ + new_func /* new_func */, \ + dialogs_restore_dialog /* restore_func */, \ + 0 /* view_size */, \ + singleton /* singleton */, \ + session_managed /* session_managed */, \ + remember_size /* remember_size */, \ + FALSE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + FALSE /* dockable */} + +#define DOCKABLE(id, name, blurb, icon_name, help_id, new_func, view_size, singleton) \ + { id /* identifier */, \ + name /* name */, \ + blurb /* blurb */, \ + icon_name /* icon_name */, \ + help_id /* help_id */, \ + new_func /* new_func */, \ + NULL /* restore_func */, \ + view_size /* view_size */, \ + singleton /* singleton */, \ + FALSE /* session_managed */, \ + FALSE /* remember_size */, \ + TRUE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + TRUE /* dockable */} + +#define DOCK(id, new_func) \ + { id /* identifier */, \ + NULL /* name */, \ + NULL /* blurb */, \ + NULL /* icon_name */, \ + NULL /* help_id */, \ + new_func /* new_func */, \ + dialogs_restore_dialog /* restore_func */, \ + 0 /* view_size */, \ + FALSE /* singleton */, \ + FALSE /* session_managed */, \ + FALSE /* remember_size */, \ + FALSE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + FALSE /* dockable */} + +#define DOCK_WINDOW(id, new_func) \ + { id /* identifier */, \ + NULL /* name */, \ + NULL /* blurb */, \ + NULL /* icon_name */, \ + NULL /* help_id */, \ + new_func /* new_func */, \ + dialogs_restore_dialog /* restore_func */, \ + 0 /* view_size */, \ + FALSE /* singleton */, \ + TRUE /* session_managed */, \ + TRUE /* remember_size */, \ + TRUE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + FALSE /* dockable */} + +#define LISTGRID(id, new_func, name, blurb, icon_name, help_id, view_size) \ + { "gimp-"#id"-list" /* identifier */, \ + name /* name */, \ + blurb /* blurb */, \ + icon_name /* icon_name */, \ + help_id /* help_id */, \ + dialogs_##new_func##_list_view_new /* new_func */, \ + NULL /* restore_func */, \ + view_size /* view_size */, \ + FALSE /* singleton */, \ + FALSE /* session_managed */, \ + FALSE /* remember_size */, \ + TRUE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + TRUE /* dockable */}, \ + { "gimp-"#id"-grid" /* identifier */, \ + name /* name */, \ + blurb /* blurb */, \ + icon_name /* icon_name */, \ + help_id /* help_id */, \ + dialogs_##new_func##_grid_view_new /* new_func */, \ + NULL /* restore_func */, \ + view_size /* view_size */, \ + FALSE /* singleton */, \ + FALSE /* session_managed */, \ + FALSE /* remember_size */, \ + TRUE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + TRUE /* dockable */} + +#define LIST(id, new_func, name, blurb, icon_name, help_id, view_size) \ + { "gimp-"#id"-list" /* identifier */, \ + name /* name */, \ + blurb /* blurb */, \ + icon_name /* icon_name */, \ + help_id /* help_id */, \ + dialogs_##new_func##_list_view_new /* new_func */, \ + NULL /* restore_func */, \ + view_size /* view_size */, \ + FALSE /* singleton */, \ + FALSE /* session_managed */, \ + FALSE /* remember_size */, \ + TRUE /* remember_if_open */, \ + TRUE /* hideable */, \ + FALSE /* image_window */, \ + TRUE /* dockable */} + + +static GtkWidget * dialogs_restore_dialog (GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + GimpSessionInfo *info); +static GtkWidget * dialogs_restore_window (GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + GimpSessionInfo *info); + + +static const GimpDialogFactoryEntry entries[] = +{ + /* foreign toplevels without constructor */ + FOREIGN ("gimp-brightness-contrast-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-color-balance-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-color-picker-tool-dialog", TRUE, TRUE), + FOREIGN ("gimp-colorize-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-crop-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-curves-tool-dialog", TRUE, TRUE), + FOREIGN ("gimp-desaturate-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-foreground-select-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-gegl-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-gradient-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-hue-saturation-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-levels-tool-dialog", TRUE, TRUE), + FOREIGN ("gimp-measure-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-offset-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-operation-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-posterize-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-rotate-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-scale-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-shear-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-text-tool-dialog", TRUE, TRUE), + FOREIGN ("gimp-threshold-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-transform-3d-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-perspective-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-unified-transform-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-handle-transform-tool-dialog", TRUE, FALSE), + + FOREIGN ("gimp-toolbox-color-dialog", TRUE, FALSE), + FOREIGN ("gimp-gradient-editor-color-dialog", TRUE, FALSE), + FOREIGN ("gimp-palette-editor-color-dialog", TRUE, FALSE), + FOREIGN ("gimp-colormap-editor-color-dialog", TRUE, FALSE), + + FOREIGN ("gimp-controller-editor-dialog", FALSE, TRUE), + FOREIGN ("gimp-controller-action-dialog", FALSE, TRUE), + + /* ordinary toplevels */ + TOPLEVEL ("gimp-image-new-dialog", + dialogs_image_new_new, FALSE, TRUE, FALSE), + TOPLEVEL ("gimp-file-open-dialog", + dialogs_file_open_new, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-file-open-location-dialog", + dialogs_file_open_location_new, FALSE, TRUE, FALSE), + TOPLEVEL ("gimp-file-save-dialog", + dialogs_file_save_new, FALSE, TRUE, TRUE), + TOPLEVEL ("gimp-file-export-dialog", + dialogs_file_export_new, FALSE, TRUE, TRUE), + + /* singleton toplevels */ + TOPLEVEL ("gimp-preferences-dialog", + dialogs_preferences_get, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-input-devices-dialog", + dialogs_input_devices_get, TRUE, TRUE, FALSE), + TOPLEVEL ("gimp-keyboard-shortcuts-dialog", + dialogs_keyboard_shortcuts_get, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-module-dialog", + dialogs_module_get, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-palette-import-dialog", + dialogs_palette_import_get, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-tips-dialog", + dialogs_tips_get, TRUE, FALSE, FALSE), + TOPLEVEL ("gimp-about-dialog", + dialogs_about_get, TRUE, FALSE, FALSE), + TOPLEVEL ("gimp-action-search-dialog", + dialogs_action_search_get, TRUE, TRUE, TRUE), + TOPLEVEL ("gimp-error-dialog", + dialogs_error_get, TRUE, FALSE, FALSE), + TOPLEVEL ("gimp-critical-dialog", + dialogs_critical_get, TRUE, FALSE, FALSE), + TOPLEVEL ("gimp-close-all-dialog", + dialogs_close_all_get, TRUE, FALSE, FALSE), + TOPLEVEL ("gimp-quit-dialog", + dialogs_quit_get, TRUE, FALSE, FALSE), + + /* docks */ + DOCK ("gimp-dock", + dialogs_dock_new), + DOCK ("gimp-toolbox", + dialogs_toolbox_new), + + /* dock windows */ + DOCK_WINDOW ("gimp-dock-window", + dialogs_dock_window_new), + DOCK_WINDOW ("gimp-toolbox-window", + dialogs_toolbox_dock_window_new), + + /* singleton dockables */ + DOCKABLE ("gimp-tool-options", + N_("Tool Options"), NULL, GIMP_ICON_DIALOG_TOOL_OPTIONS, + GIMP_HELP_TOOL_OPTIONS_DIALOG, + dialogs_tool_options_new, 0, TRUE), + DOCKABLE ("gimp-device-status", + N_("Devices"), N_("Device Status"), GIMP_ICON_DIALOG_DEVICE_STATUS, + GIMP_HELP_DEVICE_STATUS_DIALOG, + dialogs_device_status_new, 0, TRUE), + DOCKABLE ("gimp-error-console", + N_("Errors"), N_("Error Console"), GIMP_ICON_DIALOG_WARNING, + GIMP_HELP_ERRORS_DIALOG, + dialogs_error_console_new, 0, TRUE), + DOCKABLE ("gimp-cursor-view", + N_("Pointer"), N_("Pointer Information"), GIMP_ICON_CURSOR, + GIMP_HELP_POINTER_INFO_DIALOG, + dialogs_cursor_view_new, 0, TRUE), + DOCKABLE ("gimp-dashboard", + N_("Dashboard"), N_("Dashboard"), GIMP_ICON_DIALOG_DASHBOARD, + GIMP_HELP_DASHBOARD_DIALOG, + dialogs_dashboard_new, 0, TRUE), + + /* list & grid views */ + LISTGRID (image, image, + N_("Images"), NULL, GIMP_ICON_DIALOG_IMAGES, + GIMP_HELP_IMAGE_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (brush, brush, + N_("Brushes"), NULL, GIMP_ICON_BRUSH, + GIMP_HELP_BRUSH_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (dynamics, dynamics, + N_("Paint Dynamics"), NULL, GIMP_ICON_DYNAMICS, + GIMP_HELP_DYNAMICS_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (mypaint-brush, mypaint_brush, + N_("MyPaint Brushes"), NULL, GIMP_ICON_MYPAINT_BRUSH, + GIMP_HELP_MYPAINT_BRUSH_DIALOG, GIMP_VIEW_SIZE_LARGE), + LISTGRID (pattern, pattern, + N_("Patterns"), NULL, GIMP_ICON_PATTERN, + GIMP_HELP_PATTERN_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (gradient, gradient, + N_("Gradients"), NULL, GIMP_ICON_GRADIENT, + GIMP_HELP_GRADIENT_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (palette, palette, + N_("Palettes"), NULL, GIMP_ICON_PALETTE, + GIMP_HELP_PALETTE_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (font, font, + N_("Fonts"), NULL, GIMP_ICON_FONT, + GIMP_HELP_FONT_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (buffer, buffer, + N_("Buffers"), NULL, GIMP_ICON_BUFFER, + GIMP_HELP_BUFFER_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (tool-preset, tool_preset, + N_("Tool Presets"), NULL, GIMP_ICON_TOOL_PRESET, + GIMP_HELP_TOOL_PRESET_DIALOG, GIMP_VIEW_SIZE_MEDIUM), + LISTGRID (document, document, + N_("History"), N_("Document History"), GIMP_ICON_DOCUMENT_OPEN_RECENT, + GIMP_HELP_DOCUMENT_DIALOG, GIMP_VIEW_SIZE_LARGE), + LISTGRID (template, template, + N_("Templates"), N_("Image Templates"), GIMP_ICON_TEMPLATE, + GIMP_HELP_TEMPLATE_DIALOG, GIMP_VIEW_SIZE_SMALL), + + /* image related */ + DOCKABLE ("gimp-layer-list", + N_("Layers"), NULL, GIMP_ICON_DIALOG_LAYERS, + GIMP_HELP_LAYER_DIALOG, + dialogs_layer_list_view_new, 0, FALSE), + DOCKABLE ("gimp-channel-list", + N_("Channels"), NULL, GIMP_ICON_DIALOG_CHANNELS, + GIMP_HELP_CHANNEL_DIALOG, + dialogs_channel_list_view_new, 0, FALSE), + DOCKABLE ("gimp-vectors-list", + N_("Paths"), NULL, GIMP_ICON_DIALOG_PATHS, + GIMP_HELP_PATH_DIALOG, + dialogs_vectors_list_view_new, 0, FALSE), + DOCKABLE ("gimp-indexed-palette", + N_("Colormap"), NULL, GIMP_ICON_COLORMAP, + GIMP_HELP_INDEXED_PALETTE_DIALOG, + dialogs_colormap_editor_new, 0, FALSE), + DOCKABLE ("gimp-histogram-editor", + N_("Histogram"), NULL, GIMP_ICON_HISTOGRAM, + GIMP_HELP_HISTOGRAM_DIALOG, + dialogs_histogram_editor_new, 0, FALSE), + DOCKABLE ("gimp-selection-editor", + N_("Selection"), N_("Selection Editor"), GIMP_ICON_SELECTION, + GIMP_HELP_SELECTION_DIALOG, + dialogs_selection_editor_new, 0, FALSE), + DOCKABLE ("gimp-symmetry-editor", + N_("Symmetry Painting"), NULL, GIMP_ICON_SYMMETRY, + GIMP_HELP_SYMMETRY_DIALOG, + dialogs_symmetry_editor_new, 0, FALSE), + DOCKABLE ("gimp-undo-history", + N_("Undo"), N_("Undo History"), GIMP_ICON_DIALOG_UNDO_HISTORY, + GIMP_HELP_UNDO_DIALOG, + dialogs_undo_editor_new, 0, FALSE), + DOCKABLE ("gimp-sample-point-editor", + N_("Sample Points"), N_("Sample Points"), GIMP_ICON_SAMPLE_POINT, + GIMP_HELP_SAMPLE_POINT_DIALOG, + dialogs_sample_point_editor_new, 0, FALSE), + + /* display related */ + DOCKABLE ("gimp-navigation-view", + N_("Navigation"), N_("Display Navigation"), GIMP_ICON_DIALOG_NAVIGATION, + GIMP_HELP_NAVIGATION_DIALOG, + dialogs_navigation_editor_new, 0, FALSE), + + /* editors */ + DOCKABLE ("gimp-color-editor", + N_("FG/BG"), N_("FG/BG Color"), GIMP_ICON_COLORS_DEFAULT, + GIMP_HELP_COLOR_DIALOG, + dialogs_color_editor_new, 0, FALSE), + + /* singleton editors */ + DOCKABLE ("gimp-brush-editor", + N_("Brush Editor"), NULL, GIMP_ICON_BRUSH, + GIMP_HELP_BRUSH_EDITOR_DIALOG, + dialogs_brush_editor_get, 0, TRUE), + DOCKABLE ("gimp-dynamics-editor", + N_("Paint Dynamics Editor"), NULL, GIMP_ICON_DYNAMICS, + GIMP_HELP_DYNAMICS_EDITOR_DIALOG, + dialogs_dynamics_editor_get, 0, TRUE), + DOCKABLE ("gimp-gradient-editor", + N_("Gradient Editor"), NULL, GIMP_ICON_GRADIENT, + GIMP_HELP_GRADIENT_EDITOR_DIALOG, + dialogs_gradient_editor_get, 0, TRUE), + DOCKABLE ("gimp-palette-editor", + N_("Palette Editor"), NULL, GIMP_ICON_PALETTE, + GIMP_HELP_PALETTE_EDITOR_DIALOG, + dialogs_palette_editor_get, 0, TRUE), + DOCKABLE ("gimp-tool-preset-editor", + N_("Tool Preset Editor"), NULL, GIMP_ICON_TOOL_PRESET, + GIMP_HELP_TOOL_PRESET_EDITOR_DIALOG, + dialogs_tool_preset_editor_get, 0, TRUE), + + /* image windows */ + IMAGE_WINDOW ("gimp-empty-image-window", + TRUE, TRUE), + IMAGE_WINDOW ("gimp-single-image-window", + TRUE, TRUE) +}; + +/** + * dialogs_restore_dialog: + * @factory: + * @screen: + * @monitor: + * @info: + * + * Creates a top level widget based on the given session info object + * in which other widgets later can be be put, typically also restored + * from the same session info object. + * + * Returns: + **/ +static GtkWidget * +dialogs_restore_dialog (GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + GimpSessionInfo *info) +{ + GtkWidget *dialog; + GimpCoreConfig *config = gimp_dialog_factory_get_context (factory)->gimp->config; + + GIMP_LOG (DIALOG_FACTORY, "restoring toplevel \"%s\" (info %p)", + gimp_session_info_get_factory_entry (info)->identifier, + info); + + dialog = + gimp_dialog_factory_dialog_new (factory, screen, monitor, + NULL /*ui_manager*/, + gimp_session_info_get_factory_entry (info)->identifier, + gimp_session_info_get_factory_entry (info)->view_size, + ! GIMP_GUI_CONFIG (config)->hide_docks); + + g_object_set_data (G_OBJECT (dialog), GIMP_DIALOG_VISIBILITY_KEY, + GINT_TO_POINTER (GIMP_GUI_CONFIG (config)->hide_docks ? + GIMP_DIALOG_VISIBILITY_HIDDEN : + GIMP_DIALOG_VISIBILITY_VISIBLE)); + + return dialog; +} + +/** + * dialogs_restore_window: + * @factory: + * @screen: + * @monitor: + * @info: + * + * "restores" the image window. We don't really restore anything since + * the image window is created earlier, so we just look for and return + * the already-created image window. + * + * Returns: + **/ +static GtkWidget * +dialogs_restore_window (GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + GimpSessionInfo *info) +{ + Gimp *gimp = gimp_dialog_factory_get_context (factory)->gimp; + GimpDisplay *display = GIMP_DISPLAY (gimp_get_empty_display (gimp)); + GimpDisplayShell *shell = gimp_display_get_shell (display); + GtkWidget *dialog; + + dialog = GTK_WIDGET (gimp_display_shell_get_window (shell)); + + return dialog; +} + + +/* public functions */ + +void +dialogs_init (Gimp *gimp, + GimpMenuFactory *menu_factory) +{ + GimpDialogFactory *factory = NULL; + gint i = 0; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_MENU_FACTORY (menu_factory)); + + factory = gimp_dialog_factory_new ("toplevel", + gimp_get_user_context (gimp), + menu_factory); + gimp_dialog_factory_set_singleton (factory); + + for (i = 0; i < G_N_ELEMENTS (entries); i++) + gimp_dialog_factory_register_entry (factory, + entries[i].identifier, + gettext (entries[i].name), + gettext (entries[i].blurb), + entries[i].icon_name, + entries[i].help_id, + entries[i].new_func, + entries[i].restore_func, + entries[i].view_size, + entries[i].singleton, + entries[i].session_managed, + entries[i].remember_size, + entries[i].remember_if_open, + entries[i].hideable, + entries[i].image_window, + entries[i].dockable); + + global_recent_docks = gimp_list_new (GIMP_TYPE_SESSION_INFO, FALSE); +} + +void +dialogs_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp_dialog_factory_get_singleton ()) + { + /* run dispose manually so the factory destroys its dialogs, which + * might in turn directly or indirectly ref the factory + */ + g_object_run_dispose (G_OBJECT (gimp_dialog_factory_get_singleton ())); + + g_object_unref (gimp_dialog_factory_get_singleton ()); + gimp_dialog_factory_set_singleton (NULL); + } + + g_clear_object (&global_recent_docks); +} + +static void +dialogs_ensure_factory_entry_on_recent_dock (GimpSessionInfo *info) +{ + if (! gimp_session_info_get_factory_entry (info)) + { + GimpDialogFactoryEntry *entry = NULL; + + /* The recent docks container only contains session infos for + * dock windows + */ + entry = gimp_dialog_factory_find_entry (gimp_dialog_factory_get_singleton (), + "gimp-dock-window"); + + gimp_session_info_set_factory_entry (info, entry); + } +} + +static GFile * +dialogs_get_dockrc_file (void) +{ + const gchar *basename; + + basename = g_getenv ("GIMP_TESTING_DOCKRC_NAME"); + if (! basename) + basename = "dockrc"; + + return gimp_directory_file (basename, NULL); +} + +void +dialogs_load_recent_docks (Gimp *gimp) +{ + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = dialogs_get_dockrc_file (); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (global_recent_docks), + file, + NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + + g_clear_error (&error); + } + + g_object_unref (file); + + /* In GIMP 2.6 dockrc did not contain the factory entries for the + * session infos, so set that up manually if needed + */ + gimp_container_foreach (global_recent_docks, + (GFunc) dialogs_ensure_factory_entry_on_recent_dock, + NULL); + + gimp_list_reverse (GIMP_LIST (global_recent_docks)); +} + +void +dialogs_save_recent_docks (Gimp *gimp) +{ + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = dialogs_get_dockrc_file (); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (global_recent_docks), + file, + "recently closed docks", + "end of recently closed docks", + NULL, &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } + + g_object_unref (file); +} + +GtkWidget * +dialogs_get_toolbox (void) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (gimp_dialog_factory_get_singleton ()), NULL); + + for (list = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ()); + list; + list = g_list_next (list)) + { + if (GIMP_IS_DOCK_WINDOW (list->data) && + gimp_dock_window_has_toolbox (list->data)) + return list->data; + } + + return NULL; +} + +GtkWidget * +dialogs_get_dialog (GObject *attach_object, + const gchar *attach_key) +{ + g_return_val_if_fail (G_IS_OBJECT (attach_object), NULL); + g_return_val_if_fail (attach_key != NULL, NULL); + + return g_object_get_data (attach_object, attach_key); +} + +void +dialogs_attach_dialog (GObject *attach_object, + const gchar *attach_key, + GtkWidget *dialog) +{ + g_return_if_fail (G_IS_OBJECT (attach_object)); + g_return_if_fail (attach_key != NULL); + g_return_if_fail (GTK_IS_WIDGET (dialog)); + + g_object_set_data (attach_object, attach_key, dialog); + g_object_set_data (G_OBJECT (dialog), "gimp-dialogs-attach-key", + (gpointer) attach_key); + + g_signal_connect_object (dialog, "destroy", + G_CALLBACK (dialogs_detach_dialog), + attach_object, + G_CONNECT_SWAPPED); +} + +void +dialogs_detach_dialog (GObject *attach_object, + GtkWidget *dialog) +{ + const gchar *attach_key; + + g_return_if_fail (G_IS_OBJECT (attach_object)); + g_return_if_fail (GTK_IS_WIDGET (dialog)); + + attach_key = g_object_get_data (G_OBJECT (dialog), + "gimp-dialogs-attach-key"); + + g_return_if_fail (attach_key != NULL); + + g_object_set_data (attach_object, attach_key, NULL); + + g_signal_handlers_disconnect_by_func (dialog, + dialogs_detach_dialog, + attach_object); +} + +void +dialogs_destroy_dialog (GObject *attach_object, + const gchar *attach_key) +{ + GtkWidget *dialog; + + g_return_if_fail (G_IS_OBJECT (attach_object)); + g_return_if_fail (attach_key != NULL); + + dialog = g_object_get_data (attach_object, attach_key); + + if (dialog) + gtk_widget_destroy (dialog); +} diff --git a/app/dialogs/dialogs.h b/app/dialogs/dialogs.h new file mode 100644 index 0000000..387dfa2 --- /dev/null +++ b/app/dialogs/dialogs.h @@ -0,0 +1,50 @@ +/* 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 . + */ + +#ifndef __DIALOGS_H__ +#define __DIALOGS_H__ + + +extern GimpDialogFactory *global_dialog_factory; +extern GimpContainer *global_recent_docks; + + +void dialogs_init (Gimp *gimp, + GimpMenuFactory *menu_factory); +void dialogs_exit (Gimp *gimp); + +void dialogs_load_recent_docks (Gimp *gimp); +void dialogs_save_recent_docks (Gimp *gimp); + +GtkWidget * dialogs_get_toolbox (void); + + +/* attaching dialogs to arbitrary objects, and detaching them + * automatically upon destruction + */ +GtkWidget * dialogs_get_dialog (GObject *attach_object, + const gchar *attach_key); +void dialogs_attach_dialog (GObject *attach_object, + const gchar *attach_key, + GtkWidget *dialog); +void dialogs_detach_dialog (GObject *attach_object, + GtkWidget *dialog); +void dialogs_destroy_dialog (GObject *attach_object, + const gchar *attach_key); + + +#endif /* __DIALOGS_H__ */ diff --git a/app/dialogs/file-open-dialog.c b/app/dialogs/file-open-dialog.c new file mode 100644 index 0000000..9b4c5e9 --- /dev/null +++ b/app/dialogs/file-open-dialog.c @@ -0,0 +1,276 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" +#include "core/gimplayer.h" +#include "core/gimpprogress.h" + +#include "file/file-open.h" +#include "file/gimp-file.h" + +#include "widgets/gimpfiledialog.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpopendialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "file-open-dialog.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void file_open_dialog_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp); +static GimpImage *file_open_dialog_open_image (GtkWidget *dialog, + Gimp *gimp, + GFile *file, + GimpPlugInProcedure *load_proc); +static gboolean file_open_dialog_open_layers (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GimpPlugInProcedure *load_proc); + + +/* public functions */ + +GtkWidget * +file_open_dialog_new (Gimp *gimp) +{ + GtkWidget *dialog; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + dialog = gimp_open_dialog_new (gimp); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); + + gimp_file_dialog_load_state (GIMP_FILE_DIALOG (dialog), + "gimp-file-open-dialog-state"); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_open_dialog_response), + gimp); + + return dialog; +} + + +/* private functions */ + +static void +file_open_dialog_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp) +{ + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + GimpOpenDialog *open_dialog = GIMP_OPEN_DIALOG (dialog); + GSList *files; + GSList *list; + gboolean success = FALSE; + + gimp_file_dialog_save_state (GIMP_FILE_DIALOG (dialog), + "gimp-file-open-dialog-state"); + + if (response_id != GTK_RESPONSE_OK) + { + if (! file_dialog->busy) + gtk_widget_destroy (dialog); + + return; + } + + files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog)); + + if (files) + g_object_set_data_full (G_OBJECT (gimp), GIMP_FILE_OPEN_LAST_FILE_KEY, + g_object_ref (files->data), + (GDestroyNotify) g_object_unref); + + gimp_file_dialog_set_sensitive (file_dialog, FALSE); + + /* When we are going to open new image windows, unset the transient + * window. We don't need it since we will use gdk_window_raise() to + * keep the dialog on top. And if we don't do it, then the dialog + * will pull the image window it was invoked from on top of all the + * new opened image windows, and we don't want that to happen. + */ + if (! open_dialog->open_as_layers) + gtk_window_set_transient_for (GTK_WINDOW (dialog), NULL); + + if (file_dialog->image) + g_object_ref (file_dialog->image); + + for (list = files; list; list = g_slist_next (list)) + { + GFile *file = list->data; + + if (open_dialog->open_as_layers) + { + if (! file_dialog->image) + { + gimp_open_dialog_set_image ( + open_dialog, + file_open_dialog_open_image (dialog, + gimp, + file, + file_dialog->file_proc), + TRUE); + + if (file_dialog->image) + { + g_object_ref (file_dialog->image); + success = TRUE; + } + } + else if (file_open_dialog_open_layers (dialog, + file_dialog->image, + file, + file_dialog->file_proc)) + { + success = TRUE; + } + } + else + { + if (file_open_dialog_open_image (dialog, + gimp, + file, + file_dialog->file_proc)) + { + success = TRUE; + + /* Make the dialog stay on top of all images we open if + * we open say 10 at once + */ + gdk_window_raise (gtk_widget_get_window (dialog)); + } + } + + if (file_dialog->canceled) + break; + } + + if (success) + { + if (file_dialog->image) + { + if (open_dialog->open_as_layers) + gimp_image_flush (file_dialog->image); + + g_object_unref (file_dialog->image); + } + + gtk_widget_destroy (dialog); + } + else + { + if (file_dialog->image) + g_object_unref (file_dialog->image); + + gimp_file_dialog_set_sensitive (file_dialog, TRUE); + } + + g_slist_free_full (files, (GDestroyNotify) g_object_unref); +} + +static GimpImage * +file_open_dialog_open_image (GtkWidget *dialog, + Gimp *gimp, + GFile *file, + GimpPlugInProcedure *load_proc) +{ + GimpImage *image; + GimpPDBStatusType status; + GError *error = NULL; + + image = file_open_with_proc_and_display (gimp, + gimp_get_user_context (gimp), + GIMP_PROGRESS (dialog), + file, file, FALSE, + load_proc, + G_OBJECT (gtk_widget_get_screen (dialog)), + gimp_widget_get_monitor (dialog), + &status, &error); + + if (! image && status != GIMP_PDB_CANCEL) + { + gimp_message (gimp, G_OBJECT (dialog), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + return image; +} + +static gboolean +file_open_dialog_open_layers (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GimpPlugInProcedure *load_proc) +{ + GList *new_layers; + GimpPDBStatusType status; + GError *error = NULL; + + new_layers = file_open_layers (image->gimp, + gimp_get_user_context (image->gimp), + GIMP_PROGRESS (dialog), + image, FALSE, + file, GIMP_RUN_INTERACTIVE, load_proc, + &status, &error); + + if (new_layers) + { + gimp_image_add_layers (image, new_layers, + GIMP_IMAGE_ACTIVE_PARENT, -1, + 0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image), + _("Open layers")); + + g_list_free (new_layers); + + return TRUE; + } + else if (status != GIMP_PDB_CANCEL) + { + gimp_message (image->gimp, G_OBJECT (dialog), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + return FALSE; +} diff --git a/app/dialogs/file-open-dialog.h b/app/dialogs/file-open-dialog.h new file mode 100644 index 0000000..fba4896 --- /dev/null +++ b/app/dialogs/file-open-dialog.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 . + */ + +#ifndef __FILE_OPEN_DIALOG_H__ +#define __FILE_OPEN_DIALOG_H__ + + +GtkWidget * file_open_dialog_new (Gimp *gimp); + + +#endif /* __FILE_OPEN_DIALOG_H__ */ diff --git a/app/dialogs/file-open-location-dialog.c b/app/dialogs/file-open-location-dialog.c new file mode 100644 index 0000000..dcdffc9 --- /dev/null +++ b/app/dialogs/file-open-location-dialog.c @@ -0,0 +1,289 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpprogress.h" + +#include "file/file-open.h" +#include "file/file-utils.h" + +#include "widgets/gimpcontainerentry.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpprogressbox.h" +#include "widgets/gimpwidgets-utils.h" + +#include "file-open-location-dialog.h" + +#include "gimp-intl.h" + + +static void file_open_location_response (GtkDialog *dialog, + gint response_id, + Gimp *gimp); + +static gboolean file_open_location_completion (GtkEntryCompletion *completion, + const gchar *key, + GtkTreeIter *iter, + gpointer data); + + +/* public functions */ + +GtkWidget * +file_open_location_dialog_new (Gimp *gimp) +{ + GimpContext *context; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *entry; + GtkEntryCompletion *completion; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + dialog = gimp_dialog_new (_("Open Location"), + "gimp-file-open-location", + NULL, 0, + gimp_standard_help_func, + GIMP_HELP_FILE_OPEN_LOCATION, + + _("_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); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_open_location_response), + gimp); + + 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); + + /* we don't want the context to affect the entry, so create + * a scratch one instead of using e.g. the user context + */ + context = gimp_context_new (gimp, "file-open-location-dialog", NULL); + entry = gimp_container_entry_new (gimp->documents, context, + GIMP_VIEW_SIZE_SMALL, 0); + g_object_unref (context); + + completion = gtk_entry_get_completion (GTK_ENTRY (entry)); + gtk_entry_completion_set_match_func (completion, + file_open_location_completion, + NULL, NULL); + + 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); + gtk_widget_show (entry); + + g_object_set_data (G_OBJECT (dialog), "location-entry", entry); + + return dialog; +} + + +/* private functions */ + +static void +file_open_location_response (GtkDialog *dialog, + gint response_id, + Gimp *gimp) +{ + GtkWidget *entry; + GtkWidget *box; + const gchar *text = NULL; + + box = g_object_get_data (G_OBJECT (dialog), "progress-box"); + + if (response_id != GTK_RESPONSE_OK) + { + if (box && GIMP_PROGRESS_BOX (box)->active) + gimp_progress_cancel (GIMP_PROGRESS (box)); + else + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return; + } + + entry = g_object_get_data (G_OBJECT (dialog), "location-entry"); + text = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (text && strlen (text)) + { + GimpImage *image; + gchar *filename; + GFile *file; + GimpPDBStatusType status; + GError *error = NULL; + + filename = g_filename_from_uri (text, NULL, NULL); + + if (filename) + { + file = g_file_new_for_uri (text); + g_free (filename); + } + else + { + file = file_utils_filename_to_file (gimp, text, &error); + } + + if (!box) + { + box = gimp_progress_box_new (); + gtk_container_set_border_width (GTK_CONTAINER (box), 12); + gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + box, FALSE, FALSE, 0); + + g_object_set_data (G_OBJECT (dialog), "progress-box", box); + } + + if (file) + { + GFile *entered_file = g_file_new_for_uri (text); + + /* should not fail but does, see issue #3093 */ + if (! entered_file) + entered_file = g_object_ref (file); + + gtk_widget_show (box); + + gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE); + gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, FALSE); + + image = file_open_with_proc_and_display (gimp, + gimp_get_user_context (gimp), + GIMP_PROGRESS (box), + file, entered_file, + FALSE, NULL, + G_OBJECT (gtk_widget_get_screen (entry)), + gimp_widget_get_monitor (entry), + &status, &error); + + gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, TRUE); + gtk_editable_set_editable (GTK_EDITABLE (entry), TRUE); + + g_object_unref (entered_file); + + if (image == NULL && status != GIMP_PDB_CANCEL) + { + gimp_message (gimp, G_OBJECT (box), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + g_object_unref (file); + + if (image != NULL) + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + return; + } + } + else + { + gimp_message (gimp, G_OBJECT (box), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + text, + /* error should never be NULL, also issue #3093 */ + error ? error->message : _("Invalid URI")); + g_clear_error (&error); + } + } +} + +static gboolean +file_open_location_completion (GtkEntryCompletion *completion, + const gchar *key, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeModel *model = gtk_entry_completion_get_model (completion); + gchar *name; + gchar *normalized; + gchar *case_normalized; + gboolean match; + + gtk_tree_model_get (model, iter, + 1, &name, + -1); + + if (! name) + return FALSE; + + normalized = g_utf8_normalize (name, -1, G_NORMALIZE_ALL); + case_normalized = g_utf8_casefold (normalized, -1); + + match = (strncmp (key, case_normalized, strlen (key)) == 0); + + if (! match) + { + const gchar *colon = strchr (case_normalized, ':'); + + if (colon && strlen (colon) > 2 && colon[1] == '/' && colon[2] == '/') + match = (strncmp (key, colon + 3, strlen (key)) == 0); + } + + g_free (normalized); + g_free (case_normalized); + g_free (name); + + return match; +} diff --git a/app/dialogs/file-open-location-dialog.h b/app/dialogs/file-open-location-dialog.h new file mode 100644 index 0000000..b2926cd --- /dev/null +++ b/app/dialogs/file-open-location-dialog.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 . + */ + +#ifndef __FILE_OPEN_LOCATION_DIALOG_H__ +#define __FILE_OPEN_LOCATION_DIALOG_H__ + + +GtkWidget * file_open_location_dialog_new (Gimp *gimp); + + +#endif /* __FILE_OPEN_LOCATION_DIALOG_H__ */ diff --git a/app/dialogs/file-save-dialog.c b/app/dialogs/file-save-dialog.c new file mode 100644 index 0000000..a7aa857 --- /dev/null +++ b/app/dialogs/file-save-dialog.c @@ -0,0 +1,816 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimpprogress.h" + +#include "plug-in/gimppluginmanager-file.h" +#include "plug-in/gimppluginprocedure.h" + +#include "file/file-save.h" +#include "file/gimp-file.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpexportdialog.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpsavedialog.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "file-save-dialog.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +typedef enum +{ + CHECK_URI_FAIL, + CHECK_URI_OK, + CHECK_URI_SWITCH_DIALOGS +} CheckUriResult; + + +/* local function prototypes */ + +static GtkFileChooserConfirmation + file_save_dialog_confirm_overwrite (GtkWidget *dialog, + Gimp *gimp); +static void file_save_dialog_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp); +static CheckUriResult file_save_dialog_check_file (GtkWidget *save_dialog, + Gimp *gimp, + GFile **ret_file, + gchar **ret_basename, + GimpPlugInProcedure **ret_save_proc); +static gboolean file_save_dialog_no_overwrite_confirmation (GimpFileDialog *dialog, + Gimp *gimp); +static GimpPlugInProcedure * + file_save_dialog_find_procedure (GimpFileDialog *dialog, + GFile *file); +static gboolean file_save_dialog_switch_dialogs (GimpFileDialog *file_dialog, + Gimp *gimp, + const gchar *basename); +static gboolean file_save_dialog_use_extension (GtkWidget *save_dialog, + GFile *file); + + +/* public functions */ + +GtkWidget * +file_save_dialog_new (Gimp *gimp, + gboolean export) +{ + GtkWidget *dialog; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (! export) + { + dialog = gimp_save_dialog_new (gimp); + + gimp_file_dialog_load_state (GIMP_FILE_DIALOG (dialog), + "gimp-file-save-dialog-state"); + } + else + { + dialog = gimp_export_dialog_new (gimp); + + gimp_file_dialog_load_state (GIMP_FILE_DIALOG (dialog), + "gimp-file-export-dialog-state"); + } + + g_signal_connect (dialog, "confirm-overwrite", + G_CALLBACK (file_save_dialog_confirm_overwrite), + gimp); + + g_signal_connect (dialog, "response", + G_CALLBACK (file_save_dialog_response), + gimp); + + return dialog; +} + + +/* private functions */ + +static GtkFileChooserConfirmation +file_save_dialog_confirm_overwrite (GtkWidget *dialog, + Gimp *gimp) +{ + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + + if (file_save_dialog_no_overwrite_confirmation (file_dialog, gimp)) + /* The URI will not be accepted whatever happens, so don't + * bother asking the user about overwriting files + */ + return GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME; + else + return GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM; +} + +static void +file_save_dialog_response (GtkWidget *dialog, + gint response_id, + Gimp *gimp) +{ + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + GFile *file; + gchar *basename; + GimpPlugInProcedure *save_proc; + + if (GIMP_IS_SAVE_DIALOG (dialog)) + { + gimp_file_dialog_save_state (file_dialog, "gimp-file-save-dialog-state"); + } + else /* GIMP_IS_EXPORT_DIALOG (dialog) */ + { + gimp_file_dialog_save_state (file_dialog, "gimp-file-export-dialog-state"); + } + + if (response_id != GTK_RESPONSE_OK) + { + if (! file_dialog->busy) + gtk_widget_destroy (dialog); + + return; + } + + g_object_ref (file_dialog); + g_object_ref (file_dialog->image); + + switch (file_save_dialog_check_file (dialog, gimp, + &file, &basename, &save_proc)) + { + case CHECK_URI_FAIL: + break; + + case CHECK_URI_OK: + { + gboolean xcf_compression = FALSE; + + gimp_file_dialog_set_sensitive (file_dialog, FALSE); + + if (GIMP_IS_SAVE_DIALOG (dialog)) + { + xcf_compression = GIMP_SAVE_DIALOG (dialog)->compression; + } + + if (file_save_dialog_save_image (GIMP_PROGRESS (dialog), + gimp, + file_dialog->image, + file, + save_proc, + GIMP_RUN_INTERACTIVE, + GIMP_IS_SAVE_DIALOG (dialog) && + ! GIMP_SAVE_DIALOG (dialog)->save_a_copy, + FALSE, + GIMP_IS_EXPORT_DIALOG (dialog), + xcf_compression, + FALSE)) + { + /* Save was successful, now store the URI in a couple of + * places that depend on it being the user that made a + * save. Lower-level URI management is handled in + * file_save() + */ + if (GIMP_IS_SAVE_DIALOG (dialog)) + { + if (GIMP_SAVE_DIALOG (dialog)->save_a_copy) + gimp_image_set_save_a_copy_file (file_dialog->image, file); + + g_object_set_data_full (G_OBJECT (file_dialog->image->gimp), + GIMP_FILE_SAVE_LAST_FILE_KEY, + g_object_ref (file), + (GDestroyNotify) g_object_unref); + } + else + { + g_object_set_data_full (G_OBJECT (file_dialog->image->gimp), + GIMP_FILE_EXPORT_LAST_FILE_KEY, + g_object_ref (file), + (GDestroyNotify) g_object_unref); + } + + /* make sure the menus are updated with the keys we've just set */ + gimp_image_flush (file_dialog->image); + + /* Handle close-after-saving */ + if (GIMP_IS_SAVE_DIALOG (dialog) && + GIMP_SAVE_DIALOG (dialog)->close_after_saving && + GIMP_SAVE_DIALOG (dialog)->display_to_close) + { + GimpDisplay *display = GIMP_DISPLAY (GIMP_SAVE_DIALOG (dialog)->display_to_close); + + if (! gimp_image_is_dirty (gimp_display_get_image (display))) + { + gimp_display_close (display); + } + } + + gtk_widget_destroy (dialog); + } + + g_object_unref (file); + g_free (basename); + + gimp_file_dialog_set_sensitive (file_dialog, TRUE); + } + break; + + case CHECK_URI_SWITCH_DIALOGS: + file_dialog->busy = TRUE; /* prevent destruction */ + gtk_dialog_response (GTK_DIALOG (dialog), FILE_SAVE_RESPONSE_OTHER_DIALOG); + file_dialog->busy = FALSE; + + gtk_widget_destroy (dialog); + break; + } + + g_object_unref (file_dialog->image); + g_object_unref (file_dialog); +} + +/* IMPORTANT: When changing this function, keep + * file_save_dialog_no_overwrite_confirmation() up to date. It is + * difficult to move logic to a common place due to how the dialog is + * implemented in GTK+ in combination with how we use it. + */ +static CheckUriResult +file_save_dialog_check_file (GtkWidget *dialog, + Gimp *gimp, + GFile **ret_file, + gchar **ret_basename, + GimpPlugInProcedure **ret_save_proc) +{ + GimpFileDialog *file_dialog = GIMP_FILE_DIALOG (dialog); + GFile *file; + gchar *uri; + gchar *basename; + GFile *basename_file; + GimpPlugInProcedure *save_proc; + GimpPlugInProcedure *uri_proc; + GimpPlugInProcedure *basename_proc; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + + if (! file) + return CHECK_URI_FAIL; + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + basename_file = g_file_new_for_uri (basename); + + save_proc = file_dialog->file_proc; + uri_proc = file_save_dialog_find_procedure (file_dialog, file); + basename_proc = file_save_dialog_find_procedure (file_dialog, basename_file); + + g_object_unref (basename_file); + + uri = g_file_get_uri (file); + + GIMP_LOG (SAVE_DIALOG, "URI = %s", uri); + GIMP_LOG (SAVE_DIALOG, "basename = %s", basename); + GIMP_LOG (SAVE_DIALOG, "selected save_proc: %s", + save_proc ? + gimp_procedure_get_label (GIMP_PROCEDURE (save_proc)) : "NULL"); + GIMP_LOG (SAVE_DIALOG, "URI save_proc: %s", + uri_proc ? + gimp_procedure_get_label (GIMP_PROCEDURE (uri_proc)) : "NULL"); + GIMP_LOG (SAVE_DIALOG, "basename save_proc: %s", + basename_proc ? + gimp_procedure_get_label (GIMP_PROCEDURE (basename_proc)) : "NULL"); + + g_free (uri); + + /* first check if the user entered an extension at all */ + if (! basename_proc) + { + GIMP_LOG (SAVE_DIALOG, "basename has no valid extension"); + + if (! strchr (basename, '.')) + { + const gchar *ext = NULL; + + GIMP_LOG (SAVE_DIALOG, "basename has no '.', trying to add extension"); + + if (! save_proc && GIMP_IS_SAVE_DIALOG (dialog)) + { + ext = "xcf"; + } + else if (save_proc && save_proc->extensions_list) + { + ext = save_proc->extensions_list->data; + } + + if (ext) + { + gchar *ext_basename; + gchar *dirname; + gchar *filename; + gchar *utf8; + + GIMP_LOG (SAVE_DIALOG, "appending .%s to basename", ext); + + ext_basename = g_strconcat (basename, ".", ext, NULL); + + g_free (basename); + basename = ext_basename; + + dirname = g_path_get_dirname (gimp_file_get_utf8_name (file)); + filename = g_build_filename (dirname, basename, NULL); + g_free (dirname); + + utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), + utf8); + g_free (utf8); + + g_free (filename); + + GIMP_LOG (SAVE_DIALOG, + "set basename to %s, rerunning response and bailing out", + basename); + + /* call the response callback again, so the + * overwrite-confirm logic can check the changed uri + */ + gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + goto fail; + } + else + { + GIMP_LOG (SAVE_DIALOG, + "save_proc has no extensions, continuing without"); + + /* there may be file formats with no extension at all, use + * the selected proc in this case. + */ + basename_proc = save_proc; + + if (! uri_proc) + uri_proc = basename_proc; + } + + if (! basename_proc) + { + GIMP_LOG (SAVE_DIALOG, + "unable to figure save_proc, bailing out"); + + if (file_save_dialog_switch_dialogs (file_dialog, gimp, basename)) + { + goto switch_dialogs; + } + + goto fail; + } + } + else if (save_proc && ! save_proc->extensions_list) + { + GIMP_LOG (SAVE_DIALOG, + "basename has '.', but save_proc has no extensions, " + "accepting random extension"); + + /* accept any random extension if the file format has + * no extensions at all + */ + basename_proc = save_proc; + + if (! uri_proc) + uri_proc = basename_proc; + } + } + + /* then check if the selected format matches the entered extension */ + if (! save_proc) + { + GIMP_LOG (SAVE_DIALOG, "no save_proc was selected from the list"); + + if (! basename_proc) + { + GIMP_LOG (SAVE_DIALOG, + "basename has no useful extension, bailing out"); + + if (file_save_dialog_switch_dialogs (file_dialog, gimp, basename)) + { + goto switch_dialogs; + } + + goto fail; + } + + GIMP_LOG (SAVE_DIALOG, "use URI's proc '%s' so indirect saving works", + gimp_procedure_get_label (GIMP_PROCEDURE (uri_proc))); + + /* use the URI's proc if no save proc was selected */ + save_proc = uri_proc; + } + else + { + GIMP_LOG (SAVE_DIALOG, "save_proc '%s' was selected from the list", + gimp_procedure_get_label (GIMP_PROCEDURE (save_proc))); + + if (save_proc != basename_proc) + { + GIMP_LOG (SAVE_DIALOG, "however the basename's proc is '%s'", + gimp_procedure_get_label (GIMP_PROCEDURE (basename_proc))); + + if (uri_proc != basename_proc) + { + GIMP_LOG (SAVE_DIALOG, + "that's impossible for remote URIs, bailing out"); + + /* remote URI */ + + gimp_message (gimp, G_OBJECT (dialog), GIMP_MESSAGE_WARNING, + _("Saving remote files needs to determine the " + "file format from the file extension. " + "Please enter a file extension that matches " + "the selected file format or enter no file " + "extension at all.")); + + goto fail; + } + else + { + GIMP_LOG (SAVE_DIALOG, + "ask the user if she really wants that filename"); + + /* local URI */ + + if (! file_save_dialog_use_extension (dialog, file)) + { + goto fail; + } + } + } + else if (save_proc != uri_proc) + { + GIMP_LOG (SAVE_DIALOG, + "use URI's proc '%s' so indirect saving works", + gimp_procedure_get_label (GIMP_PROCEDURE (uri_proc))); + + /* need to use the URI's proc for saving because e.g. + * the GIF plug-in can't save a GIF to sftp:// + */ + save_proc = uri_proc; + } + } + + if (! save_proc) + { + g_warning ("%s: EEEEEEK", G_STRFUNC); + + return CHECK_URI_FAIL; + } + + *ret_file = file; + *ret_basename = basename; + *ret_save_proc = save_proc; + + return CHECK_URI_OK; + + fail: + + g_object_unref (file); + g_free (basename); + + return CHECK_URI_FAIL; + + switch_dialogs: + + g_object_unref (file); + g_free (basename); + + return CHECK_URI_SWITCH_DIALOGS; +} + +/* + * IMPORTANT: Keep this up to date with file_save_dialog_check_uri(). + */ +static gboolean +file_save_dialog_no_overwrite_confirmation (GimpFileDialog *file_dialog, + Gimp *gimp) +{ + GFile *file; + gchar *basename; + GFile *basename_file; + GimpPlugInProcedure *basename_proc; + GimpPlugInProcedure *save_proc; + gboolean uri_will_change; + gboolean unknown_ext; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_dialog)); + + if (! file) + return FALSE; + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + basename_file = g_file_new_for_uri (basename); + + save_proc = file_dialog->file_proc; + basename_proc = file_save_dialog_find_procedure (file_dialog, basename_file); + + g_object_unref (basename_file); + + uri_will_change = (! basename_proc && + ! strchr (basename, '.') && + (! save_proc || save_proc->extensions_list)); + + unknown_ext = (! save_proc && + ! basename_proc); + + g_free (basename); + g_object_unref (file); + + return uri_will_change || unknown_ext; +} + +static GimpPlugInProcedure * +file_save_dialog_find_procedure (GimpFileDialog *file_dialog, + GFile *file) +{ + GimpPlugInManager *manager = file_dialog->gimp->plug_in_manager; + GimpFileProcedureGroup group; + + if (GIMP_IS_SAVE_DIALOG (file_dialog)) + group = GIMP_FILE_PROCEDURE_GROUP_SAVE; + else + group = GIMP_FILE_PROCEDURE_GROUP_EXPORT; + + return gimp_plug_in_manager_file_procedure_find (manager, group, file, NULL); +} + +static gboolean +file_save_other_dialog_activated (GtkWidget *label, + const gchar *uri, + GtkDialog *dialog) +{ + gtk_dialog_response (dialog, FILE_SAVE_RESPONSE_OTHER_DIALOG); + + return TRUE; +} + +static gboolean +file_save_dialog_switch_dialogs (GimpFileDialog *file_dialog, + Gimp *gimp, + const gchar *basename) +{ + GimpPlugInProcedure *proc_in_other_group; + GimpFileProcedureGroup other_group; + GFile *file; + gboolean switch_dialogs = FALSE; + + file = g_file_new_for_uri (basename); + + if (GIMP_IS_EXPORT_DIALOG (file_dialog)) + other_group = GIMP_FILE_PROCEDURE_GROUP_SAVE; + else + other_group = GIMP_FILE_PROCEDURE_GROUP_EXPORT; + + proc_in_other_group = + gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, + other_group, file, NULL); + + g_object_unref (file); + + if (proc_in_other_group) + { + GtkWidget *dialog; + const gchar *primary; + const gchar *message; + const gchar *link; + + if (GIMP_IS_EXPORT_DIALOG (file_dialog)) + { + primary = _("The given filename cannot be used for exporting"); + message = _("You can use this dialog to export to various file formats. " + "If you want to save the image to the GIMP XCF format, use " + "File→Save instead."); + link = _("Take me to the Save dialog"); + } + else + { + primary = _("The given filename cannot be used for saving"); + message = _("You can use this dialog to save to the GIMP XCF " + "format. Use File→Export to export to other file formats."); + link = _("Take me to the Export dialog"); + } + + dialog = gimp_message_dialog_new (_("Extension Mismatch"), + GIMP_ICON_DIALOG_WARNING, + GTK_WIDGET (file_dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + "%s", primary); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + "%s", message); + + if (GIMP_IS_EXPORT_DIALOG (file_dialog) || + (! GIMP_SAVE_DIALOG (file_dialog)->save_a_copy && + ! GIMP_SAVE_DIALOG (file_dialog)->close_after_saving)) + { + GtkWidget *label; + gchar *markup; + + markup = g_strdup_printf ("%s", link); + label = gtk_label_new (markup); + g_free (markup); + + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (GIMP_MESSAGE_DIALOG (dialog)->box), label, + FALSE, FALSE, 0); + gtk_widget_show (label); + + g_signal_connect (label, "activate-link", + G_CALLBACK (file_save_other_dialog_activated), + dialog); + } + + gtk_dialog_set_response_sensitive (GTK_DIALOG (file_dialog), + GTK_RESPONSE_CANCEL, FALSE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (file_dialog), + GTK_RESPONSE_OK, FALSE); + + g_object_ref (dialog); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == FILE_SAVE_RESPONSE_OTHER_DIALOG) + { + switch_dialogs = TRUE; + } + + gtk_widget_destroy (dialog); + g_object_unref (dialog); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (file_dialog), + GTK_RESPONSE_CANCEL, TRUE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (file_dialog), + GTK_RESPONSE_OK, TRUE); + } + else + { + gimp_message (gimp, G_OBJECT (file_dialog), GIMP_MESSAGE_WARNING, + _("The given filename does not have any known " + "file extension. Please enter a known file " + "extension or select a file format from the " + "file format list.")); + } + + return switch_dialogs; +} + +static gboolean +file_save_dialog_use_extension (GtkWidget *save_dialog, + GFile *file) +{ + GtkWidget *dialog; + gboolean use_name = FALSE; + + dialog = gimp_message_dialog_new (_("Extension Mismatch"), + GIMP_ICON_DIALOG_QUESTION, + save_dialog, GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_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); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("The given file extension does " + "not match the chosen file type.")); + + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Do you want to save the image using this " + "name anyway?")); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (save_dialog), + GTK_RESPONSE_CANCEL, FALSE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (save_dialog), + GTK_RESPONSE_OK, FALSE); + + g_object_ref (dialog); + + use_name = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); + + gtk_widget_destroy (dialog); + g_object_unref (dialog); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (save_dialog), + GTK_RESPONSE_CANCEL, TRUE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (save_dialog), + GTK_RESPONSE_OK, TRUE); + + return use_name; +} + +gboolean +file_save_dialog_save_image (GimpProgress *progress, + Gimp *gimp, + GimpImage *image, + GFile *file, + GimpPlugInProcedure *save_proc, + GimpRunMode run_mode, + gboolean change_saved_state, + gboolean export_backward, + gboolean export_forward, + gboolean xcf_compression, + gboolean verbose_cancel) +{ + GimpPDBStatusType status; + GError *error = NULL; + GList *list; + gboolean success = FALSE; + + for (list = gimp_action_groups_from_name ("file"); + list; + list = g_list_next (list)) + { + gimp_action_group_set_action_sensitive (list->data, "file-quit", FALSE); + } + + gimp_image_set_xcf_compression (image, xcf_compression); + + status = file_save (gimp, image, progress, file, + save_proc, run_mode, + change_saved_state, export_backward, export_forward, + &error); + + switch (status) + { + case GIMP_PDB_SUCCESS: + success = TRUE; + break; + + case GIMP_PDB_CANCEL: + if (verbose_cancel) + gimp_message_literal (gimp, + G_OBJECT (progress), GIMP_MESSAGE_INFO, + _("Saving canceled")); + break; + + default: + { + gimp_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_ERROR, + _("Saving '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), + error ? error->message : _("Unknown error")); + g_clear_error (&error); + } + break; + } + + for (list = gimp_action_groups_from_name ("file"); + list; + list = g_list_next (list)) + { + gimp_action_group_set_action_sensitive (list->data, "file-quit", TRUE); + } + + return success; +} diff --git a/app/dialogs/file-save-dialog.h b/app/dialogs/file-save-dialog.h new file mode 100644 index 0000000..92f69cb --- /dev/null +++ b/app/dialogs/file-save-dialog.h @@ -0,0 +1,42 @@ +/* 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 . + */ + +#ifndef __FILE_SAVE_DIALOG_H__ +#define __FILE_SAVE_DIALOG_H__ + + +#define FILE_SAVE_RESPONSE_OTHER_DIALOG -23 + + +GtkWidget * file_save_dialog_new (Gimp *gimp, + gboolean export); + +gboolean file_save_dialog_save_image (GimpProgress *progress_and_handler, + Gimp *gimp, + GimpImage *image, + GFile *file, + GimpPlugInProcedure *write_proc, + GimpRunMode run_mode, + gboolean save_a_copy, + gboolean export_backward, + gboolean export_forward, + gboolean xcf_compression, + gboolean verbose_cancel); + + + +#endif /* __FILE_SAVE_DIALOG_H__ */ diff --git a/app/dialogs/fill-dialog.c b/app/dialogs/fill-dialog.c new file mode 100644 index 0000000..a74bfd1 --- /dev/null +++ b/app/dialogs/fill-dialog.c @@ -0,0 +1,183 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * fill-dialog.c + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpdrawable.h" +#include "core/gimpfilloptions.h" + +#include "widgets/gimpfilleditor.h" +#include "widgets/gimpviewabledialog.h" + +#include "fill-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + + +typedef struct _FillDialog FillDialog; + +struct _FillDialog +{ + GimpItem *item; + GimpDrawable *drawable; + GimpContext *context; + GimpFillOptions *options; + GimpFillCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void fill_dialog_free (FillDialog *private); +static void fill_dialog_response (GtkWidget *dialog, + gint response_id, + FillDialog *private); + + +/* public function */ + +GtkWidget * +fill_dialog_new (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + const gchar *title, + const gchar *icon_name, + const gchar *help_id, + GtkWidget *parent, + GimpFillOptions *options, + GimpFillCallback callback, + gpointer user_data) +{ + FillDialog *private; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *fill_editor; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (FillDialog); + + private->item = item; + private->drawable = drawable; + private->context = context; + private->options = gimp_fill_options_new (context->gimp, context, TRUE); + private->callback = callback; + private->user_data = user_data; + + gimp_config_sync (G_OBJECT (options), + G_OBJECT (private->options), 0); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (item), context, + title, "gimp-fill-options", + icon_name, + _("Choose Fill Style"), + parent, + gimp_standard_help_func, + help_id, + + _("_Reset"), RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Fill"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + RESPONSE_RESET, + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) fill_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (fill_dialog_response), + private); + + 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); + + fill_editor = gimp_fill_editor_new (private->options, FALSE); + gtk_box_pack_start (GTK_BOX (main_vbox), fill_editor, FALSE, FALSE, 0); + gtk_widget_show (fill_editor); + + return dialog; +} + + +/* private functions */ + +static void +fill_dialog_free (FillDialog *private) +{ + g_object_unref (private->options); + + g_slice_free (FillDialog, private); +} + +static void +fill_dialog_response (GtkWidget *dialog, + gint response_id, + FillDialog *private) +{ + switch (response_id) + { + case RESPONSE_RESET: + gimp_config_reset (GIMP_CONFIG (private->options)); + break; + + case GTK_RESPONSE_OK: + private->callback (dialog, + private->item, + private->drawable, + private->context, + private->options, + private->user_data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} diff --git a/app/dialogs/fill-dialog.h b/app/dialogs/fill-dialog.h new file mode 100644 index 0000000..35570cb --- /dev/null +++ b/app/dialogs/fill-dialog.h @@ -0,0 +1,45 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * fill-dialog.h + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __FILL_DIALOG_H__ +#define __FILL_DIALOG_H__ + + +typedef void (* GimpFillCallback) (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpFillOptions *options, + gpointer user_data); + + +GtkWidget * fill_dialog_new (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + const gchar *title, + const gchar *icon_name, + const gchar *help_id, + GtkWidget *parent, + GimpFillOptions *options, + GimpFillCallback callback, + gpointer user_data); + + +#endif /* __FILL_DIALOG_H__ */ diff --git a/app/dialogs/grid-dialog.c b/app/dialogs/grid-dialog.c new file mode 100644 index 0000000..622ff97 --- /dev/null +++ b/app/dialogs/grid-dialog.c @@ -0,0 +1,173 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-grid.h" +#include "core/gimpimage-undo.h" +#include "core/gimpimage-undo-push.h" +#include "core/gimpgrid.h" + +#include "widgets/gimpgrideditor.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "grid-dialog.h" + +#include "gimp-intl.h" + + +#define GRID_RESPONSE_RESET 1 + + +typedef struct _GridDialog GridDialog; + +struct _GridDialog +{ + GimpImage *image; + GimpGrid *grid; + GimpGrid *grid_backup; +}; + + +/* local functions */ + +static void grid_dialog_free (GridDialog *private); +static void grid_dialog_response (GtkWidget *dialog, + gint response_id, + GridDialog *private); + + +/* public function */ + +GtkWidget * +grid_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent) +{ + GridDialog *private; + GtkWidget *dialog; + GtkWidget *editor; + gdouble xres; + gdouble yres; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL); + + private = g_slice_new0 (GridDialog); + + private->image = image; + private->grid = gimp_image_get_grid (image); + private->grid_backup = gimp_config_duplicate (GIMP_CONFIG (private->grid)); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Configure Grid"), "gimp-grid-configure", + GIMP_ICON_GRID, _("Configure Image Grid"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_GRID, + + _("_Reset"), GRID_RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GRID_RESPONSE_RESET, + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) grid_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (grid_dialog_response), + private); + + gimp_image_get_resolution (image, &xres, &yres); + + editor = gimp_grid_editor_new (private->grid, context, xres, yres); + gtk_container_set_border_width (GTK_CONTAINER (editor), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + editor, TRUE, TRUE, 0); + + gtk_widget_show (editor); + + return dialog; +} + + +/* local functions */ + +static void +grid_dialog_free (GridDialog *private) +{ + g_object_unref (private->grid_backup); + + g_slice_free (GridDialog, private); +} + +static void +grid_dialog_response (GtkWidget *dialog, + gint response_id, + GridDialog *private) +{ + switch (response_id) + { + case GRID_RESPONSE_RESET: + gimp_config_sync (G_OBJECT (private->image->gimp->config->default_grid), + G_OBJECT (private->grid), 0); + break; + + case GTK_RESPONSE_OK: + if (! gimp_config_is_equal_to (GIMP_CONFIG (private->grid_backup), + GIMP_CONFIG (private->grid))) + { + gimp_image_undo_push_image_grid (private->image, _("Grid"), + private->grid_backup); + gimp_image_flush (private->image); + } + + gtk_widget_destroy (dialog); + break; + + default: + gimp_image_set_grid (private->image, private->grid_backup, FALSE); + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/grid-dialog.h b/app/dialogs/grid-dialog.h new file mode 100644 index 0000000..19670f9 --- /dev/null +++ b/app/dialogs/grid-dialog.h @@ -0,0 +1,29 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GRID_DIALOG_H__ +#define __GRID_DIALOG_H__ + + +GtkWidget * grid_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent); + + +#endif /* __GRID_DIALOG_H__ */ diff --git a/app/dialogs/image-merge-layers-dialog.c b/app/dialogs/image-merge-layers-dialog.c new file mode 100644 index 0000000..ce6ceac --- /dev/null +++ b/app/dialogs/image-merge-layers-dialog.c @@ -0,0 +1,192 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpitemstack.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "image-merge-layers-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _ImageMergeLayersDialog ImageMergeLayersDialog; + +struct _ImageMergeLayersDialog +{ + GimpImage *image; + GimpContext *context; + GimpMergeType merge_type; + gboolean merge_active_group; + gboolean discard_invisible; + GimpMergeLayersCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void image_merge_layers_dialog_free (ImageMergeLayersDialog *private); +static void image_merge_layers_dialog_response (GtkWidget *dialog, + gint response_id, + ImageMergeLayersDialog *private); + + +/* public functions */ + +GtkWidget * +image_merge_layers_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + GimpMergeLayersCallback callback, + gpointer user_data) +{ + ImageMergeLayersDialog *private; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *button; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + private = g_slice_new0 (ImageMergeLayersDialog); + + private->image = image; + private->context = context; + private->merge_type = merge_type; + private->merge_active_group = merge_active_group; + private->discard_invisible = discard_invisible; + private->callback = callback; + private->user_data = user_data; + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Merge Layers"), "gimp-image-merge-layers", + GIMP_ICON_LAYER_MERGE_DOWN, + _("Layers Merge Options"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_MERGE_LAYERS, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Merge"), 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); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) image_merge_layers_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (image_merge_layers_dialog_response), + private); + + 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_enum_radio_frame_new_with_range (GIMP_TYPE_MERGE_TYPE, + GIMP_EXPAND_AS_NECESSARY, + GIMP_CLIP_TO_BOTTOM_LAYER, + gtk_label_new (_("Final, Merged Layer should be:")), + G_CALLBACK (gimp_radio_button_update), + &private->merge_type, + &button); + gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), + private->merge_type); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + button = gtk_check_button_new_with_mnemonic (_("Merge within active _group only")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->merge_active_group); + 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), + &private->merge_active_group); + + if (gimp_item_stack_is_flat (GIMP_ITEM_STACK (gimp_image_get_layers (image)))) + gtk_widget_set_sensitive (button, FALSE); + + button = gtk_check_button_new_with_mnemonic (_("_Discard invisible layers")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->discard_invisible); + 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), + &private->discard_invisible); + + return dialog; +} + + +/* private functions */ + +static void +image_merge_layers_dialog_free (ImageMergeLayersDialog *private) +{ + g_slice_free (ImageMergeLayersDialog, private); +} + +static void +image_merge_layers_dialog_response (GtkWidget *dialog, + gint response_id, + ImageMergeLayersDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + private->callback (dialog, + private->image, + private->context, + private->merge_type, + private->merge_active_group, + private->discard_invisible, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/image-merge-layers-dialog.h b/app/dialogs/image-merge-layers-dialog.h new file mode 100644 index 0000000..ee96515 --- /dev/null +++ b/app/dialogs/image-merge-layers-dialog.h @@ -0,0 +1,42 @@ +/* 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 . + */ + +#ifndef __IMAGE_MERGE_LAYERS_DIALOG_H__ +#define __IMAGE_MERGE_LAYERS_DIALOG_H__ + + +typedef void (* GimpMergeLayersCallback) (GtkWidget *dialog, + GimpImage *image, + GimpContext *context, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + gpointer user_data); + + +GtkWidget * + image_merge_layers_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpMergeType merge_type, + gboolean merge_active_group, + gboolean discard_invisible, + GimpMergeLayersCallback callback, + gpointer user_data); + + +#endif /* __IMAGE_MERGE_LAYERS_DIALOG_H__ */ diff --git a/app/dialogs/image-new-dialog.c b/app/dialogs/image-new-dialog.c new file mode 100644 index 0000000..3650329 --- /dev/null +++ b/app/dialogs/image-new-dialog.c @@ -0,0 +1,380 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-new.h" +#include "core/gimptemplate.h" + +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimptemplateeditor.h" +#include "widgets/gimpwidgets-utils.h" + +#include "image-new-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + +typedef struct +{ + GtkWidget *dialog; + GtkWidget *confirm_dialog; + + GtkWidget *combo; + GtkWidget *editor; + + GimpContext *context; + GimpTemplate *template; +} ImageNewDialog; + + +/* local function prototypes */ + +static void image_new_dialog_free (ImageNewDialog *private); +static void image_new_dialog_response (GtkWidget *widget, + gint response_id, + ImageNewDialog *private); +static void image_new_template_changed (GimpContext *context, + GimpTemplate *template, + ImageNewDialog *private); +static void image_new_confirm_dialog (ImageNewDialog *private); +static void image_new_create_image (ImageNewDialog *private); + + +/* public functions */ + +GtkWidget * +image_new_dialog_new (GimpContext *context) +{ + ImageNewDialog *private; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *hbox; + GtkWidget *label; + GimpSizeEntry *entry; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + private = g_slice_new0 (ImageNewDialog); + + private->context = gimp_context_new (context->gimp, "image-new-dialog", + context); + private->template = g_object_new (GIMP_TYPE_TEMPLATE, NULL); + + private->dialog = dialog = + gimp_dialog_new (_("Create a New Image"), + "gimp-image-new", + NULL, 0, + gimp_standard_help_func, GIMP_HELP_FILE_NEW, + + _("_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); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_set_data_full (G_OBJECT (dialog), + "gimp-image-new-dialog", private, + (GDestroyNotify) image_new_dialog_free); + + g_signal_connect (dialog, "response", + G_CALLBACK (image_new_dialog_response), + private); + + 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); + + /* The template combo */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new_with_mnemonic (_("_Template:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + private->combo = g_object_new (GIMP_TYPE_CONTAINER_COMBO_BOX, + "container", context->gimp->templates, + "context", private->context, + "view-size", 16, + "view-border-width", 0, + "ellipsize", PANGO_ELLIPSIZE_NONE, + "focus-on-click", FALSE, + NULL); + gtk_box_pack_start (GTK_BOX (hbox), private->combo, TRUE, TRUE, 0); + gtk_widget_show (private->combo); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), private->combo); + + g_signal_connect (private->context, "template-changed", + G_CALLBACK (image_new_template_changed), + private); + + /* Template editor */ + private->editor = gimp_template_editor_new (private->template, context->gimp, + FALSE); + gtk_box_pack_start (GTK_BOX (main_vbox), private->editor, FALSE, FALSE, 0); + gtk_widget_show (private->editor); + + entry = GIMP_SIZE_ENTRY (gimp_template_editor_get_size_se (GIMP_TEMPLATE_EDITOR (private->editor))); + gimp_size_entry_set_activates_default (entry, TRUE); + gimp_size_entry_grab_focus (entry); + + image_new_template_changed (private->context, + gimp_context_get_template (private->context), + private); + + return dialog; +} + +void +image_new_dialog_set (GtkWidget *dialog, + GimpImage *image, + GimpTemplate *template) +{ + ImageNewDialog *private; + + g_return_if_fail (GIMP_IS_DIALOG (dialog)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + g_return_if_fail (template == NULL || GIMP_IS_TEMPLATE (template)); + + private = g_object_get_data (G_OBJECT (dialog), "gimp-image-new-dialog"); + + g_return_if_fail (private != NULL); + + gimp_context_set_template (private->context, template); + + if (! template) + { + template = gimp_image_new_get_last_template (private->context->gimp, + image); + + image_new_template_changed (private->context, template, private); + + g_object_unref (template); + } +} + + +/* private functions */ + +static void +image_new_dialog_free (ImageNewDialog *private) +{ + g_object_unref (private->context); + g_object_unref (private->template); + + g_slice_free (ImageNewDialog, private); +} + +static void +image_new_dialog_response (GtkWidget *dialog, + gint response_id, + ImageNewDialog *private) +{ + switch (response_id) + { + case RESPONSE_RESET: + gimp_config_sync (G_OBJECT (private->context->gimp->config->default_image), + G_OBJECT (private->template), 0); + gimp_context_set_template (private->context, NULL); + break; + + case GTK_RESPONSE_OK: + if (gimp_template_get_initial_size (private->template) > + GIMP_GUI_CONFIG (private->context->gimp->config)->max_new_image_size) + image_new_confirm_dialog (private); + else + image_new_create_image (private); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} + +static void +image_new_template_changed (GimpContext *context, + GimpTemplate *template, + ImageNewDialog *private) +{ + GimpTemplateEditor *editor; + GtkWidget *chain; + gdouble xres, yres; + gchar *comment; + + if (! template) + return; + + editor = GIMP_TEMPLATE_EDITOR (private->editor); + chain = gimp_template_editor_get_resolution_chain (editor); + + xres = gimp_template_get_resolution_x (template); + yres = gimp_template_get_resolution_y (template); + + gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain), + ABS (xres - yres) < GIMP_MIN_RESOLUTION); + + comment = (gchar *) gimp_template_get_comment (template); + + if (! comment || ! strlen (comment)) + comment = g_strdup (gimp_template_get_comment (private->template)); + else + comment = NULL; + + /* make sure the resolution values are copied first (see bug #546924) */ + gimp_config_sync (G_OBJECT (template), G_OBJECT (private->template), + GIMP_TEMPLATE_PARAM_COPY_FIRST); + gimp_config_sync (G_OBJECT (template), G_OBJECT (private->template), 0); + + if (comment) + { + g_object_set (private->template, + "comment", comment, + NULL); + + g_free (comment); + } +} + + +/* the confirm dialog */ + +static void +image_new_confirm_response (GtkWidget *dialog, + gint response_id, + ImageNewDialog *private) +{ + gtk_widget_destroy (dialog); + + private->confirm_dialog = NULL; + + if (response_id == GTK_RESPONSE_OK) + image_new_create_image (private); + else + gtk_widget_set_sensitive (private->dialog, TRUE); +} + +static void +image_new_confirm_dialog (ImageNewDialog *private) +{ + GimpGuiConfig *config; + GtkWidget *dialog; + gchar *size; + + if (private->confirm_dialog) + { + gtk_window_present (GTK_WINDOW (private->confirm_dialog)); + return; + } + + private->confirm_dialog = + dialog = gimp_message_dialog_new (_("Confirm Image Size"), + GIMP_ICON_DIALOG_WARNING, + private->dialog, + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (private->confirm_dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect (dialog, "response", + G_CALLBACK (image_new_confirm_response), + private); + + size = g_format_size (gimp_template_get_initial_size (private->template)); + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("You are trying to create an image " + "with a size of %s."), size); + g_free (size); + + config = GIMP_GUI_CONFIG (private->context->gimp->config); + size = g_format_size (config->max_new_image_size); + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("An image of the chosen size will use more " + "memory than what is configured as " + "\"Maximum new image size\" in the Preferences " + "dialog (currently %s)."), size); + g_free (size); + + gtk_widget_set_sensitive (private->dialog, FALSE); + + gtk_widget_show (dialog); +} + +static void +image_new_create_image (ImageNewDialog *private) +{ + GimpTemplate *template = g_object_ref (private->template); + Gimp *gimp = private->context->gimp; + GimpImage *image; + + gtk_widget_hide (private->dialog); + + image = gimp_image_new_from_template (gimp, template, + gimp_get_user_context (gimp)); + gimp_create_display (gimp, image, gimp_template_get_unit (template), 1.0, + G_OBJECT (gtk_widget_get_screen (private->dialog)), + gimp_widget_get_monitor (private->dialog)); + g_object_unref (image); + + gtk_widget_destroy (private->dialog); + + gimp_image_new_set_last_template (gimp, template); + + g_object_unref (template); +} diff --git a/app/dialogs/image-new-dialog.h b/app/dialogs/image-new-dialog.h new file mode 100644 index 0000000..e619cb7 --- /dev/null +++ b/app/dialogs/image-new-dialog.h @@ -0,0 +1,29 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 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 . + */ + +#ifndef __IMAGE_NEW_DIALOG_H__ +#define __IMAGE_NEW_DIALOG_H__ + + +GtkWidget * image_new_dialog_new (GimpContext *context); + +void image_new_dialog_set (GtkWidget *dialog, + GimpImage *image, + GimpTemplate *template); + + +#endif /* __IMAGE_NEW_DIALOG_H__ */ diff --git a/app/dialogs/image-properties-dialog.c b/app/dialogs/image-properties-dialog.c new file mode 100644 index 0000000..bbfcdaf --- /dev/null +++ b/app/dialogs/image-properties-dialog.c @@ -0,0 +1,100 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * image-properties-dialog.c + * Copyright (C) 2005 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpimagecommenteditor.h" +#include "widgets/gimpimagepropview.h" +#include "widgets/gimpimageprofileview.h" +#include "widgets/gimpviewabledialog.h" + +#include "image-properties-dialog.h" + +#include "gimp-intl.h" + + +GtkWidget * +image_properties_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent) +{ + GtkWidget *dialog; + GtkWidget *notebook; + GtkWidget *view; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + _("Image Properties"), + "gimp-image-properties", + "dialog-information", + _("Image Properties"), + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_PROPERTIES, + + _("_Close"), GTK_RESPONSE_OK, + + NULL); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + notebook, FALSE, FALSE, 0); + gtk_widget_show (notebook); + + view = gimp_image_prop_view_new (image); + gtk_container_set_border_width (GTK_CONTAINER (view), 12); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + view, gtk_label_new_with_mnemonic (_("_Properties"))); + gtk_widget_show (view); + + view = gimp_image_profile_view_new (image); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + view, gtk_label_new_with_mnemonic (_("C_olor Profile"))); + gtk_widget_show (view); + + view = gimp_image_comment_editor_new (image); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + view, gtk_label_new_with_mnemonic (_("Co_mment"))); + gtk_widget_show (view); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0); + + return dialog; +} diff --git a/app/dialogs/image-properties-dialog.h b/app/dialogs/image-properties-dialog.h new file mode 100644 index 0000000..57a14ff --- /dev/null +++ b/app/dialogs/image-properties-dialog.h @@ -0,0 +1,30 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * image-properties-dialog.h + * Copyright (C) 2005 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __IMAGE_PROPERTIES_DIALOG_H__ +#define __IMAGE_PROPERTIES_DIALOG_H__ + + +GtkWidget * image_properties_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent); + + +#endif /* __IMAGE_PROPERTIES_DIALOG_H__ */ diff --git a/app/dialogs/image-scale-dialog.c b/app/dialogs/image-scale-dialog.c new file mode 100644 index 0000000..36b6a3f --- /dev/null +++ b/app/dialogs/image-scale-dialog.c @@ -0,0 +1,291 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-scale.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" + +#include "scale-dialog.h" + +#include "image-scale-dialog.h" + +#include "gimp-intl.h" + + +typedef struct +{ + GtkWidget *dialog; + + GimpImage *image; + + gint width; + gint height; + GimpUnit unit; + GimpInterpolationType interpolation; + gdouble xresolution; + gdouble yresolution; + GimpUnit resolution_unit; + + GimpScaleCallback callback; + gpointer user_data; +} ImageScaleDialog; + + +/* local function prototypes */ + +static void image_scale_dialog_free (ImageScaleDialog *private); +static void image_scale_callback (GtkWidget *widget, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer data); + +static GtkWidget * image_scale_confirm_dialog (ImageScaleDialog *private); +static void image_scale_confirm_large (ImageScaleDialog *private, + gint64 new_memsize, + gint64 max_memsize); +static void image_scale_confirm_small (ImageScaleDialog *private); +static void image_scale_confirm_response (GtkWidget *widget, + gint response_id, + ImageScaleDialog *private); + + +/* public functions */ + +GtkWidget * +image_scale_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpUnit unit, + GimpInterpolationType interpolation, + GimpScaleCallback callback, + gpointer user_data) +{ + ImageScaleDialog *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (ImageScaleDialog); + + private->image = image; + private->callback = callback; + private->user_data = user_data; + + private->dialog = scale_dialog_new (GIMP_VIEWABLE (image), context, + C_("dialog-title", "Scale Image"), + "gimp-image-scale", + parent, + gimp_standard_help_func, + GIMP_HELP_IMAGE_SCALE, + unit, + interpolation, + image_scale_callback, + private); + + g_object_weak_ref (G_OBJECT (private->dialog), + (GWeakNotify) image_scale_dialog_free, private); + + return private->dialog; +} + + +/* private functions */ + +static void +image_scale_dialog_free (ImageScaleDialog *private) +{ + g_slice_free (ImageScaleDialog, private); +} + +static void +image_scale_callback (GtkWidget *widget, + GimpViewable *viewable, + gint width, + gint height, + GimpUnit unit, + GimpInterpolationType interpolation, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer data) +{ + ImageScaleDialog *private = data; + GimpImage *image = GIMP_IMAGE (viewable); + GimpImageScaleCheckType scale_check; + gint64 max_memsize; + gint64 new_memsize; + + private->width = width; + private->height = height; + private->unit = unit; + private->interpolation = interpolation; + private->xresolution = xresolution; + private->yresolution = yresolution; + private->resolution_unit = resolution_unit; + + gtk_widget_set_sensitive (widget, FALSE); + + max_memsize = GIMP_GUI_CONFIG (image->gimp->config)->max_new_image_size; + + scale_check = gimp_image_scale_check (image, + width, height, max_memsize, + &new_memsize); + switch (scale_check) + { + case GIMP_IMAGE_SCALE_TOO_BIG: + image_scale_confirm_large (private, new_memsize, max_memsize); + break; + + case GIMP_IMAGE_SCALE_TOO_SMALL: + image_scale_confirm_small (private); + break; + + case GIMP_IMAGE_SCALE_OK: + private->callback (private->dialog, + GIMP_VIEWABLE (private->image), + private->width, + private->height, + private->unit, + private->interpolation, + private->xresolution, + private->yresolution, + private->resolution_unit, + private->user_data); + break; + } +} + +static GtkWidget * +image_scale_confirm_dialog (ImageScaleDialog *private) +{ + GtkWidget *widget; + + widget = gimp_message_dialog_new (_("Confirm Scaling"), + GIMP_ICON_DIALOG_WARNING, + private->dialog, + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, + GIMP_HELP_IMAGE_SCALE_WARNING, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Scale"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (widget), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect (widget, "response", + G_CALLBACK (image_scale_confirm_response), + private); + + return widget; +} + +static void +image_scale_confirm_large (ImageScaleDialog *private, + gint64 new_memsize, + gint64 max_memsize) +{ + GtkWidget *widget = image_scale_confirm_dialog (private); + gchar *size; + + size = g_format_size (new_memsize); + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (widget)->box, + _("You are trying to create an image " + "with a size of %s."), size); + g_free (size); + + size = g_format_size (max_memsize); + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (widget)->box, + _("Scaling the image to the chosen size will " + "make it use more memory than what is " + "configured as \"Maximum Image Size\" in the " + "Preferences dialog (currently %s)."), size); + g_free (size); + + gtk_widget_show (widget); +} + +static void +image_scale_confirm_small (ImageScaleDialog *private) +{ + GtkWidget *widget = image_scale_confirm_dialog (private); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (widget)->box, + _("Scaling the image to the chosen size " + "will shrink some layers completely " + "away.")); + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (widget)->box, + _("Is this what you want to do?")); + + gtk_widget_show (widget); +} + +static void +image_scale_confirm_response (GtkWidget *widget, + gint response_id, + ImageScaleDialog *private) +{ + gtk_widget_destroy (widget); + + if (response_id == GTK_RESPONSE_OK) + { + private->callback (private->dialog, + GIMP_VIEWABLE (private->image), + private->width, + private->height, + private->unit, + private->interpolation, + private->xresolution, + private->yresolution, + private->resolution_unit, + private->user_data); + } + else + { + gtk_widget_set_sensitive (private->dialog, TRUE); + } +} diff --git a/app/dialogs/image-scale-dialog.h b/app/dialogs/image-scale-dialog.h new file mode 100644 index 0000000..fe3d257 --- /dev/null +++ b/app/dialogs/image-scale-dialog.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __IMAGE_SCALE_DIALOG_H__ +#define __IMAGE_SCALE_DIALOG_H__ + + +GtkWidget * image_scale_dialog_new (GimpImage *image, + GimpContext *context, + GtkWidget *parent, + GimpUnit unit, + GimpInterpolationType interpolation, + GimpScaleCallback callback, + gpointer user_data); + + +#endif /* __IMAGE_SCALE_DIALOG_H__ */ diff --git a/app/dialogs/input-devices-dialog.c b/app/dialogs/input-devices-dialog.c new file mode 100644 index 0000000..ace9431 --- /dev/null +++ b/app/dialogs/input-devices-dialog.c @@ -0,0 +1,102 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpdeviceeditor.h" +#include "widgets/gimpdevices.h" +#include "widgets/gimphelp-ids.h" + +#include "input-devices-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_SAVE 1 + + +/* local function prototypes */ + +static void input_devices_dialog_response (GtkWidget *dialog, + guint response_id, + Gimp *gimp); + + +/* public functions */ + +GtkWidget * +input_devices_dialog_new (Gimp *gimp) +{ + GtkWidget *dialog; + GtkWidget *content_area; + GtkWidget *editor; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + dialog = gimp_dialog_new (_("Configure Input Devices"), + "gimp-input-devices-dialog", + NULL, 0, + gimp_standard_help_func, + GIMP_HELP_INPUT_DEVICES, + + _("_Save"), RESPONSE_SAVE, + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + g_signal_connect (dialog, "response", + G_CALLBACK (input_devices_dialog_response), + gimp); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + editor = gimp_device_editor_new (gimp); + gtk_container_set_border_width (GTK_CONTAINER (editor), 12); + gtk_box_pack_start (GTK_BOX (content_area), editor, TRUE, TRUE, 0); + gtk_widget_show (editor); + + return dialog; +} + + +/* private functions */ + +static void +input_devices_dialog_response (GtkWidget *dialog, + guint response_id, + Gimp *gimp) +{ + switch (response_id) + { + case RESPONSE_SAVE: + gimp_devices_save (gimp, TRUE); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} diff --git a/app/dialogs/input-devices-dialog.h b/app/dialogs/input-devices-dialog.h new file mode 100644 index 0000000..54d50d2 --- /dev/null +++ b/app/dialogs/input-devices-dialog.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 . + */ + +#ifndef __INPUT_DEVICES_DIALOG_H__ +#define __INPUT_DEVICES_DIALOG_H__ + + +GtkWidget * input_devices_dialog_new (Gimp *gimp); + + +#endif /* __INPUT_DEVICES_DIALOG_H__ */ diff --git a/app/dialogs/item-options-dialog.c b/app/dialogs/item-options-dialog.c new file mode 100644 index 0000000..84493d9 --- /dev/null +++ b/app/dialogs/item-options-dialog.c @@ -0,0 +1,491 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpitem.h" + +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "item-options-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _ItemOptionsDialog ItemOptionsDialog; + +struct _ItemOptionsDialog +{ + GimpImage *image; + GimpItem *item; + GimpContext *context; + gboolean visible; + gboolean linked; + GimpColorTag color_tag; + gboolean lock_content; + gboolean lock_position; + GimpItemOptionsCallback callback; + gpointer user_data; + + GtkWidget *left_vbox; + GtkWidget *left_table; + gint table_row; + GtkWidget *name_entry; + GtkWidget *right_frame; + GtkWidget *right_vbox; + GtkWidget *lock_position_toggle; +}; + + +/* local function prototypes */ + +static void item_options_dialog_free (ItemOptionsDialog *private); +static void item_options_dialog_response (GtkWidget *dialog, + gint response_id, + ItemOptionsDialog *private); +static GtkWidget * check_button_with_icon_new (const gchar *label, + const gchar *icon_name, + GtkBox *vbox); + + +/* public functions */ + +GtkWidget * +item_options_dialog_new (GimpImage *image, + GimpItem *item, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *name_label, + const gchar *lock_content_icon_name, + const gchar *lock_content_label, + const gchar *lock_position_label, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + GimpItemOptionsCallback callback, + gpointer user_data) +{ + ItemOptionsDialog *private; + GtkWidget *dialog; + GimpViewable *viewable; + GtkWidget *main_hbox; + GtkWidget *table; + GtkWidget *button; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (item == NULL || GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (title != NULL, NULL); + g_return_val_if_fail (role != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (desc != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (ItemOptionsDialog); + + private->image = image; + private->item = item; + private->context = context; + private->visible = item_visible; + private->linked = item_linked; + private->color_tag = item_color_tag; + private->lock_content = item_lock_content; + private->lock_position = item_lock_position; + private->callback = callback; + private->user_data = user_data; + + if (item) + viewable = GIMP_VIEWABLE (item); + else + viewable = GIMP_VIEWABLE (image); + + dialog = gimp_viewable_dialog_new (viewable, context, + title, role, icon_name, desc, + parent, + gimp_standard_help_func, help_id, + + _("_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 (item_options_dialog_response), + private); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) item_options_dialog_free, private); + + g_object_set_data (G_OBJECT (dialog), "item-options-dialog-private", private); + + 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 (dialog))), + main_hbox, TRUE, TRUE, 0); + gtk_widget_show (main_hbox); + + private->left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_box_pack_start (GTK_BOX (main_hbox), private->left_vbox, TRUE, TRUE, 0); + gtk_widget_show (private->left_vbox); + + private->left_table = table = gtk_table_new (1, 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 (private->left_vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + /* The name label and entry */ + if (name_label) + { + GtkWidget *hbox; + GtkWidget *radio; + GtkWidget *radio_box; + GList *children; + GList *list; + + private->name_entry = gtk_entry_new (); + gtk_entry_set_activates_default (GTK_ENTRY (private->name_entry), TRUE); + gtk_entry_set_text (GTK_ENTRY (private->name_entry), item_name); + gimp_table_attach_aligned (GTK_TABLE (table), 0, private->table_row++, + name_label, 0.0, 0.5, + private->name_entry, 1, FALSE); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, private->table_row++, + _("Color tag:"), 0.0, 0.5, + hbox, 1, TRUE); + + radio_box = gimp_enum_radio_box_new (GIMP_TYPE_COLOR_TAG, + G_CALLBACK (gimp_radio_button_update), + &private->color_tag, + &radio); + gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (radio), + private->color_tag); + + children = gtk_container_get_children (GTK_CONTAINER (radio_box)); + + for (list = children; + list; + list = g_list_next (list)) + { + GimpColorTag color_tag; + GimpRGB color; + GtkWidget *image; + + radio = list->data; + + g_object_ref (radio); + gtk_container_remove (GTK_CONTAINER (radio_box), radio); + g_object_set (radio, "draw-indicator", FALSE, NULL); + gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0); + g_object_unref (radio); + + gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (radio))); + + color_tag = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (radio), + "gimp-item-data")); + + if (gimp_get_color_tag_color (color_tag, &color, FALSE)) + { + GtkSettings *settings = gtk_widget_get_settings (dialog); + gint w, h; + + image = gimp_color_area_new (&color, GIMP_COLOR_AREA_FLAT, 0); + gimp_color_area_set_color_config (GIMP_COLOR_AREA (image), + context->gimp->config->color_management); + gtk_icon_size_lookup_for_settings (settings, + GTK_ICON_SIZE_MENU, &w, &h); + gtk_widget_set_size_request (image, w, h); + } + else + { + image = gtk_image_new_from_icon_name (GIMP_ICON_CLOSE, + GTK_ICON_SIZE_MENU); + } + + gtk_container_add (GTK_CONTAINER (radio), image); + gtk_widget_show (image); + } + + g_list_free (children); + gtk_widget_destroy (radio_box); + } + + /* The switches frame & vbox */ + + private->right_frame = gimp_frame_new (_("Switches")); + gtk_box_pack_start (GTK_BOX (main_hbox), private->right_frame, + FALSE, FALSE, 0); + gtk_widget_show (private->right_frame); + + private->right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_container_add (GTK_CONTAINER (private->right_frame), private->right_vbox); + gtk_widget_show (private->right_vbox); + + button = check_button_with_icon_new (_("_Visible"), + GIMP_ICON_VISIBLE, + GTK_BOX (private->right_vbox)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->visible); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->visible); + + button = check_button_with_icon_new (_("_Linked"), + GIMP_ICON_LINKED, + GTK_BOX (private->right_vbox)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->linked); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->linked); + + button = check_button_with_icon_new (lock_content_label, + lock_content_icon_name, + GTK_BOX (private->right_vbox)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->lock_content); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->lock_content); + + button = check_button_with_icon_new (lock_position_label, + GIMP_ICON_TOOL_MOVE, + GTK_BOX (private->right_vbox)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->lock_position); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->lock_position); + + private->lock_position_toggle = button; + + return dialog; +} + +GtkWidget * +item_options_dialog_get_vbox (GtkWidget *dialog) +{ + ItemOptionsDialog *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_val_if_fail (private != NULL, NULL); + + return private->left_vbox; +} + +GtkWidget * +item_options_dialog_get_table (GtkWidget *dialog, + gint *next_row) +{ + ItemOptionsDialog *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL); + g_return_val_if_fail (next_row != NULL, NULL); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_val_if_fail (private != NULL, NULL); + + *next_row = private->table_row; + + return private->left_table; +} + +GtkWidget * +item_options_dialog_get_name_entry (GtkWidget *dialog) +{ + ItemOptionsDialog *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_val_if_fail (private != NULL, NULL); + + return private->name_entry; +} + +GtkWidget * +item_options_dialog_get_lock_position (GtkWidget *dialog) +{ + ItemOptionsDialog *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_val_if_fail (private != NULL, NULL); + + return private->lock_position_toggle; +} + +void +item_options_dialog_add_widget (GtkWidget *dialog, + const gchar *label, + GtkWidget *widget) +{ + ItemOptionsDialog *private; + + g_return_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_if_fail (private != NULL); + + gimp_table_attach_aligned (GTK_TABLE (private->left_table), + 0, private->table_row++, + label, 0.0, 0.5, + widget, 1, FALSE); +} + +GtkWidget * +item_options_dialog_add_switch (GtkWidget *dialog, + const gchar *icon_name, + const gchar *label) +{ + ItemOptionsDialog *private; + + g_return_val_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog), NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_val_if_fail (private != NULL, NULL); + + return check_button_with_icon_new (label, icon_name, + GTK_BOX (private->right_vbox)); +} + +void +item_options_dialog_set_switches_visible (GtkWidget *dialog, + gboolean visible) +{ + ItemOptionsDialog *private; + + g_return_if_fail (GIMP_IS_VIEWABLE_DIALOG (dialog)); + + private = g_object_get_data (G_OBJECT (dialog), + "item-options-dialog-private"); + + g_return_if_fail (private != NULL); + + gtk_widget_set_visible (private->right_frame, visible); +} + + +/* private functions */ + +static void +item_options_dialog_free (ItemOptionsDialog *private) +{ + g_slice_free (ItemOptionsDialog, private); +} + +static void +item_options_dialog_response (GtkWidget *dialog, + gint response_id, + ItemOptionsDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + const gchar *name = NULL; + + if (private->name_entry) + name = gtk_entry_get_text (GTK_ENTRY (private->name_entry)); + + private->callback (dialog, + private->image, + private->item, + private->context, + name, + private->visible, + private->linked, + private->color_tag, + private->lock_content, + private->lock_position, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} + +static GtkWidget * +check_button_with_icon_new (const gchar *label, + const gchar *icon_name, + GtkBox *vbox) +{ + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *image; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (vbox, hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + button = gtk_check_button_new_with_mnemonic (label); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + return button; +} diff --git a/app/dialogs/item-options-dialog.h b/app/dialogs/item-options-dialog.h new file mode 100644 index 0000000..f7aa75f --- /dev/null +++ b/app/dialogs/item-options-dialog.h @@ -0,0 +1,74 @@ +/* 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 . + */ + +#ifndef __ITEM_OPTIONS_DIALOG_H__ +#define __ITEM_OPTIONS_DIALOG_H__ + + +typedef void (* GimpItemOptionsCallback) (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data); + + +GtkWidget * item_options_dialog_new (GimpImage *image, + GimpItem *item, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *name_label, + const gchar *lock_content_icon_name, + const gchar *lock_content_label, + const gchar *lock_position_label, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + GimpItemOptionsCallback callback, + gpointer user_data); + +GtkWidget * item_options_dialog_get_vbox (GtkWidget *dialog); +GtkWidget * item_options_dialog_get_table (GtkWidget *dialog, + gint *next_row); +GtkWidget * item_options_dialog_get_name_entry (GtkWidget *dialog); +GtkWidget * item_options_dialog_get_lock_position (GtkWidget *dialog); + +void item_options_dialog_add_widget (GtkWidget *dialog, + const gchar *label, + GtkWidget *widget); +GtkWidget * item_options_dialog_add_switch (GtkWidget *dialog, + const gchar *icon_name, + const gchar *label); + +void item_options_dialog_set_switches_visible (GtkWidget *dialog, + gboolean visible); + + +#endif /* __ITEM_OPTIONS_DIALOG_H__ */ diff --git a/app/dialogs/keyboard-shortcuts-dialog.c b/app/dialogs/keyboard-shortcuts-dialog.c new file mode 100644 index 0000000..9fd354b --- /dev/null +++ b/app/dialogs/keyboard-shortcuts-dialog.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpactioneditor.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" + +#include "menus/menus.h" + +#include "keyboard-shortcuts-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_SAVE 1 + + +/* local function prototypes */ + +static void keyboard_shortcuts_dialog_response (GtkWidget *dialog, + gint response, + Gimp *gimp); + + +/* public functions */ + +GtkWidget * +keyboard_shortcuts_dialog_new (Gimp *gimp) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *editor; + GtkWidget *box; + GtkWidget *button; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + dialog = gimp_dialog_new (_("Configure Keyboard Shortcuts"), + "gimp-keyboard-shortcuts-dialog", + NULL, 0, + gimp_standard_help_func, + GIMP_HELP_KEYBOARD_SHORTCUTS, + + _("_Save"), RESPONSE_SAVE, + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + g_signal_connect (dialog, "response", + G_CALLBACK (keyboard_shortcuts_dialog_response), + gimp); + + 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); + + editor = gimp_action_editor_new (gimp_ui_managers_from_name ("")->data, + NULL, TRUE); + gtk_box_pack_start (GTK_BOX (vbox), editor, TRUE, TRUE, 0); + gtk_widget_show (editor); + + box = gimp_hint_box_new (_("To edit a shortcut key, click on the " + "corresponding row and type a new " + "accelerator, or press backspace to " + "clear.")); + gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0); + gtk_widget_show (box); + + button = gimp_prop_check_button_new (G_OBJECT (gimp->config), "save-accels", + _("S_ave keyboard shortcuts on exit")); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + return dialog; +} + + +/* private functions */ + +static void +keyboard_shortcuts_dialog_response (GtkWidget *dialog, + gint response, + Gimp *gimp) +{ + switch (response) + { + case RESPONSE_SAVE: + menus_save (gimp, TRUE); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} diff --git a/app/dialogs/keyboard-shortcuts-dialog.h b/app/dialogs/keyboard-shortcuts-dialog.h new file mode 100644 index 0000000..3c79912 --- /dev/null +++ b/app/dialogs/keyboard-shortcuts-dialog.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 . + */ + +#ifndef __KEYBOARD_SHORTCUTS_DIALOG_H__ +#define __KEYBOARD_SHORTCUTS_DIALOG_H__ + + +GtkWidget * keyboard_shortcuts_dialog_new (Gimp *gimp); + + +#endif /* __KEYBOARD_SHORTCUTS_DIALOG_H__ */ diff --git a/app/dialogs/layer-add-mask-dialog.c b/app/dialogs/layer-add-mask-dialog.c new file mode 100644 index 0000000..475d8ab --- /dev/null +++ b/app/dialogs/layer-add-mask-dialog.c @@ -0,0 +1,229 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpchannel.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" + +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "layer-add-mask-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _LayerAddMaskDialog LayerAddMaskDialog; + +struct _LayerAddMaskDialog +{ + GimpLayer *layer; + GimpAddMaskType add_mask_type; + GimpChannel *channel; + gboolean invert; + GimpAddMaskCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void layer_add_mask_dialog_free (LayerAddMaskDialog *private); +static void layer_add_mask_dialog_response (GtkWidget *dialog, + gint response_id, + LayerAddMaskDialog *private); +static gboolean layer_add_mask_dialog_channel_selected (GimpContainerView *view, + GimpViewable *viewable, + gpointer insert_data, + LayerAddMaskDialog *dialog); + + +/* public functions */ + +GtkWidget * +layer_add_mask_dialog_new (GimpLayer *layer, + GimpContext *context, + GtkWidget *parent, + GimpAddMaskType add_mask_type, + gboolean invert, + GimpAddMaskCallback callback, + gpointer user_data) +{ + LayerAddMaskDialog *private; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *combo; + GtkWidget *button; + GimpImage *image; + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + private = g_slice_new0 (LayerAddMaskDialog); + + private->layer = layer; + private->add_mask_type = add_mask_type; + private->invert = invert; + private->callback = callback; + private->user_data = user_data; + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (layer), context, + _("Add Layer Mask"), "gimp-layer-add-mask", + GIMP_ICON_LAYER_MASK, + _("Add a Mask to the Layer"), + parent, + gimp_standard_help_func, + GIMP_HELP_LAYER_MASK_ADD, + + _("_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); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) layer_add_mask_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (layer_add_mask_dialog_response), + private); + + 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_enum_radio_frame_new (GIMP_TYPE_ADD_MASK_TYPE, + gtk_label_new (_("Initialize Layer Mask to:")), + G_CALLBACK (gimp_radio_button_update), + &private->add_mask_type, + &button); + gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), + private->add_mask_type); + + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + image = gimp_item_get_image (GIMP_ITEM (layer)); + + combo = gimp_container_combo_box_new (gimp_image_get_channels (image), + context, + GIMP_VIEW_SIZE_SMALL, 1); + gimp_enum_radio_frame_add (GTK_FRAME (frame), combo, + GIMP_ADD_MASK_CHANNEL, TRUE); + gtk_widget_show (combo); + + g_signal_connect (combo, "select-item", + G_CALLBACK (layer_add_mask_dialog_channel_selected), + private); + + channel = gimp_image_get_active_channel (image); + + if (! channel) + channel = GIMP_CHANNEL (gimp_container_get_first_child (gimp_image_get_channels (image))); + + gimp_container_view_select_item (GIMP_CONTAINER_VIEW (combo), + GIMP_VIEWABLE (channel)); + + button = gtk_check_button_new_with_mnemonic (_("In_vert mask")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), private->invert); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->invert); + + return dialog; +} + + +/* private functions */ + +static void +layer_add_mask_dialog_free (LayerAddMaskDialog *private) +{ + g_slice_free (LayerAddMaskDialog, private); +} + +static void +layer_add_mask_dialog_response (GtkWidget *dialog, + gint response_id, + LayerAddMaskDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (private->layer)); + + if (private->add_mask_type == GIMP_ADD_MASK_CHANNEL && + ! private->channel) + { + gimp_message_literal (image->gimp, + G_OBJECT (dialog), GIMP_MESSAGE_WARNING, + _("Please select a channel first")); + return; + } + + private->callback (dialog, + private->layer, + private->add_mask_type, + private->channel, + private->invert, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} + +static gboolean +layer_add_mask_dialog_channel_selected (GimpContainerView *view, + GimpViewable *viewable, + gpointer insert_data, + LayerAddMaskDialog *private) +{ + private->channel = GIMP_CHANNEL (viewable); + + return TRUE; +} diff --git a/app/dialogs/layer-add-mask-dialog.h b/app/dialogs/layer-add-mask-dialog.h new file mode 100644 index 0000000..ecdd9d3 --- /dev/null +++ b/app/dialogs/layer-add-mask-dialog.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 . + */ + +#ifndef __LAYER_ADD_MASK_DIALOG_H__ +#define __LAYER_ADD_MASK_DIALOG_H__ + + +typedef void (* GimpAddMaskCallback) (GtkWidget *dialog, + GimpLayer *layer, + GimpAddMaskType add_mask_type, + GimpChannel *channel, + gboolean invert, + gpointer user_data); + + +GtkWidget * layer_add_mask_dialog_new (GimpLayer *layer, + GimpContext *context, + GtkWidget *parent, + GimpAddMaskType add_mask_type, + gboolean invert, + GimpAddMaskCallback callback, + gpointer user_data); + + +#endif /* __LAYER_ADD_MASK_DIALOG_H__ */ diff --git a/app/dialogs/layer-options-dialog.c b/app/dialogs/layer-options-dialog.c new file mode 100644 index 0000000..f392ed1 --- /dev/null +++ b/app/dialogs/layer-options-dialog.c @@ -0,0 +1,592 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "core/gimpcontext.h" +#include "core/gimpdrawable-filters.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" + +#include "text/gimptext.h" +#include "text/gimptextlayer.h" + +#include "widgets/gimpcontainertreeview.h" +#include "widgets/gimplayermodebox.h" +#include "widgets/gimpspinscale.h" +#include "widgets/gimpviewabledialog.h" + +#include "item-options-dialog.h" +#include "layer-options-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _LayerOptionsDialog LayerOptionsDialog; + +struct _LayerOptionsDialog +{ + GimpLayer *layer; + GimpLayerMode mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + gdouble opacity; + GimpFillType fill_type; + gboolean lock_alpha; + gboolean rename_text_layers; + GimpLayerOptionsCallback callback; + gpointer user_data; + + GtkWidget *mode_box; + GtkWidget *blend_space_combo; + GtkWidget *composite_space_combo; + GtkWidget *composite_mode_combo; + GtkWidget *size_se; + GtkWidget *offset_se; +}; + + +/* local function prototypes */ + +static void layer_options_dialog_free (LayerOptionsDialog *private); +static void layer_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data); +static void + layer_options_dialog_update_mode_sensitivity (LayerOptionsDialog *private); +static void layer_options_dialog_mode_notify (GtkWidget *widget, + const GParamSpec *pspec, + LayerOptionsDialog *private); +static void layer_options_dialog_rename_toggled (GtkWidget *widget, + LayerOptionsDialog *private); + + +/* public functions */ + +GtkWidget * +layer_options_dialog_new (GimpImage *image, + GimpLayer *layer, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_content, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + GimpLayerOptionsCallback callback, + gpointer user_data) +{ + LayerOptionsDialog *private; + GtkWidget *dialog; + GtkWidget *table; + GtkListStore *space_model; + GtkWidget *combo; + GtkWidget *scale; + GtkWidget *label; + GtkAdjustment *adjustment; + GtkWidget *spinbutton; + GtkWidget *button; + GimpLayerModeContext mode_context; + gdouble xres; + gdouble yres; + gint row = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + + private = g_slice_new0 (LayerOptionsDialog); + + private->layer = layer; + private->mode = layer_mode; + private->blend_space = layer_blend_space; + private->composite_space = layer_composite_space; + private->composite_mode = layer_composite_mode; + private->opacity = layer_opacity * 100.0; + private->fill_type = layer_fill_type; + private->lock_alpha = layer_lock_alpha; + private->rename_text_layers = FALSE; + private->callback = callback; + private->user_data = user_data; + + if (layer && gimp_item_is_text_layer (GIMP_ITEM (layer))) + private->rename_text_layers = GIMP_TEXT_LAYER (layer)->auto_rename; + + dialog = item_options_dialog_new (image, GIMP_ITEM (layer), context, + parent, title, role, + icon_name, desc, help_id, + _("Layer _name:"), + GIMP_ICON_TOOL_PAINTBRUSH, + _("Lock _pixels"), + _("Lock position and _size"), + layer_name, + layer_visible, + layer_linked, + layer_color_tag, + layer_lock_content, + layer_lock_position, + layer_options_dialog_callback, + private); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) layer_options_dialog_free, private); + + if (! layer || gimp_viewable_get_children (GIMP_VIEWABLE (layer)) == NULL) + mode_context = GIMP_LAYER_MODE_CONTEXT_LAYER; + else + mode_context = GIMP_LAYER_MODE_CONTEXT_GROUP; + + private->mode_box = gimp_layer_mode_box_new (mode_context); + item_options_dialog_add_widget (dialog, _("_Mode:"), private->mode_box); + gimp_layer_mode_box_set_mode (GIMP_LAYER_MODE_BOX (private->mode_box), + private->mode); + + g_signal_connect (private->mode_box, "notify::layer-mode", + G_CALLBACK (layer_options_dialog_mode_notify), + private); + + space_model = + gimp_enum_store_new_with_range (GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL); + + private->blend_space_combo = combo = + gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (space_model)); + item_options_dialog_add_widget (dialog, _("_Blend space:"), combo); + gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo), + "gimp-layer-color-space"); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->blend_space, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->blend_space); + + private->composite_space_combo = combo = + gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (space_model)); + item_options_dialog_add_widget (dialog, _("Compos_ite space:"), combo); + gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo), + "gimp-layer-color-space"); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->composite_space, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->composite_space); + + g_object_unref (space_model); + + private->composite_mode_combo = combo = + gimp_enum_combo_box_new (GIMP_TYPE_LAYER_COMPOSITE_MODE); + item_options_dialog_add_widget (dialog, _("Composite mo_de:"), combo); + gimp_enum_combo_box_set_icon_prefix (GIMP_ENUM_COMBO_BOX (combo), + "gimp-layer-composite"); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->composite_mode, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->composite_mode); + + /* set the sensitivity of above 3 menus */ + layer_options_dialog_update_mode_sensitivity (private); + + adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (private->opacity, 0.0, 100.0, + 1.0, 10.0, 0.0)); + scale = gimp_spin_scale_new (adjustment, NULL, 1); + item_options_dialog_add_widget (dialog, _("_Opacity:"), scale); + + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &private->opacity); + + table = item_options_dialog_get_table (dialog, &row); + + gimp_image_get_resolution (image, &xres, &yres); + + if (! layer) + { + /* The size labels */ + label = gtk_label_new (_("Width:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 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, row + 1, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* The size sizeentry */ + adjustment = (GtkAdjustment *) + gtk_adjustment_new (1, 1, 1, 1, 10, 0); + spinbutton = gimp_spin_button_new (adjustment, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10); + + private->size_se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", + TRUE, TRUE, FALSE, 10, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gtk_table_set_col_spacing (GTK_TABLE (private->size_se), 1, 4); + gtk_table_set_row_spacing (GTK_TABLE (private->size_se), 0, 2); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->size_se), + GTK_SPIN_BUTTON (spinbutton), NULL); + gtk_table_attach_defaults (GTK_TABLE (private->size_se), spinbutton, + 1, 2, 0, 1); + gtk_widget_show (spinbutton); + + gtk_table_attach (GTK_TABLE (table), private->size_se, 1, 2, row, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (private->size_se); + + gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->size_se), + GIMP_UNIT_PIXEL); + + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 0, + xres, FALSE); + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 1, + yres, FALSE); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 0, + GIMP_MIN_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 1, + GIMP_MIN_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 0, + 0, gimp_image_get_width (image)); + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 1, + 0, gimp_image_get_height (image)); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 0, + gimp_image_get_width (image)); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 1, + gimp_image_get_height (image)); + + row += 2; + } + + /* The offset labels */ + label = gtk_label_new (_("Offset X:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Offset Y:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row + 1, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* The offset sizeentry */ + adjustment = (GtkAdjustment *) + gtk_adjustment_new (0, 1, 1, 1, 10, 0); + spinbutton = gimp_spin_button_new (adjustment, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10); + + private->offset_se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", + TRUE, TRUE, FALSE, 10, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gtk_table_set_col_spacing (GTK_TABLE (private->offset_se), 1, 4); + gtk_table_set_row_spacing (GTK_TABLE (private->offset_se), 0, 2); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->offset_se), + GTK_SPIN_BUTTON (spinbutton), NULL); + gtk_table_attach_defaults (GTK_TABLE (private->offset_se), spinbutton, + 1, 2, 0, 1); + gtk_widget_show (spinbutton); + + gtk_table_attach (GTK_TABLE (table), private->offset_se, 1, 2, row, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (private->offset_se); + + gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->offset_se), + GIMP_UNIT_PIXEL); + + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 0, + xres, FALSE); + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 1, + yres, FALSE); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset_se), 0, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset_se), 1, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->offset_se), 0, + 0, gimp_image_get_width (image)); + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->offset_se), 1, + 0, gimp_image_get_height (image)); + + if (layer) + { + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 0, + gimp_item_get_offset_x (GIMP_ITEM (layer))); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 1, + gimp_item_get_offset_y (GIMP_ITEM (layer))); + } + else + { + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 0, 0); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 1, 0); + } + + row += 2; + + /* set the spacings after adding widgets or GtkTable will warn */ + gtk_table_set_row_spacing (GTK_TABLE (table), 3, 4); + if (! layer) + gtk_table_set_row_spacing (GTK_TABLE (table), 5, 4); + + if (! layer) + { + /* The fill type */ + combo = gimp_enum_combo_box_new (GIMP_TYPE_FILL_TYPE); + gimp_table_attach_aligned (GTK_TABLE (table), 0, row++, + _("_Fill with:"), 0.0, 0.5, + combo, 1, FALSE); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->fill_type, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->fill_type); + } + + if (layer) + { + GtkWidget *left_vbox = item_options_dialog_get_vbox (dialog); + GtkWidget *frame; + GimpContainer *filters; + GtkWidget *view; + + frame = gimp_frame_new (_("Active Filters")); + gtk_box_pack_start (GTK_BOX (left_vbox), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + filters = gimp_drawable_get_filters (GIMP_DRAWABLE (layer)); + + view = gimp_container_tree_view_new (filters, context, + GIMP_VIEW_SIZE_SMALL, 0); + gtk_container_add (GTK_CONTAINER (frame), view); + gtk_widget_show (view); + } + + button = item_options_dialog_get_lock_position (dialog); + + if (private->size_se) + g_object_bind_property (G_OBJECT (button), "active", + G_OBJECT (private->size_se), "sensitive", + G_BINDING_SYNC_CREATE | + G_BINDING_INVERT_BOOLEAN); + + g_object_bind_property (G_OBJECT (button), "active", + G_OBJECT (private->offset_se), "sensitive", + G_BINDING_SYNC_CREATE | + G_BINDING_INVERT_BOOLEAN); + + button = item_options_dialog_add_switch (dialog, + GIMP_ICON_TRANSPARENCY, + _("Lock _alpha")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->lock_alpha); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->lock_alpha); + + /* For text layers add a toggle to control "auto-rename" */ + if (layer && gimp_item_is_text_layer (GIMP_ITEM (layer))) + { + button = item_options_dialog_add_switch (dialog, + GIMP_ICON_TOOL_TEXT, + _("Set name from _text")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->rename_text_layers); + g_signal_connect (button, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &private->rename_text_layers); + + g_signal_connect (button, "toggled", + G_CALLBACK (layer_options_dialog_rename_toggled), + private); + } + + return dialog; +} + + +/* private functions */ + +static void +layer_options_dialog_free (LayerOptionsDialog *private) +{ + g_slice_free (LayerOptionsDialog, private); +} + +static void +layer_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data) +{ + LayerOptionsDialog *private = user_data; + gint width = 0; + gint height = 0; + gint offset_x; + gint offset_y; + + if (private->size_se) + { + width = + RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), + 0)); + height = + RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), + 1)); + } + + offset_x = + RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se), + 0)); + offset_y = + RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se), + 1)); + + private->callback (dialog, + image, + GIMP_LAYER (item), + context, + item_name, + private->mode, + private->blend_space, + private->composite_space, + private->composite_mode, + private->opacity / 100.0, + private->fill_type, + width, + height, + offset_x, + offset_y, + item_visible, + item_linked, + item_color_tag, + item_lock_content, + item_lock_position, + private->lock_alpha, + private->rename_text_layers, + private->user_data); +} + +static void +layer_options_dialog_update_mode_sensitivity (LayerOptionsDialog *private) +{ + gboolean mutable; + + mutable = gimp_layer_mode_is_blend_space_mutable (private->mode); + gtk_widget_set_sensitive (private->blend_space_combo, mutable); + + mutable = gimp_layer_mode_is_composite_space_mutable (private->mode); + gtk_widget_set_sensitive (private->composite_space_combo, mutable); + + mutable = gimp_layer_mode_is_composite_mode_mutable (private->mode); + gtk_widget_set_sensitive (private->composite_mode_combo, mutable); +} + +static void +layer_options_dialog_mode_notify (GtkWidget *widget, + const GParamSpec *pspec, + LayerOptionsDialog *private) +{ + private->mode = gimp_layer_mode_box_get_mode (GIMP_LAYER_MODE_BOX (widget)); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->blend_space_combo), + GIMP_LAYER_COLOR_SPACE_AUTO); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->composite_space_combo), + GIMP_LAYER_COLOR_SPACE_AUTO); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->composite_mode_combo), + GIMP_LAYER_COMPOSITE_AUTO); + + layer_options_dialog_update_mode_sensitivity (private); +} + +static void +layer_options_dialog_rename_toggled (GtkWidget *widget, + LayerOptionsDialog *private) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) && + gimp_item_is_text_layer (GIMP_ITEM (private->layer))) + { + GimpTextLayer *text_layer = GIMP_TEXT_LAYER (private->layer); + GimpText *text = gimp_text_layer_get_text (text_layer); + + if (text && text->text) + { + GtkWidget *dialog; + GtkWidget *name_entry; + gchar *name = gimp_utf8_strtrim (text->text, 30); + + dialog = gtk_widget_get_toplevel (widget); + + name_entry = item_options_dialog_get_name_entry (dialog); + + gtk_entry_set_text (GTK_ENTRY (name_entry), name); + + g_free (name); + } + } +} diff --git a/app/dialogs/layer-options-dialog.h b/app/dialogs/layer-options-dialog.h new file mode 100644 index 0000000..ceb0502 --- /dev/null +++ b/app/dialogs/layer-options-dialog.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 . + */ + +#ifndef __LAYER_OPTIONS_DIALOG_H__ +#define __LAYER_OPTIONS_DIALOG_H__ + + +typedef void (* GimpLayerOptionsCallback) (GtkWidget *dialog, + GimpImage *image, + GimpLayer *layer, + GimpContext *context, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gint layer_width, + gint layer_height, + gint layer_offset_x, + gint layer_offset_y, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_content, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + gboolean rename_text_layer, + gpointer user_data); + + +GtkWidget * layer_options_dialog_new (GimpImage *image, + GimpLayer *layer, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *layer_name, + GimpLayerMode layer_mode, + GimpLayerColorSpace layer_blend_space, + GimpLayerColorSpace layer_composite_space, + GimpLayerCompositeMode layer_composite_mode, + gdouble layer_opacity, + GimpFillType layer_fill_type, + gboolean layer_visible, + gboolean layer_linked, + GimpColorTag layer_color_tag, + gboolean layer_lock_content, + gboolean layer_lock_position, + gboolean layer_lock_alpha, + GimpLayerOptionsCallback callback, + gpointer user_data); + + +#endif /* __LAYER_OPTIONS_DIALOG_H__ */ diff --git a/app/dialogs/lebl-dialog.c b/app/dialogs/lebl-dialog.c new file mode 100644 index 0000000..53bd492 --- /dev/null +++ b/app/dialogs/lebl-dialog.c @@ -0,0 +1,869 @@ +#include "config.h" + +#include +#include + +#include +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "lebl-dialog.h" + +#include "gimp-intl.h" + +/* phish code */ +#define PHSHFRAMES 8 +#define PHSHORIGWIDTH 288 +#define PHSHORIGHEIGHT 22 +#define PHSHWIDTH (PHSHORIGWIDTH/PHSHFRAMES) +#define PHSHHEIGHT PHSHORIGHEIGHT +#define PHSHCHECKTIMEOUT (g_random_int()%120*1000) +#define PHSHTIMEOUT 120 +#define PHSHHIDETIMEOUT 80 +#define PHSHXS 5 +#define PHSHYS ((g_random_int() % 2) + 1) +#define PHSHXSHIDEFACTOR 2.5 +#define PHSHYSHIDEFACTOR 2.5 +#define PHSHPIXELSTOREMOVE(p) (p[3] < 55 || p[2] > 200) + +static void +phsh_unsea(GdkPixbuf *gp) +{ + guchar *pixels = gdk_pixbuf_get_pixels (gp); + int rs = gdk_pixbuf_get_rowstride (gp); + int w = gdk_pixbuf_get_width (gp); + int h = gdk_pixbuf_get_height (gp); + int x, y; + + for (y = 0; y < h; y++, pixels += rs) { + guchar *p = pixels; + for (x = 0; x < w; x++, p+=4) { + if (PHSHPIXELSTOREMOVE(p)) + p[3] = 0; + } + } +} + +static GdkPixbuf * +get_phsh_frame (GdkPixbuf *pb, int frame) +{ + GdkPixbuf *newpb; + + newpb = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + PHSHWIDTH, PHSHHEIGHT); + gdk_pixbuf_copy_area (pb, frame * PHSHWIDTH, 0, + PHSHWIDTH, PHSHHEIGHT, newpb, 0, 0); + + return newpb; +} + +typedef struct { + gboolean live; + int x, y; +} InvGoat; + +typedef struct { + gboolean good; + int y; + int x; +} InvShot; + + +static GtkWidget *geginv = NULL; +static GtkWidget *geginv_canvas = NULL; +static GtkWidget *geginv_label = NULL; +static GdkPixbuf *inv_goat1 = NULL; +static GdkPixbuf *inv_goat2 = NULL; +static GdkPixbuf *inv_phsh1 = NULL; +static GdkPixbuf *inv_phsh2 = NULL; +static int inv_phsh_state = 0; +static int inv_goat_state = 0; +static int inv_width = 0; +static int inv_height = 0; +static int inv_goat_width = 0; +static int inv_goat_height = 0; +static int inv_phsh_width = 0; +static int inv_phsh_height = 0; +#define INV_ROWS 3 +#define INV_COLS 5 +static InvGoat invs[INV_COLS][INV_ROWS] = { { { FALSE, 0, 0 } } }; +static int inv_num = INV_ROWS * INV_COLS; +static double inv_factor = 1.0; +static int inv_our_x = 0; +static int inv_x = 0; +static int inv_y = 0; +static int inv_first_col = 0; +static int inv_last_col = INV_COLS-1; +static int inv_level = 0; +static int inv_lives = 0; +static gboolean inv_do_pause = FALSE; +static gboolean inv_reverse = FALSE; +static gboolean inv_game_over = FALSE; +static gboolean inv_left_pressed = FALSE; +static gboolean inv_right_pressed = FALSE; +static gboolean inv_fire_pressed = FALSE; +static gboolean inv_left_released = FALSE; +static gboolean inv_right_released = FALSE; +static gboolean inv_fire_released = FALSE; +static gboolean inv_paused = FALSE; +static GSList *inv_shots = NULL; +static guint inv_draw_idle = 0; + +static void +inv_show_status (void) +{ + gchar *s, *t, *u, *v, *w; + if (geginv == NULL) + return; + + if (inv_game_over) { + t = g_strdup_printf (_("GAME OVER at level %d!"), + inv_level+1); + u = g_strdup_printf ("%s", t); + /* Translators: the first and third strings are similar to a + * title, and the second string is a small information text. + * The spaces are there only to separate all the strings, so + try to keep them as is. */ + s = g_strdup_printf (_("%1$s %2$s %3$s"), + u, _("Press 'q' to quit"), u); + g_free (t); + g_free (u); + + } else if (inv_paused) { + t = g_strdup_printf("%s", _("Paused")); + /* Translators: the first string is a title and the second + * string is a small information text. */ + s = g_strdup_printf (_("%1$s\t%2$s"), + t, _("Press 'p' to unpause")); + g_free (t); + + } else { + t = g_strdup_printf ("%d", inv_level+1); + u = g_strdup_printf ("%d", inv_lives); + v = g_strdup_printf (_("Level: %s, Lives: %s"), t, u); + w = g_strdup_printf ("%s", v); + /* Translators: the first string is a title and the second + * string is a small information text. */ + s = g_strdup_printf (_("%1$s\t%2$s"), w, + _("Left/Right to move, Space to fire, 'p' to pause, 'q' to quit")); + g_free (t); + g_free (u); + g_free (v); + g_free (w); + + } + gtk_label_set_markup (GTK_LABEL (geginv_label), s); + + g_free (s); +} + +static gboolean +inv_draw (gpointer data) +{ + inv_draw_idle = 0; + + if (geginv) + gtk_widget_queue_draw (data); + + return FALSE; +} + +static void +inv_queue_draw (GtkWidget *window) +{ + if (inv_draw_idle == 0) + inv_draw_idle = g_idle_add (inv_draw, window); +} + +static void +inv_draw_explosion (int x, int y) +{ + cairo_t *cr; + int i; + + if ( ! gtk_widget_is_drawable (geginv_canvas)) + return; + + cr = gdk_cairo_create ( gtk_widget_get_window (geginv_canvas)); + + cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); + + for (i = 5; i < 100; i += 5) { + cairo_arc (cr, x, y, i, 0, 2 * G_PI); + cairo_fill (cr); + gdk_flush (); + g_usleep (50000); + } + + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + + for (i = 5; i < 100; i += 5) { + cairo_arc (cr, x, y, i, 0, 2 * G_PI); + cairo_fill (cr); + gdk_flush (); + g_usleep (50000); + } + + cairo_destroy (cr); + + inv_queue_draw (geginv); +} + + +static void +inv_do_game_over (void) +{ + GSList *li; + + inv_game_over = TRUE; + + for (li = inv_shots; li != NULL; li = li->next) { + InvShot *shot = li->data; + shot->good = FALSE; + } + + inv_queue_draw (geginv); + + inv_show_status (); +} + + +static GdkPixbuf * +pb_scale (GdkPixbuf *pb, double scale) +{ + int w, h; + + if (scale == 1.0) + return (GdkPixbuf *)g_object_ref ((GObject *)pb); + + w = gdk_pixbuf_get_width (pb) * scale; + h = gdk_pixbuf_get_height (pb) * scale; + + return gdk_pixbuf_scale_simple (pb, w, h, + GDK_INTERP_BILINEAR); +} + +static void +refind_first_and_last (void) +{ + int i, j; + + for (i = 0; i < INV_COLS; i++) { + gboolean all_null = TRUE; + for (j = 0; j < INV_ROWS; j++) { + if (invs[i][j].live) { + all_null = FALSE; + break; + } + } + if ( ! all_null) { + inv_first_col = i; + break; + } + } + + for (i = INV_COLS-1; i >= 0; i--) { + gboolean all_null = TRUE; + for (j = 0; j < INV_ROWS; j++) { + if (invs[i][j].live) { + all_null = FALSE; + break; + } + } + if ( ! all_null) { + inv_last_col = i; + break; + } + } +} + +static void +whack_gegl (int i, int j) +{ + if ( ! invs[i][j].live) + return; + + invs[i][j].live = FALSE; + inv_num --; + + if (inv_num > 0) { + refind_first_and_last (); + } else { + inv_x = 70; + inv_y = 70; + inv_first_col = 0; + inv_last_col = INV_COLS-1; + inv_reverse = FALSE; + + g_slist_foreach (inv_shots, (GFunc)g_free, NULL); + g_slist_free (inv_shots); + inv_shots = NULL; + + for (i = 0; i < INV_COLS; i++) { + for (j = 0; j < INV_ROWS; j++) { + invs[i][j].live = TRUE; + invs[i][j].x = 70 + i * 100; + invs[i][j].y = 70 + j * 80; + } + } + inv_num = INV_ROWS * INV_COLS; + + inv_level ++; + + inv_show_status (); + } + + inv_queue_draw (geginv); +} + +static gboolean +geginv_timeout (gpointer data) +{ + int i, j; + int limitx1; + int limitx2; + int speed; + int shots; + int max_shots; + + if (inv_paused) + return TRUE; + + if (geginv != data || + inv_num <= 0 || + inv_y > 700) + return FALSE; + + limitx1 = 70 - (inv_first_col * 100); + limitx2 = 800 - 70 - (inv_last_col * 100); + + if (inv_game_over) { + inv_y += 30; + } else { + if (inv_num < (INV_COLS*INV_ROWS)/3) + speed = 45+2*inv_level; + else if (inv_num < (2*INV_COLS*INV_ROWS)/3) + speed = 30+2*inv_level; + else + speed = 15+2*inv_level; + + if (inv_reverse) { + inv_x -= speed; + if (inv_x < limitx1) { + inv_reverse = FALSE; + inv_x = (limitx1 + (limitx1 - inv_x)); + inv_y += 30+inv_level; + } + } else { + inv_x += speed; + if (inv_x > limitx2) { + inv_reverse = TRUE; + inv_x = (limitx2 - (inv_x - limitx2)); + inv_y += 30+inv_level; + } + } + } + + for (i = 0; i < INV_COLS; i++) { + for (j = 0; j < INV_ROWS; j++) { + if (invs[i][j].live) { + invs[i][j].x = inv_x + i * 100; + invs[i][j].y = inv_y + j * 80; + + if ( ! inv_game_over && + invs[i][j].y >= 570) { + inv_do_game_over (); + } else if ( ! inv_game_over && + invs[i][j].y >= 530 && + invs[i][j].x + 40 > inv_our_x - 25 && + invs[i][j].x - 40 < inv_our_x + 25) { + whack_gegl (i,j); + inv_lives --; + inv_draw_explosion (inv_our_x, 550); + if (inv_lives <= 0) { + inv_do_game_over (); + } else { + g_slist_foreach (inv_shots, (GFunc)g_free, NULL); + g_slist_free (inv_shots); + inv_shots = NULL; + inv_our_x = 400; + inv_do_pause = TRUE; + inv_show_status (); + } + } + } + } + } + + shots = 0; + max_shots = (g_random_int () >> 3) % (2+inv_level); + while ( ! inv_game_over && shots < MIN (max_shots, inv_num)) { + int i = (g_random_int () >> 3) % INV_COLS; + for (j = INV_ROWS-1; j >= 0; j--) { + if (invs[i][j].live) { + InvShot *shot = g_new0 (InvShot, 1); + + shot->good = FALSE; + shot->x = invs[i][j].x + (g_random_int () % 6) - 3; + shot->y = invs[i][j].y + inv_goat_height/2 + (g_random_int () % 3); + + inv_shots = g_slist_prepend (inv_shots, shot); + shots++; + break; + } + } + } + + inv_goat_state = (inv_goat_state+1) % 2; + + inv_queue_draw (geginv); + + g_timeout_add (((inv_num/4)+1) * 100, geginv_timeout, geginv); + + return FALSE; +} + +static gboolean +find_gegls (int x, int y) +{ + int i, j; + + /* FIXME: this is stupid, we can do better */ + for (i = 0; i < INV_COLS; i++) { + for (j = 0; j < INV_ROWS; j++) { + int ix = invs[i][j].x; + int iy = invs[i][j].y; + + if ( ! invs[i][j].live) + continue; + + if (y >= iy - 30 && + y <= iy + 30 && + x >= ix - 40 && + x <= ix + 40) { + whack_gegl (i, j); + return TRUE; + } + } + } + + return FALSE; +} + + +static gboolean +geginv_move_timeout (gpointer data) +{ + GSList *li; + static int shot_inhibit = 0; + + if (inv_paused) + return TRUE; + + if (geginv != data || + inv_num <= 0 || + inv_y > 700) + return FALSE; + + inv_phsh_state = (inv_phsh_state+1)%10; + + /* we will be drawing something */ + if (inv_shots != NULL) + inv_queue_draw (geginv); + + li = inv_shots; + while (li != NULL) { + InvShot *shot = li->data; + + if (shot->good) { + shot->y -= 30; + if (find_gegls (shot->x, shot->y) || + shot->y <= 0) { + GSList *list = li; + /* we were restarted */ + if (inv_shots == NULL) + return TRUE; + li = li->next; + g_free (shot); + inv_shots = g_slist_delete_link (inv_shots, list); + continue; + } + } else /* bad */ { + shot->y += 30; + if ( ! inv_game_over && + shot->y >= 535 && + shot->y <= 565 && + shot->x >= inv_our_x - 25 && + shot->x <= inv_our_x + 25) { + inv_lives --; + inv_draw_explosion (inv_our_x, 550); + if (inv_lives <= 0) { + inv_do_game_over (); + } else { + g_slist_foreach (inv_shots, (GFunc)g_free, NULL); + g_slist_free (inv_shots); + inv_shots = NULL; + inv_our_x = 400; + inv_do_pause = TRUE; + inv_show_status (); + return TRUE; + } + } + + if (shot->y >= 600) { + GSList *list = li; + li = li->next; + g_free (shot); + inv_shots = g_slist_delete_link (inv_shots, list); + continue; + } + } + + li = li->next; + } + + if ( ! inv_game_over) { + if (inv_left_pressed && inv_our_x > 100) { + inv_our_x -= 20; + inv_queue_draw (geginv); + } else if (inv_right_pressed && inv_our_x < 700) { + inv_our_x += 20; + inv_queue_draw (geginv); + } + } + + if (shot_inhibit > 0) + shot_inhibit--; + + if ( ! inv_game_over && inv_fire_pressed && shot_inhibit == 0) { + InvShot *shot = g_new0 (InvShot, 1); + + shot->good = TRUE; + shot->x = inv_our_x; + shot->y = 540; + + inv_shots = g_slist_prepend (inv_shots, shot); + + shot_inhibit = 5; + + inv_queue_draw (geginv); + } + + if (inv_left_released) + inv_left_pressed = FALSE; + if (inv_right_released) + inv_right_pressed = FALSE; + if (inv_fire_released) + inv_fire_pressed = FALSE; + + return TRUE; +} + +static gboolean +inv_key_press (GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + switch (event->keyval) { + case GDK_KEY_Left: + case GDK_KEY_KP_Left: + case GDK_KEY_Pointer_Left: + inv_left_pressed = TRUE; + inv_left_released = FALSE; + return TRUE; + case GDK_KEY_Right: + case GDK_KEY_KP_Right: + case GDK_KEY_Pointer_Right: + inv_right_pressed = TRUE; + inv_right_released = FALSE; + return TRUE; + case GDK_KEY_space: + case GDK_KEY_KP_Space: + inv_fire_pressed = TRUE; + inv_fire_released = FALSE; + return TRUE; + default: + break; + } + return FALSE; +} + +static gboolean +inv_key_release (GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + switch (event->keyval) { + case GDK_KEY_Left: + case GDK_KEY_KP_Left: + case GDK_KEY_Pointer_Left: + inv_left_released = TRUE; + return TRUE; + case GDK_KEY_Right: + case GDK_KEY_KP_Right: + case GDK_KEY_Pointer_Right: + inv_right_released = TRUE; + return TRUE; + case GDK_KEY_space: + case GDK_KEY_KP_Space: + inv_fire_released = TRUE; + return TRUE; + case GDK_KEY_q: + case GDK_KEY_Q: + gtk_widget_destroy (widget); + return TRUE; + case GDK_KEY_p: + case GDK_KEY_P: + inv_paused = ! inv_paused; + inv_show_status (); + return TRUE; + default: + break; + } + return FALSE; +} + +static gboolean +ensure_creatures (void) +{ + GdkPixbuf *pb, *pb1; + + if (inv_goat1 != NULL) + return TRUE; + + pb = gdk_pixbuf_new_from_resource ("/org/gimp/lebl-dialog/wanda.png", + NULL); + if (pb == NULL) + return FALSE; + + pb1 = get_phsh_frame (pb, 1); + inv_phsh1 = pb_scale (pb1, inv_factor); + g_object_unref (G_OBJECT (pb1)); + phsh_unsea (inv_phsh1); + + pb1 = get_phsh_frame (pb, 2); + inv_phsh2 = pb_scale (pb1, inv_factor); + g_object_unref (G_OBJECT (pb1)); + phsh_unsea (inv_phsh2); + + g_object_unref (G_OBJECT (pb)); + + pb = gdk_pixbuf_new_from_resource ("/org/gimp/lebl-dialog/gegl-1.png", + NULL); + if (pb == NULL) { + g_object_unref (G_OBJECT (inv_phsh1)); + g_object_unref (G_OBJECT (inv_phsh2)); + return FALSE; + } + + inv_goat1 = pb_scale (pb, inv_factor * 0.66); + g_object_unref (pb); + + pb = gdk_pixbuf_new_from_resource ("/org/gimp/lebl-dialog/gegl-2.png", + NULL); + if (pb == NULL) { + g_object_unref (G_OBJECT (inv_goat1)); + g_object_unref (G_OBJECT (inv_phsh1)); + g_object_unref (G_OBJECT (inv_phsh2)); + return FALSE; + } + + inv_goat2 = pb_scale (pb, inv_factor * 0.66); + g_object_unref (pb); + + inv_goat_width = gdk_pixbuf_get_width (inv_goat1); + inv_goat_height = gdk_pixbuf_get_height (inv_goat1); + inv_phsh_width = gdk_pixbuf_get_width (inv_phsh1); + inv_phsh_height = gdk_pixbuf_get_height (inv_phsh1); + + return TRUE; +} + +static void +geginv_destroyed (GtkWidget *w, gpointer data) +{ + geginv = NULL; +} + +static gboolean +inv_expose (GtkWidget *widget, GdkEventExpose *event) +{ + cairo_t *cr; + GdkPixbuf *goat; + GSList *li; + int i, j; + + if (geginv == NULL) { + inv_draw_idle = 0; + return TRUE; + } + + if ( ! gtk_widget_is_drawable (geginv_canvas)) + return TRUE; + + cr = gdk_cairo_create (gtk_widget_get_window (geginv_canvas)); + + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_paint (cr); + + if (inv_goat_state == 0) + goat = inv_goat1; + else + goat = inv_goat2; + + for (i = 0; i < INV_COLS; i++) { + for (j = 0; j < INV_ROWS; j++) { + int x, y; + if ( ! invs[i][j].live) + continue; + + x = invs[i][j].x*inv_factor - inv_goat_width/2, + y = invs[i][j].y*inv_factor - inv_goat_height/2, + + gdk_cairo_set_source_pixbuf (cr, goat, x, y); + cairo_rectangle (cr, + x, y, + inv_goat_width, + inv_goat_height); + cairo_fill (cr); + } + } + + for (li = inv_shots; li != NULL; li = li->next) { + InvShot *shot = li->data; + + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + cairo_rectangle (cr, + (shot->x-1)*inv_factor, + (shot->y-4)*inv_factor, + 3, 8); + cairo_fill (cr); + } + + if ( ! inv_game_over) { + GdkPixbuf *phsh; + + if (inv_phsh_state < 5) { + phsh = inv_phsh1; + } else { + phsh = inv_phsh2; + } + + gdk_cairo_set_source_pixbuf (cr, phsh, + inv_our_x*inv_factor - inv_phsh_width/2, + 550*inv_factor - inv_phsh_height/2); + cairo_rectangle (cr, + inv_our_x*inv_factor - inv_phsh_width/2, + 550*inv_factor - inv_phsh_height/2, + inv_phsh_width, + inv_phsh_height); + cairo_fill (cr); + } + + cairo_destroy (cr); + + gdk_flush (); + + if (inv_do_pause) { + g_usleep (G_USEC_PER_SEC); + inv_do_pause = FALSE; + } + + inv_draw_idle = 0; + return TRUE; +} + +gboolean gimp_lebl_dialog (void); + +gboolean +gimp_lebl_dialog (void) +{ + GtkWidget *vbox; + int i, j; + + if (geginv != NULL) { + gtk_window_present (GTK_WINDOW (geginv)); + return FALSE; + } + + inv_width = 800; + inv_height = 600; + + if (inv_width > gdk_screen_get_width (gdk_screen_get_default ()) * 0.9) { + inv_width = gdk_screen_get_width (gdk_screen_get_default ()) * 0.9; + inv_height = inv_width * (600.0/800.0); + } + + if (inv_height > gdk_screen_get_height (gdk_screen_get_default ()) * 0.9) { + inv_height = gdk_screen_get_height (gdk_screen_get_default ()) * 0.9; + inv_width = inv_height * (800.0/600.0); + } + + inv_factor = (double)inv_width / 800.0; + + if ( ! ensure_creatures ()) + return FALSE; + + geginv = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_position (GTK_WINDOW (geginv), GTK_WIN_POS_CENTER); + gtk_window_set_title (GTK_WINDOW (geginv), _("Killer GEGLs from Outer Space")); + g_object_set (G_OBJECT (geginv), "resizable", FALSE, NULL); + g_signal_connect (G_OBJECT (geginv), "destroy", + G_CALLBACK (geginv_destroyed), + NULL); + + geginv_canvas = gtk_drawing_area_new (); + gtk_widget_set_size_request (geginv_canvas, inv_width, inv_height); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (geginv), vbox); + gtk_box_pack_start (GTK_BOX (vbox), geginv_canvas, TRUE, TRUE, 0); + + geginv_label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (vbox), geginv_label, FALSE, FALSE, 0); + + inv_our_x = 400; + inv_x = 70; + inv_y = 70; + inv_first_col = 0; + inv_level = 0; + inv_lives = 3; + inv_last_col = INV_COLS-1; + inv_reverse = FALSE; + inv_game_over = FALSE; + inv_left_pressed = FALSE; + inv_right_pressed = FALSE; + inv_fire_pressed = FALSE; + inv_left_released = FALSE; + inv_right_released = FALSE; + inv_fire_released = FALSE; + inv_paused = FALSE; + + gtk_widget_add_events (geginv, GDK_KEY_RELEASE_MASK); + + g_signal_connect (G_OBJECT (geginv), "key_press_event", + G_CALLBACK (inv_key_press), NULL); + g_signal_connect (G_OBJECT (geginv), "key_release_event", + G_CALLBACK (inv_key_release), NULL); + g_signal_connect (G_OBJECT (geginv_canvas), "expose_event", + G_CALLBACK (inv_expose), NULL); + + g_slist_foreach (inv_shots, (GFunc)g_free, NULL); + g_slist_free (inv_shots); + inv_shots = NULL; + + for (i = 0; i < INV_COLS; i++) { + for (j = 0; j < INV_ROWS; j++) { + invs[i][j].live = TRUE; + invs[i][j].x = 70 + i * 100; + invs[i][j].y = 70 + j * 80; + } + } + inv_num = INV_ROWS * INV_COLS; + + g_timeout_add (((inv_num/4)+1) * 100, geginv_timeout, geginv); + g_timeout_add (90, geginv_move_timeout, geginv); + + inv_show_status (); + + gtk_widget_show_all (geginv); + return FALSE; +} diff --git a/app/dialogs/lebl-dialog.h b/app/dialogs/lebl-dialog.h new file mode 100644 index 0000000..e1d5797 --- /dev/null +++ b/app/dialogs/lebl-dialog.h @@ -0,0 +1,15397 @@ +#include + +#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)) +# define SECTION __attribute__ ((section (".gresource.lebl_dialog"), aligned (8))) +#else +# define SECTION +#endif + +static const SECTION union { const guint8 data[121880]; const double alignment; void * const ptr;} lebl_dialog_resource_data = { { + 0x47, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4b, 0x50, 0x90, 0x0b, + 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x4c, 0x00, 0xe8, 0x00, 0x00, 0x00, + 0xec, 0x00, 0x00, 0x00, 0xd4, 0xb5, 0x02, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xec, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x4c, 0x00, 0xf0, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x00, 0x00, 0x42, 0x94, 0x5a, 0xe4, + 0x06, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x76, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x08, 0xa2, 0x00, 0x00, 0xc3, 0xac, 0x6c, 0xe4, + 0x06, 0x00, 0x00, 0x00, 0x08, 0xa2, 0x00, 0x00, + 0x0a, 0x00, 0x76, 0x00, 0x18, 0xa2, 0x00, 0x00, + 0xc0, 0x78, 0x01, 0x00, 0x27, 0x85, 0xb8, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x78, 0x01, 0x00, + 0x05, 0x00, 0x4c, 0x00, 0xc8, 0x78, 0x01, 0x00, + 0xcc, 0x78, 0x01, 0x00, 0xd0, 0x92, 0xb4, 0x63, + 0x06, 0x00, 0x00, 0x00, 0xcc, 0x78, 0x01, 0x00, + 0x09, 0x00, 0x76, 0x00, 0xd8, 0x78, 0x01, 0x00, + 0x00, 0xdc, 0x01, 0x00, 0xb2, 0xd2, 0x44, 0xee, + 0x04, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x00, + 0x0c, 0x00, 0x4c, 0x00, 0x0c, 0xdc, 0x01, 0x00, + 0x18, 0xdc, 0x01, 0x00, 0x6f, 0x72, 0x67, 0x2f, + 0x04, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x67, 0x65, 0x67, 0x6c, + 0x2d, 0x31, 0x2e, 0x70, 0x6e, 0x67, 0x00, 0x00, + 0xf8, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x64, 0x6b, 0x50, 0x00, 0x00, 0xa0, 0xf8, + 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x8c, + 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x68, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc5, 0xc9, 0xc5, 0xd2, 0xd6, 0xd2, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf9, 0xfa, 0xf9, 0x74, 0x77, + 0x74, 0x56, 0x58, 0x56, 0xc3, 0xc6, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x97, 0x9a, 0x97, 0x21, 0x21, 0x21, + 0x3c, 0x3d, 0x3c, 0xaa, 0xad, 0xaa, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd1, 0xd4, 0xd1, 0x8e, 0x91, 0x8e, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, + 0xe9, 0x66, 0x68, 0x66, 0x27, 0x27, 0x27, 0x43, + 0x44, 0x43, 0x9e, 0xa2, 0x9e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, + 0x96, 0x1c, 0x1d, 0x1c, 0x8c, 0x8e, 0x8c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xbe, 0xba, + 0x49, 0x4b, 0x49, 0x5b, 0x5d, 0x5b, 0x49, 0x4a, + 0x49, 0xb0, 0xb3, 0xb0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x8a, 0x8c, 0x8a, 0x2d, 0x2e, 0x2d, + 0x5b, 0x5d, 0x5b, 0xdf, 0xe3, 0xdf, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x6c, 0x6f, 0x6c, 0x6e, + 0x71, 0x6e, 0x87, 0x8a, 0x87, 0x59, 0x5b, 0x59, + 0xbf, 0xc2, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x78, 0x7a, + 0x78, 0x5c, 0x5d, 0x5c, 0x4b, 0x4d, 0x4b, 0xc8, + 0xcb, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xac, 0xaf, 0xac, 0x5a, 0x5c, 0x5a, 0xc1, 0xc5, + 0xc1, 0x8a, 0x8d, 0x8a, 0x60, 0x62, 0x60, 0xcd, + 0xd0, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, + 0xe2, 0xdf, 0x60, 0x62, 0x60, 0x82, 0x85, 0x82, + 0x56, 0x58, 0x56, 0xa9, 0xac, 0xa9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd9, 0xdd, 0xd9, 0x5f, + 0x61, 0x5f, 0x97, 0x9a, 0x97, 0xd5, 0xd8, 0xd5, + 0x66, 0x68, 0x66, 0x86, 0x89, 0x86, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0x68, 0x6a, + 0x68, 0x9d, 0xa0, 0x9d, 0x6a, 0x6c, 0x6a, 0x90, + 0x93, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x8e, 0x90, 0x8e, 0x6e, 0x71, + 0x6e, 0xec, 0xf0, 0xec, 0xa8, 0xab, 0xa8, 0x56, + 0x59, 0x56, 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, + 0xe2, 0xde, 0x6f, 0x71, 0x6f, 0xaf, 0xb2, 0xaf, + 0x75, 0x78, 0x75, 0x73, 0x76, 0x73, 0xfc, 0xfc, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc5, + 0xc9, 0xc5, 0x51, 0x54, 0x51, 0xc8, 0xcc, 0xc8, + 0xde, 0xe2, 0xde, 0x64, 0x66, 0x64, 0xa9, 0xac, + 0xa9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xce, 0xd2, 0xce, 0x6a, 0x6c, + 0x6a, 0xc3, 0xc7, 0xc3, 0x79, 0x7b, 0x79, 0x74, + 0x76, 0x74, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xed, 0xf0, 0xed, 0x65, 0x67, + 0x65, 0x8d, 0x8f, 0x8d, 0xeb, 0xef, 0xeb, 0x98, + 0x9b, 0x98, 0x69, 0x6b, 0x69, 0xe4, 0xe8, 0xe4, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, + 0xbc, 0xb9, 0x65, 0x67, 0x65, 0xd0, 0xd3, 0xd0, + 0x7c, 0x7e, 0x7c, 0x8c, 0x8e, 0x8c, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x9a, 0x9d, 0x9a, 0x60, 0x61, 0x60, + 0xe3, 0xe6, 0xe3, 0xd4, 0xd8, 0xd4, 0x57, 0x59, + 0x57, 0xc4, 0xc7, 0xc4, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x92, 0x94, 0x92, 0x71, 0x73, + 0x71, 0xd5, 0xd9, 0xd5, 0x61, 0x64, 0x61, 0xa3, + 0xa6, 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xd3, + 0xd0, 0x54, 0x55, 0x54, 0xc8, 0xcb, 0xc8, 0xff, + 0xff, 0xff, 0x70, 0x72, 0x70, 0x91, 0x94, 0x91, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x60, + 0x62, 0x60, 0x92, 0x94, 0x92, 0xd8, 0xdb, 0xd8, + 0x51, 0x52, 0x51, 0xb1, 0xb4, 0xb1, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd0, 0xd3, 0xd0, 0xe1, 0xe5, + 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x59, 0x5b, 0x59, + 0xb4, 0xb7, 0xb4, 0xff, 0xff, 0xff, 0x9b, 0x9e, + 0x9b, 0x5f, 0x61, 0x5f, 0xd6, 0xda, 0xd6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9e, 0xa0, 0x9e, 0x59, 0x5b, 0x59, 0xcd, 0xd1, + 0xcd, 0xca, 0xce, 0xca, 0x57, 0x59, 0x57, 0xc7, + 0xca, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xba, 0xbd, 0xba, 0x9c, 0x9e, 0x9c, 0xd2, 0xd5, + 0xd2, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0x5e, + 0x60, 0x5e, 0xa5, 0xa8, 0xa5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x66, 0x68, 0x66, 0xb4, 0xb7, 0xb4, 0xff, + 0xff, 0xff, 0xc7, 0xcb, 0xc7, 0x5f, 0x60, 0x5f, + 0xbb, 0xbd, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xbb, 0xbf, 0xbb, 0x63, 0x65, 0x63, 0xac, + 0xaf, 0xac, 0xeb, 0xef, 0xeb, 0x9b, 0x9d, 0x9b, + 0x70, 0x72, 0x70, 0xe9, 0xec, 0xe9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd9, 0xdd, 0xd9, 0x6a, 0x6c, 0x6a, 0x3a, + 0x3b, 0x3a, 0x88, 0x8a, 0x88, 0xdc, 0xe0, 0xdc, + 0x8a, 0x8d, 0x8a, 0x2b, 0x2b, 0x2b, 0x9a, 0x9c, + 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xdd, 0xe1, 0xdd, 0x57, 0x59, 0x57, + 0xbc, 0xc0, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc0, 0xc3, 0xc0, 0xdf, 0xe3, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa4, 0xa7, 0xa4, 0x66, 0x68, 0x66, + 0xa8, 0xab, 0xa8, 0xf2, 0xf6, 0xf2, 0xe0, 0xe4, + 0xe0, 0x69, 0x6b, 0x69, 0xa7, 0xa9, 0xa7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xc2, 0xbf, + 0x5a, 0x5c, 0x5a, 0x66, 0x68, 0x66, 0x66, 0x68, + 0x66, 0x8c, 0x8e, 0x8c, 0x50, 0x52, 0x50, 0x4d, + 0x4f, 0x4d, 0x9e, 0xa1, 0x9e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb6, 0xb9, + 0xb6, 0x61, 0x64, 0x61, 0xdc, 0xe0, 0xdc, 0xff, + 0xff, 0xff, 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xf0, 0xee, 0x98, 0x9b, 0x98, 0x57, 0x59, + 0x57, 0xa8, 0xab, 0xa8, 0xed, 0xf1, 0xed, 0xf4, + 0xf8, 0xf4, 0xb1, 0xb4, 0xb1, 0x57, 0x59, 0x57, + 0xd7, 0xdb, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa7, 0xaa, 0xa7, 0x71, 0x74, 0x71, 0xa2, + 0xa4, 0xa2, 0x2a, 0x2b, 0x2a, 0x47, 0x49, 0x47, + 0xa2, 0xa5, 0xa2, 0x72, 0x74, 0x72, 0x9e, 0xa1, + 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xba, 0xbe, 0xba, 0xa3, 0xa6, 0xa3, + 0xee, 0xf2, 0xee, 0xe1, 0xe4, 0xe1, 0x82, 0x85, + 0x82, 0x80, 0x83, 0x80, 0xba, 0xbd, 0xba, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe3, 0xdf, + 0x95, 0x97, 0x95, 0xc5, 0xc8, 0xc5, 0xff, 0xff, + 0xff, 0xdd, 0xe0, 0xdd, 0x85, 0x88, 0x85, 0x5b, + 0x5d, 0x5b, 0xa9, 0xab, 0xa9, 0xec, 0xf0, 0xec, + 0xf8, 0xfc, 0xf8, 0xe3, 0xe7, 0xe3, 0x76, 0x78, + 0x76, 0x7d, 0x80, 0x7d, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x82, 0x7f, + 0x7a, 0x7d, 0x7a, 0xc7, 0xcb, 0xc7, 0x64, 0x66, + 0x64, 0x8d, 0x90, 0x8d, 0xdf, 0xe3, 0xdf, 0x7b, + 0x7d, 0x7b, 0x9a, 0x9d, 0x9a, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xe0, + 0xdc, 0xe4, 0xe8, 0xe4, 0xac, 0xae, 0xac, 0x9f, + 0xa2, 0x9f, 0xae, 0xb1, 0xae, 0x7b, 0x7d, 0x7b, + 0x54, 0x56, 0x54, 0xb6, 0xb9, 0xb6, 0xff, 0xff, + 0xff, 0xe0, 0xe4, 0xe0, 0x7a, 0x7c, 0x7a, 0x5d, + 0x5f, 0x5d, 0x9d, 0xa0, 0x9d, 0xcc, 0xd0, 0xcc, + 0x8b, 0x8d, 0x8b, 0xc5, 0xc9, 0xc5, 0xf3, 0xf6, + 0xf3, 0xf8, 0xfc, 0xf8, 0xee, 0xf2, 0xee, 0x92, + 0x95, 0x92, 0x59, 0x5b, 0x59, 0xc2, 0xc6, 0xc2, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, + 0xf5, 0x5b, 0x5d, 0x5b, 0xa2, 0xa5, 0xa2, 0xf0, + 0xf4, 0xf0, 0xe8, 0xec, 0xe8, 0xee, 0xf2, 0xee, + 0xf2, 0xf6, 0xf2, 0x7c, 0x7e, 0x7c, 0x8f, 0x92, + 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, + 0xc1, 0xbe, 0x84, 0x87, 0x84, 0xc5, 0xc9, 0xc5, + 0x63, 0x65, 0x63, 0x98, 0x9b, 0x98, 0xf5, 0xf9, + 0xf5, 0xe3, 0xe7, 0xe3, 0x60, 0x61, 0x60, 0x4d, + 0x4f, 0x4d, 0xaa, 0xad, 0xaa, 0xef, 0xf3, 0xef, + 0xe9, 0xed, 0xe9, 0xa0, 0xa3, 0xa0, 0x4f, 0x51, + 0x4f, 0xd6, 0xd9, 0xd6, 0xed, 0xf1, 0xed, 0xf6, + 0xfa, 0xf6, 0xf4, 0xf8, 0xf4, 0xdd, 0xe1, 0xdd, + 0x99, 0x9c, 0x99, 0x4e, 0x50, 0x4e, 0xa2, 0xa5, + 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc6, 0xc9, 0xc6, 0x57, 0x59, 0x57, + 0xc1, 0xc4, 0xc1, 0xeb, 0xee, 0xeb, 0xeb, 0xef, + 0xeb, 0xeb, 0xef, 0xeb, 0xe9, 0xed, 0xe9, 0x7f, + 0x81, 0x7f, 0x9f, 0xa1, 0x9f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc6, 0xc9, 0xc6, 0x6a, 0x6c, 0x6a, 0x8c, 0x8f, + 0x8c, 0xc6, 0xca, 0xc6, 0x63, 0x65, 0x63, 0xbb, + 0xbe, 0xbb, 0xf8, 0xfc, 0xf8, 0xeb, 0xef, 0xeb, + 0x70, 0x72, 0x70, 0x1f, 0x1f, 0x1f, 0x99, 0x9c, + 0x99, 0xf1, 0xf5, 0xf1, 0xf8, 0xfc, 0xf8, 0xce, + 0xd1, 0xce, 0x55, 0x57, 0x55, 0xcc, 0xd0, 0xcc, + 0xf7, 0xfb, 0xf7, 0xe7, 0xeb, 0xe7, 0xaa, 0xac, + 0xaa, 0x6c, 0x6e, 0x6c, 0x56, 0x58, 0x56, 0xa5, + 0xa8, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x95, 0x97, + 0x95, 0x31, 0x32, 0x31, 0x69, 0x6b, 0x69, 0x7a, + 0x7c, 0x7a, 0x79, 0x7a, 0x79, 0x79, 0x7b, 0x79, + 0x89, 0x8b, 0x89, 0x69, 0x6b, 0x69, 0x94, 0x97, + 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdc, 0xdf, 0xdc, 0x72, 0x74, 0x72, 0x8f, + 0x92, 0x8f, 0xe5, 0xe8, 0xe5, 0xdb, 0xde, 0xdb, + 0x61, 0x63, 0x61, 0xa7, 0xab, 0xa7, 0xf0, 0xf4, + 0xf0, 0xc5, 0xc8, 0xc5, 0x4a, 0x4c, 0x4a, 0x2b, + 0x2b, 0x2b, 0xaa, 0xad, 0xaa, 0xf0, 0xf4, 0xf0, + 0xf6, 0xfa, 0xf6, 0xcc, 0xd0, 0xcc, 0x55, 0x57, + 0x55, 0xbd, 0xc1, 0xbd, 0xb1, 0xb4, 0xb1, 0x6e, + 0x6f, 0x6e, 0x59, 0x5a, 0x59, 0x8d, 0x8f, 0x8d, + 0xcb, 0xcf, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0xfa, 0xf9, 0x72, 0x75, 0x72, 0x57, 0x59, 0x57, + 0x77, 0x79, 0x77, 0x87, 0x89, 0x87, 0x83, 0x85, + 0x83, 0x85, 0x88, 0x85, 0x90, 0x92, 0x90, 0x6b, + 0x6d, 0x6b, 0x95, 0x98, 0x95, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd2, 0xd6, 0xd2, 0x76, 0x79, 0x76, + 0x7f, 0x82, 0x7f, 0xe0, 0xe3, 0xe0, 0xff, 0xff, + 0xff, 0xed, 0xf0, 0xed, 0x6d, 0x70, 0x6d, 0x76, + 0x79, 0x76, 0x94, 0x97, 0x94, 0x5d, 0x5f, 0x5d, + 0x65, 0x67, 0x65, 0x68, 0x6b, 0x68, 0x5b, 0x5d, + 0x5b, 0xa3, 0xa6, 0xa3, 0xbc, 0xbf, 0xbc, 0x88, + 0x8b, 0x88, 0x47, 0x49, 0x47, 0xad, 0xb0, 0xad, + 0x76, 0x79, 0x76, 0x87, 0x8a, 0x87, 0xd1, 0xd4, + 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xca, 0xcd, 0xca, 0x5c, 0x5e, + 0x5c, 0xc7, 0xca, 0xc7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x71, 0x73, 0x71, 0x9e, 0xa1, + 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0x74, 0x76, + 0x74, 0x73, 0x75, 0x73, 0xd9, 0xdd, 0xd9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa5, 0xa8, 0xa5, 0x58, 0x5a, 0x58, 0x6b, 0x6d, + 0x6b, 0x9a, 0x9d, 0x9a, 0xd6, 0xd9, 0xd6, 0xd7, + 0xda, 0xd7, 0x8d, 0x90, 0x8d, 0x66, 0x68, 0x66, + 0x62, 0x64, 0x62, 0x54, 0x56, 0x54, 0x3f, 0x40, + 0x3f, 0xab, 0xae, 0xab, 0xd7, 0xdb, 0xd7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, + 0xa4, 0xa2, 0x5b, 0x5d, 0x5b, 0xf2, 0xf3, 0xf2, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6a, + 0x6c, 0x6a, 0x9c, 0xa0, 0x9c, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe1, 0xdd, 0x68, + 0x6a, 0x68, 0x63, 0x65, 0x63, 0xcc, 0xd0, 0xcc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, + 0xd6, 0xd2, 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdb, 0xdf, 0xdb, 0xc6, 0xca, 0xc6, 0x8c, + 0x8f, 0x8c, 0x36, 0x36, 0x36, 0x60, 0x62, 0x60, + 0xda, 0xde, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf9, 0xfa, 0xf9, 0x79, 0x7b, 0x79, 0x80, 0x83, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x5d, 0x5f, 0x5d, 0xa1, 0xa4, + 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, + 0x6f, 0x71, 0x6f, 0x63, 0x65, 0x63, 0xcb, 0xcf, + 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa3, 0xa7, 0xa3, 0x56, 0x59, 0x56, 0x8d, 0x90, + 0x8d, 0x59, 0x5c, 0x59, 0xac, 0xaf, 0xac, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd6, 0xd9, 0xd6, 0x68, + 0x6a, 0x68, 0xab, 0xae, 0xab, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, + 0x66, 0x64, 0xb1, 0xb5, 0xb1, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, + 0xe9, 0x79, 0x7b, 0x79, 0x5b, 0x5d, 0x5b, 0xcd, + 0xd1, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc4, 0xc7, 0xc4, 0x4f, 0x51, 0x4f, 0x8d, + 0x8f, 0x8d, 0xe1, 0xe5, 0xe1, 0x81, 0x84, 0x81, + 0x7a, 0x7d, 0x7a, 0xf1, 0xf3, 0xf1, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb4, 0xb7, 0xb4, 0x67, 0x69, 0x67, 0xcd, 0xd1, + 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe1, 0xe5, 0xe1, 0x63, 0x64, 0x63, 0xbc, 0xbf, + 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x96, 0x99, 0x96, 0x65, 0x68, 0x65, + 0xc3, 0xc7, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd1, 0xd5, 0xd1, 0x6b, 0x6d, 0x6b, + 0x8f, 0x92, 0x8f, 0xf1, 0xf3, 0xf1, 0xff, 0xff, + 0xff, 0xbb, 0xbe, 0xbb, 0x63, 0x66, 0x63, 0xc8, + 0xcb, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x86, 0x89, 0x86, 0x81, + 0x84, 0x81, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcd, 0xd1, 0xcd, 0x59, + 0x5a, 0x59, 0xcb, 0xcf, 0xcb, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xb0, 0xb4, 0xb0, 0x4d, 0x4f, + 0x4d, 0xad, 0xb0, 0xad, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xcd, 0xd1, 0xcd, 0x68, 0x6a, + 0x68, 0x66, 0x68, 0x66, 0xe9, 0xec, 0xe9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0xe8, 0xe4, + 0x72, 0x74, 0x72, 0x8d, 0x8f, 0x8d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, 0xe9, + 0x5b, 0x5c, 0x5b, 0xa4, 0xa6, 0xa4, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbd, 0xc0, 0xbd, 0x4f, 0x50, 0x4f, 0xd0, 0xd4, + 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd5, 0xd2, 0x62, + 0x64, 0x62, 0x9f, 0xa2, 0x9f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, + 0x7e, 0x7b, 0x67, 0x69, 0x67, 0xc7, 0xcb, 0xc7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa0, 0xa3, 0xa0, 0x57, + 0x59, 0x57, 0xdf, 0xe3, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc4, 0xc7, 0xc4, 0x4f, 0x51, 0x4f, 0xc4, + 0xc8, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbe, 0xbb, 0x65, + 0x67, 0x65, 0xdc, 0xe0, 0xdc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x96, 0x98, 0x96, 0x5b, 0x5d, 0x5b, 0xfc, 0xfc, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x99, 0x9c, 0x99, 0x5c, 0x5e, 0x5c, 0xc5, 0xc9, + 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xd2, 0xcf, 0x52, 0x54, 0x52, 0xb2, 0xb5, + 0xb2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x9d, 0xa0, 0x9d, + 0x6f, 0x71, 0x6f, 0xed, 0xf0, 0xed, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa9, 0xac, 0xa9, 0x7a, 0x7c, 0x7a, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x85, 0x87, 0x85, 0x7e, + 0x81, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, + 0xfc, 0x92, 0x95, 0x92, 0x42, 0x43, 0x42, 0xac, + 0xaf, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, + 0x95, 0x92, 0x6a, 0x6c, 0x6a, 0xdd, 0xe0, 0xdd, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, + 0xe9, 0x6e, 0x70, 0x6e, 0xa2, 0xa5, 0xa2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x8b, 0x8e, 0x8b, 0x7b, + 0x7e, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x88, 0x8a, 0x88, 0x89, 0x8c, 0x89, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa7, 0xaa, 0xa7, 0x58, 0x5a, 0x58, + 0x50, 0x51, 0x50, 0xb3, 0xb7, 0xb3, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd9, 0xdd, 0xd9, 0x63, 0x65, + 0x63, 0x9c, 0x9f, 0x9c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc4, 0xc8, 0xc4, 0x5c, 0x5e, 0x5c, + 0xd0, 0xd4, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x81, 0x7e, 0x89, 0x8c, 0x89, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, + 0xba, 0xbd, 0xba, 0xa5, 0xa8, 0xa5, 0xb7, 0xba, + 0xb7, 0xc0, 0xc4, 0xc0, 0xcf, 0xd3, 0xcf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x84, 0x86, 0x84, 0x78, + 0x7a, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc7, 0xcb, 0xc7, 0x53, 0x55, + 0x53, 0x94, 0x97, 0x94, 0x8b, 0x8d, 0x8b, 0x69, + 0x6b, 0x69, 0xdf, 0xe3, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa1, 0xa4, 0xa1, 0x61, 0x63, 0x61, + 0xba, 0xbd, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x9e, + 0x9b, 0x6e, 0x70, 0x6e, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7e, 0x7b, 0xa6, + 0xa9, 0xa6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd6, 0xda, 0xd6, 0x8e, 0x91, + 0x8e, 0x58, 0x5a, 0x58, 0x5a, 0x5c, 0x5a, 0x72, + 0x74, 0x72, 0x6f, 0x71, 0x6f, 0x5c, 0x5e, 0x5c, + 0x53, 0x54, 0x53, 0x90, 0x93, 0x90, 0xed, 0xf0, + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x90, 0x93, 0x90, 0x64, 0x67, 0x64, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbe, 0xbb, 0x56, + 0x58, 0x56, 0x82, 0x85, 0x82, 0xe4, 0xe8, 0xe4, + 0xda, 0xde, 0xda, 0x55, 0x58, 0x55, 0xa5, 0xa9, + 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, + 0xf5, 0x91, 0x94, 0x91, 0x5e, 0x60, 0x5e, 0xb1, + 0xb4, 0xb1, 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, + 0xe4, 0xe0, 0x73, 0x76, 0x73, 0x9b, 0x9e, 0x9b, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe1, 0xdd, + 0x64, 0x67, 0x64, 0xb8, 0xbc, 0xb8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd7, 0xdb, 0xd7, 0x95, 0x98, 0x95, 0x5d, + 0x5f, 0x5d, 0x6a, 0x6d, 0x6a, 0xa7, 0xaa, 0xa7, + 0xd5, 0xd9, 0xd5, 0xff, 0xff, 0xff, 0xf9, 0xfa, + 0xf9, 0xd6, 0xda, 0xd6, 0xa5, 0xa8, 0xa5, 0x55, + 0x57, 0x55, 0xa1, 0xa4, 0xa1, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbc, 0xbf, 0xbc, 0x53, + 0x55, 0x53, 0xd2, 0xd6, 0xd2, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xcd, 0xca, + 0x66, 0x69, 0x66, 0x75, 0x78, 0x75, 0xe1, 0xe4, + 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, + 0x93, 0x90, 0x6e, 0x70, 0x6e, 0xd4, 0xd8, 0xd4, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xe4, 0xe1, + 0xa1, 0xa4, 0xa1, 0x57, 0x59, 0x57, 0x94, 0x96, + 0x94, 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc5, 0xc8, 0xc5, 0x67, 0x69, + 0x67, 0xc3, 0xc6, 0xc3, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd2, 0xd6, 0xd2, 0x55, 0x57, 0x55, 0xbf, + 0xc3, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, 0xe9, 0xc5, + 0xc9, 0xc5, 0x9b, 0x9e, 0x9b, 0x6e, 0x70, 0x6e, + 0x6d, 0x6f, 0x6d, 0xa1, 0xa4, 0xa1, 0xdd, 0xe1, + 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x8e, 0x90, 0x8e, 0x6e, 0x70, + 0x6e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe4, 0xe0, 0x5b, 0x5d, 0x5b, 0x9a, 0x9c, + 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, + 0xf5, 0x72, 0x74, 0x72, 0x7d, 0x7f, 0x7d, 0xd8, + 0xdb, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0x61, 0x63, + 0x61, 0x9d, 0xa0, 0x9d, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, + 0xa7, 0xa3, 0x61, 0x63, 0x61, 0x79, 0x7b, 0x79, + 0xd5, 0xd8, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, + 0x9d, 0x9a, 0x73, 0x76, 0x73, 0xe0, 0xe4, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc6, 0xc3, + 0x59, 0x5b, 0x59, 0xd0, 0xd4, 0xd0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xe9, 0xec, + 0xe9, 0xdc, 0xe0, 0xdc, 0xdb, 0xdf, 0xdb, 0xdb, + 0xdf, 0xdb, 0xdb, 0xdf, 0xdb, 0xdb, 0xdf, 0xdb, + 0xd8, 0xdc, 0xd8, 0xcd, 0xd0, 0xcd, 0xca, 0xcd, + 0xca, 0xca, 0xcd, 0xca, 0xca, 0xcd, 0xca, 0xca, + 0xcd, 0xca, 0xd0, 0xd4, 0xd0, 0xdb, 0xdf, 0xdb, + 0xdb, 0xdf, 0xdb, 0xdb, 0xdf, 0xdb, 0xdb, 0xdf, + 0xdb, 0xdb, 0xdf, 0xdb, 0xdb, 0xdf, 0xdb, 0xdb, + 0xdf, 0xdb, 0xdb, 0xdf, 0xdb, 0xce, 0xd2, 0xce, + 0xc9, 0xcc, 0xc9, 0xbc, 0xbf, 0xbc, 0xb5, 0xb9, + 0xb5, 0xaa, 0xad, 0xaa, 0xa7, 0xaa, 0xa7, 0xa4, + 0xa7, 0xa4, 0x90, 0x93, 0x90, 0x75, 0x77, 0x75, + 0x68, 0x6a, 0x68, 0x5b, 0x5d, 0x5b, 0x6d, 0x70, + 0x6d, 0xa0, 0xa3, 0xa0, 0xd3, 0xd6, 0xd3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, + 0xb6, 0xb3, 0x4b, 0x4c, 0x4b, 0xe0, 0xe3, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, + 0xa6, 0xa3, 0x5f, 0x61, 0x5f, 0xd1, 0xd4, 0xd1, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, + 0xd8, 0xd4, 0x85, 0x88, 0x85, 0x77, 0x79, 0x77, + 0xd9, 0xdd, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa1, 0xa4, 0xa1, 0x60, 0x62, 0x60, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcf, + 0xcc, 0x71, 0x73, 0x71, 0x5c, 0x5d, 0x5c, 0x8b, + 0x8e, 0x8b, 0xbb, 0xbe, 0xbb, 0xdb, 0xdf, 0xdb, + 0xe2, 0xe5, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe0, 0xe3, 0xe0, 0xdc, 0xe0, 0xdc, 0xdc, + 0xe0, 0xdc, 0xcd, 0xd1, 0xcd, 0xbc, 0xbf, 0xbc, + 0xaf, 0xb3, 0xaf, 0x5f, 0x60, 0x5f, 0x9c, 0x9e, + 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xab, 0xae, 0xab, 0x4b, 0x4d, 0x4b, 0x95, + 0x98, 0x95, 0x95, 0x98, 0x95, 0x84, 0x86, 0x84, + 0x7a, 0x7d, 0x7a, 0x7f, 0x81, 0x7f, 0x81, 0x83, + 0x81, 0x80, 0x83, 0x80, 0x73, 0x75, 0x73, 0x6d, + 0x6f, 0x6d, 0x65, 0x67, 0x65, 0x5a, 0x5c, 0x5a, + 0x5c, 0x5f, 0x5c, 0x63, 0x66, 0x63, 0x63, 0x66, + 0x63, 0x63, 0x66, 0x63, 0x67, 0x6a, 0x67, 0x6c, + 0x6e, 0x6c, 0x6c, 0x6e, 0x6c, 0x6c, 0x6e, 0x6c, + 0x6c, 0x6e, 0x6c, 0x6a, 0x6c, 0x6a, 0x64, 0x66, + 0x64, 0x6d, 0x70, 0x6d, 0x65, 0x67, 0x65, 0x63, + 0x66, 0x63, 0x63, 0x66, 0x63, 0x63, 0x66, 0x63, + 0x63, 0x66, 0x63, 0x63, 0x66, 0x63, 0x65, 0x67, + 0x65, 0x62, 0x64, 0x62, 0x65, 0x67, 0x65, 0x62, + 0x63, 0x62, 0x5d, 0x5e, 0x5d, 0x5a, 0x5c, 0x5a, + 0x64, 0x66, 0x64, 0x6d, 0x70, 0x6d, 0x84, 0x86, + 0x84, 0x8a, 0x8d, 0x8a, 0xa2, 0xa5, 0xa2, 0xc7, + 0xcb, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xcb, 0x57, 0x59, + 0x57, 0xd6, 0xda, 0xd6, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x6b, 0x6e, + 0x6b, 0x73, 0x75, 0x73, 0xbf, 0xc2, 0xbf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xbf, 0xbc, 0x5e, 0x5f, 0x5e, 0x65, 0x67, + 0x65, 0xd8, 0xdc, 0xd8, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xde, + 0xda, 0x61, 0x62, 0x61, 0xaf, 0xb2, 0xaf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, + 0xa1, 0xa4, 0xa1, 0x6b, 0x6e, 0x6b, 0x60, 0x61, + 0x60, 0x64, 0x66, 0x64, 0x6e, 0x70, 0x6e, 0x86, + 0x89, 0x86, 0x94, 0x97, 0x94, 0x94, 0x96, 0x94, + 0x94, 0x96, 0x94, 0x91, 0x94, 0x91, 0x91, 0x94, + 0x91, 0x91, 0x94, 0x91, 0x93, 0x96, 0x93, 0x92, + 0x95, 0x92, 0x84, 0x86, 0x84, 0x68, 0x6b, 0x68, + 0x66, 0x68, 0x66, 0x6e, 0x70, 0x6e, 0x6a, 0x6d, + 0x6a, 0x60, 0x62, 0x60, 0x50, 0x52, 0x50, 0x4b, + 0x4c, 0x4b, 0xc7, 0xcb, 0xc7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, + 0x43, 0x44, 0x43, 0x6f, 0x72, 0x6f, 0x76, 0x79, + 0x76, 0x76, 0x78, 0x76, 0x75, 0x78, 0x75, 0x83, + 0x86, 0x83, 0x8a, 0x8c, 0x8a, 0x94, 0x97, 0x94, + 0xa5, 0xa8, 0xa5, 0xac, 0xaf, 0xac, 0xab, 0xae, + 0xab, 0xb2, 0xb5, 0xb2, 0xb8, 0xbb, 0xb8, 0xbf, + 0xc3, 0xbf, 0xbf, 0xc3, 0xbf, 0xbf, 0xc3, 0xbf, + 0xc6, 0xca, 0xc6, 0xd4, 0xd8, 0xd4, 0xd7, 0xda, + 0xd7, 0xd7, 0xda, 0xd7, 0xd7, 0xda, 0xd7, 0xd5, + 0xd8, 0xd5, 0xc9, 0xcc, 0xc9, 0xca, 0xcd, 0xca, + 0xc1, 0xc4, 0xc1, 0xbf, 0xc3, 0xbf, 0xbf, 0xc3, + 0xbf, 0xbf, 0xc3, 0xbf, 0xbf, 0xc3, 0xbf, 0xbf, + 0xc3, 0xbf, 0xc1, 0xc4, 0xc1, 0xc7, 0xcb, 0xc7, + 0xd0, 0xd3, 0xd0, 0xd7, 0xda, 0xd7, 0xd7, 0xda, + 0xd7, 0xd9, 0xdd, 0xd9, 0xde, 0xe2, 0xde, 0xe4, + 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, + 0xd6, 0xd2, 0x5a, 0x5c, 0x5a, 0xcb, 0xcf, 0xcb, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc1, 0xc5, 0xc1, 0x70, 0x72, 0x70, + 0x58, 0x5a, 0x58, 0x7a, 0x7c, 0x7a, 0xa5, 0xa8, + 0xa5, 0x93, 0x97, 0x93, 0x4f, 0x51, 0x4f, 0x38, + 0x39, 0x38, 0x63, 0x65, 0x63, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0x92, 0x90, + 0x6b, 0x6d, 0x6b, 0xe1, 0xe4, 0xe1, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, + 0xe1, 0xdd, 0xc9, 0xcc, 0xc9, 0xa1, 0xa4, 0xa1, + 0x99, 0x9b, 0x99, 0x97, 0x9a, 0x97, 0x81, 0x84, + 0x81, 0x7f, 0x81, 0x7f, 0x7f, 0x81, 0x7f, 0x73, + 0x76, 0x73, 0x73, 0x76, 0x73, 0x73, 0x76, 0x73, + 0x7c, 0x7e, 0x7c, 0x87, 0x8a, 0x87, 0x99, 0x9c, + 0x99, 0x99, 0x9c, 0x99, 0xa4, 0xa7, 0xa4, 0xb1, + 0xb4, 0xb1, 0xc3, 0xc7, 0xc3, 0xc3, 0xc6, 0xc3, + 0x70, 0x73, 0x70, 0x7b, 0x7d, 0x7b, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x82, 0x85, 0x82, 0x7d, 0x80, 0x7d, 0xdf, + 0xe3, 0xdf, 0xf1, 0xf3, 0xf1, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xba, 0xbe, 0xba, 0x52, 0x54, + 0x52, 0xdb, 0xdf, 0xdb, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc9, 0xcc, 0xc9, 0x87, + 0x8a, 0x87, 0x63, 0x65, 0x63, 0x52, 0x54, 0x52, + 0x72, 0x74, 0x72, 0xa5, 0xa8, 0xa5, 0x4e, 0x50, + 0x4e, 0xd9, 0xdd, 0xd9, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd9, 0xdc, 0xd9, 0x53, 0x55, 0x53, 0xa8, + 0xab, 0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc4, 0xc8, 0xc4, 0x55, 0x58, 0x55, 0xbc, + 0xbf, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xdd, 0xe1, 0xdd, 0x70, 0x73, 0x70, + 0xa5, 0xa8, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb8, 0xbb, 0xb8, 0x6b, + 0x6d, 0x6b, 0x7e, 0x81, 0x7e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xda, + 0xd6, 0x7b, 0x7e, 0x7b, 0x7d, 0x7f, 0x7d, 0xc3, + 0xc7, 0xc3, 0x52, 0x54, 0x52, 0xc5, 0xc9, 0xc5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x77, 0x7a, 0x77, 0x75, 0x78, 0x75, 0xf5, 0xf7, + 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, + 0x6c, 0x6e, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xd1, + 0xce, 0x68, 0x6a, 0x68, 0xc0, 0xc3, 0xc0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd4, 0xd7, 0xd4, 0x90, 0x92, 0x90, + 0x5b, 0x5d, 0x5b, 0x81, 0x84, 0x81, 0xd6, 0xd9, + 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xda, 0xde, 0xda, 0x5e, 0x60, 0x5e, + 0xb4, 0xb7, 0xb4, 0xde, 0xe1, 0xde, 0x58, 0x5a, + 0x58, 0x9e, 0xa1, 0x9e, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa7, 0xaa, 0xa7, 0x59, + 0x5b, 0x59, 0xcf, 0xd3, 0xcf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xdc, + 0xd9, 0x6a, 0x6c, 0x6a, 0x9a, 0x9c, 0x9a, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xb2, 0xb5, 0xb2, 0x69, 0x6b, 0x69, + 0xd3, 0xd7, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0x9b, 0x9e, 0x9b, 0x60, 0x62, + 0x60, 0x6b, 0x6e, 0x6b, 0xb8, 0xbb, 0xb8, 0xed, + 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0xa3, + 0xa0, 0x5b, 0x5d, 0x5b, 0xe5, 0xe8, 0xe5, 0xec, + 0xf0, 0xec, 0x73, 0x75, 0x73, 0x92, 0x95, 0x92, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd4, 0xd7, 0xd4, 0x5d, 0x5e, 0x5d, 0x9f, 0xa2, + 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x9a, 0x9d, 0x9a, 0x64, 0x66, 0x64, + 0xd3, 0xd6, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0x8d, + 0x8b, 0x73, 0x75, 0x73, 0xf5, 0xf7, 0xf5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xcc, 0xd0, 0xcc, 0x74, 0x76, 0x74, 0x62, + 0x64, 0x62, 0xa1, 0xa4, 0xa1, 0xe0, 0xe4, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, + 0xfa, 0xf9, 0x76, 0x78, 0x76, 0x8c, 0x8e, 0x8c, + 0xf6, 0xfa, 0xf6, 0xf4, 0xf8, 0xf4, 0x91, 0x94, + 0x91, 0x82, 0x85, 0x82, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x82, + 0x85, 0x82, 0x75, 0x78, 0x75, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe9, 0xec, 0xe9, 0x61, 0x63, + 0x61, 0x95, 0x98, 0x95, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x75, 0x77, 0x75, 0x87, 0x8a, 0x87, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf1, 0xf3, 0xf1, 0x7c, 0x7f, 0x7c, + 0x68, 0x6a, 0x68, 0xd8, 0xdb, 0xd8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd8, 0xdc, 0xd8, 0x61, 0x63, + 0x61, 0xae, 0xb0, 0xae, 0xf8, 0xfc, 0xf8, 0xf4, + 0xf8, 0xf4, 0x91, 0x95, 0x91, 0x79, 0x7b, 0x79, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xb5, 0xb8, 0xb5, 0x56, 0x59, + 0x56, 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xac, + 0xaf, 0xac, 0x59, 0x5c, 0x59, 0xce, 0xd2, 0xce, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x5d, 0x5f, + 0x5d, 0xa5, 0xa8, 0xa5, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xc9, + 0xc6, 0x60, 0x62, 0x60, 0xb6, 0xba, 0xb6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, + 0xb7, 0xb4, 0x5f, 0x61, 0x5f, 0xd4, 0xd7, 0xd4, + 0xf8, 0xfc, 0xf8, 0xe1, 0xe5, 0xe1, 0x6c, 0x6e, + 0x6c, 0x9f, 0xa2, 0x9f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, + 0xd8, 0xd4, 0x57, 0x59, 0x57, 0xbc, 0xc0, 0xbc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd7, 0xdb, 0xd7, 0x61, 0x62, 0x61, 0x9e, 0xa1, + 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, + 0xc7, 0xc3, 0x56, 0x58, 0x56, 0xce, 0xd1, 0xce, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa0, 0xa3, 0xa0, 0x5a, 0x5b, 0x5a, + 0xdc, 0xdf, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x93, 0x97, 0x93, 0x70, 0x72, + 0x70, 0xe4, 0xe7, 0xe4, 0xef, 0xf3, 0xef, 0xa2, + 0xa6, 0xa2, 0x55, 0x57, 0x55, 0xd5, 0xd8, 0xd5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6e, 0x71, + 0x6e, 0x97, 0x9a, 0x97, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa1, 0xa4, 0xa1, 0x5b, + 0x5d, 0x5b, 0xdb, 0xdf, 0xdb, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xa0, 0xa3, 0xa0, 0x69, 0x6b, + 0x69, 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa4, 0xa7, + 0xa4, 0x5c, 0x5e, 0x5c, 0xdc, 0xdf, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, + 0x77, 0x74, 0x85, 0x87, 0x85, 0xec, 0xf0, 0xec, + 0xbe, 0xc2, 0xbe, 0x55, 0x58, 0x55, 0x8a, 0x8d, + 0x8a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x97, 0x9a, 0x97, 0x84, 0x86, 0x84, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xde, 0xdb, + 0x6b, 0x6d, 0x6b, 0x8d, 0x90, 0x8d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, + 0x81, 0x7e, 0x83, 0x86, 0x83, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xb8, 0xbc, 0xb8, 0x68, 0x6a, 0x68, + 0xd8, 0xdc, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x5b, 0x5d, 0x5b, 0x9b, 0x9e, + 0x9b, 0xbf, 0xc3, 0xbf, 0x60, 0x62, 0x60, 0x93, + 0x96, 0x93, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xaa, + 0xa7, 0x69, 0x6b, 0x69, 0xed, 0xf0, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x9c, 0x9f, 0x9c, 0x64, 0x65, 0x64, 0xd2, + 0xd5, 0xd2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe2, 0xdf, 0x64, 0x66, 0x64, 0xa4, 0xa7, + 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xd3, + 0xd0, 0x66, 0x68, 0x66, 0xc3, 0xc7, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xda, 0xdd, 0xda, 0x4a, + 0x4c, 0x4a, 0x82, 0x85, 0x82, 0x66, 0x69, 0x66, + 0x93, 0x95, 0x93, 0xed, 0xf0, 0xed, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa7, 0xaa, 0xa7, 0x65, 0x66, 0x65, + 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xed, 0xf0, 0xed, 0x63, 0x65, 0x63, + 0x93, 0x96, 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc7, 0xcb, 0xc7, 0x55, + 0x57, 0x55, 0xc8, 0xcb, 0xc8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd3, 0xd7, 0xd3, 0x5a, 0x5c, 0x5a, + 0xb1, 0xb5, 0xb1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc1, 0xc5, 0xc1, 0x39, 0x3a, 0x39, 0x43, 0x45, + 0x43, 0x83, 0x85, 0x83, 0xf9, 0xfa, 0xf9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xaa, + 0xa7, 0x68, 0x6b, 0x68, 0xed, 0xf0, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xc5, + 0xc2, 0x5e, 0x60, 0x5e, 0xcd, 0xd1, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0x5b, 0x5d, 0x5b, 0xf1, 0xf3, + 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe2, + 0xdf, 0x6f, 0x71, 0x6f, 0xac, 0xaf, 0xac, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe3, 0xe0, 0xb0, + 0xb3, 0xb0, 0xb0, 0xb3, 0xb0, 0xf9, 0xfa, 0xf9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x91, 0x93, 0x91, 0x82, 0x85, 0x82, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe0, 0xe4, 0xe0, 0xc3, 0xc6, 0xc3, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9d, 0xa0, 0x9d, 0x8f, + 0x92, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x83, 0x81, + 0x82, 0x84, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, 0x76, + 0x74, 0x8f, 0x92, 0x8f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd5, 0xd9, 0xd5, 0xdc, 0xe0, 0xdc, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x9c, 0xa0, 0x9c, 0x65, 0x67, 0x65, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, + 0xf7, 0xf5, 0x6b, 0x6e, 0x6b, 0xab, 0xae, 0xab, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xca, 0xc6, + 0x58, 0x59, 0x58, 0xd1, 0xd5, 0xd1, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xcd, 0xd0, 0xcd, 0x54, 0x56, + 0x54, 0xcd, 0xd1, 0xcd, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfc, 0xfc, 0xfc, 0x70, 0x72, 0x70, 0x99, + 0x9c, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, + 0xc3, 0xc0, 0x57, 0x58, 0x57, 0xdb, 0xdf, 0xdb, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x98, 0x9b, 0x98, 0x64, 0x66, 0x64, 0xe9, 0xec, + 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xa6, 0xa9, 0xa6, 0x62, 0x64, + 0x62, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xc8, 0x52, + 0x54, 0x52, 0xc5, 0xc9, 0xc5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a, + 0x8c, 0x8a, 0x72, 0x75, 0x72, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x67, 0x6a, 0x67, 0x93, 0x95, + 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0x71, 0x73, 0x71, 0x8c, 0x8e, + 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, + 0x8e, 0x8c, 0x6d, 0x6f, 0x6d, 0xe4, 0xe8, 0xe4, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe1, 0xdd, 0x66, + 0x69, 0x66, 0xa7, 0xaa, 0xa7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc3, 0xc6, 0xc3, 0x60, 0x62, + 0x60, 0xc5, 0xc9, 0xc5, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd2, 0xd6, 0xd2, 0x6a, 0x6c, 0x6a, 0xc0, 0xc4, + 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xe1, 0xe4, + 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, + 0xe1, 0xdd, 0x65, 0x67, 0x65, 0x93, 0x96, 0x93, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc6, 0xca, 0xc6, 0x62, + 0x64, 0x62, 0xc2, 0xc6, 0xc2, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe4, 0xe0, 0x8d, + 0x90, 0x8d, 0x71, 0x74, 0x71, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xcc, 0xd0, 0xcc, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0x9b, + 0x98, 0x68, 0x6a, 0x68, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x97, 0x9b, 0x97, 0x65, 0x68, 0x65, 0xd6, 0xda, + 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe9, 0xec, 0xe9, 0xbf, 0xc3, 0xbf, + 0x77, 0x79, 0x77, 0x36, 0x37, 0x36, 0x37, 0x38, + 0x37, 0xe1, 0xe5, 0xe1, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb2, 0xaf, 0x58, + 0x5a, 0x58, 0xb0, 0xb2, 0xb0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc5, 0xc8, 0xc5, 0x4f, 0x51, 0x4f, + 0xc5, 0xc8, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdf, 0xe3, 0xdf, 0x59, 0x5b, 0x59, 0x9f, + 0xa2, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe3, 0xdf, 0xa3, 0xa6, 0xa3, 0x67, 0x6a, + 0x67, 0x5d, 0x5e, 0x5d, 0x7f, 0x82, 0x7f, 0x78, + 0x7a, 0x78, 0x4a, 0x4b, 0x4a, 0xe9, 0xec, 0xe9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x60, 0x62, 0x60, 0x41, 0x42, 0x41, 0x53, 0x55, + 0x53, 0xce, 0xd1, 0xce, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, + 0xf5, 0x69, 0x6c, 0x69, 0x87, 0x89, 0x87, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xad, 0xaa, + 0x60, 0x61, 0x60, 0xd8, 0xdc, 0xd8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xcd, + 0xca, 0xa1, 0xa3, 0xa1, 0x6e, 0x70, 0x6e, 0x5f, + 0x62, 0x5f, 0x90, 0x92, 0x90, 0xcd, 0xd0, 0xcd, + 0xff, 0xff, 0xff, 0x9f, 0xa2, 0x9f, 0x66, 0x69, + 0x66, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc3, 0xc7, 0xc3, 0x52, 0x53, 0x52, 0xa7, + 0xaa, 0xa7, 0x6f, 0x72, 0x6f, 0x76, 0x78, 0x76, + 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0xa6, 0xa2, + 0x57, 0x59, 0x57, 0xda, 0xde, 0xda, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xf3, + 0xf1, 0x70, 0x72, 0x70, 0x92, 0x95, 0x92, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc9, 0xcc, 0xc9, 0x92, + 0x95, 0x92, 0x5b, 0x5d, 0x5b, 0x6a, 0x6c, 0x6a, + 0x8e, 0x91, 0x8e, 0xce, 0xd2, 0xce, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x89, + 0x8c, 0x89, 0x7a, 0x7c, 0x7a, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0x8e, 0x8b, + 0x79, 0x7b, 0x79, 0xf9, 0xfa, 0xf9, 0xc8, 0xcc, + 0xc8, 0x5a, 0x5c, 0x5a, 0xa4, 0xa7, 0xa4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd9, 0xdd, 0xd9, 0x57, 0x59, 0x57, 0xb7, + 0xba, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xbf, 0xc2, 0xbf, 0x56, 0x58, 0x56, + 0xd1, 0xd5, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, + 0xe1, 0xe5, 0xe1, 0xe1, 0xe5, 0xe1, 0xe1, 0xe5, + 0xe1, 0xe4, 0xe8, 0xe4, 0xf5, 0xf7, 0xf5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdc, 0xe0, 0xdc, 0xc5, + 0xc9, 0xc5, 0x9a, 0x9d, 0x9a, 0x69, 0x6b, 0x69, + 0x5d, 0x5f, 0x5d, 0x6d, 0x6f, 0x6d, 0xb0, 0xb4, + 0xb0, 0xe0, 0xe4, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0x77, 0x79, 0x77, 0x91, 0x95, + 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd3, + 0xcf, 0x58, 0x5a, 0x58, 0xb8, 0xbb, 0xb8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xa5, 0xa8, 0xa5, + 0x5f, 0x60, 0x5f, 0xc5, 0xc8, 0xc5, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7c, 0x7f, 0x7c, 0x88, 0x8a, 0x88, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x92, + 0x8f, 0x68, 0x6a, 0x68, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, + 0xe2, 0xdf, 0xb0, 0xb4, 0xb0, 0xd9, 0xdc, 0xd9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0xc3, 0xc0, 0xb7, 0xbb, 0xb7, 0x99, 0x9b, + 0x99, 0x82, 0x84, 0x82, 0x73, 0x76, 0x73, 0x73, + 0x76, 0x73, 0x73, 0x76, 0x73, 0x76, 0x78, 0x76, + 0x7b, 0x7d, 0x7b, 0x80, 0x82, 0x80, 0x84, 0x87, + 0x84, 0x82, 0x85, 0x82, 0x7d, 0x80, 0x7d, 0x8f, + 0x92, 0x8f, 0x9d, 0xa0, 0x9d, 0xae, 0xb1, 0xae, + 0xb6, 0xb9, 0xb6, 0xb5, 0xb8, 0xb5, 0xb5, 0xb8, + 0xb5, 0xb6, 0xb9, 0xb6, 0xbf, 0xc3, 0xbf, 0xc4, + 0xc7, 0xc4, 0xc8, 0xcb, 0xc8, 0xc8, 0xcb, 0xc8, + 0xc8, 0xcb, 0xc8, 0xc8, 0xcb, 0xc8, 0xc8, 0xcb, + 0xc8, 0xc8, 0xcb, 0xc8, 0xc8, 0xcb, 0xc8, 0xc8, + 0xcb, 0xc8, 0xc8, 0xcb, 0xc8, 0xc4, 0xc7, 0xc4, + 0xc1, 0xc4, 0xc1, 0xc1, 0xc4, 0xc1, 0xb7, 0xba, + 0xb7, 0xb5, 0xb8, 0xb5, 0xb6, 0xb9, 0xb6, 0xa3, + 0xa7, 0xa3, 0x8c, 0x8f, 0x8c, 0x81, 0x84, 0x81, + 0x64, 0x66, 0x64, 0x5e, 0x60, 0x5e, 0x45, 0x46, + 0x45, 0x63, 0x64, 0x63, 0xc1, 0xc4, 0xc1, 0xf5, + 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd9, 0xdd, 0xd9, 0x69, + 0x6b, 0x69, 0xab, 0xae, 0xab, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa1, 0xa4, 0xa1, 0x63, 0x65, 0x63, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xde, 0xe2, 0xde, 0x70, 0x72, 0x70, 0x70, + 0x72, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xac, 0xaf, 0xac, 0x62, + 0x64, 0x62, 0xd2, 0xd5, 0xd2, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, + 0xe8, 0xe5, 0x69, 0x6b, 0x69, 0x88, 0x8b, 0x88, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x9d, 0xa0, 0x9d, 0x3b, 0x3d, + 0x3b, 0x9f, 0xa2, 0x9f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd5, 0xd8, 0xd5, 0x67, 0x69, 0x67, 0x8b, + 0x8e, 0x8b, 0x7c, 0x7f, 0x7c, 0x9e, 0xa1, 0x9e, + 0xab, 0xae, 0xab, 0xab, 0xae, 0xab, 0xab, 0xae, + 0xab, 0xab, 0xae, 0xab, 0xa4, 0xa7, 0xa4, 0x92, + 0x94, 0x92, 0x88, 0x8a, 0x88, 0x82, 0x84, 0x82, + 0x73, 0x75, 0x73, 0x77, 0x79, 0x77, 0x7c, 0x7e, + 0x7c, 0x78, 0x7b, 0x78, 0x69, 0x6b, 0x69, 0x63, + 0x65, 0x63, 0x5c, 0x5e, 0x5c, 0x50, 0x52, 0x50, + 0x5b, 0x5d, 0x5b, 0x60, 0x62, 0x60, 0x65, 0x67, + 0x65, 0x65, 0x67, 0x65, 0x65, 0x67, 0x65, 0x65, + 0x67, 0x65, 0x65, 0x67, 0x65, 0x64, 0x66, 0x64, + 0x50, 0x51, 0x50, 0x4d, 0x4e, 0x4d, 0x61, 0x63, + 0x61, 0x60, 0x62, 0x60, 0x5d, 0x5f, 0x5d, 0x5d, + 0x5f, 0x5d, 0x58, 0x5a, 0x58, 0x60, 0x62, 0x60, + 0x68, 0x6a, 0x68, 0x6b, 0x6e, 0x6b, 0x75, 0x77, + 0x75, 0x83, 0x86, 0x83, 0x9e, 0xa2, 0x9e, 0xc0, + 0xc4, 0xc0, 0x9e, 0xa1, 0x9e, 0x73, 0x76, 0x73, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xce, 0xd2, 0xce, 0x59, 0x5b, 0x59, 0xb5, 0xb8, + 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0x6e, 0x71, + 0x6e, 0x8c, 0x8f, 0x8c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbd, 0xc0, 0xbd, 0x50, 0x52, 0x50, 0xad, 0xb1, + 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd1, 0xd5, 0xd1, 0x59, 0x5b, 0x59, 0xa5, 0xa8, + 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xce, 0xd1, 0xce, 0x63, 0x66, + 0x63, 0xbb, 0xbe, 0xbb, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe1, 0xe5, 0xe1, 0x59, + 0x5a, 0x59, 0x3a, 0x3b, 0x3a, 0x83, 0x86, 0x83, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xc8, + 0x5b, 0x5e, 0x5b, 0xcf, 0xd2, 0xcf, 0xfc, 0xfc, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, + 0xde, 0xe2, 0xde, 0xdb, 0xde, 0xdb, 0xd8, 0xdb, + 0xd8, 0xd0, 0xd3, 0xd0, 0xd0, 0xd4, 0xd0, 0xd1, + 0xd4, 0xd1, 0xd1, 0xd4, 0xd1, 0xd1, 0xd4, 0xd1, + 0xd1, 0xd4, 0xd1, 0xd1, 0xd4, 0xd1, 0xd1, 0xd4, + 0xd1, 0xcb, 0xcf, 0xcb, 0x62, 0x64, 0x62, 0x76, + 0x78, 0x76, 0xc8, 0xcb, 0xc8, 0xd1, 0xd4, 0xd1, + 0xd1, 0xd4, 0xd1, 0xd1, 0xd4, 0xd1, 0xd4, 0xd7, + 0xd4, 0xd9, 0xdd, 0xd9, 0xdd, 0xe1, 0xdd, 0xe9, + 0xec, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, + 0xc8, 0x54, 0x56, 0x54, 0xcd, 0xd0, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xca, 0xce, 0xca, 0x63, + 0x66, 0x63, 0xc4, 0xc7, 0xc4, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xae, + 0xb1, 0xae, 0x5f, 0x61, 0x5f, 0xca, 0xce, 0xca, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, + 0x9d, 0x9b, 0x60, 0x62, 0x60, 0xda, 0xde, 0xda, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x86, + 0x89, 0x86, 0x74, 0x77, 0x74, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, + 0xb0, 0xad, 0x67, 0x69, 0x67, 0xd9, 0xdd, 0xd9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x96, 0x99, 0x96, 0x6b, 0x6d, 0x6b, 0x7f, 0x81, + 0x7f, 0x62, 0x64, 0x62, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc8, 0xcb, 0xc8, 0x4d, 0x4f, 0x4d, 0xcb, + 0xce, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x61, 0x62, 0x61, 0x9a, 0x9d, 0x9a, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe9, 0xec, 0xe9, 0x63, 0x65, 0x63, + 0xb9, 0xbc, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb9, 0xbd, 0xb9, 0x6c, 0x6f, 0x6c, 0xd6, 0xda, + 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7c, 0x7e, 0x7c, 0x87, 0x89, + 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0x5a, 0x5c, + 0x5a, 0xa4, 0xa7, 0xa4, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xbc, 0xbf, 0xbc, 0x58, 0x5a, + 0x58, 0xdc, 0xe0, 0xdc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x88, 0x8b, 0x88, 0x79, 0x7b, + 0x79, 0xf2, 0xf3, 0xf2, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd1, 0xd5, 0xd1, 0x5d, 0x5f, 0x5d, 0xb0, + 0xb3, 0xb0, 0xbb, 0xbe, 0xbb, 0x53, 0x54, 0x53, + 0xd5, 0xd9, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xc8, + 0x44, 0x46, 0x44, 0xc4, 0xc7, 0xc4, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfc, 0xfc, 0xfc, 0x5b, 0x5d, 0x5b, 0xa5, + 0xa8, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x67, 0x69, 0x67, 0x9a, 0x9d, 0x9a, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa2, 0xa5, 0xa2, 0x66, + 0x68, 0x66, 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd8, 0xdb, 0xd8, 0x57, + 0x59, 0x57, 0xb5, 0xb8, 0xb5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x8b, 0x8e, 0x8b, 0x79, 0x7c, 0x79, + 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, + 0xe1, 0xdd, 0x60, 0x62, 0x60, 0xb1, 0xb4, 0xb1, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0x5e, + 0x5f, 0x5e, 0xa7, 0xaa, 0xa7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x93, 0x96, 0x93, + 0x62, 0x63, 0x62, 0xe1, 0xe5, 0xe1, 0xda, 0xdd, + 0xda, 0x5e, 0x60, 0x5e, 0xb4, 0xb8, 0xb4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xca, 0xce, 0xca, 0x49, 0x4a, 0x49, 0xc4, + 0xc7, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd3, 0xcf, + 0x51, 0x52, 0x51, 0xbd, 0xc0, 0xbd, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7d, 0x7b, + 0x85, 0x87, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x98, 0x9b, 0x98, 0x6c, 0x6e, 0x6c, 0xe4, 0xe8, + 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa1, 0xa3, 0xa1, 0x6a, 0x6b, 0x6a, 0xe2, 0xe5, + 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xc1, + 0xbe, 0x5b, 0x5d, 0x5b, 0xba, 0xbd, 0xba, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x94, + 0x91, 0x73, 0x76, 0x73, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd0, 0xd3, 0xd0, 0x52, 0x54, 0x52, 0xc5, 0xc9, + 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xd0, + 0xcc, 0x62, 0x64, 0x62, 0xa3, 0xa6, 0xa3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0x7b, 0x78, + 0x87, 0x8a, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xd6, 0xd3, + 0x58, 0x5a, 0x58, 0xc4, 0xc7, 0xc4, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xbd, 0xc0, 0xbd, 0x4c, 0x4d, 0x4c, 0xcf, + 0xd2, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x95, 0x99, 0x95, 0x68, 0x6b, 0x68, 0xe0, + 0xe4, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, 0x80, + 0x83, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdd, 0xe0, 0xdd, 0x5d, 0x5f, 0x5d, 0x9a, + 0x9d, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf9, 0xfa, 0xf9, 0x7b, 0x7e, 0x7b, + 0x73, 0x75, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc7, 0xcb, 0xc7, 0x4a, 0x4c, 0x4a, + 0xc6, 0xc9, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xab, 0xaf, 0xab, 0x5d, + 0x5f, 0x5d, 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x95, 0x98, 0x95, 0x6a, 0x6c, 0x6a, + 0xd6, 0xd9, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa0, 0xa4, 0xa0, 0x63, 0x65, 0x63, 0xe1, + 0xe5, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd7, 0xdb, 0xd7, 0x60, 0x61, 0x60, 0xc4, + 0xc7, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbe, 0xbb, + 0x5e, 0x60, 0x5e, 0xd9, 0xdc, 0xd9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb4, 0xb2, + 0x68, 0x6a, 0x68, 0xd5, 0xd9, 0xd5, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x82, 0x84, 0x82, 0x82, 0x84, 0x82, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xbf, 0xbc, + 0x52, 0x54, 0x52, 0xce, 0xd2, 0xce, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xaf, 0xb3, 0xaf, 0x4b, 0x4d, 0x4b, 0xd8, + 0xdb, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x5f, 0x60, 0x5f, 0xa6, 0xa9, 0xa6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x80, 0x7e, 0x8a, 0x8d, 0x8a, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xdf, 0xe3, 0xdf, 0x58, 0x5a, + 0x58, 0x9e, 0xa1, 0x9e, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xbe, 0xba, + 0x55, 0x56, 0x55, 0xd4, 0xd7, 0xd4, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, + 0x67, 0x69, 0x67, 0xb7, 0xba, 0xb7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa9, 0xac, 0xa9, 0x65, 0x67, 0x65, 0xde, + 0xe2, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xca, 0xc7, 0x61, 0x62, 0x61, 0xc3, + 0xc6, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x79, 0x7b, 0x79, 0x90, + 0x93, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x8f, 0x91, 0x8f, 0x76, 0x78, 0x76, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xe0, 0xdc, + 0x62, 0x64, 0x62, 0x99, 0x9c, 0x99, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a, 0x8c, 0x8a, + 0x7c, 0x7e, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd4, 0xd8, 0xd4, 0x57, 0x59, 0x57, 0xbe, + 0xc1, 0xbe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, + 0xaa, 0xa7, 0x63, 0x65, 0x63, 0xdc, 0xdf, 0xdc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xdf, 0xe3, 0xdf, 0x57, 0x58, 0x57, 0xb5, + 0xb8, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe9, 0xec, 0xe9, 0x6d, 0x6f, 0x6d, 0xac, + 0xb0, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xaa, 0xa7, + 0x70, 0x72, 0x70, 0xf1, 0xf3, 0xf1, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0xd5, 0xd1, + 0x63, 0x65, 0x63, 0xb8, 0xbc, 0xb8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x63, 0x66, 0x63, 0x91, 0x94, 0x91, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xdc, 0xdf, 0xdc, 0x6d, 0x6f, 0x6d, + 0xa5, 0xa9, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xab, 0xae, 0xab, 0x61, + 0x63, 0x61, 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xae, 0xb1, 0xae, 0x5f, 0x61, 0x5f, 0xd7, + 0xdb, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xbd, 0xb9, + 0x54, 0x56, 0x54, 0xdc, 0xdf, 0xdc, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdc, 0xdf, 0xdc, 0x63, 0x65, 0x63, 0x98, 0x9b, + 0x98, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6b, 0x6d, 0x6b, 0x95, 0x98, 0x95, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x76, 0x78, 0x76, 0xa6, 0xa9, 0xa6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xb6, 0xb9, 0xb6, 0x5f, 0x61, 0x5f, 0xd9, + 0xdd, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd7, 0xdb, 0xd7, 0x6e, 0x70, 0x6e, 0xba, + 0xbd, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x64, 0x66, 0x64, 0x9c, + 0x9f, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xbf, + 0xbc, 0x64, 0x66, 0x64, 0xcd, 0xd0, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdb, 0xdf, 0xdb, 0x55, 0x57, 0x55, 0xaf, 0xb2, + 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xc6, 0xc2, + 0x55, 0x57, 0x55, 0xbb, 0xbe, 0xbb, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x91, 0x94, 0x91, 0x67, 0x6a, 0x67, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa2, 0xa5, 0xa2, 0x58, + 0x5a, 0x58, 0xdc, 0xdf, 0xdc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x89, 0x8c, 0x89, 0x76, + 0x78, 0x76, 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x71, 0x73, 0x71, 0x95, + 0x98, 0x95, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0xd8, 0xd5, + 0x52, 0x54, 0x52, 0xb0, 0xb3, 0xb0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd3, 0xcf, + 0x63, 0x65, 0x63, 0xb9, 0xbc, 0xb9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x67, 0x69, 0x67, 0xa2, 0xa5, 0xa2, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x98, 0x9b, 0x98, 0x6b, 0x6e, 0x6b, + 0xe1, 0xe5, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x84, + 0x87, 0x84, 0x7f, 0x81, 0x7f, 0xfc, 0xfc, 0xfc, + 0xff, 0xff, 0xff, 0xe5, 0xe8, 0xe5, 0xbc, 0xc0, + 0xbc, 0x67, 0x69, 0x67, 0x2e, 0x2e, 0x2e, 0x97, + 0x9a, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0x76, 0x78, 0x76, + 0x8e, 0x91, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd8, 0xdc, 0xd8, + 0x66, 0x69, 0x66, 0x93, 0x96, 0x93, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb3, 0xb6, 0xb3, 0x63, 0x65, 0x63, 0xd2, 0xd5, + 0xd2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6b, 0x6d, 0x6b, 0x82, 0x84, 0x82, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x78, 0x7b, 0x78, 0x7a, + 0x7d, 0x7a, 0xdd, 0xe0, 0xdd, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xaa, 0xad, 0xaa, 0x5c, 0x5e, 0x5c, 0xca, + 0xcd, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x6b, 0x6d, 0x6b, 0xb9, + 0xbd, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x72, 0x75, + 0x72, 0x8d, 0x90, 0x8d, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xb9, 0xbd, 0xb9, 0x5c, 0x5e, + 0x5c, 0xab, 0xae, 0xab, 0x97, 0x9a, 0x97, 0x69, + 0x6b, 0x69, 0x5c, 0x5e, 0x5c, 0x7e, 0x80, 0x7e, + 0x68, 0x69, 0x68, 0x6e, 0x71, 0x6e, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xe0, + 0xdc, 0x68, 0x6a, 0x68, 0xaf, 0xb2, 0xaf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa2, 0xa5, 0xa2, 0x60, 0x62, 0x60, 0xce, + 0xd1, 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xce, 0xd1, 0xce, 0x62, + 0x64, 0x62, 0xb8, 0xbb, 0xb8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7a, 0x7d, 0x7a, 0x84, + 0x87, 0x84, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0xc6, 0xc3, 0x5e, 0x60, 0x5e, 0x8a, 0x8c, + 0x8a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x72, 0x70, + 0x85, 0x87, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, + 0x61, 0x63, 0x61, 0xc4, 0xc8, 0xc4, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x67, 0x69, 0x67, 0xac, 0xaf, 0xac, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, + 0xe3, 0xdf, 0x69, 0x6a, 0x69, 0x42, 0x44, 0x42, + 0x62, 0x64, 0x62, 0x99, 0x9b, 0x99, 0xca, 0xcd, + 0xca, 0xe8, 0xec, 0xe8, 0xa6, 0xa9, 0xa6, 0x53, + 0x55, 0x53, 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc3, 0xc7, 0xc3, 0x66, 0x68, 0x66, + 0xcf, 0xd2, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf9, 0xfa, 0xf9, 0x61, 0x63, 0x61, + 0x91, 0x94, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe4, 0xe8, 0xe4, 0x6e, 0x70, 0x6e, 0x95, 0x98, + 0x95, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x8c, 0x8e, 0x8c, 0x87, 0x89, 0x87, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, + 0xb2, 0xaf, 0x59, 0x5b, 0x59, 0xac, 0xaf, 0xac, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, 0x9c, + 0x99, 0x61, 0x63, 0x61, 0xcc, 0xd0, 0xcc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd6, 0xda, 0xd6, 0x56, 0x58, 0x56, 0xc3, + 0xc7, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xd1, 0xd5, 0xd1, 0x50, 0x52, + 0x50, 0xc4, 0xc7, 0xc4, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0x9b, + 0x98, 0x44, 0x46, 0x44, 0xcb, 0xce, 0xcb, 0xf1, + 0xf5, 0xf1, 0xf7, 0xfb, 0xf7, 0xf7, 0xfb, 0xf7, + 0xcb, 0xcf, 0xcb, 0x57, 0x5a, 0x57, 0xd7, 0xda, + 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa6, 0xaa, + 0xa6, 0x68, 0x6b, 0x68, 0xdc, 0xe0, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb5, + 0xb2, 0x59, 0x5b, 0x59, 0xd2, 0xd5, 0xd2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, + 0x8f, 0x8c, 0x6e, 0x71, 0x6e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x91, 0x94, 0x91, 0x7e, + 0x81, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xdb, 0xdf, 0xdb, 0x5e, 0x60, + 0x5e, 0x3a, 0x3b, 0x3a, 0x86, 0x89, 0x86, 0xc1, + 0xc4, 0xc1, 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0x8b, + 0x8e, 0x8b, 0x36, 0x38, 0x36, 0x7d, 0x80, 0x7d, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, + 0x48, 0x4a, 0x48, 0x93, 0x96, 0x93, 0xb4, 0xb7, + 0xb4, 0xba, 0xbe, 0xba, 0xc6, 0xc9, 0xc6, 0x93, + 0x96, 0x93, 0x52, 0x54, 0x52, 0xd4, 0xd8, 0xd4, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd4, 0xd7, 0xd4, 0x5a, 0x5c, 0x5a, + 0xc1, 0xc4, 0xc1, 0xef, 0xf3, 0xef, 0xd6, 0xda, + 0xd6, 0xed, 0xf1, 0xed, 0xe2, 0xe6, 0xe2, 0x60, + 0x62, 0x60, 0xaf, 0xb2, 0xaf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x8e, 0x91, 0x8e, 0x7c, 0x7f, 0x7c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7a, 0x7c, 0x7a, 0x8a, 0x8c, 0x8a, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xa0, 0xa3, 0xa0, 0x5e, 0x60, + 0x5e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0x67, 0x69, 0x67, 0xe1, 0xe5, + 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, + 0xb8, 0xb5, 0x4f, 0x50, 0x4f, 0x8d, 0x8f, 0x8d, + 0x6b, 0x6d, 0x6b, 0x57, 0x59, 0x57, 0x6d, 0x6f, + 0x6d, 0x92, 0x95, 0x92, 0xa5, 0xa8, 0xa5, 0xa7, + 0xaa, 0xa7, 0xa0, 0xa3, 0xa0, 0x8a, 0x8d, 0x8a, + 0x5d, 0x5f, 0x5d, 0x51, 0x53, 0x51, 0x6f, 0x72, + 0x6f, 0x69, 0x6b, 0x69, 0xdd, 0xe1, 0xdd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xca, 0xcd, 0xca, 0x30, 0x32, 0x30, 0x57, + 0x58, 0x57, 0x65, 0x67, 0x65, 0x68, 0x6a, 0x68, + 0x6d, 0x6f, 0x6d, 0x3c, 0x3d, 0x3c, 0x5e, 0x60, + 0x5e, 0xe9, 0xec, 0xe9, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7c, 0x7e, 0x7c, 0x89, 0x8c, 0x89, 0xb7, + 0xbb, 0xb7, 0x5d, 0x60, 0x5d, 0x93, 0x96, 0x93, + 0xca, 0xcd, 0xca, 0x75, 0x78, 0x75, 0x8d, 0x90, + 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x79, + 0x77, 0x82, 0x85, 0x82, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xc8, 0x56, 0x57, + 0x56, 0xc5, 0xc9, 0xc5, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, + 0xc7, 0xc3, 0x51, 0x53, 0x51, 0xcf, 0xd3, 0xcf, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb1, 0xb3, 0xb1, 0x4f, + 0x51, 0x4f, 0xd6, 0xda, 0xd6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, 0x6c, 0x6f, + 0x6c, 0xb1, 0xb4, 0xb1, 0x82, 0x84, 0x82, 0xb2, + 0xb5, 0xb2, 0xa0, 0xa3, 0xa0, 0x81, 0x84, 0x81, + 0x65, 0x67, 0x65, 0x66, 0x69, 0x66, 0x71, 0x74, + 0x71, 0x82, 0x84, 0x82, 0x70, 0x73, 0x70, 0x73, + 0x76, 0x73, 0xb3, 0xb6, 0xb3, 0x59, 0x5b, 0x59, + 0xcb, 0xce, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xcb, 0xc8, + 0x50, 0x53, 0x50, 0xb8, 0xbc, 0xb8, 0xd7, 0xda, + 0xd7, 0xd7, 0xda, 0xd7, 0xd2, 0xd5, 0xd2, 0x74, + 0x76, 0x74, 0x79, 0x7c, 0x79, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb5, 0xb2, + 0x58, 0x5a, 0x58, 0x73, 0x75, 0x73, 0x53, 0x55, + 0x53, 0x64, 0x67, 0x64, 0x57, 0x5a, 0x57, 0x46, + 0x48, 0x46, 0x6e, 0x70, 0x6e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x73, 0x76, 0x73, 0x9c, 0x9f, 0x9c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, + 0x9b, 0x98, 0x6e, 0x70, 0x6e, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0x63, 0x65, + 0x63, 0xab, 0xae, 0xab, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb8, 0xbb, 0xb8, 0x57, 0x59, 0x57, 0xd5, 0xd8, + 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, + 0x7d, 0x7b, 0x80, 0x81, 0x80, 0x95, 0x98, 0x95, + 0x76, 0x79, 0x76, 0xee, 0xf0, 0xee, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa8, 0xab, 0xa8, 0x6f, 0x71, 0x6f, 0xd1, 0xd5, + 0xd1, 0x5d, 0x5f, 0x5d, 0xa1, 0xa4, 0xa1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc8, 0xcb, 0xc8, 0x5f, 0x62, 0x5f, 0xd4, + 0xd8, 0xd4, 0xf0, 0xf4, 0xf0, 0xf4, 0xf8, 0xf4, + 0xe8, 0xec, 0xe8, 0x73, 0x75, 0x73, 0x9a, 0x9d, + 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe1, 0xe5, 0xe1, 0x48, 0x4a, 0x48, 0x31, + 0x33, 0x31, 0x85, 0x87, 0x85, 0xe1, 0xe5, 0xe1, + 0xa1, 0xa4, 0xa1, 0x49, 0x4a, 0x49, 0x74, 0x76, + 0x74, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x66, 0x68, + 0x66, 0xaf, 0xb2, 0xaf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xed, 0xf0, 0xed, 0x71, 0x73, 0x71, 0x91, 0x93, + 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7f, 0x81, 0x7f, 0x83, 0x86, 0x83, + 0xf9, 0xfa, 0xf9, 0xdb, 0xdf, 0xdb, 0xae, 0xb1, + 0xae, 0x8d, 0x8f, 0x8d, 0x9f, 0xa2, 0x9f, 0x5c, + 0x5d, 0x5c, 0xc6, 0xc9, 0xc6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xa6, 0xa9, 0xa6, 0x58, 0x5a, + 0x58, 0x5b, 0x5d, 0x5b, 0x92, 0x95, 0x92, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa8, 0xac, 0xa8, 0x68, + 0x6b, 0x68, 0xc8, 0xcb, 0xc8, 0x56, 0x58, 0x56, + 0x90, 0x93, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xba, 0xbd, 0xba, + 0x5e, 0x61, 0x5e, 0xb4, 0xb7, 0xb4, 0x96, 0x98, + 0x96, 0xc9, 0xcc, 0xc9, 0xd9, 0xdd, 0xd9, 0x56, + 0x58, 0x56, 0xaf, 0xb2, 0xaf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6b, 0x6d, 0x6b, 0x20, 0x21, 0x20, 0xa5, 0xa8, + 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, + 0xd7, 0xd3, 0xd8, 0xdb, 0xd8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x5e, 0x5f, 0x5e, 0x91, 0x93, 0x91, + 0xdc, 0xe0, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd2, 0xcf, 0x5f, + 0x61, 0x5f, 0xb3, 0xb5, 0xb3, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0xac, + 0xa9, 0x40, 0x41, 0x40, 0x71, 0x74, 0x71, 0x60, + 0x61, 0x60, 0x5f, 0x61, 0x5f, 0x87, 0x89, 0x87, + 0xbd, 0xc0, 0xbd, 0x50, 0x52, 0x50, 0xa8, 0xab, + 0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, + 0xe3, 0xdf, 0x85, 0x88, 0x85, 0x57, 0x59, 0x57, + 0xb9, 0xbc, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0xc6, 0xc3, 0x57, 0x59, 0x57, 0x61, 0x62, + 0x61, 0x41, 0x43, 0x41, 0x9e, 0xa1, 0x9e, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa9, 0xac, 0xa9, 0x5a, 0x5c, 0x5a, 0x69, + 0x6b, 0x69, 0x35, 0x36, 0x35, 0x72, 0x75, 0x72, + 0xbc, 0xc0, 0xbc, 0x61, 0x62, 0x61, 0xd1, 0xd5, + 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9d, 0xa0, 0x9d, 0x39, + 0x3a, 0x39, 0xb4, 0xb7, 0xb4, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0x51, + 0x4f, 0x34, 0x35, 0x34, 0x54, 0x56, 0x54, 0x80, + 0x83, 0x80, 0xae, 0xb1, 0xae, 0xcc, 0xd0, 0xcc, + 0xab, 0xae, 0xab, 0x64, 0x67, 0x64, 0xd4, 0xd8, + 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xd1, 0xd4, 0xd1, 0x3a, 0x3b, 0x3a, + 0x6a, 0x6c, 0x6a, 0xb3, 0xb6, 0xb3, 0xe3, 0xe7, + 0xe3, 0xf5, 0xf9, 0xf5, 0xed, 0xf1, 0xed, 0x66, + 0x68, 0x66, 0x9c, 0x9f, 0x9c, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, + 0xc6, 0xc3, 0xa9, 0xac, 0xa9, 0xb4, 0xb7, 0xb4, + 0xe5, 0xe8, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0xa6, 0xa3, + 0x23, 0x24, 0x23, 0x4b, 0x4c, 0x4b, 0x9b, 0x9e, + 0x9b, 0x52, 0x54, 0x52, 0x66, 0x68, 0x66, 0x4b, + 0x4d, 0x4b, 0xe1, 0xe5, 0xe1, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdb, 0xdf, 0xdb, 0xbf, 0xc2, 0xbf, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x5d, 0x5f, 0x5d, 0x89, 0x8b, 0x89, + 0x9f, 0xa1, 0x9f, 0x70, 0x72, 0x70, 0x61, 0x63, + 0x61, 0x57, 0x59, 0x57, 0x4f, 0x51, 0x4f, 0x70, + 0x72, 0x70, 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0xf0, + 0xed, 0x56, 0x57, 0x56, 0xa9, 0xac, 0xa9, 0xf4, + 0xf8, 0xf4, 0xf7, 0xfb, 0xf7, 0xf7, 0xfb, 0xf7, + 0xf5, 0xf9, 0xf5, 0x77, 0x79, 0x77, 0x79, 0x7c, + 0x79, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x91, 0x93, 0x91, 0x19, 0x1a, 0x19, 0x8d, + 0x90, 0x8d, 0xf1, 0xf3, 0xf1, 0x89, 0x8d, 0x89, + 0x35, 0x36, 0x35, 0x4d, 0x4f, 0x4d, 0xfc, 0xfc, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x65, + 0x63, 0xb2, 0xb5, 0xb2, 0xf2, 0xf6, 0xf2, 0xe7, + 0xeb, 0xe7, 0xd7, 0xda, 0xd7, 0xb3, 0xb6, 0xb3, + 0x49, 0x4b, 0x49, 0x7e, 0x80, 0x7e, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x72, 0x74, 0x72, + 0x90, 0x92, 0x90, 0xe5, 0xe9, 0xe5, 0xb3, 0xb6, + 0xb3, 0xb3, 0xb7, 0xb3, 0xe7, 0xeb, 0xe7, 0x97, + 0x9b, 0x97, 0x6f, 0x72, 0x6f, 0xe0, 0xe4, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xac, 0xaf, 0xac, + 0x64, 0x66, 0x64, 0xd2, 0xd6, 0xd2, 0xff, 0xff, + 0xff, 0xd2, 0xd5, 0xd2, 0x50, 0x52, 0x50, 0x65, + 0x67, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x66, 0x68, 0x66, 0xaf, 0xb2, 0xaf, + 0xef, 0xf3, 0xef, 0xe5, 0xe9, 0xe5, 0xf4, 0xf8, + 0xf4, 0xed, 0xf1, 0xed, 0x67, 0x69, 0x67, 0xa9, + 0xad, 0xa9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x92, 0x94, 0x92, 0x70, 0x72, 0x70, 0xb9, + 0xbc, 0xb9, 0x3f, 0x40, 0x3f, 0x44, 0x45, 0x44, + 0x7e, 0x80, 0x7e, 0x98, 0x9a, 0x98, 0x58, 0x5a, + 0x58, 0xca, 0xcd, 0xca, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa5, 0xa8, 0xa5, 0xa7, 0xaa, 0xa7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x69, 0x6b, + 0x69, 0xa1, 0xa4, 0xa1, 0xb9, 0xbc, 0xb9, 0x72, + 0x73, 0x72, 0xca, 0xcd, 0xca, 0xe0, 0xe4, 0xe0, + 0x57, 0x59, 0x57, 0xb7, 0xbb, 0xb7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xb6, 0xb9, 0xb6, + 0x5d, 0x5f, 0x5d, 0x76, 0x78, 0x76, 0x56, 0x58, + 0x56, 0xa7, 0xaa, 0xa7, 0x6c, 0x6e, 0x6c, 0x42, + 0x44, 0x42, 0x47, 0x48, 0x47, 0xc1, 0xc5, 0xc1, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x77, 0x7a, 0x77, 0x8b, 0x8e, 0x8b, + 0x6a, 0x6c, 0x6a, 0x2a, 0x2a, 0x2a, 0x81, 0x83, + 0x81, 0xbf, 0xc3, 0xbf, 0x57, 0x59, 0x57, 0xca, + 0xcd, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xd3, 0xd6, 0xd3, 0x59, 0x5a, 0x59, 0x34, + 0x35, 0x34, 0x99, 0x9c, 0x99, 0xff, 0xff, 0xff, + 0xd4, 0xd7, 0xd4, 0x6a, 0x6d, 0x6a, 0x3a, 0x3c, + 0x3a, 0xa8, 0xab, 0xa8, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x75, 0x77, + 0x75, 0x4a, 0x4b, 0x4a, 0x4b, 0x4d, 0x4b, 0x95, + 0x98, 0x95, 0x5b, 0x5c, 0x5b, 0x78, 0x7a, 0x78, + 0x5a, 0x5c, 0x5a, 0xdd, 0xe0, 0xdd, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, + 0x67, 0x6a, 0x67, 0x31, 0x32, 0x31, 0xd4, 0xd7, + 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc6, 0xc9, 0xc6, 0xe1, 0xe5, 0xe1, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x6a, 0x6d, 0x6a, 0x1e, 0x1f, 0x1e, + 0x7d, 0x7f, 0x7d, 0xde, 0xe2, 0xde, 0x76, 0x78, + 0x76, 0x30, 0x31, 0x30, 0x64, 0x65, 0x64, 0xed, + 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa6, 0xaa, 0xa6, 0x7f, + 0x82, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6a, 0x6b, + 0x6a, 0x37, 0x39, 0x37, 0xc1, 0xc4, 0xc1, 0xff, + 0xff, 0xff, 0xb2, 0xb6, 0xb2, 0x2c, 0x2d, 0x2c, + 0x7f, 0x81, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xbf, 0xc3, 0xbf, 0xaf, 0xb2, 0xaf, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xba, 0xbd, 0xba, 0xd1, 0xd4, 0xd1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x28, 0x75, 0x75, 0x61, 0x79, 0x29, + 0x67, 0x65, 0x67, 0x6c, 0x2d, 0x32, 0x2e, 0x70, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x98, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x64, 0x6b, 0x50, 0x00, 0x00, 0xd6, 0x98, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x10, + 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x68, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc5, 0xc9, 0xc5, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf9, 0xfa, 0xf9, 0xff, + 0x74, 0x77, 0x74, 0xff, 0x56, 0x58, 0x56, 0xff, + 0xc3, 0xc6, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x97, 0x9a, 0x97, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x3c, 0x3d, 0x3c, 0xff, 0xaa, 0xad, 0xaa, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0x8e, 0x91, 0x8e, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0x66, 0x68, 0x66, 0xff, + 0x27, 0x27, 0x27, 0xff, 0x43, 0x44, 0x43, 0xff, + 0x9e, 0xa2, 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x96, 0x99, 0x96, 0xff, 0x1c, 0x1d, 0x1c, 0xff, + 0x8c, 0x8e, 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xba, 0xbe, 0xba, 0xff, + 0x49, 0x4b, 0x49, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0x49, 0x4a, 0x49, 0xff, 0xb0, 0xb3, 0xb0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x8a, 0x8c, 0x8a, 0xff, 0x2d, 0x2e, 0x2d, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0xdf, 0xe3, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6c, 0x6f, 0x6c, 0xff, 0x6e, 0x71, 0x6e, 0xff, + 0x87, 0x8a, 0x87, 0xff, 0x59, 0x5b, 0x59, 0xff, + 0xbf, 0xc2, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0x78, 0x7a, 0x78, 0xff, 0x5c, 0x5d, 0x5c, 0xff, + 0x4b, 0x4d, 0x4b, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0x5a, 0x5c, 0x5a, 0xff, + 0xc1, 0xc5, 0xc1, 0xff, 0x8a, 0x8d, 0x8a, 0xff, + 0x60, 0x62, 0x60, 0xff, 0xcd, 0xd0, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe2, 0xdf, 0xff, + 0x60, 0x62, 0x60, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x56, 0x58, 0x56, 0xff, 0xa9, 0xac, 0xa9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd9, 0xdd, 0xd9, 0xff, 0x5f, 0x61, 0x5f, 0xff, + 0x97, 0x9a, 0x97, 0xff, 0xd5, 0xd8, 0xd5, 0xff, + 0x66, 0x68, 0x66, 0xff, 0x86, 0x89, 0x86, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0x9d, 0xa0, 0x9d, 0xff, + 0x6a, 0x6c, 0x6a, 0xff, 0x90, 0x93, 0x90, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x8e, 0x90, 0x8e, 0xff, + 0x6e, 0x71, 0x6e, 0xff, 0xec, 0xf0, 0xec, 0xff, + 0xa8, 0xab, 0xa8, 0xff, 0x56, 0x59, 0x56, 0xff, + 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0x6f, 0x71, 0x6f, 0xff, 0xaf, 0xb2, 0xaf, 0xff, + 0x75, 0x78, 0x75, 0xff, 0x73, 0x76, 0x73, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc5, 0xc9, 0xc5, 0xff, + 0x51, 0x54, 0x51, 0xff, 0xc8, 0xcc, 0xc8, 0xff, + 0xde, 0xe2, 0xde, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xa9, 0xac, 0xa9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xce, 0xd2, 0xce, 0xff, + 0x6a, 0x6c, 0x6a, 0xff, 0xc3, 0xc7, 0xc3, 0xff, + 0x79, 0x7b, 0x79, 0xff, 0x74, 0x76, 0x74, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xf0, 0xed, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x8d, 0x8f, 0x8d, 0xff, + 0xeb, 0xef, 0xeb, 0xff, 0x98, 0x9b, 0x98, 0xff, + 0x69, 0x6b, 0x69, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb9, 0xbc, 0xb9, 0xff, + 0x65, 0x67, 0x65, 0xff, 0xd0, 0xd3, 0xd0, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x8c, 0x8e, 0x8c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9a, 0x9d, 0x9a, 0xff, 0x60, 0x61, 0x60, 0xff, + 0xe3, 0xe6, 0xe3, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0x57, 0x59, 0x57, 0xff, 0xc4, 0xc7, 0xc4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x92, 0x94, 0x92, 0xff, + 0x71, 0x73, 0x71, 0xff, 0xd5, 0xd9, 0xd5, 0xff, + 0x61, 0x64, 0x61, 0xff, 0xa3, 0xa6, 0xa3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd0, 0xd3, 0xd0, 0xff, 0x54, 0x55, 0x54, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x70, 0x72, 0x70, 0xff, 0x91, 0x94, 0x91, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0x60, 0x62, 0x60, 0xff, + 0x92, 0x94, 0x92, 0xff, 0xd8, 0xdb, 0xd8, 0xff, + 0x51, 0x52, 0x51, 0xff, 0xb1, 0xb4, 0xb1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0x59, 0x5b, 0x59, 0xff, + 0xb4, 0xb7, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0x5f, 0x61, 0x5f, 0xff, + 0xd6, 0xda, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9e, 0xa0, 0x9e, 0xff, 0x59, 0x5b, 0x59, 0xff, + 0xcd, 0xd1, 0xcd, 0xff, 0xca, 0xce, 0xca, 0xff, + 0x57, 0x59, 0x57, 0xff, 0xc7, 0xca, 0xc7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x66, 0x68, 0x66, 0xff, + 0xb4, 0xb7, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0xcb, 0xc7, 0xff, 0x5f, 0x60, 0x5f, 0xff, + 0xbb, 0xbd, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbf, 0xbb, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xeb, 0xef, 0xeb, 0xff, 0x9b, 0x9d, 0x9b, 0xff, + 0x70, 0x72, 0x70, 0xff, 0xe9, 0xec, 0xe9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0x57, 0x59, 0x57, 0xff, + 0xbc, 0xc0, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0xdf, 0xe3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0x66, 0x68, 0x66, 0xff, + 0xa8, 0xab, 0xa8, 0xff, 0xf2, 0xf6, 0xf2, 0xff, + 0xe0, 0xe4, 0xe0, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xa7, 0xa9, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf9, 0xfa, 0xf9, 0xff, 0xed, 0xef, 0xed, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb9, 0xb6, 0xff, 0x61, 0x64, 0x61, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xf0, 0xee, 0xff, 0x98, 0x9b, 0x98, 0xff, + 0x57, 0x59, 0x57, 0xff, 0xa8, 0xab, 0xa8, 0xff, + 0xed, 0xf1, 0xed, 0xff, 0xf4, 0xf8, 0xf4, 0xff, + 0xb1, 0xb4, 0xb1, 0xff, 0x57, 0x59, 0x57, 0xff, + 0xd7, 0xdb, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xdf, 0xdd, 0xff, 0xae, 0xb1, 0xae, 0xff, + 0xc7, 0xc9, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xba, 0xbe, 0xba, 0xff, 0xa3, 0xa6, 0xa3, 0xff, + 0xee, 0xf2, 0xee, 0xff, 0xe1, 0xe4, 0xe1, 0xff, + 0x82, 0x85, 0x82, 0xff, 0x80, 0x83, 0x80, 0xff, + 0xba, 0xbd, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe3, 0xdf, 0xff, + 0x95, 0x97, 0x95, 0xff, 0xc5, 0xc8, 0xc5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe0, 0xdd, 0xff, + 0x85, 0x88, 0x85, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0xa9, 0xab, 0xa9, 0xff, 0xec, 0xf0, 0xec, 0xff, + 0xf8, 0xfc, 0xf8, 0xff, 0xe3, 0xe7, 0xe3, 0xff, + 0x76, 0x78, 0x76, 0xff, 0x7d, 0x80, 0x7d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xd2, 0xd5, 0xd2, 0xff, 0x4d, 0x4f, 0x4d, 0xff, + 0x85, 0x87, 0x85, 0xff, 0xd8, 0xd9, 0xd8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xac, 0xae, 0xac, 0xff, 0x9f, 0xa2, 0x9f, 0xff, + 0xae, 0xb1, 0xae, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0x54, 0x56, 0x54, 0xff, 0xb6, 0xb9, 0xb6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0x7a, 0x7c, 0x7a, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0x9d, 0xa0, 0x9d, 0xff, 0xcc, 0xd0, 0xcc, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0xc5, 0xc9, 0xc5, 0xff, + 0xf3, 0xf6, 0xf3, 0xff, 0xf8, 0xfc, 0xf8, 0xff, + 0xee, 0xf2, 0xee, 0xff, 0x92, 0x95, 0x92, 0xff, + 0x59, 0x5b, 0x59, 0xff, 0xc2, 0xc6, 0xc2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xef, 0xee, 0xff, 0xf6, 0xf6, 0xf6, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0x61, 0x63, 0x61, 0xff, + 0x57, 0x58, 0x57, 0xff, 0x9f, 0xa2, 0x9f, 0xff, + 0xee, 0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbe, 0xc1, 0xbe, 0xff, + 0x84, 0x87, 0x84, 0xff, 0xc5, 0xc9, 0xc5, 0xff, + 0x63, 0x65, 0x63, 0xff, 0x98, 0x9b, 0x98, 0xff, + 0xf5, 0xf9, 0xf5, 0xff, 0xe3, 0xe7, 0xe3, 0xff, + 0x60, 0x61, 0x60, 0xff, 0x4d, 0x4f, 0x4d, 0xff, + 0xaa, 0xad, 0xaa, 0xff, 0xef, 0xf3, 0xef, 0xff, + 0xe9, 0xed, 0xe9, 0xff, 0xa0, 0xa3, 0xa0, 0xff, + 0x4f, 0x51, 0x4f, 0xff, 0xd6, 0xd9, 0xd6, 0xff, + 0xed, 0xf1, 0xed, 0xff, 0xf6, 0xfa, 0xf6, 0xff, + 0xf4, 0xf8, 0xf4, 0xff, 0xdd, 0xe1, 0xdd, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0x4e, 0x50, 0x4e, 0xff, + 0xa2, 0xa5, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcd, 0xcc, 0xff, + 0xa1, 0xa3, 0xa1, 0xff, 0xa7, 0xaa, 0xa7, 0xff, + 0xbf, 0xc2, 0xbf, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0x6c, 0x6e, 0x6c, 0xff, 0x85, 0x88, 0x85, 0xff, + 0xb2, 0xb4, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc6, 0xc9, 0xc6, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x8c, 0x8f, 0x8c, 0xff, 0xc6, 0xca, 0xc6, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xbb, 0xbe, 0xbb, 0xff, + 0xf8, 0xfc, 0xf8, 0xff, 0xeb, 0xef, 0xeb, 0xff, + 0x70, 0x72, 0x70, 0xff, 0x1f, 0x1f, 0x1f, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0xf1, 0xf5, 0xf1, 0xff, + 0xf8, 0xfc, 0xf8, 0xff, 0xce, 0xd1, 0xce, 0xff, + 0x55, 0x57, 0x55, 0xff, 0xcc, 0xd0, 0xcc, 0xff, + 0xf7, 0xfb, 0xf7, 0xff, 0xe7, 0xeb, 0xe7, 0xff, + 0xaa, 0xac, 0xaa, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0x56, 0x58, 0x56, 0xff, 0xa5, 0xa8, 0xa5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf5, 0xf5, 0xff, 0x9b, 0x9d, 0x9b, 0xff, + 0x45, 0x46, 0x45, 0xff, 0x67, 0x69, 0x67, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0x63, 0x66, 0x63, 0xff, + 0xb6, 0xb9, 0xb6, 0xff, 0x8b, 0x8d, 0x8b, 0xff, + 0x8b, 0x8e, 0x8b, 0xff, 0xc2, 0xc4, 0xc2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdc, 0xdf, 0xdc, 0xff, + 0x72, 0x74, 0x72, 0xff, 0x8f, 0x92, 0x8f, 0xff, + 0xe5, 0xe8, 0xe5, 0xff, 0xdb, 0xde, 0xdb, 0xff, + 0x61, 0x63, 0x61, 0xff, 0xa7, 0xab, 0xa7, 0xff, + 0xf0, 0xf4, 0xf0, 0xff, 0xc5, 0xc8, 0xc5, 0xff, + 0x4a, 0x4c, 0x4a, 0xff, 0x2b, 0x2b, 0x2b, 0xff, + 0xaa, 0xad, 0xaa, 0xff, 0xf0, 0xf4, 0xf0, 0xff, + 0xf6, 0xfa, 0xf6, 0xff, 0xcc, 0xd0, 0xcc, 0xff, + 0x55, 0x57, 0x55, 0xff, 0xbd, 0xc1, 0xbd, 0xff, + 0xb1, 0xb4, 0xb1, 0xff, 0x6e, 0x6f, 0x6e, 0xff, + 0x59, 0x5a, 0x59, 0xff, 0x8d, 0x8f, 0x8d, 0xff, + 0xcb, 0xcf, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0xa8, 0xab, 0xa8, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0x7d, 0x7f, 0x7d, 0xff, + 0x40, 0x42, 0x40, 0xff, 0x62, 0x64, 0x62, 0xff, + 0xbb, 0xbe, 0xbb, 0xff, 0xd2, 0xd5, 0xd2, 0xff, + 0x7e, 0x80, 0x7e, 0xff, 0x99, 0x9b, 0x99, 0xff, + 0xdc, 0xdd, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd2, 0xd6, 0xd2, 0xff, 0x76, 0x79, 0x76, 0xff, + 0x7f, 0x82, 0x7f, 0xff, 0xe0, 0xe3, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xf0, 0xed, 0xff, + 0x6d, 0x70, 0x6d, 0xff, 0x76, 0x79, 0x76, 0xff, + 0x94, 0x97, 0x94, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0xa3, 0xa6, 0xa3, 0xff, + 0xbc, 0xbf, 0xbc, 0xff, 0x88, 0x8b, 0x88, 0xff, + 0x47, 0x49, 0x47, 0xff, 0xad, 0xb0, 0xad, 0xff, + 0x76, 0x79, 0x76, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcd, 0xcb, 0xff, + 0x84, 0x86, 0x84, 0xff, 0x8a, 0x8d, 0x8a, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x97, 0x99, 0x97, 0xff, + 0xea, 0xee, 0xea, 0xff, 0xed, 0xf1, 0xed, 0xff, + 0xaf, 0xb2, 0xaf, 0xff, 0x79, 0x7b, 0x79, 0xff, + 0x9c, 0x9f, 0x9c, 0xff, 0xf2, 0xf2, 0xf2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0x74, 0x76, 0x74, 0xff, 0x73, 0x75, 0x73, 0xff, + 0xd9, 0xdd, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0x58, 0x5a, 0x58, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0xd6, 0xd9, 0xd6, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0x8d, 0x90, 0x8d, 0xff, 0x66, 0x68, 0x66, 0xff, + 0x62, 0x64, 0x62, 0xff, 0x54, 0x56, 0x54, 0xff, + 0x3f, 0x40, 0x3f, 0xff, 0xab, 0xae, 0xab, 0xff, + 0xd7, 0xdb, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xed, 0xed, 0xff, + 0x96, 0x99, 0x96, 0xff, 0x7b, 0x7e, 0x7b, 0xff, + 0xc3, 0xc7, 0xc3, 0xff, 0xec, 0xf0, 0xec, 0xff, + 0xeb, 0xef, 0xeb, 0xff, 0xc2, 0xc5, 0xc2, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0x75, 0x77, 0x75, 0xff, + 0x80, 0x83, 0x80, 0xff, 0xb8, 0xba, 0xb8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0x68, 0x6a, 0x68, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xcc, 0xd0, 0xcc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xc6, 0xca, 0xc6, 0xff, 0x8c, 0x8f, 0x8c, 0xff, + 0x36, 0x36, 0x36, 0xff, 0x60, 0x62, 0x60, 0xff, + 0xda, 0xde, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0xc4, 0xc3, 0xff, 0x70, 0x72, 0x70, 0xff, + 0xb1, 0xb4, 0xb1, 0xff, 0xe5, 0xe8, 0xe5, 0xff, + 0xa9, 0xab, 0xa9, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0x85, 0x88, 0x85, 0xff, 0xb1, 0xb3, 0xb1, 0xff, + 0x80, 0x82, 0x80, 0xff, 0x8e, 0x91, 0x8e, 0xff, + 0xce, 0xd0, 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0x6f, 0x71, 0x6f, 0xff, 0x63, 0x65, 0x63, 0xff, + 0xcb, 0xcf, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa3, 0xa7, 0xa3, 0xff, 0x56, 0x59, 0x56, 0xff, + 0x8d, 0x90, 0x8d, 0xff, 0x59, 0x5c, 0x59, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf6, 0xf7, 0xf6, 0xff, 0x85, 0x87, 0x85, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x81, 0x83, 0x81, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0x83, 0x85, 0x83, 0xff, + 0xca, 0xcb, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd3, 0xd4, 0xd3, 0xff, 0x6b, 0x6e, 0x6b, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0xe7, 0xe8, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0x79, 0x7b, 0x79, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0xcd, 0xd1, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc4, 0xc7, 0xc4, 0xff, + 0x4f, 0x51, 0x4f, 0xff, 0x8d, 0x8f, 0x8d, 0xff, + 0xe1, 0xe5, 0xe1, 0xff, 0x81, 0x84, 0x81, 0xff, + 0x7a, 0x7d, 0x7a, 0xff, 0xf1, 0xf3, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0xf3, 0xf3, 0xff, 0xab, 0xae, 0xab, 0xff, + 0x3f, 0x40, 0x3f, 0xff, 0x64, 0x66, 0x64, 0xff, + 0x98, 0x99, 0x98, 0xff, 0xe5, 0xe5, 0xe5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xad, 0xae, 0xad, 0xff, + 0x7a, 0x7d, 0x7a, 0xff, 0xbe, 0xc1, 0xbe, 0xff, + 0xfa, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x96, 0x99, 0x96, 0xff, 0x65, 0x68, 0x65, 0xff, + 0xc3, 0xc7, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd1, 0xd5, 0xd1, 0xff, 0x6b, 0x6d, 0x6b, 0xff, + 0x8f, 0x92, 0x8f, 0xff, 0xf1, 0xf3, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbe, 0xbb, 0xff, + 0x63, 0x66, 0x63, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc9, 0xca, 0xc9, 0xff, + 0x6d, 0x70, 0x6d, 0xff, 0x88, 0x8b, 0x88, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x89, 0x8b, 0x89, 0xff, 0x93, 0x95, 0x93, 0xff, + 0xda, 0xdd, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb4, 0xb0, 0xff, + 0x4d, 0x4f, 0x4d, 0xff, 0xad, 0xb0, 0xad, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcd, 0xd1, 0xcd, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0x66, 0x68, 0x66, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0x72, 0x74, 0x72, 0xff, 0x8d, 0x8f, 0x8d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0x8a, 0x8c, 0x8a, 0xff, 0x95, 0x97, 0x95, 0xff, + 0xf0, 0xf1, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf8, 0xf7, 0xff, + 0xc9, 0xcd, 0xc9, 0xff, 0x60, 0x61, 0x60, 0xff, + 0xaf, 0xb2, 0xaf, 0xff, 0xeb, 0xed, 0xeb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd2, 0xd5, 0xd2, 0xff, 0x62, 0x64, 0x62, 0xff, + 0x9f, 0xa2, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7e, 0x7b, 0xff, + 0x67, 0x69, 0x67, 0xff, 0xc7, 0xcb, 0xc7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0x57, 0x59, 0x57, 0xff, + 0xdf, 0xe3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, 0xff, + 0xb0, 0xb3, 0xb0, 0xff, 0x63, 0x65, 0x63, 0xff, + 0xd2, 0xd3, 0xd2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe2, 0xe4, 0xe2, 0xff, 0x9a, 0x9c, 0x9a, 0xff, + 0x6b, 0x6c, 0x6b, 0xff, 0xd7, 0xdb, 0xd7, 0xff, + 0xfb, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x96, 0x98, 0x96, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0x5c, 0x5e, 0x5c, 0xff, + 0xc5, 0xc9, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xd2, 0xcf, 0xff, 0x52, 0x54, 0x52, 0xff, + 0xb2, 0xb5, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcb, 0xcc, 0xcb, 0xff, 0x7e, 0x80, 0x7e, 0xff, + 0x9f, 0xa1, 0x9f, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcc, 0xce, 0xcc, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0xa5, 0xa7, 0xa5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x85, 0x87, 0x85, 0xff, 0x7e, 0x81, 0x7e, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0x92, 0x95, 0x92, 0xff, + 0x42, 0x43, 0x42, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x92, 0x95, 0x92, 0xff, + 0x6a, 0x6c, 0x6a, 0xff, 0xdd, 0xe0, 0xdd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0x8f, 0x91, 0x8f, 0xff, + 0x8f, 0x92, 0x8f, 0xff, 0xd9, 0xdc, 0xd9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0x81, 0x84, 0x81, 0xff, + 0xca, 0xcb, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x88, 0x8a, 0x88, 0xff, 0x89, 0x8c, 0x89, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x58, 0x5a, 0x58, 0xff, + 0x50, 0x51, 0x50, 0xff, 0xb3, 0xb7, 0xb3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd9, 0xdd, 0xd9, 0xff, + 0x63, 0x65, 0x63, 0xff, 0x9c, 0x9f, 0x9c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf4, 0xf4, 0xf4, 0xff, 0xbb, 0xbe, 0xbb, 0xff, + 0x6d, 0x6f, 0x6d, 0xff, 0xcc, 0xce, 0xcc, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd5, 0xd6, 0xd5, 0xff, 0x85, 0x88, 0x85, 0xff, + 0x88, 0x8b, 0x88, 0xff, 0xf4, 0xf4, 0xf4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0xba, 0xbd, 0xba, 0xff, 0xa5, 0xa8, 0xa5, 0xff, + 0xb7, 0xba, 0xb7, 0xff, 0xc0, 0xc4, 0xc0, 0xff, + 0xcf, 0xd3, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x84, 0x86, 0x84, 0xff, 0x78, 0x7a, 0x78, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc7, 0xcb, 0xc7, 0xff, + 0x53, 0x55, 0x53, 0xff, 0x94, 0x97, 0x94, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xdf, 0xe3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0x61, 0x63, 0x61, 0xff, + 0xba, 0xbd, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd1, 0xd2, 0xd1, 0xff, + 0x88, 0x8b, 0x88, 0xff, 0xa0, 0xa3, 0xa0, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbd, 0xbf, 0xbd, 0xff, + 0x7c, 0x7f, 0x7c, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd6, 0xda, 0xd6, 0xff, + 0x8e, 0x91, 0x8e, 0xff, 0x58, 0x5a, 0x58, 0xff, + 0x5a, 0x5c, 0x5a, 0xff, 0x72, 0x74, 0x72, 0xff, + 0x6f, 0x71, 0x6f, 0xff, 0x5c, 0x5e, 0x5c, 0xff, + 0x53, 0x54, 0x53, 0xff, 0x90, 0x93, 0x90, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x90, 0x93, 0x90, 0xff, 0x64, 0x67, 0x64, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbb, 0xbe, 0xbb, 0xff, 0x56, 0x58, 0x56, 0xff, + 0x82, 0x85, 0x82, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xda, 0xde, 0xda, 0xff, 0x55, 0x58, 0x55, 0xff, + 0xa5, 0xa9, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0x91, 0x94, 0x91, 0xff, + 0x5e, 0x60, 0x5e, 0xff, 0xb1, 0xb4, 0xb1, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0x73, 0x76, 0x73, 0xff, 0x9b, 0x9e, 0x9b, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0x64, 0x67, 0x64, 0xff, 0xb8, 0xbc, 0xb8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd7, 0xdb, 0xd7, 0xff, + 0x95, 0x98, 0x95, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0x6a, 0x6d, 0x6a, 0xff, 0xa7, 0xaa, 0xa7, 0xff, + 0xd5, 0xd9, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf9, 0xfa, 0xf9, 0xff, 0xd6, 0xda, 0xd6, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0x55, 0x57, 0x55, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xbf, 0xbc, 0xff, 0x53, 0x55, 0x53, 0xff, + 0xd2, 0xd6, 0xd2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xca, 0xcd, 0xca, 0xff, + 0x66, 0x69, 0x66, 0xff, 0x75, 0x78, 0x75, 0xff, + 0xe1, 0xe4, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x90, 0x93, 0x90, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe1, 0xe4, 0xe1, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0x57, 0x59, 0x57, 0xff, + 0x94, 0x96, 0x94, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc5, 0xc8, 0xc5, 0xff, + 0x67, 0x69, 0x67, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0x55, 0x57, 0x55, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0xc5, 0xc9, 0xc5, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0x6e, 0x70, 0x6e, 0xff, + 0x6d, 0x6f, 0x6d, 0xff, 0xa1, 0xa4, 0xa1, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x8e, 0x90, 0x8e, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe4, 0xe0, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0x9a, 0x9c, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0x72, 0x74, 0x72, 0xff, + 0x7d, 0x7f, 0x7d, 0xff, 0xd8, 0xdb, 0xd8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0x61, 0x63, 0x61, 0xff, 0x9d, 0xa0, 0x9d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa3, 0xa7, 0xa3, 0xff, + 0x61, 0x63, 0x61, 0xff, 0x79, 0x7b, 0x79, 0xff, + 0xd5, 0xd8, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0x73, 0x76, 0x73, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0x59, 0x5b, 0x59, 0xff, 0xd0, 0xd4, 0xd0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0xdc, 0xe0, 0xdc, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xd8, 0xdc, 0xd8, 0xff, 0xcd, 0xd0, 0xcd, 0xff, + 0xca, 0xcd, 0xca, 0xff, 0xca, 0xcd, 0xca, 0xff, + 0xca, 0xcd, 0xca, 0xff, 0xca, 0xcd, 0xca, 0xff, + 0xd0, 0xd4, 0xd0, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xce, 0xd2, 0xce, 0xff, + 0xc9, 0xcc, 0xc9, 0xff, 0xbc, 0xbf, 0xbc, 0xff, + 0xb5, 0xb9, 0xb5, 0xff, 0xaa, 0xad, 0xaa, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0xa4, 0xa7, 0xa4, 0xff, + 0x90, 0x93, 0x90, 0xff, 0x75, 0x77, 0x75, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0x6d, 0x70, 0x6d, 0xff, 0xa0, 0xa3, 0xa0, 0xff, + 0xd3, 0xd6, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb3, 0xb6, 0xb3, 0xff, + 0x4b, 0x4c, 0x4b, 0xff, 0xe0, 0xe3, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa3, 0xa6, 0xa3, 0xff, + 0x5f, 0x61, 0x5f, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0x85, 0x88, 0x85, 0xff, 0x77, 0x79, 0x77, 0xff, + 0xd9, 0xdd, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0x60, 0x62, 0x60, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcc, 0xcf, 0xcc, 0xff, 0x71, 0x73, 0x71, 0xff, + 0x5c, 0x5d, 0x5c, 0xff, 0x8b, 0x8e, 0x8b, 0xff, + 0xbb, 0xbe, 0xbb, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xe2, 0xe5, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe3, 0xe0, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xdc, 0xe0, 0xdc, 0xff, + 0xcd, 0xd1, 0xcd, 0xff, 0xbc, 0xbf, 0xbc, 0xff, + 0xaf, 0xb3, 0xaf, 0xff, 0x5f, 0x60, 0x5f, 0xff, + 0x9c, 0x9e, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xab, 0xae, 0xab, 0xff, + 0x4b, 0x4d, 0x4b, 0xff, 0x95, 0x98, 0x95, 0xff, + 0x95, 0x98, 0x95, 0xff, 0x84, 0x86, 0x84, 0xff, + 0x7a, 0x7d, 0x7a, 0xff, 0x7f, 0x81, 0x7f, 0xff, + 0x81, 0x83, 0x81, 0xff, 0x80, 0x83, 0x80, 0xff, + 0x73, 0x75, 0x73, 0xff, 0x6d, 0x6f, 0x6d, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x5a, 0x5c, 0x5a, 0xff, + 0x5c, 0x5f, 0x5c, 0xff, 0x63, 0x66, 0x63, 0xff, + 0x63, 0x66, 0x63, 0xff, 0x63, 0x66, 0x63, 0xff, + 0x67, 0x6a, 0x67, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0x6c, 0x6e, 0x6c, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0x6c, 0x6e, 0x6c, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x64, 0x66, 0x64, 0xff, 0x6d, 0x70, 0x6d, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x63, 0x66, 0x63, 0xff, + 0x63, 0x66, 0x63, 0xff, 0x63, 0x66, 0x63, 0xff, + 0x63, 0x66, 0x63, 0xff, 0x63, 0x66, 0x63, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x62, 0x64, 0x62, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x62, 0x63, 0x62, 0xff, + 0x5d, 0x5e, 0x5d, 0xff, 0x5a, 0x5c, 0x5a, 0xff, + 0x64, 0x66, 0x64, 0xff, 0x6d, 0x70, 0x6d, 0xff, + 0x84, 0x86, 0x84, 0xff, 0x8a, 0x8d, 0x8a, 0xff, + 0xa2, 0xa5, 0xa2, 0xff, 0xc7, 0xcb, 0xc7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xcb, 0xff, + 0x57, 0x59, 0x57, 0xff, 0xd6, 0xda, 0xd6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0x6b, 0x6e, 0x6b, 0xff, 0x73, 0x75, 0x73, 0xff, + 0xbf, 0xc2, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xbf, 0xbc, 0xff, 0x5e, 0x5f, 0x5e, 0xff, + 0x97, 0x99, 0x97, 0xff, 0xe1, 0xe4, 0xe1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xda, 0xde, 0xda, 0xff, 0x61, 0x62, 0x61, 0xff, + 0xaf, 0xb2, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0x6b, 0x6e, 0x6b, 0xff, + 0x60, 0x61, 0x60, 0xff, 0x64, 0x66, 0x64, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0x86, 0x89, 0x86, 0xff, + 0x94, 0x97, 0x94, 0xff, 0x94, 0x96, 0x94, 0xff, + 0x94, 0x96, 0x94, 0xff, 0x91, 0x94, 0x91, 0xff, + 0x91, 0x94, 0x91, 0xff, 0x91, 0x94, 0x91, 0xff, + 0x93, 0x96, 0x93, 0xff, 0x92, 0x95, 0x92, 0xff, + 0x84, 0x86, 0x84, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0x66, 0x68, 0x66, 0xff, 0x6e, 0x70, 0x6e, 0xff, + 0x6a, 0x6d, 0x6a, 0xff, 0x60, 0x62, 0x60, 0xff, + 0x50, 0x52, 0x50, 0xff, 0x4b, 0x4c, 0x4b, 0xff, + 0xc7, 0xcb, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, 0xff, + 0x43, 0x44, 0x43, 0xff, 0x6f, 0x72, 0x6f, 0xff, + 0x76, 0x79, 0x76, 0xff, 0x76, 0x78, 0x76, 0xff, + 0x75, 0x78, 0x75, 0xff, 0x83, 0x86, 0x83, 0xff, + 0x8a, 0x8c, 0x8a, 0xff, 0x94, 0x97, 0x94, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xb2, 0xb5, 0xb2, 0xff, + 0xb8, 0xbb, 0xb8, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xbf, 0xc3, 0xbf, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xc6, 0xca, 0xc6, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0xd7, 0xda, 0xd7, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0xd7, 0xda, 0xd7, 0xff, 0xd5, 0xd8, 0xd5, 0xff, + 0xc9, 0xcc, 0xc9, 0xff, 0xca, 0xcd, 0xca, 0xff, + 0xc1, 0xc4, 0xc1, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xbf, 0xc3, 0xbf, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xbf, 0xc3, 0xbf, 0xff, 0xbf, 0xc3, 0xbf, 0xff, + 0xc1, 0xc4, 0xc1, 0xff, 0xc7, 0xcb, 0xc7, 0xff, + 0xd0, 0xd3, 0xd0, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0xd7, 0xda, 0xd7, 0xff, 0xd9, 0xdd, 0xd9, 0xff, + 0xde, 0xe2, 0xde, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0x5a, 0x5c, 0x5a, 0xff, 0xcb, 0xcf, 0xcb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc1, 0xc5, 0xc1, 0xff, 0x70, 0x72, 0x70, 0xff, + 0x58, 0x5a, 0x58, 0xff, 0x7a, 0x7c, 0x7a, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0x93, 0x97, 0x93, 0xff, + 0x49, 0x4b, 0x49, 0xff, 0x71, 0x73, 0x71, 0xff, + 0x91, 0x93, 0x91, 0xff, 0xe2, 0xe5, 0xe2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x90, 0x92, 0x90, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0xe1, 0xe4, 0xe1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdd, 0xe1, 0xdd, 0xff, + 0xc9, 0xcc, 0xc9, 0xff, 0xa1, 0xa4, 0xa1, 0xff, + 0x99, 0x9b, 0x99, 0xff, 0x97, 0x9a, 0x97, 0xff, + 0x81, 0x84, 0x81, 0xff, 0x7f, 0x81, 0x7f, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x73, 0x76, 0x73, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0x99, 0x9c, 0x99, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0xb1, 0xb4, 0xb1, 0xff, + 0xc3, 0xc7, 0xc3, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0x70, 0x73, 0x70, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0xdf, 0xe3, 0xdf, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xba, 0xbe, 0xba, 0xff, + 0x52, 0x54, 0x52, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0xcc, 0xc9, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xbc, 0xbd, 0xbc, 0xff, + 0xa9, 0xaa, 0xa9, 0xff, 0xa5, 0xa8, 0xa5, 0xff, + 0x5a, 0x5c, 0x5a, 0xff, 0xaf, 0xb3, 0xaf, 0xff, + 0xe9, 0xeb, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd9, 0xdc, 0xd9, 0xff, + 0x53, 0x55, 0x53, 0xff, 0xa8, 0xab, 0xa8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc4, 0xc8, 0xc4, 0xff, + 0x55, 0x58, 0x55, 0xff, 0xbc, 0xbf, 0xbc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0x70, 0x73, 0x70, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb8, 0xbb, 0xb8, 0xff, 0x6b, 0x6d, 0x6b, 0xff, + 0x7e, 0x81, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0xa6, 0xa7, 0xa6, 0xff, + 0x8c, 0x8d, 0x8c, 0xff, 0xa4, 0xa7, 0xa4, 0xff, + 0x95, 0x98, 0x95, 0xff, 0x73, 0x76, 0x73, 0xff, + 0xb5, 0xb7, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x77, 0x7a, 0x77, 0xff, 0x75, 0x78, 0x75, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, 0xff, + 0x6c, 0x6e, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xce, 0xd1, 0xce, 0xff, 0x68, 0x6a, 0x68, 0xff, + 0xc0, 0xc3, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd4, 0xd7, 0xd4, 0xff, 0x90, 0x92, 0x90, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0x81, 0x84, 0x81, 0xff, + 0xd6, 0xd9, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0x78, 0x7a, 0x78, 0xff, 0xaf, 0xb1, 0xaf, 0xff, + 0xdf, 0xe2, 0xdf, 0xff, 0x75, 0x77, 0x75, 0xff, + 0x8b, 0x8e, 0x8b, 0xff, 0xdd, 0xde, 0xdd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x59, 0x5b, 0x59, 0xff, + 0xcf, 0xd3, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd9, 0xdc, 0xd9, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x9a, 0x9c, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb2, 0xb5, 0xb2, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xd3, 0xd7, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0x9b, 0x9e, 0x9b, 0xff, + 0x60, 0x62, 0x60, 0xff, 0x6b, 0x6e, 0x6b, 0xff, + 0xb8, 0xbb, 0xb8, 0xff, 0xed, 0xf0, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe2, 0xe0, 0xff, + 0x75, 0x78, 0x75, 0xff, 0xaf, 0xb2, 0xaf, 0xff, + 0xeb, 0xee, 0xeb, 0xff, 0xbd, 0xc0, 0xbd, 0xff, + 0x8b, 0x8e, 0x8b, 0xff, 0xa1, 0xa3, 0xa1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd4, 0xd7, 0xd4, 0xff, 0x5d, 0x5e, 0x5d, 0xff, + 0x9f, 0xa2, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9a, 0x9d, 0x9a, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xd3, 0xd6, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0x73, 0x75, 0x73, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcc, 0xd0, 0xcc, 0xff, + 0x74, 0x76, 0x74, 0xff, 0x62, 0x64, 0x62, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdc, 0xde, 0xdc, 0xff, + 0x87, 0x89, 0x87, 0xff, 0x95, 0x97, 0x95, 0xff, + 0xf6, 0xfa, 0xf6, 0xff, 0xf0, 0xf4, 0xf0, 0xff, + 0x97, 0x9b, 0x97, 0xff, 0x86, 0x88, 0x86, 0xff, + 0xe2, 0xe3, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x75, 0x78, 0x75, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe9, 0xec, 0xe9, 0xff, + 0x61, 0x63, 0x61, 0xff, 0x95, 0x98, 0x95, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x75, 0x77, 0x75, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0x7c, 0x7f, 0x7c, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0xd8, 0xdb, 0xd8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfb, 0xff, + 0x82, 0x84, 0x82, 0xff, 0x93, 0x95, 0x93, 0xff, + 0xe1, 0xe4, 0xe1, 0xff, 0xf1, 0xf5, 0xf1, 0xff, + 0xac, 0xb0, 0xac, 0xff, 0x82, 0x84, 0x82, 0xff, + 0xd5, 0xd7, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0x56, 0x59, 0x56, 0xff, 0xdd, 0xe1, 0xdd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0x59, 0x5c, 0x59, 0xff, 0xce, 0xd2, 0xce, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0xa5, 0xa8, 0xa5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc6, 0xc9, 0xc6, 0xff, 0x60, 0x62, 0x60, 0xff, + 0xb6, 0xba, 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf4, 0xf2, 0xff, + 0xa2, 0xa5, 0xa2, 0xff, 0x82, 0x84, 0x82, 0xff, + 0xd8, 0xdc, 0xd8, 0xff, 0xf0, 0xf4, 0xf0, 0xff, + 0xb0, 0xb3, 0xb0, 0xff, 0x60, 0x62, 0x60, 0xff, + 0xdc, 0xdd, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0xd8, 0xd4, 0xff, + 0x57, 0x59, 0x57, 0xff, 0xbc, 0xc0, 0xbc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd7, 0xdb, 0xd7, 0xff, 0x61, 0x62, 0x61, 0xff, + 0x9e, 0xa1, 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc7, 0xc3, 0xff, + 0x56, 0x58, 0x56, 0xff, 0xce, 0xd1, 0xce, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0x5a, 0x5b, 0x5a, 0xff, + 0xdc, 0xdf, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xb7, 0xbb, 0xb7, 0xff, 0x69, 0x6c, 0x69, 0xff, + 0xcb, 0xcd, 0xcb, 0xff, 0xe6, 0xea, 0xe6, 0xff, + 0x98, 0x9b, 0x98, 0xff, 0x74, 0x77, 0x74, 0xff, + 0xdb, 0xdc, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6e, 0x71, 0x6e, 0xff, 0x97, 0x9a, 0x97, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0xdb, 0xdf, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa0, 0xa3, 0xa0, 0xff, + 0x69, 0x6b, 0x69, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0x5c, 0x5e, 0x5c, 0xff, + 0xdc, 0xdf, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc5, 0xc7, 0xc5, 0xff, 0x81, 0x84, 0x81, 0xff, + 0xa6, 0xa9, 0xa6, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0x8a, 0x8d, 0x8a, 0xff, 0x97, 0x9a, 0x97, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x97, 0x9a, 0x97, 0xff, 0x84, 0x86, 0x84, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdb, 0xde, 0xdb, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0x8d, 0x90, 0x8d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7e, 0x81, 0x7e, 0xff, + 0x83, 0x86, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb8, 0xbc, 0xb8, 0xff, 0x68, 0x6a, 0x68, 0xff, + 0xd8, 0xdc, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xed, 0xed, 0xed, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x8e, 0x90, 0x8e, 0xff, 0xba, 0xbe, 0xba, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0xf9, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9c, 0x9f, 0x9c, 0xff, + 0x64, 0x65, 0x64, 0xff, 0xd2, 0xd5, 0xd2, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe2, 0xdf, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd0, 0xd3, 0xd0, 0xff, 0x66, 0x68, 0x66, 0xff, + 0xc3, 0xc7, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9d, 0x9e, 0x9d, 0xff, + 0x72, 0x75, 0x72, 0xff, 0x7e, 0x81, 0x7e, 0xff, + 0x82, 0x85, 0x82, 0xff, 0xe4, 0xe5, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x65, 0x66, 0x65, 0xff, + 0xe4, 0xe8, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0x63, 0x65, 0x63, 0xff, + 0x93, 0x96, 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0xcb, 0xc7, 0xff, 0x55, 0x57, 0x55, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd3, 0xd7, 0xd3, 0xff, 0x5a, 0x5c, 0x5a, 0xff, + 0xb1, 0xb5, 0xb1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xc7, 0xc9, 0xc7, 0xff, + 0x47, 0x49, 0x47, 0xff, 0x51, 0x54, 0x51, 0xff, + 0x91, 0x93, 0x91, 0xff, 0xf1, 0xf2, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc2, 0xc5, 0xc2, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0xcd, 0xd1, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe2, 0xdf, 0xff, 0x6f, 0x71, 0x6f, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd6, 0xd9, 0xd6, 0xff, + 0x7f, 0x82, 0x7f, 0xff, 0x89, 0x8b, 0x89, 0xff, + 0xd3, 0xd5, 0xd3, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x91, 0x93, 0x91, 0xff, 0x82, 0x85, 0x82, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe4, 0xe0, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9d, 0xa0, 0x9d, 0xff, 0x8f, 0x92, 0x8f, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x81, 0x83, 0x81, 0xff, + 0x82, 0x84, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf4, 0xf3, 0xff, + 0xdc, 0xdf, 0xdc, 0xff, 0xdc, 0xdd, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x74, 0x76, 0x74, 0xff, 0x8f, 0x92, 0x8f, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd5, 0xd9, 0xd5, 0xff, 0xdc, 0xe0, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9c, 0xa0, 0x9c, 0xff, + 0x65, 0x67, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0x6b, 0x6e, 0x6b, 0xff, 0xab, 0xae, 0xab, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc6, 0xca, 0xc6, 0xff, + 0x58, 0x59, 0x58, 0xff, 0xd1, 0xd5, 0xd1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcd, 0xd0, 0xcd, 0xff, + 0x54, 0x56, 0x54, 0xff, 0xcd, 0xd1, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0x70, 0x72, 0x70, 0xff, 0x99, 0x9c, 0x99, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0x57, 0x58, 0x57, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x98, 0x9b, 0x98, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0x62, 0x64, 0x62, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0x52, 0x54, 0x52, 0xff, + 0xc5, 0xc9, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x8a, 0x8c, 0x8a, 0xff, + 0x72, 0x75, 0x72, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe0, 0xe0, 0xff, 0x6b, 0x6d, 0x6b, 0xff, + 0x8f, 0x91, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf3, 0xf1, 0xff, 0x71, 0x73, 0x71, 0xff, + 0x8c, 0x8e, 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xce, 0xcf, 0xce, 0xff, 0x88, 0x8a, 0x88, 0xff, + 0xa9, 0xab, 0xa9, 0xff, 0xe8, 0xea, 0xe8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0x66, 0x69, 0x66, 0xff, + 0xa7, 0xaa, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xf8, 0xf8, 0xf8, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe6, 0xe7, 0xe6, 0xff, 0x9b, 0x9e, 0x9b, 0xff, + 0x81, 0x84, 0x81, 0xff, 0xc6, 0xc7, 0xc6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf1, 0xef, 0xff, 0x7a, 0x7c, 0x7a, 0xff, + 0xc0, 0xc4, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xc4, 0xc6, 0xc4, 0xff, + 0xb1, 0xb3, 0xb1, 0xff, 0xf0, 0xf0, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xce, 0xd0, 0xce, 0xff, + 0x84, 0x87, 0x84, 0xff, 0x94, 0x96, 0x94, 0xff, + 0xf9, 0xfa, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdb, 0xdc, 0xdb, 0xff, + 0xa9, 0xac, 0xa9, 0xff, 0x74, 0x76, 0x74, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x9e, 0x9c, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0x56, 0x58, 0x56, 0xff, 0xe5, 0xe6, 0xe5, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf5, 0xf5, 0xff, + 0xae, 0xb1, 0xae, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0xc3, 0xc6, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf6, 0xf5, 0xff, + 0xda, 0xdc, 0xda, 0xff, 0x8b, 0x8e, 0x8b, 0xff, + 0x69, 0x6b, 0x69, 0xff, 0xad, 0xb1, 0xad, 0xff, + 0xdb, 0xdd, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0x87, 0x89, 0x87, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x5d, 0x5e, 0x5d, 0xff, + 0x45, 0x46, 0x45, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe8, 0xe7, 0xff, 0xc7, 0xca, 0xc7, 0xff, + 0xeb, 0xec, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xea, 0xec, 0xea, 0xff, 0x73, 0x75, 0x73, 0xff, + 0x80, 0x82, 0x80, 0xff, 0xe6, 0xe7, 0xe6, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xd4, 0xd7, 0xd4, 0xff, + 0x87, 0x8a, 0x87, 0xff, 0x7a, 0x7d, 0x7a, 0xff, + 0xac, 0xae, 0xac, 0xff, 0xfa, 0xfa, 0xfa, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe3, 0xdf, 0xff, 0xa3, 0xa6, 0xa3, 0xff, + 0x67, 0x6a, 0x67, 0xff, 0xc8, 0xc9, 0xc8, 0xff, + 0xcf, 0xd0, 0xcf, 0xff, 0x87, 0x89, 0x87, 0xff, + 0x6f, 0x71, 0x6f, 0xff, 0xf3, 0xf4, 0xf3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xf6, 0xff, + 0xa1, 0xa3, 0xa1, 0xff, 0x66, 0x68, 0x66, 0xff, + 0x91, 0x93, 0x91, 0xff, 0xdd, 0xde, 0xdd, 0xff, + 0xfa, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xb6, 0xb9, 0xb6, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xee, 0xef, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xca, 0xc8, 0xff, 0x93, 0x96, 0x93, 0xff, + 0x7d, 0x7f, 0x7d, 0xff, 0xb7, 0xb9, 0xb7, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xca, 0xcd, 0xca, 0xff, 0xa1, 0xa3, 0xa1, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0x5f, 0x62, 0x5f, 0xff, + 0xb5, 0xb6, 0xb5, 0xff, 0xf9, 0xf9, 0xf9, 0xff, + 0xdf, 0xe0, 0xdf, 0xff, 0x89, 0x8c, 0x89, 0xff, + 0x96, 0x98, 0x96, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xcb, 0xcd, 0xcb, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x7a, 0x7a, 0x7a, 0xff, + 0x59, 0x5b, 0x59, 0xff, 0x92, 0x94, 0x92, 0xff, + 0xd2, 0xd5, 0xd2, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe9, 0xe7, 0xff, + 0x96, 0x99, 0x96, 0xff, 0x87, 0x89, 0x87, 0xff, + 0xd1, 0xd2, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xd6, 0xd6, 0xd6, 0xff, + 0x7f, 0x82, 0x7f, 0xff, 0x75, 0x77, 0x75, 0xff, + 0xc5, 0xc8, 0xc5, 0xff, 0xed, 0xee, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xef, 0xef, 0xff, 0xcf, 0xd1, 0xcf, 0xff, + 0xdc, 0xdd, 0xdc, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0xcc, 0xc9, 0xff, 0x92, 0x95, 0x92, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x8e, 0x91, 0x8e, 0xff, 0xce, 0xd2, 0xce, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfa, 0xfb, 0xfa, 0xff, + 0xc4, 0xc6, 0xc4, 0xff, 0x82, 0x85, 0x82, 0xff, + 0xb4, 0xb6, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe2, 0xe3, 0xe2, 0xff, 0x95, 0x98, 0x95, 0xff, + 0x8f, 0x90, 0x8f, 0xff, 0xd1, 0xd3, 0xd1, 0xff, + 0x9c, 0x9f, 0x9c, 0xff, 0x7f, 0x81, 0x7f, 0xff, + 0x91, 0x93, 0x91, 0xff, 0xd6, 0xd8, 0xd6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xd2, 0xd3, 0xd2, 0xff, 0x85, 0x87, 0x85, 0xff, + 0x9d, 0x9e, 0x9d, 0xff, 0xec, 0xed, 0xec, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xff, + 0xcd, 0xd0, 0xcd, 0xff, 0x72, 0x74, 0x72, 0xff, + 0x8e, 0x90, 0x8e, 0xff, 0xc4, 0xc5, 0xc4, 0xff, + 0xf6, 0xf7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xdb, 0xdd, 0xdb, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0xb6, 0xb9, 0xb6, 0xff, 0xf3, 0xf3, 0xf3, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xe1, 0xe5, 0xe1, 0xff, 0xe1, 0xe5, 0xe1, 0xff, + 0xe1, 0xe5, 0xe1, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0xf5, 0xf7, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xc5, 0xc9, 0xc5, 0xff, + 0x9a, 0x9d, 0x9a, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0x6d, 0x6f, 0x6d, 0xff, + 0xb0, 0xb4, 0xb0, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xef, 0xed, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0x88, 0x8b, 0x88, 0xff, + 0xcf, 0xd1, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, 0xff, + 0xb2, 0xb5, 0xb2, 0xff, 0x83, 0x85, 0x83, 0xff, + 0xbc, 0xbd, 0xbc, 0xff, 0xfc, 0xfd, 0xfc, 0xff, + 0xed, 0xef, 0xed, 0xff, 0xbd, 0xbf, 0xbd, 0xff, + 0x82, 0x85, 0x82, 0xff, 0x8c, 0x8e, 0x8c, 0xff, + 0xd7, 0xd9, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf2, 0xf2, 0xf2, 0xff, 0xa8, 0xab, 0xa8, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xb9, 0xbb, 0xb9, 0xff, + 0x85, 0x88, 0x85, 0xff, 0x80, 0x83, 0x80, 0xff, + 0xcd, 0xd0, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xde, 0xe0, 0xde, 0xff, 0x8a, 0x8d, 0x8a, 0xff, + 0x8e, 0x91, 0x8e, 0xff, 0xde, 0xe0, 0xde, 0xff, + 0xe0, 0xe0, 0xe0, 0xff, 0x82, 0x84, 0x82, 0xff, + 0x73, 0x76, 0x73, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x73, 0x76, 0x73, 0xff, 0x76, 0x78, 0x76, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x80, 0x82, 0x80, 0xff, + 0x84, 0x87, 0x84, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0x8f, 0x92, 0x8f, 0xff, + 0x9d, 0xa0, 0x9d, 0xff, 0xae, 0xb1, 0xae, 0xff, + 0xb6, 0xb9, 0xb6, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0xb5, 0xb8, 0xb5, 0xff, 0xb6, 0xb9, 0xb6, 0xff, + 0xbf, 0xc3, 0xbf, 0xff, 0xc4, 0xc7, 0xc4, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xc8, 0xcb, 0xc8, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xc4, 0xc7, 0xc4, 0xff, + 0xc1, 0xc4, 0xc1, 0xff, 0xc1, 0xc4, 0xc1, 0xff, + 0xb7, 0xba, 0xb7, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0xb6, 0xb9, 0xb6, 0xff, 0xa3, 0xa7, 0xa3, 0xff, + 0x8c, 0x8f, 0x8c, 0xff, 0x81, 0x84, 0x81, 0xff, + 0x64, 0x66, 0x64, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0x45, 0x46, 0x45, 0xff, 0x63, 0x64, 0x63, 0xff, + 0xc1, 0xc4, 0xc1, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe3, 0xe0, 0xff, + 0x87, 0x8a, 0x87, 0xff, 0x93, 0x96, 0x93, 0xff, + 0xe2, 0xe3, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd3, 0xd5, 0xd3, 0xff, + 0x84, 0x87, 0x84, 0xff, 0x9e, 0xa0, 0x9e, 0xff, + 0xe6, 0xe7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xf6, 0xff, + 0xb9, 0xbd, 0xb9, 0xff, 0x6e, 0x70, 0x6e, 0xff, + 0x83, 0x84, 0x83, 0xff, 0xf7, 0xf8, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0x71, 0x73, 0x71, 0xff, 0x88, 0x8b, 0x88, 0xff, + 0xf4, 0xf4, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe5, 0xe3, 0xff, + 0xad, 0xb0, 0xad, 0xff, 0x78, 0x7b, 0x78, 0xff, + 0x86, 0x89, 0x86, 0xff, 0xd8, 0xdb, 0xd8, 0xff, + 0xf4, 0xf5, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd8, 0xda, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf4, 0xf5, 0xf4, 0xff, 0xfb, 0xfb, 0xfb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf2, 0xf2, 0xf2, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0x52, 0x55, 0x52, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xcf, 0xd0, 0xcf, 0xff, + 0xba, 0xbc, 0xba, 0xff, 0xab, 0xae, 0xab, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xab, 0xae, 0xab, 0xff, + 0xa4, 0xa7, 0xa4, 0xff, 0x92, 0x94, 0x92, 0xff, + 0x88, 0x8a, 0x88, 0xff, 0x82, 0x84, 0x82, 0xff, + 0x73, 0x75, 0x73, 0xff, 0x77, 0x79, 0x77, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x78, 0x7b, 0x78, 0xff, + 0x69, 0x6b, 0x69, 0xff, 0x63, 0x65, 0x63, 0xff, + 0x5c, 0x5e, 0x5c, 0xff, 0x50, 0x52, 0x50, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0x60, 0x62, 0x60, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x65, 0x67, 0x65, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x65, 0x67, 0x65, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x64, 0x66, 0x64, 0xff, + 0x50, 0x51, 0x50, 0xff, 0x4d, 0x4e, 0x4d, 0xff, + 0x61, 0x63, 0x61, 0xff, 0x60, 0x62, 0x60, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0x58, 0x5a, 0x58, 0xff, 0x60, 0x62, 0x60, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0x6b, 0x6e, 0x6b, 0xff, + 0x75, 0x77, 0x75, 0xff, 0x83, 0x86, 0x83, 0xff, + 0x9e, 0xa2, 0x9e, 0xff, 0xc0, 0xc4, 0xc0, 0xff, + 0x9e, 0xa1, 0x9e, 0xff, 0x73, 0x76, 0x73, 0xff, + 0xed, 0xf0, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0x79, 0x7c, 0x79, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0xee, 0xef, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xf0, 0xee, 0xff, 0xa2, 0xa5, 0xa2, 0xff, + 0x7c, 0x7f, 0x7c, 0xff, 0xd1, 0xd2, 0xd1, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf2, 0xf1, 0xff, 0xae, 0xb0, 0xae, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0x9d, 0xa1, 0x9d, 0xff, + 0xe9, 0xea, 0xe9, 0xff, 0xfc, 0xfd, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb2, 0xb4, 0xb2, 0xff, 0x75, 0x77, 0x75, 0xff, + 0xc1, 0xc4, 0xc1, 0xff, 0xf6, 0xf7, 0xf6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xeb, 0xeb, 0xeb, 0xff, 0xaa, 0xae, 0xaa, 0xff, + 0x61, 0x63, 0x61, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0xdc, 0xdd, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe2, 0xdf, 0xff, 0xa9, 0xab, 0xa9, 0xff, + 0xbb, 0xbd, 0xbb, 0xff, 0xd8, 0xda, 0xd8, 0xff, + 0xb5, 0xb9, 0xb5, 0xff, 0xd7, 0xd8, 0xd7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd6, 0xd8, 0xd6, 0xff, + 0x72, 0x74, 0x72, 0xff, 0x8f, 0x91, 0x8f, 0xff, + 0xe1, 0xe2, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xde, 0xe2, 0xde, 0xff, 0xdb, 0xde, 0xdb, 0xff, + 0xd8, 0xdb, 0xd8, 0xff, 0xd0, 0xd3, 0xd0, 0xff, + 0xd0, 0xd4, 0xd0, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xcb, 0xcf, 0xcb, 0xff, + 0x62, 0x64, 0x62, 0xff, 0x76, 0x78, 0x76, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xd4, 0xd7, 0xd4, 0xff, 0xd9, 0xdd, 0xd9, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0xe9, 0xec, 0xe9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0x54, 0x56, 0x54, 0xff, + 0xcd, 0xd0, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc4, 0xc7, 0xc4, 0xff, + 0x73, 0x76, 0x73, 0xff, 0xc2, 0xc5, 0xc2, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0xca, 0xc8, 0xff, 0x76, 0x78, 0x76, 0xff, + 0x9d, 0xa0, 0x9d, 0xff, 0xed, 0xee, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf1, 0xf0, 0xff, + 0xaf, 0xb0, 0xaf, 0xff, 0x73, 0x75, 0x73, 0xff, + 0xaa, 0xad, 0xaa, 0xff, 0xeb, 0xec, 0xeb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xdf, 0xe1, 0xdf, 0xff, 0x8e, 0x90, 0x8e, 0xff, + 0x92, 0x95, 0x92, 0xff, 0xdc, 0xde, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xf2, 0xf4, 0xf2, 0xff, + 0x9f, 0xa1, 0x9f, 0xff, 0x7a, 0x7d, 0x7a, 0xff, + 0x9c, 0x9e, 0x9c, 0xff, 0xe7, 0xe9, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe2, 0xe4, 0xe2, 0xff, 0xb9, 0xbb, 0xb9, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0x6d, 0x6e, 0x6d, 0xff, + 0xf0, 0xf0, 0xf0, 0xff, 0xdc, 0xdd, 0xdc, 0xff, + 0x47, 0x49, 0x47, 0xff, 0x95, 0x98, 0x95, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xee, 0xed, 0xff, + 0xab, 0xaf, 0xab, 0xff, 0x65, 0x67, 0x65, 0xff, + 0xc9, 0xcb, 0xc9, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x61, 0x62, 0x61, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xec, 0xe9, 0xff, 0x63, 0x65, 0x63, 0xff, + 0xb9, 0xbc, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0x69, 0x6b, 0x69, 0xff, 0xda, 0xde, 0xda, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x91, 0x93, 0x91, 0xff, 0x78, 0x7a, 0x78, 0xff, + 0xdb, 0xdd, 0xdb, 0xff, 0xfc, 0xfd, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xeb, 0xe9, 0xff, 0x9c, 0x9f, 0x9c, 0xff, + 0x80, 0x82, 0x80, 0xff, 0xc5, 0xc7, 0xc5, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf6, 0xf7, 0xf6, 0xff, 0xc2, 0xc4, 0xc2, 0xff, + 0x83, 0x86, 0x83, 0xff, 0xa7, 0xa9, 0xa7, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe2, 0xe4, 0xe2, 0xff, 0xa9, 0xad, 0xa9, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0xfa, 0xfa, 0xfa, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xea, 0xeb, 0xea, 0xff, 0xcc, 0xce, 0xcc, 0xff, + 0x90, 0x93, 0x90, 0xff, 0x66, 0x68, 0x66, 0xff, + 0x99, 0x9b, 0x99, 0xff, 0xce, 0xcf, 0xce, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x73, 0x73, 0x73, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0xb9, 0xba, 0xb9, 0xff, 0xf9, 0xfa, 0xf9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xda, 0xdd, 0xda, 0xff, 0x79, 0x7b, 0x79, 0xff, + 0xa3, 0xa5, 0xa3, 0xff, 0xe5, 0xe6, 0xe5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0x5b, 0x5d, 0x5b, 0xff, 0xa5, 0xa8, 0xa5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x67, 0x69, 0x67, 0xff, + 0x9a, 0x9d, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0xf1, 0xf1, 0xff, 0x94, 0x97, 0x94, 0xff, + 0x79, 0x7b, 0x79, 0xff, 0xe5, 0xe8, 0xe5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xd5, 0xd8, 0xd5, 0xff, + 0x67, 0x69, 0x67, 0xff, 0xa7, 0xaa, 0xa7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xd9, 0xda, 0xd9, 0xff, + 0x8b, 0x8e, 0x8b, 0xff, 0x8f, 0x91, 0x8f, 0xff, + 0xdf, 0xe1, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xef, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xcd, 0xd0, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe6, 0xe7, 0xe6, 0xff, + 0xad, 0xb1, 0xad, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xaa, 0xad, 0xaa, 0xff, 0xe1, 0xe2, 0xe1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xf8, 0xf7, 0xff, 0xe7, 0xe9, 0xe7, 0xff, + 0xa2, 0xa5, 0xa2, 0xff, 0x78, 0x7a, 0x78, 0xff, + 0x86, 0x89, 0x86, 0xff, 0xaa, 0xac, 0xaa, 0xff, + 0xe1, 0xe3, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd6, 0xd6, 0xd6, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0x69, 0x6a, 0x69, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0xfc, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xef, 0xee, 0xff, 0xb0, 0xb4, 0xb0, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0xc1, 0xc4, 0xc1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd3, 0xcf, 0xff, + 0x51, 0x52, 0x51, 0xff, 0xbd, 0xc0, 0xbd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0x85, 0x87, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe4, 0xe4, 0xe4, 0xff, 0x90, 0x93, 0x90, 0xff, + 0x98, 0x9a, 0x98, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xed, 0xed, 0xed, 0xff, 0x9e, 0xa0, 0x9e, 0xff, + 0x79, 0x7b, 0x79, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf6, 0xf5, 0xff, + 0xbc, 0xbe, 0xbc, 0xff, 0x6e, 0x70, 0x6e, 0xff, + 0x9d, 0x9f, 0x9d, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xec, 0xee, 0xec, 0xff, 0x5f, 0x60, 0x5f, 0xff, + 0xa2, 0xa5, 0xa2, 0xff, 0xfa, 0xfa, 0xfa, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0x7e, 0x82, 0x7e, 0xff, 0xa1, 0xa4, 0xa1, 0xff, + 0xe1, 0xe3, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xf3, 0xf4, 0xf3, 0xff, + 0xc2, 0xc5, 0xc2, 0xff, 0x87, 0x8a, 0x87, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x95, 0x97, 0x95, 0xff, + 0xce, 0xd0, 0xce, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xce, 0xd0, 0xce, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x93, 0x97, 0x93, 0xff, + 0xd4, 0xd5, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xe3, 0xe7, 0xe3, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0x9d, 0xa0, 0x9d, 0xff, + 0xe3, 0xe4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbd, 0xc0, 0xbd, 0xff, + 0x4c, 0x4d, 0x4c, 0xff, 0xcf, 0xd2, 0xcf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x95, 0x99, 0x95, 0xff, + 0x68, 0x6b, 0x68, 0xff, 0xe0, 0xe4, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd0, 0xd1, 0xd0, 0xff, 0x85, 0x87, 0x85, 0xff, + 0xb0, 0xb1, 0xb0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf4, 0xf3, 0xff, + 0xb7, 0xba, 0xb7, 0xff, 0x77, 0x79, 0x77, 0xff, + 0xac, 0xae, 0xac, 0xff, 0xf6, 0xf7, 0xf6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfa, 0xfb, 0xfa, 0xff, 0x95, 0x99, 0x95, 0xff, + 0x62, 0x64, 0x62, 0xff, 0xd4, 0xd6, 0xd4, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x9c, 0x9d, 0x9c, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0xd9, 0xda, 0xd9, 0xff, + 0xfb, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb9, 0xba, 0xb9, 0xff, 0x7f, 0x82, 0x7f, 0xff, + 0x97, 0x99, 0x97, 0xff, 0xf1, 0xf2, 0xf1, 0xff, + 0xfb, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf8, 0xf7, 0xff, + 0xce, 0xd0, 0xce, 0xff, 0x9c, 0x9e, 0x9c, 0xff, + 0x7e, 0x80, 0x7e, 0xff, 0x7c, 0x7f, 0x7c, 0xff, + 0xc2, 0xc5, 0xc2, 0xff, 0xf1, 0xf1, 0xf1, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, 0xff, + 0xc6, 0xc8, 0xc6, 0xff, 0x7b, 0x7e, 0x7b, 0xff, + 0x8a, 0x8d, 0x8a, 0xff, 0xe8, 0xeb, 0xe8, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf7, 0xf6, 0xff, + 0xbc, 0xbe, 0xbc, 0xff, 0x87, 0x89, 0x87, 0xff, + 0xb8, 0xba, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbe, 0xbb, 0xff, + 0x5e, 0x60, 0x5e, 0xff, 0xd9, 0xdc, 0xd9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb4, 0xb2, 0xff, + 0x68, 0x6a, 0x68, 0xff, 0xd5, 0xd9, 0xd5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xbd, 0xbc, 0xff, 0x84, 0x86, 0x84, 0xff, + 0xc4, 0xc5, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe1, 0xe0, 0xff, + 0x8f, 0x91, 0x8f, 0xff, 0x85, 0x88, 0x85, 0xff, + 0xd9, 0xdb, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd2, 0xcf, 0xff, + 0x75, 0x78, 0x75, 0xff, 0x94, 0x97, 0x94, 0xff, + 0xe2, 0xe3, 0xe2, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc9, 0xcb, 0xc9, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0xa1, 0xa4, 0xa1, 0xff, + 0xe9, 0xeb, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe1, 0xe0, 0xff, + 0x75, 0x78, 0x75, 0xff, 0x8c, 0x8f, 0x8c, 0xff, + 0xde, 0xdf, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xdb, 0xdd, 0xdb, 0xff, 0xae, 0xb2, 0xae, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0x5c, 0x5e, 0x5c, 0xff, + 0xad, 0xb0, 0xad, 0xff, 0xde, 0xdf, 0xde, 0xff, + 0xf6, 0xf7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xae, 0xb1, 0xae, 0xff, + 0x72, 0x75, 0x72, 0xff, 0xab, 0xae, 0xab, 0xff, + 0xea, 0xec, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfa, 0xfa, 0xfa, 0xff, 0x78, 0x7a, 0x78, 0xff, + 0x89, 0x8c, 0x89, 0xff, 0xe4, 0xe4, 0xe4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa9, 0xac, 0xa9, 0xff, + 0x65, 0x67, 0x65, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc7, 0xca, 0xc7, 0xff, + 0x61, 0x62, 0x61, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa0, 0xa1, 0xa0, 0xff, 0x81, 0x84, 0x81, 0xff, + 0xd4, 0xd5, 0xd4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xbe, 0xbf, 0xbe, 0xff, + 0x81, 0x83, 0x81, 0xff, 0xb9, 0xba, 0xb9, 0xff, + 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf3, 0xf2, 0xff, + 0xb6, 0xb8, 0xb6, 0xff, 0x82, 0x85, 0x82, 0xff, + 0xa6, 0xa9, 0xa6, 0xff, 0xec, 0xee, 0xec, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe5, 0xe3, 0xff, + 0x95, 0x98, 0x95, 0xff, 0x77, 0x79, 0x77, 0xff, + 0xcd, 0xcf, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x8a, 0x8c, 0x8a, 0xff, + 0x90, 0x93, 0x90, 0xff, 0xca, 0xcc, 0xca, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf0, 0xef, 0xff, 0xdc, 0xdd, 0xdc, 0xff, + 0x82, 0x84, 0x82, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x97, 0x99, 0x97, 0xff, 0xc9, 0xca, 0xc9, 0xff, + 0xf1, 0xf2, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0xe2, 0xe0, 0xff, + 0x9f, 0xa2, 0x9f, 0xff, 0x70, 0x71, 0x70, 0xff, + 0xbf, 0xc2, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb0, 0xaf, 0xff, + 0x78, 0x7a, 0x78, 0xff, 0xb1, 0xb2, 0xb1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa7, 0xaa, 0xa7, 0xff, + 0x70, 0x72, 0x70, 0xff, 0xf1, 0xf3, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd1, 0xd5, 0xd1, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xb8, 0xbc, 0xb8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x8c, 0x8d, 0x8c, 0xff, 0x8b, 0x8e, 0x8b, 0xff, + 0xe3, 0xe4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe3, 0xe5, 0xe3, 0xff, 0x8f, 0x91, 0x8f, 0xff, + 0x91, 0x94, 0x91, 0xff, 0xdf, 0xe0, 0xdf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0xea, 0xe9, 0xff, 0xa4, 0xa7, 0xa4, 0xff, + 0x76, 0x78, 0x76, 0xff, 0xc7, 0xc9, 0xc7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xe9, 0xeb, 0xe9, 0xff, 0xb7, 0xba, 0xb7, 0xff, + 0x5d, 0x5e, 0x5d, 0xff, 0x4a, 0x4a, 0x4a, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb3, 0xb4, 0xb3, 0xff, 0x63, 0x64, 0x63, 0xff, + 0xa3, 0xa6, 0xa3, 0xff, 0xf6, 0xf7, 0xf6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfa, 0xfa, 0xfa, 0xff, 0xfb, 0xfb, 0xfb, 0xff, + 0xa8, 0xaa, 0xa8, 0xff, 0x76, 0x78, 0x76, 0xff, + 0x87, 0x89, 0x87, 0xff, 0xb3, 0xb5, 0xb3, 0xff, + 0xe4, 0xe5, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0xe9, 0xe5, 0xff, 0x7a, 0x7c, 0x7a, 0xff, + 0x8b, 0x8e, 0x8b, 0xff, 0xd4, 0xd6, 0xd4, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xed, 0xed, 0xff, + 0x7e, 0x81, 0x7e, 0xff, 0x86, 0x88, 0x86, 0xff, + 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb6, 0xb9, 0xb6, 0xff, + 0x5f, 0x61, 0x5f, 0xff, 0xd9, 0xdd, 0xd9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd7, 0xdb, 0xd7, 0xff, + 0x6e, 0x70, 0x6e, 0xff, 0xba, 0xbd, 0xba, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7b, 0x7d, 0x7b, 0xff, 0x98, 0x9b, 0x98, 0xff, + 0xf1, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc4, 0xc7, 0xc4, 0xff, 0x74, 0x77, 0x74, 0xff, + 0xb5, 0xb8, 0xb5, 0xff, 0xf3, 0xf4, 0xf3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xde, 0xe0, 0xde, 0xff, + 0x7e, 0x81, 0x7e, 0xff, 0x8e, 0x90, 0x8e, 0xff, + 0xeb, 0xeb, 0xeb, 0xff, 0xd3, 0xd4, 0xd3, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x7c, 0x7e, 0x7c, 0xff, 0x70, 0x71, 0x70, 0xff, + 0x67, 0x6a, 0x67, 0xff, 0xf9, 0xfa, 0xf9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe9, 0xea, 0xe9, 0xff, + 0x5c, 0x5e, 0x5c, 0xff, 0x40, 0x41, 0x40, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0xeb, 0xed, 0xeb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcc, 0xce, 0xcc, 0xff, 0x91, 0x92, 0x91, 0xff, + 0x81, 0x83, 0x81, 0xff, 0x9a, 0x9c, 0x9a, 0xff, + 0xde, 0xe0, 0xde, 0xff, 0xfb, 0xfb, 0xfb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc6, 0xc7, 0xc6, 0xff, + 0x81, 0x84, 0x81, 0xff, 0x8f, 0x91, 0x8f, 0xff, + 0xe3, 0xe5, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb7, 0xb6, 0xff, 0x88, 0x8a, 0x88, 0xff, + 0xba, 0xbc, 0xba, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xd5, 0xd8, 0xd5, 0xff, + 0x52, 0x54, 0x52, 0xff, 0xb0, 0xb3, 0xb0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcf, 0xd3, 0xcf, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xba, 0xbd, 0xba, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x6d, 0x6f, 0x6d, 0xff, 0xb1, 0xb5, 0xb1, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9a, 0x9c, 0x9a, 0xff, 0x6e, 0x71, 0x6e, 0xff, + 0xd6, 0xda, 0xd6, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb3, 0xb6, 0xb3, 0xff, 0x61, 0x63, 0x61, 0xff, + 0x93, 0x95, 0x93, 0xff, 0x83, 0x86, 0x83, 0xff, + 0x83, 0x85, 0x83, 0xff, 0xaa, 0xad, 0xaa, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xb4, 0xb7, 0xb4, 0xff, + 0x64, 0x67, 0x64, 0xff, 0xcb, 0xce, 0xcb, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x96, 0x97, 0x96, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0x8c, 0x8e, 0x8c, 0xff, + 0x74, 0x76, 0x74, 0xff, 0x93, 0x96, 0x93, 0xff, + 0xe3, 0xe4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf2, 0xf0, 0xff, 0xc1, 0xc3, 0xc1, 0xff, + 0x8a, 0x8c, 0x8a, 0xff, 0x73, 0x75, 0x73, 0xff, + 0xc5, 0xc8, 0xc5, 0xff, 0xed, 0xee, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xb0, 0xb2, 0xb0, 0xff, 0x70, 0x73, 0x70, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xe7, 0xe8, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe6, 0xe7, 0xe6, 0xff, 0x95, 0x98, 0x95, 0xff, + 0x71, 0x73, 0x71, 0xff, 0xda, 0xde, 0xda, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x78, 0x7b, 0x78, 0xff, 0x7a, 0x7d, 0x7a, 0xff, + 0xdd, 0xe0, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xaa, 0xad, 0xaa, 0xff, + 0x5c, 0x5e, 0x5c, 0xff, 0xd1, 0xd4, 0xd1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xd1, 0xd5, 0xd1, 0xff, + 0x6d, 0x6f, 0x6d, 0xff, 0xc7, 0xcb, 0xc7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf5, 0xf5, 0xff, + 0x73, 0x76, 0x73, 0xff, 0x93, 0x96, 0x93, 0xff, + 0xfb, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe4, 0xe0, 0xff, 0x85, 0x86, 0x85, 0xff, + 0x4e, 0x50, 0x4e, 0xff, 0x75, 0x78, 0x75, 0xff, + 0xbc, 0xbf, 0xbc, 0xff, 0xe5, 0xe9, 0xe5, 0xff, + 0xf5, 0xf9, 0xf5, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0x84, 0x87, 0x84, 0xff, 0x9d, 0xa0, 0x9d, 0xff, + 0xe5, 0xe7, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0xc8, 0xc7, 0xff, 0x75, 0x77, 0x75, 0xff, + 0xb4, 0xb7, 0xb4, 0xff, 0xda, 0xde, 0xda, 0xff, + 0x97, 0x99, 0x97, 0xff, 0x76, 0x79, 0x76, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xd4, 0xd7, 0xd4, 0xff, + 0xc0, 0xc2, 0xc0, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0x8b, 0x8d, 0x8b, 0xff, 0xd1, 0xd2, 0xd1, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe1, 0xe3, 0xe1, 0xff, 0xa1, 0xa4, 0xa1, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0xb3, 0xb5, 0xb3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc1, 0xc3, 0xc1, 0xff, + 0x74, 0x77, 0x74, 0xff, 0x9b, 0x9e, 0x9b, 0xff, + 0xe9, 0xeb, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0xc6, 0xc3, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0x8a, 0x8c, 0x8a, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x70, 0x72, 0x70, 0xff, + 0x85, 0x87, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf6, 0xf5, 0xff, 0xb9, 0xbc, 0xb9, 0xff, + 0x70, 0x72, 0x70, 0xff, 0xd0, 0xd3, 0xd0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xd9, 0xda, 0xd9, 0xff, + 0x72, 0x74, 0x72, 0xff, 0xba, 0xbd, 0xba, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfb, 0xfc, 0xfb, 0xff, 0xc2, 0xc4, 0xc2, 0xff, + 0x6f, 0x71, 0x6f, 0xff, 0x8f, 0x92, 0x8f, 0xff, + 0xdc, 0xe0, 0xdc, 0xff, 0xe7, 0xeb, 0xe7, 0xff, + 0xe4, 0xe8, 0xe4, 0xff, 0xe3, 0xe7, 0xe3, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0x82, 0x84, 0x82, 0xff, + 0xc5, 0xc7, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf7, 0xf6, 0xff, + 0x7a, 0x7c, 0x7a, 0xff, 0x96, 0x99, 0x96, 0xff, + 0xcf, 0xd2, 0xcf, 0xff, 0xec, 0xf0, 0xec, 0xff, + 0xe3, 0xe7, 0xe3, 0xff, 0x9f, 0xa1, 0x9f, 0xff, + 0x58, 0x5a, 0x58, 0xff, 0x83, 0x86, 0x83, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0x90, 0x92, 0x90, 0xff, + 0xc9, 0xcb, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xe1, 0xe5, 0xe1, 0xff, + 0x82, 0x84, 0x82, 0xff, 0x78, 0x7b, 0x78, 0xff, + 0xdb, 0xdc, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe6, 0xe7, 0xe6, 0xff, + 0x9f, 0xa1, 0x9f, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0xd1, 0xd4, 0xd1, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb2, 0xaf, 0xff, + 0x59, 0x5b, 0x59, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0x61, 0x63, 0x61, 0xff, + 0xcc, 0xd0, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf0, 0xef, 0xff, 0xa2, 0xa5, 0xa2, 0xff, + 0x66, 0x69, 0x66, 0xff, 0xaf, 0xb2, 0xaf, 0xff, + 0xd4, 0xd6, 0xd4, 0xff, 0xe1, 0xe3, 0xe1, 0xff, + 0xdc, 0xde, 0xdc, 0xff, 0xa0, 0xa3, 0xa0, 0xff, + 0x74, 0x76, 0x74, 0xff, 0xd5, 0xd7, 0xd5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xee, 0xed, 0xff, + 0xab, 0xae, 0xab, 0xff, 0x80, 0x82, 0x80, 0xff, + 0xb4, 0xb8, 0xb4, 0xff, 0xb1, 0xb5, 0xb1, 0xff, + 0x7b, 0x7f, 0x7b, 0xff, 0x9f, 0xa2, 0x9f, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0x69, 0x6c, 0x69, 0xff, + 0x9b, 0x9c, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xad, 0xae, 0xad, 0xff, + 0x7d, 0x80, 0x7d, 0xff, 0x93, 0x96, 0x93, 0xff, + 0xaf, 0xb2, 0xaf, 0xff, 0xb0, 0xb3, 0xb0, 0xff, + 0xf0, 0xf4, 0xf0, 0xff, 0xd2, 0xd6, 0xd2, 0xff, + 0x78, 0x7a, 0x78, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0x84, 0x87, 0x84, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xc6, 0xc7, 0xc6, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x93, 0x95, 0x93, 0xff, 0xec, 0xed, 0xec, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xeb, 0xeb, 0xeb, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0x6d, 0x6f, 0x6d, 0xff, + 0x98, 0x9a, 0x98, 0xff, 0xdc, 0xdd, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0x5e, 0x60, 0x5e, 0xff, 0x3a, 0x3b, 0x3a, 0xff, + 0x86, 0x89, 0x86, 0xff, 0xc1, 0xc4, 0xc1, 0xff, + 0xde, 0xe2, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd4, 0xd8, 0xd4, 0xff, 0x8b, 0x8e, 0x8b, 0xff, + 0x36, 0x38, 0x36, 0xff, 0x7d, 0x80, 0x7d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0xe7, 0xe5, 0xff, 0x82, 0x85, 0x82, 0xff, + 0x4a, 0x4b, 0x4a, 0xff, 0x72, 0x74, 0x72, 0xff, + 0x88, 0x8b, 0x88, 0xff, 0x98, 0x9b, 0x98, 0xff, + 0x92, 0x95, 0x92, 0xff, 0x6d, 0x70, 0x6d, 0xff, + 0x8a, 0x8d, 0x8a, 0xff, 0xe6, 0xe8, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, + 0xe7, 0xe7, 0xe7, 0xff, 0x89, 0x8c, 0x89, 0xff, + 0x77, 0x79, 0x77, 0xff, 0x81, 0x84, 0x81, 0xff, + 0x57, 0x59, 0x57, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0x65, 0x68, 0x65, 0xff, 0x4a, 0x4c, 0x4a, 0xff, + 0x7a, 0x7c, 0x7a, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd3, 0xd4, 0xd3, 0xff, 0x68, 0x6a, 0x68, 0xff, + 0x50, 0x51, 0x50, 0xff, 0x62, 0x64, 0x62, 0xff, + 0x4b, 0x4b, 0x4b, 0xff, 0x88, 0x8a, 0x88, 0xff, + 0xd9, 0xdc, 0xd9, 0xff, 0xd0, 0xd3, 0xd0, 0xff, + 0x60, 0x62, 0x60, 0xff, 0x7e, 0x81, 0x7e, 0xff, + 0xd5, 0xd7, 0xd5, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf5, 0xf5, 0xf5, 0xff, 0xb4, 0xb7, 0xb4, 0xff, + 0x58, 0x5a, 0x58, 0xff, 0xb0, 0xb3, 0xb0, 0xff, + 0xed, 0xed, 0xed, 0xff, 0xf8, 0xf8, 0xf8, 0xff, + 0xd4, 0xd6, 0xd4, 0xff, 0x99, 0x9c, 0x99, 0xff, + 0x99, 0x9c, 0x99, 0xff, 0x97, 0x9a, 0x97, 0xff, + 0x6a, 0x6c, 0x6a, 0xff, 0xaf, 0xb1, 0xaf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0x4f, 0x50, 0x4f, 0xff, 0x8d, 0x8f, 0x8d, 0xff, + 0x6b, 0x6d, 0x6b, 0xff, 0x57, 0x59, 0x57, 0xff, + 0x6d, 0x6f, 0x6d, 0xff, 0x92, 0x95, 0x92, 0xff, + 0xa5, 0xa8, 0xa5, 0xff, 0xa7, 0xaa, 0xa7, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0x8a, 0x8d, 0x8a, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0x51, 0x53, 0x51, 0xff, + 0x6f, 0x72, 0x6f, 0xff, 0x69, 0x6b, 0x69, 0xff, + 0xdd, 0xe1, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xde, 0xe0, 0xde, 0xff, 0x80, 0x82, 0x80, 0xff, + 0x80, 0x83, 0x80, 0xff, 0xa9, 0xac, 0xa9, 0xff, + 0xa7, 0xa9, 0xa7, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0x6b, 0x6c, 0x6b, 0xff, 0x57, 0x59, 0x57, 0xff, + 0xac, 0xae, 0xac, 0xff, 0xf4, 0xf6, 0xf4, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0x53, 0x55, 0x53, 0xff, 0x48, 0x4a, 0x48, 0xff, + 0x7a, 0x7c, 0x7a, 0xff, 0xdb, 0xdf, 0xdb, 0xff, + 0xb9, 0xbc, 0xb9, 0xff, 0x87, 0x89, 0x87, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xf6, 0xf6, 0xf6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x83, 0x84, 0x83, 0xff, 0x3c, 0x3e, 0x3c, 0xff, + 0x4e, 0x50, 0x4e, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x71, 0x73, 0x71, 0xff, 0x7e, 0x80, 0x7e, 0xff, + 0xc7, 0xca, 0xc7, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0x8e, 0x91, 0x8e, 0xff, 0xcd, 0xcf, 0xcd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe6, 0xe3, 0xff, + 0x9b, 0x9e, 0x9b, 0xff, 0x82, 0x85, 0x82, 0xff, + 0xb4, 0xb6, 0xb4, 0xff, 0xd7, 0xd9, 0xd7, 0xff, + 0x8e, 0x90, 0x8e, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xb5, 0xb8, 0xb5, 0xff, 0xe4, 0xe8, 0xe4, 0xff, + 0x7f, 0x82, 0x7f, 0xff, 0x80, 0x83, 0x80, 0xff, + 0xd1, 0xd2, 0xd1, 0xff, 0xf9, 0xfa, 0xf9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x96, 0x99, 0x96, 0xff, + 0x6c, 0x6f, 0x6c, 0xff, 0xb1, 0xb4, 0xb1, 0xff, + 0x82, 0x84, 0x82, 0xff, 0xb2, 0xb5, 0xb2, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0x81, 0x84, 0x81, 0xff, + 0x65, 0x67, 0x65, 0xff, 0x66, 0x69, 0x66, 0xff, + 0x71, 0x74, 0x71, 0xff, 0x82, 0x84, 0x82, 0xff, + 0x70, 0x73, 0x70, 0xff, 0x73, 0x76, 0x73, 0xff, + 0xb3, 0xb6, 0xb3, 0xff, 0x59, 0x5b, 0x59, 0xff, + 0xcb, 0xce, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd7, 0xd9, 0xd7, 0xff, 0x7c, 0x7f, 0x7c, 0xff, + 0xad, 0xb1, 0xad, 0xff, 0xdf, 0xe3, 0xdf, 0xff, + 0xe7, 0xea, 0xe7, 0xff, 0xde, 0xe2, 0xde, 0xff, + 0x94, 0x97, 0x94, 0xff, 0x7c, 0x7e, 0x7c, 0xff, + 0xd3, 0xd4, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xf0, 0xed, 0xff, + 0x77, 0x79, 0x77, 0xff, 0x33, 0x34, 0x33, 0xff, + 0x82, 0x84, 0x82, 0xff, 0xe8, 0xe9, 0xe8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xec, 0xed, 0xec, 0xff, + 0xed, 0xef, 0xed, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe5, 0xe7, 0xe5, 0xff, + 0x9a, 0x9d, 0x9a, 0xff, 0x64, 0x66, 0x64, 0xff, + 0xab, 0xad, 0xab, 0xff, 0xd7, 0xda, 0xd7, 0xff, + 0x7f, 0x81, 0x7f, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0x80, 0x83, 0x80, 0xff, 0x76, 0x78, 0x76, 0xff, + 0xc6, 0xc9, 0xc6, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfb, 0xff, + 0xeb, 0xeb, 0xeb, 0xff, 0x8a, 0x8c, 0x8a, 0xff, + 0x58, 0x59, 0x58, 0xff, 0x6a, 0x6c, 0x6a, 0xff, + 0x84, 0x86, 0x84, 0xff, 0xcb, 0xcf, 0xcb, 0xff, + 0xf1, 0xf5, 0xf1, 0xff, 0xf5, 0xf9, 0xf5, 0xff, + 0xc0, 0xc3, 0xc0, 0xff, 0x81, 0x85, 0x81, 0xff, + 0x8d, 0x90, 0x8d, 0xff, 0xde, 0xe1, 0xde, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7d, 0x7b, 0xff, + 0x80, 0x81, 0x80, 0xff, 0x95, 0x98, 0x95, 0xff, + 0x76, 0x79, 0x76, 0xff, 0xee, 0xf0, 0xee, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa8, 0xab, 0xa8, 0xff, 0x6f, 0x71, 0x6f, 0xff, + 0xd1, 0xd5, 0xd1, 0xff, 0x5d, 0x5f, 0x5d, 0xff, + 0xa1, 0xa4, 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc4, 0xc7, 0xc4, 0xff, 0x6e, 0x71, 0x6e, 0xff, + 0xab, 0xae, 0xab, 0xff, 0xb5, 0xb8, 0xb5, 0xff, + 0xd6, 0xda, 0xd6, 0xff, 0xe1, 0xe5, 0xe1, 0xff, + 0x82, 0x85, 0x82, 0xff, 0x94, 0x97, 0x94, 0xff, + 0xe9, 0xea, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xb2, 0xb3, 0xb2, 0xff, 0x54, 0x56, 0x54, 0xff, + 0x7d, 0x7f, 0x7d, 0xff, 0xde, 0xdf, 0xde, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xda, 0xdb, 0xda, 0xff, 0xea, 0xeb, 0xea, 0xff, + 0xf1, 0xf2, 0xf1, 0xff, 0xda, 0xdc, 0xda, 0xff, + 0x72, 0x75, 0x72, 0xff, 0x40, 0x41, 0x40, 0xff, + 0x5d, 0x5f, 0x5d, 0xff, 0xc0, 0xc3, 0xc0, 0xff, + 0xee, 0xef, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xce, 0xd0, 0xce, 0xff, + 0x79, 0x7b, 0x79, 0xff, 0x51, 0x52, 0x51, 0xff, + 0xa0, 0xa3, 0xa0, 0xff, 0xec, 0xf0, 0xec, 0xff, + 0xe6, 0xea, 0xe6, 0xff, 0xce, 0xd2, 0xce, 0xff, + 0xdf, 0xe3, 0xdf, 0xff, 0x99, 0x9c, 0x99, 0xff, + 0x64, 0x66, 0x64, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xec, 0xed, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa6, 0xa9, 0xa6, 0xff, + 0x58, 0x5a, 0x58, 0xff, 0x5b, 0x5d, 0x5b, 0xff, + 0x92, 0x95, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa8, 0xac, 0xa8, 0xff, 0x68, 0x6b, 0x68, 0xff, + 0xc8, 0xcb, 0xc8, 0xff, 0x56, 0x58, 0x56, 0xff, + 0x90, 0x93, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xac, 0xaf, 0xac, 0xff, 0x5e, 0x60, 0x5e, 0xff, + 0x77, 0x79, 0x77, 0xff, 0x58, 0x59, 0x58, 0xff, + 0x96, 0x98, 0x96, 0xff, 0xca, 0xcd, 0xca, 0xff, + 0x63, 0x65, 0x63, 0xff, 0xad, 0xb0, 0xad, 0xff, + 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0xe2, 0xe0, 0xff, 0xac, 0xaf, 0xac, 0xff, + 0xc1, 0xc3, 0xc1, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xee, 0xed, 0xff, + 0x61, 0x63, 0x61, 0xff, 0x5e, 0x5f, 0x5e, 0xff, + 0xac, 0xae, 0xac, 0xff, 0xef, 0xf1, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf6, 0xf5, 0xff, + 0xcb, 0xce, 0xcb, 0xff, 0x58, 0x59, 0x58, 0xff, + 0x9e, 0xa1, 0x9e, 0xff, 0xdf, 0xe2, 0xdf, 0xff, + 0xae, 0xb1, 0xae, 0xff, 0x73, 0x76, 0x73, 0xff, + 0x6b, 0x6c, 0x6b, 0xff, 0x77, 0x79, 0x77, 0xff, + 0x50, 0x52, 0x50, 0xff, 0x6f, 0x71, 0x6f, 0xff, + 0xc6, 0xc9, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe3, 0xdf, 0xff, + 0x85, 0x88, 0x85, 0xff, 0x57, 0x59, 0x57, 0xff, + 0xb9, 0xbc, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0xc6, 0xc3, 0xff, 0x57, 0x59, 0x57, 0xff, + 0x61, 0x62, 0x61, 0xff, 0x41, 0x43, 0x41, 0xff, + 0xa8, 0xab, 0xa8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xff, + 0x98, 0x9b, 0x98, 0xff, 0x2b, 0x2c, 0x2b, 0xff, + 0x55, 0x56, 0x55, 0xff, 0x77, 0x79, 0x77, 0xff, + 0x63, 0x66, 0x63, 0xff, 0x95, 0x98, 0x95, 0xff, + 0x5f, 0x61, 0x5f, 0xff, 0xd5, 0xd9, 0xd5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfa, 0xfa, 0xfa, 0xff, 0xee, 0xef, 0xee, 0xff, + 0xf4, 0xf4, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf1, 0xf2, 0xf1, 0xff, + 0xc6, 0xc9, 0xc6, 0xff, 0xbb, 0xbd, 0xbb, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf9, 0xf8, 0xff, 0xa1, 0xa2, 0xa1, 0xff, + 0x82, 0x84, 0x82, 0xff, 0x9f, 0xa2, 0x9f, 0xff, + 0x88, 0x8b, 0x88, 0xff, 0x56, 0x58, 0x56, 0xff, + 0x92, 0x95, 0x92, 0xff, 0x83, 0x86, 0x83, 0xff, + 0x5d, 0x60, 0x5d, 0xff, 0x4c, 0x4e, 0x4c, 0xff, + 0xbc, 0xbf, 0xbc, 0xff, 0xf7, 0xf8, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf7, 0xf5, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc6, 0xc3, 0xff, + 0xa9, 0xac, 0xa9, 0xff, 0xb4, 0xb7, 0xb4, 0xff, + 0xeb, 0xed, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe9, 0xe9, 0xe9, 0xff, + 0x79, 0x7b, 0x79, 0xff, 0x2f, 0x30, 0x2f, 0xff, + 0x90, 0x93, 0x90, 0xff, 0xc2, 0xc5, 0xc2, 0xff, + 0x69, 0x6c, 0x69, 0xff, 0x51, 0x52, 0x51, 0xff, + 0x65, 0x67, 0x65, 0xff, 0xe9, 0xec, 0xe9, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfb, 0xfb, 0xfb, 0xff, 0xf7, 0xf8, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe4, 0xe4, 0xe4, 0xff, + 0x92, 0x94, 0x92, 0xff, 0x68, 0x6a, 0x68, 0xff, + 0x76, 0x78, 0x76, 0xff, 0x6e, 0x70, 0x6e, 0xff, + 0xda, 0xdb, 0xda, 0xff, 0xe7, 0xe8, 0xe7, 0xff, + 0xd5, 0xd6, 0xd5, 0xff, 0xe1, 0xe3, 0xe1, 0xff, + 0xe5, 0xe7, 0xe5, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe8, 0xe7, 0xff, + 0x96, 0x98, 0x96, 0xff, 0x7e, 0x81, 0x7e, 0xff, + 0xd4, 0xd6, 0xd4, 0xff, 0xe6, 0xe8, 0xe6, 0xff, + 0x90, 0x93, 0x90, 0xff, 0x46, 0x48, 0x46, 0xff, + 0x83, 0x84, 0x83, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xcb, 0xcd, 0xcb, 0xff, 0x80, 0x82, 0x80, 0xff, + 0x48, 0x49, 0x48, 0xff, 0x6c, 0x6e, 0x6c, 0xff, + 0xda, 0xdc, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xf8, 0xf9, 0xf8, 0xff, + 0xbc, 0xbe, 0xbc, 0xff, 0x77, 0x7a, 0x77, 0xff, + 0xae, 0xaf, 0xae, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf1, 0xf0, 0xff, 0xc0, 0xc2, 0xc0, 0xff, + 0x5c, 0x5f, 0x5c, 0xff, 0x4c, 0x4e, 0x4c, 0xff, + 0xec, 0xed, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe8, 0xe7, 0xff, 0xc9, 0xcb, 0xc9, 0xff, + 0xe0, 0xe1, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xff, + 0xae, 0xb1, 0xae, 0xff, 0x9a, 0x9d, 0x9a, 0xff, + 0xe3, 0xe4, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf4, 0xf4, 0xf4, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xfa, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x28, 0x75, 0x75, 0x61, 0x79, 0x29, + 0x67, 0x69, 0x6d, 0x70, 0x2f, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x77, 0x61, 0x6e, 0x64, + 0x61, 0x2e, 0x70, 0x6e, 0x67, 0x00, 0x00, 0x00, + 0x18, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x64, 0x6b, 0x50, 0x00, 0x00, 0x63, 0x18, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x16, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x39, 0x6d, 0xb8, 0xbf, 0x40, 0x7a, 0xcf, 0xb9, + 0x41, 0x7c, 0xd2, 0xb8, 0x38, 0x6c, 0xb7, 0xbf, + 0x39, 0x6d, 0xb9, 0xbe, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x38, 0x6e, 0xba, 0xbf, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x33, 0x63, 0xa9, 0xc3, 0x3c, 0x74, 0xc5, 0xbc, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xb9, 0x40, 0x7c, 0xd2, 0xb9, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xba, 0x40, 0x7c, 0xd2, 0xba, + 0x39, 0x6f, 0xbc, 0xbf, 0x40, 0x7c, 0xd2, 0xba, + 0x40, 0x7c, 0xd2, 0xbb, 0x30, 0x5d, 0x9d, 0xc8, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x40, 0x7c, 0xd2, 0xbb, + 0x40, 0x7c, 0xd2, 0xbb, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3a, 0x72, 0xc2, 0xc0, + 0x3e, 0x7b, 0xd0, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x31, 0x61, 0xa5, 0xc7, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x3f, 0x7c, 0xd2, 0xbc, + 0x40, 0x7b, 0xce, 0xbd, 0x3f, 0x7c, 0xd2, 0xbc, + 0x3f, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x49, 0x78, 0xc0, 0xc0, 0x9b, 0x62, 0x30, 0xed, + 0xac, 0x5d, 0x11, 0xf9, 0xa3, 0x61, 0x23, 0xf2, + 0x66, 0x6f, 0x89, 0xcf, 0x45, 0x7a, 0xc6, 0xbf, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd2, 0xbc, 0x40, 0x7c, 0xd2, 0xbc, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x38, 0x6d, 0xb9, 0xc3, 0x3f, 0x7a, 0xd0, 0xbe, + 0x40, 0x7c, 0xd3, 0xbd, 0x38, 0x6c, 0xb9, 0xc3, + 0x38, 0x6d, 0xbb, 0xc3, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x39, 0x6e, 0xbc, 0xc2, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x33, 0x64, 0xaa, 0xc7, 0x3c, 0x74, 0xc6, 0xc0, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x39, 0x6f, 0xbd, 0xc2, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x30, 0x5d, 0x9e, 0xca, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x3a, 0x71, 0xc1, 0xc1, 0x40, 0x7c, 0xd3, 0xbd, + 0x3f, 0x7b, 0xd1, 0xbd, 0x32, 0x62, 0xa7, 0xc8, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x41, 0x7b, 0xcf, 0xbe, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x49, 0x78, 0xc1, 0xc1, 0x9b, 0x62, 0x31, 0xed, + 0xac, 0x5d, 0x11, 0xf9, 0xa3, 0x61, 0x24, 0xf2, + 0x66, 0x6f, 0x8a, 0xcf, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x44, 0x7a, 0xca, 0xbf, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x40, 0x7c, 0xd3, 0xbd, 0x40, 0x7c, 0xd3, 0xbd, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7d, 0xd0, 0xb1, 0x49, 0x73, 0xad, 0xc2, + 0x43, 0x7e, 0xd2, 0xb0, 0x42, 0x67, 0x9d, 0xc6, + 0x3d, 0x64, 0x9c, 0xc5, 0x43, 0x7e, 0xd2, 0xb0, + 0x42, 0x67, 0xa0, 0xbf, 0x68, 0x71, 0x8b, 0xc4, + 0x5f, 0x73, 0x9a, 0xc0, 0x45, 0x7d, 0xcc, 0xb2, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x4a, 0x7b, 0xc5, 0xb3, 0x45, 0x7d, 0xd0, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x44, 0x7e, 0xd2, 0xb0, 0x48, 0x79, 0xc1, 0xb8, + 0x46, 0x78, 0xc0, 0xb8, 0x44, 0x7e, 0xd2, 0xb0, + 0x3f, 0x59, 0x7f, 0xd4, 0x40, 0x6f, 0xb4, 0xba, + 0x44, 0x7e, 0xd2, 0xb0, 0x3a, 0x68, 0xaa, 0xbc, + 0x45, 0x7d, 0xd0, 0xb0, 0x44, 0x7d, 0xce, 0xb2, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x43, 0x7e, 0xd2, 0xb1, 0x43, 0x7e, 0xd2, 0xb1, + 0x42, 0x7e, 0xd2, 0xb1, 0x42, 0x7e, 0xd2, 0xb1, + 0x42, 0x7e, 0xd2, 0xb1, 0x42, 0x7e, 0xd2, 0xb1, + 0x42, 0x7e, 0xd2, 0xb1, 0x42, 0x7e, 0xd2, 0xb1, + 0x42, 0x7e, 0xd2, 0xb2, 0x42, 0x7e, 0xd2, 0xb2, + 0x42, 0x7e, 0xd2, 0xb2, 0x42, 0x7e, 0xd2, 0xb2, + 0x42, 0x7e, 0xd2, 0xb2, 0x42, 0x7e, 0xd2, 0xb2, + 0x48, 0x74, 0xb3, 0xc1, 0x42, 0x7e, 0xd2, 0xb2, + 0x41, 0x7c, 0xce, 0xb3, 0x38, 0x4d, 0x6a, 0xde, + 0x42, 0x7e, 0xd2, 0xb2, 0x39, 0x68, 0xac, 0xbe, + 0x43, 0x7c, 0xd0, 0xb2, 0x43, 0x7c, 0xce, 0xb3, + 0x42, 0x7d, 0xd2, 0xb2, 0x42, 0x7d, 0xd2, 0xb2, + 0x42, 0x7d, 0xd2, 0xb2, 0x42, 0x7d, 0xd2, 0xb2, + 0x42, 0x7d, 0xd2, 0xb2, 0x42, 0x7d, 0xd2, 0xb2, + 0x42, 0x7d, 0xd2, 0xb2, 0x42, 0x7d, 0xd2, 0xb2, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xcf, 0xb4, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7c, 0xd1, 0xb3, + 0x48, 0x74, 0xb4, 0xc2, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x5c, 0x83, 0xd6, 0x59, 0x6d, 0x94, 0xc5, + 0x89, 0x67, 0x4e, 0xdc, 0x94, 0x56, 0x1a, 0xf6, + 0xb1, 0x5a, 0x05, 0xfe, 0xb0, 0x59, 0x05, 0xfd, + 0xab, 0x5f, 0x16, 0xf6, 0x98, 0x64, 0x37, 0xe6, + 0x64, 0x71, 0x90, 0xc5, 0x50, 0x77, 0xb5, 0xba, + 0xa8, 0x5e, 0x18, 0xf4, 0xd9, 0x9d, 0x58, 0xfe, + 0xf7, 0xc7, 0x85, 0xff, 0xeb, 0xb3, 0x6d, 0xff, + 0xb5, 0x62, 0x0f, 0xfd, 0x88, 0x64, 0x4a, 0xdd, + 0x91, 0x61, 0x3a, 0xe4, 0x48, 0x7a, 0xc5, 0xb6, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb3, + 0x42, 0x7d, 0xd2, 0xb3, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7c, 0xd0, 0xb5, 0x49, 0x72, 0xae, 0xc5, + 0x42, 0x7d, 0xd2, 0xb4, 0x41, 0x67, 0x9e, 0xc9, + 0x3d, 0x64, 0x9c, 0xc8, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x67, 0xa1, 0xc3, 0x66, 0x71, 0x8c, 0xc7, + 0x5e, 0x72, 0x9b, 0xc3, 0x44, 0x7c, 0xcc, 0xb5, + 0x42, 0x7d, 0xd2, 0xb4, 0x42, 0x7d, 0xd2, 0xb4, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x49, 0x7b, 0xc5, 0xb8, 0x43, 0x7c, 0xd0, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x42, 0x7d, 0xd2, 0xb5, + 0x42, 0x7d, 0xd2, 0xb5, 0x45, 0x79, 0xc2, 0xbe, + 0x43, 0x77, 0xc1, 0xbd, 0x41, 0x7d, 0xd2, 0xb6, + 0x3d, 0x59, 0x80, 0xd7, 0x3d, 0x6f, 0xb5, 0xbf, + 0x41, 0x7d, 0xd2, 0xb6, 0x38, 0x68, 0xab, 0xc1, + 0x42, 0x7c, 0xd0, 0xb6, 0x42, 0x7c, 0xce, 0xb7, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x41, 0x7d, 0xd2, 0xb6, + 0x47, 0x74, 0xb4, 0xc4, 0x41, 0x7d, 0xd2, 0xb6, + 0x41, 0x7d, 0xd2, 0xb6, 0x39, 0x4d, 0x69, 0xe1, + 0x41, 0x7d, 0xd2, 0xb6, 0x3a, 0x6c, 0xb2, 0xbf, + 0x3f, 0x79, 0xcc, 0xb7, 0x42, 0x7c, 0xce, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xcf, 0xb8, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7d, 0xd2, 0xb7, 0x41, 0x7d, 0xd2, 0xb7, + 0x41, 0x7c, 0xd2, 0xb7, 0x41, 0x7c, 0xd2, 0xb7, + 0x41, 0x7c, 0xd2, 0xb7, 0x41, 0x7c, 0xd2, 0xb7, + 0x41, 0x7c, 0xd2, 0xb7, 0x41, 0x7c, 0xd2, 0xb7, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x7c, 0xd2, 0xb8, 0x41, 0x7c, 0xd2, 0xb8, + 0x49, 0x75, 0xb5, 0xc6, 0x41, 0x7c, 0xd2, 0xb8, + 0x41, 0x75, 0xc1, 0xbe, 0x4b, 0x56, 0x6c, 0xde, + 0x88, 0x66, 0x50, 0xdf, 0x94, 0x56, 0x1b, 0xf6, + 0xb0, 0x58, 0x02, 0xfe, 0xb0, 0x59, 0x05, 0xfd, + 0xab, 0x5f, 0x16, 0xf6, 0x97, 0x64, 0x38, 0xe8, + 0x63, 0x70, 0x91, 0xc9, 0x4f, 0x77, 0xb6, 0xbf, + 0xa8, 0x5e, 0x18, 0xf5, 0xd8, 0x99, 0x52, 0xfd, + 0xf7, 0xc6, 0x83, 0xff, 0xea, 0xb1, 0x68, 0xff, + 0xb4, 0x61, 0x0d, 0xfd, 0x7f, 0x67, 0x5d, 0xda, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x81, 0x66, 0x59, 0xdc, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x41, 0x7c, 0xd2, 0xb9, 0x41, 0x7c, 0xd2, 0xb9, + 0x45, 0x80, 0xd2, 0xa7, 0x45, 0x80, 0xd2, 0xa7, + 0x45, 0x80, 0xd2, 0xa7, 0x45, 0x80, 0xd2, 0xa7, + 0x45, 0x80, 0xd2, 0xa7, 0x69, 0x83, 0xa7, 0xcf, + 0x4f, 0x82, 0xc8, 0xb0, 0x6a, 0x8b, 0xb9, 0xc4, + 0x8b, 0x7f, 0x7a, 0xe4, 0xad, 0x66, 0x20, 0xf3, + 0xa7, 0x7e, 0x54, 0xfe, 0xd7, 0xa3, 0x66, 0xfc, + 0xd6, 0xa0, 0x64, 0xfd, 0xbb, 0x6d, 0x23, 0xfd, + 0xa6, 0x62, 0x1f, 0xee, 0x7a, 0x6c, 0x6a, 0xc9, + 0x49, 0x7d, 0xc9, 0xa9, 0x45, 0x80, 0xd2, 0xa8, + 0x77, 0x6d, 0x70, 0xc8, 0x9e, 0x65, 0x2f, 0xe6, + 0xa6, 0x62, 0x21, 0xee, 0xac, 0x60, 0x18, 0xf4, + 0xb3, 0x62, 0x16, 0xf9, 0xb0, 0x59, 0x05, 0xfd, + 0x93, 0x62, 0x3a, 0xdf, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x7f, 0xd2, 0xa8, + 0x70, 0x87, 0xa6, 0xd5, 0x45, 0x7f, 0xd2, 0xa8, + 0x83, 0x93, 0xab, 0xdf, 0x69, 0x7d, 0xa0, 0xc5, + 0x87, 0x69, 0x56, 0xd9, 0x92, 0x69, 0x44, 0xf3, + 0xb0, 0x5b, 0x09, 0xfc, 0xb2, 0x5b, 0x08, 0xfe, + 0xac, 0x5f, 0x15, 0xf4, 0x8e, 0x67, 0x48, 0xd9, + 0x66, 0x72, 0x91, 0xbc, 0x45, 0x7f, 0xd2, 0xa8, + 0x45, 0x7f, 0xd2, 0xa8, 0x45, 0x7f, 0xd2, 0xa8, + 0x45, 0x7f, 0xd2, 0xa8, 0x4b, 0x7c, 0xc5, 0xab, + 0x5e, 0x76, 0xa0, 0xb7, 0x59, 0x77, 0xab, 0xb3, + 0x52, 0x7a, 0xb7, 0xb0, 0x4c, 0x7c, 0xc4, 0xac, + 0x46, 0x7e, 0xd0, 0xa8, 0x45, 0x7f, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x45, 0x80, 0xd2, 0xa9, 0x45, 0x80, 0xd2, 0xa9, + 0x5f, 0x82, 0xb4, 0xc3, 0x59, 0x84, 0xc0, 0xba, + 0x55, 0x85, 0xc8, 0xb4, 0x87, 0x8f, 0x9a, 0xea, + 0x86, 0x68, 0x55, 0xd4, 0x90, 0x6e, 0x4f, 0xf4, + 0xb0, 0x5a, 0x07, 0xfc, 0xb0, 0x58, 0x02, 0xfe, + 0xae, 0x5d, 0x0d, 0xf9, 0xa3, 0x63, 0x26, 0xeb, + 0x88, 0x68, 0x52, 0xd6, 0x58, 0x77, 0xab, 0xb5, + 0x45, 0x7f, 0xd2, 0xaa, 0x45, 0x7f, 0xd2, 0xaa, + 0x6d, 0x70, 0x84, 0xc2, 0xa5, 0x62, 0x23, 0xed, + 0xb0, 0x5b, 0x08, 0xfb, 0xb1, 0x5a, 0x07, 0xfe, + 0xaf, 0x5e, 0x10, 0xf9, 0xa2, 0x5f, 0x1f, 0xee, + 0x65, 0x72, 0x90, 0xbe, 0x44, 0x7f, 0xd2, 0xaa, + 0x44, 0x7f, 0xd2, 0xaa, 0x44, 0x7f, 0xd2, 0xaa, + 0x44, 0x7f, 0xd2, 0xaa, 0x44, 0x7f, 0xd2, 0xaa, + 0x44, 0x7f, 0xd2, 0xaa, 0x44, 0x7f, 0xd2, 0xaa, + 0x44, 0x7f, 0xd2, 0xaa, 0x44, 0x7f, 0xd2, 0xaa, + 0x44, 0x7f, 0xd2, 0xaa, 0x44, 0x80, 0xd2, 0xaa, + 0x44, 0x80, 0xd2, 0xaa, 0x44, 0x80, 0xd2, 0xaa, + 0x44, 0x80, 0xd2, 0xaa, 0x44, 0x80, 0xd2, 0xaa, + 0x44, 0x80, 0xd2, 0xaa, 0x44, 0x80, 0xd2, 0xaa, + 0x67, 0x83, 0xa9, 0xcf, 0x50, 0x81, 0xc4, 0xb4, + 0x90, 0x7f, 0x72, 0xe7, 0xac, 0x76, 0x41, 0xfc, + 0xee, 0xc1, 0x88, 0xff, 0xc6, 0xb4, 0x9c, 0xff, + 0xfd, 0xe5, 0xc1, 0xff, 0xfd, 0xe6, 0xc3, 0xff, + 0xfa, 0xdc, 0xb1, 0xff, 0xf1, 0xc7, 0x8e, 0xff, + 0xca, 0x86, 0x40, 0xfc, 0xb1, 0x5b, 0x06, 0xfe, + 0xde, 0xa4, 0x5f, 0xfe, 0xfd, 0xd7, 0xa0, 0xff, + 0xfc, 0xc7, 0x7a, 0xff, 0xf5, 0xc5, 0x84, 0xff, + 0xb0, 0x5b, 0x08, 0xfb, 0x4f, 0x7a, 0xbb, 0xb1, + 0xad, 0x5b, 0x0b, 0xf9, 0xaa, 0x5e, 0x14, 0xf5, + 0x5b, 0x76, 0xa5, 0xb8, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x44, 0x7f, 0xd2, 0xab, + 0x44, 0x7f, 0xd2, 0xab, 0x69, 0x83, 0xa8, 0xd1, + 0x4e, 0x81, 0xc9, 0xb3, 0x69, 0x8b, 0xba, 0xc7, + 0x8a, 0x7f, 0x7a, 0xe6, 0xad, 0x66, 0x20, 0xf4, + 0xa5, 0x7a, 0x50, 0xfe, 0xd5, 0x9d, 0x5d, 0xfc, + 0xd1, 0x95, 0x53, 0xfc, 0xb4, 0x5e, 0x0b, 0xfd, + 0xa5, 0x61, 0x1f, 0xef, 0x79, 0x6c, 0x6c, 0xcc, + 0x48, 0x7d, 0xc9, 0xae, 0x44, 0x80, 0xd2, 0xac, + 0x76, 0x6d, 0x71, 0xca, 0x9d, 0x65, 0x30, 0xe7, + 0xa5, 0x62, 0x22, 0xee, 0xaa, 0x5f, 0x16, 0xf5, + 0xaf, 0x5b, 0x0c, 0xf9, 0xb0, 0x58, 0x04, 0xfd, + 0x92, 0x62, 0x3b, 0xe0, 0x44, 0x80, 0xd2, 0xac, + 0x44, 0x80, 0xd2, 0xac, 0x44, 0x80, 0xd2, 0xac, + 0x44, 0x80, 0xd2, 0xac, 0x44, 0x80, 0xd2, 0xac, + 0x44, 0x7f, 0xd2, 0xad, 0x44, 0x7f, 0xd2, 0xad, + 0x44, 0x7f, 0xd2, 0xad, 0x44, 0x7f, 0xd2, 0xad, + 0x44, 0x7f, 0xd2, 0xad, 0x44, 0x7f, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x6f, 0x86, 0xa7, 0xd7, 0x44, 0x7e, 0xd2, 0xad, + 0x83, 0x93, 0xab, 0xe1, 0x68, 0x7d, 0xa1, 0xc9, + 0x86, 0x69, 0x58, 0xdb, 0x92, 0x69, 0x44, 0xf4, + 0xb0, 0x5b, 0x09, 0xfc, 0xb2, 0x5b, 0x08, 0xfe, + 0xab, 0x5f, 0x15, 0xf5, 0x8d, 0x67, 0x4a, 0xdb, + 0x64, 0x72, 0x92, 0xc0, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x4a, 0x7b, 0xc6, 0xb0, + 0x5d, 0x75, 0xa1, 0xbb, 0x57, 0x76, 0xac, 0xb8, + 0x50, 0x79, 0xb8, 0xb4, 0x4b, 0x7b, 0xc4, 0xb1, + 0x45, 0x7d, 0xd0, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x44, 0x7e, 0xd2, 0xad, 0x44, 0x7e, 0xd2, 0xad, + 0x42, 0x7e, 0xd2, 0xad, 0x42, 0x7e, 0xd2, 0xad, + 0x42, 0x7e, 0xd2, 0xad, 0x42, 0x7e, 0xd2, 0xad, + 0x42, 0x7e, 0xd2, 0xad, 0x43, 0x7e, 0xd3, 0xae, + 0x59, 0x81, 0xb9, 0xc3, 0x5c, 0x84, 0xbd, 0xc2, + 0x4c, 0x81, 0xcd, 0xb4, 0x8a, 0x91, 0x9b, 0xef, + 0x82, 0x68, 0x5b, 0xd5, 0x8f, 0x6f, 0x51, 0xf4, + 0xaf, 0x5b, 0x08, 0xfb, 0xb1, 0x58, 0x02, 0xfe, + 0xae, 0x5c, 0x0c, 0xfa, 0xa3, 0x62, 0x26, 0xed, + 0x89, 0x66, 0x4f, 0xda, 0x58, 0x76, 0xa7, 0xba, + 0x43, 0x7e, 0xd3, 0xae, 0x43, 0x7e, 0xd3, 0xae, + 0x66, 0x71, 0x8d, 0xc2, 0xa2, 0x63, 0x27, 0xec, + 0xaf, 0x5b, 0x08, 0xfb, 0xb1, 0x5a, 0x07, 0xfe, + 0xaf, 0x5f, 0x0f, 0xfa, 0xa4, 0x5e, 0x20, 0xef, + 0x6a, 0x70, 0x87, 0xc4, 0x43, 0x7e, 0xd3, 0xae, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x43, 0x7e, 0xd2, 0xaf, 0x43, 0x7e, 0xd2, 0xaf, + 0x51, 0x7e, 0xbf, 0xbd, 0x65, 0x84, 0xb0, 0xcd, + 0x88, 0x6a, 0x57, 0xda, 0xa4, 0x88, 0x6e, 0xfe, + 0xec, 0xbe, 0x83, 0xff, 0xc9, 0xb6, 0x9d, 0xff, + 0xfa, 0xe3, 0xc0, 0xff, 0xfd, 0xe5, 0xc2, 0xff, + 0xfa, 0xdb, 0xae, 0xff, 0xf0, 0xc5, 0x8b, 0xff, + 0xc9, 0x84, 0x3c, 0xfc, 0xb0, 0x59, 0x03, 0xfe, + 0xdd, 0xa1, 0x59, 0xfe, 0xfd, 0xd8, 0xa2, 0xff, + 0xfc, 0xc9, 0x7e, 0xff, 0xf5, 0xc4, 0x83, 0xff, + 0xb0, 0x5b, 0x09, 0xfb, 0x4e, 0x79, 0xbc, 0xb6, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0xae, 0x58, 0x04, 0xfc, 0x54, 0x77, 0xb1, 0xb9, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x43, 0x7e, 0xd2, 0xb0, + 0x43, 0x7e, 0xd2, 0xb0, 0x44, 0x7e, 0xd2, 0xb0, + 0x48, 0x81, 0xd2, 0x9e, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x46, 0x7a, 0xc4, 0xa3, + 0x45, 0x4c, 0x56, 0xe6, 0x90, 0x5a, 0x28, 0xf0, + 0x6c, 0x62, 0x57, 0xfe, 0xe5, 0xd1, 0xb2, 0xff, + 0xa8, 0x9e, 0x8f, 0xff, 0xfb, 0xde, 0xb4, 0xff, + 0xfd, 0xe1, 0xb7, 0xff, 0xfe, 0xe5, 0xbf, 0xff, + 0xf9, 0xdb, 0xb2, 0xff, 0xdd, 0xac, 0x72, 0xfd, + 0xb5, 0x67, 0x1c, 0xf9, 0xa7, 0x60, 0x1b, 0xee, + 0xc4, 0x7e, 0x37, 0xfb, 0xec, 0xba, 0x7c, 0xff, + 0xf2, 0xc6, 0x8c, 0xff, 0xf5, 0xcd, 0x96, 0xff, + 0xdc, 0xa3, 0x60, 0xfe, 0xa8, 0x60, 0x1c, 0xee, + 0x4b, 0x7e, 0xc8, 0xa2, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x49, 0x80, 0xcb, 0xa1, + 0x4f, 0x7d, 0xc1, 0xa3, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x41, 0x61, 0x8d, 0xbf, 0x4a, 0x59, 0x70, 0xd4, + 0x73, 0x5a, 0x46, 0xea, 0x7a, 0x61, 0x46, 0xfe, + 0xb8, 0x9d, 0x79, 0xff, 0xcb, 0xb8, 0x9c, 0xff, + 0xfd, 0xe6, 0xc3, 0xff, 0xfd, 0xe7, 0xc5, 0xff, + 0xfb, 0xdf, 0xb6, 0xff, 0xee, 0xc1, 0x87, 0xfe, + 0xc7, 0x82, 0x3a, 0xfb, 0xa7, 0x61, 0x1d, 0xed, + 0x6f, 0x71, 0x82, 0xb9, 0x68, 0x73, 0x8f, 0xb5, + 0x99, 0x65, 0x38, 0xdd, 0xb1, 0x5b, 0x07, 0xfb, + 0xb6, 0x62, 0x0e, 0xfa, 0xb6, 0x63, 0x11, 0xfb, + 0xb2, 0x5b, 0x05, 0xfc, 0xb1, 0x57, 0x00, 0xff, + 0xae, 0x58, 0x04, 0xfc, 0x80, 0x6a, 0x5f, 0xc9, + 0x47, 0x80, 0xcf, 0xa1, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x47, 0x81, 0xd2, 0xa0, 0x47, 0x81, 0xd2, 0xa0, + 0x46, 0x80, 0xd1, 0xa1, 0x42, 0x4e, 0x5e, 0xe2, + 0x79, 0x60, 0x4e, 0xdb, 0x60, 0x57, 0x4e, 0xff, + 0xd1, 0xab, 0x7a, 0xfe, 0x93, 0x8b, 0x7f, 0xff, + 0xfb, 0xe3, 0xc0, 0xff, 0xfd, 0xe8, 0xc7, 0xff, + 0xfd, 0xe4, 0xbe, 0xff, 0xf8, 0xd6, 0xa7, 0xff, + 0xec, 0xbe, 0x83, 0xfe, 0xc7, 0x82, 0x38, 0xfd, + 0xa6, 0x64, 0x25, 0xec, 0xa9, 0x60, 0x1a, 0xf0, + 0xbd, 0x70, 0x23, 0xfb, 0xeb, 0xb3, 0x6b, 0xff, + 0xf9, 0xcd, 0x8d, 0xff, 0xf8, 0xca, 0x89, 0xff, + 0xb6, 0x62, 0x0e, 0xfc, 0x8c, 0x68, 0x4e, 0xd2, + 0x47, 0x81, 0xd2, 0xa1, 0x47, 0x81, 0xd2, 0xa1, + 0x47, 0x81, 0xd2, 0xa1, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x81, 0xd3, 0xa2, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x81, 0xd3, 0xa2, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x81, 0xd3, 0xa2, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x81, 0xd3, 0xa2, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x81, 0xd3, 0xa2, 0x47, 0x81, 0xd3, 0xa2, + 0x47, 0x80, 0xd3, 0xa2, 0x47, 0x80, 0xd3, 0xa2, + 0x47, 0x80, 0xd3, 0xa2, 0x47, 0x80, 0xd3, 0xa2, + 0x4e, 0x75, 0xb1, 0xad, 0x59, 0x46, 0x35, 0xfa, + 0xb6, 0x87, 0x54, 0xfd, 0x7d, 0x77, 0x6d, 0xff, + 0xf1, 0xd6, 0xaf, 0xff, 0xa0, 0x93, 0x80, 0xff, + 0xfb, 0xd4, 0x9a, 0xff, 0xfd, 0xd6, 0x9d, 0xff, + 0xfd, 0xd8, 0xa2, 0xff, 0xfd, 0xde, 0xb0, 0xff, + 0xfd, 0xe6, 0xc3, 0xff, 0xfc, 0xe1, 0xbc, 0xff, + 0xfd, 0xd5, 0x99, 0xff, 0xfc, 0xc7, 0x7b, 0xff, + 0xfc, 0xc7, 0x79, 0xff, 0xcf, 0x8c, 0x44, 0xfc, + 0x82, 0x69, 0x5c, 0xcb, 0x46, 0x80, 0xd2, 0xa2, + 0x9c, 0x64, 0x31, 0xe2, 0xc0, 0x69, 0x0d, 0xff, + 0xac, 0x5d, 0x10, 0xf6, 0x4d, 0x7d, 0xc4, 0xa6, + 0x46, 0x80, 0xd2, 0xa2, 0x46, 0x80, 0xd2, 0xa2, + 0x46, 0x80, 0xd2, 0xa2, 0x46, 0x80, 0xd2, 0xa2, + 0x46, 0x80, 0xd2, 0xa2, 0x46, 0x80, 0xd2, 0xa2, + 0x46, 0x80, 0xd2, 0xa2, 0x46, 0x80, 0xd2, 0xa2, + 0x46, 0x80, 0xd2, 0xa3, 0x46, 0x80, 0xd2, 0xa3, + 0x46, 0x80, 0xd2, 0xa3, 0x46, 0x80, 0xd2, 0xa3, + 0x46, 0x80, 0xd2, 0xa3, 0x44, 0x79, 0xc4, 0xa8, + 0x45, 0x4c, 0x57, 0xe8, 0x8f, 0x5a, 0x29, 0xf1, + 0x6c, 0x62, 0x56, 0xfe, 0xe5, 0xcf, 0xaf, 0xff, + 0xa8, 0x9f, 0x90, 0xff, 0xfb, 0xe0, 0xb8, 0xff, + 0xfd, 0xe4, 0xbf, 0xff, 0xfe, 0xe6, 0xc2, 0xff, + 0xf7, 0xd1, 0x9a, 0xff, 0xd6, 0x9a, 0x54, 0xfc, + 0xb1, 0x5f, 0x0f, 0xf9, 0xa6, 0x60, 0x1c, 0xee, + 0xc4, 0x7e, 0x36, 0xfc, 0xeb, 0xb6, 0x73, 0xff, + 0xf0, 0xc0, 0x82, 0xff, 0xf5, 0xc9, 0x8e, 0xff, + 0xda, 0x9f, 0x59, 0xfe, 0xa7, 0x60, 0x1d, 0xef, + 0x4a, 0x7d, 0xc9, 0xa6, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x46, 0x80, 0xd2, 0xa4, 0x46, 0x80, 0xd2, 0xa4, + 0x40, 0x61, 0x8e, 0xc3, 0x4a, 0x59, 0x71, 0xd6, + 0x73, 0x5a, 0x47, 0xeb, 0x7a, 0x61, 0x46, 0xfe, + 0xb8, 0x9d, 0x79, 0xff, 0xcb, 0xb8, 0x9c, 0xff, + 0xfd, 0xe6, 0xc3, 0xff, 0xfd, 0xe7, 0xc5, 0xff, + 0xfb, 0xdf, 0xb6, 0xff, 0xee, 0xc1, 0x87, 0xfe, + 0xc7, 0x82, 0x3a, 0xfb, 0xa7, 0x61, 0x1d, 0xee, + 0x6e, 0x71, 0x84, 0xbe, 0x67, 0x72, 0x91, 0xb9, + 0x98, 0x65, 0x39, 0xdf, 0xb1, 0x5b, 0x07, 0xfb, + 0xb6, 0x62, 0x0e, 0xfa, 0xb6, 0x63, 0x11, 0xfb, + 0xb1, 0x5b, 0x05, 0xfd, 0xb1, 0x57, 0x00, 0xff, + 0xae, 0x58, 0x04, 0xfc, 0x7f, 0x6a, 0x60, 0xcc, + 0x46, 0x7f, 0xcf, 0xa6, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x46, 0x80, 0xd2, 0xa5, + 0x46, 0x80, 0xd2, 0xa5, 0x41, 0x4e, 0x62, 0xe1, + 0x75, 0x61, 0x55, 0xda, 0x63, 0x57, 0x4c, 0xfe, + 0xc8, 0xa3, 0x74, 0xfe, 0x93, 0x8b, 0x7e, 0xff, + 0xfb, 0xe3, 0xbf, 0xff, 0xfd, 0xe8, 0xc7, 0xff, + 0xfd, 0xe4, 0xbf, 0xff, 0xf9, 0xd8, 0xa9, 0xff, + 0xec, 0xc0, 0x86, 0xfe, 0xc9, 0x86, 0x3c, 0xfd, + 0xaa, 0x65, 0x24, 0xef, 0xa5, 0x61, 0x1f, 0xee, + 0xbb, 0x6d, 0x1e, 0xfb, 0xe9, 0xb0, 0x67, 0xff, + 0xf9, 0xcc, 0x8c, 0xff, 0xf9, 0xcd, 0x8d, 0xff, + 0xba, 0x67, 0x13, 0xfc, 0x91, 0x67, 0x42, 0xda, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd1, 0xa6, 0x45, 0x80, 0xd1, 0xa6, + 0x45, 0x80, 0xd2, 0xa7, 0x45, 0x80, 0xd2, 0xa7, + 0x4f, 0x7b, 0xbd, 0xad, 0x69, 0x4a, 0x2d, 0xf8, + 0x9d, 0x76, 0x4c, 0xfd, 0x97, 0x8c, 0x7c, 0xff, + 0xca, 0xb7, 0x9b, 0xff, 0xa5, 0x97, 0x81, 0xff, + 0xf0, 0xcc, 0x97, 0xff, 0xfd, 0xd7, 0x9e, 0xff, + 0xfd, 0xd9, 0xa4, 0xff, 0xfd, 0xdf, 0xb2, 0xff, + 0xfd, 0xe7, 0xc5, 0xff, 0xfc, 0xe1, 0xbc, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xc8, 0x7c, 0xff, + 0xfc, 0xc8, 0x7c, 0xff, 0xcf, 0x8c, 0x43, 0xfc, + 0x81, 0x6a, 0x5e, 0xce, 0x45, 0x80, 0xd2, 0xa7, + 0x45, 0x80, 0xd2, 0xa7, 0x47, 0x7f, 0xcd, 0xa8, + 0xb1, 0x57, 0x00, 0xff, 0x99, 0x62, 0x33, 0xe3, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x45, 0x80, 0xd2, 0xa8, 0x45, 0x80, 0xd2, 0xa8, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x68, 0x75, 0x93, 0xab, + 0x83, 0x46, 0x0b, 0xfb, 0x18, 0x15, 0x10, 0xff, + 0x39, 0x33, 0x2a, 0xff, 0x42, 0x32, 0x1e, 0xff, + 0x38, 0x2c, 0x1c, 0xff, 0xd1, 0xa4, 0x66, 0xff, + 0xfd, 0xd5, 0x9b, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd8, 0xa1, 0xff, 0xfd, 0xe0, 0xb5, 0xff, + 0xfd, 0xe5, 0xc3, 0xff, 0xf5, 0xd5, 0xaa, 0xff, + 0xfc, 0xce, 0x8f, 0xff, 0xfd, 0xd4, 0x9a, 0xff, + 0xfc, 0xc6, 0x75, 0xff, 0xf5, 0xc7, 0x88, 0xff, + 0xb1, 0x5e, 0x0f, 0xf9, 0x57, 0x7d, 0xb4, 0x9f, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0xa9, 0x5a, 0x0f, 0xf2, 0x73, 0x70, 0x7c, 0xb4, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x6d, 0x70, 0x82, 0xb2, 0x42, 0x24, 0x06, 0xfd, + 0x2b, 0x23, 0x19, 0xff, 0x16, 0x14, 0x10, 0xff, + 0x49, 0x38, 0x20, 0xff, 0x79, 0x5c, 0x36, 0xff, + 0xec, 0xc2, 0x89, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd9, 0xa4, 0xff, 0xfd, 0xdf, 0xb1, 0xff, + 0xfd, 0xe6, 0xc3, 0xff, 0xfa, 0xdb, 0xaf, 0xff, + 0xdb, 0xa5, 0x63, 0xfc, 0xbd, 0x72, 0x25, 0xfa, + 0xec, 0xc5, 0x96, 0xfe, 0xfa, 0xd5, 0xa0, 0xff, + 0xfd, 0xd5, 0x9c, 0xff, 0xf4, 0xbc, 0x6f, 0xff, + 0xb8, 0x62, 0x0c, 0xfb, 0xa3, 0x61, 0x24, 0xe5, + 0x5e, 0x78, 0xa7, 0xa4, 0x49, 0x82, 0xd2, 0x97, + 0x49, 0x82, 0xd2, 0x97, 0x49, 0x82, 0xd2, 0x97, + 0x49, 0x82, 0xd2, 0x97, 0x49, 0x82, 0xd2, 0x97, + 0x49, 0x82, 0xd2, 0x97, 0x49, 0x82, 0xd2, 0x97, + 0x49, 0x83, 0xd2, 0x98, 0x49, 0x83, 0xd2, 0x98, + 0x49, 0x83, 0xd2, 0x98, 0x49, 0x83, 0xd2, 0x98, + 0x49, 0x83, 0xd2, 0x98, 0x49, 0x83, 0xd2, 0x98, + 0x49, 0x83, 0xd2, 0x98, 0x49, 0x83, 0xd2, 0x98, + 0x5c, 0x7a, 0xa9, 0xa5, 0x88, 0x4b, 0x11, 0xf7, + 0x19, 0x14, 0x0e, 0xff, 0x39, 0x33, 0x2b, 0xff, + 0x3a, 0x2d, 0x1d, 0xff, 0x36, 0x2b, 0x1c, 0xff, + 0xd1, 0xa4, 0x68, 0xff, 0xfd, 0xd5, 0x9b, 0xff, + 0xfd, 0xd8, 0xa2, 0xff, 0xfd, 0xdb, 0xaa, 0xff, + 0xfd, 0xe1, 0xb8, 0xff, 0xfd, 0xe7, 0xc6, 0xff, + 0xfa, 0xdc, 0xb2, 0xff, 0xf1, 0xc1, 0x83, 0xff, + 0xfd, 0xe2, 0xba, 0xff, 0xfc, 0xd1, 0x92, 0xff, + 0xfd, 0xd2, 0x96, 0xff, 0xc5, 0x7f, 0x35, 0xfc, + 0x8e, 0x68, 0x4b, 0xce, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x4b, 0x81, 0xcb, 0x9b, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x49, 0x82, 0xd2, 0x99, + 0x49, 0x82, 0xd2, 0x99, 0x4c, 0x81, 0xca, 0x9b, + 0xa5, 0x61, 0x1f, 0xe9, 0xa8, 0x7e, 0x4c, 0xfe, + 0x18, 0x16, 0x13, 0xff, 0x3b, 0x33, 0x26, 0xff, + 0x3f, 0x31, 0x1d, 0xff, 0x38, 0x2c, 0x1c, 0xff, + 0xc3, 0x98, 0x5b, 0xff, 0xfd, 0xd5, 0x9a, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xd4, 0x99, 0xff, + 0xfc, 0xcf, 0x8d, 0xff, 0xfc, 0xb6, 0x51, 0xff, + 0xfd, 0xcf, 0x8c, 0xff, 0xb8, 0x69, 0x1a, 0xfa, + 0x61, 0x78, 0xa0, 0xa9, 0x49, 0x83, 0xd1, 0x99, + 0x7e, 0x6c, 0x64, 0xc1, 0xc5, 0x73, 0x18, 0xfb, + 0xc6, 0x73, 0x18, 0xfb, 0x94, 0x66, 0x3f, 0xd6, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x4a, 0x82, 0xd2, 0x9a, + 0x4a, 0x82, 0xd2, 0x9a, 0x67, 0x74, 0x94, 0xae, + 0x84, 0x47, 0x0c, 0xfb, 0x18, 0x15, 0x10, 0xff, + 0x39, 0x33, 0x2a, 0xff, 0x42, 0x32, 0x1e, 0xff, + 0x38, 0x2c, 0x1c, 0xff, 0xd1, 0xa4, 0x66, 0xff, + 0xfd, 0xd5, 0x9b, 0xff, 0xfd, 0xd8, 0xa1, 0xff, + 0xfd, 0xdf, 0xb1, 0xff, 0xfd, 0xe5, 0xc1, 0xff, + 0xfd, 0xe4, 0xc1, 0xff, 0xf5, 0xd4, 0xa7, 0xff, + 0xfc, 0xcf, 0x8f, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfc, 0xcb, 0x84, 0xff, 0xf5, 0xc5, 0x83, 0xff, + 0xb0, 0x5c, 0x0c, 0xf9, 0x55, 0x7d, 0xb5, 0xa4, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x83, 0xd2, 0x9b, 0x48, 0x83, 0xd2, 0x9b, + 0x48, 0x82, 0xd2, 0x9c, 0x48, 0x82, 0xd2, 0x9c, + 0x48, 0x82, 0xd2, 0x9c, 0x48, 0x82, 0xd2, 0x9c, + 0x6b, 0x70, 0x84, 0xb6, 0x42, 0x24, 0x06, 0xfd, + 0x2b, 0x23, 0x19, 0xff, 0x16, 0x14, 0x10, 0xff, + 0x49, 0x38, 0x20, 0xff, 0x79, 0x5c, 0x36, 0xff, + 0xec, 0xc2, 0x89, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd9, 0xa4, 0xff, 0xfd, 0xdf, 0xb1, 0xff, + 0xfd, 0xe6, 0xc3, 0xff, 0xfa, 0xdb, 0xaf, 0xff, + 0xdb, 0xa5, 0x63, 0xfc, 0xbd, 0x72, 0x25, 0xfa, + 0xec, 0xc5, 0x96, 0xfe, 0xfa, 0xd5, 0xa0, 0xff, + 0xfd, 0xd5, 0x9c, 0xff, 0xf4, 0xbc, 0x6f, 0xff, + 0xb7, 0x62, 0x0c, 0xfb, 0xa3, 0x61, 0x25, 0xe6, + 0x5d, 0x7a, 0xa8, 0xa9, 0x49, 0x83, 0xd2, 0x9c, + 0x49, 0x83, 0xd2, 0x9c, 0x49, 0x83, 0xd2, 0x9c, + 0x49, 0x83, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x49, 0x81, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x49, 0x81, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x49, 0x81, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x49, 0x81, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x49, 0x81, 0xd2, 0x9c, 0x49, 0x81, 0xd2, 0x9c, + 0x58, 0x7a, 0xb3, 0xa5, 0x8b, 0x4f, 0x14, 0xf4, + 0x1c, 0x16, 0x0f, 0xff, 0x3c, 0x35, 0x2c, 0xff, + 0x2f, 0x26, 0x1a, 0xff, 0x35, 0x2a, 0x1c, 0xff, + 0xc9, 0x9e, 0x62, 0xff, 0xfd, 0xd5, 0x9b, 0xff, + 0xfd, 0xd8, 0xa2, 0xff, 0xfd, 0xdb, 0xa9, 0xff, + 0xfd, 0xe1, 0xb7, 0xff, 0xfd, 0xe7, 0xc6, 0xff, + 0xfb, 0xde, 0xb5, 0xff, 0xf1, 0xc1, 0x82, 0xff, + 0xfd, 0xe1, 0xb9, 0xff, 0xfc, 0xd3, 0x95, 0xff, + 0xfd, 0xd2, 0x95, 0xff, 0xca, 0x86, 0x3e, 0xfc, + 0x94, 0x65, 0x3f, 0xd7, 0x48, 0x81, 0xd2, 0x9d, + 0x48, 0x81, 0xd2, 0x9d, 0x48, 0x81, 0xd2, 0x9d, + 0x48, 0x81, 0xd2, 0x9d, 0x48, 0x81, 0xd2, 0x9d, + 0x48, 0x81, 0xd2, 0x9d, 0x48, 0x81, 0xd2, 0x9d, + 0x48, 0x81, 0xd2, 0x9d, 0x48, 0x81, 0xd2, 0x9d, + 0x4a, 0x80, 0xcb, 0xa0, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x48, 0x81, 0xd2, 0x9e, + 0x48, 0x81, 0xd2, 0x9e, 0x4b, 0x7f, 0xcb, 0xa0, + 0xa5, 0x61, 0x20, 0xea, 0xd6, 0x9f, 0x5c, 0xfe, + 0x44, 0x3e, 0x35, 0xff, 0x34, 0x2d, 0x23, 0xff, + 0x1c, 0x17, 0x11, 0xff, 0x52, 0x3c, 0x21, 0xff, + 0x81, 0x61, 0x38, 0xff, 0xee, 0xc5, 0x89, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9d, 0xff, 0xfc, 0xd4, 0x9a, 0xff, + 0xfc, 0xcf, 0x8d, 0xff, 0xfc, 0xb6, 0x51, 0xff, + 0xfd, 0xd1, 0x90, 0xff, 0xb6, 0x64, 0x12, 0xfa, + 0x5e, 0x77, 0xa2, 0xae, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x4c, 0x7e, 0xc6, 0xa2, + 0xb1, 0x58, 0x01, 0xfe, 0xb4, 0x60, 0x0e, 0xfb, + 0x65, 0x74, 0x94, 0xb3, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x47, 0x81, 0xd2, 0x9f, 0x47, 0x81, 0xd2, 0x9f, + 0x4d, 0x84, 0xd1, 0x8d, 0x4d, 0x84, 0xd1, 0x8d, + 0x4d, 0x84, 0xd1, 0x8d, 0x4d, 0x84, 0xd1, 0x8d, + 0x60, 0x7a, 0xa8, 0x9a, 0xb0, 0x5c, 0x0a, 0xf7, + 0xf6, 0xd1, 0x9e, 0xff, 0xca, 0xa2, 0x73, 0xff, + 0x2f, 0x1c, 0x09, 0xff, 0x06, 0x05, 0x03, 0xff, + 0x02, 0x01, 0x00, 0xff, 0x9c, 0x7e, 0x53, 0xff, + 0xd3, 0xa1, 0x5f, 0xff, 0xfd, 0xd6, 0x9b, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfc, 0xd6, 0x9c, 0xff, 0xfc, 0xd5, 0x99, 0xff, + 0xfc, 0xcd, 0x88, 0xff, 0xfb, 0xb8, 0x53, 0xff, + 0xfc, 0xc0, 0x69, 0xff, 0xdd, 0x9f, 0x57, 0xfe, + 0x94, 0x67, 0x42, 0xcc, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4e, 0x82, 0xca, 0x90, + 0xb1, 0x56, 0x00, 0xff, 0xb1, 0x57, 0x00, 0xff, + 0x9a, 0x63, 0x34, 0xd5, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x83, 0xd2, 0x8e, + 0x4c, 0x83, 0xd2, 0x8e, 0x78, 0x70, 0x75, 0xb1, + 0xba, 0x6b, 0x1c, 0xfb, 0xfb, 0xdc, 0xb1, 0xff, + 0x70, 0x48, 0x1e, 0xff, 0x15, 0x0f, 0x0a, 0xff, + 0x01, 0x01, 0x01, 0xff, 0x2a, 0x21, 0x15, 0xff, + 0xde, 0xb5, 0x79, 0xff, 0xea, 0xb7, 0x71, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xdc, 0xab, 0xff, + 0xfd, 0xe3, 0xbb, 0xff, 0xfd, 0xde, 0xb0, 0xff, + 0xfc, 0xc9, 0x7d, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xf7, 0xc6, 0x80, 0xff, 0xb6, 0x64, 0x10, 0xfb, + 0x83, 0x6c, 0x61, 0xba, 0x4c, 0x83, 0xd2, 0x8e, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x6c, 0x76, 0x90, 0xa6, 0x93, 0x64, 0x3b, 0xd0, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x4d, 0x84, 0xd2, 0x8f, 0x4d, 0x84, 0xd2, 0x8f, + 0x4d, 0x84, 0xd2, 0x8f, 0x61, 0x7a, 0xa7, 0x9d, + 0xb6, 0x67, 0x19, 0xf6, 0xf7, 0xd2, 0xa1, 0xff, + 0xcc, 0xa7, 0x7b, 0xff, 0x30, 0x1d, 0x0a, 0xff, + 0x06, 0x05, 0x03, 0xff, 0x06, 0x05, 0x03, 0xff, + 0xa1, 0x83, 0x56, 0xff, 0xd3, 0xa1, 0x5f, 0xff, + 0xfd, 0xd6, 0x9b, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xd5, 0x9c, 0xff, + 0xfc, 0xd9, 0xa5, 0xff, 0xfc, 0xd7, 0xa2, 0xff, + 0xfc, 0xc1, 0x6b, 0xff, 0xfc, 0xbf, 0x66, 0xff, + 0xeb, 0xb5, 0x70, 0xff, 0xa9, 0x60, 0x19, 0xeb, + 0x4b, 0x83, 0xd2, 0x90, 0x4b, 0x83, 0xd2, 0x90, + 0x4b, 0x83, 0xd2, 0x90, 0x4b, 0x83, 0xd2, 0x90, + 0x4b, 0x83, 0xd2, 0x90, 0x4b, 0x83, 0xd2, 0x90, + 0x4b, 0x83, 0xd2, 0x90, 0x4b, 0x83, 0xd2, 0x90, + 0x6f, 0x72, 0x87, 0xaa, 0x9d, 0x62, 0x30, 0xd9, + 0xac, 0x59, 0x0b, 0xf5, 0x4b, 0x84, 0xd3, 0x91, + 0x4b, 0x84, 0xd3, 0x91, 0x4b, 0x84, 0xd3, 0x91, + 0x4b, 0x84, 0xd3, 0x91, 0x4b, 0x84, 0xd3, 0x91, + 0x4b, 0x84, 0xd3, 0x91, 0x4b, 0x84, 0xd3, 0x91, + 0x4b, 0x84, 0xd3, 0x91, 0x9a, 0x64, 0x35, 0xd7, + 0xd3, 0x93, 0x4a, 0xfc, 0xfd, 0xe6, 0xc1, 0xff, + 0xd4, 0xaa, 0x74, 0xff, 0x37, 0x20, 0x0a, 0xff, + 0x09, 0x07, 0x05, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x77, 0x60, 0x3f, 0xff, 0xd1, 0xa2, 0x62, 0xff, + 0xfa, 0xd0, 0x94, 0xff, 0xfc, 0xd3, 0x96, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x83, 0xff, + 0xfb, 0xd6, 0xa1, 0xff, 0xb1, 0x58, 0x03, 0xfe, + 0x52, 0x7f, 0xc1, 0x96, 0x4b, 0x83, 0xd1, 0x91, + 0x60, 0x79, 0xa4, 0xa0, 0xb3, 0x5d, 0x08, 0xfb, + 0xf5, 0xa7, 0x38, 0xff, 0xb1, 0x5a, 0x05, 0xfb, + 0x54, 0x7e, 0xbd, 0x97, 0x4b, 0x83, 0xd1, 0x91, + 0x4b, 0x83, 0xd1, 0x91, 0x4b, 0x83, 0xd1, 0x91, + 0x4b, 0x83, 0xd1, 0x91, 0x4b, 0x83, 0xd1, 0x91, + 0x4b, 0x83, 0xd1, 0x91, 0x4b, 0x83, 0xd1, 0x91, + 0x4b, 0x83, 0xd1, 0x91, 0x4b, 0x83, 0xd1, 0x91, + 0x4b, 0x83, 0xd1, 0x91, 0x4b, 0x83, 0xd1, 0x91, + 0x5e, 0x7a, 0xa9, 0x9e, 0xb0, 0x5e, 0x0d, 0xf8, + 0xf7, 0xd3, 0xa2, 0xff, 0xca, 0xa1, 0x72, 0xff, + 0x2f, 0x1c, 0x09, 0xff, 0x06, 0x05, 0x03, 0xff, + 0x02, 0x01, 0x00, 0xff, 0x9c, 0x7e, 0x53, 0xff, + 0xd3, 0xa1, 0x5f, 0xff, 0xfd, 0xd6, 0x9b, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfc, 0xd8, 0xa1, 0xff, 0xfc, 0xd6, 0x9c, 0xff, + 0xfc, 0xcd, 0x88, 0xff, 0xfb, 0xb8, 0x53, 0xff, + 0xfc, 0xc5, 0x75, 0xff, 0xda, 0x98, 0x4b, 0xfd, + 0x93, 0x67, 0x43, 0xce, 0x4b, 0x83, 0xd2, 0x92, + 0x4b, 0x83, 0xd2, 0x92, 0x4b, 0x83, 0xd2, 0x92, + 0x4b, 0x83, 0xd2, 0x92, 0x4c, 0x82, 0xcd, 0x93, + 0x67, 0x75, 0x95, 0xa7, 0x81, 0x6b, 0x60, 0xbf, + 0x94, 0x65, 0x3f, 0xd1, 0x91, 0x66, 0x42, 0xcf, + 0x8d, 0x66, 0x49, 0xcb, 0x78, 0x6f, 0x75, 0xb5, + 0x4b, 0x83, 0xd2, 0x93, 0x4b, 0x83, 0xd2, 0x93, + 0x4b, 0x83, 0xd2, 0x93, 0x4b, 0x83, 0xd2, 0x93, + 0x4b, 0x83, 0xd2, 0x93, 0x76, 0x70, 0x77, 0xb4, + 0xba, 0x6b, 0x1c, 0xfb, 0xfb, 0xdc, 0xb1, 0xff, + 0x70, 0x48, 0x1e, 0xff, 0x15, 0x0f, 0x0a, 0xff, + 0x01, 0x01, 0x01, 0xff, 0x2a, 0x21, 0x15, 0xff, + 0xde, 0xb5, 0x79, 0xff, 0xea, 0xb7, 0x71, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xdc, 0xab, 0xff, + 0xfd, 0xe3, 0xbb, 0xff, 0xfd, 0xde, 0xb0, 0xff, + 0xfc, 0xc9, 0x7d, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xf7, 0xc6, 0x80, 0xff, 0xb6, 0x64, 0x10, 0xfb, + 0x81, 0x6c, 0x63, 0xbd, 0x4a, 0x83, 0xd2, 0x93, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x93, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x93, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x93, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x93, + 0x69, 0x75, 0x91, 0xa9, 0x92, 0x63, 0x3d, 0xd2, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x93, + 0x4a, 0x83, 0xd2, 0x93, 0x4a, 0x83, 0xd2, 0x94, + 0x4a, 0x83, 0xd2, 0x94, 0x5a, 0x7c, 0xb2, 0x9e, + 0xb1, 0x64, 0x1b, 0xf3, 0xf4, 0xce, 0x99, 0xfe, + 0xd6, 0xb3, 0x88, 0xff, 0x35, 0x20, 0x0a, 0xff, + 0x07, 0x06, 0x04, 0xff, 0x02, 0x02, 0x01, 0xff, + 0x8e, 0x73, 0x4b, 0xff, 0xd2, 0xa3, 0x61, 0xff, + 0xfc, 0xd4, 0x98, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xfd, 0xd6, 0x9c, 0xff, 0xfc, 0xd5, 0x9b, 0xff, + 0xfc, 0xd9, 0xa6, 0xff, 0xfc, 0xd8, 0xa3, 0xff, + 0xfc, 0xc3, 0x6f, 0xff, 0xfc, 0xbd, 0x61, 0xff, + 0xef, 0xbc, 0x78, 0xff, 0xac, 0x5e, 0x13, 0xf2, + 0x4a, 0x82, 0xcf, 0x95, 0x4a, 0x83, 0xd2, 0x94, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x69, 0x74, 0x90, 0xab, 0x99, 0x64, 0x36, 0xd8, + 0xae, 0x58, 0x07, 0xf9, 0x4a, 0x82, 0xd0, 0x96, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x4a, 0x83, 0xd3, 0x95, 0x4a, 0x83, 0xd3, 0x95, + 0x4a, 0x83, 0xd3, 0x95, 0x99, 0x64, 0x36, 0xd8, + 0xd1, 0x8f, 0x44, 0xfb, 0xfd, 0xe6, 0xc2, 0xff, + 0xfb, 0xd6, 0xa0, 0xff, 0x66, 0x40, 0x17, 0xff, + 0x13, 0x0e, 0x09, 0xff, 0x01, 0x01, 0x01, 0xff, + 0x25, 0x1e, 0x13, 0xff, 0xde, 0xb5, 0x78, 0xff, + 0xe9, 0xb6, 0x70, 0xff, 0xfc, 0xd3, 0x96, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xce, 0x86, 0xff, + 0xfb, 0xd5, 0x9d, 0xff, 0xb1, 0x57, 0x01, 0xfe, + 0x51, 0x7f, 0xc3, 0x9b, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x53, 0x7e, 0xbd, 0x9c, + 0xb1, 0x59, 0x03, 0xfd, 0xe5, 0x9e, 0x53, 0xfe, + 0xa7, 0x5e, 0x1b, 0xea, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4a, 0x83, 0xd2, 0x96, 0x4a, 0x83, 0xd2, 0x96, + 0x4d, 0x87, 0xd1, 0x84, 0x4d, 0x87, 0xd1, 0x84, + 0x4d, 0x87, 0xd1, 0x84, 0x56, 0x81, 0xbd, 0x8a, + 0xac, 0x5d, 0x12, 0xef, 0xee, 0xc0, 0x85, 0xfe, + 0xfd, 0xe3, 0xbb, 0xff, 0xc8, 0x7e, 0x31, 0xff, + 0xcf, 0xd9, 0xe5, 0xff, 0xf1, 0xf3, 0xf6, 0xff, + 0x94, 0x9d, 0xa7, 0xff, 0x2e, 0x29, 0x22, 0xff, + 0xee, 0xc0, 0x7c, 0xff, 0xec, 0xb6, 0x6b, 0xff, + 0xfc, 0xd3, 0x95, 0xff, 0xfc, 0xd1, 0x92, 0xff, + 0xfc, 0xcf, 0x8d, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xcf, 0x8e, 0xff, 0xc8, 0x86, 0x43, 0xfc, + 0x87, 0x6a, 0x56, 0xb9, 0x4e, 0x86, 0xd1, 0x85, + 0x4e, 0x86, 0xd1, 0x85, 0x4e, 0x86, 0xd1, 0x85, + 0x4e, 0x86, 0xd1, 0x85, 0x4e, 0x86, 0xd1, 0x85, + 0x4e, 0x86, 0xd1, 0x85, 0x5a, 0x81, 0xb8, 0x8d, + 0xb0, 0x58, 0x02, 0xfc, 0xb5, 0x5b, 0x04, 0xff, + 0xc7, 0x7e, 0x39, 0xfe, 0x79, 0x72, 0x75, 0xa8, + 0x4e, 0x87, 0xd2, 0x85, 0x4e, 0x87, 0xd2, 0x85, + 0x4e, 0x87, 0xd2, 0x85, 0x4e, 0x87, 0xd2, 0x85, + 0x7f, 0x70, 0x6a, 0xae, 0xc0, 0x76, 0x29, 0xfb, + 0xfc, 0xe0, 0xb9, 0xff, 0xeb, 0xbf, 0x88, 0xff, + 0xca, 0xac, 0x90, 0xff, 0xf0, 0xf3, 0xf7, 0xff, + 0xd3, 0xd9, 0xdf, 0xff, 0x57, 0x56, 0x55, 0xff, + 0x87, 0x71, 0x52, 0xff, 0xee, 0xb5, 0x66, 0xff, + 0xfc, 0xd5, 0x9a, 0xff, 0xfc, 0xd3, 0x95, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x7f, 0xff, 0xfc, 0xd1, 0x94, 0xff, + 0xc6, 0x7d, 0x34, 0xfd, 0xa5, 0x5e, 0x1c, 0xe4, + 0x5b, 0x7f, 0xb3, 0x8f, 0x4d, 0x86, 0xd1, 0x86, + 0x4d, 0x86, 0xd1, 0x86, 0x4d, 0x86, 0xd1, 0x86, + 0x4d, 0x86, 0xd1, 0x86, 0x4d, 0x86, 0xd1, 0x86, + 0x4d, 0x86, 0xd1, 0x86, 0x56, 0x80, 0xbd, 0x8c, + 0x85, 0x6a, 0x5a, 0xb7, 0xac, 0x5c, 0x10, 0xee, + 0xba, 0x69, 0x1b, 0xfa, 0xb4, 0x5e, 0x0c, 0xfe, + 0x54, 0x81, 0xc1, 0x8b, 0x4d, 0x86, 0xd1, 0x86, + 0x4d, 0x86, 0xd1, 0x86, 0x4d, 0x86, 0xd1, 0x86, + 0x5e, 0x7d, 0xae, 0x92, 0xb8, 0x6b, 0x1e, 0xf6, + 0xf9, 0xd7, 0xa8, 0xff, 0xfd, 0xe4, 0xbf, 0xff, + 0xc8, 0x7e, 0x31, 0xff, 0xcf, 0xd9, 0xe5, 0xff, + 0xf1, 0xf3, 0xf6, 0xff, 0x94, 0x9d, 0xa7, 0xff, + 0x2e, 0x29, 0x22, 0xff, 0xee, 0xc0, 0x7c, 0xff, + 0xec, 0xb6, 0x6b, 0xff, 0xfc, 0xd3, 0x95, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x80, 0xff, 0xfd, 0xd9, 0xa4, 0xff, + 0xc9, 0x81, 0x37, 0xfc, 0x9b, 0x62, 0x30, 0xd5, + 0x5b, 0x7e, 0xb4, 0x90, 0x4e, 0x85, 0xd1, 0x87, + 0x4e, 0x84, 0xd1, 0x88, 0x4e, 0x84, 0xd1, 0x88, + 0x4e, 0x84, 0xd1, 0x88, 0x5e, 0x7d, 0xad, 0x94, + 0x8d, 0x68, 0x4d, 0xc0, 0xae, 0x5b, 0x0b, 0xf3, + 0xbc, 0x6c, 0x1e, 0xf9, 0xe0, 0x93, 0x4a, 0xfe, + 0xb4, 0x5e, 0x0b, 0xfe, 0x53, 0x81, 0xc6, 0x8b, + 0x4e, 0x84, 0xd1, 0x88, 0x4e, 0x84, 0xd1, 0x88, + 0x4d, 0x84, 0xd1, 0x88, 0x4d, 0x84, 0xd1, 0x88, + 0x4d, 0x84, 0xd1, 0x88, 0x4d, 0x84, 0xd1, 0x88, + 0x7f, 0x6d, 0x68, 0xb2, 0xbe, 0x74, 0x25, 0xf9, + 0xfd, 0xe4, 0xbd, 0xff, 0xfd, 0xd9, 0xa4, 0xff, + 0xd1, 0x8c, 0x41, 0xff, 0xcc, 0xd4, 0xde, 0xff, + 0xf3, 0xf6, 0xf9, 0xff, 0xa3, 0xac, 0xb8, 0xff, + 0x37, 0x32, 0x2c, 0xff, 0xd3, 0xac, 0x71, 0xff, + 0xe8, 0xab, 0x5c, 0xff, 0xfc, 0xce, 0x89, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc6, 0x77, 0xff, + 0xfd, 0xdb, 0xaa, 0xff, 0xe8, 0xaf, 0x67, 0xfd, + 0xaf, 0x5d, 0x0f, 0xf2, 0x72, 0x73, 0x83, 0xa4, + 0x51, 0x83, 0xc9, 0x8a, 0xb1, 0x58, 0x01, 0xfe, + 0xfc, 0xb0, 0x41, 0xff, 0xc4, 0x70, 0x15, 0xfb, + 0x82, 0x6c, 0x61, 0xb5, 0x4e, 0x85, 0xd1, 0x88, + 0x4e, 0x85, 0xd1, 0x88, 0x4e, 0x85, 0xd1, 0x88, + 0x4e, 0x85, 0xd1, 0x88, 0x4e, 0x85, 0xd1, 0x88, + 0x4d, 0x84, 0xd2, 0x89, 0x4d, 0x84, 0xd2, 0x89, + 0x4d, 0x84, 0xd2, 0x89, 0x4d, 0x84, 0xd2, 0x89, + 0x4d, 0x84, 0xd2, 0x89, 0x56, 0x7f, 0xbe, 0x8f, + 0xac, 0x5d, 0x12, 0xf0, 0xef, 0xc3, 0x8b, 0xfe, + 0xfd, 0xe1, 0xb8, 0xff, 0xc8, 0x7e, 0x31, 0xff, + 0xcf, 0xd9, 0xe5, 0xff, 0xf1, 0xf3, 0xf6, 0xff, + 0x94, 0x9d, 0xa7, 0xff, 0x2e, 0x29, 0x22, 0xff, + 0xee, 0xc0, 0x7c, 0xff, 0xec, 0xb6, 0x6b, 0xff, + 0xfc, 0xd3, 0x95, 0xff, 0xfc, 0xd1, 0x92, 0xff, + 0xfc, 0xcf, 0x8d, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfd, 0xd4, 0x9a, 0xff, 0xc3, 0x79, 0x30, 0xfc, + 0x86, 0x6a, 0x58, 0xbc, 0x4d, 0x85, 0xd0, 0x8a, + 0x4d, 0x85, 0xd0, 0x8a, 0x5f, 0x7c, 0xaa, 0x96, + 0x97, 0x65, 0x39, 0xcf, 0xb3, 0x5c, 0x0a, 0xfb, + 0xbe, 0x70, 0x26, 0xfa, 0xd2, 0x87, 0x3c, 0xfb, + 0xe2, 0x95, 0x4c, 0xff, 0xe3, 0x98, 0x50, 0xff, + 0xba, 0x67, 0x15, 0xfd, 0x7e, 0x6e, 0x69, 0xb2, + 0x4d, 0x85, 0xd0, 0x8a, 0x4d, 0x85, 0xd0, 0x8a, + 0x4d, 0x85, 0xd0, 0x8a, 0x4d, 0x85, 0xd0, 0x8a, + 0x7d, 0x6f, 0x6b, 0xb1, 0xc0, 0x76, 0x29, 0xfb, + 0xfc, 0xe0, 0xb9, 0xff, 0xeb, 0xbf, 0x88, 0xff, + 0xca, 0xac, 0x90, 0xff, 0xf0, 0xf3, 0xf7, 0xff, + 0xd3, 0xd9, 0xdf, 0xff, 0x57, 0x56, 0x55, 0xff, + 0x87, 0x71, 0x52, 0xff, 0xee, 0xb5, 0x66, 0xff, + 0xfc, 0xd5, 0x9a, 0xff, 0xfc, 0xd3, 0x95, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x7f, 0xff, 0xfc, 0xd1, 0x94, 0xff, + 0xc6, 0x7d, 0x34, 0xfd, 0xa4, 0x5e, 0x1c, 0xe6, + 0x59, 0x7f, 0xb4, 0x94, 0x4c, 0x86, 0xd1, 0x8b, + 0x4c, 0x86, 0xd1, 0x8b, 0x4c, 0x86, 0xd1, 0x8b, + 0x4c, 0x86, 0xd1, 0x8b, 0x4c, 0x86, 0xd1, 0x8b, + 0x4c, 0x86, 0xd1, 0x8b, 0x55, 0x80, 0xbe, 0x91, + 0x84, 0x6a, 0x5c, 0xba, 0xab, 0x5c, 0x11, 0xef, + 0xba, 0x69, 0x1b, 0xfa, 0xb4, 0x5e, 0x0c, 0xfe, + 0x54, 0x81, 0xc2, 0x90, 0x4d, 0x85, 0xd1, 0x8b, + 0x4d, 0x85, 0xd1, 0x8b, 0x4d, 0x85, 0xd1, 0x8b, + 0x57, 0x7f, 0xba, 0x92, 0xb4, 0x68, 0x1e, 0xf2, + 0xf6, 0xd3, 0xa3, 0xff, 0xfd, 0xe5, 0xc1, 0xff, + 0xcd, 0x85, 0x3a, 0xff, 0xcd, 0xd6, 0xe2, 0xff, + 0xf3, 0xf5, 0xf8, 0xff, 0x9c, 0xa5, 0xb0, 0xff, + 0x33, 0x2e, 0x27, 0xff, 0xde, 0xb4, 0x75, 0xff, + 0xe9, 0xb1, 0x66, 0xff, 0xfc, 0xd3, 0x96, 0xff, + 0xfc, 0xd1, 0x92, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xce, 0x89, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x80, 0xff, 0xfd, 0xd8, 0xa2, 0xff, + 0xcf, 0x8b, 0x43, 0xfc, 0x9e, 0x60, 0x29, 0xdc, + 0x5c, 0x7d, 0xae, 0x97, 0x4c, 0x84, 0xd1, 0x8c, + 0x4c, 0x84, 0xd1, 0x8c, 0x4c, 0x84, 0xd1, 0x8c, + 0x4c, 0x84, 0xd1, 0x8c, 0x5a, 0x7d, 0xb3, 0x95, + 0x87, 0x6a, 0x54, 0xbf, 0xad, 0x5c, 0x0f, 0xf1, + 0xb9, 0x65, 0x15, 0xfa, 0xdc, 0x8c, 0x3d, 0xfe, + 0xb6, 0x62, 0x11, 0xfc, 0x5b, 0x7d, 0xb1, 0x96, + 0x4c, 0x84, 0xd1, 0x8c, 0x4c, 0x84, 0xd1, 0x8c, + 0x4c, 0x84, 0xd1, 0x8c, 0x4c, 0x84, 0xd1, 0x8c, + 0x4d, 0x84, 0xd1, 0x8d, 0x4d, 0x84, 0xd1, 0x8d, + 0x7e, 0x6d, 0x6a, 0xb5, 0xbd, 0x71, 0x20, 0xf9, + 0xfd, 0xe4, 0xbd, 0xff, 0xfd, 0xda, 0xa8, 0xff, + 0xeb, 0xb7, 0x76, 0xff, 0xcb, 0xae, 0x93, 0xff, + 0xf0, 0xf3, 0xf8, 0xff, 0xd3, 0xd8, 0xdf, 0xff, + 0x54, 0x54, 0x52, 0xff, 0x8b, 0x75, 0x54, 0xff, + 0xed, 0xb4, 0x64, 0xff, 0xfc, 0xce, 0x89, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc7, 0x78, 0xff, + 0xfd, 0xdc, 0xac, 0xff, 0xe7, 0xae, 0x66, 0xfd, + 0xaf, 0x5d, 0x0f, 0xf3, 0x71, 0x73, 0x85, 0xa8, + 0x4d, 0x84, 0xd1, 0x8d, 0x7a, 0x6f, 0x70, 0xb3, + 0xc1, 0x73, 0x25, 0xfa, 0xfa, 0xbf, 0x7f, 0xff, + 0xb2, 0x5a, 0x06, 0xfc, 0x5d, 0x7c, 0xae, 0x9a, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x4c, 0x84, 0xd2, 0x8e, 0x4c, 0x84, 0xd2, 0x8e, + 0x51, 0x88, 0xd1, 0x7c, 0x51, 0x88, 0xd1, 0x7c, + 0x51, 0x88, 0xd1, 0x7c, 0x98, 0x67, 0x3a, 0xc5, + 0xd5, 0x94, 0x4a, 0xfc, 0xfd, 0xe5, 0xbf, 0xff, + 0xf5, 0xc6, 0x88, 0xff, 0xd8, 0x9f, 0x66, 0xff, + 0x63, 0x87, 0xb6, 0xff, 0x5e, 0x6b, 0x7b, 0xff, + 0x56, 0x7d, 0xaf, 0xff, 0xc5, 0xc5, 0xc3, 0xff, + 0xe4, 0xb6, 0x72, 0xff, 0xe1, 0xa0, 0x49, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x80, 0xff, 0xfc, 0xc8, 0x7b, 0xff, + 0xfc, 0xc6, 0x76, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xc7, 0x7b, 0xff, 0xfa, 0xd7, 0xa3, 0xff, + 0xd1, 0x92, 0x4d, 0xfb, 0x9e, 0x65, 0x32, 0xce, + 0x67, 0x7b, 0x9d, 0x8f, 0x55, 0x83, 0xc5, 0x81, + 0x50, 0x87, 0xd1, 0x7d, 0x50, 0x87, 0xd1, 0x7d, + 0x5a, 0x81, 0xb9, 0x85, 0x84, 0x6d, 0x5e, 0xad, + 0xb4, 0x5c, 0x07, 0xff, 0xe6, 0x9e, 0x59, 0xff, + 0xdd, 0x99, 0x56, 0xfe, 0x8c, 0x69, 0x50, 0xb6, + 0x50, 0x87, 0xd1, 0x7d, 0x50, 0x87, 0xd1, 0x7d, + 0x50, 0x87, 0xd1, 0x7d, 0x61, 0x7e, 0xac, 0x89, + 0xb1, 0x5c, 0x08, 0xf9, 0xfb, 0xdd, 0xb4, 0xff, + 0xfd, 0xdf, 0xb1, 0xff, 0xd8, 0x93, 0x4a, 0xff, + 0xa3, 0xb4, 0xc9, 0xff, 0x6c, 0x83, 0xa1, 0xff, + 0x62, 0x7b, 0x9c, 0xff, 0xa3, 0xb8, 0xd1, 0xff, + 0xb2, 0xa5, 0x91, 0xff, 0xfc, 0xbd, 0x61, 0xff, + 0xf5, 0xc2, 0x7a, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xcd, 0x89, 0xff, + 0xfb, 0xd7, 0xa2, 0xff, 0xde, 0xa1, 0x5a, 0xfc, + 0xb3, 0x5e, 0x0b, 0xfa, 0x9c, 0x63, 0x32, 0xcd, + 0x68, 0x7a, 0x9c, 0x90, 0x68, 0x7a, 0x9c, 0x90, + 0x81, 0x6e, 0x66, 0xaa, 0x97, 0x65, 0x3a, 0xc6, + 0xaa, 0x5e, 0x18, 0xe5, 0xb7, 0x66, 0x17, 0xfb, + 0xd1, 0x8a, 0x47, 0xfb, 0xee, 0xab, 0x6a, 0xff, + 0xf9, 0xb7, 0x76, 0xff, 0xb7, 0x63, 0x14, 0xf9, + 0x62, 0x7e, 0xa7, 0x8c, 0x50, 0x87, 0xd1, 0x7e, + 0x50, 0x87, 0xd1, 0x7e, 0x50, 0x87, 0xd1, 0x7e, + 0xa3, 0x63, 0x28, 0xd7, 0xf2, 0xc9, 0x94, 0xfe, + 0xfd, 0xe4, 0xbf, 0xff, 0xf5, 0xc6, 0x89, 0xff, + 0xd8, 0x9f, 0x66, 0xff, 0x63, 0x87, 0xb6, 0xff, + 0x5e, 0x6b, 0x7b, 0xff, 0x56, 0x7d, 0xaf, 0xff, + 0xc5, 0xc5, 0xc3, 0xff, 0xe4, 0xb6, 0x72, 0xff, + 0xe1, 0xa0, 0x49, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xcf, 0x8d, 0xff, + 0xfc, 0xd8, 0xa3, 0xff, 0xe2, 0xa5, 0x5c, 0xfd, + 0xb2, 0x5b, 0x09, 0xfa, 0x9e, 0x64, 0x31, 0xcf, + 0x6d, 0x7a, 0x94, 0x95, 0x7f, 0x70, 0x6c, 0xa8, + 0xa6, 0x60, 0x1d, 0xdf, 0xb3, 0x5c, 0x09, 0xfb, + 0xd6, 0x8c, 0x46, 0xfb, 0xf1, 0xb4, 0x7a, 0xff, + 0xf9, 0xbe, 0x83, 0xff, 0xf9, 0xbb, 0x7f, 0xff, + 0xb3, 0x5b, 0x07, 0xf9, 0x65, 0x7d, 0xa4, 0x8e, + 0x51, 0x88, 0xd1, 0x7f, 0x51, 0x88, 0xd1, 0x7f, + 0x51, 0x88, 0xd1, 0x7f, 0x51, 0x88, 0xd1, 0x7f, + 0x51, 0x88, 0xd1, 0x7f, 0x52, 0x85, 0xcc, 0x81, + 0xae, 0x5d, 0x0e, 0xf1, 0xf5, 0xcb, 0x91, 0xff, + 0xfd, 0xdd, 0xaf, 0xff, 0xfa, 0xd0, 0x95, 0xff, + 0xd2, 0x8f, 0x4d, 0xff, 0x68, 0x89, 0xb4, 0xff, + 0x5f, 0x6d, 0x7d, 0xff, 0x57, 0x7f, 0xb2, 0xff, + 0xcb, 0xcc, 0xcd, 0xff, 0xd4, 0xae, 0x77, 0xff, + 0xe3, 0xa1, 0x49, 0xff, 0xfc, 0xc9, 0x7d, 0xff, + 0xfc, 0xc7, 0x78, 0xff, 0xfc, 0xc5, 0x73, 0xff, + 0xfc, 0xc3, 0x6f, 0xff, 0xfb, 0xc1, 0x6a, 0xff, + 0xfb, 0xc1, 0x6e, 0xff, 0xfc, 0xd4, 0x97, 0xff, + 0xf7, 0xc9, 0x88, 0xff, 0xbb, 0x6a, 0x19, 0xfa, + 0xa5, 0x60, 0x20, 0xdd, 0xb1, 0x57, 0x00, 0xff, + 0xfc, 0xb4, 0x4b, 0xff, 0xe3, 0x94, 0x2e, 0xff, + 0xa6, 0x64, 0x24, 0xdd, 0x50, 0x88, 0xd1, 0x80, + 0x50, 0x88, 0xd1, 0x80, 0x50, 0x88, 0xd1, 0x80, + 0x50, 0x88, 0xd1, 0x80, 0x50, 0x88, 0xd1, 0x80, + 0x50, 0x88, 0xd1, 0x80, 0x50, 0x88, 0xd1, 0x80, + 0x50, 0x88, 0xd1, 0x80, 0x50, 0x88, 0xd1, 0x80, + 0x50, 0x88, 0xd1, 0x80, 0x97, 0x67, 0x3c, 0xc7, + 0xd8, 0x9b, 0x56, 0xfc, 0xfd, 0xe3, 0xbc, 0xff, + 0xf5, 0xc6, 0x88, 0xff, 0xd8, 0x9f, 0x66, 0xff, + 0x63, 0x87, 0xb6, 0xff, 0x5e, 0x6b, 0x7b, 0xff, + 0x56, 0x7d, 0xaf, 0xff, 0xc5, 0xc5, 0xc3, 0xff, + 0xe4, 0xb6, 0x72, 0xff, 0xe1, 0xa0, 0x49, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xcc, 0x84, 0xff, + 0xfc, 0xca, 0x80, 0xff, 0xfc, 0xc8, 0x7b, 0xff, + 0xfc, 0xc6, 0x76, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xcd, 0x88, 0xff, 0xfa, 0xd5, 0x9f, 0xff, + 0xcb, 0x84, 0x37, 0xfb, 0x9e, 0x65, 0x31, 0xd1, + 0x9d, 0x64, 0x31, 0xd1, 0xb5, 0x61, 0x0f, 0xfb, + 0xd9, 0x90, 0x47, 0xfd, 0xf5, 0xb0, 0x6c, 0xff, + 0xf8, 0xa7, 0x59, 0xff, 0xf5, 0x99, 0x40, 0xff, + 0xf3, 0x8d, 0x2a, 0xff, 0xf8, 0xab, 0x60, 0xff, + 0xbc, 0x6e, 0x23, 0xfb, 0x64, 0x7b, 0xa4, 0x90, + 0x4f, 0x86, 0xd0, 0x82, 0x4f, 0x86, 0xd0, 0x82, + 0x4f, 0x86, 0xd0, 0x82, 0x60, 0x7d, 0xac, 0x8e, + 0xb0, 0x5c, 0x08, 0xf9, 0xfb, 0xdd, 0xb4, 0xff, + 0xfd, 0xdf, 0xb1, 0xff, 0xd8, 0x93, 0x4a, 0xff, + 0xa3, 0xb4, 0xc9, 0xff, 0x6c, 0x83, 0xa1, 0xff, + 0x62, 0x7b, 0x9c, 0xff, 0xa3, 0xb8, 0xd1, 0xff, + 0xb2, 0xa5, 0x91, 0xff, 0xfc, 0xbd, 0x61, 0xff, + 0xf5, 0xc2, 0x7a, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xcd, 0x89, 0xff, + 0xfb, 0xd7, 0xa2, 0xff, 0xde, 0xa1, 0x5a, 0xfc, + 0xb3, 0x5e, 0x0b, 0xfb, 0x9c, 0x63, 0x33, 0xcf, + 0x67, 0x79, 0x9e, 0x94, 0x67, 0x79, 0x9e, 0x94, + 0x80, 0x6d, 0x68, 0xac, 0x96, 0x64, 0x3c, 0xc8, + 0xaa, 0x5e, 0x18, 0xe6, 0xb3, 0x5d, 0x0a, 0xfb, + 0xcd, 0x7f, 0x35, 0xfb, 0xec, 0xa5, 0x5f, 0xff, + 0xf9, 0xb6, 0x75, 0xff, 0xb7, 0x63, 0x14, 0xfa, + 0x62, 0x7c, 0xa8, 0x90, 0x50, 0x85, 0xd1, 0x82, + 0x50, 0x85, 0xd1, 0x82, 0x50, 0x85, 0xd1, 0x82, + 0x9b, 0x65, 0x35, 0xce, 0xee, 0xc2, 0x88, 0xfe, + 0xfd, 0xe5, 0xc1, 0xff, 0xf7, 0xcd, 0x91, 0xff, + 0xd4, 0x95, 0x54, 0xff, 0x66, 0x88, 0xb5, 0xff, + 0x5f, 0x6c, 0x7c, 0xff, 0x56, 0x7e, 0xb2, 0xff, + 0xca, 0xca, 0xc9, 0xff, 0xdc, 0xb2, 0x75, 0xff, + 0xe2, 0xa1, 0x4a, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xcc, 0x84, 0xff, 0xfc, 0xca, 0x80, 0xff, + 0xfc, 0xc8, 0x7b, 0xff, 0xfc, 0xc6, 0x77, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xcd, 0x88, 0xff, + 0xfc, 0xd9, 0xa6, 0xff, 0xe4, 0xaa, 0x61, 0xfd, + 0xb3, 0x5d, 0x0a, 0xfb, 0x9e, 0x63, 0x2d, 0xd4, + 0x6d, 0x77, 0x91, 0x9a, 0x7a, 0x71, 0x73, 0xa7, + 0xa4, 0x60, 0x22, 0xdc, 0xb1, 0x5b, 0x08, 0xfb, + 0xcf, 0x7f, 0x30, 0xfb, 0xed, 0xa1, 0x59, 0xff, + 0xf9, 0xb0, 0x6a, 0xff, 0xf9, 0xb6, 0x75, 0xff, + 0xb8, 0x61, 0x0f, 0xf8, 0x6c, 0x79, 0x92, 0x9a, + 0x4f, 0x87, 0xd1, 0x84, 0x4f, 0x87, 0xd1, 0x84, + 0x4f, 0x87, 0xd1, 0x84, 0x4f, 0x87, 0xd1, 0x84, + 0x4f, 0x87, 0xd1, 0x84, 0x51, 0x85, 0xcc, 0x85, + 0xae, 0x5d, 0x0e, 0xf2, 0xf5, 0xc9, 0x8d, 0xff, + 0xfd, 0xdf, 0xb2, 0xff, 0xfd, 0xd6, 0x9c, 0xff, + 0xd7, 0x92, 0x4a, 0xff, 0xa0, 0xb2, 0xca, 0xff, + 0x6e, 0x84, 0xa1, 0xff, 0x64, 0x7d, 0x9e, 0xff, + 0xa7, 0xba, 0xd3, 0xff, 0xaf, 0xa1, 0x8d, 0xff, + 0xfc, 0xbd, 0x61, 0xff, 0xf5, 0xbd, 0x6e, 0xff, + 0xfc, 0xc7, 0x78, 0xff, 0xfc, 0xc5, 0x73, 0xff, + 0xfc, 0xc3, 0x6f, 0xff, 0xfb, 0xc1, 0x6a, 0xff, + 0xfb, 0xc2, 0x70, 0xff, 0xfc, 0xd4, 0x99, 0xff, + 0xf6, 0xc7, 0x84, 0xff, 0xba, 0x67, 0x15, 0xfa, + 0xa5, 0x61, 0x21, 0xdf, 0xb1, 0x5a, 0x06, 0xfa, + 0xe9, 0xa7, 0x5d, 0xff, 0xfa, 0xc1, 0x81, 0xff, + 0xbe, 0x6d, 0x1c, 0xf9, 0x75, 0x73, 0x7d, 0xa5, + 0x4e, 0x86, 0xd1, 0x85, 0x4e, 0x86, 0xd1, 0x85, + 0x4e, 0x86, 0xd1, 0x85, 0x4e, 0x86, 0xd1, 0x85, + 0x4e, 0x86, 0xd1, 0x85, 0x4e, 0x86, 0xd1, 0x85, + 0x52, 0x89, 0xd1, 0x73, 0x52, 0x89, 0xd1, 0x73, + 0x68, 0x7d, 0x9e, 0x85, 0xb3, 0x5d, 0x0a, 0xfa, + 0xfb, 0xda, 0xac, 0xff, 0xfd, 0xda, 0xa8, 0xff, + 0xf9, 0xce, 0x95, 0xff, 0xda, 0x96, 0x53, 0xff, + 0x85, 0x9c, 0xbb, 0xff, 0x36, 0x65, 0xa2, 0xff, + 0x80, 0x98, 0xb6, 0xff, 0xe0, 0xe0, 0xdc, 0xff, + 0xc2, 0x9d, 0x6d, 0xff, 0xe3, 0xa0, 0x45, 0xff, + 0xfc, 0xca, 0x81, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xc2, 0x6e, 0xff, + 0xfb, 0xbf, 0x66, 0xff, 0xfc, 0xba, 0x5a, 0xff, + 0xfc, 0xb6, 0x4e, 0xff, 0xfc, 0xb8, 0x55, 0xff, + 0xfb, 0xcc, 0x8a, 0xff, 0xe0, 0xaa, 0x6b, 0xfc, + 0xc2, 0x7b, 0x35, 0xf8, 0xba, 0x6c, 0x20, 0xfc, + 0xb5, 0x63, 0x13, 0xf7, 0xb1, 0x5e, 0x0e, 0xf4, + 0xba, 0x6a, 0x1e, 0xfc, 0xca, 0x83, 0x3d, 0xfa, + 0xed, 0xb0, 0x75, 0xff, 0xf8, 0xad, 0x65, 0xff, + 0xd8, 0x94, 0x51, 0xfc, 0x8a, 0x6b, 0x57, 0xaa, + 0x53, 0x88, 0xd2, 0x74, 0x53, 0x88, 0xd2, 0x74, + 0x53, 0x88, 0xd2, 0x74, 0xa5, 0x62, 0x24, 0xd5, + 0xe9, 0xb7, 0x76, 0xfd, 0xfd, 0xe3, 0xbb, 0xff, + 0xfd, 0xd8, 0xa1, 0xff, 0xe4, 0xa3, 0x5e, 0xff, + 0xd0, 0xc3, 0xb7, 0xff, 0x42, 0x6c, 0xa2, 0xff, + 0x42, 0x6c, 0xa2, 0xff, 0xc9, 0xce, 0xd2, 0xff, + 0xda, 0xd8, 0xd3, 0xff, 0xe8, 0xa3, 0x43, 0xff, + 0xf9, 0xc5, 0x7b, 0xff, 0xfc, 0xc8, 0x7d, 0xff, + 0xfc, 0xc6, 0x76, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xc2, 0x6e, 0xff, 0xfc, 0xbf, 0x65, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfc, 0xb6, 0x4e, 0xff, + 0xfc, 0xbb, 0x5b, 0xff, 0xfc, 0xca, 0x82, 0xff, + 0xf8, 0xcb, 0x8c, 0xff, 0xdd, 0x99, 0x51, 0xfd, + 0xb8, 0x65, 0x14, 0xfb, 0xba, 0x68, 0x1a, 0xf9, + 0xce, 0x83, 0x3b, 0xf8, 0xe3, 0x9f, 0x5c, 0xfe, + 0xf1, 0xbb, 0x87, 0xff, 0xf8, 0xc5, 0x93, 0xff, + 0xf8, 0xb1, 0x6c, 0xff, 0xf6, 0x95, 0x35, 0xff, + 0xf9, 0xb5, 0x73, 0xff, 0xb5, 0x5c, 0x07, 0xf8, + 0x6d, 0x7b, 0x93, 0x8b, 0x52, 0x89, 0xd0, 0x75, + 0x52, 0x89, 0xd0, 0x75, 0x7a, 0x73, 0x79, 0x97, + 0xcd, 0x88, 0x40, 0xfb, 0xfd, 0xe7, 0xc5, 0xff, + 0xfd, 0xd8, 0xa4, 0xff, 0xf9, 0xce, 0x95, 0xff, + 0xda, 0x96, 0x53, 0xff, 0x85, 0x9c, 0xbb, 0xff, + 0x36, 0x65, 0xa2, 0xff, 0x80, 0x98, 0xb6, 0xff, + 0xe0, 0xe0, 0xdc, 0xff, 0xc2, 0x9d, 0x6d, 0xff, + 0xe3, 0xa0, 0x45, 0xff, 0xfc, 0xca, 0x81, 0xff, + 0xfc, 0xc6, 0x76, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xc2, 0x6e, 0xff, 0xfb, 0xbf, 0x63, 0xff, + 0xfb, 0xb8, 0x58, 0xff, 0xfc, 0xb4, 0x4e, 0xff, + 0xfc, 0xbb, 0x5e, 0xff, 0xfd, 0xcd, 0x87, 0xff, + 0xf8, 0xca, 0x8a, 0xff, 0xdb, 0x95, 0x49, 0xfd, + 0xb6, 0x62, 0x10, 0xfb, 0xc3, 0x73, 0x24, 0xf7, + 0xe8, 0x9d, 0x56, 0xff, 0xf8, 0xc1, 0x8b, 0xff, + 0xfa, 0xbe, 0x84, 0xff, 0xf8, 0xa7, 0x59, 0xff, + 0xf6, 0x96, 0x37, 0xff, 0xf9, 0xb5, 0x74, 0xff, + 0xb3, 0x5a, 0x03, 0xfa, 0x66, 0x7b, 0xa2, 0x87, + 0x51, 0x87, 0xd1, 0x77, 0x51, 0x87, 0xd1, 0x77, + 0x51, 0x87, 0xd1, 0x77, 0x51, 0x87, 0xd1, 0x77, + 0x51, 0x87, 0xd1, 0x77, 0x86, 0x6c, 0x5c, 0xa9, + 0xc5, 0x7c, 0x2e, 0xf9, 0xfd, 0xe3, 0xbb, 0xff, + 0xfc, 0xd5, 0x9c, 0xff, 0xfb, 0xd2, 0x97, 0xff, + 0xd8, 0x8f, 0x47, 0xff, 0x99, 0xac, 0xc3, 0xff, + 0x36, 0x65, 0xa1, 0xff, 0x70, 0x8d, 0xb0, 0xff, + 0xde, 0xde, 0xdb, 0xff, 0xc0, 0xa5, 0x82, 0xff, + 0xe2, 0x9b, 0x3d, 0xff, 0xfc, 0xc6, 0x78, 0xff, + 0xfb, 0xc1, 0x6c, 0xff, 0xfb, 0xbe, 0x63, 0xff, + 0xfc, 0xb5, 0x4e, 0xff, 0xfc, 0xb1, 0x43, 0xff, + 0xfc, 0xaf, 0x3d, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xbe, 0x65, 0xff, 0xf9, 0xc9, 0x88, 0xff, + 0xe5, 0xa8, 0x61, 0xff, 0xb1, 0x57, 0x01, 0xff, + 0xfb, 0xb8, 0x55, 0xff, 0xef, 0xa6, 0x43, 0xff, + 0xaf, 0x5f, 0x10, 0xee, 0x52, 0x88, 0xd1, 0x77, + 0x52, 0x88, 0xd1, 0x77, 0x52, 0x88, 0xd1, 0x77, + 0x52, 0x88, 0xd1, 0x77, 0x52, 0x88, 0xd1, 0x77, + 0x52, 0x87, 0xd1, 0x78, 0x52, 0x87, 0xd1, 0x78, + 0x52, 0x87, 0xd1, 0x78, 0x52, 0x87, 0xd1, 0x78, + 0x67, 0x7b, 0xa0, 0x89, 0xb5, 0x61, 0x0f, 0xfa, + 0xfb, 0xdb, 0xae, 0xff, 0xfd, 0xd8, 0xa3, 0xff, + 0xf9, 0xce, 0x95, 0xff, 0xda, 0x96, 0x53, 0xff, + 0x85, 0x9c, 0xbb, 0xff, 0x36, 0x65, 0xa2, 0xff, + 0x80, 0x98, 0xb6, 0xff, 0xe0, 0xe0, 0xdc, 0xff, + 0xc2, 0x9d, 0x6d, 0xff, 0xe3, 0xa0, 0x45, 0xff, + 0xfc, 0xca, 0x81, 0xff, 0xfc, 0xc6, 0x76, 0xff, + 0xfc, 0xc4, 0x73, 0xff, 0xfc, 0xc2, 0x6e, 0xff, + 0xfb, 0xbf, 0x66, 0xff, 0xfc, 0xba, 0x5a, 0xff, + 0xfc, 0xb6, 0x4e, 0xff, 0xfc, 0xbe, 0x63, 0xff, + 0xfb, 0xd1, 0x95, 0xff, 0xdd, 0xa1, 0x5b, 0xfc, + 0xde, 0x9d, 0x56, 0xfd, 0xf7, 0xbb, 0x78, 0xff, + 0xf8, 0xa1, 0x47, 0xff, 0xf4, 0x7d, 0x0a, 0xff, + 0xf2, 0x77, 0x00, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf3, 0x78, 0x00, 0xff, 0xf8, 0xa1, 0x4d, 0xff, + 0xc7, 0x79, 0x2f, 0xf8, 0x7c, 0x72, 0x72, 0x9f, + 0x51, 0x88, 0xd2, 0x79, 0x51, 0x88, 0xd2, 0x79, + 0x51, 0x88, 0xd2, 0x79, 0xa4, 0x63, 0x26, 0xd7, + 0xe9, 0xb7, 0x76, 0xfd, 0xfd, 0xe3, 0xbb, 0xff, + 0xfd, 0xd8, 0xa1, 0xff, 0xe4, 0xa3, 0x5e, 0xff, + 0xd0, 0xc3, 0xb7, 0xff, 0x42, 0x6c, 0xa2, 0xff, + 0x42, 0x6c, 0xa2, 0xff, 0xc9, 0xce, 0xd2, 0xff, + 0xda, 0xd8, 0xd3, 0xff, 0xe8, 0xa3, 0x43, 0xff, + 0xf9, 0xc5, 0x7b, 0xff, 0xfc, 0xc8, 0x7d, 0xff, + 0xfc, 0xc6, 0x76, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xc2, 0x6e, 0xff, 0xfc, 0xbf, 0x65, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfc, 0xb6, 0x4e, 0xff, + 0xfc, 0xbb, 0x5b, 0xff, 0xfc, 0xca, 0x82, 0xff, + 0xf8, 0xcb, 0x8c, 0xff, 0xdd, 0x99, 0x51, 0xfd, + 0xb8, 0x65, 0x14, 0xfb, 0xba, 0x68, 0x1a, 0xf9, + 0xcb, 0x7d, 0x31, 0xf9, 0xe0, 0x90, 0x43, 0xfe, + 0xec, 0xa2, 0x5a, 0xff, 0xf7, 0xb2, 0x6f, 0xff, + 0xf8, 0xa1, 0x4e, 0xff, 0xf6, 0x8a, 0x22, 0xff, + 0xf9, 0xb4, 0x72, 0xff, 0xb5, 0x5c, 0x07, 0xf8, + 0x6b, 0x7a, 0x96, 0x8f, 0x50, 0x88, 0xd1, 0x7a, + 0x50, 0x88, 0xd1, 0x7a, 0x71, 0x77, 0x8a, 0x94, + 0xc6, 0x7e, 0x33, 0xfb, 0xfd, 0xe7, 0xc5, 0xff, + 0xfd, 0xd9, 0xa5, 0xff, 0xfb, 0xd2, 0x9a, 0xff, + 0xd8, 0x91, 0x4a, 0xff, 0x91, 0xa6, 0xc0, 0xff, + 0x36, 0x65, 0xa1, 0xff, 0x76, 0x91, 0xb3, 0xff, + 0xdf, 0xdf, 0xdc, 0xff, 0xc2, 0xa3, 0x79, 0xff, + 0xe2, 0x9e, 0x43, 0xff, 0xfc, 0xca, 0x81, 0xff, + 0xfc, 0xc6, 0x77, 0xff, 0xfc, 0xc4, 0x73, 0xff, + 0xfc, 0xc2, 0x6e, 0xff, 0xfb, 0xbf, 0x65, 0xff, + 0xfb, 0xb9, 0x58, 0xff, 0xfc, 0xb5, 0x4e, 0xff, + 0xfc, 0xbb, 0x5d, 0xff, 0xfc, 0xcb, 0x84, 0xff, + 0xf9, 0xcc, 0x8d, 0xff, 0xde, 0x9a, 0x4d, 0xfd, + 0xb6, 0x63, 0x13, 0xfa, 0xc0, 0x70, 0x20, 0xf8, + 0xe4, 0x95, 0x47, 0xfe, 0xf6, 0xb1, 0x6d, 0xff, + 0xf8, 0xa4, 0x53, 0xff, 0xf6, 0x8c, 0x25, 0xff, + 0xf5, 0x7d, 0x08, 0xff, 0xf9, 0xb4, 0x71, 0xff, + 0xb6, 0x5c, 0x05, 0xf8, 0x6f, 0x79, 0x8f, 0x93, + 0x51, 0x89, 0xd1, 0x7b, 0x51, 0x89, 0xd1, 0x7b, + 0x51, 0x89, 0xd1, 0x7b, 0x51, 0x89, 0xd1, 0x7b, + 0x51, 0x89, 0xd1, 0x7b, 0x85, 0x6d, 0x5e, 0xac, + 0xc4, 0x79, 0x29, 0xf9, 0xfd, 0xe4, 0xbd, 0xff, + 0xfc, 0xd5, 0x9c, 0xff, 0xfc, 0xd5, 0x9b, 0xff, + 0xe2, 0xa1, 0x59, 0xff, 0xcd, 0xc2, 0xb8, 0xff, + 0x41, 0x6c, 0xa1, 0xff, 0x44, 0x6d, 0xa2, 0xff, + 0xc8, 0xcd, 0xd2, 0xff, 0xd6, 0xd3, 0xcf, 0xff, + 0xe7, 0xa2, 0x42, 0xff, 0xf9, 0xc0, 0x71, 0xff, + 0xfb, 0xc1, 0x6e, 0xff, 0xfb, 0xbe, 0x63, 0xff, + 0xfc, 0xb5, 0x4e, 0xff, 0xfc, 0xb1, 0x43, 0xff, + 0xfc, 0xaf, 0x3d, 0xff, 0xfc, 0xaf, 0x3f, 0xff, + 0xfc, 0xc0, 0x6a, 0xff, 0xf9, 0xc8, 0x87, 0xff, + 0xe4, 0xa2, 0x56, 0xff, 0xf5, 0xb8, 0x6e, 0xff, + 0xfa, 0xb9, 0x6e, 0xff, 0xf9, 0xb3, 0x65, 0xff, + 0xd0, 0x80, 0x30, 0xfb, 0x89, 0x6a, 0x55, 0xb3, + 0x50, 0x87, 0xd1, 0x7d, 0x50, 0x87, 0xd1, 0x7d, + 0x50, 0x87, 0xd1, 0x7d, 0x50, 0x87, 0xd1, 0x7d, + 0x50, 0x87, 0xd1, 0x7d, 0x50, 0x87, 0xd1, 0x7d, + 0x55, 0x8a, 0xd1, 0x6b, 0x55, 0x8a, 0xd1, 0x6b, + 0xa7, 0x62, 0x22, 0xd2, 0xe2, 0xa5, 0x5e, 0xfd, + 0xfc, 0xe1, 0xb6, 0xff, 0xfc, 0xd4, 0x97, 0xff, + 0xfc, 0xd7, 0x9f, 0xff, 0xe7, 0xa2, 0x5a, 0xff, + 0xdc, 0xba, 0x96, 0xff, 0xd1, 0xce, 0xc7, 0xff, + 0xcb, 0xcb, 0xc5, 0xff, 0xd0, 0xc4, 0xb2, 0xff, + 0xce, 0x88, 0x45, 0xff, 0xf3, 0xba, 0x6d, 0xff, + 0xfc, 0xc6, 0x79, 0xff, 0xfb, 0xc0, 0x69, 0xff, + 0xfb, 0xbd, 0x5e, 0xff, 0xfd, 0xb4, 0x4b, 0xff, + 0xfc, 0xaf, 0x3f, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfa, 0xb6, 0x5a, 0xff, + 0xfa, 0xb6, 0x64, 0xff, 0xf9, 0xb4, 0x68, 0xff, + 0xf6, 0xb2, 0x6d, 0xff, 0xf3, 0xb0, 0x6f, 0xff, + 0xf9, 0xb2, 0x6c, 0xff, 0xf9, 0xb9, 0x7b, 0xff, + 0xf8, 0xa3, 0x53, 0xff, 0xf7, 0x97, 0x3b, 0xff, + 0xcd, 0x88, 0x44, 0xf9, 0x84, 0x70, 0x63, 0x9a, + 0x54, 0x8b, 0xd1, 0x6b, 0x54, 0x8b, 0xd1, 0x6b, + 0x80, 0x71, 0x6c, 0x94, 0xbe, 0x71, 0x22, 0xf9, + 0xfd, 0xe5, 0xc0, 0xff, 0xfc, 0xd6, 0x9e, 0xff, + 0xfc, 0xd7, 0x9f, 0xff, 0xf7, 0xca, 0x8a, 0xff, + 0xe1, 0x9b, 0x55, 0xff, 0xd7, 0xcd, 0xbf, 0xff, + 0xcc, 0xcc, 0xc6, 0xff, 0xcd, 0xcb, 0xc2, 0xff, + 0xd8, 0xae, 0x81, 0xff, 0xd8, 0x9a, 0x4c, 0xff, + 0xfc, 0xcc, 0x86, 0xff, 0xfc, 0xc2, 0x6f, 0xff, + 0xfb, 0xc1, 0x68, 0xff, 0xfb, 0xba, 0x57, 0xff, + 0xfb, 0xb2, 0x47, 0xff, 0xfc, 0xaf, 0x3d, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa9, 0x3d, 0xff, 0xfa, 0xb2, 0x5b, 0xff, + 0xfa, 0xbb, 0x75, 0xff, 0xf9, 0xb3, 0x6b, 0xff, + 0xf8, 0xae, 0x66, 0xff, 0xf8, 0xac, 0x62, 0xff, + 0xf8, 0xac, 0x64, 0xff, 0xf7, 0x9c, 0x45, 0xff, + 0xf6, 0x8f, 0x2b, 0xff, 0xf5, 0x8a, 0x22, 0xff, + 0xf5, 0xae, 0x69, 0xff, 0xb1, 0x59, 0x03, 0xf9, + 0x57, 0x89, 0xca, 0x6f, 0x55, 0x8b, 0xd0, 0x6d, + 0x58, 0x88, 0xc8, 0x6f, 0xb0, 0x5f, 0x11, 0xed, + 0xfa, 0xdc, 0xb1, 0xff, 0xfc, 0xde, 0xb0, 0xff, + 0xfc, 0xd4, 0x97, 0xff, 0xfc, 0xd7, 0x9f, 0xff, + 0xe7, 0xa2, 0x5a, 0xff, 0xdc, 0xba, 0x96, 0xff, + 0xd1, 0xce, 0xc7, 0xff, 0xcb, 0xcb, 0xc5, 0xff, + 0xd0, 0xc4, 0xb2, 0xff, 0xce, 0x88, 0x45, 0xff, + 0xf3, 0xba, 0x6d, 0xff, 0xfc, 0xc6, 0x79, 0xff, + 0xfb, 0xc0, 0x67, 0xff, 0xfb, 0xb9, 0x56, 0xff, + 0xfb, 0xb1, 0x46, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xac, 0x43, 0xff, 0xfa, 0xb6, 0x63, 0xff, + 0xf9, 0xbe, 0x7a, 0xff, 0xf9, 0xb4, 0x6d, 0xff, + 0xf7, 0xa3, 0x52, 0xff, 0xf7, 0x9a, 0x40, 0xff, + 0xf8, 0xa1, 0x4e, 0xff, 0xf7, 0x9a, 0x40, 0xff, + 0xf6, 0x99, 0x3f, 0xff, 0xf3, 0xac, 0x68, 0xff, + 0xb1, 0x59, 0x05, 0xf6, 0x54, 0x8a, 0xd0, 0x6e, + 0x54, 0x8a, 0xd0, 0x6e, 0x54, 0x8a, 0xd0, 0x6e, + 0x54, 0x8a, 0xd0, 0x6e, 0x54, 0x8a, 0xd0, 0x6e, + 0x54, 0x8a, 0xd0, 0x6e, 0xb0, 0x5d, 0x0c, 0xf1, + 0xf4, 0xca, 0x90, 0xff, 0xfc, 0xd9, 0xa5, 0xff, + 0xfc, 0xd0, 0x8f, 0xff, 0xfc, 0xd5, 0x9a, 0xff, + 0xeb, 0xaa, 0x60, 0xff, 0xde, 0xb3, 0x87, 0xff, + 0xd1, 0xce, 0xc6, 0xff, 0xcb, 0xcb, 0xc5, 0xff, + 0xd0, 0xc7, 0xb8, 0xff, 0xd5, 0x91, 0x50, 0xff, + 0xea, 0xae, 0x5f, 0xff, 0xfb, 0xc4, 0x70, 0xff, + 0xfc, 0xb6, 0x4d, 0xff, 0xfc, 0xb0, 0x3f, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xad, 0x41, 0xff, + 0xfa, 0xb0, 0x50, 0xff, 0xb2, 0x5a, 0x02, 0xff, + 0xfa, 0xba, 0x5f, 0xff, 0xf9, 0xb8, 0x5b, 0xff, + 0xb1, 0x59, 0x03, 0xfb, 0x53, 0x8a, 0xd1, 0x6f, + 0x53, 0x8a, 0xd1, 0x6f, 0x53, 0x8a, 0xd1, 0x6f, + 0x53, 0x8a, 0xd1, 0x6f, 0x53, 0x8a, 0xd1, 0x6f, + 0x53, 0x8a, 0xd1, 0x6f, 0x53, 0x8a, 0xd1, 0x6f, + 0x53, 0x8a, 0xd1, 0x6f, 0x53, 0x8a, 0xd1, 0x6f, + 0xa6, 0x63, 0x23, 0xd4, 0xe3, 0xab, 0x68, 0xfd, + 0xfc, 0xde, 0xaf, 0xff, 0xfc, 0xd4, 0x97, 0xff, + 0xfc, 0xd7, 0x9f, 0xff, 0xe7, 0xa2, 0x5a, 0xff, + 0xdc, 0xba, 0x96, 0xff, 0xd1, 0xce, 0xc7, 0xff, + 0xcb, 0xcb, 0xc5, 0xff, 0xd0, 0xc4, 0xb2, 0xff, + 0xce, 0x88, 0x45, 0xff, 0xf3, 0xba, 0x6d, 0xff, + 0xfc, 0xc6, 0x79, 0xff, 0xfb, 0xc0, 0x69, 0xff, + 0xfb, 0xbd, 0x5e, 0xff, 0xfd, 0xb4, 0x4b, 0xff, + 0xfc, 0xaf, 0x3f, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb2, 0x47, 0xff, 0xfb, 0xb9, 0x60, 0xff, + 0xfa, 0xad, 0x51, 0xff, 0xf7, 0x91, 0x1e, 0xff, + 0xf6, 0x82, 0x0a, 0xff, 0xf3, 0x78, 0x00, 0xff, + 0xf2, 0x77, 0x00, 0xff, 0xf2, 0x77, 0x00, 0xff, + 0xf3, 0x78, 0x00, 0xff, 0xf6, 0x90, 0x2d, 0xff, + 0xe1, 0x94, 0x49, 0xfe, 0x9f, 0x62, 0x2c, 0xc8, + 0x54, 0x8a, 0xd1, 0x70, 0x54, 0x8a, 0xd2, 0x71, + 0x7e, 0x71, 0x70, 0x99, 0xbe, 0x71, 0x22, 0xf9, + 0xfd, 0xe5, 0xc0, 0xff, 0xfc, 0xd6, 0x9e, 0xff, + 0xfc, 0xd7, 0x9f, 0xff, 0xf7, 0xca, 0x8a, 0xff, + 0xe1, 0x9b, 0x55, 0xff, 0xd7, 0xcd, 0xbf, 0xff, + 0xcc, 0xcc, 0xc6, 0xff, 0xcd, 0xcb, 0xc2, 0xff, + 0xd8, 0xae, 0x81, 0xff, 0xd8, 0x9a, 0x4c, 0xff, + 0xfc, 0xcc, 0x86, 0xff, 0xfc, 0xc2, 0x6f, 0xff, + 0xfb, 0xc1, 0x68, 0xff, 0xfb, 0xba, 0x57, 0xff, + 0xfb, 0xb2, 0x47, 0xff, 0xfc, 0xaf, 0x3d, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa9, 0x3d, 0xff, 0xfa, 0xb2, 0x5b, 0xff, + 0xfa, 0xbb, 0x75, 0xff, 0xf9, 0xb3, 0x6a, 0xff, + 0xf8, 0xa1, 0x4d, 0xff, 0xf7, 0x95, 0x36, 0xff, + 0xf6, 0x89, 0x20, 0xff, 0xf5, 0x7c, 0x07, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf5, 0x80, 0x0e, 0xff, + 0xf5, 0xae, 0x69, 0xff, 0xb1, 0x59, 0x04, 0xf9, + 0x55, 0x87, 0xca, 0x73, 0x53, 0x89, 0xd0, 0x71, + 0x54, 0x87, 0xce, 0x72, 0xab, 0x5f, 0x15, 0xe6, + 0xf8, 0xd6, 0xa9, 0xff, 0xfc, 0xdf, 0xb3, 0xff, + 0xfc, 0xd3, 0x96, 0xff, 0xfc, 0xd7, 0xa0, 0xff, + 0xe9, 0xa8, 0x5e, 0xff, 0xdd, 0xb7, 0x8e, 0xff, + 0xd1, 0xcf, 0xc7, 0xff, 0xcb, 0xcb, 0xc5, 0xff, + 0xd1, 0xc5, 0xb5, 0xff, 0xd4, 0x8f, 0x4b, 0xff, + 0xed, 0xb6, 0x68, 0xff, 0xfc, 0xc7, 0x7a, 0xff, + 0xfb, 0xc1, 0x68, 0xff, 0xfb, 0xb9, 0x59, 0xff, + 0xfb, 0xb2, 0x48, 0xff, 0xfc, 0xaf, 0x3d, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xac, 0x41, 0xff, 0xfa, 0xb6, 0x62, 0xff, + 0xf9, 0xbd, 0x78, 0xff, 0xf9, 0xb4, 0x6b, 0xff, + 0xf7, 0x96, 0x38, 0xff, 0xf5, 0x7f, 0x0c, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf5, 0x79, 0x00, 0xff, + 0xf5, 0x81, 0x11, 0xff, 0xf5, 0xac, 0x65, 0xff, + 0xb1, 0x58, 0x03, 0xfb, 0x54, 0x87, 0xcb, 0x75, + 0x52, 0x89, 0xd1, 0x73, 0x52, 0x89, 0xd1, 0x73, + 0x52, 0x89, 0xd1, 0x73, 0x52, 0x89, 0xd1, 0x73, + 0x52, 0x89, 0xd1, 0x73, 0xaf, 0x5d, 0x0c, 0xf1, + 0xf4, 0xc8, 0x8d, 0xff, 0xfc, 0xda, 0xa8, 0xff, + 0xfc, 0xd0, 0x8f, 0xff, 0xfc, 0xd3, 0x95, 0xff, + 0xf7, 0xc7, 0x81, 0xff, 0xe1, 0x9b, 0x55, 0xff, + 0xd7, 0xcd, 0xbf, 0xff, 0xcc, 0xcc, 0xc6, 0xff, + 0xcd, 0xcb, 0xc2, 0xff, 0xd8, 0xae, 0x81, 0xff, + 0xd6, 0x96, 0x48, 0xff, 0xfb, 0xc6, 0x77, 0xff, + 0xfc, 0xb6, 0x4e, 0xff, 0xfc, 0xb0, 0x3f, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xae, 0x44, 0xff, + 0xfb, 0xb6, 0x5c, 0xff, 0xf8, 0xa8, 0x46, 0xff, + 0xf4, 0x90, 0x1e, 0xff, 0xf7, 0xa6, 0x4b, 0xff, + 0xdf, 0x92, 0x43, 0xff, 0x9c, 0x63, 0x31, 0xc7, + 0x53, 0x89, 0xd1, 0x74, 0x53, 0x89, 0xd1, 0x74, + 0x53, 0x89, 0xd1, 0x74, 0x53, 0x89, 0xd1, 0x74, + 0x53, 0x89, 0xd1, 0x74, 0x53, 0x89, 0xd1, 0x74, + 0x59, 0x8b, 0xd1, 0x62, 0x59, 0x8a, 0xcc, 0x63, + 0xb1, 0x5b, 0x06, 0xf6, 0xf5, 0xc7, 0x87, 0xff, + 0xfc, 0xd4, 0x99, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xd2, 0x94, 0xff, 0xf9, 0xcb, 0x8c, 0xff, + 0xee, 0xa7, 0x5d, 0xff, 0xeb, 0x9e, 0x52, 0xff, + 0xe6, 0xa3, 0x5f, 0xff, 0xed, 0x9f, 0x52, 0xff, + 0xf4, 0xb7, 0x6b, 0xff, 0xfb, 0xcb, 0x84, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfb, 0xb2, 0x46, 0xff, + 0xfc, 0xaf, 0x3d, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb0, 0x41, 0xff, 0xfb, 0xb9, 0x61, 0xff, + 0xfa, 0xb6, 0x65, 0xff, 0xf8, 0x9d, 0x38, 0xff, + 0xf5, 0x80, 0x09, 0xff, 0xf0, 0x77, 0x01, 0xff, + 0xf2, 0x8d, 0x2b, 0xff, 0xf5, 0xa4, 0x54, 0xff, + 0xf6, 0x94, 0x35, 0xff, 0xf8, 0xa4, 0x54, 0xff, + 0xbf, 0x72, 0x29, 0xfa, 0x6e, 0x7d, 0x98, 0x77, + 0x56, 0x8c, 0xd1, 0x63, 0x58, 0x8d, 0xd1, 0x63, + 0x8b, 0x6d, 0x5a, 0x98, 0xc5, 0x7c, 0x31, 0xf9, + 0xfd, 0xd8, 0xa2, 0xff, 0xfc, 0xd0, 0x8b, 0xff, + 0xfc, 0xcf, 0x8b, 0xff, 0xfc, 0xd7, 0x9e, 0xff, + 0xf4, 0xb9, 0x73, 0xff, 0xec, 0x9f, 0x52, 0xff, + 0xe7, 0xa3, 0x5f, 0xff, 0xec, 0x9e, 0x52, 0xff, + 0xef, 0xa9, 0x5d, 0xff, 0xfa, 0xc7, 0x81, 0xff, + 0xfb, 0xc3, 0x6f, 0xff, 0xfc, 0xb6, 0x4f, 0xff, + 0xfc, 0xaf, 0x41, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa4, 0x31, 0xff, 0xf9, 0x9a, 0x28, 0xff, + 0xf8, 0x9c, 0x37, 0xff, 0xf7, 0x94, 0x2f, 0xff, + 0xf4, 0x8a, 0x24, 0xff, 0xf3, 0x93, 0x38, 0xff, + 0xf3, 0x9d, 0x4c, 0xff, 0xf3, 0x97, 0x3e, 0xff, + 0xf3, 0x8d, 0x29, 0xff, 0xf5, 0x8e, 0x28, 0xff, + 0xe9, 0xa3, 0x60, 0xff, 0xaa, 0x5e, 0x19, 0xd6, + 0x57, 0x8c, 0xd2, 0x64, 0x57, 0x8c, 0xd2, 0x64, + 0x59, 0x8a, 0xcb, 0x66, 0xb1, 0x5c, 0x09, 0xf3, + 0xf7, 0xcd, 0x92, 0xff, 0xfd, 0xd4, 0x95, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xd2, 0x94, 0xff, + 0xf9, 0xcb, 0x8c, 0xff, 0xee, 0xa7, 0x5d, 0xff, + 0xeb, 0x9e, 0x52, 0xff, 0xe6, 0xa3, 0x5f, 0xff, + 0xed, 0x9f, 0x52, 0xff, 0xf4, 0xb7, 0x6b, 0xff, + 0xfb, 0xc9, 0x80, 0xff, 0xfd, 0xb6, 0x4f, 0xff, + 0xfc, 0xaf, 0x41, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa4, 0x31, 0xff, 0xf9, 0x9a, 0x28, 0xff, + 0xf8, 0x9d, 0x38, 0xff, 0xf7, 0x98, 0x37, 0xff, + 0xf4, 0x8a, 0x25, 0xff, 0xf3, 0x93, 0x38, 0xff, + 0xf3, 0x9d, 0x4c, 0xff, 0xf3, 0x97, 0x3e, 0xff, + 0xf4, 0x9a, 0x42, 0xff, 0xe9, 0xa3, 0x5e, 0xff, + 0xa9, 0x5f, 0x19, 0xd8, 0x57, 0x8c, 0xd0, 0x66, + 0x57, 0x8c, 0xd0, 0x66, 0x57, 0x8c, 0xd0, 0x66, + 0x57, 0x8c, 0xd0, 0x66, 0x57, 0x8c, 0xd0, 0x66, + 0x57, 0x8c, 0xd0, 0x66, 0xaf, 0x5f, 0x10, 0xe8, + 0xf1, 0xbf, 0x7c, 0xff, 0xfd, 0xcd, 0x89, 0xff, + 0xfc, 0xcb, 0x83, 0xff, 0xfc, 0xcf, 0x8c, 0xff, + 0xfa, 0xcd, 0x8d, 0xff, 0xf0, 0xa8, 0x5e, 0xff, + 0xeb, 0x9d, 0x50, 0xff, 0xe6, 0xa4, 0x60, 0xff, + 0xec, 0x9d, 0x51, 0xff, 0xf3, 0xb1, 0x62, 0xff, + 0xfb, 0xc5, 0x74, 0xff, 0xfc, 0xb1, 0x44, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xb2, 0x4d, 0xff, + 0xf5, 0xb4, 0x62, 0xff, 0xb6, 0x5e, 0x07, 0xff, + 0xfc, 0xc2, 0x6c, 0xff, 0xf9, 0xbc, 0x66, 0xff, + 0xb1, 0x59, 0x03, 0xfb, 0x57, 0x8b, 0xd1, 0x66, + 0x57, 0x8b, 0xd1, 0x66, 0x57, 0x8b, 0xd1, 0x66, + 0x57, 0x8b, 0xd1, 0x66, 0x57, 0x8b, 0xd1, 0x66, + 0x57, 0x8b, 0xd1, 0x66, 0x57, 0x8b, 0xd1, 0x66, + 0x57, 0x8b, 0xd1, 0x66, 0x57, 0x8a, 0xcc, 0x67, + 0xb1, 0x5c, 0x08, 0xf7, 0xf7, 0xce, 0x97, 0xff, + 0xfc, 0xcf, 0x8f, 0xff, 0xfc, 0xce, 0x88, 0xff, + 0xfc, 0xd2, 0x94, 0xff, 0xf9, 0xcb, 0x8c, 0xff, + 0xee, 0xa7, 0x5d, 0xff, 0xeb, 0x9e, 0x52, 0xff, + 0xe6, 0xa3, 0x5f, 0xff, 0xed, 0x9f, 0x52, 0xff, + 0xf4, 0xb7, 0x6b, 0xff, 0xfb, 0xcb, 0x84, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfb, 0xb2, 0x46, 0xff, + 0xfc, 0xaf, 0x3d, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb0, 0x41, 0xff, 0xfb, 0xb8, 0x5f, 0xff, + 0xfa, 0xb6, 0x65, 0xff, 0xf9, 0xac, 0x58, 0xff, + 0xf8, 0xa4, 0x4e, 0xff, 0xf7, 0x9a, 0x40, 0xff, + 0xf6, 0x90, 0x2d, 0xff, 0xf6, 0x86, 0x1a, 0xff, + 0xf5, 0x7a, 0x02, 0xff, 0xf5, 0x7f, 0x0c, 0xff, + 0xf1, 0xac, 0x69, 0xff, 0xaf, 0x5c, 0x0b, 0xed, + 0x56, 0x8a, 0xd1, 0x68, 0x56, 0x8a, 0xd1, 0x68, + 0x89, 0x6d, 0x5c, 0x9b, 0xc5, 0x7c, 0x32, 0xf9, + 0xfd, 0xd8, 0xa2, 0xff, 0xfc, 0xd0, 0x8b, 0xff, + 0xfc, 0xcf, 0x8b, 0xff, 0xfc, 0xd7, 0x9e, 0xff, + 0xf4, 0xb9, 0x73, 0xff, 0xec, 0x9f, 0x52, 0xff, + 0xe7, 0xa3, 0x5f, 0xff, 0xec, 0x9e, 0x52, 0xff, + 0xef, 0xa9, 0x5d, 0xff, 0xfa, 0xc7, 0x81, 0xff, + 0xfb, 0xc3, 0x6f, 0xff, 0xfc, 0xb6, 0x4f, 0xff, + 0xfc, 0xaf, 0x41, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa4, 0x31, 0xff, 0xf9, 0x9a, 0x28, 0xff, + 0xf8, 0x9c, 0x37, 0xff, 0xf7, 0x8f, 0x25, 0xff, + 0xf3, 0x78, 0x00, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf0, 0x76, 0x00, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf2, 0x77, 0x00, 0xff, 0xf5, 0x84, 0x15, 0xff, + 0xe9, 0xa3, 0x60, 0xff, 0xa9, 0x5e, 0x1a, 0xd8, + 0x57, 0x8b, 0xd2, 0x69, 0x57, 0x8b, 0xd2, 0x69, + 0x56, 0x8a, 0xd0, 0x69, 0xb0, 0x5e, 0x0e, 0xed, + 0xf3, 0xc7, 0x89, 0xff, 0xfc, 0xd3, 0x97, 0xff, + 0xfc, 0xce, 0x88, 0xff, 0xfc, 0xd2, 0x92, 0xff, + 0xfa, 0xcf, 0x90, 0xff, 0xef, 0xa9, 0x5f, 0xff, + 0xeb, 0x9e, 0x50, 0xff, 0xe5, 0xa4, 0x60, 0xff, + 0xec, 0x9e, 0x52, 0xff, 0xf3, 0xb5, 0x69, 0xff, + 0xfb, 0xca, 0x81, 0xff, 0xfc, 0xb7, 0x52, 0xff, + 0xfc, 0xaf, 0x40, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xa4, 0x32, 0xff, 0xf9, 0x9a, 0x28, 0xff, + 0xf8, 0x9d, 0x38, 0xff, 0xf7, 0x94, 0x30, 0xff, + 0xf4, 0x79, 0x02, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf0, 0x76, 0x00, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf2, 0x83, 0x16, 0xff, 0xed, 0xa4, 0x5e, 0xff, + 0xad, 0x5e, 0x12, 0xe3, 0x56, 0x8b, 0xd1, 0x6a, + 0x56, 0x8b, 0xd1, 0x6a, 0x56, 0x8b, 0xd1, 0x6a, + 0x56, 0x8b, 0xd1, 0x6a, 0x56, 0x8b, 0xd1, 0x6a, + 0x56, 0x8b, 0xd1, 0x6a, 0xaf, 0x5f, 0x11, 0xe9, + 0xf1, 0xbe, 0x7a, 0xff, 0xfd, 0xce, 0x8b, 0xff, + 0xfc, 0xcb, 0x83, 0xff, 0xfc, 0xca, 0x82, 0xff, + 0xfc, 0xd3, 0x95, 0xff, 0xf3, 0xb5, 0x6b, 0xff, + 0xeb, 0x9f, 0x52, 0xff, 0xe6, 0xa4, 0x60, 0xff, + 0xeb, 0x9d, 0x51, 0xff, 0xf0, 0xa9, 0x59, 0xff, + 0xfa, 0xc0, 0x6e, 0xff, 0xfc, 0xb7, 0x53, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xb3, 0x50, 0xff, + 0xfb, 0xbe, 0x6f, 0xff, 0xfa, 0xae, 0x53, 0xff, + 0xf4, 0x93, 0x24, 0xff, 0xf7, 0xa2, 0x44, 0xff, + 0xe3, 0x99, 0x4d, 0xff, 0xa5, 0x60, 0x21, 0xd1, + 0x55, 0x8a, 0xd1, 0x6b, 0x54, 0x8b, 0xd1, 0x6b, + 0x54, 0x8b, 0xd1, 0x6b, 0x54, 0x8b, 0xd1, 0x6b, + 0x54, 0x8b, 0xd1, 0x6b, 0x54, 0x8b, 0xd1, 0x6b, + 0x59, 0x8e, 0xd0, 0x59, 0x59, 0x8e, 0xd0, 0x59, + 0x9e, 0x64, 0x30, 0xb3, 0xd2, 0x8a, 0x3a, 0xfb, + 0xfd, 0xcb, 0x83, 0xff, 0xfc, 0xbf, 0x65, 0xff, + 0xfc, 0xc6, 0x77, 0xff, 0xfc, 0xcd, 0x89, 0xff, + 0xfc, 0xd2, 0x95, 0xff, 0xfa, 0xc9, 0x84, 0xff, + 0xfa, 0xc4, 0x7c, 0xff, 0xfc, 0xcb, 0x85, 0xff, + 0xfb, 0xc8, 0x7a, 0xff, 0xfb, 0xb4, 0x4b, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xb2, 0x46, 0xff, + 0xfc, 0xca, 0x82, 0xff, 0xe0, 0xa3, 0x5f, 0xfd, + 0xd5, 0x8c, 0x3e, 0xfb, 0xf2, 0xaf, 0x66, 0xff, + 0xf9, 0xb3, 0x6c, 0xff, 0xf7, 0x96, 0x39, 0xff, + 0xf2, 0x90, 0x33, 0xff, 0xf2, 0xa1, 0x53, 0xff, + 0xf5, 0x93, 0x36, 0xff, 0xf4, 0xaf, 0x6b, 0xff, + 0xb5, 0x63, 0x12, 0xf7, 0x5a, 0x8e, 0xd1, 0x5a, + 0x5a, 0x8e, 0xd1, 0x5a, 0x5a, 0x8e, 0xd1, 0x5a, + 0x5c, 0x8c, 0xca, 0x5c, 0xb1, 0x5d, 0x0a, 0xef, + 0xf0, 0xbc, 0x79, 0xff, 0xfc, 0xbe, 0x63, 0xff, + 0xfd, 0xc2, 0x6b, 0xff, 0xfd, 0xc8, 0x7e, 0xff, + 0xfc, 0xd3, 0x96, 0xff, 0xfc, 0xcf, 0x8e, 0xff, + 0xfa, 0xc5, 0x7e, 0xff, 0xfb, 0xc8, 0x7f, 0xff, + 0xfc, 0xcb, 0x82, 0xff, 0xfb, 0xbc, 0x60, 0xff, + 0xfc, 0xae, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xae, 0x3d, 0xff, + 0xfa, 0xae, 0x49, 0xff, 0xfa, 0xc0, 0x7a, 0xff, + 0xe8, 0xa6, 0x61, 0xff, 0xee, 0xac, 0x6a, 0xff, + 0xf9, 0xb9, 0x7b, 0xff, 0xf5, 0xa8, 0x5f, 0xff, + 0xf3, 0xa0, 0x51, 0xff, 0xf4, 0x97, 0x3d, 0xff, + 0xf5, 0x8d, 0x28, 0xff, 0xf5, 0x8f, 0x2b, 0xff, + 0xdd, 0x9d, 0x5d, 0xfc, 0x98, 0x65, 0x39, 0xa9, + 0x59, 0x8d, 0xcf, 0x5b, 0x59, 0x8d, 0xcf, 0x5b, + 0x58, 0x8e, 0xd0, 0x5c, 0x99, 0x67, 0x39, 0xad, + 0xd0, 0x8a, 0x3c, 0xfa, 0xfd, 0xc9, 0x81, 0xff, + 0xfc, 0xbc, 0x5e, 0xff, 0xfd, 0xc2, 0x6d, 0xff, + 0xfc, 0xcd, 0x89, 0xff, 0xfc, 0xd2, 0x95, 0xff, + 0xfa, 0xc9, 0x84, 0xff, 0xfa, 0xc2, 0x7a, 0xff, + 0xfb, 0xc7, 0x7b, 0xff, 0xfb, 0xc4, 0x70, 0xff, + 0xfc, 0xb2, 0x46, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xae, 0x3d, 0xff, + 0xfa, 0xac, 0x45, 0xff, 0xfa, 0xc0, 0x79, 0xff, + 0xe9, 0xa6, 0x5f, 0xff, 0xee, 0xab, 0x68, 0xff, + 0xf9, 0xbb, 0x7f, 0xff, 0xf5, 0xaa, 0x61, 0xff, + 0xf3, 0xa0, 0x51, 0xff, 0xf4, 0x97, 0x3d, 0xff, + 0xf6, 0x9b, 0x43, 0xff, 0xe3, 0x9f, 0x5d, 0xfe, + 0x9d, 0x65, 0x32, 0xb3, 0x5a, 0x8d, 0xd0, 0x5d, + 0x5a, 0x8d, 0xd0, 0x5d, 0x5a, 0x8d, 0xd0, 0x5d, + 0x5a, 0x8d, 0xd0, 0x5d, 0x5a, 0x8d, 0xd0, 0x5d, + 0x5a, 0x8d, 0xd0, 0x5d, 0x95, 0x68, 0x42, 0xa3, + 0xcc, 0x84, 0x33, 0xfb, 0xfa, 0xc6, 0x78, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfc, 0xc3, 0x6e, 0xff, + 0xfc, 0xcb, 0x82, 0xff, 0xfc, 0xd1, 0x91, 0xff, + 0xfb, 0xc9, 0x80, 0xff, 0xfa, 0xc2, 0x76, 0xff, + 0xfb, 0xc5, 0x73, 0xff, 0xfc, 0xc3, 0x71, 0xff, + 0xfc, 0xb4, 0x4a, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xbd, 0x61, 0xff, 0xf7, 0xc4, 0x82, 0xff, + 0xc7, 0x7c, 0x2e, 0xfb, 0xc4, 0x74, 0x1f, 0xff, + 0xfc, 0xc5, 0x73, 0xff, 0xf4, 0xb8, 0x66, 0xff, + 0xb1, 0x5d, 0x09, 0xf1, 0x59, 0x8e, 0xd0, 0x5d, + 0x59, 0x8e, 0xd0, 0x5d, 0x59, 0x8e, 0xd0, 0x5d, + 0x58, 0x8d, 0xd1, 0x5e, 0x58, 0x8d, 0xd1, 0x5e, + 0x58, 0x8d, 0xd1, 0x5e, 0x58, 0x8d, 0xd1, 0x5e, + 0x58, 0x8d, 0xd1, 0x5e, 0x58, 0x8d, 0xd1, 0x5e, + 0x9d, 0x65, 0x33, 0xb5, 0xd8, 0x98, 0x51, 0xfc, + 0xfc, 0xc3, 0x70, 0xff, 0xfc, 0xbf, 0x65, 0xff, + 0xfc, 0xc6, 0x77, 0xff, 0xfc, 0xcd, 0x89, 0xff, + 0xfc, 0xd2, 0x95, 0xff, 0xfa, 0xc9, 0x84, 0xff, + 0xfa, 0xc4, 0x7c, 0xff, 0xfc, 0xcb, 0x85, 0xff, + 0xfb, 0xc8, 0x7a, 0xff, 0xfb, 0xb4, 0x4b, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xb2, 0x46, 0xff, + 0xfc, 0xca, 0x82, 0xff, 0xe0, 0xa3, 0x5e, 0xfc, + 0xc8, 0x7f, 0x35, 0xf7, 0xd0, 0x83, 0x35, 0xf8, + 0xd5, 0x83, 0x32, 0xfc, 0xdb, 0x86, 0x34, 0xfe, + 0xe3, 0x95, 0x49, 0xff, 0xeb, 0xa5, 0x61, 0xff, + 0xf8, 0xab, 0x60, 0xff, 0xf5, 0x84, 0x16, 0xff, + 0xf2, 0xb0, 0x70, 0xff, 0xb0, 0x5b, 0x0a, 0xec, + 0x58, 0x8d, 0xd1, 0x5f, 0x58, 0x8d, 0xd1, 0x5f, + 0x5a, 0x8b, 0xca, 0x61, 0xb1, 0x5d, 0x0a, 0xf0, + 0xf0, 0xbc, 0x79, 0xff, 0xfc, 0xbe, 0x63, 0xff, + 0xfd, 0xc2, 0x6b, 0xff, 0xfd, 0xc8, 0x7e, 0xff, + 0xfc, 0xd3, 0x96, 0xff, 0xfc, 0xcf, 0x8e, 0xff, + 0xfa, 0xc5, 0x7e, 0xff, 0xfb, 0xc8, 0x7f, 0xff, + 0xfc, 0xcb, 0x82, 0xff, 0xfb, 0xbc, 0x60, 0xff, + 0xfc, 0xae, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfb, 0xae, 0x3d, 0xff, + 0xfa, 0xae, 0x49, 0xff, 0xfa, 0xc0, 0x7a, 0xff, + 0xe8, 0xa6, 0x61, 0xff, 0xee, 0xa8, 0x62, 0xff, + 0xf8, 0xad, 0x64, 0xff, 0xf4, 0x91, 0x32, 0xff, + 0xf0, 0x79, 0x06, 0xff, 0xf2, 0x77, 0x00, 0xff, + 0xf4, 0x78, 0x00, 0xff, 0xf5, 0x85, 0x18, 0xff, + 0xdd, 0x9d, 0x5d, 0xfc, 0x97, 0x65, 0x3d, 0xac, + 0x58, 0x8d, 0xd2, 0x60, 0x58, 0x8d, 0xd2, 0x60, + 0x58, 0x8d, 0xd2, 0x60, 0x92, 0x6b, 0x47, 0xa3, + 0xcb, 0x81, 0x33, 0xf9, 0xfd, 0xcc, 0x85, 0xff, + 0xfc, 0xbb, 0x5c, 0xff, 0xfc, 0xc1, 0x6d, 0xff, + 0xfc, 0xcd, 0x88, 0xff, 0xfc, 0xd2, 0x95, 0xff, + 0xfb, 0xca, 0x85, 0xff, 0xfa, 0xc3, 0x7a, 0xff, + 0xfb, 0xc7, 0x7a, 0xff, 0xfb, 0xc4, 0x73, 0xff, + 0xfc, 0xb2, 0x49, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfa, 0xab, 0x43, 0xff, 0xfb, 0xbf, 0x77, 0xff, + 0xeb, 0xa8, 0x62, 0xff, 0xec, 0xa5, 0x5c, 0xff, + 0xf9, 0xb0, 0x6a, 0xff, 0xf5, 0x94, 0x38, 0xff, + 0xf0, 0x7a, 0x08, 0xff, 0xf2, 0x77, 0x00, 0xff, + 0xf4, 0x84, 0x19, 0xff, 0xe6, 0xa0, 0x5c, 0xff, + 0xa2, 0x63, 0x27, 0xc2, 0x58, 0x8c, 0xd0, 0x61, + 0x58, 0x8c, 0xd0, 0x61, 0x58, 0x8c, 0xd0, 0x61, + 0x58, 0x8c, 0xd0, 0x61, 0x59, 0x8b, 0xd1, 0x62, + 0x59, 0x8b, 0xd1, 0x62, 0x94, 0x68, 0x45, 0xa6, + 0xcc, 0x83, 0x32, 0xfb, 0xfc, 0xc7, 0x7d, 0xff, + 0xfc, 0xba, 0x59, 0xff, 0xfc, 0xc3, 0x6e, 0xff, + 0xfc, 0xc6, 0x77, 0xff, 0xfc, 0xd0, 0x8f, 0xff, + 0xfb, 0xcc, 0x85, 0xff, 0xfa, 0xc1, 0x76, 0xff, + 0xfb, 0xc1, 0x6f, 0xff, 0xfc, 0xc4, 0x74, 0xff, + 0xfc, 0xba, 0x5a, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xbf, 0x66, 0xff, 0xf7, 0xc4, 0x80, 0xff, + 0xce, 0x85, 0x38, 0xfb, 0xe3, 0xa1, 0x58, 0xfe, + 0xf7, 0xbe, 0x7c, 0xff, 0xf7, 0xb3, 0x69, 0xff, + 0xd2, 0x82, 0x34, 0xfb, 0x92, 0x68, 0x46, 0xa7, + 0x56, 0x8c, 0xd1, 0x63, 0x56, 0x8c, 0xd1, 0x63, + 0x56, 0x8c, 0xd1, 0x63, 0x56, 0x8c, 0xd1, 0x63, + 0x56, 0x8c, 0xd1, 0x63, 0x56, 0x8c, 0xd1, 0x63, + 0x5b, 0x91, 0xd0, 0x51, 0x5b, 0x91, 0xd0, 0x51, + 0x71, 0x80, 0x95, 0x64, 0xb2, 0x5a, 0x05, 0xfa, + 0xce, 0x86, 0x39, 0xff, 0xf8, 0xb1, 0x4e, 0xff, + 0xfc, 0xb1, 0x43, 0xff, 0xfc, 0xb5, 0x4a, 0xff, + 0xfc, 0xb9, 0x55, 0xff, 0xf9, 0xb8, 0x5c, 0xff, + 0xec, 0xa9, 0x4f, 0xff, 0xed, 0xa3, 0x40, 0xff, + 0xf9, 0xab, 0x3b, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xf5, 0xa6, 0x38, 0xff, 0xf8, 0xaa, 0x3a, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb5, 0x4e, 0xff, 0xfa, 0xcb, 0x89, 0xff, + 0xd1, 0x8f, 0x47, 0xfa, 0xae, 0x5f, 0x11, 0xde, + 0xa1, 0x64, 0x2a, 0xb2, 0xb1, 0x59, 0x04, 0xf6, + 0xc4, 0x6e, 0x1b, 0xf7, 0xe9, 0x97, 0x48, 0xff, + 0xf8, 0xbe, 0x85, 0xff, 0xf9, 0xc5, 0x93, 0xff, + 0xf7, 0xae, 0x69, 0xff, 0xf3, 0xb2, 0x73, 0xff, + 0xb5, 0x64, 0x15, 0xf0, 0x5d, 0x90, 0xd1, 0x52, + 0x5d, 0x90, 0xd1, 0x52, 0x5d, 0x90, 0xd1, 0x52, + 0x5d, 0x90, 0xd1, 0x52, 0x97, 0x6a, 0x41, 0x9a, + 0xc1, 0x73, 0x22, 0xfb, 0xe2, 0xa4, 0x58, 0xff, + 0xfc, 0xb1, 0x43, 0xff, 0xfc, 0xae, 0x3e, 0xff, + 0xfc, 0xb1, 0x45, 0xff, 0xfb, 0xb7, 0x53, 0xff, + 0xfc, 0xb9, 0x5a, 0xff, 0xf9, 0xb3, 0x4f, 0xff, + 0xf1, 0xa4, 0x3b, 0xff, 0xf8, 0xaa, 0x3a, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xf5, 0xa6, 0x38, 0xff, + 0xf8, 0xaa, 0x3a, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfb, 0xb0, 0x43, 0xff, 0xfa, 0xc3, 0x74, 0xff, + 0xf3, 0xbf, 0x7e, 0xff, 0xc5, 0x7a, 0x2d, 0xf9, + 0xaf, 0x5c, 0x0d, 0xe3, 0xb0, 0x5a, 0x08, 0xeb, + 0xc0, 0x70, 0x23, 0xf8, 0xe7, 0xa5, 0x64, 0xfe, + 0xf8, 0xc6, 0x96, 0xff, 0xf4, 0xb4, 0x75, 0xff, + 0xf5, 0xa0, 0x4e, 0xff, 0xf6, 0x97, 0x3b, 0xff, + 0xe0, 0x9a, 0x58, 0xfe, 0x9e, 0x64, 0x2f, 0xad, + 0x5b, 0x8f, 0xcf, 0x53, 0x5b, 0x8f, 0xcf, 0x53, + 0x5b, 0x8f, 0xcf, 0x53, 0x6d, 0x83, 0xa0, 0x62, + 0xb2, 0x59, 0x05, 0xfb, 0xcc, 0x85, 0x37, 0xff, + 0xf6, 0xb1, 0x4d, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb0, 0x3f, 0xff, 0xfc, 0xb4, 0x4a, 0xff, + 0xfc, 0xb7, 0x54, 0xff, 0xf8, 0xb4, 0x53, 0xff, + 0xee, 0xa3, 0x3f, 0xff, 0xf6, 0xa8, 0x39, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xf5, 0xa6, 0x38, 0xff, + 0xf8, 0xaa, 0x3a, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfb, 0xae, 0x3f, 0xff, 0xf9, 0xbe, 0x6a, 0xff, + 0xf6, 0xc3, 0x82, 0xff, 0xca, 0x7e, 0x2d, 0xf9, + 0xb0, 0x5c, 0x0c, 0xe5, 0xb0, 0x5b, 0x07, 0xed, + 0xc3, 0x73, 0x27, 0xf8, 0xeb, 0xab, 0x6d, 0xff, + 0xf8, 0xc9, 0x9a, 0xff, 0xf4, 0xaf, 0x6c, 0xff, + 0xf5, 0xa2, 0x51, 0xff, 0xde, 0x9b, 0x5b, 0xfd, + 0x98, 0x66, 0x3c, 0xa0, 0x5a, 0x8e, 0xd0, 0x54, + 0x5a, 0x8e, 0xd0, 0x54, 0x5a, 0x8e, 0xd0, 0x54, + 0x5a, 0x8e, 0xd0, 0x54, 0x5c, 0x8f, 0xd0, 0x54, + 0x5c, 0x8f, 0xd0, 0x54, 0x6e, 0x83, 0xa3, 0x62, + 0xb2, 0x5b, 0x07, 0xfa, 0xc4, 0x7a, 0x2c, 0xff, + 0xf3, 0xa8, 0x41, 0xff, 0xfc, 0xaf, 0x3d, 0xff, + 0xfc, 0xb0, 0x43, 0xff, 0xfb, 0xb4, 0x4c, 0xff, + 0xfc, 0xb8, 0x54, 0xff, 0xf6, 0xb3, 0x53, 0xff, + 0xec, 0xa2, 0x40, 0xff, 0xf6, 0xa7, 0x39, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xf2, 0xa3, 0x35, 0xff, 0xed, 0x9e, 0x32, 0xff, + 0xf9, 0xac, 0x3d, 0xff, 0xf8, 0xac, 0x3d, 0xff, + 0xf7, 0xab, 0x3c, 0xff, 0xf6, 0xbd, 0x69, 0xff, + 0xec, 0xba, 0x75, 0xff, 0xb7, 0x64, 0x11, 0xf9, + 0xa7, 0x5d, 0x18, 0xcf, 0xd4, 0x8c, 0x39, 0xfc, + 0xfc, 0xc5, 0x73, 0xff, 0xea, 0xa9, 0x55, 0xff, + 0xaf, 0x60, 0x13, 0xdd, 0x5b, 0x8e, 0xd0, 0x55, + 0x5b, 0x8e, 0xd0, 0x55, 0x5b, 0x8e, 0xd0, 0x55, + 0x5b, 0x8e, 0xd0, 0x55, 0x5b, 0x8e, 0xd0, 0x55, + 0x5b, 0x8e, 0xd0, 0x55, 0x5b, 0x8e, 0xd0, 0x55, + 0x5b, 0x8e, 0xd0, 0x55, 0x5b, 0x8e, 0xd0, 0x55, + 0x71, 0x80, 0x97, 0x68, 0xb6, 0x63, 0x12, 0xfa, + 0xce, 0x86, 0x3a, 0xff, 0xf8, 0xac, 0x40, 0xff, + 0xfc, 0xb1, 0x43, 0xff, 0xfc, 0xb5, 0x4a, 0xff, + 0xfc, 0xb9, 0x55, 0xff, 0xf9, 0xb8, 0x5c, 0xff, + 0xec, 0xa9, 0x4f, 0xff, 0xed, 0xa3, 0x40, 0xff, + 0xf9, 0xab, 0x3b, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xf5, 0xa6, 0x38, 0xff, 0xf8, 0xaa, 0x3a, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xb5, 0x4e, 0xff, 0xfa, 0xcb, 0x89, 0xff, + 0xd0, 0x8c, 0x43, 0xf9, 0xad, 0x5f, 0x12, 0xdd, + 0x89, 0x70, 0x5f, 0x86, 0x8f, 0x6c, 0x4f, 0x92, + 0x96, 0x68, 0x3f, 0xa0, 0x9d, 0x63, 0x31, 0xae, + 0xa6, 0x62, 0x22, 0xc0, 0xb0, 0x5b, 0x09, 0xea, + 0xd5, 0x85, 0x36, 0xfb, 0xf8, 0xac, 0x62, 0xff, + 0xf0, 0xae, 0x6d, 0xff, 0xb1, 0x5b, 0x0b, 0xe7, + 0x5c, 0x8e, 0xcf, 0x57, 0x5c, 0x8e, 0xcf, 0x57, + 0x5c, 0x8e, 0xcf, 0x57, 0x96, 0x6a, 0x43, 0x9d, + 0xc1, 0x73, 0x22, 0xfb, 0xe2, 0xa4, 0x58, 0xff, + 0xfc, 0xb1, 0x43, 0xff, 0xfc, 0xae, 0x3e, 0xff, + 0xfc, 0xb1, 0x45, 0xff, 0xfb, 0xb7, 0x53, 0xff, + 0xfc, 0xb9, 0x5a, 0xff, 0xf9, 0xb3, 0x4f, 0xff, + 0xf1, 0xa4, 0x3b, 0xff, 0xf8, 0xaa, 0x3a, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xf5, 0xa6, 0x38, 0xff, + 0xf8, 0xaa, 0x3a, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfb, 0xb0, 0x43, 0xff, 0xfa, 0xc3, 0x74, 0xff, + 0xf3, 0xbf, 0x7e, 0xff, 0xc5, 0x7a, 0x2d, 0xf9, + 0xaf, 0x5c, 0x0d, 0xe4, 0xb0, 0x5a, 0x09, 0xeb, + 0xbf, 0x6d, 0x1e, 0xf8, 0xe4, 0x98, 0x4d, 0xfe, + 0xf6, 0xb0, 0x6c, 0xff, 0xf3, 0x9d, 0x4c, 0xff, + 0xf4, 0x8f, 0x2d, 0xff, 0xf6, 0x8f, 0x2b, 0xff, + 0xe0, 0x9a, 0x58, 0xfe, 0x9e, 0x64, 0x30, 0xaf, + 0x5b, 0x8f, 0xcf, 0x57, 0x5b, 0x8f, 0xcf, 0x57, + 0x5b, 0x8f, 0xcf, 0x57, 0x66, 0x86, 0xb2, 0x60, + 0xb1, 0x5a, 0x04, 0xfa, 0xcb, 0x80, 0x32, 0xff, + 0xf5, 0xb1, 0x51, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3f, 0xff, 0xfc, 0xb3, 0x49, 0xff, + 0xfc, 0xb7, 0x52, 0xff, 0xf8, 0xb4, 0x54, 0xff, + 0xee, 0xa4, 0x40, 0xff, 0xf5, 0xa7, 0x39, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xf6, 0xa7, 0x38, 0xff, + 0xf7, 0xa9, 0x39, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xfb, 0xae, 0x3d, 0xff, 0xf9, 0xbe, 0x67, 0xff, + 0xf7, 0xc5, 0x83, 0xff, 0xcf, 0x82, 0x33, 0xf9, + 0xb1, 0x5d, 0x0b, 0xe9, 0xb1, 0x5c, 0x0a, 0xe9, + 0xbe, 0x6b, 0x1a, 0xf8, 0xe4, 0x97, 0x4b, 0xff, + 0xf7, 0xb1, 0x6e, 0xff, 0xf2, 0x99, 0x43, 0xff, + 0xf4, 0x8e, 0x2c, 0xff, 0xe2, 0x9d, 0x59, 0xfe, + 0x9d, 0x65, 0x33, 0xaf, 0x59, 0x8e, 0xd0, 0x59, + 0x59, 0x8e, 0xd0, 0x59, 0x59, 0x8e, 0xd0, 0x59, + 0x59, 0x8e, 0xd0, 0x59, 0x59, 0x8e, 0xd0, 0x59, + 0x59, 0x8e, 0xd0, 0x59, 0x6b, 0x82, 0xa5, 0x67, + 0xb1, 0x5b, 0x06, 0xfb, 0xc5, 0x7b, 0x2d, 0xff, + 0xf4, 0xac, 0x45, 0xff, 0xfc, 0xaf, 0x3d, 0xff, + 0xfc, 0xb0, 0x43, 0xff, 0xfb, 0xb2, 0x48, 0xff, + 0xfb, 0xb5, 0x4e, 0xff, 0xf2, 0xad, 0x4f, 0xff, + 0xef, 0xa9, 0x48, 0xff, 0xfa, 0xaf, 0x42, 0xff, + 0xfc, 0xaf, 0x3e, 0xff, 0xfc, 0xaf, 0x3e, 0xff, + 0xf2, 0xa3, 0x35, 0xff, 0xed, 0x9e, 0x32, 0xff, + 0xf9, 0xac, 0x3d, 0xff, 0xf8, 0xac, 0x3d, 0xff, + 0xf7, 0xab, 0x3d, 0xff, 0xf6, 0xc0, 0x70, 0xff, + 0xed, 0xba, 0x76, 0xff, 0xb6, 0x62, 0x0e, 0xf9, + 0x9d, 0x65, 0x31, 0xb2, 0xac, 0x5e, 0x13, 0xdc, + 0xdc, 0x95, 0x49, 0xfd, 0xf9, 0xbf, 0x7f, 0xff, + 0xbb, 0x67, 0x16, 0xf8, 0x7e, 0x76, 0x7b, 0x7a, + 0x5b, 0x8d, 0xd1, 0x5a, 0x5b, 0x8d, 0xd1, 0x5a, + 0x5b, 0x8d, 0xd1, 0x5a, 0x5b, 0x8d, 0xd1, 0x5a, + 0x5b, 0x8d, 0xd1, 0x5a, 0x5b, 0x8d, 0xd1, 0x5a, + 0x5f, 0x91, 0xd0, 0x48, 0x5f, 0x91, 0xd0, 0x48, + 0x5f, 0x91, 0xd0, 0x48, 0xaa, 0x63, 0x1c, 0xc2, + 0xcd, 0x8c, 0x48, 0xfd, 0xb9, 0x6f, 0x22, 0xff, + 0xbd, 0x6a, 0x12, 0xff, 0xbf, 0x6c, 0x11, 0xff, + 0xc1, 0x74, 0x20, 0xff, 0xd2, 0x90, 0x40, 0xff, + 0xe4, 0x9a, 0x34, 0xff, 0xf0, 0xa6, 0x3b, 0xff, + 0xef, 0xa6, 0x3a, 0xff, 0xee, 0xa5, 0x3a, 0xff, + 0xed, 0xa4, 0x3a, 0xff, 0xd4, 0x84, 0x22, 0xff, + 0xc3, 0x6f, 0x12, 0xff, 0xe3, 0x99, 0x32, 0xff, + 0xe9, 0xa1, 0x39, 0xff, 0xe9, 0xac, 0x53, 0xff, + 0xe3, 0xba, 0x7f, 0xff, 0xbf, 0x77, 0x2d, 0xf8, + 0xa9, 0x62, 0x1d, 0xc1, 0x63, 0x8d, 0xbf, 0x4d, + 0x5e, 0x92, 0xd0, 0x49, 0x65, 0x8b, 0xbd, 0x4e, + 0x96, 0x6b, 0x45, 0x8b, 0xb1, 0x5b, 0x0c, 0xdf, + 0xb3, 0x5b, 0x06, 0xfa, 0xca, 0x7a, 0x2e, 0xf7, + 0xdf, 0x90, 0x44, 0xfe, 0xe7, 0x99, 0x4d, 0xff, + 0xb7, 0x62, 0x11, 0xfc, 0x7d, 0x7b, 0x83, 0x63, + 0x5d, 0x90, 0xd1, 0x49, 0x5d, 0x90, 0xd1, 0x49, + 0x5d, 0x90, 0xd1, 0x49, 0x66, 0x8b, 0xbb, 0x4f, + 0xaf, 0x5d, 0x0e, 0xe2, 0xc5, 0x82, 0x3e, 0xfe, + 0xbe, 0x70, 0x20, 0xff, 0xc7, 0x76, 0x19, 0xff, + 0xc8, 0x77, 0x19, 0xff, 0xc6, 0x76, 0x1e, 0xff, + 0xd9, 0x99, 0x4d, 0xff, 0xe1, 0x9b, 0x3d, 0xff, + 0xef, 0xa4, 0x3a, 0xff, 0xef, 0xa6, 0x3a, 0xff, + 0xee, 0xa5, 0x3a, 0xff, 0xed, 0xa4, 0x3a, 0xff, + 0xd4, 0x84, 0x22, 0xff, 0xbf, 0x6b, 0x0f, 0xff, + 0xd2, 0x83, 0x21, 0xff, 0xd9, 0x8b, 0x28, 0xff, + 0xdc, 0x91, 0x2c, 0xff, 0xe0, 0xa0, 0x49, 0xff, + 0xe5, 0xb7, 0x79, 0xff, 0xd7, 0x9c, 0x55, 0xfc, + 0xb4, 0x5d, 0x0a, 0xf8, 0x9b, 0x67, 0x3a, 0x99, + 0x5e, 0x90, 0xcb, 0x4b, 0x5e, 0x91, 0xd1, 0x4a, + 0x8c, 0x71, 0x5d, 0x7a, 0xac, 0x5e, 0x13, 0xd0, + 0xb5, 0x60, 0x0e, 0xf9, 0xcf, 0x82, 0x39, 0xf8, + 0xe1, 0x99, 0x53, 0xff, 0xef, 0xab, 0x6a, 0xff, + 0xe4, 0x99, 0x50, 0xff, 0xac, 0x5f, 0x14, 0xcd, + 0x5e, 0x91, 0xd1, 0x4a, 0x5e, 0x91, 0xd1, 0x4a, + 0x5e, 0x91, 0xd1, 0x4a, 0x5e, 0x91, 0xd1, 0x4a, + 0xa4, 0x64, 0x28, 0xb2, 0xc9, 0x89, 0x49, 0xfc, + 0xbc, 0x74, 0x29, 0xff, 0xbd, 0x6b, 0x12, 0xff, + 0xc0, 0x6d, 0x12, 0xff, 0xc5, 0x78, 0x22, 0xff, + 0xd5, 0x98, 0x4e, 0xff, 0xdc, 0x95, 0x39, 0xff, + 0xed, 0xa2, 0x38, 0xff, 0xef, 0xa6, 0x3a, 0xff, + 0xee, 0xa5, 0x3a, 0xff, 0xed, 0xa4, 0x3a, 0xff, + 0xd4, 0x84, 0x22, 0xff, 0xc0, 0x6c, 0x10, 0xff, + 0xda, 0x8c, 0x29, 0xff, 0xe6, 0x9d, 0x35, 0xff, + 0xe9, 0xa1, 0x39, 0xff, 0xe8, 0xa6, 0x46, 0xff, + 0xe9, 0xbc, 0x7b, 0xff, 0xe4, 0xae, 0x68, 0xff, + 0xb7, 0x61, 0x0f, 0xf8, 0xa1, 0x65, 0x2e, 0xa7, + 0x60, 0x8d, 0xc6, 0x4d, 0x5f, 0x8e, 0xcc, 0x4c, + 0x8f, 0x6e, 0x54, 0x81, 0xae, 0x5c, 0x0e, 0xda, + 0xbc, 0x6d, 0x22, 0xf9, 0xe2, 0x9d, 0x5a, 0xfd, + 0xf3, 0xb6, 0x7b, 0xff, 0xe5, 0x9a, 0x52, 0xff, + 0xab, 0x60, 0x16, 0xca, 0x5d, 0x91, 0xd0, 0x4c, + 0x5d, 0x91, 0xd0, 0x4c, 0x5d, 0x91, 0xd0, 0x4c, + 0x5d, 0x91, 0xd0, 0x4c, 0x5d, 0x91, 0xd0, 0x4c, + 0x5d, 0x91, 0xd0, 0x4c, 0x5d, 0x91, 0xd0, 0x4c, + 0xa9, 0x63, 0x1f, 0xc1, 0xd3, 0x9e, 0x66, 0xfd, + 0xb9, 0x71, 0x28, 0xff, 0xb4, 0x60, 0x09, 0xff, + 0xba, 0x67, 0x0f, 0xff, 0xbc, 0x6d, 0x16, 0xff, + 0xd1, 0x92, 0x4a, 0xff, 0xdc, 0x9a, 0x48, 0xff, + 0xea, 0xa1, 0x3a, 0xff, 0xec, 0xa4, 0x3a, 0xff, + 0xeb, 0xa3, 0x39, 0xff, 0xda, 0x8d, 0x29, 0xff, + 0xbd, 0x68, 0x0d, 0xff, 0xe4, 0x9c, 0x35, 0xff, + 0xe7, 0xa0, 0x38, 0xff, 0xe6, 0xa0, 0x3b, 0xff, + 0xe5, 0xb3, 0x69, 0xff, 0xd9, 0xa2, 0x5f, 0xfe, + 0xb1, 0x5d, 0x0a, 0xf7, 0x8f, 0x6e, 0x53, 0x83, + 0xa8, 0x62, 0x1f, 0xc0, 0xe4, 0xa4, 0x53, 0xff, + 0xfc, 0xc6, 0x77, 0xff, 0xcb, 0x7f, 0x2b, 0xf8, + 0x97, 0x69, 0x41, 0x93, 0x5e, 0x90, 0xd0, 0x4c, + 0x5e, 0x90, 0xd0, 0x4c, 0x5e, 0x90, 0xd0, 0x4c, + 0x5e, 0x90, 0xd0, 0x4c, 0x5e, 0x90, 0xd0, 0x4c, + 0x5e, 0x90, 0xd0, 0x4c, 0x5e, 0x90, 0xd0, 0x4c, + 0x5e, 0x90, 0xd0, 0x4c, 0x5e, 0x90, 0xd0, 0x4c, + 0x5d, 0x91, 0xd0, 0x4d, 0xa9, 0x63, 0x1d, 0xc4, + 0xd0, 0x94, 0x56, 0xfd, 0xb9, 0x6e, 0x21, 0xff, + 0xbd, 0x69, 0x0f, 0xff, 0xbf, 0x6c, 0x11, 0xff, + 0xc1, 0x74, 0x20, 0xff, 0xd2, 0x90, 0x40, 0xff, + 0xe4, 0x9a, 0x34, 0xff, 0xf0, 0xa6, 0x3b, 0xff, + 0xef, 0xa6, 0x3a, 0xff, 0xee, 0xa5, 0x3a, 0xff, + 0xed, 0xa4, 0x3a, 0xff, 0xd4, 0x84, 0x22, 0xff, + 0xc3, 0x6f, 0x12, 0xff, 0xe3, 0x99, 0x32, 0xff, + 0xe9, 0xa1, 0x39, 0xff, 0xe9, 0xac, 0x53, 0xff, + 0xe3, 0xba, 0x7f, 0xff, 0xbe, 0x75, 0x2b, 0xf8, + 0xa8, 0x62, 0x20, 0xc0, 0x60, 0x8e, 0xc3, 0x50, + 0x5d, 0x91, 0xd0, 0x4d, 0x5c, 0x8f, 0xd1, 0x4e, + 0x5c, 0x8f, 0xd1, 0x4e, 0x5c, 0x8f, 0xd1, 0x4e, + 0x5c, 0x8f, 0xd1, 0x4e, 0x6a, 0x86, 0xaf, 0x58, + 0xb1, 0x59, 0x02, 0xf7, 0xf4, 0xaf, 0x6e, 0xff, + 0xce, 0x7e, 0x30, 0xf8, 0xa1, 0x62, 0x28, 0xb1, + 0x5c, 0x8f, 0xd1, 0x4e, 0x5c, 0x8f, 0xd1, 0x4e, + 0x5c, 0x8f, 0xd1, 0x4e, 0x65, 0x8a, 0xbc, 0x54, + 0xae, 0x5e, 0x0f, 0xe3, 0xc5, 0x82, 0x3e, 0xfe, + 0xbe, 0x70, 0x20, 0xff, 0xc7, 0x76, 0x19, 0xff, + 0xc8, 0x77, 0x19, 0xff, 0xc6, 0x76, 0x1e, 0xff, + 0xd9, 0x99, 0x4d, 0xff, 0xe1, 0x9b, 0x3d, 0xff, + 0xef, 0xa4, 0x3a, 0xff, 0xef, 0xa6, 0x3a, 0xff, + 0xee, 0xa5, 0x3a, 0xff, 0xed, 0xa4, 0x3a, 0xff, + 0xd3, 0x84, 0x22, 0xff, 0xc0, 0x6b, 0x0f, 0xff, + 0xd3, 0x83, 0x21, 0xff, 0xd9, 0x8b, 0x28, 0xff, + 0xdc, 0x91, 0x2c, 0xff, 0xe0, 0xa0, 0x49, 0xff, + 0xe5, 0xb7, 0x79, 0xff, 0xd7, 0x9b, 0x54, 0xfd, + 0xb3, 0x5d, 0x0a, 0xf8, 0x9a, 0x67, 0x3c, 0x9c, + 0x5e, 0x8f, 0xc9, 0x50, 0x5e, 0x90, 0xcf, 0x4f, + 0x8b, 0x71, 0x60, 0x7d, 0xac, 0x5e, 0x14, 0xd1, + 0xb5, 0x5f, 0x0d, 0xf9, 0xcd, 0x7d, 0x2f, 0xf8, + 0xe0, 0x92, 0x48, 0xff, 0xef, 0xa7, 0x64, 0xff, + 0xe4, 0x99, 0x50, 0xff, 0xab, 0x5f, 0x15, 0xcf, + 0x5d, 0x8f, 0xcf, 0x4f, 0x5d, 0x8f, 0xcf, 0x4f, + 0x5d, 0x8f, 0xcf, 0x4f, 0x5d, 0x8f, 0xcf, 0x4f, + 0x9f, 0x67, 0x32, 0xa7, 0xc6, 0x85, 0x41, 0xfb, + 0xbd, 0x76, 0x2f, 0xff, 0xbd, 0x6a, 0x13, 0xff, + 0xc0, 0x6d, 0x11, 0xff, 0xc4, 0x76, 0x1f, 0xff, + 0xd4, 0x97, 0x4d, 0xff, 0xdb, 0x94, 0x3a, 0xff, + 0xec, 0xa2, 0x38, 0xff, 0xef, 0xa6, 0x3a, 0xff, + 0xee, 0xa5, 0x3a, 0xff, 0xed, 0xa4, 0x3a, 0xff, + 0xd7, 0x88, 0x25, 0xff, 0xbf, 0x6b, 0x0f, 0xff, + 0xd8, 0x8a, 0x27, 0xff, 0xe6, 0x9d, 0x35, 0xff, + 0xe9, 0xa1, 0x39, 0xff, 0xe8, 0xa5, 0x44, 0xff, + 0xe9, 0xbb, 0x78, 0xff, 0xe6, 0xb2, 0x6d, 0xff, + 0xb8, 0x64, 0x11, 0xf7, 0xa2, 0x63, 0x2a, 0xb2, + 0x61, 0x8b, 0xc1, 0x54, 0x5c, 0x90, 0xd0, 0x50, + 0x8a, 0x72, 0x5f, 0x7f, 0xad, 0x5e, 0x12, 0xd5, + 0xb5, 0x60, 0x0e, 0xfa, 0xda, 0x8a, 0x3c, 0xfd, + 0xf1, 0xa9, 0x64, 0xff, 0xe8, 0x9b, 0x51, 0xff, + 0xae, 0x5d, 0x11, 0xd8, 0x5c, 0x90, 0xd0, 0x50, + 0x5c, 0x90, 0xd0, 0x50, 0x5c, 0x90, 0xd0, 0x50, + 0x5c, 0x90, 0xd0, 0x50, 0x5c, 0x90, 0xd0, 0x50, + 0x5c, 0x90, 0xd0, 0x50, 0x5c, 0x90, 0xd0, 0x50, + 0xa8, 0x63, 0x20, 0xc3, 0xd3, 0x9b, 0x62, 0xfd, + 0xb7, 0x6e, 0x23, 0xff, 0xb5, 0x61, 0x0b, 0xff, + 0xbb, 0x69, 0x10, 0xff, 0xc1, 0x75, 0x23, 0xff, + 0xd5, 0x99, 0x50, 0xff, 0xe1, 0x9c, 0x40, 0xff, + 0xed, 0xa4, 0x3a, 0xff, 0xec, 0xa4, 0x3a, 0xff, + 0xeb, 0xa3, 0x39, 0xff, 0xda, 0x8d, 0x29, 0xff, + 0xbd, 0x68, 0x0d, 0xff, 0xe4, 0x9c, 0x35, 0xff, + 0xe7, 0xa0, 0x38, 0xff, 0xe6, 0xa0, 0x3c, 0xff, + 0xe5, 0xb5, 0x70, 0xff, 0xd9, 0xa3, 0x5f, 0xfe, + 0xb1, 0x5c, 0x08, 0xf8, 0x8c, 0x6e, 0x57, 0x86, + 0x5b, 0x8f, 0xd0, 0x51, 0x88, 0x71, 0x62, 0x7f, + 0xc1, 0x6f, 0x1e, 0xf6, 0xf6, 0xb6, 0x6f, 0xff, + 0xb1, 0x58, 0x02, 0xfa, 0x60, 0x8d, 0xc9, 0x54, + 0x5d, 0x90, 0xd1, 0x52, 0x5d, 0x90, 0xd1, 0x52, + 0x5d, 0x90, 0xd1, 0x52, 0x5d, 0x90, 0xd1, 0x52, + 0x5d, 0x90, 0xd1, 0x52, 0x5d, 0x90, 0xd1, 0x52, + 0x62, 0x94, 0xd0, 0x40, 0x62, 0x94, 0xd0, 0x40, + 0x62, 0x94, 0xd0, 0x40, 0x6b, 0x8b, 0xb5, 0x47, + 0xae, 0x5e, 0x10, 0xda, 0xd9, 0xad, 0x7d, 0xfc, + 0xe5, 0xcb, 0xab, 0xff, 0xe8, 0xc7, 0x9b, 0xff, + 0xe6, 0xb5, 0x6e, 0xff, 0xde, 0x9a, 0x36, 0xff, + 0xdd, 0x99, 0x36, 0xff, 0xdc, 0x99, 0x36, 0xff, + 0xdb, 0x98, 0x35, 0xff, 0xda, 0x97, 0x35, 0xff, + 0xc7, 0x7b, 0x1e, 0xff, 0xb3, 0x58, 0x00, 0xff, + 0xc3, 0x62, 0x01, 0xff, 0xb4, 0x5a, 0x01, 0xff, + 0xbc, 0x68, 0x12, 0xff, 0xc1, 0x82, 0x3d, 0xff, + 0xb0, 0x62, 0x16, 0xfa, 0xa1, 0x66, 0x2e, 0x9b, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x73, 0x86, 0xa1, 0x4d, 0x95, 0x6b, 0x48, 0x7d, + 0xa8, 0x5f, 0x1e, 0xb2, 0xb0, 0x5e, 0x0f, 0xd0, + 0xb2, 0x5b, 0x08, 0xe3, 0xa3, 0x60, 0x23, 0xaa, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x6f, 0x86, 0xa6, 0x4b, 0xb6, 0x69, 0x1d, 0xe2, + 0xda, 0xaf, 0x80, 0xfd, 0xd9, 0xb6, 0x8e, 0xff, + 0xe2, 0xbe, 0x8f, 0xff, 0xe8, 0xbd, 0x80, 0xff, + 0xdf, 0x9d, 0x3d, 0xff, 0xdd, 0x99, 0x36, 0xff, + 0xdc, 0x99, 0x36, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xc7, 0x7b, 0x1e, 0xff, + 0xb4, 0x58, 0x00, 0xff, 0xce, 0x66, 0x00, 0xff, + 0xca, 0x67, 0x02, 0xff, 0xc6, 0x64, 0x04, 0xff, + 0xc0, 0x64, 0x08, 0xff, 0xbd, 0x68, 0x10, 0xff, + 0xb7, 0x5f, 0x0a, 0xff, 0xb2, 0x5d, 0x07, 0xef, + 0x7a, 0x7d, 0x8e, 0x55, 0x60, 0x91, 0xd1, 0x41, + 0x60, 0x91, 0xd1, 0x41, 0x60, 0x91, 0xd1, 0x41, + 0x60, 0x91, 0xd1, 0x41, 0x60, 0x91, 0xd1, 0x41, + 0x76, 0x81, 0x9a, 0x51, 0x97, 0x69, 0x41, 0x85, + 0xac, 0x5f, 0x16, 0xc2, 0xb3, 0x5b, 0x07, 0xe8, + 0xb2, 0x59, 0x02, 0xf7, 0xaf, 0x5b, 0x09, 0xe2, + 0x61, 0x92, 0xd1, 0x42, 0x61, 0x92, 0xd1, 0x42, + 0x61, 0x92, 0xd1, 0x42, 0x61, 0x92, 0xd1, 0x42, + 0x61, 0x91, 0xca, 0x43, 0xac, 0x60, 0x17, 0xc9, + 0xd8, 0xad, 0x7e, 0xfc, 0xe7, 0xcf, 0xb1, 0xff, + 0xea, 0xcb, 0xa2, 0xff, 0xe9, 0xbc, 0x7c, 0xff, + 0xde, 0x9b, 0x39, 0xff, 0xdd, 0x99, 0x36, 0xff, + 0xdc, 0x99, 0x36, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xc7, 0x7b, 0x1e, 0xff, + 0xb3, 0x58, 0x00, 0xff, 0xc9, 0x65, 0x01, 0xff, + 0xbf, 0x61, 0x03, 0xff, 0xb4, 0x5b, 0x01, 0xff, + 0xb7, 0x5e, 0x06, 0xff, 0xbd, 0x6d, 0x1b, 0xff, + 0xbe, 0x6d, 0x1a, 0xfc, 0xb2, 0x5d, 0x0a, 0xe9, + 0x87, 0x75, 0x6b, 0x68, 0x60, 0x93, 0xcf, 0x43, + 0x60, 0x93, 0xcf, 0x43, 0x60, 0x93, 0xcf, 0x43, + 0x60, 0x93, 0xcf, 0x43, 0x60, 0x93, 0xcf, 0x43, + 0x86, 0x77, 0x6f, 0x66, 0xaa, 0x5e, 0x17, 0xc1, + 0xb4, 0x5c, 0x09, 0xfa, 0xc8, 0x71, 0x1d, 0xfb, + 0xb2, 0x5a, 0x04, 0xf0, 0x60, 0x93, 0xcf, 0x43, + 0x60, 0x93, 0xcf, 0x43, 0x60, 0x93, 0xcf, 0x43, + 0x60, 0x93, 0xcf, 0x43, 0x60, 0x93, 0xcf, 0x43, + 0x60, 0x93, 0xcf, 0x43, 0x60, 0x93, 0xcf, 0x43, + 0x6a, 0x8a, 0xb3, 0x4a, 0xb3, 0x62, 0x13, 0xe6, + 0xe3, 0xc2, 0x9c, 0xfe, 0xe9, 0xd2, 0xb4, 0xff, + 0xea, 0xcc, 0xa3, 0xff, 0xea, 0xc5, 0x8f, 0xff, + 0xdf, 0xa2, 0x48, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xd9, 0x96, 0x35, 0xff, + 0xd1, 0x8b, 0x2c, 0xff, 0xb3, 0x5a, 0x01, 0xff, + 0xb3, 0x59, 0x00, 0xff, 0xce, 0x8a, 0x2b, 0xff, + 0xd4, 0x96, 0x3a, 0xff, 0xd4, 0xa7, 0x66, 0xff, + 0xc1, 0x8b, 0x47, 0xfc, 0xaf, 0x59, 0x06, 0xf1, + 0x85, 0x75, 0x6c, 0x67, 0x61, 0x90, 0xc8, 0x44, + 0xb2, 0x5b, 0x06, 0xf3, 0xf4, 0xbb, 0x6c, 0xff, + 0xf2, 0xb5, 0x63, 0xff, 0xb2, 0x5a, 0x05, 0xf7, + 0x6b, 0x88, 0xae, 0x4c, 0x5f, 0x92, 0xcf, 0x43, + 0x5e, 0x93, 0xd0, 0x44, 0x5e, 0x93, 0xd0, 0x44, + 0x5e, 0x93, 0xd0, 0x44, 0x5e, 0x93, 0xd0, 0x44, + 0x5e, 0x93, 0xd0, 0x44, 0x5e, 0x93, 0xd0, 0x44, + 0x5e, 0x93, 0xd0, 0x44, 0x5e, 0x93, 0xd0, 0x44, + 0x5e, 0x93, 0xd0, 0x44, 0x67, 0x8b, 0xb6, 0x4b, + 0xae, 0x5e, 0x10, 0xdb, 0xdd, 0xb6, 0x8e, 0xfc, + 0xe5, 0xca, 0xa7, 0xff, 0xe8, 0xc6, 0x97, 0xff, + 0xe6, 0xb5, 0x6e, 0xff, 0xde, 0x9a, 0x36, 0xff, + 0xdd, 0x99, 0x36, 0xff, 0xdc, 0x99, 0x36, 0xff, + 0xdb, 0x98, 0x35, 0xff, 0xda, 0x97, 0x35, 0xff, + 0xc7, 0x7b, 0x1e, 0xff, 0xb3, 0x58, 0x00, 0xff, + 0xc3, 0x62, 0x01, 0xff, 0xb4, 0x5a, 0x01, 0xff, + 0xbc, 0x68, 0x11, 0xff, 0xc1, 0x82, 0x3c, 0xff, + 0xaf, 0x62, 0x14, 0xfa, 0xa0, 0x66, 0x32, 0x9c, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0xb1, 0x59, 0x04, 0xf1, 0xcf, 0x88, 0x42, 0xfb, + 0xaf, 0x5c, 0x0b, 0xdd, 0x68, 0x8a, 0xbc, 0x4a, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0x60, 0x91, 0xd0, 0x45, 0x60, 0x91, 0xd0, 0x45, + 0x6d, 0x85, 0xa8, 0x50, 0xb5, 0x69, 0x1e, 0xe3, + 0xda, 0xaf, 0x80, 0xfd, 0xd9, 0xb6, 0x8e, 0xff, + 0xe2, 0xbe, 0x8f, 0xff, 0xe8, 0xbd, 0x80, 0xff, + 0xdf, 0x9d, 0x3d, 0xff, 0xdd, 0x99, 0x36, 0xff, + 0xdc, 0x99, 0x36, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xc7, 0x7b, 0x1d, 0xff, + 0xb6, 0x5a, 0x01, 0xff, 0xcf, 0x67, 0x01, 0xff, + 0xca, 0x67, 0x02, 0xff, 0xc6, 0x64, 0x04, 0xff, + 0xc0, 0x64, 0x08, 0xff, 0xbd, 0x68, 0x10, 0xff, + 0xb6, 0x5f, 0x0a, 0xff, 0xb2, 0x5d, 0x07, 0xef, + 0x78, 0x7e, 0x92, 0x5a, 0x5f, 0x90, 0xd1, 0x46, + 0x5f, 0x90, 0xd1, 0x46, 0x5f, 0x90, 0xd1, 0x46, + 0x5e, 0x91, 0xcf, 0x46, 0x5e, 0x91, 0xcf, 0x46, + 0x73, 0x82, 0x9c, 0x55, 0x95, 0x6a, 0x44, 0x88, + 0xab, 0x60, 0x17, 0xc4, 0xb2, 0x5b, 0x07, 0xe9, + 0xb2, 0x59, 0x02, 0xf7, 0xaf, 0x5b, 0x09, 0xe3, + 0x5e, 0x91, 0xcf, 0x46, 0x5e, 0x91, 0xcf, 0x46, + 0x5e, 0x91, 0xcf, 0x46, 0x5e, 0x91, 0xcf, 0x46, + 0x5e, 0x91, 0xcf, 0x46, 0xaa, 0x61, 0x1d, 0xbe, + 0xd4, 0xa8, 0x78, 0xfa, 0xe6, 0xcd, 0xaf, 0xff, + 0xea, 0xcb, 0xa3, 0xff, 0xe9, 0xbe, 0x80, 0xff, + 0xdf, 0x9d, 0x3c, 0xff, 0xdd, 0x99, 0x36, 0xff, + 0xdc, 0x99, 0x36, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xca, 0x7e, 0x21, 0xff, + 0xb2, 0x58, 0x00, 0xff, 0xc9, 0x65, 0x01, 0xff, + 0xc0, 0x61, 0x03, 0xff, 0xb5, 0x5a, 0x02, 0xff, + 0xb6, 0x5d, 0x05, 0xff, 0xbd, 0x6c, 0x1a, 0xff, + 0xbe, 0x6e, 0x1b, 0xfd, 0xb1, 0x5d, 0x06, 0xee, + 0x8a, 0x73, 0x63, 0x72, 0x60, 0x92, 0xcf, 0x47, + 0x60, 0x92, 0xcf, 0x47, 0x60, 0x92, 0xcf, 0x47, + 0x60, 0x92, 0xcf, 0x47, 0x60, 0x92, 0xcf, 0x47, + 0x7f, 0x79, 0x7a, 0x65, 0xa8, 0x61, 0x1c, 0xbc, + 0xb3, 0x5c, 0x06, 0xf9, 0xc8, 0x72, 0x1d, 0xfb, + 0xb2, 0x58, 0x02, 0xf8, 0x5f, 0x8f, 0xcc, 0x48, + 0x5d, 0x92, 0xcf, 0x47, 0x5f, 0x91, 0xd0, 0x48, + 0x5f, 0x91, 0xd0, 0x48, 0x5f, 0x91, 0xd0, 0x48, + 0x5f, 0x91, 0xd0, 0x48, 0x5f, 0x91, 0xd0, 0x48, + 0x69, 0x89, 0xb5, 0x4f, 0xb3, 0x62, 0x13, 0xe7, + 0xe2, 0xbf, 0x96, 0xfe, 0xe9, 0xd3, 0xb5, 0xff, + 0xeb, 0xcc, 0xa1, 0xff, 0xe8, 0xbb, 0x7c, 0xff, + 0xdc, 0x9b, 0x39, 0xff, 0xdb, 0x98, 0x35, 0xff, + 0xda, 0x97, 0x35, 0xff, 0xd9, 0x96, 0x35, 0xff, + 0xd1, 0x8b, 0x2c, 0xff, 0xb3, 0x5a, 0x01, 0xff, + 0xb3, 0x59, 0x00, 0xff, 0xce, 0x8a, 0x2b, 0xff, + 0xd4, 0x96, 0x3b, 0xff, 0xd5, 0xa9, 0x6b, 0xff, + 0xc1, 0x8a, 0x45, 0xfd, 0xaf, 0x59, 0x06, 0xf2, + 0x84, 0x76, 0x70, 0x6d, 0x5e, 0x92, 0xd0, 0x49, + 0x5e, 0x92, 0xd0, 0x49, 0x9b, 0x66, 0x36, 0x9a, + 0xcd, 0x6f, 0x0d, 0xfa, 0xcf, 0x6f, 0x0a, 0xfb, + 0xab, 0x5f, 0x17, 0xc7, 0x5e, 0x92, 0xd0, 0x49, + 0x5e, 0x92, 0xd0, 0x49, 0x5e, 0x92, 0xd0, 0x49, + 0x5e, 0x92, 0xd0, 0x49, 0x5e, 0x92, 0xd0, 0x49, + 0x5e, 0x92, 0xd0, 0x49, 0x5e, 0x92, 0xd0, 0x49, + 0x64, 0x94, 0xcf, 0x37, 0x64, 0x94, 0xcf, 0x37, + 0x64, 0x94, 0xcf, 0x37, 0x64, 0x94, 0xcf, 0x37, + 0x75, 0x83, 0x9d, 0x44, 0xaf, 0x5b, 0x08, 0xe5, + 0xc6, 0x8f, 0x4c, 0xfd, 0xcc, 0xa8, 0x74, 0xff, + 0xcb, 0x9e, 0x5a, 0xff, 0xca, 0x8e, 0x36, 0xff, + 0xc9, 0x8c, 0x31, 0xff, 0xc8, 0x8b, 0x31, 0xff, + 0xc7, 0x8a, 0x31, 0xff, 0xc2, 0x82, 0x29, 0xff, + 0xb6, 0x60, 0x08, 0xff, 0xcf, 0x66, 0x00, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf3, 0x7a, 0x04, 0xff, + 0xe4, 0x7a, 0x15, 0xff, 0xd0, 0x79, 0x20, 0xff, + 0xb2, 0x5b, 0x07, 0xf1, 0x57, 0x7d, 0xa3, 0x42, + 0x57, 0x85, 0xb7, 0x3d, 0x60, 0x91, 0xc5, 0x3a, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x65, 0x94, 0xd0, 0x38, 0x65, 0x94, 0xd0, 0x38, + 0x65, 0x94, 0xd0, 0x38, 0x65, 0x94, 0xd0, 0x38, + 0x65, 0x94, 0xd0, 0x38, 0x78, 0x85, 0x9c, 0x45, + 0xb1, 0x5d, 0x0b, 0xe1, 0xcf, 0x99, 0x5c, 0xfb, + 0xce, 0xaa, 0x77, 0xff, 0xcb, 0x9e, 0x5b, 0xff, + 0xca, 0x8f, 0x38, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc7, 0x8a, 0x31, 0xff, + 0xc1, 0x80, 0x27, 0xff, 0xb6, 0x61, 0x09, 0xff, + 0xb6, 0x5a, 0x01, 0xff, 0xd3, 0x69, 0x01, 0xff, + 0xef, 0x76, 0x00, 0xff, 0xf5, 0x7b, 0x04, 0xff, + 0xf7, 0x95, 0x2a, 0xff, 0xfa, 0xaf, 0x4b, 0xff, + 0xfb, 0xbf, 0x69, 0xff, 0xc0, 0x73, 0x21, 0xf7, + 0x9b, 0x68, 0x39, 0x83, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x67, 0x91, 0xbf, 0x3d, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x63, 0x95, 0xd1, 0x39, + 0x63, 0x95, 0xd1, 0x39, 0x6e, 0x8e, 0xb6, 0x3f, + 0xae, 0x5d, 0x0d, 0xd9, 0xc1, 0x86, 0x44, 0xfb, + 0xca, 0xa6, 0x71, 0xff, 0xcb, 0x9c, 0x58, 0xff, + 0xca, 0x8e, 0x35, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc7, 0x8a, 0x31, 0xff, + 0xc2, 0x82, 0x29, 0xff, 0xb5, 0x60, 0x09, 0xff, + 0xbe, 0x5f, 0x01, 0xff, 0xf1, 0x77, 0x00, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf5, 0x80, 0x0f, 0xff, + 0xf3, 0x98, 0x34, 0xff, 0xec, 0xa4, 0x4c, 0xff, + 0xb9, 0x66, 0x12, 0xfc, 0x7d, 0x71, 0x6e, 0x5c, + 0x5d, 0x8c, 0xcc, 0x3c, 0x62, 0x93, 0xd1, 0x3a, + 0x62, 0x93, 0xd1, 0x3a, 0x62, 0x93, 0xd1, 0x3a, + 0x62, 0x93, 0xd1, 0x3a, 0x62, 0x93, 0xd1, 0x3a, + 0x62, 0x93, 0xd1, 0x3a, 0x62, 0x93, 0xd1, 0x3a, + 0x79, 0x82, 0x8c, 0x4e, 0xa6, 0x61, 0x22, 0xa4, + 0xb0, 0x58, 0x03, 0xf3, 0x79, 0x82, 0x90, 0x4d, + 0x61, 0x94, 0xcf, 0x3b, 0x61, 0x94, 0xcf, 0x3b, + 0x61, 0x94, 0xcf, 0x3b, 0x61, 0x94, 0xcf, 0x3b, + 0x61, 0x94, 0xcf, 0x3b, 0x61, 0x94, 0xcf, 0x3b, + 0x61, 0x94, 0xcf, 0x3b, 0x81, 0x7b, 0x7a, 0x56, + 0xb0, 0x5b, 0x07, 0xf5, 0xce, 0xa1, 0x68, 0xff, + 0xcb, 0x9f, 0x5f, 0xff, 0xca, 0x8f, 0x39, 0xff, + 0xc9, 0x8c, 0x31, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc6, 0x88, 0x2f, 0xff, + 0xb7, 0x66, 0x0d, 0xff, 0xcf, 0x68, 0x00, 0xff, + 0xda, 0x6c, 0x00, 0xff, 0xb8, 0x63, 0x0c, 0xff, + 0xc2, 0x99, 0x5e, 0xff, 0xb7, 0x79, 0x33, 0xfa, + 0xad, 0x5b, 0x09, 0xe4, 0x70, 0x71, 0x78, 0x57, + 0x57, 0x83, 0xbb, 0x40, 0x90, 0x6d, 0x52, 0x6e, + 0xba, 0x65, 0x0c, 0xf5, 0xf5, 0xb4, 0x5a, 0xff, + 0xb7, 0x63, 0x0e, 0xf8, 0x9a, 0x69, 0x3e, 0x81, + 0x63, 0x93, 0xcf, 0x3b, 0x63, 0x93, 0xcf, 0x3b, + 0x63, 0x93, 0xcf, 0x3b, 0x63, 0x93, 0xcf, 0x3b, + 0x63, 0x93, 0xcf, 0x3b, 0x63, 0x93, 0xcf, 0x3b, + 0x63, 0x93, 0xcf, 0x3b, 0x63, 0x93, 0xcf, 0x3b, + 0x63, 0x93, 0xcf, 0x3b, 0x63, 0x93, 0xcf, 0x3b, + 0x63, 0x93, 0xcf, 0x3b, 0x62, 0x94, 0xd0, 0x3c, + 0x73, 0x84, 0xa1, 0x48, 0xaf, 0x5b, 0x0a, 0xe6, + 0xcb, 0x9b, 0x61, 0xfd, 0xcb, 0xa7, 0x6f, 0xff, + 0xcb, 0x97, 0x4b, 0xff, 0xca, 0x8c, 0x31, 0xff, + 0xc9, 0x8c, 0x31, 0xff, 0xc8, 0x8b, 0x31, 0xff, + 0xc7, 0x8a, 0x31, 0xff, 0xc2, 0x82, 0x29, 0xff, + 0xb6, 0x60, 0x08, 0xff, 0xcf, 0x66, 0x00, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf3, 0x7d, 0x0b, 0xff, + 0xe4, 0x7b, 0x17, 0xff, 0xcf, 0x78, 0x1e, 0xff, + 0xb2, 0x5b, 0x05, 0xf2, 0x55, 0x77, 0xa5, 0x47, + 0x56, 0x85, 0xbc, 0x41, 0x5e, 0x8f, 0xc5, 0x3e, + 0x62, 0x94, 0xd0, 0x3c, 0x60, 0x92, 0xd0, 0x3d, + 0x60, 0x92, 0xd0, 0x3d, 0x60, 0x92, 0xd0, 0x3d, + 0x60, 0x92, 0xd0, 0x3d, 0x60, 0x92, 0xd0, 0x3d, + 0xb0, 0x59, 0x05, 0xea, 0xa8, 0x63, 0x23, 0xac, + 0x64, 0x8e, 0xbf, 0x41, 0x60, 0x92, 0xd0, 0x3d, + 0x60, 0x92, 0xd0, 0x3d, 0x60, 0x92, 0xd0, 0x3d, + 0x60, 0x92, 0xd0, 0x3d, 0x60, 0x92, 0xd0, 0x3d, + 0x60, 0x92, 0xd0, 0x3d, 0x73, 0x85, 0xa0, 0x4a, + 0xb1, 0x5d, 0x0c, 0xe1, 0xcf, 0x99, 0x5d, 0xfb, + 0xce, 0xaa, 0x77, 0xff, 0xcb, 0x9e, 0x5b, 0xff, + 0xca, 0x8f, 0x38, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc7, 0x8a, 0x31, 0xff, + 0xc1, 0x80, 0x28, 0xff, 0xb7, 0x61, 0x09, 0xff, + 0xb6, 0x5a, 0x01, 0xff, 0xd3, 0x69, 0x01, 0xff, + 0xef, 0x76, 0x00, 0xff, 0xf5, 0x7b, 0x04, 0xff, + 0xf7, 0x95, 0x2a, 0xff, 0xfa, 0xaf, 0x4b, 0xff, + 0xfb, 0xbf, 0x69, 0xff, 0xc0, 0x73, 0x21, 0xf7, + 0x99, 0x69, 0x3d, 0x86, 0x62, 0x93, 0xd1, 0x3e, + 0x62, 0x93, 0xd1, 0x3e, 0x62, 0x93, 0xd1, 0x3e, + 0x62, 0x93, 0xd1, 0x3e, 0x62, 0x93, 0xd1, 0x3e, + 0x62, 0x93, 0xd1, 0x3e, 0x62, 0x93, 0xd1, 0x3e, + 0x62, 0x93, 0xd1, 0x3e, 0x62, 0x93, 0xd1, 0x3e, + 0x62, 0x93, 0xd1, 0x3e, 0x66, 0x8f, 0xc1, 0x42, + 0x61, 0x92, 0xd1, 0x3e, 0x61, 0x92, 0xd1, 0x3e, + 0x61, 0x92, 0xd1, 0x3e, 0x61, 0x92, 0xd1, 0x3e, + 0x61, 0x92, 0xd1, 0x3e, 0x66, 0x8c, 0xc4, 0x41, + 0xae, 0x5f, 0x12, 0xd0, 0xbe, 0x80, 0x3e, 0xfa, + 0xca, 0xa5, 0x70, 0xff, 0xcb, 0x9e, 0x5a, 0xff, + 0xca, 0x8e, 0x36, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc7, 0x8a, 0x31, 0xff, + 0xc3, 0x82, 0x2a, 0xff, 0xb6, 0x62, 0x0b, 0xff, + 0xbc, 0x5d, 0x01, 0xff, 0xf0, 0x77, 0x00, 0xff, + 0xf5, 0x79, 0x00, 0xff, 0xf5, 0x7f, 0x0d, 0xff, + 0xf3, 0x96, 0x31, 0xff, 0xec, 0xa4, 0x4b, 0xff, + 0xbd, 0x6b, 0x17, 0xfc, 0x85, 0x6b, 0x59, 0x6e, + 0x5e, 0x8d, 0xc8, 0x41, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x75, 0x84, 0x99, 0x4e, 0xa3, 0x64, 0x28, 0xa0, + 0xb0, 0x59, 0x04, 0xf0, 0x82, 0x78, 0x77, 0x5c, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x60, 0x93, 0xcf, 0x3f, + 0x60, 0x93, 0xcf, 0x3f, 0x7f, 0x7b, 0x7d, 0x59, + 0xb0, 0x5a, 0x06, 0xf5, 0xcd, 0x9e, 0x5f, 0xff, + 0xcb, 0x9f, 0x5f, 0xff, 0xca, 0x90, 0x3b, 0xff, + 0xc9, 0x8c, 0x31, 0xff, 0xc9, 0x8c, 0x31, 0xff, + 0xc8, 0x8b, 0x31, 0xff, 0xc6, 0x88, 0x2f, 0xff, + 0xb7, 0x66, 0x0d, 0xff, 0xcf, 0x68, 0x00, 0xff, + 0xda, 0x6c, 0x00, 0xff, 0xb8, 0x63, 0x0d, 0xff, + 0xc2, 0x9b, 0x61, 0xff, 0xb7, 0x77, 0x31, 0xfa, + 0xad, 0x5b, 0x09, 0xe4, 0x70, 0x70, 0x7c, 0x5b, + 0x5a, 0x87, 0xbf, 0x44, 0x62, 0x91, 0xcc, 0x41, + 0x5f, 0x94, 0xd0, 0x40, 0xae, 0x60, 0x13, 0xcc, + 0xc8, 0x6a, 0x09, 0xff, 0xb2, 0x5a, 0x04, 0xf2, + 0x73, 0x84, 0x9b, 0x4f, 0x5f, 0x94, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x61, 0x93, 0xd0, 0x40, 0x61, 0x93, 0xd0, 0x40, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x65, 0x96, 0xce, 0x2f, + 0x65, 0x96, 0xce, 0x2f, 0x81, 0x7e, 0x7f, 0x44, + 0xb0, 0x5e, 0x0f, 0xcb, 0xa8, 0x5d, 0x10, 0xf7, + 0xaf, 0x82, 0x4a, 0xff, 0xb7, 0x96, 0x67, 0xff, + 0xb5, 0x8e, 0x55, 0xff, 0xb4, 0x8b, 0x4d, 0xff, + 0xb4, 0x88, 0x47, 0xff, 0xb2, 0x84, 0x43, 0xff, + 0xb1, 0x87, 0x4c, 0xff, 0xb3, 0x5c, 0x06, 0xff, + 0xe7, 0x74, 0x05, 0xff, 0xf5, 0x7b, 0x04, 0xff, + 0xf6, 0x86, 0x11, 0xff, 0xfa, 0xad, 0x4e, 0xff, + 0xc1, 0x72, 0x21, 0xf6, 0x7c, 0x59, 0x3a, 0x71, + 0x43, 0x67, 0x8b, 0x40, 0x4d, 0x70, 0x9b, 0x3b, + 0x55, 0x7f, 0xae, 0x36, 0x62, 0x91, 0xc5, 0x31, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x64, 0x97, 0xcf, 0x2f, 0x64, 0x97, 0xcf, 0x2f, + 0x77, 0x88, 0x9d, 0x3a, 0xaa, 0x62, 0x1c, 0xa2, + 0xb1, 0x5d, 0x09, 0xf7, 0xb8, 0x83, 0x44, 0xfb, + 0xbc, 0x9a, 0x69, 0xff, 0xb6, 0x91, 0x5d, 0xff, + 0xb4, 0x8e, 0x54, 0xff, 0xb4, 0x8b, 0x4d, 0xff, + 0xb2, 0x86, 0x47, 0xff, 0xb2, 0x8e, 0x58, 0xff, + 0xb3, 0x8b, 0x56, 0xff, 0xb6, 0x6f, 0x27, 0xff, + 0xb3, 0x59, 0x02, 0xff, 0xc7, 0x68, 0x0c, 0xff, + 0xea, 0x9a, 0x3c, 0xff, 0xfc, 0xbf, 0x66, 0xff, + 0xfc, 0xbf, 0x66, 0xff, 0xee, 0xb5, 0x69, 0xff, + 0xb0, 0x5d, 0x0d, 0xd8, 0x53, 0x7d, 0xb0, 0x37, + 0x60, 0x8e, 0xc6, 0x32, 0x66, 0x95, 0xd0, 0x30, + 0x66, 0x95, 0xd0, 0x30, 0x66, 0x95, 0xd0, 0x30, + 0x66, 0x95, 0xd0, 0x30, 0x66, 0x95, 0xd0, 0x30, + 0x66, 0x95, 0xd0, 0x30, 0x66, 0x95, 0xd0, 0x30, + 0x66, 0x95, 0xd0, 0x30, 0x66, 0x95, 0xd0, 0x30, + 0x66, 0x95, 0xd0, 0x30, 0x64, 0x97, 0xd1, 0x31, + 0x64, 0x97, 0xd1, 0x31, 0x64, 0x97, 0xd1, 0x31, + 0x64, 0x97, 0xd1, 0x31, 0x64, 0x97, 0xd1, 0x31, + 0x77, 0x84, 0x9b, 0x3e, 0xac, 0x62, 0x17, 0xb4, + 0xa5, 0x58, 0x0b, 0xf7, 0xab, 0x7d, 0x44, 0xfe, + 0xb7, 0x94, 0x60, 0xff, 0xb5, 0x8c, 0x50, 0xff, + 0xb4, 0x89, 0x48, 0xff, 0xb4, 0x86, 0x42, 0xff, + 0xb2, 0x82, 0x3e, 0xff, 0xb2, 0x87, 0x4a, 0xff, + 0xb2, 0x6f, 0x26, 0xff, 0xb7, 0x5c, 0x04, 0xff, + 0xe8, 0x77, 0x0a, 0xff, 0xf6, 0x8c, 0x1e, 0xff, + 0xf9, 0x9c, 0x29, 0xff, 0xfc, 0xbc, 0x5d, 0xff, + 0xcd, 0x87, 0x39, 0xf7, 0x8b, 0x5c, 0x2e, 0x85, + 0x4a, 0x6f, 0xa0, 0x3e, 0x50, 0x7d, 0xb2, 0x39, + 0x5d, 0x8e, 0xc9, 0x34, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x6e, 0x8a, 0xb0, 0x39, 0x71, 0x87, 0xa6, 0x3c, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x63, 0x94, 0xd1, 0x32, 0x63, 0x94, 0xd1, 0x32, + 0x93, 0x6e, 0x4f, 0x64, 0xb1, 0x5a, 0x05, 0xf0, + 0xad, 0x74, 0x32, 0xfb, 0xb6, 0x91, 0x5a, 0xff, + 0xb7, 0x8a, 0x47, 0xff, 0xb6, 0x85, 0x3c, 0xff, + 0xb5, 0x81, 0x35, 0xff, 0xb4, 0x7d, 0x2e, 0xff, + 0xb5, 0x63, 0x0d, 0xff, 0xe4, 0x76, 0x0c, 0xff, + 0xf5, 0x81, 0x10, 0xff, 0xc1, 0x63, 0x09, 0xff, + 0xb0, 0x5c, 0x08, 0xfa, 0xa6, 0x5a, 0x0f, 0xc9, + 0x4d, 0x59, 0x69, 0x51, 0x42, 0x60, 0x85, 0x45, + 0x4b, 0x6b, 0x93, 0x40, 0xa9, 0x5e, 0x15, 0xba, + 0xbd, 0x66, 0x0b, 0xfd, 0xb4, 0x5b, 0x04, 0xf8, + 0xa5, 0x64, 0x26, 0x93, 0x65, 0x96, 0xce, 0x32, + 0x65, 0x96, 0xce, 0x32, 0x65, 0x96, 0xce, 0x32, + 0x64, 0x94, 0xcf, 0x33, 0x64, 0x94, 0xcf, 0x33, + 0x64, 0x94, 0xcf, 0x33, 0x64, 0x94, 0xcf, 0x33, + 0x64, 0x94, 0xcf, 0x33, 0x64, 0x94, 0xcf, 0x33, + 0x64, 0x94, 0xcf, 0x33, 0x64, 0x94, 0xcf, 0x33, + 0x64, 0x94, 0xcf, 0x33, 0x64, 0x94, 0xcf, 0x33, + 0x64, 0x94, 0xcf, 0x33, 0x7f, 0x7e, 0x84, 0x48, + 0xb0, 0x5e, 0x10, 0xcc, 0xae, 0x67, 0x20, 0xf8, + 0xb0, 0x87, 0x54, 0xff, 0xb7, 0x91, 0x5b, 0xff, + 0xb5, 0x89, 0x49, 0xff, 0xb4, 0x86, 0x42, 0xff, + 0xb4, 0x84, 0x3b, 0xff, 0xb2, 0x81, 0x3a, 0xff, + 0xb1, 0x82, 0x42, 0xff, 0xb3, 0x5b, 0x05, 0xff, + 0xe7, 0x76, 0x09, 0xff, 0xf5, 0x7e, 0x0a, 0xff, + 0xf6, 0x88, 0x16, 0xff, 0xfa, 0xb1, 0x56, 0xff, + 0xbf, 0x6e, 0x1a, 0xf6, 0x7b, 0x5a, 0x3f, 0x74, + 0x43, 0x69, 0x92, 0x44, 0x4c, 0x71, 0xa1, 0x3f, + 0x53, 0x7f, 0xb4, 0x3a, 0x60, 0x90, 0xca, 0x35, + 0x62, 0x95, 0xd0, 0x34, 0x62, 0x95, 0xd0, 0x34, + 0x62, 0x95, 0xd0, 0x34, 0x62, 0x95, 0xd0, 0x34, + 0x8d, 0x73, 0x5e, 0x5c, 0x62, 0x95, 0xd0, 0x34, + 0x62, 0x95, 0xd0, 0x34, 0x62, 0x95, 0xd0, 0x34, + 0x62, 0x95, 0xd0, 0x34, 0x62, 0x95, 0xd0, 0x34, + 0x62, 0x95, 0xd0, 0x34, 0x62, 0x95, 0xd0, 0x34, + 0x62, 0x95, 0xd0, 0x34, 0x65, 0x93, 0xd0, 0x35, + 0x76, 0x86, 0xa2, 0x40, 0xa9, 0x62, 0x1f, 0xa5, + 0xb1, 0x5d, 0x09, 0xf7, 0xb8, 0x83, 0x44, 0xfc, + 0xbc, 0x9a, 0x69, 0xff, 0xb6, 0x91, 0x5d, 0xff, + 0xb4, 0x8e, 0x54, 0xff, 0xb4, 0x8b, 0x4d, 0xff, + 0xb2, 0x86, 0x47, 0xff, 0xb2, 0x8e, 0x58, 0xff, + 0xb3, 0x8b, 0x56, 0xff, 0xb6, 0x6f, 0x27, 0xff, + 0xb3, 0x59, 0x02, 0xff, 0xc7, 0x68, 0x0c, 0xff, + 0xea, 0x9a, 0x3c, 0xff, 0xfc, 0xbf, 0x66, 0xff, + 0xfc, 0xbf, 0x66, 0xff, 0xee, 0xb5, 0x69, 0xff, + 0xb0, 0x5d, 0x0e, 0xd9, 0x55, 0x7f, 0xae, 0x3c, + 0x61, 0x8f, 0xc2, 0x37, 0x65, 0x93, 0xd0, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x63, 0x95, 0xd1, 0x35, 0x63, 0x95, 0xd1, 0x35, + 0x72, 0x8a, 0xab, 0x3e, 0xab, 0x61, 0x1c, 0xad, + 0xa7, 0x58, 0x0a, 0xf8, 0xaa, 0x7b, 0x40, 0xfe, + 0xb6, 0x94, 0x61, 0xff, 0xb5, 0x8d, 0x50, 0xff, + 0xb4, 0x89, 0x49, 0xff, 0xb4, 0x86, 0x42, 0xff, + 0xb2, 0x82, 0x3d, 0xff, 0xb1, 0x87, 0x4a, 0xff, + 0xb2, 0x72, 0x2a, 0xff, 0xb5, 0x5b, 0x04, 0xff, + 0xe5, 0x76, 0x09, 0xff, 0xf6, 0x8c, 0x1d, 0xff, + 0xf9, 0x9b, 0x28, 0xff, 0xfc, 0xba, 0x5a, 0xff, + 0xd4, 0x90, 0x41, 0xf9, 0x93, 0x5a, 0x28, 0x93, + 0x4c, 0x6e, 0x9c, 0x43, 0x52, 0x7b, 0xac, 0x3e, + 0x5d, 0x8a, 0xc0, 0x39, 0x62, 0x93, 0xd2, 0x36, + 0x62, 0x93, 0xd2, 0x36, 0x62, 0x93, 0xd2, 0x36, + 0x62, 0x93, 0xd2, 0x36, 0x62, 0x93, 0xd2, 0x36, + 0x62, 0x93, 0xd2, 0x36, 0x62, 0x93, 0xd2, 0x36, + 0x69, 0x8c, 0xbc, 0x3b, 0x72, 0x84, 0xa2, 0x42, + 0x62, 0x93, 0xd2, 0x36, 0x64, 0x94, 0xcf, 0x37, + 0x64, 0x94, 0xcf, 0x37, 0x64, 0x94, 0xcf, 0x37, + 0x64, 0x94, 0xcf, 0x37, 0x64, 0x94, 0xcf, 0x37, + 0x64, 0x94, 0xcf, 0x37, 0x64, 0x94, 0xcf, 0x37, + 0x92, 0x6f, 0x53, 0x68, 0xb1, 0x5a, 0x05, 0xf1, + 0xac, 0x70, 0x2c, 0xfb, 0xb6, 0x8f, 0x56, 0xff, + 0xb7, 0x8a, 0x47, 0xff, 0xb6, 0x85, 0x3c, 0xff, + 0xb5, 0x81, 0x36, 0xff, 0xb4, 0x7d, 0x2f, 0xff, + 0xb5, 0x63, 0x0e, 0xff, 0xe4, 0x76, 0x0c, 0xff, + 0xf5, 0x81, 0x10, 0xff, 0xc1, 0x63, 0x08, 0xff, + 0xb0, 0x5b, 0x07, 0xfb, 0xa5, 0x5a, 0x11, 0xca, + 0x4d, 0x5a, 0x6f, 0x56, 0x44, 0x63, 0x8d, 0x4a, + 0x48, 0x70, 0x99, 0x46, 0x52, 0x79, 0xa8, 0x41, + 0x5d, 0x88, 0xbb, 0x3c, 0xb1, 0x59, 0x03, 0xf3, + 0xb2, 0x5b, 0x06, 0xea, 0x8b, 0x75, 0x64, 0x5d, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x63, 0x96, 0xcf, 0x38, 0x63, 0x96, 0xcf, 0x38, + 0x69, 0x96, 0xcd, 0x26, 0x69, 0x96, 0xcd, 0x26, + 0x69, 0x96, 0xcd, 0x26, 0x69, 0x96, 0xcd, 0x26, + 0x69, 0x96, 0xcd, 0x26, 0x69, 0x96, 0xcd, 0x26, + 0x69, 0x96, 0xcd, 0x26, 0x98, 0x65, 0x3a, 0x62, + 0xae, 0x59, 0x06, 0xe3, 0xad, 0x64, 0x17, 0xf7, + 0xb0, 0x76, 0x34, 0xfa, 0xa6, 0x75, 0x3b, 0xfe, + 0xa8, 0x7c, 0x45, 0xff, 0xb0, 0x83, 0x4b, 0xff, + 0xa7, 0x72, 0x33, 0xf9, 0xa9, 0x5b, 0x0d, 0xfb, + 0xb8, 0x5d, 0x06, 0xfe, 0xf2, 0x8c, 0x24, 0xff, + 0xf8, 0x95, 0x21, 0xff, 0xfb, 0xba, 0x5c, 0xff, + 0xce, 0x88, 0x3c, 0xf8, 0x82, 0x51, 0x23, 0x82, + 0x36, 0x4e, 0x6d, 0x41, 0x3b, 0x59, 0x7b, 0x3c, + 0x46, 0x63, 0x8d, 0x36, 0x4f, 0x74, 0xa4, 0x30, + 0x61, 0x8b, 0xc2, 0x2a, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x65, 0x95, 0xcf, 0x27, 0x65, 0x95, 0xcf, 0x27, + 0x7d, 0x76, 0x73, 0x3e, 0xa8, 0x5d, 0x14, 0xad, + 0xb4, 0x5e, 0x0b, 0xf9, 0xb9, 0x70, 0x25, 0xf5, + 0xb2, 0x73, 0x32, 0xf7, 0xb8, 0x80, 0x40, 0xfb, + 0xc9, 0x8f, 0x4b, 0xfc, 0xbf, 0x78, 0x30, 0xf6, + 0xb4, 0x5e, 0x0b, 0xfb, 0xad, 0x59, 0x07, 0xe7, + 0x89, 0x4d, 0x15, 0xa5, 0x83, 0x4d, 0x17, 0x9a, + 0xad, 0x5a, 0x07, 0xe4, 0xbb, 0x6b, 0x18, 0xf6, + 0xe2, 0xa0, 0x50, 0xfe, 0xde, 0x9e, 0x51, 0xfd, + 0xab, 0x5c, 0x0e, 0xc7, 0x42, 0x63, 0x8d, 0x36, + 0x4a, 0x74, 0xa4, 0x30, 0x5b, 0x8b, 0xc2, 0x2a, + 0x65, 0x95, 0xcf, 0x27, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x68, 0x97, 0xd0, 0x28, 0x68, 0x97, 0xd0, 0x28, + 0x8d, 0x6d, 0x4f, 0x53, 0xad, 0x5b, 0x0a, 0xd6, + 0xab, 0x61, 0x14, 0xf7, 0xab, 0x6f, 0x2d, 0xf8, + 0xa3, 0x70, 0x34, 0xfe, 0xa6, 0x78, 0x3e, 0xff, + 0xad, 0x7e, 0x42, 0xff, 0xa8, 0x70, 0x30, 0xfc, + 0xa5, 0x60, 0x19, 0xf7, 0xac, 0x56, 0x02, 0xfb, + 0xb3, 0x58, 0x01, 0xfc, 0xdd, 0x89, 0x2d, 0xfd, + 0xfb, 0xbc, 0x62, 0xff, 0xfc, 0xc4, 0x72, 0xff, + 0xc6, 0x79, 0x27, 0xf5, 0x7f, 0x54, 0x2c, 0x7a, + 0x39, 0x5e, 0x7f, 0x3e, 0x44, 0x68, 0x91, 0x38, + 0x4c, 0x7a, 0xa8, 0x32, 0x5c, 0x90, 0xc5, 0x2c, + 0x66, 0x99, 0xd1, 0x29, 0x66, 0x99, 0xd1, 0x29, + 0x66, 0x99, 0xd1, 0x29, 0x66, 0x99, 0xd1, 0x29, + 0x66, 0x99, 0xd1, 0x29, 0x66, 0x99, 0xd1, 0x29, + 0x66, 0x99, 0xd1, 0x29, 0x66, 0x99, 0xd1, 0x29, + 0x66, 0x99, 0xd1, 0x29, 0x66, 0x99, 0xd1, 0x29, + 0x69, 0x96, 0xcd, 0x2a, 0x69, 0x96, 0xcd, 0x2a, + 0x69, 0x96, 0xcd, 0x2a, 0x69, 0x96, 0xcd, 0x2a, + 0x69, 0x96, 0xcd, 0x2a, 0x64, 0x96, 0xcd, 0x2a, + 0x64, 0x96, 0xcd, 0x2a, 0x76, 0x86, 0x9a, 0x35, + 0xaa, 0x5e, 0x14, 0xb0, 0xa9, 0x5b, 0x0e, 0xf9, + 0xb6, 0x7f, 0x3c, 0xff, 0xa8, 0x7b, 0x40, 0xff, + 0xa4, 0x7c, 0x46, 0xff, 0xa9, 0x81, 0x4b, 0xff, + 0xb4, 0x5d, 0x07, 0xff, 0xf0, 0x88, 0x23, 0xff, + 0xf5, 0x7d, 0x09, 0xff, 0xef, 0x8b, 0x2c, 0xff, + 0xb2, 0x5a, 0x04, 0xf9, 0x29, 0x3f, 0x59, 0x50, + 0x2c, 0x47, 0x62, 0x4b, 0x33, 0x50, 0x69, 0x46, + 0x45, 0x56, 0x69, 0x46, 0xaf, 0x58, 0x03, 0xef, + 0xaa, 0x5e, 0x12, 0xb6, 0x88, 0x71, 0x5c, 0x4d, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x67, 0x98, 0xce, 0x2a, + 0x67, 0x98, 0xce, 0x2a, 0x96, 0x68, 0x3d, 0x65, + 0xae, 0x5a, 0x07, 0xe4, 0xb1, 0x6d, 0x25, 0xf8, + 0xb0, 0x78, 0x3a, 0xfa, 0xa6, 0x76, 0x3e, 0xfe, + 0xa9, 0x7d, 0x47, 0xff, 0xb0, 0x82, 0x49, 0xff, + 0xa8, 0x71, 0x33, 0xfa, 0xaa, 0x5c, 0x10, 0xfb, + 0xb8, 0x5c, 0x04, 0xfe, 0xf3, 0x8f, 0x2a, 0xff, + 0xf8, 0x97, 0x26, 0xff, 0xfb, 0xbd, 0x63, 0xff, + 0xcc, 0x84, 0x34, 0xf7, 0x81, 0x51, 0x27, 0x84, + 0x39, 0x53, 0x75, 0x43, 0x3d, 0x5e, 0x83, 0x3e, + 0x48, 0x68, 0x96, 0x38, 0x51, 0x7a, 0xad, 0x32, + 0x62, 0x90, 0xca, 0x2c, 0x65, 0x95, 0xcf, 0x2b, + 0x65, 0x95, 0xcf, 0x2b, 0x65, 0x95, 0xcf, 0x2b, + 0x65, 0x95, 0xcf, 0x2b, 0x68, 0x97, 0xcf, 0x2c, + 0x68, 0x97, 0xcf, 0x2c, 0x68, 0x97, 0xcf, 0x2c, + 0x68, 0x97, 0xcf, 0x2c, 0x68, 0x97, 0xcf, 0x2c, + 0x68, 0x97, 0xcf, 0x2c, 0x68, 0x97, 0xcf, 0x2c, + 0x68, 0x97, 0xcf, 0x2c, 0x68, 0x97, 0xcf, 0x2c, + 0x68, 0x97, 0xcf, 0x2c, 0x68, 0x97, 0xcf, 0x2c, + 0x7f, 0x78, 0x78, 0x43, 0xa8, 0x5e, 0x16, 0xaf, + 0xb4, 0x5e, 0x0b, 0xf9, 0xb9, 0x70, 0x25, 0xf6, + 0xb2, 0x73, 0x32, 0xf7, 0xb8, 0x80, 0x40, 0xfb, + 0xc9, 0x8f, 0x4b, 0xfd, 0xbf, 0x78, 0x30, 0xf6, + 0xb4, 0x5e, 0x0b, 0xfb, 0xad, 0x5a, 0x07, 0xe8, + 0x89, 0x4d, 0x17, 0xa7, 0x83, 0x4e, 0x1b, 0x9c, + 0xae, 0x5a, 0x08, 0xe5, 0xbb, 0x6b, 0x18, 0xf6, + 0xe2, 0xa0, 0x50, 0xfe, 0xde, 0x9e, 0x51, 0xfd, + 0xab, 0x5c, 0x0f, 0xc9, 0x49, 0x67, 0x8e, 0x3b, + 0x51, 0x78, 0xa3, 0x35, 0x63, 0x90, 0xc7, 0x2e, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x66, 0x95, 0xd0, 0x2c, 0x66, 0x95, 0xd0, 0x2c, + 0x64, 0x96, 0xd1, 0x2d, 0x64, 0x96, 0xd1, 0x2d, + 0x86, 0x70, 0x5e, 0x51, 0xad, 0x5d, 0x0c, 0xd0, + 0xab, 0x60, 0x13, 0xf7, 0xab, 0x6f, 0x2c, 0xf7, + 0xa3, 0x6f, 0x34, 0xfd, 0xa6, 0x77, 0x3e, 0xff, + 0xae, 0x7f, 0x43, 0xff, 0xa8, 0x71, 0x31, 0xfc, + 0xa4, 0x61, 0x1a, 0xf7, 0xaa, 0x56, 0x02, 0xfb, + 0xb3, 0x58, 0x01, 0xfc, 0xd9, 0x83, 0x28, 0xfd, + 0xfb, 0xba, 0x61, 0xff, 0xfc, 0xc3, 0x6f, 0xff, + 0xcc, 0x83, 0x31, 0xf6, 0x87, 0x55, 0x26, 0x88, + 0x3d, 0x58, 0x7f, 0x42, 0x47, 0x64, 0x8e, 0x3d, + 0x4e, 0x73, 0xa2, 0x37, 0x5f, 0x8a, 0xbf, 0x30, + 0x64, 0x96, 0xd1, 0x2d, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x67, 0x94, 0xce, 0x2e, + 0x67, 0x94, 0xce, 0x2e, 0x77, 0x86, 0x9e, 0x39, + 0xaa, 0x5e, 0x15, 0xb1, 0xa8, 0x59, 0x0b, 0xf9, + 0xb6, 0x7d, 0x37, 0xff, 0xa8, 0x79, 0x3d, 0xff, + 0xa4, 0x7a, 0x42, 0xff, 0xa9, 0x81, 0x49, 0xff, + 0xb4, 0x5d, 0x07, 0xff, 0xf0, 0x88, 0x23, 0xff, + 0xf5, 0x7d, 0x09, 0xff, 0xef, 0x8b, 0x2c, 0xff, + 0xb2, 0x5a, 0x05, 0xf9, 0x30, 0x45, 0x5d, 0x55, + 0x33, 0x4c, 0x66, 0x50, 0x36, 0x51, 0x70, 0x4b, + 0x3e, 0x5c, 0x7d, 0x45, 0x48, 0x69, 0x8d, 0x3f, + 0x77, 0x6a, 0x5f, 0x53, 0xa7, 0x5d, 0x15, 0xae, + 0x73, 0x89, 0xa5, 0x38, 0x65, 0x96, 0xce, 0x2f, + 0x65, 0x96, 0xce, 0x2f, 0x65, 0x96, 0xce, 0x2f, + 0x65, 0x96, 0xce, 0x2f, 0x65, 0x96, 0xce, 0x2f, + 0x65, 0x96, 0xce, 0x2f, 0x65, 0x96, 0xce, 0x2f, + 0x65, 0x96, 0xce, 0x2f, 0x65, 0x96, 0xce, 0x2f, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x57, 0x7b, 0xa7, 0x23, + 0x46, 0x64, 0x88, 0x2b, 0x8b, 0x56, 0x24, 0x6d, + 0x95, 0x53, 0x16, 0x8a, 0x9b, 0x53, 0x0f, 0xa3, + 0xa5, 0x58, 0x0d, 0xbf, 0xa4, 0x58, 0x0b, 0xc6, + 0x82, 0x48, 0x12, 0x97, 0x50, 0x36, 0x22, 0x70, + 0x92, 0x4c, 0x09, 0xba, 0xcb, 0x78, 0x24, 0xf9, + 0xfb, 0xb7, 0x5f, 0xff, 0xfa, 0xc4, 0x76, 0xff, + 0xb5, 0x60, 0x0c, 0xf8, 0x52, 0x41, 0x33, 0x58, + 0x2d, 0x3d, 0x56, 0x3e, 0x36, 0x48, 0x5f, 0x38, + 0x3e, 0x58, 0x72, 0x31, 0x48, 0x67, 0x8b, 0x2a, + 0x5a, 0x87, 0xb4, 0x22, 0x66, 0x99, 0xcc, 0x1e, + 0x66, 0x99, 0xcc, 0x1e, 0x66, 0x99, 0xcc, 0x1e, + 0x66, 0x99, 0xcc, 0x1e, 0x66, 0x99, 0xcc, 0x1e, + 0x6a, 0x9b, 0xcd, 0x1e, 0x6a, 0x9b, 0xcd, 0x1e, + 0x6a, 0x9b, 0xcd, 0x1e, 0x6a, 0x9b, 0xcd, 0x1e, + 0x6a, 0x9b, 0xcd, 0x1e, 0x6a, 0x9b, 0xcd, 0x1e, + 0x6a, 0x9b, 0xcd, 0x1e, 0x6a, 0x9b, 0xcd, 0x1e, + 0x6a, 0x9b, 0xcd, 0x1e, 0x6a, 0x9b, 0xcd, 0x1e, + 0x55, 0x7f, 0xa2, 0x24, 0x42, 0x67, 0x8b, 0x2a, + 0x5a, 0x58, 0x53, 0x3f, 0x75, 0x51, 0x2e, 0x5d, + 0x7f, 0x50, 0x1f, 0x76, 0x89, 0x4e, 0x15, 0x8f, + 0x8e, 0x4d, 0x0f, 0xa0, 0x6f, 0x43, 0x1a, 0x83, + 0x30, 0x2d, 0x2c, 0x60, 0x18, 0x23, 0x2e, 0x5d, + 0x18, 0x23, 0x2e, 0x5d, 0x1a, 0x26, 0x35, 0x56, + 0x1f, 0x2c, 0x3b, 0x51, 0x63, 0x41, 0x25, 0x6e, + 0xa4, 0x57, 0x0c, 0xbd, 0xb0, 0x59, 0x03, 0xf2, + 0x62, 0x4d, 0x3f, 0x4f, 0x3e, 0x58, 0x77, 0x31, + 0x47, 0x64, 0x8e, 0x2b, 0x5e, 0x83, 0xb6, 0x23, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x67, 0x98, 0xce, 0x1f, + 0x67, 0x98, 0xce, 0x1f, 0x6b, 0x9a, 0xd0, 0x20, + 0x57, 0x78, 0xa7, 0x26, 0x45, 0x68, 0x90, 0x2c, + 0x83, 0x56, 0x30, 0x61, 0x8f, 0x53, 0x1c, 0x82, + 0x97, 0x53, 0x11, 0x9e, 0xa3, 0x57, 0x0d, 0xbd, + 0xa4, 0x58, 0x0c, 0xc7, 0x86, 0x4a, 0x12, 0x9e, + 0x5d, 0x3d, 0x1e, 0x7c, 0x20, 0x27, 0x2f, 0x62, + 0x41, 0x31, 0x25, 0x71, 0xaa, 0x58, 0x05, 0xe2, + 0xcd, 0x82, 0x31, 0xf9, 0xee, 0xb4, 0x66, 0xff, + 0xb2, 0x5a, 0x04, 0xf3, 0x3f, 0x41, 0x4c, 0x47, + 0x35, 0x4c, 0x66, 0x39, 0x3d, 0x56, 0x7a, 0x32, + 0x45, 0x68, 0x90, 0x2c, 0x5c, 0x86, 0xb1, 0x24, + 0x6b, 0x9a, 0xd0, 0x20, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x68, 0x97, 0xd1, 0x21, + 0x68, 0x97, 0xd1, 0x21, 0x61, 0x96, 0xca, 0x22, + 0x50, 0x76, 0xa1, 0x29, 0x84, 0x5c, 0x33, 0x5f, + 0xa3, 0x59, 0x10, 0xaa, 0xab, 0x5b, 0x0c, 0xca, + 0xab, 0x58, 0x06, 0xe3, 0xb1, 0x59, 0x03, 0xf5, + 0xbe, 0x69, 0x17, 0xfd, 0xf7, 0x94, 0x34, 0xff, + 0xf6, 0x91, 0x2f, 0xff, 0xe9, 0x97, 0x43, 0xff, + 0xaf, 0x59, 0x07, 0xf1, 0x23, 0x33, 0x42, 0x50, + 0x25, 0x37, 0x4b, 0x4a, 0x2d, 0x40, 0x57, 0x43, + 0x55, 0x4f, 0x49, 0x4c, 0x65, 0x55, 0x49, 0x4a, + 0x46, 0x67, 0x87, 0x2f, 0x5b, 0x7c, 0xaa, 0x27, + 0x6c, 0x99, 0xd2, 0x21, 0x6c, 0x99, 0xd2, 0x21, + 0x6c, 0x99, 0xd2, 0x21, 0x6c, 0x99, 0xd2, 0x21, + 0x6c, 0x99, 0xd2, 0x21, 0x6c, 0x99, 0xd2, 0x21, + 0x6c, 0x99, 0xd2, 0x21, 0x6c, 0x99, 0xd2, 0x21, + 0x6c, 0x99, 0xd2, 0x21, 0x6c, 0x99, 0xd2, 0x21, + 0x6c, 0x99, 0xd2, 0x21, 0x69, 0x96, 0xcd, 0x22, + 0x69, 0x96, 0xcd, 0x22, 0x69, 0x96, 0xcd, 0x22, + 0x69, 0x96, 0xcd, 0x22, 0x69, 0x96, 0xcd, 0x22, + 0x69, 0x96, 0xcd, 0x22, 0x59, 0x79, 0xa5, 0x28, + 0x4a, 0x69, 0x89, 0x30, 0x8a, 0x56, 0x28, 0x6f, + 0x92, 0x54, 0x19, 0x8c, 0x9a, 0x53, 0x10, 0xa5, + 0xa4, 0x58, 0x0d, 0xc0, 0xa3, 0x58, 0x0c, 0xc7, + 0x82, 0x48, 0x14, 0x9a, 0x4e, 0x38, 0x25, 0x73, + 0x92, 0x4d, 0x0a, 0xbb, 0xc9, 0x73, 0x1c, 0xf9, + 0xfb, 0xbb, 0x67, 0xff, 0xfa, 0xc3, 0x74, 0xff, + 0xb4, 0x5e, 0x09, 0xf8, 0x53, 0x44, 0x39, 0x5a, + 0x30, 0x44, 0x5d, 0x3f, 0x3a, 0x55, 0x6f, 0x39, + 0x41, 0x5f, 0x82, 0x33, 0x4b, 0x73, 0x9c, 0x2c, + 0x60, 0x90, 0xc0, 0x25, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x67, 0x98, 0xce, 0x23, 0x67, 0x98, 0xce, 0x23, + 0x57, 0x7c, 0xa7, 0x29, 0x46, 0x6c, 0x8d, 0x2f, + 0x5c, 0x5a, 0x5d, 0x42, 0x72, 0x55, 0x35, 0x61, + 0x80, 0x52, 0x24, 0x7a, 0x88, 0x4f, 0x18, 0x92, + 0x8d, 0x4f, 0x12, 0xa3, 0x70, 0x46, 0x1e, 0x86, + 0x32, 0x32, 0x33, 0x65, 0x1c, 0x2a, 0x37, 0x61, + 0x1c, 0x2a, 0x37, 0x61, 0x1e, 0x2c, 0x3d, 0x5b, + 0x24, 0x33, 0x45, 0x55, 0x63, 0x46, 0x29, 0x72, + 0xa3, 0x58, 0x0d, 0xbf, 0xb0, 0x59, 0x03, 0xf2, + 0x60, 0x52, 0x46, 0x53, 0x42, 0x5e, 0x7f, 0x36, + 0x4a, 0x6f, 0x8f, 0x30, 0x5f, 0x8c, 0xb2, 0x28, + 0x6a, 0x9a, 0xcf, 0x24, 0x6a, 0x9a, 0xcf, 0x24, + 0x6a, 0x9a, 0xcf, 0x24, 0x6a, 0x9a, 0xcf, 0x24, + 0x6a, 0x9a, 0xcf, 0x24, 0x6a, 0x9a, 0xcf, 0x24, + 0x68, 0x97, 0xd0, 0x24, 0x68, 0x97, 0xd0, 0x24, + 0x68, 0x97, 0xd0, 0x24, 0x68, 0x97, 0xd0, 0x24, + 0x68, 0x97, 0xd0, 0x24, 0x68, 0x97, 0xd0, 0x24, + 0x68, 0x97, 0xd0, 0x24, 0x68, 0x97, 0xd0, 0x24, + 0x68, 0x97, 0xd0, 0x24, 0x68, 0x97, 0xd0, 0x24, + 0x5d, 0x82, 0xae, 0x29, 0x4a, 0x6a, 0x8f, 0x30, + 0x7e, 0x56, 0x37, 0x61, 0x8e, 0x55, 0x1f, 0x83, + 0x95, 0x54, 0x14, 0x9e, 0xa2, 0x57, 0x0e, 0xbc, + 0xa5, 0x57, 0x0c, 0xc9, 0x89, 0x4c, 0x13, 0xa2, + 0x5f, 0x3f, 0x21, 0x80, 0x28, 0x2b, 0x32, 0x66, + 0x3a, 0x31, 0x2a, 0x70, 0xa8, 0x57, 0x07, 0xdc, + 0xc9, 0x7c, 0x2a, 0xf9, 0xf0, 0xb6, 0x6a, 0xff, + 0xb2, 0x5a, 0x05, 0xf5, 0x4d, 0x49, 0x4a, 0x52, + 0x35, 0x4e, 0x6f, 0x3e, 0x3f, 0x5b, 0x7a, 0x38, + 0x47, 0x6b, 0x8e, 0x32, 0x5b, 0x7f, 0xb0, 0x2a, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x66, 0x99, 0xd1, 0x25, + 0x66, 0x99, 0xd1, 0x25, 0x69, 0x96, 0xcd, 0x26, + 0x69, 0x96, 0xcd, 0x26, 0x68, 0x8f, 0xca, 0x27, + 0x53, 0x74, 0xa6, 0x2e, 0x86, 0x5d, 0x3a, 0x62, + 0xa2, 0x59, 0x13, 0xac, 0xab, 0x5b, 0x0d, 0xcb, + 0xab, 0x59, 0x07, 0xe4, 0xb1, 0x59, 0x03, 0xf5, + 0xbe, 0x69, 0x17, 0xfd, 0xf7, 0x94, 0x34, 0xff, + 0xf6, 0x91, 0x2f, 0xff, 0xe9, 0x97, 0x43, 0xff, + 0xaf, 0x5a, 0x08, 0xf1, 0x27, 0x33, 0x48, 0x54, + 0x2a, 0x3a, 0x51, 0x4e, 0x32, 0x44, 0x5d, 0x47, + 0x37, 0x4b, 0x6b, 0x40, 0x3d, 0x57, 0x7b, 0x3a, + 0x4e, 0x65, 0x86, 0x36, 0x5c, 0x79, 0xad, 0x2c, + 0x69, 0x96, 0xcd, 0x26, 0x69, 0x96, 0xcd, 0x26, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x67, 0x98, 0xce, 0x27, 0x67, 0x98, 0xce, 0x27, + 0x68, 0x97, 0xd0, 0x14, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x5b, 0x84, 0xad, 0x19, + 0x4a, 0x62, 0x83, 0x1f, 0x35, 0x50, 0x6b, 0x26, + 0x2e, 0x45, 0x56, 0x2c, 0x29, 0x39, 0x4e, 0x31, + 0x25, 0x33, 0x45, 0x37, 0x1d, 0x2d, 0x3a, 0x3d, + 0x1b, 0x2a, 0x36, 0x42, 0x19, 0x24, 0x2f, 0x46, + 0x22, 0x26, 0x2a, 0x4c, 0xac, 0x57, 0x05, 0xe1, + 0xe1, 0xa4, 0x5a, 0xfe, 0xc3, 0x75, 0x23, 0xf7, + 0xa0, 0x55, 0x0b, 0xb0, 0x24, 0x32, 0x3f, 0x38, + 0x29, 0x39, 0x4e, 0x31, 0x2e, 0x45, 0x56, 0x2c, + 0x35, 0x50, 0x6b, 0x26, 0x4c, 0x66, 0x88, 0x1e, + 0x68, 0x96, 0xc5, 0x16, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x5b, 0x84, 0xad, 0x19, 0x4a, 0x62, 0x83, 0x1f, + 0x35, 0x50, 0x6b, 0x26, 0x2e, 0x45, 0x56, 0x2c, + 0x2d, 0x38, 0x51, 0x32, 0x28, 0x32, 0x44, 0x38, + 0x20, 0x2d, 0x3d, 0x3e, 0x1e, 0x29, 0x39, 0x43, + 0x1d, 0x24, 0x33, 0x46, 0x1b, 0x22, 0x30, 0x49, + 0x1b, 0x22, 0x30, 0x49, 0x1d, 0x24, 0x33, 0x46, + 0x1e, 0x29, 0x39, 0x43, 0x20, 0x2d, 0x3d, 0x3e, + 0x28, 0x31, 0x43, 0x39, 0x34, 0x39, 0x4c, 0x34, + 0x33, 0x44, 0x5a, 0x2d, 0x3c, 0x50, 0x72, 0x26, + 0x52, 0x62, 0x8b, 0x1f, 0x6e, 0x90, 0xc7, 0x17, + 0x6f, 0x99, 0xcc, 0x16, 0x6f, 0x99, 0xcc, 0x16, + 0x6f, 0x99, 0xcc, 0x16, 0x6f, 0x99, 0xcc, 0x16, + 0x6f, 0x99, 0xcc, 0x16, 0x6f, 0x99, 0xcc, 0x16, + 0x6f, 0x99, 0xcc, 0x16, 0x6b, 0x9c, 0xce, 0x17, + 0x6b, 0x9c, 0xce, 0x17, 0x6b, 0x9c, 0xce, 0x17, + 0x6b, 0x9c, 0xce, 0x17, 0x6b, 0x9c, 0xce, 0x17, + 0x6b, 0x9c, 0xce, 0x17, 0x6b, 0x9c, 0xce, 0x17, + 0x6b, 0x9c, 0xce, 0x17, 0x6b, 0x9c, 0xce, 0x17, + 0x5e, 0x84, 0xb3, 0x1b, 0x4d, 0x64, 0x8b, 0x21, + 0x3a, 0x55, 0x75, 0x27, 0x31, 0x48, 0x5e, 0x2e, + 0x2d, 0x3c, 0x55, 0x33, 0x28, 0x35, 0x47, 0x39, + 0x20, 0x30, 0x40, 0x3f, 0x1e, 0x29, 0x38, 0x44, + 0x1c, 0x27, 0x35, 0x47, 0x1b, 0x25, 0x33, 0x4a, + 0x1b, 0x25, 0x33, 0x4a, 0x24, 0x2b, 0x31, 0x4b, + 0x9d, 0x54, 0x0b, 0xb1, 0xb1, 0x58, 0x02, 0xfe, + 0x7c, 0x49, 0x1b, 0x6e, 0x2c, 0x3a, 0x53, 0x34, + 0x30, 0x46, 0x5c, 0x2f, 0x39, 0x52, 0x72, 0x28, + 0x4d, 0x64, 0x8b, 0x21, 0x66, 0x8e, 0xc1, 0x19, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x68, 0x97, 0xcf, 0x18, 0x68, 0x97, 0xcf, 0x18, + 0x6c, 0x9b, 0xd1, 0x18, 0x6c, 0x9b, 0xd1, 0x18, + 0x52, 0x7b, 0x9c, 0x1f, 0x3c, 0x5d, 0x7f, 0x26, + 0x31, 0x4d, 0x63, 0x2e, 0x2c, 0x3f, 0x58, 0x34, + 0x22, 0x38, 0x49, 0x3b, 0xa3, 0x53, 0x07, 0xbe, + 0xde, 0x89, 0x37, 0xfe, 0xef, 0x9b, 0x4a, 0xff, + 0xd1, 0x7c, 0x29, 0xfa, 0xb0, 0x59, 0x05, 0xf1, + 0x5e, 0x3c, 0x1b, 0x70, 0x1c, 0x2b, 0x39, 0x47, + 0x1f, 0x2f, 0x3e, 0x41, 0x27, 0x39, 0x4a, 0x3a, + 0x2d, 0x41, 0x5a, 0x33, 0x33, 0x4f, 0x66, 0x2d, + 0x3c, 0x5d, 0x7f, 0x26, 0x5b, 0x88, 0xb6, 0x1c, + 0x6c, 0x9b, 0xd1, 0x18, 0x6c, 0x9b, 0xd1, 0x18, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x69, 0x9e, 0xd2, 0x19, + 0x69, 0x9e, 0xd2, 0x19, 0x55, 0x7f, 0xb2, 0x1e, + 0x46, 0x63, 0x8d, 0x24, 0x36, 0x55, 0x73, 0x2a, + 0x2f, 0x45, 0x64, 0x30, 0x29, 0x3c, 0x53, 0x37, + 0x21, 0x36, 0x4b, 0x3d, 0x1e, 0x2e, 0x41, 0x42, + 0x1c, 0x2a, 0x3c, 0x48, 0x1e, 0x28, 0x35, 0x4c, + 0x22, 0x28, 0x2f, 0x52, 0xab, 0x57, 0x04, 0xe2, + 0xdf, 0x9e, 0x4e, 0xfd, 0xc1, 0x72, 0x1d, 0xf7, + 0x9f, 0x56, 0x0d, 0xb2, 0x26, 0x3c, 0x4d, 0x3b, + 0x2f, 0x42, 0x55, 0x36, 0x35, 0x4a, 0x64, 0x30, + 0x3e, 0x5d, 0x7c, 0x29, 0x52, 0x70, 0x96, 0x22, + 0x6d, 0x99, 0xcc, 0x1a, 0x6d, 0x99, 0xcc, 0x1a, + 0x6d, 0x99, 0xcc, 0x1a, 0x6d, 0x99, 0xcc, 0x1a, + 0x6d, 0x99, 0xcc, 0x1a, 0x6d, 0x99, 0xcc, 0x1a, + 0x6d, 0x99, 0xcc, 0x1a, 0x6d, 0x99, 0xcc, 0x1a, + 0x6d, 0x99, 0xcc, 0x1a, 0x6d, 0x99, 0xcc, 0x1a, + 0x6d, 0x99, 0xcc, 0x1a, 0x6a, 0x9c, 0xcd, 0x1b, + 0x6a, 0x9c, 0xcd, 0x1b, 0x6a, 0x9c, 0xcd, 0x1b, + 0x6a, 0x9c, 0xcd, 0x1b, 0x6a, 0x9c, 0xcd, 0x1b, + 0x5a, 0x8b, 0xb4, 0x1f, 0x4b, 0x6e, 0x90, 0x25, + 0x3b, 0x5e, 0x76, 0x2b, 0x34, 0x4e, 0x68, 0x31, + 0x2e, 0x45, 0x58, 0x37, 0x26, 0x3f, 0x50, 0x3c, + 0x22, 0x36, 0x45, 0x42, 0x20, 0x32, 0x40, 0x47, + 0x1f, 0x2c, 0x3a, 0x4a, 0x1d, 0x2b, 0x38, 0x4d, + 0x1d, 0x2b, 0x38, 0x4d, 0x1f, 0x2c, 0x3a, 0x4a, + 0x20, 0x32, 0x40, 0x47, 0x22, 0x36, 0x45, 0x42, + 0x25, 0x3a, 0x4f, 0x3d, 0x34, 0x45, 0x53, 0x39, + 0x34, 0x4e, 0x68, 0x31, 0x3b, 0x5e, 0x76, 0x2b, + 0x4d, 0x6a, 0x94, 0x24, 0x64, 0x91, 0xc8, 0x1c, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x67, 0x98, 0xcf, 0x1b, 0x67, 0x98, 0xcf, 0x1b, + 0x5d, 0x88, 0xbb, 0x1e, 0x4b, 0x67, 0x90, 0x25, + 0x3c, 0x5b, 0x7f, 0x2a, 0x35, 0x4a, 0x6a, 0x30, + 0x2e, 0x40, 0x58, 0x37, 0x29, 0x3e, 0x53, 0x3d, + 0x26, 0x36, 0x49, 0x42, 0x23, 0x31, 0x43, 0x48, + 0x22, 0x2c, 0x3d, 0x4b, 0x1d, 0x2a, 0x3a, 0x4e, + 0x1d, 0x2a, 0x3a, 0x4e, 0x20, 0x2d, 0x3a, 0x4d, + 0x98, 0x53, 0x0e, 0xaa, 0xb1, 0x58, 0x02, 0xfe, + 0x83, 0x4e, 0x1b, 0x7c, 0x2d, 0x44, 0x5b, 0x38, + 0x38, 0x4c, 0x6b, 0x32, 0x3e, 0x5a, 0x77, 0x2d, + 0x49, 0x6b, 0x93, 0x26, 0x69, 0x95, 0xca, 0x1d, + 0x6b, 0x9a, 0xd0, 0x1c, 0x6b, 0x9a, 0xd0, 0x1c, + 0x6b, 0x9a, 0xd0, 0x1c, 0x6b, 0x9a, 0xd0, 0x1c, + 0x6b, 0x9a, 0xd0, 0x1c, 0x6b, 0x9a, 0xd0, 0x1c, + 0x6b, 0x9a, 0xd0, 0x1c, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x69, 0x96, 0xd1, 0x1d, 0x69, 0x96, 0xd1, 0x1d, + 0x55, 0x71, 0xa2, 0x24, 0x41, 0x5e, 0x82, 0x2b, + 0x38, 0x4c, 0x70, 0x32, 0x32, 0x44, 0x5f, 0x38, + 0x27, 0x37, 0x4f, 0x40, 0xa3, 0x53, 0x09, 0xbf, + 0xde, 0x89, 0x37, 0xfe, 0xef, 0x9b, 0x4a, 0xff, + 0xd1, 0x7c, 0x29, 0xfa, 0xb0, 0x59, 0x05, 0xf1, + 0x5d, 0x3c, 0x21, 0x72, 0x22, 0x2f, 0x40, 0x4b, + 0x28, 0x36, 0x48, 0x46, 0x2b, 0x3b, 0x4f, 0x40, + 0x32, 0x48, 0x5f, 0x38, 0x3d, 0x51, 0x70, 0x32, + 0x47, 0x64, 0x88, 0x2b, 0x61, 0x87, 0xb4, 0x22, + 0x6c, 0x99, 0xcc, 0x1e, 0x6c, 0x99, 0xcc, 0x1e, + 0x6c, 0x99, 0xcc, 0x1e, 0x6c, 0x99, 0xcc, 0x1e, + 0x6c, 0x99, 0xcc, 0x1e, 0x6c, 0x99, 0xcc, 0x1e, + 0x6c, 0x99, 0xcc, 0x1e, 0x6c, 0x99, 0xcc, 0x1e, + 0x6c, 0x99, 0xcc, 0x1e, 0x6c, 0x99, 0xcc, 0x1e, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x62, 0x89, 0xc4, 0x0d, 0x3f, 0x59, 0x7f, 0x14, + 0x31, 0x44, 0x58, 0x1a, 0x29, 0x31, 0x4a, 0x1f, + 0x23, 0x2a, 0x3f, 0x24, 0x18, 0x25, 0x37, 0x29, + 0x17, 0x23, 0x35, 0x2b, 0x15, 0x25, 0x30, 0x2f, + 0x15, 0x25, 0x2f, 0x30, 0x58, 0x3a, 0x1a, 0x4a, + 0xb1, 0x5a, 0x04, 0xf3, 0x99, 0x51, 0x0b, 0x8d, + 0x18, 0x2b, 0x3e, 0x29, 0x1a, 0x2e, 0x43, 0x26, + 0x26, 0x36, 0x4d, 0x21, 0x31, 0x4e, 0x62, 0x1a, + 0x46, 0x71, 0x9b, 0x12, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x71, 0x9c, 0xd5, 0x0d, 0x71, 0x9c, 0xd5, 0x0d, + 0x71, 0x9c, 0xd5, 0x0d, 0x71, 0x9c, 0xd5, 0x0d, + 0x71, 0x9c, 0xd5, 0x0d, 0x6d, 0x91, 0xc8, 0x0e, + 0x48, 0x61, 0x85, 0x15, 0x38, 0x4b, 0x5e, 0x1b, + 0x2f, 0x37, 0x4f, 0x20, 0x22, 0x30, 0x44, 0x25, + 0x1f, 0x2b, 0x3e, 0x29, 0x1c, 0x28, 0x39, 0x2c, + 0x1b, 0x25, 0x30, 0x2f, 0x1a, 0x25, 0x2f, 0x30, + 0x1a, 0x25, 0x2f, 0x30, 0x1b, 0x25, 0x30, 0x2f, + 0x1c, 0x28, 0x39, 0x2c, 0x1f, 0x2b, 0x3e, 0x29, + 0x21, 0x2e, 0x43, 0x26, 0x2e, 0x36, 0x4d, 0x21, + 0x3a, 0x4e, 0x62, 0x1a, 0x55, 0x71, 0x9b, 0x12, + 0x71, 0x9c, 0xd5, 0x0d, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x6b, 0xa1, 0xc9, 0x0e, + 0x6b, 0xa1, 0xc9, 0x0e, 0x66, 0x99, 0xbb, 0x0f, + 0x45, 0x68, 0x7f, 0x16, 0x36, 0x48, 0x5b, 0x1c, + 0x2e, 0x3d, 0x4d, 0x21, 0x27, 0x34, 0x47, 0x27, + 0x23, 0x2f, 0x41, 0x2b, 0x21, 0x2c, 0x37, 0x2e, + 0x1f, 0x29, 0x34, 0x31, 0x1e, 0x28, 0x33, 0x32, + 0x1e, 0x28, 0x33, 0x32, 0x1f, 0x29, 0x34, 0x31, + 0x21, 0x2c, 0x37, 0x2e, 0x6e, 0x45, 0x1e, 0x4c, + 0x27, 0x34, 0x47, 0x27, 0x2b, 0x3a, 0x50, 0x23, + 0x3f, 0x51, 0x64, 0x1c, 0x59, 0x72, 0x99, 0x14, + 0x73, 0x99, 0xcc, 0x0f, 0x73, 0x99, 0xcc, 0x0f, + 0x73, 0x99, 0xcc, 0x0f, 0x73, 0x99, 0xcc, 0x0f, + 0x73, 0x99, 0xcc, 0x0f, 0x73, 0x99, 0xcc, 0x0f, + 0x73, 0x99, 0xcc, 0x0f, 0x73, 0x99, 0xcc, 0x0f, + 0x73, 0x99, 0xcc, 0x0f, 0x73, 0x99, 0xcc, 0x0f, + 0x6d, 0x9e, 0xce, 0x10, 0x6d, 0x9e, 0xce, 0x10, + 0x6d, 0x9e, 0xce, 0x10, 0x6d, 0x9e, 0xce, 0x10, + 0x6d, 0x9e, 0xce, 0x10, 0x6d, 0x9e, 0xce, 0x10, + 0x6d, 0x9e, 0xce, 0x10, 0x6d, 0x9e, 0xce, 0x10, + 0x6d, 0x9e, 0xce, 0x10, 0x6d, 0x9e, 0xce, 0x10, + 0x5d, 0x86, 0xae, 0x13, 0x44, 0x62, 0x75, 0x1a, + 0x36, 0x45, 0x5c, 0x21, 0x27, 0x3a, 0x4e, 0x27, + 0x46, 0x3c, 0x2e, 0x39, 0xb0, 0x57, 0x00, 0xf4, + 0xb4, 0x5e, 0x09, 0xf8, 0xb0, 0x57, 0x01, 0xe8, + 0x91, 0x4d, 0x09, 0x98, 0x3f, 0x2d, 0x22, 0x4b, + 0x1a, 0x23, 0x30, 0x3a, 0x1c, 0x25, 0x33, 0x36, + 0x1e, 0x28, 0x38, 0x32, 0x22, 0x2e, 0x3f, 0x2c, + 0x28, 0x35, 0x50, 0x26, 0x37, 0x3f, 0x5f, 0x20, + 0x4d, 0x63, 0x90, 0x17, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x68, 0x97, 0xd1, 0x10, + 0x68, 0x97, 0xd1, 0x10, 0x6f, 0x9b, 0xd3, 0x11, + 0x51, 0x73, 0xa2, 0x16, 0x3f, 0x5b, 0x76, 0x1c, + 0x34, 0x43, 0x61, 0x22, 0x27, 0x3a, 0x55, 0x27, + 0x22, 0x34, 0x45, 0x2c, 0x1f, 0x2e, 0x3e, 0x31, + 0x1d, 0x2c, 0x3a, 0x34, 0x1b, 0x25, 0x37, 0x37, + 0x1b, 0x24, 0x36, 0x38, 0x56, 0x38, 0x20, 0x51, + 0xb0, 0x58, 0x02, 0xf4, 0x96, 0x51, 0x0c, 0x91, + 0x20, 0x30, 0x41, 0x2f, 0x23, 0x35, 0x47, 0x2b, + 0x28, 0x3c, 0x57, 0x26, 0x37, 0x47, 0x67, 0x20, + 0x4d, 0x6e, 0x9b, 0x17, 0x6f, 0x9b, 0xd3, 0x11, + 0x6f, 0x9b, 0xd3, 0x11, 0x6f, 0x9b, 0xd3, 0x11, + 0x6f, 0x9b, 0xd3, 0x11, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x6a, 0x9f, 0xca, 0x12, + 0x6a, 0x9f, 0xca, 0x12, 0x5d, 0x93, 0xbb, 0x13, + 0x47, 0x70, 0x8e, 0x19, 0x37, 0x4f, 0x67, 0x20, + 0x29, 0x44, 0x59, 0x25, 0x25, 0x3e, 0x50, 0x29, + 0x21, 0x37, 0x42, 0x2e, 0x1f, 0x34, 0x3e, 0x31, + 0x1e, 0x2d, 0x3c, 0x33, 0x1d, 0x2c, 0x3a, 0x34, + 0x21, 0x2b, 0x3e, 0x35, 0x22, 0x2c, 0x3f, 0x34, + 0x23, 0x33, 0x42, 0x32, 0x25, 0x36, 0x46, 0x2f, + 0x29, 0x3b, 0x4d, 0x2b, 0x2e, 0x43, 0x5d, 0x26, + 0x3f, 0x4f, 0x6f, 0x20, 0x55, 0x74, 0x9f, 0x18, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x70, 0x99, 0xcc, 0x13, 0x70, 0x99, 0xcc, 0x13, + 0x6c, 0x9d, 0xce, 0x13, 0x6c, 0x9d, 0xce, 0x13, + 0x6c, 0x9d, 0xce, 0x13, 0x6c, 0x9d, 0xce, 0x13, + 0x6c, 0x9d, 0xce, 0x13, 0x66, 0x99, 0xbf, 0x14, + 0x4e, 0x75, 0x93, 0x1a, 0x3d, 0x55, 0x6c, 0x21, + 0x2e, 0x49, 0x5d, 0x26, 0x2a, 0x42, 0x55, 0x2a, + 0x26, 0x3c, 0x48, 0x2e, 0x23, 0x33, 0x42, 0x32, + 0x22, 0x31, 0x3f, 0x34, 0x21, 0x30, 0x3e, 0x35, + 0x21, 0x30, 0x3e, 0x35, 0x22, 0x31, 0x3f, 0x34, + 0x23, 0x33, 0x42, 0x32, 0x6c, 0x4a, 0x22, 0x50, + 0x29, 0x41, 0x4d, 0x2b, 0x2e, 0x49, 0x5d, 0x26, + 0x3f, 0x57, 0x6f, 0x20, 0x55, 0x7f, 0x9f, 0x18, + 0x6c, 0x9d, 0xce, 0x13, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x68, 0x97, 0xd0, 0x14, 0x68, 0x97, 0xd0, 0x14, + 0x58, 0x85, 0xb1, 0x17, 0x44, 0x5d, 0x7f, 0x1e, + 0x30, 0x4b, 0x67, 0x25, 0x2e, 0x45, 0x56, 0x2c, + 0x49, 0x40, 0x3b, 0x3c, 0xb0, 0x57, 0x00, 0xf4, + 0xb4, 0x5e, 0x09, 0xf8, 0xb0, 0x58, 0x01, 0xe8, + 0x90, 0x4e, 0x0c, 0x9a, 0x3e, 0x34, 0x29, 0x4e, + 0x1c, 0x2d, 0x39, 0x3e, 0x22, 0x2f, 0x3c, 0x3b, + 0x25, 0x33, 0x46, 0x36, 0x29, 0x39, 0x4e, 0x31, + 0x2f, 0x47, 0x58, 0x2b, 0x37, 0x52, 0x6e, 0x25, + 0x55, 0x7a, 0xa0, 0x1b, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6d, 0x9b, 0xd1, 0x15, 0x6d, 0x9b, 0xd1, 0x15, + 0x6a, 0x9e, 0xd3, 0x15, 0x6a, 0x9e, 0xd3, 0x15, + 0x80, 0x80, 0xbf, 0x03, 0x80, 0x80, 0xbf, 0x03, + 0x80, 0x80, 0xbf, 0x03, 0x80, 0x80, 0xbf, 0x03, + 0x80, 0x80, 0xbf, 0x03, 0x80, 0x80, 0xbf, 0x03, + 0x80, 0x80, 0xbf, 0x03, 0x66, 0x99, 0xcc, 0x04, + 0x66, 0x99, 0xcc, 0x04, 0x66, 0x99, 0xcc, 0x04, + 0x66, 0x99, 0xcc, 0x04, 0x48, 0x48, 0x6d, 0x07, + 0x2e, 0x2e, 0x45, 0x0b, 0x24, 0x24, 0x36, 0x0e, + 0x1c, 0x1c, 0x2a, 0x12, 0x19, 0x19, 0x26, 0x14, + 0x17, 0x17, 0x22, 0x16, 0x18, 0x18, 0x24, 0x15, + 0x5e, 0x35, 0x15, 0x21, 0x1f, 0x1f, 0x2f, 0x10, + 0x27, 0x27, 0x3a, 0x0d, 0x3f, 0x3f, 0x5f, 0x08, + 0x66, 0x66, 0x99, 0x05, 0x66, 0x99, 0xcc, 0x04, + 0x66, 0x99, 0xcc, 0x04, 0x66, 0x99, 0xcc, 0x04, + 0x66, 0x99, 0xcc, 0x04, 0x66, 0x99, 0xcc, 0x04, + 0x66, 0x99, 0xcc, 0x04, 0x66, 0x99, 0xcc, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x80, 0xaa, 0xd5, 0x04, 0x80, 0xaa, 0xd5, 0x04, + 0x48, 0x6d, 0x6d, 0x07, 0x2e, 0x45, 0x45, 0x0b, + 0x24, 0x36, 0x36, 0x0e, 0x1c, 0x2a, 0x2a, 0x12, + 0x19, 0x26, 0x26, 0x14, 0x17, 0x22, 0x22, 0x16, + 0x17, 0x22, 0x2e, 0x16, 0x19, 0x26, 0x33, 0x14, + 0x1e, 0x2d, 0x3c, 0x11, 0x24, 0x36, 0x48, 0x0e, + 0x38, 0x55, 0x71, 0x09, 0x55, 0x7f, 0xaa, 0x06, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x6d, 0x92, 0xdb, 0x05, + 0x6d, 0x92, 0xdb, 0x05, 0x80, 0x9f, 0xbf, 0x06, + 0x80, 0x9f, 0xbf, 0x06, 0x80, 0x9f, 0xbf, 0x06, + 0x80, 0x9f, 0xbf, 0x06, 0x80, 0x9f, 0xbf, 0x06, + 0x80, 0x9f, 0xbf, 0x06, 0x80, 0x9f, 0xbf, 0x06, + 0x55, 0x71, 0x71, 0x09, 0x3a, 0x4e, 0x4e, 0x0d, + 0x2f, 0x3f, 0x3f, 0x10, 0x26, 0x33, 0x33, 0x14, + 0x22, 0x2e, 0x2e, 0x16, 0x1f, 0x2a, 0x2a, 0x18, + 0x21, 0x2c, 0x2c, 0x17, 0x24, 0x30, 0x30, 0x15, + 0x2a, 0x38, 0x38, 0x12, 0x33, 0x44, 0x44, 0x0f, + 0x4c, 0x66, 0x66, 0x0a, 0x6d, 0x91, 0x91, 0x07, + 0x80, 0x9f, 0xbf, 0x06, 0x80, 0x9f, 0xbf, 0x06, + 0x80, 0x9f, 0xbf, 0x06, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x71, 0xaa, 0xc6, 0x07, + 0x71, 0xaa, 0xc6, 0x07, 0x5f, 0x9f, 0x9f, 0x08, + 0x33, 0x55, 0x55, 0x0f, 0x24, 0x3c, 0x3c, 0x15, + 0x1d, 0x27, 0x3a, 0x1a, 0x19, 0x22, 0x2a, 0x1e, + 0x16, 0x1e, 0x25, 0x22, 0x15, 0x1c, 0x23, 0x24, + 0x14, 0x1b, 0x22, 0x25, 0x14, 0x1b, 0x22, 0x25, + 0x15, 0x1c, 0x23, 0x24, 0x17, 0x1e, 0x26, 0x21, + 0x19, 0x22, 0x2a, 0x1e, 0x1e, 0x28, 0x3d, 0x19, + 0x28, 0x35, 0x50, 0x13, 0x45, 0x5c, 0x8b, 0x0b, + 0x66, 0x99, 0xcc, 0x07, 0x66, 0x99, 0xcc, 0x07, + 0x66, 0x99, 0xcc, 0x07, 0x66, 0x99, 0xcc, 0x07, + 0x66, 0x99, 0xcc, 0x07, 0x66, 0x99, 0xcc, 0x07, + 0x66, 0x99, 0xcc, 0x07, 0x66, 0x99, 0xcc, 0x07, + 0x66, 0x99, 0xcc, 0x07, 0x66, 0x99, 0xcc, 0x07, + 0x74, 0xa2, 0xd1, 0x08, 0x74, 0xa2, 0xd1, 0x08, + 0x74, 0xa2, 0xd1, 0x08, 0x74, 0xa2, 0xd1, 0x08, + 0x74, 0xa2, 0xd1, 0x08, 0x74, 0xa2, 0xd1, 0x08, + 0x74, 0xa2, 0xd1, 0x08, 0x74, 0xa2, 0xd1, 0x08, + 0x74, 0xa2, 0xd1, 0x08, 0x74, 0xa2, 0xd1, 0x08, + 0x74, 0xa2, 0xd1, 0x08, 0x71, 0x8d, 0xc6, 0x09, + 0x55, 0x6a, 0x94, 0x0c, 0x38, 0x46, 0x63, 0x12, + 0x2c, 0x37, 0x4d, 0x17, 0x27, 0x31, 0x3a, 0x1a, + 0x23, 0x2b, 0x34, 0x1d, 0x20, 0x29, 0x31, 0x1f, + 0x1f, 0x27, 0x2f, 0x20, 0x1f, 0x27, 0x2f, 0x20, + 0x51, 0x38, 0x20, 0x2c, 0x24, 0x2d, 0x36, 0x1c, + 0x28, 0x33, 0x47, 0x19, 0x33, 0x3f, 0x66, 0x14, + 0x48, 0x5b, 0x91, 0x0e, 0x66, 0x7f, 0xcc, 0x0a, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x6a, 0x95, 0xd5, 0x09, 0x6a, 0x95, 0xd5, 0x09, + 0x76, 0x9d, 0xd8, 0x0a, 0x76, 0x9d, 0xd8, 0x0a, + 0x62, 0x75, 0x9c, 0x0d, 0x4b, 0x5a, 0x78, 0x11, + 0x3f, 0x4c, 0x66, 0x14, 0x37, 0x42, 0x58, 0x17, + 0x33, 0x3d, 0x47, 0x19, 0x2f, 0x38, 0x42, 0x1b, + 0x31, 0x3a, 0x44, 0x1a, 0x35, 0x3f, 0x55, 0x18, + 0x39, 0x45, 0x5c, 0x16, 0x43, 0x50, 0x6b, 0x13, + 0x5b, 0x6d, 0x91, 0x0e, 0x73, 0x8b, 0xb9, 0x0b, + 0x76, 0x9d, 0xd8, 0x0a, 0x76, 0x9d, 0xd8, 0x0a, + 0x76, 0x9d, 0xd8, 0x0a, 0x76, 0x9d, 0xd8, 0x0a, + 0x76, 0x9d, 0xd8, 0x0a, 0x76, 0x9d, 0xd8, 0x0a, + 0x76, 0x9d, 0xd8, 0x0a, 0x76, 0x9d, 0xd8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x6d, 0xa4, 0xc8, 0x0a, 0x6d, 0xa4, 0xc8, 0x0a, + 0x4e, 0x75, 0x9c, 0x0d, 0x3f, 0x5f, 0x7f, 0x10, + 0x33, 0x4c, 0x66, 0x14, 0x2e, 0x45, 0x5c, 0x16, + 0x28, 0x3d, 0x47, 0x19, 0x25, 0x38, 0x42, 0x1b, + 0x27, 0x3a, 0x44, 0x1a, 0x33, 0x47, 0x51, 0x19, + 0x39, 0x51, 0x68, 0x16, 0x3f, 0x59, 0x72, 0x14, + 0x4f, 0x6f, 0x8f, 0x10, 0x6a, 0x94, 0xbf, 0x0c, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x66, 0x99, 0xcc, 0x0b, + 0x66, 0x99, 0xcc, 0x0b, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x70, 0x9f, 0xcf, 0x0c, 0x62, 0x89, 0xc4, 0x0d, + 0x3f, 0x59, 0x7f, 0x14, 0x33, 0x47, 0x5b, 0x19, + 0x29, 0x31, 0x4a, 0x1f, 0x24, 0x2b, 0x41, 0x23, + 0x1a, 0x27, 0x3a, 0x27, 0x18, 0x25, 0x37, 0x29, + 0x18, 0x24, 0x36, 0x2a, 0x18, 0x24, 0x36, 0x2a, + 0x18, 0x25, 0x37, 0x29, 0x1a, 0x28, 0x3c, 0x26, + 0x24, 0x2b, 0x41, 0x23, 0x2a, 0x33, 0x4c, 0x1e, + 0x37, 0x4d, 0x6e, 0x17, 0x4f, 0x6f, 0x9f, 0x10, + 0x70, 0x9f, 0xcf, 0x0c, 0x70, 0x9f, 0xcf, 0x0c, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x69, 0x96, 0xd2, 0x0d, 0x69, 0x96, 0xd2, 0x0d, + 0x00, 0x00, 0x28, 0x75, 0x75, 0x61, 0x79, 0x29, + 0x6c, 0x65, 0x62, 0x6c, 0x2d, 0x64, 0x69, 0x61, + 0x6c, 0x6f, 0x67, 0x2f, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 +} }; + +static GStaticResource static_resource = { lebl_dialog_resource_data.data, sizeof (lebl_dialog_resource_data.data), NULL, NULL, NULL }; +extern GResource *lebl_dialog_get_resource (void); +GResource *lebl_dialog_get_resource (void) +{ + return g_static_resource_get_resource (&static_resource); +} +/* + If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and + destructors, in a sane way, including e.g. on library unload. If not you're on + your own. + + Some compilers need #pragma to handle this, which does not work with macros, + so the way you need to use this is (for constructors): + + #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA + #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) + #endif + G_DEFINE_CONSTRUCTOR(my_constructor) + static void my_constructor(void) { + ... + } + +*/ + +#ifndef __GTK_DOC_IGNORE__ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); +#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); + +#elif defined (_MSC_VER) && (_MSC_VER >= 1500) +/* Visual studio 2008 and later has _Pragma */ + +#define G_HAS_CONSTRUCTORS 1 + +/* We do some weird things to avoid the constructors being optimized + * away on VS2015 if WholeProgramOptimization is enabled. First we + * make a reference to the array from the wrapper to make sure its + * references. Then we use a pragma to make sure the wrapper function + * symbol is always included at the link stage. Also, the symbols + * need to be extern (but not dllexport), even though they are not + * really used from another object file. + */ + +/* We need to account for differences between the mangling of symbols + * for Win32 (x86) and x64 programs, as symbols on Win32 are prefixed + * with an underscore but symbols on x64 are not. + */ +#ifdef _WIN64 +#define G_MSVC_SYMBOL_PREFIX "" +#else +#define G_MSVC_SYMBOL_PREFIX "_" +#endif + +#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX) +#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX) + +#define G_MSVC_CTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; + +#define G_MSVC_DTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined (_MSC_VER) + +#define G_HAS_CONSTRUCTORS 1 + +/* Pre Visual studio 2008 must use #pragma section */ +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined(__SUNPRO_C) + +/* This is not tested, but i believe it should work, based on: + * https://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c + */ + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + init(_func) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + fini(_func) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); + +#else + +/* constructors not supported for this compiler */ + +#endif + +#endif /* __GTK_DOC_IGNORE__ */ + +#ifdef G_HAS_CONSTRUCTORS + +#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(resource_constructor) +#endif +G_DEFINE_CONSTRUCTOR(resource_constructor) +#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(resource_destructor) +#endif +G_DEFINE_DESTRUCTOR(resource_destructor) + +#else +#warning "Constructor not supported on this compiler, linking in resources will not work" +#endif + +static void resource_constructor (void) +{ + g_static_resource_init (&static_resource); +} + +static void resource_destructor (void) +{ + g_static_resource_fini (&static_resource); +} diff --git a/app/dialogs/module-dialog.c b/app/dialogs/module-dialog.c new file mode 100644 index 0000000..be1a438 --- /dev/null +++ b/app/dialogs/module-dialog.c @@ -0,0 +1,526 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * module-dialog.c + * (C) 1999 Austin Donnelly + * (C) 2008 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmodule/gimpmodule.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimp-modules.h" + +#include "widgets/gimphelp-ids.h" + +#include "module-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_REFRESH 1 + +enum +{ + COLUMN_NAME, + COLUMN_ENABLED, + COLUMN_MODULE, + N_COLUMNS +}; + +enum +{ + INFO_AUTHOR, + INFO_VERSION, + INFO_DATE, + INFO_COPYRIGHT, + INFO_LOCATION, + N_INFOS +}; + +typedef struct _ModuleDialog ModuleDialog; + +struct _ModuleDialog +{ + Gimp *gimp; + + GimpModule *selected; + GtkListStore *list; + + GtkWidget *hint; + GtkWidget *table; + GtkWidget *label[N_INFOS]; + GtkWidget *error_box; + GtkWidget *error_label; +}; + + +/* local function prototypes */ + +static void dialog_response (GtkWidget *widget, + gint response_id, + ModuleDialog *private); +static void dialog_destroy_callback (GtkWidget *widget, + ModuleDialog *private); +static void dialog_select_callback (GtkTreeSelection *sel, + ModuleDialog *private); +static void dialog_enabled_toggled (GtkCellRendererToggle *celltoggle, + const gchar *path_string, + ModuleDialog *private); +static void make_list_item (gpointer data, + gpointer user_data); +static void dialog_info_add (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private); +static void dialog_info_remove (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private); +static void dialog_info_update (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private); +static void dialog_info_init (ModuleDialog *private, + GtkWidget *table); + + +/* public functions */ + +GtkWidget * +module_dialog_new (Gimp *gimp) +{ + ModuleDialog *private; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *sw; + GtkWidget *view; + GtkWidget *image; + GtkTreeSelection *sel; + GtkTreeIter iter; + GtkTreeViewColumn *col; + GtkCellRenderer *rend; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + private = g_slice_new0 (ModuleDialog); + + private->gimp = gimp; + + dialog = gimp_dialog_new (_("Module Manager"), + "gimp-modules", NULL, 0, + gimp_standard_help_func, GIMP_HELP_MODULE_DIALOG, + + _("_Refresh"), RESPONSE_REFRESH, + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_CLOSE, + RESPONSE_REFRESH, + -1); + + g_signal_connect (dialog, "response", + G_CALLBACK (dialog_response), + private); + + 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); + + private->hint = gimp_hint_box_new (_("You will have to restart GIMP " + "for the changes to take effect.")); + gtk_box_pack_start (GTK_BOX (vbox), private->hint, FALSE, FALSE, 0); + + if (gimp->write_modulerc) + gtk_widget_show (private->hint); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); + gtk_widget_set_size_request (sw, 124, 100); + gtk_widget_show (sw); + + private->list = gtk_list_store_new (N_COLUMNS, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + GIMP_TYPE_MODULE); + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (private->list)); + g_object_unref (private->list); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); + + g_list_foreach (gimp->module_db->modules, make_list_item, private); + + rend = gtk_cell_renderer_toggle_new (); + + g_signal_connect (rend, "toggled", + G_CALLBACK (dialog_enabled_toggled), + private); + + col = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (col, rend, FALSE); + gtk_tree_view_column_add_attribute (col, rend, "active", COLUMN_ENABLED); + + gtk_tree_view_append_column (GTK_TREE_VIEW (view), col); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 1, + _("Module"), + gtk_cell_renderer_text_new (), + "text", COLUMN_NAME, + NULL); + + gtk_container_add (GTK_CONTAINER (sw), view); + gtk_widget_show (view); + + private->table = gtk_table_new (2, N_INFOS, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (private->table), 6); + gtk_box_pack_start (GTK_BOX (vbox), private->table, FALSE, FALSE, 0); + gtk_widget_show (private->table); + + private->error_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), private->error_box, FALSE, FALSE, 0); + + image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_WARNING, + GTK_ICON_SIZE_BUTTON); + gtk_box_pack_start (GTK_BOX (private->error_box), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + private->error_label = gtk_label_new (NULL); + gtk_label_set_xalign (GTK_LABEL (private->error_label), 0.0); + gtk_box_pack_start (GTK_BOX (private->error_box), + private->error_label, TRUE, TRUE, 0); + gtk_widget_show (private->error_label); + + dialog_info_init (private, private->table); + + dialog_info_update (gimp->module_db, private->selected, private); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + + g_signal_connect (sel, "changed", + G_CALLBACK (dialog_select_callback), + private); + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (private->list), &iter)) + gtk_tree_selection_select_iter (sel, &iter); + + /* hook the GimpModuleDB signals so we can refresh the display + * appropriately. + */ + g_signal_connect (gimp->module_db, "add", + G_CALLBACK (dialog_info_add), + private); + g_signal_connect (gimp->module_db, "remove", + G_CALLBACK (dialog_info_remove), + private); + g_signal_connect (gimp->module_db, "module-modified", + G_CALLBACK (dialog_info_update), + private); + + g_signal_connect (dialog, "destroy", + G_CALLBACK (dialog_destroy_callback), + private); + + return dialog; +} + + +/* private functions */ + +static void +dialog_response (GtkWidget *widget, + gint response_id, + ModuleDialog *private) +{ + if (response_id == RESPONSE_REFRESH) + gimp_modules_refresh (private->gimp); + else + gtk_widget_destroy (widget); +} + +static void +dialog_destroy_callback (GtkWidget *widget, + ModuleDialog *private) +{ + g_signal_handlers_disconnect_by_func (private->gimp->module_db, + dialog_info_add, + private); + g_signal_handlers_disconnect_by_func (private->gimp->module_db, + dialog_info_remove, + private); + g_signal_handlers_disconnect_by_func (private->gimp->module_db, + dialog_info_update, + private); + + g_slice_free (ModuleDialog, private); +} + +static void +dialog_select_callback (GtkTreeSelection *sel, + ModuleDialog *private) +{ + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (sel, NULL, &iter)) + { + GimpModule *module; + + gtk_tree_model_get (GTK_TREE_MODEL (private->list), &iter, + COLUMN_MODULE, &module, -1); + + if (module) + g_object_unref (module); + + if (private->selected == module) + return; + + private->selected = module; + + dialog_info_update (private->gimp->module_db, private->selected, private); + } +} + +static void +dialog_enabled_toggled (GtkCellRendererToggle *celltoggle, + const gchar *path_string, + ModuleDialog *private) +{ + GtkTreePath *path; + GtkTreeIter iter; + GimpModule *module = NULL; + + path = gtk_tree_path_new_from_string (path_string); + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (private->list), &iter, path)) + { + g_warning ("%s: bad tree path?", G_STRFUNC); + return; + } + + gtk_tree_path_free (path); + + gtk_tree_model_get (GTK_TREE_MODEL (private->list), &iter, + COLUMN_MODULE, &module, + -1); + + if (module) + { + gimp_module_set_load_inhibit (module, ! module->load_inhibit); + g_object_unref (module); + + private->gimp->write_modulerc = TRUE; + gtk_widget_show (private->hint); + } +} + +static void +dialog_list_item_update (ModuleDialog *private, + GtkTreeIter *iter, + GimpModule *module) +{ + gtk_list_store_set (private->list, iter, + COLUMN_NAME, (module->info ? + gettext (module->info->purpose) : + gimp_filename_to_utf8 (module->filename)), + COLUMN_ENABLED, ! module->load_inhibit, + COLUMN_MODULE, module, + -1); +} + +static void +make_list_item (gpointer data, + gpointer user_data) +{ + GimpModule *module = data; + ModuleDialog *private = user_data; + GtkTreeIter iter; + + if (! private->selected) + private->selected = module; + + gtk_list_store_append (private->list, &iter); + + dialog_list_item_update (private, &iter, module); +} + +static void +dialog_info_add (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private) +{ + make_list_item (module, private); +} + +static void +dialog_info_remove (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private) +{ + GtkTreeIter iter; + + /* FIXME: Use gtk_list_store_foreach_remove when it becomes available */ + + if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (private->list), &iter)) + return; + + do + { + GimpModule *this; + + gtk_tree_model_get (GTK_TREE_MODEL (private->list), &iter, + COLUMN_MODULE, &this, + -1); + + if (this) + g_object_unref (this); + + if (this == module) + { + gtk_list_store_remove (private->list, &iter); + return; + } + } + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (private->list), &iter)); + + g_warning ("%s: Tried to remove a module not in the dialog's list.", + G_STRFUNC); +} + +static void +dialog_info_update (GimpModuleDB *db, + GimpModule *module, + ModuleDialog *private) +{ + GtkTreeModel *model = GTK_TREE_MODEL (private->list); + GtkTreeIter iter; + const gchar *text[N_INFOS] = { NULL, }; + gchar *location = NULL; + gboolean iter_valid; + gint i; + gboolean show_error; + + for (iter_valid = gtk_tree_model_get_iter_first (model, &iter); + iter_valid; + iter_valid = gtk_tree_model_iter_next (model, &iter)) + { + GimpModule *this; + + gtk_tree_model_get (model, &iter, + COLUMN_MODULE, &this, + -1); + if (this) + g_object_unref (this); + + if (this == module) + break; + } + + if (iter_valid) + dialog_list_item_update (private, &iter, module); + + /* only update the info if we're actually showing it */ + if (module != private->selected) + return; + + if (! module) + { + for (i = 0; i < N_INFOS; i++) + gtk_label_set_text (GTK_LABEL (private->label[i]), NULL); + + gtk_label_set_text (GTK_LABEL (private->error_label), NULL); + gtk_widget_hide (private->error_box); + + return; + } + + if (module->on_disk) + location = g_filename_display_name (module->filename); + + if (module->info) + { + text[INFO_AUTHOR] = module->info->author; + text[INFO_VERSION] = module->info->version; + text[INFO_DATE] = module->info->date; + text[INFO_COPYRIGHT] = module->info->copyright; + text[INFO_LOCATION] = module->on_disk ? location : _("Only in memory"); + } + else + { + text[INFO_LOCATION] = (module->on_disk ? + location : _("No longer available")); + } + + for (i = 0; i < N_INFOS; i++) + gtk_label_set_text (GTK_LABEL (private->label[i]), + text[i] ? text[i] : "--"); + g_free (location); + + /* Show errors */ + show_error = (module->state == GIMP_MODULE_STATE_ERROR && + module->last_module_error); + gtk_label_set_text (GTK_LABEL (private->error_label), + show_error ? module->last_module_error : NULL); + gtk_widget_set_visible (private->error_box, show_error); +} + +static void +dialog_info_init (ModuleDialog *private, + GtkWidget *table) +{ + GtkWidget *label; + gint i; + + const gchar * const text[] = + { + N_("Author:"), + N_("Version:"), + N_("Date:"), + N_("Copyright:"), + N_("Location:") + }; + + for (i = 0; i < G_N_ELEMENTS (text); i++) + { + label = gtk_label_new (gettext (text[i])); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, i, i + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2); + gtk_widget_show (label); + + private->label[i] = gtk_label_new (""); + gtk_label_set_xalign (GTK_LABEL (private->label[i]), 0.0); + gtk_label_set_ellipsize (GTK_LABEL (private->label[i]), + PANGO_ELLIPSIZE_END); + gtk_table_attach (GTK_TABLE (private->table), private->label[i], + 1, 2, i, i + 1, + GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2); + gtk_widget_show (private->label[i]); + } +} diff --git a/app/dialogs/module-dialog.h b/app/dialogs/module-dialog.h new file mode 100644 index 0000000..17aaef0 --- /dev/null +++ b/app/dialogs/module-dialog.h @@ -0,0 +1,27 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * module-dialog.h + * (C) 1999 Austin Donnelly + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MODULE_DIALOG_H__ + + +GtkWidget * module_dialog_new (Gimp *gimp); + + +#endif /* __MODULE_DIALOG_H__ */ diff --git a/app/dialogs/palette-import-dialog.c b/app/dialogs/palette-import-dialog.c new file mode 100644 index 0000000..d922c2f --- /dev/null +++ b/app/dialogs/palette-import-dialog.c @@ -0,0 +1,886 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpdatafactory.h" +#include "core/gimpdrawable.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimppalette.h" +#include "core/gimppalette-import.h" + +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimpdnd.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpview.h" +#include "widgets/gimpwidgets-utils.h" + +#include "palette-import-dialog.h" + +#include "gimp-intl.h" + + +typedef enum +{ + GRADIENT_IMPORT, + IMAGE_IMPORT, + FILE_IMPORT +} ImportType; + + +typedef struct _ImportDialog ImportDialog; + +struct _ImportDialog +{ + GtkWidget *dialog; + + ImportType import_type; + GimpContext *context; + GimpImage *image; + + GimpPalette *palette; + + GtkWidget *gradient_radio; + GtkWidget *image_radio; + GtkWidget *file_radio; + + GtkWidget *gradient_combo; + GtkWidget *image_combo; + GtkWidget *file_chooser; + + GtkWidget *sample_merged_toggle; + GtkWidget *selection_only_toggle; + + GtkWidget *entry; + GtkAdjustment *num_colors; + GtkAdjustment *columns; + GtkAdjustment *threshold; + + GtkWidget *preview; + GtkWidget *no_colors_label; +}; + + +static void palette_import_free (ImportDialog *private); +static void palette_import_response (GtkWidget *dialog, + gint response_id, + ImportDialog *private); +static void palette_import_gradient_changed (GimpContext *context, + GimpGradient *gradient, + ImportDialog *private); +static void palette_import_image_changed (GimpContext *context, + GimpImage *image, + ImportDialog *private); +static void palette_import_layer_changed (GimpImage *image, + ImportDialog *private); +static void palette_import_mask_changed (GimpImage *image, + ImportDialog *private); +static void palette_import_filename_changed (GtkFileChooser *button, + ImportDialog *private); +static void import_dialog_drop_callback (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data); +static void palette_import_grad_callback (GtkWidget *widget, + ImportDialog *private); +static void palette_import_image_callback (GtkWidget *widget, + ImportDialog *private); +static void palette_import_file_callback (GtkWidget *widget, + ImportDialog *private); +static void palette_import_columns_changed (GtkAdjustment *adjustment, + ImportDialog *private); +static void palette_import_image_add (GimpContainer *container, + GimpImage *image, + ImportDialog *private); +static void palette_import_image_remove (GimpContainer *container, + GimpImage *image, + ImportDialog *private); +static void palette_import_make_palette (ImportDialog *private); + + +/* public functions */ + +GtkWidget * +palette_import_dialog_new (GimpContext *context) +{ + ImportDialog *private; + GimpGradient *gradient; + GtkWidget *dialog; + GtkWidget *main_hbox; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *abox; + GtkSizeGroup *size_group; + GSList *group = NULL; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + gradient = gimp_context_get_gradient (context); + + private = g_slice_new0 (ImportDialog); + + private->import_type = GRADIENT_IMPORT; + private->context = gimp_context_new (context->gimp, "Palette Import", + context); + + dialog = private->dialog = + gimp_dialog_new (_("Import a New Palette"), + "gimp-palette-import", NULL, 0, + gimp_standard_help_func, + GIMP_HELP_PALETTE_IMPORT, + + _("_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); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) palette_import_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (palette_import_response), + private); + + gimp_dnd_viewable_dest_add (dialog, + GIMP_TYPE_GRADIENT, + import_dialog_drop_callback, + private); + gimp_dnd_viewable_dest_add (dialog, + GIMP_TYPE_IMAGE, + import_dialog_drop_callback, + private); + + 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 (dialog))), + 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, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + + /* The "Source" frame */ + + frame = gimp_frame_new (_("Select Source")); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + 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); + gtk_widget_show (table); + + private->gradient_radio = + gtk_radio_button_new_with_mnemonic (group, _("_Gradient")); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (private->gradient_radio)); + gtk_table_attach (GTK_TABLE (table), private->gradient_radio, + 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (private->gradient_radio); + + g_signal_connect (private->gradient_radio, "toggled", + G_CALLBACK (palette_import_grad_callback), + private); + + private->image_radio = + gtk_radio_button_new_with_mnemonic (group, _("I_mage")); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (private->image_radio)); + gtk_table_attach (GTK_TABLE (table), private->image_radio, + 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (private->image_radio); + + g_signal_connect (private->image_radio, "toggled", + G_CALLBACK (palette_import_image_callback), + private); + + gtk_widget_set_sensitive (private->image_radio, + ! gimp_container_is_empty (context->gimp->images)); + + private->sample_merged_toggle = + gtk_check_button_new_with_mnemonic (_("Sample _Merged")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->sample_merged_toggle), + TRUE); + gtk_table_attach (GTK_TABLE (table), private->sample_merged_toggle, + 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (private->sample_merged_toggle); + + g_signal_connect_swapped (private->sample_merged_toggle, "toggled", + G_CALLBACK (palette_import_make_palette), + private); + + private->selection_only_toggle = + gtk_check_button_new_with_mnemonic (_("_Selected Pixels only")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->selection_only_toggle), + FALSE); + gtk_table_attach (GTK_TABLE (table), private->selection_only_toggle, + 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (private->selection_only_toggle); + + g_signal_connect_swapped (private->selection_only_toggle, "toggled", + G_CALLBACK (palette_import_make_palette), + private); + + private->file_radio = + gtk_radio_button_new_with_mnemonic (group, _("Palette _file")); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (private->image_radio)); + gtk_table_attach (GTK_TABLE (table), private->file_radio, + 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (private->file_radio); + + g_signal_connect (private->file_radio, "toggled", + G_CALLBACK (palette_import_file_callback), + private); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); + + /* The gradient menu */ + private->gradient_combo = + gimp_container_combo_box_new (gimp_data_factory_get_container (context->gimp->gradient_factory), + private->context, 24, 1); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + NULL, 0.0, 0.5, private->gradient_combo, 1, FALSE); + gtk_size_group_add_widget (size_group, private->gradient_combo); + + /* The image menu */ + private->image_combo = + gimp_container_combo_box_new (context->gimp->images, private->context, + 24, 1); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + NULL, 0.0, 0.5, private->image_combo, 1, FALSE); + gtk_size_group_add_widget (size_group, private->image_combo); + + /* Palette file name entry */ + private->file_chooser = gtk_file_chooser_button_new (_("Select Palette File"), + GTK_FILE_CHOOSER_ACTION_OPEN); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 4, + NULL, 0.0, 0.5, private->file_chooser, 1, FALSE); + gtk_size_group_add_widget (size_group, private->file_chooser); + + g_object_unref (size_group); + + + /* The "Import" frame */ + + frame = gimp_frame_new (_("Import Options")); + gtk_box_pack_start (GTK_BOX (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), 6); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + /* The source's name */ + private->entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (private->entry), + gradient ? + gimp_object_get_name (gradient) : _("New import")); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("Palette _name:"), 0.0, 0.5, + private->entry, 2, FALSE); + + /* The # of colors */ + private->num_colors = + GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 0, 1, + _("N_umber of colors:"), -1, 5, + 256, 2, 10000, 1, 10, 0, + TRUE, 0.0, 0.0, + NULL, NULL)); + gimp_scale_entry_set_logarithmic (GTK_OBJECT (private->num_colors), TRUE); + + g_signal_connect_swapped (private->num_colors, + "value-changed", + G_CALLBACK (palette_import_make_palette), + private); + + /* The columns */ + private->columns = + GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 0, 2, + _("C_olumns:"), -1, 5, + 16, 0, 64, 1, 8, 0, + TRUE, 0.0, 0.0, + NULL, NULL)); + + g_signal_connect (private->columns, "value-changed", + G_CALLBACK (palette_import_columns_changed), + private); + + /* The interval */ + private->threshold = + GTK_ADJUSTMENT (gimp_scale_entry_new (GTK_TABLE (table), 0, 3, + _("I_nterval:"), -1, 5, + 1, 1, 128, 1, 8, 0, + TRUE, 0.0, 0.0, + NULL, NULL)); + + g_signal_connect_swapped (private->threshold, "value-changed", + G_CALLBACK (palette_import_make_palette), + private); + + + /* The "Preview" frame */ + frame = gimp_frame_new (_("Preview")); + gtk_box_pack_start (GTK_BOX (main_hbox), 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); + + 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); + + private->preview = gimp_view_new_full_by_types (private->context, + GIMP_TYPE_VIEW, + GIMP_TYPE_PALETTE, + 192, 192, 1, + TRUE, FALSE, FALSE); + gtk_container_add (GTK_CONTAINER (abox), private->preview); + gtk_widget_show (private->preview); + + private->no_colors_label = + gtk_label_new (_("The selected source contains no colors.")); + gtk_widget_set_size_request (private->no_colors_label, 194, -1); + gtk_label_set_line_wrap (GTK_LABEL (private->no_colors_label), TRUE); + gimp_label_set_attributes (GTK_LABEL (private->no_colors_label), + PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC, + -1); + gtk_box_pack_start (GTK_BOX (vbox), private->no_colors_label, FALSE, FALSE, 0); + gtk_widget_show (private->no_colors_label); + + + /* keep the dialog up-to-date */ + + g_signal_connect (context->gimp->images, "add", + G_CALLBACK (palette_import_image_add), + private); + g_signal_connect (context->gimp->images, "remove", + G_CALLBACK (palette_import_image_remove), + private); + + g_signal_connect (private->context, "gradient-changed", + G_CALLBACK (palette_import_gradient_changed), + private); + g_signal_connect (private->context, "image-changed", + G_CALLBACK (palette_import_image_changed), + private); + g_signal_connect (private->file_chooser, "selection-changed", + G_CALLBACK (palette_import_filename_changed), + private); + + palette_import_grad_callback (private->gradient_radio, private); + + return dialog; +} + + +/* private functions */ + +static void +palette_import_free (ImportDialog *private) +{ + Gimp *gimp = private->context->gimp; + + g_signal_handlers_disconnect_by_func (gimp->images, + palette_import_image_add, + private); + g_signal_handlers_disconnect_by_func (gimp->images, + palette_import_image_remove, + private); + + if (private->palette) + g_object_unref (private->palette); + + g_object_unref (private->context); + + g_slice_free (ImportDialog, private); +} + + +/* the palette import response callback ************************************/ + +static void +palette_import_response (GtkWidget *dialog, + gint response_id, + ImportDialog *private) +{ + palette_import_image_changed (private->context, NULL, private); + + if (response_id == GTK_RESPONSE_OK) + { + Gimp *gimp = private->context->gimp; + + if (private->palette && + gimp_palette_get_n_colors (private->palette) > 0) + { + const gchar *name = gtk_entry_get_text (GTK_ENTRY (private->entry)); + + if (name && *name) + gimp_object_set_name (GIMP_OBJECT (private->palette), name); + + gimp_container_add (gimp_data_factory_get_container (gimp->palette_factory), + GIMP_OBJECT (private->palette)); + } + else + { + gimp_message_literal (gimp, G_OBJECT (dialog), GIMP_MESSAGE_ERROR, + _("There is no palette to import.")); + return; + } + } + + gtk_widget_destroy (dialog); +} + + +/* functions to create & update the import dialog's gradient selection *****/ + +static void +palette_import_gradient_changed (GimpContext *context, + GimpGradient *gradient, + ImportDialog *private) +{ + if (gradient && private->import_type == GRADIENT_IMPORT) + { + gtk_entry_set_text (GTK_ENTRY (private->entry), + gimp_object_get_name (gradient)); + + palette_import_make_palette (private); + } +} + +static void +palette_import_image_changed (GimpContext *context, + GimpImage *image, + ImportDialog *private) +{ + if (private->image) + { + g_signal_handlers_disconnect_by_func (private->image, + palette_import_layer_changed, + private); + g_signal_handlers_disconnect_by_func (private->image, + palette_import_mask_changed, + private); + } + + private->image = image; + + if (private->import_type == IMAGE_IMPORT) + { + gboolean sensitive = FALSE; + + if (image) + { + gchar *label; + + label = g_strdup_printf ("%s-%d", + gimp_image_get_display_name (image), + gimp_image_get_ID (image)); + + gtk_entry_set_text (GTK_ENTRY (private->entry), label); + g_free (label); + + palette_import_make_palette (private); + + if (gimp_image_get_base_type (image) != GIMP_INDEXED) + sensitive = TRUE; + } + + gtk_widget_set_sensitive (private->sample_merged_toggle, sensitive); + gtk_widget_set_sensitive (private->selection_only_toggle, sensitive); + gimp_scale_entry_set_sensitive (GTK_OBJECT (private->threshold), + sensitive); + gimp_scale_entry_set_sensitive (GTK_OBJECT (private->num_colors), + sensitive); + } + + if (private->image) + { + g_signal_connect (private->image, "active-layer-changed", + G_CALLBACK (palette_import_layer_changed), + private); + g_signal_connect (private->image, "mask-changed", + G_CALLBACK (palette_import_mask_changed), + private); + } +} + +static void +palette_import_layer_changed (GimpImage *image, + ImportDialog *private) +{ + if (private->import_type == IMAGE_IMPORT && + ! gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (private->sample_merged_toggle))) + { + palette_import_make_palette (private); + } +} + +static void +palette_import_mask_changed (GimpImage *image, + ImportDialog *private) +{ + if (private->import_type == IMAGE_IMPORT && + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (private->selection_only_toggle))) + { + palette_import_make_palette (private); + } +} + +static void +palette_import_filename_changed (GtkFileChooser *button, + ImportDialog *private) +{ + gchar *filename; + + if (private->import_type != FILE_IMPORT) + return; + + filename = gtk_file_chooser_get_filename (button); + + if (filename) + { + gchar *basename = g_filename_display_basename (filename); + + /* TODO: strip filename extension */ + gtk_entry_set_text (GTK_ENTRY (private->entry), basename); + g_free (basename); + } + else + { + gtk_entry_set_text (GTK_ENTRY (private->entry), ""); + } + + g_free (filename); + + palette_import_make_palette (private); +} + +static void +import_dialog_drop_callback (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data) +{ + ImportDialog *private = data; + + gimp_context_set_by_type (private->context, + G_TYPE_FROM_INSTANCE (viewable), + GIMP_OBJECT (viewable)); + + if (GIMP_IS_GRADIENT (viewable) && + private->import_type != GRADIENT_IMPORT) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->gradient_radio), + TRUE); + } + else if (GIMP_IS_IMAGE (viewable) && + private->import_type != IMAGE_IMPORT) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->image_radio), + TRUE); + } +} + + +/* the import source menu item callbacks ***********************************/ + +static void +palette_import_set_sensitive (ImportDialog *private) +{ + gboolean gradient = (private->import_type == GRADIENT_IMPORT); + gboolean image = (private->import_type == IMAGE_IMPORT); + gboolean file = (private->import_type == FILE_IMPORT); + + gtk_widget_set_sensitive (private->gradient_combo, gradient); + gtk_widget_set_sensitive (private->image_combo, image); + gtk_widget_set_sensitive (private->sample_merged_toggle, image); + gtk_widget_set_sensitive (private->selection_only_toggle, image); + gtk_widget_set_sensitive (private->file_chooser, file); + + gimp_scale_entry_set_sensitive (GTK_OBJECT (private->num_colors), ! file); + gimp_scale_entry_set_sensitive (GTK_OBJECT (private->columns), ! file); + gimp_scale_entry_set_sensitive (GTK_OBJECT (private->threshold), image); +} + +static void +palette_import_grad_callback (GtkWidget *widget, + ImportDialog *private) +{ + GimpGradient *gradient; + + if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + return; + + private->import_type = GRADIENT_IMPORT; + + gradient = gimp_context_get_gradient (private->context); + + gtk_entry_set_text (GTK_ENTRY (private->entry), + gimp_object_get_name (gradient)); + + palette_import_set_sensitive (private); + + palette_import_make_palette (private); +} + +static void +palette_import_image_callback (GtkWidget *widget, + ImportDialog *private) +{ + GimpImage *image; + + if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + return; + + private->import_type = IMAGE_IMPORT; + + image = gimp_context_get_image (private->context); + + if (! image) + { + GimpContainer *images = private->context->gimp->images; + + image = GIMP_IMAGE (gimp_container_get_first_child (images)); + } + + palette_import_set_sensitive (private); + + palette_import_image_changed (private->context, image, private); +} + +static void +palette_import_file_callback (GtkWidget *widget, + ImportDialog *private) +{ + gchar *filename; + + if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + return; + + private->import_type = FILE_IMPORT; + + filename = + gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (private->file_chooser)); + + if (filename) + { + gchar *basename = g_filename_display_basename (filename); + + /* TODO: strip filename extension */ + gtk_entry_set_text (GTK_ENTRY (private->entry), basename); + g_free (basename); + + g_free (filename); + } + else + { + gtk_entry_set_text (GTK_ENTRY (private->entry), ""); + } + + palette_import_set_sensitive (private); +} + +static void +palette_import_columns_changed (GtkAdjustment *adj, + ImportDialog *private) +{ + if (private->palette) + gimp_palette_set_columns (private->palette, + ROUND (gtk_adjustment_get_value (adj))); +} + + +/* functions & callbacks to keep the import dialog uptodate ****************/ + +static void +palette_import_image_add (GimpContainer *container, + GimpImage *image, + ImportDialog *private) +{ + if (! gtk_widget_is_sensitive (private->image_radio)) + { + gtk_widget_set_sensitive (private->image_radio, TRUE); + gimp_context_set_image (private->context, image); + } +} + +static void +palette_import_image_remove (GimpContainer *container, + GimpImage *image, + ImportDialog *private) +{ + if (! gimp_container_get_n_children (private->context->gimp->images)) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->image_radio))) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->gradient_radio), + TRUE); + + gtk_widget_set_sensitive (private->image_radio, FALSE); + } +} + +static void +palette_import_make_palette (ImportDialog *private) +{ + GimpPalette *palette = NULL; + const gchar *palette_name; + gint n_colors; + gint n_columns; + gint threshold; + + palette_name = gtk_entry_get_text (GTK_ENTRY (private->entry)); + + if (! palette_name || ! strlen (palette_name)) + palette_name = _("Untitled"); + + n_colors = ROUND (gtk_adjustment_get_value (private->num_colors)); + n_columns = ROUND (gtk_adjustment_get_value (private->columns)); + threshold = ROUND (gtk_adjustment_get_value (private->threshold)); + + switch (private->import_type) + { + case GRADIENT_IMPORT: + { + GimpGradient *gradient; + + gradient = gimp_context_get_gradient (private->context); + + palette = gimp_palette_import_from_gradient (gradient, + private->context, + FALSE, + GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, + palette_name, + n_colors); + } + break; + + case IMAGE_IMPORT: + { + GimpImage *image = gimp_context_get_image (private->context); + gboolean sample_merged; + gboolean selection_only; + + sample_merged = + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (private->sample_merged_toggle)); + + selection_only = + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (private->selection_only_toggle)); + + if (gimp_image_get_base_type (image) == GIMP_INDEXED) + { + palette = gimp_palette_import_from_indexed_image (image, + private->context, + palette_name); + } + else if (sample_merged) + { + palette = gimp_palette_import_from_image (image, + private->context, + palette_name, + n_colors, + threshold, + selection_only); + } + else + { + GimpDrawable *drawable; + + drawable = GIMP_DRAWABLE (gimp_image_get_active_layer (image)); + + palette = gimp_palette_import_from_drawable (drawable, + private->context, + palette_name, + n_colors, + threshold, + selection_only); + } + } + break; + + case FILE_IMPORT: + { + GFile *file; + GError *error = NULL; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (private->file_chooser)); + + palette = gimp_palette_import_from_file (private->context, + file, + palette_name, &error); + g_object_unref (file); + + if (! palette) + { + gimp_message_literal (private->context->gimp, + G_OBJECT (private->dialog), GIMP_MESSAGE_ERROR, + error->message); + g_error_free (error); + } + } + break; + } + + if (private->palette) + g_object_unref (private->palette); + + private->palette = palette; + + if (palette) + { + gimp_palette_set_columns (palette, n_columns); + + gimp_view_set_viewable (GIMP_VIEW (private->preview), + GIMP_VIEWABLE (palette)); + + } + + gtk_widget_set_visible (private->no_colors_label, + ! (palette && + gimp_palette_get_n_colors (palette) > 0)); +} diff --git a/app/dialogs/palette-import-dialog.h b/app/dialogs/palette-import-dialog.h new file mode 100644 index 0000000..baa5677 --- /dev/null +++ b/app/dialogs/palette-import-dialog.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 . + */ + +#ifndef __PALETTE_IMPORT_DIALOG_H__ +#define __PALETTE_IMPORT_DIALOG_H__ + + +GtkWidget * palette_import_dialog_new (GimpContext *context); + + +#endif /* __PALETTE_IMPORT_DIALOG_H__ */ diff --git a/app/dialogs/preferences-dialog-utils.c b/app/dialogs/preferences-dialog-utils.c new file mode 100644 index 0000000..1a1aaaa --- /dev/null +++ b/app/dialogs/preferences-dialog-utils.c @@ -0,0 +1,402 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "widgets/gimpcolorpanel.h" +#include "widgets/gimppropwidgets.h" +#include "widgets/gimpwidgets-constructors.h" + +#include "preferences-dialog-utils.h" + + +GtkWidget * +prefs_frame_new (const gchar *label, + GtkContainer *parent, + gboolean expand) +{ + GtkWidget *frame; + GtkWidget *vbox; + + frame = gimp_frame_new (label); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + if (GTK_IS_BOX (parent)) + gtk_box_pack_start (GTK_BOX (parent), frame, expand, expand, 0); + else + gtk_container_add (parent, frame); + + gtk_widget_show (frame); + + return vbox; +} + +GtkWidget * +prefs_table_new (gint rows, + GtkContainer *parent) +{ + GtkWidget *table; + + table = gtk_table_new (rows, 2, FALSE); + + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + + if (GTK_IS_BOX (parent)) + gtk_box_pack_start (GTK_BOX (parent), table, FALSE, FALSE, 0); + else + gtk_container_add (parent, table); + + gtk_widget_show (table); + + return table; +} + +GtkWidget * +prefs_hint_box_new (const gchar *icon_name, + const gchar *text) +{ + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *label; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + label = gtk_label_new (text); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC, + -1); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show (label); + + gtk_widget_show (hbox); + + return hbox; +} + +GtkWidget * +prefs_button_add (const gchar *icon_name, + const gchar *label, + GtkBox *box) +{ + GtkWidget *button; + + button = gimp_icon_button_new (icon_name, label); + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + return button; +} + +GtkWidget * +prefs_check_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkBox *vbox) +{ + GtkWidget *button; + + button = gimp_prop_check_button_new (config, property_name, label); + + if (button) + { + gtk_box_pack_start (vbox, button, FALSE, FALSE, 0); + gtk_widget_show (button); + } + + return button; +} + +GtkWidget * +prefs_check_button_add_with_icon (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *icon_name, + GtkBox *vbox, + GtkSizeGroup *group) +{ + GtkWidget *button; + GtkWidget *hbox; + GtkWidget *image; + + button = gimp_prop_check_button_new (config, property_name, label); + if (!button) + return NULL; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (vbox, hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON); + gtk_misc_set_padding (GTK_MISC (image), 2, 2); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + if (group) + gtk_size_group_add_widget (group, image); + + return button; +} + +GtkWidget * +prefs_widget_add_aligned (GtkWidget *widget, + const gchar *text, + GtkTable *table, + gint table_row, + gboolean left_align, + GtkSizeGroup *group) +{ + GtkWidget *label = gimp_table_attach_aligned (table, 0, table_row, + text, 0.0, 0.5, + widget, 1, left_align); + if (group) + gtk_size_group_add_widget (group, label); + + return label; +} + +GtkWidget * +prefs_color_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *title, + GtkTable *table, + gint table_row, + GtkSizeGroup *group, + GimpContext *context) +{ + GtkWidget *button; + GParamSpec *pspec; + gboolean has_alpha; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), + property_name); + + has_alpha = gimp_param_spec_rgb_has_alpha (pspec); + + button = gimp_prop_color_button_new (config, property_name, + title, + PREFS_COLOR_BUTTON_WIDTH, + PREFS_COLOR_BUTTON_HEIGHT, + has_alpha ? + GIMP_COLOR_AREA_SMALL_CHECKS : + GIMP_COLOR_AREA_FLAT); + + if (button) + { + if (context) + gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), context); + + prefs_widget_add_aligned (button, label, table, table_row, TRUE, group); + } + + return button; +} + +GtkWidget * +prefs_entry_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *entry = gimp_prop_entry_new (config, property_name, -1); + + if (entry) + prefs_widget_add_aligned (entry, label, table, table_row, FALSE, group); + + return entry; +} + +GtkWidget * +prefs_spin_button_add (GObject *config, + const gchar *property_name, + gdouble step_increment, + gdouble page_increment, + gint digits, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *button = gimp_prop_spin_button_new (config, property_name, + step_increment, + page_increment, + digits); + + if (button) + prefs_widget_add_aligned (button, label, table, table_row, TRUE, group); + + return button; +} + +GtkWidget * +prefs_memsize_entry_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *entry = gimp_prop_memsize_entry_new (config, property_name); + + if (entry) + prefs_widget_add_aligned (entry, label, table, table_row, TRUE, group); + + return entry; +} + +GtkWidget * +prefs_file_chooser_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *dialog_title, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *button; + + button = gimp_prop_file_chooser_button_new (config, property_name, + dialog_title, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + + if (button) + prefs_widget_add_aligned (button, label, table, table_row, FALSE, group); + + return button; +} + +GtkWidget * +prefs_enum_combo_box_add (GObject *config, + const gchar *property_name, + gint minimum, + gint maximum, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *combo = gimp_prop_enum_combo_box_new (config, property_name, + minimum, maximum); + + if (combo) + prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group); + + return combo; +} + +GtkWidget * +prefs_boolean_combo_box_add (GObject *config, + const gchar *property_name, + const gchar *true_text, + const gchar *false_text, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *combo = gimp_prop_boolean_combo_box_new (config, property_name, + true_text, false_text); + + if (combo) + prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group); + + return combo; +} + +#ifdef HAVE_ISO_CODES +GtkWidget * +prefs_language_combo_box_add (GObject *config, + const gchar *property_name, + GtkBox *vbox) +{ + GtkWidget *combo = gimp_prop_language_combo_box_new (config, property_name); + + if (combo) + { + gtk_box_pack_start (vbox, combo, FALSE, FALSE, 0); + gtk_widget_show (combo); + } + + return combo; +} +#endif + +GtkWidget * +prefs_profile_combo_box_add (GObject *config, + const gchar *property_name, + GtkListStore *profile_store, + const gchar *dialog_title, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group, + GObject *profile_path_config, + const gchar *profile_path_property_name) +{ + GtkWidget *combo = gimp_prop_profile_combo_box_new (config, + property_name, + profile_store, + dialog_title, + profile_path_config, + profile_path_property_name); + + if (combo) + prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group); + + return combo; +} + +GtkWidget * +prefs_compression_combo_box_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group) +{ + GtkWidget *combo = gimp_prop_compression_combo_box_new (config, + property_name); + + if (combo) + prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group); + + return combo; +} diff --git a/app/dialogs/preferences-dialog-utils.h b/app/dialogs/preferences-dialog-utils.h new file mode 100644 index 0000000..46f2be9 --- /dev/null +++ b/app/dialogs/preferences-dialog-utils.h @@ -0,0 +1,134 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#ifndef __PREFERENCES_DIALOG_UTILS_H__ +#define __PREFERENCES_DIALOG_UTILS_H__ + + +#define PREFS_COLOR_BUTTON_WIDTH 40 +#define PREFS_COLOR_BUTTON_HEIGHT 24 + + +GtkWidget * prefs_frame_new (const gchar *label, + GtkContainer *parent, + gboolean expand); +GtkWidget * prefs_table_new (gint rows, + GtkContainer *parent); + +GtkWidget * prefs_hint_box_new (const gchar *icon_name, + const gchar *text); + +GtkWidget * prefs_button_add (const gchar *icon_name, + const gchar *label, + GtkBox *box); +GtkWidget * prefs_check_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkBox *box); +GtkWidget * prefs_check_button_add_with_icon (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *icon_name, + GtkBox *box, + GtkSizeGroup *group); + +GtkWidget * prefs_widget_add_aligned (GtkWidget *widget, + const gchar *text, + GtkTable *table, + gint table_row, + gboolean left_align, + GtkSizeGroup *group); + +GtkWidget * prefs_color_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *title, + GtkTable *table, + gint table_row, + GtkSizeGroup *group, + GimpContext *context); + +GtkWidget * prefs_entry_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); +GtkWidget * prefs_spin_button_add (GObject *config, + const gchar *property_name, + gdouble step_increment, + gdouble page_increment, + gint digits, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); +GtkWidget * prefs_memsize_entry_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); + +GtkWidget * prefs_file_chooser_button_add (GObject *config, + const gchar *property_name, + const gchar *label, + const gchar *dialog_title, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); + +GtkWidget * prefs_enum_combo_box_add (GObject *config, + const gchar *property_name, + gint minimum, + gint maximum, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); +GtkWidget * prefs_boolean_combo_box_add (GObject *config, + const gchar *property_name, + const gchar *true_text, + const gchar *false_text, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); +#ifdef HAVE_ISO_CODES +GtkWidget * prefs_language_combo_box_add (GObject *config, + const gchar *property_name, + GtkBox *vbox); +#endif +GtkWidget * prefs_profile_combo_box_add (GObject *config, + const gchar *property_name, + GtkListStore *profile_store, + const gchar *dialog_title, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group, + GObject *profile_path_config, + const gchar *profile_path_property_name); +GtkWidget * prefs_compression_combo_box_add (GObject *config, + const gchar *property_name, + const gchar *label, + GtkTable *table, + gint table_row, + GtkSizeGroup *group); + + +#endif /* __PREFERENCES_DIALOG_H__ */ diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c new file mode 100644 index 0000000..cde1576 --- /dev/null +++ b/app/dialogs/preferences-dialog.c @@ -0,0 +1,3409 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "gimp-version.h" + +#include "config/gimprc.h" + +#include "core/gimp.h" +#include "core/gimptemplate.h" + +#include "plug-in/gimppluginmanager.h" + +#include "widgets/gimpaction-history.h" +#include "widgets/gimpcolorpanel.h" +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpcontrollerlist.h" +#include "widgets/gimpdevices.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpgrideditor.h" +#include "widgets/gimphelp.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpiconsizescale.h" +#include "widgets/gimplanguagecombobox.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimppluginview.h" +#include "widgets/gimpprefsbox.h" +#include "widgets/gimppropwidgets.h" +#include "widgets/gimpstrokeeditor.h" +#include "widgets/gimptemplateeditor.h" +#include "widgets/gimptooleditor.h" +#include "widgets/gimpwidgets-utils.h" + +#include "menus/menus.h" + +#include "tools/gimp-tools.h" + +#include "gui/session.h" +#include "gui/icon-themes.h" +#include "gui/themes.h" + +#include "preferences-dialog.h" +#include "preferences-dialog-utils.h" +#include "resolution-calibrate-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + + +/* preferences local functions */ + +static GtkWidget * prefs_dialog_new (Gimp *gimp, + GimpConfig *config); +static void prefs_config_notify (GObject *config, + GParamSpec *param_spec, + GObject *config_copy); +static void prefs_config_copy_notify (GObject *config_copy, + GParamSpec *param_spec, + GObject *config); +static void prefs_response (GtkWidget *widget, + gint response_id, + GtkWidget *dialog); + +static void prefs_message (GtkMessageType type, + gboolean destroy, + const gchar *message); + +static void prefs_color_management_reset (GtkWidget *widget, + GObject *config); +static void prefs_dialog_defaults_reset (GtkWidget *widget, + GObject *config); +static void prefs_folders_reset (GtkWidget *widget, + GObject *config); +static void prefs_path_reset (GtkWidget *widget, + GObject *config); + +static void prefs_import_raw_procedure_callback (GtkWidget *widget, + GObject *config); +static void prefs_resolution_source_callback (GtkWidget *widget, + GObject *config); +static void prefs_resolution_calibrate_callback (GtkWidget *widget, + GtkWidget *entry); +static void prefs_input_devices_dialog (GtkWidget *widget, + Gimp *gimp); +static void prefs_keyboard_shortcuts_dialog (GtkWidget *widget, + Gimp *gimp); +static void prefs_menus_save_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_menus_clear_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_menus_remove_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_session_save_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_session_clear_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_devices_save_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_devices_clear_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_search_clear_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_tool_options_save_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_tool_options_clear_callback (GtkWidget *widget, + Gimp *gimp); +static void prefs_help_language_change_callback (GtkComboBox *combo, + Gimp *gimp); +static void prefs_help_language_change_callback2 (GtkComboBox *combo, + GtkContainer *box); + + +/* private variables */ + +static GtkWidget *prefs_dialog = NULL; +static GtkWidget *tool_editor = NULL; + + +/* public function */ + +GtkWidget * +preferences_dialog_create (Gimp *gimp) +{ + GimpConfig *config; + GimpConfig *config_copy; + GimpConfig *config_orig; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (prefs_dialog) + return prefs_dialog; + + /* turn off autosaving while the prefs dialog is open */ + gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), FALSE); + + config = GIMP_CONFIG (gimp->edit_config); + config_copy = gimp_config_duplicate (config); + config_orig = gimp_config_duplicate (config); + + g_signal_connect_object (config, "notify", + G_CALLBACK (prefs_config_notify), + config_copy, 0); + g_signal_connect_object (config_copy, "notify", + G_CALLBACK (prefs_config_copy_notify), + config, 0); + + prefs_dialog = prefs_dialog_new (gimp, config_copy); + + g_object_add_weak_pointer (G_OBJECT (prefs_dialog), + (gpointer) &prefs_dialog); + + g_object_set_data (G_OBJECT (prefs_dialog), "gimp", gimp); + + g_object_set_data_full (G_OBJECT (prefs_dialog), "config-copy", config_copy, + (GDestroyNotify) g_object_unref); + g_object_set_data_full (G_OBJECT (prefs_dialog), "config-orig", config_orig, + (GDestroyNotify) g_object_unref); + + return prefs_dialog; +} + + +/* private functions */ + +static void +prefs_config_notify (GObject *config, + GParamSpec *param_spec, + GObject *config_copy) +{ + GValue global_value = G_VALUE_INIT; + GValue copy_value = G_VALUE_INIT; + + g_value_init (&global_value, param_spec->value_type); + g_value_init (©_value, param_spec->value_type); + + g_object_get_property (config, param_spec->name, &global_value); + g_object_get_property (config_copy, param_spec->name, ©_value); + + if (g_param_values_cmp (param_spec, &global_value, ©_value)) + { + g_signal_handlers_block_by_func (config_copy, + prefs_config_copy_notify, + config); + + g_object_set_property (config_copy, param_spec->name, &global_value); + + g_signal_handlers_unblock_by_func (config_copy, + prefs_config_copy_notify, + config); + } + + g_value_unset (&global_value); + g_value_unset (©_value); +} + +static void +prefs_config_copy_notify (GObject *config_copy, + GParamSpec *param_spec, + GObject *config) +{ + GValue copy_value = G_VALUE_INIT; + GValue global_value = G_VALUE_INIT; + + g_value_init (©_value, param_spec->value_type); + g_value_init (&global_value, param_spec->value_type); + + g_object_get_property (config_copy, param_spec->name, ©_value); + g_object_get_property (config, param_spec->name, &global_value); + + if (g_param_values_cmp (param_spec, ©_value, &global_value)) + { + if (param_spec->flags & GIMP_CONFIG_PARAM_CONFIRM) + { +#ifdef GIMP_CONFIG_DEBUG + g_print ("NOT Applying prefs change of '%s' to edit_config " + "because it needs confirmation\n", + param_spec->name); +#endif + } + else + { +#ifdef GIMP_CONFIG_DEBUG + g_print ("Applying prefs change of '%s' to edit_config\n", + param_spec->name); +#endif + g_signal_handlers_block_by_func (config, + prefs_config_notify, + config_copy); + + g_object_set_property (config, param_spec->name, ©_value); + + g_signal_handlers_unblock_by_func (config, + prefs_config_notify, + config_copy); + } + } + + g_value_unset (©_value); + g_value_unset (&global_value); +} + +static void +prefs_response (GtkWidget *widget, + gint response_id, + GtkWidget *dialog) +{ + Gimp *gimp = g_object_get_data (G_OBJECT (dialog), "gimp"); + + switch (response_id) + { + case RESPONSE_RESET: + { + GtkWidget *confirm; + + confirm = gimp_message_dialog_new (_("Reset All Preferences"), + GIMP_ICON_DIALOG_QUESTION, + dialog, + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Reset"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (confirm), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (confirm)->box, + _("Do you really want to reset all " + "preferences to default values?")); + + if (gimp_dialog_run (GIMP_DIALOG (confirm)) == GTK_RESPONSE_OK) + { + GimpConfig *config_copy; + + config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy"); + + gimp_config_reset (config_copy); + gimp_rc_load_system (GIMP_RC (config_copy)); + + /* don't use the default value if there is no help browser */ + if (! gimp_help_browser_is_installed (gimp)) + { + g_object_set (config_copy, + "help-browser", GIMP_HELP_BROWSER_WEB_BROWSER, + NULL); + } + } + + gtk_widget_destroy (confirm); + + return; + } + break; + + case GTK_RESPONSE_OK: + { + GObject *config_copy; + GList *restart_diff; + GList *confirm_diff; + GList *list; + + config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy"); + + /* destroy config_orig */ + g_object_set_data (G_OBJECT (dialog), "config-orig", NULL); + + gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE); + + confirm_diff = gimp_config_diff (G_OBJECT (gimp->edit_config), + config_copy, + GIMP_CONFIG_PARAM_CONFIRM); + + g_object_freeze_notify (G_OBJECT (gimp->edit_config)); + + for (list = confirm_diff; list; list = g_list_next (list)) + { + GParamSpec *param_spec = list->data; + GValue value = G_VALUE_INIT; + + g_value_init (&value, param_spec->value_type); + + g_object_get_property (config_copy, + param_spec->name, &value); + g_object_set_property (G_OBJECT (gimp->edit_config), + param_spec->name, &value); + + g_value_unset (&value); + } + + g_object_thaw_notify (G_OBJECT (gimp->edit_config)); + + g_list_free (confirm_diff); + + gimp_rc_save (GIMP_RC (gimp->edit_config)); + + /* spit out a solely informational warning about changed values + * which need restart + */ + restart_diff = gimp_config_diff (G_OBJECT (gimp->edit_config), + G_OBJECT (gimp->config), + GIMP_CONFIG_PARAM_RESTART); + + if (restart_diff) + { + GString *string; + + string = g_string_new (_("You will have to restart GIMP for " + "the following changes to take effect:")); + g_string_append (string, "\n\n"); + + for (list = restart_diff; list; list = g_list_next (list)) + { + GParamSpec *param_spec = list->data; + + g_string_append_printf (string, "%s\n", param_spec->name); + } + + prefs_message (GTK_MESSAGE_INFO, FALSE, string->str); + + g_string_free (string, TRUE); + } + + g_list_free (restart_diff); + } + break; + + default: + { + GObject *config_orig; + GList *diff; + GList *list; + + config_orig = g_object_get_data (G_OBJECT (dialog), "config-orig"); + + /* destroy config_copy */ + g_object_set_data (G_OBJECT (dialog), "config-copy", NULL); + + gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE); + + diff = gimp_config_diff (G_OBJECT (gimp->edit_config), + config_orig, + GIMP_CONFIG_PARAM_SERIALIZE); + + g_object_freeze_notify (G_OBJECT (gimp->edit_config)); + + for (list = diff; list; list = g_list_next (list)) + { + GParamSpec *param_spec = list->data; + GValue value = G_VALUE_INIT; + + g_value_init (&value, param_spec->value_type); + + g_object_get_property (config_orig, + param_spec->name, &value); + g_object_set_property (G_OBJECT (gimp->edit_config), + param_spec->name, &value); + + g_value_unset (&value); + } + + gimp_tool_editor_revert_changes (GIMP_TOOL_EDITOR (tool_editor)); + + g_object_thaw_notify (G_OBJECT (gimp->edit_config)); + + g_list_free (diff); + } + + tool_editor = NULL; + } + + /* enable autosaving again */ + gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), TRUE); + + gtk_widget_destroy (dialog); +} + +static void +prefs_color_management_reset (GtkWidget *widget, + GObject *config) +{ + GimpCoreConfig *core_config = GIMP_CORE_CONFIG (config); + + gimp_config_reset (GIMP_CONFIG (core_config->color_management)); + gimp_config_reset_property (config, "color-profile-policy"); + gimp_config_reset_property (config, "filter-tool-show-color-options"); +} + +static void +prefs_dialog_defaults_reset (GtkWidget *widget, + GObject *config) +{ + GParamSpec **pspecs; + guint n_pspecs; + guint i; + + pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), + &n_pspecs); + + g_object_freeze_notify (config); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + + if (pspec->owner_type == GIMP_TYPE_DIALOG_CONFIG) + gimp_config_reset_property (config, pspec->name); + } + + gimp_config_reset_property (config, "filter-tool-max-recent"); + gimp_config_reset_property (config, "filter-tool-use-last-settings"); + gimp_config_reset_property (config, "filter-tool-show-color-options"); + + g_object_thaw_notify (config); + + g_free (pspecs); +} + +static void +prefs_folders_reset (GtkWidget *widget, + GObject *config) +{ + gimp_config_reset_property (config, "temp-path"); + gimp_config_reset_property (config, "swap-path"); +} + +static void +prefs_path_reset (GtkWidget *widget, + GObject *config) +{ + const gchar *path_property; + const gchar *writable_property; + + path_property = g_object_get_data (G_OBJECT (widget), "path"); + writable_property = g_object_get_data (G_OBJECT (widget), "path-writable"); + + gimp_config_reset_property (config, path_property); + + if (writable_property) + gimp_config_reset_property (config, writable_property); +} + +static void +prefs_template_select_callback (GimpContainerView *view, + GimpTemplate *template, + gpointer insert_data, + GimpTemplate *edit_template) +{ + if (template) + { + /* make sure the resolution values are copied first (see bug #546924) */ + gimp_config_sync (G_OBJECT (template), G_OBJECT (edit_template), + GIMP_TEMPLATE_PARAM_COPY_FIRST); + gimp_config_sync (G_OBJECT (template), G_OBJECT (edit_template), + 0); + } +} + +static void +prefs_import_raw_procedure_callback (GtkWidget *widget, + GObject *config) +{ + gchar *raw_plug_in; + + raw_plug_in = gimp_plug_in_view_get_plug_in (GIMP_PLUG_IN_VIEW (widget)); + + g_object_set (config, + "import-raw-plug-in", raw_plug_in, + NULL); + + g_free (raw_plug_in); +} + +static void +prefs_resolution_source_callback (GtkWidget *widget, + GObject *config) +{ + gdouble xres; + gdouble yres; + gboolean from_gdk; + + from_gdk = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + if (from_gdk) + { + gimp_get_monitor_resolution (gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + &xres, &yres); + } + else + { + GimpSizeEntry *entry = g_object_get_data (G_OBJECT (widget), + "monitor_resolution_sizeentry"); + + g_return_if_fail (GIMP_IS_SIZE_ENTRY (entry)); + + xres = gimp_size_entry_get_refval (entry, 0); + yres = gimp_size_entry_get_refval (entry, 1); + } + + g_object_set (config, + "monitor-xresolution", xres, + "monitor-yresolution", yres, + "monitor-resolution-from-windowing-system", from_gdk, + NULL); +} + +static void +prefs_resolution_calibrate_callback (GtkWidget *widget, + GtkWidget *entry) +{ + GtkWidget *dialog; + GtkWidget *prefs_box; + const gchar *icon_name; + + dialog = gtk_widget_get_toplevel (entry); + + prefs_box = g_object_get_data (G_OBJECT (dialog), "prefs-box"); + icon_name = gimp_prefs_box_get_current_icon_name (GIMP_PREFS_BOX (prefs_box)); + + resolution_calibrate_dialog (entry, icon_name); +} + +static void +prefs_input_devices_dialog (GtkWidget *widget, + Gimp *gimp) +{ + gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + "gimp-input-devices-dialog", 0); +} + +static void +prefs_keyboard_shortcuts_dialog (GtkWidget *widget, + Gimp *gimp) +{ + gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + "gimp-keyboard-shortcuts-dialog", 0); +} + +static void +prefs_menus_save_callback (GtkWidget *widget, + Gimp *gimp) +{ + GtkWidget *clear_button; + + menus_save (gimp, TRUE); + + clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); + + if (clear_button) + gtk_widget_set_sensitive (clear_button, TRUE); +} + +static void +prefs_menus_clear_callback (GtkWidget *widget, + Gimp *gimp) +{ + GError *error = NULL; + + if (! menus_clear (gimp, &error)) + { + prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); + g_clear_error (&error); + } + else + { + gtk_widget_set_sensitive (widget, FALSE); + + prefs_message (GTK_MESSAGE_INFO, TRUE, + _("Your keyboard shortcuts will be reset to " + "default values the next time you start GIMP.")); + } +} + +static void +prefs_menus_remove_callback (GtkWidget *widget, + Gimp *gimp) +{ + GtkWidget *dialog; + + dialog = gimp_message_dialog_new (_("Remove all Keyboard Shortcuts"), + GIMP_ICON_DIALOG_QUESTION, + gtk_widget_get_toplevel (widget), + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("Cl_ear"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect_object (gtk_widget_get_toplevel (widget), "unmap", + G_CALLBACK (gtk_widget_destroy), + dialog, G_CONNECT_SWAPPED); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("Do you really want to remove all " + "keyboard shortcuts from all menus?")); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK) + { + menus_remove (gimp); + } + + gtk_widget_destroy (dialog); +} + +static void +prefs_session_save_callback (GtkWidget *widget, + Gimp *gimp) +{ + GtkWidget *clear_button; + + session_save (gimp, TRUE); + + clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); + + if (clear_button) + gtk_widget_set_sensitive (clear_button, TRUE); +} + +static void +prefs_session_clear_callback (GtkWidget *widget, + Gimp *gimp) +{ + GError *error = NULL; + + if (! session_clear (gimp, &error)) + { + prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); + g_clear_error (&error); + } + else + { + gtk_widget_set_sensitive (widget, FALSE); + + prefs_message (GTK_MESSAGE_INFO, TRUE, + _("Your window setup will be reset to " + "default values the next time you start GIMP.")); + } +} + +static void +prefs_devices_save_callback (GtkWidget *widget, + Gimp *gimp) +{ + GtkWidget *clear_button; + + gimp_devices_save (gimp, TRUE); + + clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); + + if (clear_button) + gtk_widget_set_sensitive (clear_button, TRUE); +} + +static void +prefs_devices_clear_callback (GtkWidget *widget, + Gimp *gimp) +{ + GError *error = NULL; + + if (! gimp_devices_clear (gimp, &error)) + { + prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); + g_clear_error (&error); + } + else + { + gtk_widget_set_sensitive (widget, FALSE); + + prefs_message (GTK_MESSAGE_INFO, TRUE, + _("Your input device settings will be reset to " + "default values the next time you start GIMP.")); + } +} + +static void +prefs_search_clear_callback (GtkWidget *widget, + Gimp *gimp) +{ + gimp_action_history_clear (gimp); +} + +static void +prefs_tool_options_save_callback (GtkWidget *widget, + Gimp *gimp) +{ + GtkWidget *clear_button; + + gimp_tools_save (gimp, TRUE, TRUE); + + clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); + + if (clear_button) + gtk_widget_set_sensitive (clear_button, TRUE); +} + +static void +prefs_tool_options_clear_callback (GtkWidget *widget, + Gimp *gimp) +{ + GError *error = NULL; + + if (! gimp_tools_clear (gimp, &error)) + { + prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); + g_clear_error (&error); + } + else + { + gtk_widget_set_sensitive (widget, FALSE); + + prefs_message (GTK_MESSAGE_INFO, TRUE, + _("Your tool options will be reset to " + "default values the next time you start GIMP.")); + } +} + +static void +prefs_help_language_change_callback (GtkComboBox *combo, + Gimp *gimp) +{ + gchar *help_locales = NULL; + gchar *code; + + code = gimp_language_combo_box_get_code (GIMP_LANGUAGE_COMBO_BOX (combo)); + if (code && g_strcmp0 ("", code) != 0) + { + help_locales = g_strdup_printf ("%s:", code); + } + g_object_set (gimp->config, + "help-locales", help_locales? help_locales : "", + NULL); + g_free (code); + if (help_locales) + g_free (help_locales); +} + +static void +prefs_help_language_change_callback2 (GtkComboBox *combo, + GtkContainer *box) +{ + Gimp *gimp; + GtkLabel *label = NULL; + GtkImage *icon = NULL; + GList *children; + GList *iter; + const gchar *text; + const gchar *icon_name; + + gimp = g_object_get_data (G_OBJECT (box), "gimp"); + children = gtk_container_get_children (box); + for (iter = children; iter; iter = iter->next) + { + if (GTK_IS_LABEL (iter->data)) + { + label = iter->data; + } + else if (GTK_IS_IMAGE (iter->data)) + { + icon = iter->data; + } + } + if (gimp_help_user_manual_is_installed (gimp)) + { + text = _("There's a local installation of the user manual."); + icon_name = GIMP_ICON_DIALOG_INFORMATION; + } + else + { + text = _("The user manual is not installed locally."); + icon_name = GIMP_ICON_DIALOG_WARNING; + } + if (label) + { + gtk_label_set_text (label, text); + } + if (icon) + { + gtk_image_set_from_icon_name (icon, icon_name, + GTK_ICON_SIZE_BUTTON); + } + + g_list_free (children); +} + +static void +prefs_format_string_select_callback (GtkTreeSelection *sel, + GtkEntry *entry) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (sel, &model, &iter)) + { + GValue val = G_VALUE_INIT; + + gtk_tree_model_get_value (model, &iter, 1, &val); + gtk_entry_set_text (entry, g_value_get_string (&val)); + g_value_unset (&val); + } +} + +static void +prefs_theme_select_callback (GtkTreeSelection *sel, + Gimp *gimp) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (sel, &model, &iter)) + { + GValue val = G_VALUE_INIT; + + gtk_tree_model_get_value (model, &iter, 0, &val); + g_object_set_property (G_OBJECT (gimp->config), "theme", &val); + g_value_unset (&val); + } +} + +static void +prefs_theme_reload_callback (GtkWidget *button, + Gimp *gimp) +{ + g_object_notify (G_OBJECT (gimp->config), "theme"); +} + +static void +prefs_icon_theme_select_callback (GtkTreeSelection *sel, + Gimp *gimp) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (sel, &model, &iter)) + { + GValue val = G_VALUE_INIT; + + gtk_tree_model_get_value (model, &iter, 1, &val); + g_object_set_property (G_OBJECT (gimp->config), "icon-theme", &val); + g_value_unset (&val); + } +} + +static void +prefs_canvas_padding_color_changed (GtkWidget *button, + GtkWidget *combo) +{ + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + GIMP_CANVAS_PADDING_MODE_CUSTOM); +} + +static void +prefs_display_options_frame_add (Gimp *gimp, + GObject *object, + const gchar *label, + GtkContainer *parent) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *checks_vbox; + GtkWidget *table; + GtkWidget *combo; + GtkWidget *button; + + vbox = prefs_frame_new (label, parent, FALSE); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); + gtk_widget_show (checks_vbox); + + prefs_check_button_add (object, "show-selection", + _("Show s_election"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-layer-boundary", + _("Show _layer boundary"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-canvas-boundary", + _("Show can_vas boundary"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-guides", + _("Show _guides"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-grid", + _("Show gri_d"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-sample-points", + _("Show _sample points"), + GTK_BOX (checks_vbox)); + + checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); + gtk_widget_show (checks_vbox); + +#ifndef GDK_WINDOWING_QUARTZ + prefs_check_button_add (object, "show-menubar", + _("Show _menubar"), + GTK_BOX (checks_vbox)); +#endif /* !GDK_WINDOWING_QUARTZ */ + prefs_check_button_add (object, "show-rulers", + _("Show _rulers"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-scrollbars", + _("Show scroll_bars"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "show-statusbar", + _("Show s_tatusbar"), + GTK_BOX (checks_vbox)); + + table = prefs_table_new (2, GTK_CONTAINER (vbox)); + + combo = prefs_enum_combo_box_add (object, "padding-mode", 0, 0, + _("Canvas _padding mode:"), + GTK_TABLE (table), 0, + NULL); + + button = prefs_color_button_add (object, "padding-color", + _("Custom p_adding color:"), + _("Select Custom Canvas Padding Color"), + GTK_TABLE (table), 1, NULL, + gimp_get_user_context (gimp)); + + g_signal_connect (button, "color-changed", + G_CALLBACK (prefs_canvas_padding_color_changed), + combo); + + prefs_check_button_add (object, "padding-in-show-all", + _("_Keep canvas padding in \"Show All\" mode"), + GTK_BOX (vbox)); +} + +static void +prefs_behavior_options_frame_add (Gimp *gimp, + GObject *object, + const gchar *label, + GtkContainer *parent) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *checks_vbox; + + vbox = prefs_frame_new (label, parent, FALSE); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); + gtk_widget_show (checks_vbox); + + prefs_check_button_add (object, "snap-to-guides", + _("Snap to _Guides"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "snap-to-grid", + _("S_nap to Grid"), + GTK_BOX (checks_vbox)); + + checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); + gtk_widget_show (checks_vbox); + + prefs_check_button_add (object, "snap-to-canvas", + _("Snap to Canvas _Edges"), + GTK_BOX (checks_vbox)); + prefs_check_button_add (object, "snap-to-path", + _("Snap to _Active Path"), + GTK_BOX (checks_vbox)); +} + +static void +prefs_help_func (const gchar *help_id, + gpointer help_data) +{ + GtkWidget *prefs_box; + + prefs_box = g_object_get_data (G_OBJECT (help_data), "prefs-box"); + + help_id = gimp_prefs_box_get_current_help_id (GIMP_PREFS_BOX (prefs_box)); + + gimp_standard_help_func (help_id, NULL); +} + +static void +prefs_message (GtkMessageType type, + gboolean destroy_with_parent, + const gchar *message) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (prefs_dialog), + destroy_with_parent ? + GTK_DIALOG_DESTROY_WITH_PARENT : 0, + type, GTK_BUTTONS_OK, + "%s", message); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +static GtkWidget * +prefs_dialog_new (Gimp *gimp, + GimpConfig *config) +{ + GtkWidget *dialog; + GtkTreeIter top_iter; + GtkTreeIter child_iter; + + GtkWidget *prefs_box; + GtkSizeGroup *size_group = NULL; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *vbox2; + GtkWidget *vbox3; + GtkWidget *button; + GtkWidget *button2; + GtkWidget *table; + GtkWidget *label; + GtkWidget *entry; + GtkWidget *calibrate_button; + GSList *group; + GtkWidget *separator; + GtkWidget *editor; + gint i; + + GObject *object; + GimpCoreConfig *core_config; + GimpDisplayConfig *display_config; + GList *manuals; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONFIG (config), NULL); + + object = G_OBJECT (config); + core_config = GIMP_CORE_CONFIG (config); + display_config = GIMP_DISPLAY_CONFIG (config); + + dialog = gimp_dialog_new (_("Preferences"), "gimp-preferences", + NULL, 0, + prefs_help_func, + GIMP_HELP_PREFS_DIALOG, + + _("_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); + + g_signal_connect (dialog, "response", + G_CALLBACK (prefs_response), + dialog); + + /* The prefs box */ + prefs_box = gimp_prefs_box_new (); + gtk_container_set_border_width (GTK_CONTAINER (prefs_box), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + prefs_box, TRUE, TRUE, 0); + gtk_widget_show (prefs_box); + + g_object_set_data (G_OBJECT (dialog), "prefs-box", prefs_box); + + + /**********************/ + /* System Resources */ + /**********************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-system-resources", + _("System Resources"), + _("System Resources"), + GIMP_HELP_PREFS_SYSTEM_RESOURCES, + NULL, + &top_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + vbox2 = prefs_frame_new (_("Resource Consumption"), + GTK_CONTAINER (vbox), FALSE); + +#ifdef ENABLE_MP + table = prefs_table_new (6, GTK_CONTAINER (vbox2)); +#else + table = prefs_table_new (5, GTK_CONTAINER (vbox2)); +#endif /* ENABLE_MP */ + + prefs_spin_button_add (object, "undo-levels", 1.0, 5.0, 0, + _("Minimal number of _undo levels:"), + GTK_TABLE (table), 0, size_group); + prefs_memsize_entry_add (object, "undo-size", + _("Maximum undo _memory:"), + GTK_TABLE (table), 1, size_group); + prefs_memsize_entry_add (object, "tile-cache-size", + _("Tile cache _size:"), + GTK_TABLE (table), 2, size_group); + prefs_memsize_entry_add (object, "max-new-image-size", + _("Maximum _new image size:"), + GTK_TABLE (table), 3, size_group); + + prefs_compression_combo_box_add (object, "swap-compression", + _("S_wap compression:"), + GTK_TABLE (table), 4, size_group); + +#ifdef ENABLE_MP + prefs_spin_button_add (object, "num-processors", 1.0, 4.0, 0, + _("Number of _threads to use:"), + GTK_TABLE (table), 5, size_group); +#endif /* ENABLE_MP */ + + /* Internet access */ +#ifdef CHECK_UPDATE + if (gimp_version_check_update ()) + { + vbox2 = prefs_frame_new (_("Network access"), GTK_CONTAINER (vbox), + FALSE); + + prefs_check_button_add (object, "check-updates", + _("Check for updates (requires internet)"), + GTK_BOX (vbox2)); + } +#endif + + /* Image Thumbnails */ + vbox2 = prefs_frame_new (_("Image Thumbnails"), GTK_CONTAINER (vbox), FALSE); + + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "thumbnail-size", 0, 0, + _("Size of _thumbnails:"), + GTK_TABLE (table), 0, size_group); + + prefs_memsize_entry_add (object, "thumbnail-filesize-limit", + _("Maximum _filesize for thumbnailing:"), + GTK_TABLE (table), 1, size_group); + + /* Document History */ + vbox2 = prefs_frame_new (_("Document History"), GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "save-document-history", + _("_Keep record of used files in the Recent Documents list"), + GTK_BOX (vbox2)); + + g_clear_object (&size_group); + + + /***************/ + /* Debugging */ + /***************/ + /* No debugging preferences are needed on win32. Either GIMP has been + * built with DrMinGW support (HAVE_EXCHNDL) or not. If it has, then + * the backtracing is enabled and can't be disabled. It assume it will + * work only upon a crash. + */ +#ifndef G_OS_WIN32 + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-wilber-eek", /* TODO: icon needed. */ + _("Debugging"), + _("Debugging"), + GIMP_HELP_PREFS_DEBUGGING, + NULL, + &top_iter); + + hbox = g_object_new (GIMP_TYPE_HINT_BOX, + "icon-name", GIMP_ICON_DIALOG_WARNING, + "hint", _("We hope you will never need these " + "settings, but as all software, GIMP " + "has bugs, and crashes can occur. If it " + "happens, you can help us by reporting " + "bugs."), + NULL); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + vbox2 = prefs_frame_new (_("Bug Reporting"), + GTK_CONTAINER (vbox), FALSE); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + button = prefs_enum_combo_box_add (object, "debug-policy", 0, 0, + _("Debug _policy:"), + GTK_TABLE (table), 0, NULL); + + /* Check existence of gdb or lldb to activate the preference, as a + * good hint of its prerequisite, unless backtrace() API exists, in + * which case the feature is always available. + */ + hbox = NULL; + if (! gimp_stack_trace_available (TRUE)) + { +#ifndef HAVE_EXECINFO_H + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("This feature requires \"gdb\" or \"lldb\" installed on your system.")); + gtk_widget_set_sensitive (button, FALSE); +#else + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("This feature is more efficient with \"gdb\" or \"lldb\" installed on your system.")); +#endif /* ! HAVE_EXECINFO_H */ + } + if (hbox) + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + +#endif /* ! G_OS_WIN32 */ + + /**********************/ + /* Color Management */ + /**********************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-color-management", + _("Color Management"), + _("Color Management"), + GIMP_HELP_PREFS_COLOR_MANAGEMENT, + NULL, + &top_iter); + + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + button = gimp_prefs_box_set_page_resettable (GIMP_PREFS_BOX (prefs_box), + vbox, + _("R_eset Color Management")); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_color_management_reset), + config); + + { + GObject *color_config = G_OBJECT (core_config->color_management); + GtkListStore *store; + gchar *filename; + gint row = 0; + + filename = gimp_personal_rc_file ("profilerc"); + store = gimp_color_profile_store_new (filename); + g_free (filename); + + gimp_color_profile_store_add_file (GIMP_COLOR_PROFILE_STORE (store), + NULL, NULL); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + table = prefs_table_new (1, GTK_CONTAINER (vbox)); + + prefs_enum_combo_box_add (color_config, "mode", 0, 0, + _("Image display _mode:"), + GTK_TABLE (table), row++, NULL); + + /* Color Managed Display */ + vbox2 = prefs_frame_new (_("Color Managed Display"), GTK_CONTAINER (vbox), + FALSE); + + table = prefs_table_new (4, GTK_CONTAINER (vbox2)); + row = 0; + + prefs_profile_combo_box_add (color_config, + "display-profile", + store, + _("Select Monitor Color Profile"), + _("_Monitor profile:"), + GTK_TABLE (table), row++, size_group, + object, "color-profile-path"); + + button = gimp_prop_check_button_new (color_config, + "display-profile-from-gdk", + _("_Try to use the system monitor " + "profile")); + gtk_table_attach_defaults (GTK_TABLE (table), + button, 1, 2, row, row + 1); + gtk_widget_show (button); + row++; + + prefs_enum_combo_box_add (color_config, + "display-rendering-intent", 0, 0, + _("_Rendering intent:"), + GTK_TABLE (table), row++, size_group); + + button = gimp_prop_check_button_new (color_config, + "display-use-black-point-compensation", + _("Use _black point compensation")); + gtk_table_attach_defaults (GTK_TABLE (table), + button, 1, 2, row, row + 1); + gtk_widget_show (button); + row++; + + prefs_boolean_combo_box_add (color_config, + "display-optimize", + _("Speed"), + _("Precision / Color Fidelity"), + _("_Optimize image display for:"), + GTK_TABLE (table), row++, size_group); + + /* Print Simulation (Soft-proofing) */ + vbox2 = prefs_frame_new (_("Soft-Proofing"), + GTK_CONTAINER (vbox), + FALSE); + + table = prefs_table_new (4, GTK_CONTAINER (vbox2)); + row = 0; + + prefs_profile_combo_box_add (color_config, + "printer-profile", + store, + _("Select Soft-Proofing Color Profile"), + _("_Soft-proofing profile:"), + GTK_TABLE (table), row++, size_group, + object, "color-profile-path"); + + prefs_enum_combo_box_add (color_config, + "simulation-rendering-intent", 0, 0, + _("Re_ndering intent:"), + GTK_TABLE (table), row++, size_group); + + button = gimp_prop_check_button_new (color_config, + "simulation-use-black-point-compensation", + _("Use black _point compensation")); + gtk_table_attach_defaults (GTK_TABLE (table), + button, 1, 2, row, row + 1); + gtk_widget_show (button); + row++; + + prefs_boolean_combo_box_add (color_config, + "simulation-optimize", + _("Speed"), + _("Precision / Color Fidelity"), + _("O_ptimize soft-proofing for:"), + GTK_TABLE (table), row++, size_group); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_table_attach_defaults (GTK_TABLE (table), hbox, 1, 2, row, row + 1); + gtk_widget_show (hbox); + row++; + + button = gimp_prop_check_button_new (color_config, "simulation-gamut-check", + _("Mar_k out of gamut colors")); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + button = gimp_prop_color_button_new (color_config, "out-of-gamut-color", + _("Select Warning Color"), + PREFS_COLOR_BUTTON_WIDTH, + PREFS_COLOR_BUTTON_HEIGHT, + GIMP_COLOR_AREA_FLAT); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), + gimp_get_user_context (gimp)); + + /* Preferred profiles */ + vbox2 = prefs_frame_new (_("Preferred Profiles"), GTK_CONTAINER (vbox), + FALSE); + + table = prefs_table_new (3, GTK_CONTAINER (vbox2)); + row = 0; + + prefs_profile_combo_box_add (color_config, + "rgb-profile", + store, + _("Select Preferred RGB Color Profile"), + _("_RGB profile:"), + GTK_TABLE (table), row++, size_group, + object, "color-profile-path"); + + prefs_profile_combo_box_add (color_config, + "gray-profile", + store, + _("Select Preferred Grayscale Color Profile"), + _("_Grayscale profile:"), + GTK_TABLE (table), row++, size_group, + object, "color-profile-path"); + + prefs_profile_combo_box_add (color_config, + "cmyk-profile", + store, + _("Select CMYK Color Profile"), + _("_CMYK profile:"), + GTK_TABLE (table), row++, size_group, + object, "color-profile-path"); + + /* Policies */ + vbox2 = prefs_frame_new (_("Policies"), GTK_CONTAINER (vbox), + FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, + _("_File Open behaviour:"), + GTK_TABLE (table), 0, size_group); + + /* Filter Dialogs */ + vbox2 = prefs_frame_new (_("Filter Dialogs"), GTK_CONTAINER (vbox), + FALSE); + + button = prefs_check_button_add (object, "filter-tool-show-color-options", + _("Show _advanced color options"), + GTK_BOX (vbox2)); + + g_clear_object (&size_group); + + g_object_unref (store); + } + + + /***************************/ + /* Image Import / Export */ + /***************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-import-export", + _("Image Import & Export"), + _("Image Import & Export"), + GIMP_HELP_PREFS_IMPORT_EXPORT, + NULL, + &top_iter); + + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Import Policies */ + vbox2 = prefs_frame_new (_("Import Policies"), + GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "import-promote-float", + _("Promote imported images to " + "_floating point precision"), + GTK_BOX (vbox2)); + + vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); + g_object_bind_property (button, "active", + vbox3, "sensitive", + G_BINDING_SYNC_CREATE); + button = prefs_check_button_add (object, "import-promote-dither", + _("_Dither images when promoting to " + "floating point"), + GTK_BOX (vbox3)); + + button = prefs_check_button_add (object, "import-add-alpha", + _("_Add an alpha channel to imported images"), + GTK_BOX (vbox2)); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, + _("Color _profile policy:"), + GTK_TABLE (table), 0, size_group); + + /* Export Policies */ + vbox2 = prefs_frame_new (_("Export Policies"), + GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "export-color-profile", + _("Export the i_mage's color profile by default"), + GTK_BOX (vbox2)); + button = prefs_check_button_add (object, "export-metadata-exif", + /* Translators: label for + * configuration option (checkbox). + * It determines how file export + * plug-ins handle Exif by default. + */ + _("Export _Exif metadata by default when available"), + GTK_BOX (vbox2)); + button = prefs_check_button_add (object, "export-metadata-xmp", + /* Translators: label for + * configuration option (checkbox). + * It determines how file export + * plug-ins handle XMP by default. + */ + _("Export _XMP metadata by default when available"), + GTK_BOX (vbox2)); + button = prefs_check_button_add (object, "export-metadata-iptc", + /* Translators: label for + * configuration option (checkbox). + * It determines how file export + * plug-ins handle IPTC by default. + */ + _("Export _IPTC metadata by default when available"), + GTK_BOX (vbox2)); + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("Metadata can contain sensitive information.")); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + /* Export File Type */ + vbox2 = prefs_frame_new (_("Export File Type"), GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "export-file-type", 0, 0, + _("Default export file t_ype:"), + GTK_TABLE (table), 0, size_group); + + /* Raw Image Importer */ + vbox2 = prefs_frame_new (_("Raw Image Importer"), + GTK_CONTAINER (vbox), TRUE); + + { + GtkWidget *scrolled_window; + GtkWidget *view; + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0); + gtk_widget_show (scrolled_window); + + view = gimp_plug_in_view_new (gimp->plug_in_manager->display_raw_load_procs); + gimp_plug_in_view_set_plug_in (GIMP_PLUG_IN_VIEW (view), + core_config->import_raw_plug_in); + gtk_container_add (GTK_CONTAINER (scrolled_window), view); + gtk_widget_show (view); + + g_signal_connect (view, "changed", + G_CALLBACK (prefs_import_raw_procedure_callback), + config); + } + + g_clear_object (&size_group); + + + /****************/ + /* Playground */ + /****************/ + if (gimp->show_playground) + { + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-playground", + _("Experimental Playground"), + _("Playground"), + GIMP_HELP_PREFS_PLAYGROUND, + NULL, + &top_iter); + + hbox = g_object_new (GIMP_TYPE_HINT_BOX, + "icon-name", GIMP_ICON_DIALOG_WARNING, + "hint", _("These features are unfinished, buggy " + "and may crash GIMP. It is unadvised to " + "use them unless you really know what " + "you are doing or you intend to contribute " + "patches."), + NULL); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + /* Hardware Acceleration */ + vbox2 = prefs_frame_new (_("Hardware Acceleration"), GTK_CONTAINER (vbox), + FALSE); + + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("OpenCL drivers and support are experimental, " + "expect slowdowns and possible crashes " + "(please report).")); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + + prefs_check_button_add (object, "use-opencl", + _("Use O_penCL"), + GTK_BOX (vbox2)); + + /* Very unstable tools */ + vbox2 = prefs_frame_new (_("Insane Options"), + GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "playground-npd-tool", + _("_N-Point Deformation tool"), + GTK_BOX (vbox2)); + button = prefs_check_button_add (object, "playground-seamless-clone-tool", + _("_Seamless Clone tool"), + GTK_BOX (vbox2)); + } + + + /******************/ + /* Tool Options */ + /******************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-tool-options", + C_("preferences", "Tool Options"), + C_("preferences", "Tool Options"), + GIMP_HELP_PREFS_TOOL_OPTIONS, + NULL, + &top_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* General */ + vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "edit-non-visible", + _("Allow _editing on non-visible layers"), + GTK_BOX (vbox2)); + + prefs_check_button_add (object, "save-tool-options", + _("_Save tool options on exit"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_DOCUMENT_SAVE, + _("Save Tool Options _Now"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_tool_options_save_callback), + gimp); + + button2 = prefs_button_add (GIMP_ICON_RESET, + _("_Reset Saved Tool Options to " + "Default Values"), + GTK_BOX (vbox2)); + g_signal_connect (button2, "clicked", + G_CALLBACK (prefs_tool_options_clear_callback), + gimp); + + g_object_set_data (G_OBJECT (button), "clear-button", button2); + + /* Scaling */ + vbox2 = prefs_frame_new (_("Scaling"), GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "interpolation-type", 0, 0, + _("Default _interpolation:"), + GTK_TABLE (table), 0, size_group); + + g_object_unref (size_group); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Global Brush, Pattern, ... */ + vbox2 = prefs_frame_new (_("Paint Options Shared Between Tools"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add_with_icon (object, "global-brush", + _("_Brush"), GIMP_ICON_BRUSH, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "global-dynamics", + _("_Dynamics"), GIMP_ICON_DYNAMICS, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "global-pattern", + _("_Pattern"), GIMP_ICON_PATTERN, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "global-gradient", + _("_Gradient"), GIMP_ICON_GRADIENT, + GTK_BOX (vbox2), size_group); + + /* Move Tool */ + vbox2 = prefs_frame_new (_("Move Tool"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add_with_icon (object, "move-tool-changes-active", + _("Set _layer or path as active"), + GIMP_ICON_TOOL_MOVE, + GTK_BOX (vbox2), size_group); + + g_clear_object (&size_group); + + + /*******************/ + /* Default Image */ + /*******************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-new-image", + _("Default New Image"), + _("Default Image"), + GIMP_HELP_PREFS_NEW_IMAGE, + NULL, + &top_iter); + + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + table = prefs_table_new (1, GTK_CONTAINER (vbox)); + + { + GtkWidget *combo; + + combo = gimp_container_combo_box_new (gimp->templates, + gimp_get_user_context (gimp), + 16, 0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("_Template:"), 0.0, 0.5, + combo, 1, FALSE); + + gimp_container_view_select_item (GIMP_CONTAINER_VIEW (combo), NULL); + + g_signal_connect (combo, "select-item", + G_CALLBACK (prefs_template_select_callback), + core_config->default_image); + } + + editor = gimp_template_editor_new (core_config->default_image, gimp, FALSE); + gimp_template_editor_show_advanced (GIMP_TEMPLATE_EDITOR (editor), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), editor, FALSE, FALSE, 0); + gtk_widget_show (editor); + + /* Quick Mask Color */ + vbox2 = prefs_frame_new (_("Quick Mask"), GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_color_button_add (object, "quick-mask-color", + _("Quick Mask color:"), + _("Set the default Quick Mask color"), + GTK_TABLE (table), 0, NULL, + gimp_get_user_context (gimp)); + + + /**********************************/ + /* Default Image / Default Grid */ + /**********************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-default-grid", + _("Default Image Grid"), + _("Default Grid"), + GIMP_HELP_PREFS_DEFAULT_GRID, + &top_iter, + &child_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + /* Grid */ + editor = gimp_grid_editor_new (core_config->default_grid, + gimp_get_user_context (gimp), + gimp_template_get_resolution_x (core_config->default_image), + gimp_template_get_resolution_y (core_config->default_image)); + gtk_box_pack_start (GTK_BOX (vbox), editor, TRUE, TRUE, 0); + gtk_widget_show (editor); + + + /***************/ + /* Interface */ + /***************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-interface", + _("User Interface"), + _("Interface"), + GIMP_HELP_PREFS_INTERFACE, + NULL, + &top_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + /* Language */ + + /* Only add the language entry if the iso-codes package is available. */ +#ifdef HAVE_ISO_CODES + vbox2 = prefs_frame_new (_("Language"), GTK_CONTAINER (vbox), FALSE); + + prefs_language_combo_box_add (object, "language", GTK_BOX (vbox2)); +#endif + + /* Style */ + vbox2 = prefs_frame_new (_("Style"), GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "compact-sliders", + _("Use co_mpact sliders"), + GTK_BOX (vbox2)); + + /* Previews */ + vbox2 = prefs_frame_new (_("Previews"), GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "layer-previews", + _("_Enable layer & channel previews"), + GTK_BOX (vbox2)); + + vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); + g_object_bind_property (button, "active", + vbox3, "sensitive", + G_BINDING_SYNC_CREATE); + button = prefs_check_button_add (object, "group-layer-previews", + _("Enable layer _group previews"), + GTK_BOX (vbox3)); + + table = prefs_table_new (3, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "layer-preview-size", 0, 0, + _("_Default layer & channel preview size:"), + GTK_TABLE (table), 0, NULL); + prefs_enum_combo_box_add (object, "undo-preview-size", 0, 0, + _("_Undo preview size:"), + GTK_TABLE (table), 1, NULL); + prefs_enum_combo_box_add (object, "navigation-preview-size", 0, 0, + _("Na_vigation preview size:"), + GTK_TABLE (table), 2, NULL); + + /* Keyboard Shortcuts */ + vbox2 = prefs_frame_new (_("Keyboard Shortcuts"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "can-change-accels", + _("_Use dynamic keyboard shortcuts"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_PREFERENCES_SYSTEM, + _("Configure _Keyboard Shortcuts..."), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_keyboard_shortcuts_dialog), + gimp); + + prefs_check_button_add (object, "save-accels", + _("_Save keyboard shortcuts on exit"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_DOCUMENT_SAVE, + _("Save Keyboard Shortcuts _Now"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_menus_save_callback), + gimp); + + button2 = prefs_button_add (GIMP_ICON_RESET, + _("_Reset Keyboard Shortcuts to Default Values"), + GTK_BOX (vbox2)); + g_signal_connect (button2, "clicked", + G_CALLBACK (prefs_menus_clear_callback), + gimp); + + g_object_set_data (G_OBJECT (button), "clear-button", button2); + + button = prefs_button_add (GIMP_ICON_EDIT_CLEAR, + _("Remove _All Keyboard Shortcuts"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_menus_remove_callback), + gimp); + + + /***********************/ + /* Interface / Theme */ + /***********************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-theme", + _("Theme"), + _("Theme"), + GIMP_HELP_PREFS_THEME, + &top_iter, + &child_iter); + + vbox2 = prefs_frame_new (_("Select Theme"), GTK_CONTAINER (vbox), TRUE); + + { + GtkWidget *scrolled_win; + GtkListStore *list_store; + GtkWidget *view; + GtkTreeSelection *sel; + gchar **themes; + gint n_themes; + gint i; + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (scrolled_win, -1, 80); + 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 (vbox2), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)); + gtk_container_add (GTK_CONTAINER (scrolled_win), view); + gtk_widget_show (view); + + g_object_unref (list_store); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 0, + _("Theme"), + gtk_cell_renderer_text_new (), + "text", 0, + NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 1, + _("Folder"), + gtk_cell_renderer_text_new (), + "text", 1, + NULL); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + + themes = themes_list_themes (gimp, &n_themes); + + for (i = 0; i < n_themes; i++) + { + GtkTreeIter iter; + GFile *theme_dir = themes_get_theme_dir (gimp, themes[i]); + + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + 0, themes[i], + 1, gimp_file_get_utf8_name (theme_dir), + -1); + + if (GIMP_GUI_CONFIG (object)->theme && + ! strcmp (GIMP_GUI_CONFIG (object)->theme, themes[i])) + { + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), &iter); + + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, + NULL, FALSE, 0.0, 0.0); + + gtk_tree_path_free (path); + } + } + + g_strfreev (themes); + + g_signal_connect (sel, "changed", + G_CALLBACK (prefs_theme_select_callback), + gimp); + } + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + button = prefs_button_add (GIMP_ICON_VIEW_REFRESH, + _("Reload C_urrent Theme"), + GTK_BOX (hbox)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_theme_reload_callback), + gimp); + + + /****************************/ + /* Interface / Icon Theme */ + /****************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-icon-theme", + _("Icon Theme"), + _("Icon Theme"), + GIMP_HELP_PREFS_ICON_THEME, + &top_iter, + &child_iter); + + vbox2 = prefs_frame_new (_("Select an Icon Theme"), GTK_CONTAINER (vbox), TRUE); + + { + GtkWidget *scrolled_win; + GtkWidget *icon_size_scale; + GtkListStore *list_store; + GtkWidget *view; + GtkTreeSelection *sel; + gchar **icon_themes; + gint n_icon_themes; + gint i; + + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_set_size_request (scrolled_win, -1, 80); + 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 (vbox2), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + list_store = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)); + gtk_container_add (GTK_CONTAINER (scrolled_win), view); + gtk_widget_show (view); + + g_object_unref (list_store); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 0, + NULL, + gtk_cell_renderer_pixbuf_new (), + "pixbuf", 0, + NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 1, + _("Icon Theme"), + gtk_cell_renderer_text_new (), + "text", 1, + NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 2, + _("Folder"), + gtk_cell_renderer_text_new (), + "text", 2, + NULL); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + + icon_themes = icon_themes_list_themes (gimp, &n_icon_themes); + + for (i = 0; i < n_icon_themes; i++) + { + GtkTreeIter iter; + GFile *icon_theme_dir = icon_themes_get_theme_dir (gimp, icon_themes[i]); + GFile *icon_theme_search_path = g_file_get_parent (icon_theme_dir); + GtkIconTheme *theme; + gchar *example; + GdkPixbuf *pixbuf; + + theme = gtk_icon_theme_new (); + gtk_icon_theme_prepend_search_path (theme, gimp_file_get_utf8_name(icon_theme_search_path)); + g_object_unref (icon_theme_search_path); + gtk_icon_theme_set_custom_theme (theme, icon_themes[i]); + + example = gtk_icon_theme_get_example_icon_name (theme); + if (! example) + { + /* If the icon theme didn't explicitly specify an example + * icon, try "gimp-wilber". + */ + example = g_strdup ("gimp-wilber"); + } + pixbuf = gtk_icon_theme_load_icon (theme, example, 16, 0, NULL); + + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + 0, pixbuf, + 1, icon_themes[i], + 2, gimp_file_get_utf8_name (icon_theme_dir), + -1); + g_object_unref (theme); + g_object_unref (pixbuf); + g_free (example); + + if (GIMP_GUI_CONFIG (object)->icon_theme && + ! strcmp (GIMP_GUI_CONFIG (object)->icon_theme, icon_themes[i])) + { + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), &iter); + + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, + NULL, FALSE, 0.0, 0.0); + + gtk_tree_path_free (path); + } + } + + g_strfreev (icon_themes); + + g_signal_connect (sel, "changed", + G_CALLBACK (prefs_icon_theme_select_callback), + gimp); + + icon_size_scale = gimp_icon_size_scale_new (gimp); + gtk_box_pack_start (GTK_BOX (vbox), icon_size_scale, FALSE, FALSE, 0); + gtk_widget_show (icon_size_scale); + } + + + /*************************/ + /* Interface / Toolbox */ + /*************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-toolbox", + _("Toolbox"), + _("Toolbox"), + GIMP_HELP_PREFS_TOOLBOX, + &top_iter, + &child_iter); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Appearance */ + vbox2 = prefs_frame_new (_("Appearance"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add_with_icon (object, "toolbox-wilber", + _("Show GIMP _logo (drag-and-drop target)"), + GIMP_ICON_WILBER, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "toolbox-color-area", + _("Show _foreground & background color"), + GIMP_ICON_COLORS_DEFAULT, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "toolbox-foo-area", + _("Show active _brush, pattern & gradient"), + GIMP_ICON_BRUSH, + GTK_BOX (vbox2), size_group); + prefs_check_button_add_with_icon (object, "toolbox-image-area", + _("Show active _image"), + GIMP_ICON_IMAGE, + GTK_BOX (vbox2), size_group); + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start (GTK_BOX (vbox2), separator, FALSE, FALSE, 0); + gtk_widget_show (separator); + + button = prefs_check_button_add_with_icon (object, "toolbox-groups", + _("Use tool _groups"), + NULL, + GTK_BOX (vbox2), size_group); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new (NULL); + gtk_misc_set_padding (GTK_MISC (label), 2, 2); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + gtk_size_group_add_widget (size_group, label); + + vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (hbox), TRUE); + g_object_bind_property (button, "active", + vbox3, "sensitive", + G_BINDING_SYNC_CREATE); + + table = prefs_table_new (1, GTK_CONTAINER (vbox3)); + + prefs_enum_combo_box_add (object, "toolbox-group-menu-mode", 0, 0, + _("_Menu mode:"), + GTK_TABLE (table), 0, + NULL); + + g_clear_object (&size_group); + + /* Tool Editor */ + vbox2 = prefs_frame_new (_("Tools Configuration"), + GTK_CONTAINER (vbox), TRUE); + tool_editor = gimp_tool_editor_new (gimp->tool_item_list, gimp->user_context, + GIMP_VIEW_SIZE_SMALL, 1); + + gtk_box_pack_start (GTK_BOX (vbox2), tool_editor, TRUE, TRUE, 0); + gtk_widget_show (tool_editor); + + + /*********************************/ + /* Interface / Dialog Defaults */ + /*********************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + /* FIXME need an icon */ + "gimp-prefs-controllers", + _("Dialog Defaults"), + _("Dialog Defaults"), + GIMP_HELP_PREFS_DIALOG_DEFAULTS, + &top_iter, + &child_iter); + + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + button = gimp_prefs_box_set_page_resettable (GIMP_PREFS_BOX (prefs_box), + vbox, + _("Reset Dialog _Defaults")); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_dialog_defaults_reset), + config); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Color profile import dialog */ + vbox2 = prefs_frame_new (_("Color Profile Import Dialog"), GTK_CONTAINER (vbox), + FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, + _("Color profile policy:"), + GTK_TABLE (table), 0, size_group); + + /* All color profile chooser dialogs */ + vbox2 = prefs_frame_new (_("Color Profile File Dialogs"), GTK_CONTAINER (vbox), + FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_file_chooser_button_add (object, "color-profile-path", + _("Profile folder:"), + _("Select Default Folder for Color Profiles"), + GTK_TABLE (table), 0, size_group); + + /* Convert to Color Profile Dialog */ + vbox2 = prefs_frame_new (_("Convert to Color Profile Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "image-convert-profile-intent", 0, 0, + _("Rendering intent:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "image-convert-profile-black-point-compensation", + _("Black point compensation"), + GTK_BOX (vbox2)); + + /* Convert Precision Dialog */ + vbox2 = prefs_frame_new (_("Precision Conversion Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (3, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, + "image-convert-precision-layer-dither-method", + 0, 0, + _("Dither layers:"), + GTK_TABLE (table), 0, size_group); + prefs_enum_combo_box_add (object, + "image-convert-precision-text-layer-dither-method", + 0, 0, + _("Dither text layers:"), + GTK_TABLE (table), 1, size_group); + prefs_enum_combo_box_add (object, + "image-convert-precision-channel-dither-method", + 0, 0, + _("Dither channels/masks:"), + GTK_TABLE (table), 2, size_group); + + /* Convert Indexed Dialog */ + vbox2 = prefs_frame_new (_("Indexed Conversion Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "image-convert-indexed-palette-type", 0, 0, + _("Colormap:"), + GTK_TABLE (table), 0, size_group); + prefs_spin_button_add (object, "image-convert-indexed-max-colors", 1.0, 8.0, 0, + _("Maximum number of colors:"), + GTK_TABLE (table), 1, size_group); + + prefs_check_button_add (object, "image-convert-indexed-remove-duplicates", + _("Remove unused and duplicate colors " + "from colormap"), + GTK_BOX (vbox2)); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + prefs_enum_combo_box_add (object, "image-convert-indexed-dither-type", 0, 0, + _("Color dithering:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "image-convert-indexed-dither-alpha", + _("Enable dithering of transparency"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "image-convert-indexed-dither-text-layers", + _("Enable dithering of text layers"), + GTK_BOX (vbox2)); + + /* Filter Dialogs */ + vbox2 = prefs_frame_new (_("Filter Dialogs"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "filter-tool-max-recent", 1.0, 8.0, 0, + _("Keep recent settings:"), + GTK_TABLE (table), 1, size_group); + + button = prefs_check_button_add (object, "filter-tool-use-last-settings", + _("Default to the last used settings"), + GTK_BOX (vbox2)); + button = prefs_check_button_add (object, "filter-tool-show-color-options", + _("Show advanced color options"), + GTK_BOX (vbox2)); + + /* Canvas Size Dialog */ + vbox2 = prefs_frame_new (_("Canvas Size Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "image-resize-fill-type", 0, 0, + _("Fill with:"), + GTK_TABLE (table), 0, size_group); + prefs_enum_combo_box_add (object, "image-resize-layer-set", 0, 0, + _("Resize layers:"), + GTK_TABLE (table), 1, size_group); + + prefs_check_button_add (object, "image-resize-resize-text-layers", + _("Resize text layers"), + GTK_BOX (vbox2)); + + /* New Layer Dialog */ + vbox2 = prefs_frame_new (_("New Layer Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_entry_add (object, "layer-new-name", + _("Layer name:"), + GTK_TABLE (table), 0, size_group); + + prefs_enum_combo_box_add (object, "layer-new-fill-type", 0, 0, + _("Fill type:"), + GTK_TABLE (table), 1, size_group); + + /* Layer Boundary Size Dialog */ + vbox2 = prefs_frame_new (_("Layer Boundary Size Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "layer-resize-fill-type", 0, 0, + _("Fill with:"), + GTK_TABLE (table), 0, size_group); + + /* Add Layer Mask Dialog */ + vbox2 = prefs_frame_new (_("Add Layer Mask Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "layer-add-mask-type", 0, 0, + _("Layer mask type:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "layer-add-mask-invert", + _("Invert mask"), + GTK_BOX (vbox2)); + + /* Merge Layers Dialog */ + vbox2 = prefs_frame_new (_("Merge Layers Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "layer-merge-type", + GIMP_EXPAND_AS_NECESSARY, + GIMP_CLIP_TO_BOTTOM_LAYER, + _("Merged layer size:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "layer-merge-active-group-only", + _("Merge within active group only"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "layer-merge-discard-invisible", + _("Discard invisible layers"), + GTK_BOX (vbox2)); + + /* New Channel Dialog */ + vbox2 = prefs_frame_new (_("New Channel Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_entry_add (object, "channel-new-name", + _("Channel name:"), + GTK_TABLE (table), 0, size_group); + + prefs_color_button_add (object, "channel-new-color", + _("Color and opacity:"), + _("Default New Channel Color and Opacity"), + GTK_TABLE (table), 1, size_group, + gimp_get_user_context (gimp)); + + /* New Path Dialog */ + vbox2 = prefs_frame_new (_("New Path Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_entry_add (object, "path-new-name", + _("Path name:"), + GTK_TABLE (table), 0, size_group); + + /* Export Path Dialog */ + vbox2 = prefs_frame_new (_("Export Paths Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_file_chooser_button_add (object, "path-export-path", + _("Export folder:"), + _("Select Default Folder for Exporting Paths"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "path-export-active-only", + _("Export the active path only"), + GTK_BOX (vbox2)); + + /* Import Path Dialog */ + vbox2 = prefs_frame_new (_("Import Paths Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_file_chooser_button_add (object, "path-import-path", + _("Import folder:"), + _("Select Default Folder for Importing Paths"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "path-import-merge", + _("Merge imported paths"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "path-import-scale", + _("Scale imported paths"), + GTK_BOX (vbox2)); + + /* Feather Selection Dialog */ + vbox2 = prefs_frame_new (_("Feather Selection Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "selection-feather-radius", 1.0, 10.0, 2, + _("Feather radius:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "selection-feather-edge-lock", + _("Selected areas continue outside the image"), + GTK_BOX (vbox2)); + + /* Grow Selection Dialog */ + vbox2 = prefs_frame_new (_("Grow Selection Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "selection-grow-radius", 1.0, 10.0, 0, + _("Grow radius:"), + GTK_TABLE (table), 0, size_group); + + /* Shrink Selection Dialog */ + vbox2 = prefs_frame_new (_("Shrink Selection Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "selection-shrink-radius", 1.0, 10.0, 0, + _("Shrink radius:"), + GTK_TABLE (table), 0, size_group); + + prefs_check_button_add (object, "selection-shrink-edge-lock", + _("Selected areas continue outside the image"), + GTK_BOX (vbox2)); + + /* Border Selection Dialog */ + vbox2 = prefs_frame_new (_("Border Selection Dialog"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "selection-border-radius", 1.0, 10.0, 0, + _("Border radius:"), + GTK_TABLE (table), 0, size_group); + + prefs_enum_combo_box_add (object, "selection-border-style", 0, 0, + _("Border style:"), + GTK_TABLE (table), 1, size_group); + + prefs_check_button_add (object, "selection-border-edge-lock", + _("Selected areas continue outside the image"), + GTK_BOX (vbox2)); + + /* Fill Options Dialog */ + vbox2 = prefs_frame_new (_("Fill Selection Outline & Fill Path Dialogs"), + GTK_CONTAINER (vbox), FALSE); + + table = gimp_fill_editor_new (GIMP_DIALOG_CONFIG (object)->fill_options, + FALSE); + gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + /* Stroke Options Dialog */ + vbox2 = prefs_frame_new (_("Stroke Selection & Stroke Path Dialogs"), + GTK_CONTAINER (vbox), FALSE); + + /* The stroke line width physical values could be based on either the + * x or y resolution, some average, or whatever which makes a bit of + * sense. There is no perfect answer. The actual stroke dialog though + * uses the y resolution on the opened image. So using the y resolution + * of the default image seems like the best compromise in the preferences. + */ + table = gimp_stroke_editor_new (GIMP_DIALOG_CONFIG (object)->stroke_options, + gimp_template_get_resolution_y (core_config->default_image), + FALSE); + gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + g_clear_object (&size_group); + + + /*****************************/ + /* Interface / Help System */ + /*****************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-help-system", + _("Help System"), + _("Help System"), + GIMP_HELP_PREFS_HELP, + &top_iter, + &child_iter); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* General */ + vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "show-tooltips", + _("Show _tooltips"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "show-help-button", + _("Show help _buttons"), + GTK_BOX (vbox2)); + + table = prefs_table_new (3, GTK_CONTAINER (vbox2)); + button = prefs_boolean_combo_box_add (object, "user-manual-online", + _("Use the online version"), + _("Use a locally installed copy"), + _("U_ser manual:"), + GTK_TABLE (table), 0, size_group); + gimp_help_set_help_data (button, NULL, NULL); + + manuals = gimp_help_get_installed_languages (); + entry = NULL; + if (manuals != NULL) + { + gchar *help_locales = NULL; + + entry = gimp_language_combo_box_new (TRUE, + _("User interface language")); + + g_object_get (config, "help-locales", &help_locales, NULL); + if (help_locales && strlen (help_locales)) + { + gchar *sep; + + sep = strchr (help_locales, ':'); + if (sep) + *sep = '\0'; + } + if (help_locales) + { + gimp_language_combo_box_set_code (GIMP_LANGUAGE_COMBO_BOX (entry), + help_locales); + g_free (help_locales); + } + else + { + gimp_language_combo_box_set_code (GIMP_LANGUAGE_COMBO_BOX (entry), + ""); + } + g_signal_connect (entry, "changed", + G_CALLBACK (prefs_help_language_change_callback), + gimp); + gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 1, 2); + gtk_widget_show (entry); + } + + if (gimp_help_user_manual_is_installed (gimp)) + { + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_INFORMATION, + _("There's a local installation " + "of the user manual.")); + } + else + { + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("The user manual is not installed " + "locally.")); + } + if (manuals) + { + g_object_set_data (G_OBJECT (hbox), "gimp", gimp); + g_signal_connect (entry, "changed", + G_CALLBACK (prefs_help_language_change_callback2), + hbox); + g_list_free_full (manuals, g_free); + } + + gtk_table_attach_defaults (GTK_TABLE (table), hbox, 1, 2, 2, 3); + gtk_widget_show (hbox); + + /* Help Browser */ +#ifdef HAVE_WEBKIT + /* If there is no webkit available, assume we are on a platform + * that doesn't use the help browser, so don't bother showing + * the combo. + */ + vbox2 = prefs_frame_new (_("Help Browser"), GTK_CONTAINER (vbox), FALSE); + + if (gimp_help_browser_is_installed (gimp)) + { + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + button = prefs_enum_combo_box_add (object, "help-browser", 0, 0, + _("H_elp browser to use:"), + GTK_TABLE (table), 0, size_group); + } + else + { + hbox = prefs_hint_box_new (GIMP_ICON_DIALOG_WARNING, + _("The GIMP help browser doesn't seem to " + "be installed. Using the web browser " + "instead.")); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + g_object_set (config, + "help-browser", GIMP_HELP_BROWSER_WEB_BROWSER, + NULL); + } +#else + g_object_set (config, + "help-browser", GIMP_HELP_BROWSER_WEB_BROWSER, + NULL); +#endif /* HAVE_WEBKIT */ + + /* Action Search */ + vbox2 = prefs_frame_new (_("Action Search"), GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_check_button_add (object, "search-show-unavailable-actions", + _("Show _unavailable actions"), + GTK_BOX (vbox2)); + prefs_spin_button_add (object, "action-history-size", 1.0, 10.0, 0, + _("_Maximum History Size:"), + GTK_TABLE (table), 0, size_group); + + button = prefs_button_add (GIMP_ICON_SHRED, + _("C_lear Action History"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_search_clear_callback), + gimp); + + g_clear_object (&size_group); + + + /*************************/ + /* Interface / Display */ + /*************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-display", + _("Display"), + _("Display"), + GIMP_HELP_PREFS_DISPLAY, + &top_iter, + &child_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Transparency */ + vbox2 = prefs_frame_new (_("Transparency"), GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "transparency-type", 0, 0, + _("_Check style:"), + GTK_TABLE (table), 0, size_group); + prefs_enum_combo_box_add (object, "transparency-size", 0, 0, + _("Check _size:"), + GTK_TABLE (table), 1, size_group); + + vbox2 = prefs_frame_new (_("Monitor Resolution"), + GTK_CONTAINER (vbox), FALSE); + + { + gchar *pixels_per_unit = g_strconcat (_("Pixels"), "/%s", NULL); + + entry = gimp_prop_coordinates_new (object, + "monitor-xresolution", + "monitor-yresolution", + NULL, + pixels_per_unit, + GIMP_SIZE_ENTRY_UPDATE_RESOLUTION, + 0.0, 0.0, + TRUE); + + g_free (pixels_per_unit); + } + + gtk_table_set_col_spacings (GTK_TABLE (entry), 2); + gtk_table_set_row_spacings (GTK_TABLE (entry), 2); + + gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry), + _("Horizontal"), 0, 1, 0.0); + gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry), + _("Vertical"), 0, 2, 0.0); + gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry), + _("ppi"), 1, 4, 0.0); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 24); + gtk_widget_show (entry); + gtk_widget_set_sensitive (entry, ! display_config->monitor_res_from_gdk); + + group = NULL; + + { + gdouble xres; + gdouble yres; + gchar *str; + + gimp_get_monitor_resolution (gdk_screen_get_default (), /* FIXME monitor */ + 0, /* FIXME monitor */ + &xres, &yres); + + str = g_strdup_printf (_("_Detect automatically (currently %d × %d ppi)"), + ROUND (xres), ROUND (yres)); + + button = gtk_radio_button_new_with_mnemonic (group, str); + + g_free (str); + } + + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); + gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + g_object_set_data (G_OBJECT (button), "monitor_resolution_sizeentry", entry); + + g_signal_connect (button, "toggled", + G_CALLBACK (prefs_resolution_source_callback), + config); + + button = gtk_radio_button_new_with_mnemonic (group, _("_Enter manually")); + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); + gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + if (! display_config->monitor_res_from_gdk) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + calibrate_button = gtk_button_new_with_mnemonic (_("C_alibrate...")); + label = gtk_bin_get_child (GTK_BIN (calibrate_button)); + gtk_misc_set_padding (GTK_MISC (label), 4, 0); + gtk_box_pack_start (GTK_BOX (hbox), calibrate_button, FALSE, FALSE, 0); + gtk_widget_show (calibrate_button); + gtk_widget_set_sensitive (calibrate_button, + ! display_config->monitor_res_from_gdk); + + g_object_bind_property (button, "active", + entry, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_bind_property (button, "active", + calibrate_button, "sensitive", + G_BINDING_SYNC_CREATE); + + g_signal_connect (calibrate_button, "clicked", + G_CALLBACK (prefs_resolution_calibrate_callback), + entry); + + g_clear_object (&size_group); + + + /***********************************/ + /* Interface / Window Management */ + /***********************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-window-management", + _("Window Management"), + _("Window Management"), + GIMP_HELP_PREFS_WINDOW_MANAGEMENT, + &top_iter, + &child_iter); + + vbox2 = prefs_frame_new (_("Window Manager Hints"), + GTK_CONTAINER (vbox), FALSE); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "dock-window-hint", 0, 0, + _("Hint for _docks and toolbox:"), + GTK_TABLE (table), 1, NULL); + + vbox2 = prefs_frame_new (_("Focus"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "activate-on-focus", + _("Activate the _focused image"), + GTK_BOX (vbox2)); + + /* Window Positions */ + vbox2 = prefs_frame_new (_("Window Positions"), GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "save-session-info", + _("_Save window positions on exit"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "restore-monitor", + _("Open windows on the same _monitor they were open before"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_DOCUMENT_SAVE, + _("Save Window Positions _Now"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_session_save_callback), + gimp); + + button2 = prefs_button_add (GIMP_ICON_RESET, + _("_Reset Saved Window Positions to " + "Default Values"), + GTK_BOX (vbox2)); + g_signal_connect (button2, "clicked", + G_CALLBACK (prefs_session_clear_callback), + gimp); + + g_object_set_data (G_OBJECT (button), "clear-button", button2); + + + /*******************/ + /* Image Windows */ + /*******************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-image-windows", + _("Image Windows"), + _("Image Windows"), + GIMP_HELP_PREFS_IMAGE_WINDOW, + NULL, + &top_iter); + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* General */ + vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "default-show-all", + _("Use \"Show _all\" by default"), + GTK_BOX (vbox2)); + + prefs_check_button_add (object, "default-dot-for-dot", + _("Use \"_Dot for dot\" by default"), + GTK_BOX (vbox2)); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "marching-ants-speed", 1.0, 10.0, 0, + _("Marching ants s_peed:"), + GTK_TABLE (table), 0, size_group); + + /* Zoom & Resize Behavior */ + vbox2 = prefs_frame_new (_("Zoom & Resize Behavior"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "resize-windows-on-zoom", + _("Resize window on _zoom"), + GTK_BOX (vbox2)); + prefs_check_button_add (object, "resize-windows-on-resize", + _("Resize window on image _size change"), + GTK_BOX (vbox2)); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_boolean_combo_box_add (object, "initial-zoom-to-fit", + _("Show entire image"), + "1:1", + _("Initial zoom _ratio:"), + GTK_TABLE (table), 0, size_group); + + /* Space Bar */ + vbox2 = prefs_frame_new (_("Space Bar"), + GTK_CONTAINER (vbox), FALSE); + + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "space-bar-action", 0, 0, + _("_While space bar is pressed:"), + GTK_TABLE (table), 0, size_group); + + /* Mouse Pointers */ + vbox2 = prefs_frame_new (_("Mouse Pointers"), + GTK_CONTAINER (vbox), FALSE); + + button = prefs_check_button_add (object, "show-brush-outline", + _("Show _brush outline"), + GTK_BOX (vbox2)); + + vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); + g_object_bind_property (button, "active", + vbox3, "sensitive", + G_BINDING_SYNC_CREATE); + prefs_check_button_add (object, "snap-brush-outline", + _("S_nap brush outline to stroke"), + GTK_BOX (vbox3)); + + prefs_check_button_add (object, "show-paint-tool-cursor", + _("Show pointer for paint _tools"), + GTK_BOX (vbox2)); + + table = prefs_table_new (2, GTK_CONTAINER (vbox2)); + + prefs_enum_combo_box_add (object, "cursor-mode", 0, 0, + _("Pointer _mode:"), + GTK_TABLE (table), 0, size_group); + prefs_enum_combo_box_add (object, "cursor-handedness", 0, 0, + _("Pointer _handedness:"), + GTK_TABLE (table), 1, NULL); + + g_clear_object (&size_group); + + + /********************************/ + /* Image Windows / Appearance */ + /********************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-image-windows-appearance", + _("Image Window Appearance"), + _("Appearance"), + GIMP_HELP_PREFS_IMAGE_WINDOW_APPEARANCE, + &top_iter, + &child_iter); + + gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), vbox, TRUE); + + prefs_display_options_frame_add (gimp, + G_OBJECT (display_config->default_view), + _("Default Appearance in Normal Mode"), + GTK_CONTAINER (vbox)); + + prefs_display_options_frame_add (gimp, + G_OBJECT (display_config->default_fullscreen_view), + _("Default Appearance in Fullscreen Mode"), + GTK_CONTAINER (vbox)); + + + /****************************************************/ + /* Image Windows / Image Title & Statusbar Format */ + /****************************************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-image-title", + _("Image Title & Statusbar Format"), + _("Title & Status"), + GIMP_HELP_PREFS_IMAGE_WINDOW_TITLE, + &top_iter, + &child_iter); + + { + const gchar *format_strings[] = + { + NULL, + NULL, + "%f-%p.%i (%t) %z%%", + "%f-%p.%i (%t) %d:%s", + "%f-%p.%i (%t) %wx%h", + "%f-%p-%i (%t) %wx%h (%xx%y)" + }; + + const gchar *format_names[] = + { + N_("Current format"), + N_("Default format"), + N_("Show zoom percentage"), + N_("Show zoom ratio"), + N_("Show image size"), + N_("Show drawable size") + }; + + struct + { + gchar *current_setting; + const gchar *default_setting; + const gchar *title; + const gchar *property_name; + } + formats[] = + { + { NULL, GIMP_CONFIG_DEFAULT_IMAGE_TITLE_FORMAT, + N_("Image Title Format"), "image-title-format" }, + { NULL, GIMP_CONFIG_DEFAULT_IMAGE_STATUS_FORMAT, + N_("Image Statusbar Format"), "image-status-format" } + }; + + gint format; + + gimp_assert (G_N_ELEMENTS (format_strings) == G_N_ELEMENTS (format_names)); + + formats[0].current_setting = display_config->image_title_format; + formats[1].current_setting = display_config->image_status_format; + + for (format = 0; format < G_N_ELEMENTS (formats); format++) + { + GtkWidget *scrolled_win; + GtkListStore *list_store; + GtkWidget *view; + GtkTreeSelection *sel; + gint i; + + format_strings[0] = formats[format].current_setting; + format_strings[1] = formats[format].default_setting; + + vbox2 = prefs_frame_new (gettext (formats[format].title), + GTK_CONTAINER (vbox), TRUE); + + entry = gimp_prop_entry_new (object, formats[format].property_name, 0); + gtk_box_pack_start (GTK_BOX (vbox2), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + 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 (vbox2), scrolled_win, TRUE, TRUE, 0); + gtk_widget_show (scrolled_win); + + list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); + gtk_container_add (GTK_CONTAINER (scrolled_win), view); + gtk_widget_show (view); + + g_object_unref (list_store); + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 0, + NULL, + gtk_cell_renderer_text_new (), + "text", 0, + NULL); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), 1, + NULL, + gtk_cell_renderer_text_new (), + "text", 1, + NULL); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + + for (i = 0; i < G_N_ELEMENTS (format_strings); i++) + { + GtkTreeIter iter; + + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + 0, gettext (format_names[i]), + 1, format_strings[i], + -1); + + if (i == 0) + gtk_tree_selection_select_iter (sel, &iter); + } + + g_signal_connect (sel, "changed", + G_CALLBACK (prefs_format_string_select_callback), + entry); + } + } + + + /******************************/ + /* Image Windows / Snapping */ + /******************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-image-windows-snapping", + _("Image Window Snapping Behavior"), + _("Snapping"), + GIMP_HELP_PREFS_IMAGE_WINDOW_SNAPPING, + &top_iter, + &child_iter); + + prefs_behavior_options_frame_add (gimp, + G_OBJECT (display_config->default_view), + _("Default Behavior in Normal Mode"), + GTK_CONTAINER (vbox)); + prefs_behavior_options_frame_add (gimp, + G_OBJECT (display_config->default_fullscreen_view), + _("Default Behavior in Fullscreen Mode"), + GTK_CONTAINER (vbox)); + + /* Snapping Distance */ + vbox2 = prefs_frame_new (_("General"), + GTK_CONTAINER (vbox), FALSE); + table = prefs_table_new (1, GTK_CONTAINER (vbox2)); + + prefs_spin_button_add (object, "snap-distance", 1.0, 5.0, 0, + _("_Snapping distance:"), + GTK_TABLE (table), 0, NULL); + + + /*******************/ + /* Input Devices */ + /*******************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-input-devices", + _("Input Devices"), + _("Input Devices"), + GIMP_HELP_PREFS_INPUT_DEVICES, + NULL, + &top_iter); + + /* Extended Input Devices */ + vbox2 = prefs_frame_new (_("Extended Input Devices"), + GTK_CONTAINER (vbox), FALSE); + + prefs_check_button_add (object, "devices-share-tool", + _("S_hare tool and tool options between input devices"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_PREFERENCES_SYSTEM, + _("Configure E_xtended Input Devices..."), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_input_devices_dialog), + gimp); + + prefs_check_button_add (object, "save-device-status", + _("_Save input device settings on exit"), + GTK_BOX (vbox2)); + + button = prefs_button_add (GIMP_ICON_DOCUMENT_SAVE, + _("Save Input Device Settings _Now"), + GTK_BOX (vbox2)); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_devices_save_callback), + gimp); + + button2 = prefs_button_add (GIMP_ICON_RESET, + _("_Reset Saved Input Device Settings to " + "Default Values"), + GTK_BOX (vbox2)); + g_signal_connect (button2, "clicked", + G_CALLBACK (prefs_devices_clear_callback), + gimp); + + g_object_set_data (G_OBJECT (button), "clear-button", button2); + + + /****************************/ + /* Additional Controllers */ + /****************************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-controllers", + _("Additional Input Controllers"), + _("Input Controllers"), + GIMP_HELP_PREFS_INPUT_CONTROLLERS, + &top_iter, + &child_iter); + + vbox2 = gimp_controller_list_new (gimp); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); + gtk_widget_show (vbox2); + + + /*************/ + /* Folders */ + /*************/ + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + "gimp-prefs-folders", + _("Folders"), + _("Folders"), + GIMP_HELP_PREFS_FOLDERS, + NULL, + &top_iter); + + button = gimp_prefs_box_set_page_resettable (GIMP_PREFS_BOX (prefs_box), + vbox, + _("Reset _Folders")); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_folders_reset), + config); + + { + static const struct + { + const gchar *property_name; + const gchar *label; + const gchar *dialog_title; + } + dirs[] = + { + { + "temp-path", + N_("_Temporary folder:"), + N_("Select Folder for Temporary Files") + }, + { + "swap-path", + N_("_Swap folder:"), + N_("Select Swap Folder") + } + }; + + table = prefs_table_new (G_N_ELEMENTS (dirs) + 1, GTK_CONTAINER (vbox)); + + for (i = 0; i < G_N_ELEMENTS (dirs); i++) + { + prefs_file_chooser_button_add (object, dirs[i].property_name, + gettext (dirs[i].label), + gettext (dirs[i].dialog_title), + GTK_TABLE (table), i, NULL); + } + } + + + /*********************/ + /* Folders / */ + /*********************/ + { + static const struct + { + const gchar *tree_label; + const gchar *label; + const gchar *icon; + const gchar *help_data; + const gchar *reset_label; + const gchar *fs_label; + const gchar *path_property_name; + const gchar *writable_property_name; + } + paths[] = + { + { N_("Brushes"), N_("Brush Folders"), + "folders-brushes", + GIMP_HELP_PREFS_FOLDERS_BRUSHES, + N_("Reset Brush _Folders"), + N_("Select Brush Folders"), + "brush-path", "brush-path-writable" }, + { N_("Dynamics"), N_("Dynamics Folders"), + "folders-dynamics", + GIMP_HELP_PREFS_FOLDERS_DYNAMICS, + N_("Reset Dynamics _Folders"), + N_("Select Dynamics Folders"), + "dynamics-path", "dynamics-path-writable" }, + { N_("Patterns"), N_("Pattern Folders"), + "folders-patterns", + GIMP_HELP_PREFS_FOLDERS_PATTERNS, + N_("Reset Pattern _Folders"), + N_("Select Pattern Folders"), + "pattern-path", "pattern-path-writable" }, + { N_("Palettes"), N_("Palette Folders"), + "folders-palettes", + GIMP_HELP_PREFS_FOLDERS_PALETTES, + N_("Reset Palette _Folders"), + N_("Select Palette Folders"), + "palette-path", "palette-path-writable" }, + { N_("Gradients"), N_("Gradient Folders"), + "folders-gradients", + GIMP_HELP_PREFS_FOLDERS_GRADIENTS, + N_("Reset Gradient _Folders"), + N_("Select Gradient Folders"), + "gradient-path", "gradient-path-writable" }, + { N_("Fonts"), N_("Font Folders"), + "folders-fonts", + GIMP_HELP_PREFS_FOLDERS_FONTS, + N_("Reset Font _Folders"), + N_("Select Font Folders"), + "font-path", NULL }, + { N_("Tool Presets"), N_("Tool Preset Folders"), + "folders-tool-presets", + GIMP_HELP_PREFS_FOLDERS_TOOL_PRESETS, + N_("Reset Tool Preset _Folders"), + N_("Select Tool Preset Folders"), + "tool-preset-path", "tool-preset-path-writable" }, + { N_("MyPaint Brushes"), N_("MyPaint Brush Folders"), + "folders-mypaint-brushes", + GIMP_HELP_PREFS_FOLDERS_MYPAINT_BRUSHES, + N_("Reset MyPaint Brush _Folders"), + N_("Select MyPaint Brush Folders"), + "mypaint-brush-path", "mypaint-brush-path-writable" }, + { N_("Plug-ins"), N_("Plug-in Folders"), + "folders-plug-ins", + GIMP_HELP_PREFS_FOLDERS_PLUG_INS, + N_("Reset plug-in _Folders"), + N_("Select plug-in Folders"), + "plug-in-path", NULL }, + { N_("Scripts"), N_("Script-Fu Folders"), + "folders-scripts", + GIMP_HELP_PREFS_FOLDERS_SCRIPTS, + N_("Reset Script-Fu _Folders"), + N_("Select Script-Fu Folders"), + "script-fu-path", NULL }, + { N_("Modules"), N_("Module Folders"), + "folders-modules", + GIMP_HELP_PREFS_FOLDERS_MODULES, + N_("Reset Module _Folders"), + N_("Select Module Folders"), + "module-path", NULL }, + { N_("Interpreters"), N_("Interpreter Folders"), + "folders-interp", + GIMP_HELP_PREFS_FOLDERS_INTERPRETERS, + N_("Reset Interpreter _Folders"), + N_("Select Interpreter Folders"), + "interpreter-path", NULL }, + { N_("Environment"), N_("Environment Folders"), + "folders-environ", + GIMP_HELP_PREFS_FOLDERS_ENVIRONMENT, + N_("Reset Environment _Folders"), + N_("Select Environment Folders"), + "environ-path", NULL }, + { N_("Themes"), N_("Theme Folders"), + "folders-themes", + GIMP_HELP_PREFS_FOLDERS_THEMES, + N_("Reset Theme _Folders"), + N_("Select Theme Folders"), + "theme-path", NULL }, + { N_("Icon Themes"), N_("Icon Theme Folders"), + "folders-icon-themes", + GIMP_HELP_PREFS_FOLDERS_ICON_THEMES, + N_("Reset Icon Theme _Folders"), + N_("Select Icon Theme Folders"), + "icon-theme-path", NULL } + }; + + for (i = 0; i < G_N_ELEMENTS (paths); i++) + { + GtkWidget *editor; + gchar *icon_name; + + icon_name = g_strconcat ("gimp-prefs-", paths[i].icon, NULL); + vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box), + icon_name, + gettext (paths[i].label), + gettext (paths[i].tree_label), + paths[i].help_data, + &top_iter, + &child_iter); + g_free (icon_name); + + button = gimp_prefs_box_set_page_resettable (GIMP_PREFS_BOX (prefs_box), + vbox, + gettext (paths[i].reset_label)); + g_object_set_data (G_OBJECT (button), "path", + (gpointer) paths[i].path_property_name); + g_object_set_data (G_OBJECT (button), "path-writable", + (gpointer) paths[i].writable_property_name); + g_signal_connect (button, "clicked", + G_CALLBACK (prefs_path_reset), + config); + + editor = gimp_prop_path_editor_new (object, + paths[i].path_property_name, + paths[i].writable_property_name, + gettext (paths[i].fs_label)); + gtk_box_pack_start (GTK_BOX (vbox), editor, TRUE, TRUE, 0); + gtk_widget_show (editor); + } + } + + { + GtkWidget *tv; + GtkTreeModel *model; + GtkTreePath *path; + + tv = gimp_prefs_box_get_tree_view (GIMP_PREFS_BOX (prefs_box)); + gtk_tree_view_expand_all (GTK_TREE_VIEW (tv)); + + /* collapse the Folders subtree */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tv)); + path = gtk_tree_model_get_path (model, &top_iter); + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tv), path); + gtk_tree_path_free (path); + } + + return dialog; +} diff --git a/app/dialogs/preferences-dialog.h b/app/dialogs/preferences-dialog.h new file mode 100644 index 0000000..899f293 --- /dev/null +++ b/app/dialogs/preferences-dialog.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 . + */ + +#ifndef __PREFERENCES_DIALOG_H__ +#define __PREFERENCES_DIALOG_H__ + + +GtkWidget * preferences_dialog_create (Gimp *gimp); + + +#endif /* __PREFERENCES_DIALOG_H__ */ diff --git a/app/dialogs/print-size-dialog.c b/app/dialogs/print-size-dialog.c new file mode 100644 index 0000000..e3b9ba0 --- /dev/null +++ b/app/dialogs/print-size-dialog.c @@ -0,0 +1,454 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimp-utils.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "print-size-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 +#define SB_WIDTH 8 + + +typedef struct _PrintSizeDialog PrintSizeDialog; + +struct _PrintSizeDialog +{ + GimpImage *image; + GimpSizeEntry *size_entry; + GimpSizeEntry *resolution_entry; + GimpChainButton *chain; + gdouble xres; + gdouble yres; + GimpResolutionCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void print_size_dialog_free (PrintSizeDialog *private); +static void print_size_dialog_response (GtkWidget *dialog, + gint response_id, + PrintSizeDialog *private); +static void print_size_dialog_reset (PrintSizeDialog *private); + +static void print_size_dialog_size_changed (GtkWidget *widget, + PrintSizeDialog *private); +static void print_size_dialog_resolution_changed (GtkWidget *widget, + PrintSizeDialog *private); +static void print_size_dialog_set_size (PrintSizeDialog *private, + gdouble width, + gdouble height); +static void print_size_dialog_set_resolution (PrintSizeDialog *private, + gdouble xres, + gdouble yres); + + +/* public functions */ + +GtkWidget * +print_size_dialog_new (GimpImage *image, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpResolutionCallback callback, + gpointer user_data) +{ + PrintSizeDialog *private; + GtkWidget *dialog; + GtkWidget *frame; + GtkWidget *table; + GtkWidget *entry; + GtkWidget *label; + GtkWidget *width; + GtkWidget *height; + GtkWidget *hbox; + GtkWidget *chain; + GtkAdjustment *adj; + GList *focus_chain = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (PrintSizeDialog); + + private->image = image; + private->callback = callback; + private->user_data = user_data; + + gimp_image_get_resolution (image, &private->xres, &private->yres); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, + title, role, + GIMP_ICON_DOCUMENT_PRINT_RESOLUTION, title, + parent, + help_func, help_id, + + _("_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); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) print_size_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (print_size_dialog_response), + private); + + frame = gimp_frame_new (_("Print Size")); + 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); + + table = gtk_table_new (4, 3, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2); + gtk_table_set_row_spacing (GTK_TABLE (table), 2, 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + /* the print size entry */ + + adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); + width = gimp_spin_button_new (adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (width), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (width), SB_WIDTH); + + adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); + height = gimp_spin_button_new (adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (height), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (height), SB_WIDTH); + + entry = gimp_size_entry_new (0, gimp_get_default_unit (), "%p", + FALSE, FALSE, FALSE, SB_WIDTH, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + private->size_entry = GIMP_SIZE_ENTRY (entry); + + label = gtk_label_new_with_mnemonic (_("_Width:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), width); + 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_with_mnemonic (_("H_eight:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), height); + 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_defaults (GTK_TABLE (table), hbox, 1, 2, 0, 2); + gtk_widget_show (hbox); + + gtk_table_set_row_spacing (GTK_TABLE (entry), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (entry), 1, 6); + + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry), + GTK_SPIN_BUTTON (height), NULL); + gtk_table_attach_defaults (GTK_TABLE (entry), height, 0, 1, 1, 2); + gtk_widget_show (height); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry), + GTK_SPIN_BUTTON (width), NULL); + gtk_table_attach_defaults (GTK_TABLE (entry), width, 0, 1, 0, 1); + gtk_widget_show (width); + + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, + private->xres, FALSE); + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 1, + private->yres, FALSE); + + gimp_size_entry_set_refval_boundaries + (GIMP_SIZE_ENTRY (entry), 0, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE); + gimp_size_entry_set_refval_boundaries + (GIMP_SIZE_ENTRY (entry), 1, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, + gimp_image_get_width (image)); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, + gimp_image_get_height (image)); + + /* the resolution entry */ + + adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); + width = gimp_spin_button_new (adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (width), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (width), SB_WIDTH); + + adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); + height = gimp_spin_button_new (adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (height), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (height), SB_WIDTH); + + label = gtk_label_new_with_mnemonic (_("_X resolution:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), width); + 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); + + label = gtk_label_new_with_mnemonic (_("_Y resolution:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), height); + 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); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_table_attach_defaults (GTK_TABLE (table), hbox, 1, 2, 2, 4); + gtk_widget_show (hbox); + + entry = gimp_size_entry_new (0, gimp_image_get_unit (image), _("pixels/%a"), + FALSE, FALSE, FALSE, SB_WIDTH, + GIMP_SIZE_ENTRY_UPDATE_RESOLUTION); + private->resolution_entry = GIMP_SIZE_ENTRY (entry); + + gtk_table_set_row_spacing (GTK_TABLE (entry), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (entry), 1, 2); + gtk_table_set_col_spacing (GTK_TABLE (entry), 2, 2); + + gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry), + GTK_SPIN_BUTTON (height), NULL); + gtk_table_attach_defaults (GTK_TABLE (entry), height, 0, 1, 1, 2); + gtk_widget_show (height); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry), + GTK_SPIN_BUTTON (width), NULL); + gtk_table_attach_defaults (GTK_TABLE (entry), width, 0, 1, 0, 1); + gtk_widget_show (width); + + 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, private->xres); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, private->yres); + + chain = gimp_chain_button_new (GIMP_CHAIN_RIGHT); + if (ABS (private->xres - private->yres) < GIMP_MIN_RESOLUTION) + gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain), TRUE); + gtk_table_attach_defaults (GTK_TABLE (entry), chain, 1, 2, 0, 2); + gtk_widget_show (chain); + + private->chain = GIMP_CHAIN_BUTTON (chain); + + focus_chain = g_list_prepend (focus_chain, GIMP_SIZE_ENTRY (entry)->unitmenu); + focus_chain = g_list_prepend (focus_chain, chain); + focus_chain = g_list_prepend (focus_chain, height); + focus_chain = g_list_prepend (focus_chain, width); + + gtk_container_set_focus_chain (GTK_CONTAINER (entry), focus_chain); + g_list_free (focus_chain); + + g_signal_connect (private->size_entry, "value-changed", + G_CALLBACK (print_size_dialog_size_changed), + private); + g_signal_connect (private->resolution_entry, "value-changed", + G_CALLBACK (print_size_dialog_resolution_changed), + private); + + return dialog; +} + + +/* private functions */ + +static void +print_size_dialog_free (PrintSizeDialog *private) +{ + g_slice_free (PrintSizeDialog, private); +} + +static void +print_size_dialog_response (GtkWidget *dialog, + gint response_id, + PrintSizeDialog *private) +{ + GimpSizeEntry *entry = private->resolution_entry; + + switch (response_id) + { + case RESPONSE_RESET: + print_size_dialog_reset (private); + break; + + case GTK_RESPONSE_OK: + private->callback (dialog, + private->image, + gimp_size_entry_get_refval (entry, 0), + gimp_size_entry_get_refval (entry, 1), + gimp_size_entry_get_unit (entry), + private->user_data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} + +static void +print_size_dialog_reset (PrintSizeDialog *private) +{ + gdouble xres, yres; + + gimp_size_entry_set_unit (private->resolution_entry, + gimp_get_default_unit ()); + + gimp_image_get_resolution (private->image, &xres, &yres); + print_size_dialog_set_resolution (private, xres, yres); +} + +static void +print_size_dialog_size_changed (GtkWidget *widget, + PrintSizeDialog *private) +{ + GimpImage *image = private->image; + gdouble width; + gdouble height; + gdouble xres; + gdouble yres; + gdouble scale; + + scale = gimp_unit_get_factor (gimp_size_entry_get_unit (private->size_entry)); + + width = gimp_size_entry_get_value (private->size_entry, 0); + height = gimp_size_entry_get_value (private->size_entry, 1); + + xres = scale * gimp_image_get_width (image) / MAX (0.001, width); + yres = scale * gimp_image_get_height (image) / MAX (0.001, height); + + xres = CLAMP (xres, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION); + yres = CLAMP (yres, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION); + + print_size_dialog_set_resolution (private, xres, yres); + print_size_dialog_set_size (private, + gimp_image_get_width (image), + gimp_image_get_height (image)); +} + +static void +print_size_dialog_resolution_changed (GtkWidget *widget, + PrintSizeDialog *private) +{ + GimpSizeEntry *entry = private->resolution_entry; + gdouble xres = gimp_size_entry_get_refval (entry, 0); + gdouble yres = gimp_size_entry_get_refval (entry, 1); + + print_size_dialog_set_resolution (private, xres, yres); +} + +static void +print_size_dialog_set_size (PrintSizeDialog *private, + gdouble width, + gdouble height) +{ + g_signal_handlers_block_by_func (private->size_entry, + print_size_dialog_size_changed, + private); + + gimp_size_entry_set_refval (private->size_entry, 0, width); + gimp_size_entry_set_refval (private->size_entry, 1, height); + + g_signal_handlers_unblock_by_func (private->size_entry, + print_size_dialog_size_changed, + private); +} + +static void +print_size_dialog_set_resolution (PrintSizeDialog *private, + gdouble xres, + gdouble yres) +{ + if (private->chain && gimp_chain_button_get_active (private->chain)) + { + if (xres != private->xres) + yres = xres; + else + xres = yres; + } + + private->xres = xres; + private->yres = yres; + + g_signal_handlers_block_by_func (private->resolution_entry, + print_size_dialog_resolution_changed, + private); + + gimp_size_entry_set_refval (private->resolution_entry, 0, xres); + gimp_size_entry_set_refval (private->resolution_entry, 1, yres); + + g_signal_handlers_unblock_by_func (private->resolution_entry, + print_size_dialog_resolution_changed, + private); + + g_signal_handlers_block_by_func (private->size_entry, + print_size_dialog_size_changed, + private); + + gimp_size_entry_set_resolution (private->size_entry, 0, xres, TRUE); + gimp_size_entry_set_resolution (private->size_entry, 1, yres, TRUE); + + g_signal_handlers_unblock_by_func (private->size_entry, + print_size_dialog_size_changed, + private); +} diff --git a/app/dialogs/print-size-dialog.h b/app/dialogs/print-size-dialog.h new file mode 100644 index 0000000..53f37f5 --- /dev/null +++ b/app/dialogs/print-size-dialog.h @@ -0,0 +1,41 @@ +/* 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 . + */ + +#ifndef __PRINT_SIZE_DIALOG_H__ +#define __PRINT_SIZE_DIALOG_H__ + + +typedef void (* GimpResolutionCallback) (GtkWidget *dialog, + GimpImage *image, + gdouble xresolution, + gdouble yresolution, + GimpUnit resolution_unit, + gpointer user_data); + + +GtkWidget * print_size_dialog_new (GimpImage *image, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpResolutionCallback callback, + gpointer user_data); + + +#endif /* __PRINT_SIZE_DIALOG_H__ */ diff --git a/app/dialogs/quit-dialog.c b/app/dialogs/quit-dialog.c new file mode 100644 index 0000000..11737f9 --- /dev/null +++ b/app/dialogs/quit-dialog.c @@ -0,0 +1,614 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2004 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpimagewindow.h" + +#include "widgets/gimpcellrendererbutton.h" +#include "widgets/gimpcontainertreestore.h" +#include "widgets/gimpcontainertreeview.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpdnd.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpviewrenderer.h" +#include "widgets/gimpwidgets-utils.h" + +#include "quit-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _QuitDialog QuitDialog; + +struct _QuitDialog +{ + Gimp *gimp; + GimpContainer *images; + GimpContext *context; + + gboolean do_quit; + + GtkWidget *dialog; + GimpContainerTreeView *tree_view; + GtkTreeViewColumn *save_column; + GtkWidget *ok_button; + GimpMessageBox *box; + GtkWidget *lost_label; + GtkWidget *hint_label; + + guint accel_key; + GdkModifierType accel_mods; +}; + + +static GtkWidget * quit_close_all_dialog_new (Gimp *gimp, + gboolean do_quit); +static void quit_close_all_dialog_free (QuitDialog *private); +static void quit_close_all_dialog_response (GtkWidget *dialog, + gint response_id, + QuitDialog *private); +static void quit_close_all_dialog_accel_marshal (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +static void quit_close_all_dialog_container_changed (GimpContainer *images, + GimpObject *image, + QuitDialog *private); +static void quit_close_all_dialog_image_selected (GimpContainerView *view, + GimpImage *image, + gpointer insert_data, + QuitDialog *private); +static void quit_close_all_dialog_name_cell_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data); +static void quit_close_all_dialog_save_clicked (GtkCellRenderer *cell, + const gchar *path, + GdkModifierType state, + QuitDialog *private); +static gboolean quit_close_all_dialog_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + QuitDialog *private); +static gboolean quit_close_all_idle (QuitDialog *private); + + +/* public functions */ + +GtkWidget * +quit_dialog_new (Gimp *gimp) +{ + return quit_close_all_dialog_new (gimp, TRUE); +} + +GtkWidget * +close_all_dialog_new (Gimp *gimp) +{ + return quit_close_all_dialog_new (gimp, FALSE); +} + + +/* private functions */ + +static GtkWidget * +quit_close_all_dialog_new (Gimp *gimp, + gboolean do_quit) +{ + QuitDialog *private; + GtkWidget *view; + GimpContainerTreeView *tree_view; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *dnd_widget; + GtkAccelGroup *accel_group; + GClosure *closure; + gint rows; + gint view_size; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + private = g_slice_new0 (QuitDialog); + + private->gimp = gimp; + private->do_quit = do_quit; + private->images = gimp_displays_get_dirty_images (gimp); + private->context = gimp_context_new (gimp, "close-all-dialog", + gimp_get_user_context (gimp)); + + g_return_val_if_fail (private->images != NULL, NULL); + + private->dialog = + gimp_message_dialog_new (do_quit ? _("Quit GIMP") : _("Close All Images"), + GIMP_ICON_DIALOG_WARNING, + NULL, 0, + gimp_standard_help_func, + do_quit ? + GIMP_HELP_FILE_QUIT : GIMP_HELP_FILE_CLOSE_ALL, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + + NULL); + + private->ok_button = gtk_dialog_add_button (GTK_DIALOG (private->dialog), + "", GTK_RESPONSE_OK); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (private->dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_object_weak_ref (G_OBJECT (private->dialog), + (GWeakNotify) quit_close_all_dialog_free, private); + + g_signal_connect (private->dialog, "response", + G_CALLBACK (quit_close_all_dialog_response), + private); + + /* connect D to the quit/close button */ + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (private->dialog), accel_group); + g_object_unref (accel_group); + + closure = g_closure_new_object (sizeof (GClosure), G_OBJECT (private->dialog)); + g_closure_set_marshal (closure, quit_close_all_dialog_accel_marshal); + gtk_accelerator_parse ("D", + &private->accel_key, &private->accel_mods); + gtk_accel_group_connect (accel_group, + private->accel_key, private->accel_mods, + 0, closure); + + private->box = GIMP_MESSAGE_DIALOG (private->dialog)->box; + + view_size = gimp->config->layer_preview_size; + rows = CLAMP (gimp_container_get_n_children (private->images), 3, 6); + + view = gimp_container_tree_view_new (private->images, private->context, + view_size, 1); + gimp_container_box_set_size_request (GIMP_CONTAINER_BOX (view), + -1, + rows * (view_size + 2)); + + private->tree_view = tree_view = GIMP_CONTAINER_TREE_VIEW (view); + + gtk_tree_view_column_set_expand (tree_view->main_column, TRUE); + + renderer = gimp_container_tree_view_get_name_cell (tree_view); + gtk_tree_view_column_set_cell_data_func (tree_view->main_column, + renderer, + quit_close_all_dialog_name_cell_func, + NULL, NULL); + + private->save_column = column = gtk_tree_view_column_new (); + renderer = gimp_cell_renderer_button_new (); + g_object_set (renderer, + "icon-name", "document-save", + NULL); + gtk_tree_view_column_pack_end (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, NULL); + + gtk_tree_view_append_column (tree_view->view, column); + gimp_container_tree_view_add_toggle_cell (tree_view, renderer); + + g_signal_connect (renderer, "clicked", + G_CALLBACK (quit_close_all_dialog_save_clicked), + private); + + gtk_box_pack_start (GTK_BOX (private->box), view, TRUE, TRUE, 0); + gtk_widget_show (view); + + g_signal_connect (view, "select-item", + G_CALLBACK (quit_close_all_dialog_image_selected), + private); + + dnd_widget = gimp_container_view_get_dnd_widget (GIMP_CONTAINER_VIEW (view)); + gimp_dnd_xds_source_add (dnd_widget, + (GimpDndDragViewableFunc) gimp_dnd_get_drag_data, + NULL); + + g_signal_connect (tree_view->view, "query-tooltip", + G_CALLBACK (quit_close_all_dialog_query_tooltip), + private); + + if (do_quit) + private->lost_label = gtk_label_new (_("If you quit GIMP now, " + "these changes will be lost.")); + else + private->lost_label = gtk_label_new (_("If you close these images now, " + "changes will be lost.")); + gtk_label_set_xalign (GTK_LABEL (private->lost_label), 0.0); + gtk_label_set_line_wrap (GTK_LABEL (private->lost_label), TRUE); + gtk_box_pack_start (GTK_BOX (private->box), private->lost_label, + FALSE, FALSE, 0); + gtk_widget_show (private->lost_label); + + private->hint_label = gtk_label_new (NULL); + gtk_label_set_xalign (GTK_LABEL (private->hint_label), 0.0); + gtk_label_set_line_wrap (GTK_LABEL (private->hint_label), TRUE); + gtk_box_pack_start (GTK_BOX (private->box), private->hint_label, + FALSE, FALSE, 0); + gtk_widget_show (private->hint_label); + + closure = g_cclosure_new (G_CALLBACK (quit_close_all_dialog_container_changed), + private, NULL); + g_object_watch_closure (G_OBJECT (private->dialog), closure); + g_signal_connect_closure (private->images, "add", closure, FALSE); + g_signal_connect_closure (private->images, "remove", closure, FALSE); + + quit_close_all_dialog_container_changed (private->images, NULL, + private); + + return private->dialog; +} + +static void +quit_close_all_dialog_free (QuitDialog *private) +{ + g_idle_remove_by_data (private); + g_object_unref (private->images); + g_object_unref (private->context); + + g_slice_free (QuitDialog, private); +} + +static void +quit_close_all_dialog_response (GtkWidget *dialog, + gint response_id, + QuitDialog *private) +{ + Gimp *gimp = private->gimp; + gboolean do_quit = private->do_quit; + + gtk_widget_destroy (dialog); + + if (response_id == GTK_RESPONSE_OK) + { + if (do_quit) + gimp_exit (gimp, TRUE); + else + gimp_displays_close (gimp); + } +} + +static void +quit_close_all_dialog_accel_marshal (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + gtk_dialog_response (GTK_DIALOG (closure->data), GTK_RESPONSE_OK); + + /* we handled the accelerator */ + g_value_set_boolean (return_value, TRUE); +} + +static void +quit_close_all_dialog_container_changed (GimpContainer *images, + GimpObject *image, + QuitDialog *private) +{ + gint num_images = gimp_container_get_n_children (images); + gchar *accel_string; + gchar *hint; + gchar *markup; + + accel_string = gtk_accelerator_get_label (private->accel_key, + private->accel_mods); + + gimp_message_box_set_primary_text (private->box, + /* TRANSLATORS: unless your language + msgstr[0] applies to 1 only (as + in English), replace "one" with %d. */ + ngettext ("There is one image with " + "unsaved changes:", + "There are %d images with " + "unsaved changes:", + num_images), num_images); + + if (num_images == 0) + { + gtk_widget_hide (private->lost_label); + + if (private->do_quit) + hint = g_strdup_printf (_("Press %s to quit."), + accel_string); + else + hint = g_strdup_printf (_("Press %s to close all images."), + accel_string); + + g_object_set (private->ok_button, + "label", private->do_quit ? _("_Quit") : _("Cl_ose"), + "use-stock", TRUE, + "image", NULL, + NULL); + + gtk_widget_grab_default (private->ok_button); + + /* When no image requires saving anymore, there is no harm in + * assuming completing the original quit or close-all action is + * the expected end-result. + * I don't immediately exit though because of some unfinished + * actions provoking warnings. Let's just close as soon as + * possible with an idle source. + * Also the idle source has another benefit: allowing to change + * one's mind and not exist after the last save, for instance by + * hitting Esc quickly while the last save is in progress. + */ + g_idle_add ((GSourceFunc) quit_close_all_idle, private); + } + else + { + GtkWidget *icon; + + if (private->do_quit) + hint = g_strdup_printf (_("Press %s to discard all changes and quit."), + accel_string); + else + hint = g_strdup_printf (_("Press %s to discard all changes and close all images."), + accel_string); + + gtk_widget_show (private->lost_label); + + icon = gtk_image_new_from_icon_name ("edit-delete", + GTK_ICON_SIZE_BUTTON); + g_object_set (private->ok_button, + "label", _("_Discard Changes"), + "use-stock", FALSE, + "image", icon, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (private->dialog), + GTK_RESPONSE_CANCEL); + } + + markup = g_strdup_printf ("%s", hint); + + gtk_label_set_markup (GTK_LABEL (private->hint_label), markup); + + g_free (markup); + g_free (hint); + g_free (accel_string); +} + +static void +quit_close_all_dialog_image_selected (GimpContainerView *view, + GimpImage *image, + gpointer insert_data, + QuitDialog *private) +{ + GList *list; + + for (list = gimp_get_display_iter (private->gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + if (gimp_display_get_image (display) == image) + { + gimp_display_shell_present (gimp_display_get_shell (display)); + + /* We only want to update the active shell. Give back keyboard + * focus to the quit dialog after this. + */ + gtk_window_present (GTK_WINDOW (private->dialog)); + } + } +} + +static void +quit_close_all_dialog_name_cell_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + GimpViewRenderer *renderer; + GimpImage *image; + gchar *name; + + gtk_tree_model_get (tree_model, iter, + GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer, + GIMP_CONTAINER_TREE_STORE_COLUMN_NAME, &name, + -1); + + image = GIMP_IMAGE (renderer->viewable); + + if (gimp_image_is_export_dirty (image)) + { + g_object_set (cell, + "markup", NULL, + "text", name, + NULL); + } + else + { + GFile *file; + const gchar *filename; + gchar *escaped_name; + gchar *escaped_filename; + gchar *exported; + gchar *markup; + + file = gimp_image_get_exported_file (image); + if (! file) + file = gimp_image_get_imported_file (image); + + filename = gimp_file_get_utf8_name (file); + + escaped_name = g_markup_escape_text (name, -1); + escaped_filename = g_markup_escape_text (filename, -1); + + exported = g_strdup_printf (_("Exported to %s"), escaped_filename); + markup = g_strdup_printf ("%s\n%s", escaped_name, exported); + g_free (exported); + + g_free (escaped_name); + g_free (escaped_filename); + + g_object_set (cell, + "text", NULL, + "markup", markup, + NULL); + + g_free (markup); + } + + g_object_unref (renderer); + g_free (name); +} + +static void +quit_close_all_dialog_save_clicked (GtkCellRenderer *cell, + const gchar *path_str, + GdkModifierType state, + QuitDialog *private) +{ + GtkTreePath *path = gtk_tree_path_new_from_string (path_str); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (private->tree_view->model, &iter, path)) + { + GimpViewRenderer *renderer; + GimpImage *image; + GList *list; + + gtk_tree_model_get (private->tree_view->model, &iter, + GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer, + -1); + + image = GIMP_IMAGE (renderer->viewable); + g_object_unref (renderer); + + for (list = gimp_get_display_iter (private->gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + if (gimp_display_get_image (display) == image) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpUIManager *manager; + + manager = gimp_image_window_get_ui_manager (window); + + gimp_display_shell_present (shell); + /* Make sure the quit dialog kept keyboard focus when + * the save dialog will exit. */ + gtk_window_present (GTK_WINDOW (private->dialog)); + + if (state & GDK_SHIFT_MASK) + { + gimp_ui_manager_activate_action (manager, "file", + "file-save-as"); + } + else + { + gimp_ui_manager_activate_action (manager, "file", + "file-save"); + } + } + + break; + } + } + } +} + +static gboolean +quit_close_all_dialog_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + QuitDialog *private) +{ + GtkTreePath *path; + gboolean show_tip = FALSE; + + if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y, + keyboard_tip, + NULL, &path, NULL)) + { + GtkTreeViewColumn *column = NULL; + + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y, + NULL, &column, NULL, NULL); + + if (column == private->save_column) + { + gchar *tip = g_strconcat (_("Save this image"), "\n", + gimp_get_mod_string (GDK_SHIFT_MASK), + " ", _("Save as"), + NULL); + + gtk_tooltip_set_markup (tooltip, tip); + gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (widget), tooltip, path); + + g_free (tip); + + show_tip = TRUE; + } + + gtk_tree_path_free (path); + } + + return show_tip; +} + +static gboolean +quit_close_all_idle (QuitDialog *private) +{ + gtk_dialog_response (GTK_DIALOG (private->dialog), GTK_RESPONSE_OK); + + return FALSE; +} diff --git a/app/dialogs/quit-dialog.h b/app/dialogs/quit-dialog.h new file mode 100644 index 0000000..1760976 --- /dev/null +++ b/app/dialogs/quit-dialog.h @@ -0,0 +1,28 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2004 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 . + */ + +#ifndef __QUIT_DIALOG_H__ +#define __QUIT_DIALOG_H__ + + +GtkWidget * quit_dialog_new (Gimp *gimp); +GtkWidget * close_all_dialog_new (Gimp *gimp); + + +#endif /* __QUIT_DIALOG_H__ */ diff --git a/app/dialogs/resize-dialog.c b/app/dialogs/resize-dialog.c new file mode 100644 index 0000000..7f20702 --- /dev/null +++ b/app/dialogs/resize-dialog.c @@ -0,0 +1,853 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" +#include "core/gimptemplate.h" + +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpsizebox.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpwidgets-constructors.h" + +#include "resize-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 +#define SB_WIDTH 8 + + +typedef struct _ResizeDialog ResizeDialog; + +struct _ResizeDialog +{ + GimpViewable *viewable; + GimpContext *context; + GimpContext *parent_context; + GimpFillType fill_type; + GimpItemSet layer_set; + gboolean resize_text_layers; + GimpResizeCallback callback; + gpointer user_data; + + gdouble old_xres; + gdouble old_yres; + GimpUnit old_res_unit; + gint old_width; + gint old_height; + GimpUnit old_unit; + GimpFillType old_fill_type; + GimpItemSet old_layer_set; + gboolean old_resize_text_layers; + + GtkWidget *box; + GtkWidget *offset; + GtkWidget *area; + GtkWidget *layer_set_combo; + GtkWidget *fill_type_combo; + GtkWidget *text_layers_button; + + GtkWidget *ppi_box; + GtkWidget *ppi_image; + GtkWidget *ppi_template; + GimpTemplate *template; +}; + + +/* local function prototypes */ + +static void resize_dialog_free (ResizeDialog *private); +static void resize_dialog_response (GtkWidget *dialog, + gint response_id, + ResizeDialog *private); +static void resize_dialog_reset (ResizeDialog *private); + +static void size_notify (GimpSizeBox *box, + GParamSpec *pspec, + ResizeDialog *private); +static void offset_update (GtkWidget *widget, + ResizeDialog *private); +static void offsets_changed (GtkWidget *area, + gint off_x, + gint off_y, + ResizeDialog *private); +static void offset_center_clicked (GtkWidget *widget, + ResizeDialog *private); + +static void template_changed (GimpContext *context, + GimpTemplate *template, + ResizeDialog *private); + +static void reset_template_clicked (GtkWidget *button, + ResizeDialog *private); +static void ppi_select_toggled (GtkWidget *radio, + ResizeDialog *private); + + +/* public function */ + +GtkWidget * +resize_dialog_new (GimpViewable *viewable, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpUnit unit, + GimpFillType fill_type, + GimpItemSet layer_set, + gboolean resize_text_layers, + GimpResizeCallback callback, + gpointer user_data) +{ + ResizeDialog *private; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *center_hbox; + GtkWidget *center_left_vbox; + GtkWidget *center_right_vbox; + GtkWidget *frame; + GtkWidget *button; + GtkWidget *spinbutton; + GtkWidget *entry; + GtkWidget *hbox; + GtkWidget *combo; + GtkWidget *label; + GtkWidget *template_selector; + GtkWidget *ppi_image; + GtkWidget *ppi_template; + GtkAdjustment *adjustment; + GdkPixbuf *pixbuf; + GtkSizeGroup *size_group = NULL; + GimpImage *image = NULL; + const gchar *size_title = NULL; + const gchar *layers_title = NULL; + gint width, height; + gdouble xres, yres; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + if (GIMP_IS_IMAGE (viewable)) + { + image = GIMP_IMAGE (viewable); + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + + size_title = _("Canvas Size"); + layers_title = _("Layers"); + } + else if (GIMP_IS_ITEM (viewable)) + { + GimpItem *item = GIMP_ITEM (viewable); + + image = gimp_item_get_image (item); + + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + + size_title = _("Layer Size"); + layers_title = _("Fill With"); + } + else + { + g_return_val_if_reached (NULL); + } + + private = g_slice_new0 (ResizeDialog); + + private->parent_context = context; + private->context = gimp_context_new (context->gimp, + "resize-dialog", + context); + + gimp_image_get_resolution (image, &xres, &yres); + + private->old_xres = xres; + private->old_yres = yres; + private->old_res_unit = gimp_image_get_unit (image); + + private->viewable = viewable; + private->fill_type = fill_type; + private->layer_set = layer_set; + private->resize_text_layers = resize_text_layers; + private->callback = callback; + private->user_data = user_data; + + private->old_width = width; + private->old_height = height; + private->old_unit = unit; + private->old_fill_type = private->fill_type; + private->old_layer_set = private->layer_set; + private->old_resize_text_layers = private->resize_text_layers; + + gimp_context_set_template (private->context, NULL); + + dialog = gimp_viewable_dialog_new (viewable, context, + title, role, GIMP_ICON_OBJECT_RESIZE, title, + parent, + help_func, help_id, + + _("Re_set"), RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Resize"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + RESPONSE_RESET, + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) resize_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (resize_dialog_response), + private); + + 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); + + /* template selector */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new_with_mnemonic (_("_Template:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + template_selector = g_object_new (GIMP_TYPE_CONTAINER_COMBO_BOX, + "container", context->gimp->templates, + "context", private->context, + "view-size", 16, + "view-border-width", 0, + "ellipsize", PANGO_ELLIPSIZE_NONE, + "focus-on-click", FALSE, + NULL); + + gtk_box_pack_start (GTK_BOX (hbox), template_selector, TRUE, TRUE, 0); + gtk_widget_show (template_selector); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), template_selector); + + g_signal_connect (private->context, + "template-changed", + G_CALLBACK (template_changed), + private); + + /* reset template button */ + button = gimp_icon_button_new (GIMP_ICON_RESET, NULL); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_image_set_from_icon_name (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (button))), + GIMP_ICON_RESET, GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + g_signal_connect (button, + "clicked", + G_CALLBACK (reset_template_clicked), + private); + + gimp_help_set_help_data (button, + _("Reset the template selection"), + NULL); + + /* ppi selector box */ + private->ppi_box = vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Template and image print resolution don't match.\n" + "Choose how to scale the canvas:")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE); + gtk_widget_show (hbox); + + /* actual label text is set inside template_change fn. */ + ppi_image = gtk_radio_button_new_with_label (NULL, ""); + ppi_template = gtk_radio_button_new_with_label (NULL, ""); + + private->ppi_image = ppi_image; + private->ppi_template = ppi_template; + + gtk_radio_button_set_group (GTK_RADIO_BUTTON (ppi_template), + gtk_radio_button_get_group (GTK_RADIO_BUTTON (ppi_image))); + + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (ppi_image), FALSE); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (ppi_template), FALSE); + + gtk_box_pack_start (GTK_BOX (hbox), ppi_image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), ppi_template, FALSE, FALSE, 0); + + gtk_widget_show (ppi_image); + gtk_widget_show (ppi_template); + + g_signal_connect (G_OBJECT (ppi_image), + "toggled", + G_CALLBACK (ppi_select_toggled), + private); + + g_signal_connect (G_OBJECT (ppi_template), + "toggled", + G_CALLBACK (ppi_select_toggled), + private); + + /* For space gain, organize the main widgets in both vertical and + * horizontal layout. + * The size and offset fields are on the center left, while the + * preview and the "Center" button are on center right. + */ + center_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); + gtk_box_pack_start (GTK_BOX (main_vbox), center_hbox, FALSE, FALSE, 0); + gtk_widget_show (center_hbox); + + center_left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4); + gtk_box_pack_start (GTK_BOX (center_hbox), center_left_vbox, FALSE, FALSE, 0); + gtk_widget_show (center_left_vbox); + + center_right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_box_pack_start (GTK_BOX (center_hbox), center_right_vbox, FALSE, FALSE, 0); + gtk_widget_show (center_right_vbox); + + /* size select frame */ + + frame = gimp_frame_new (size_title); + gtk_box_pack_start (GTK_BOX (center_left_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* size box */ + + private->box = g_object_new (GIMP_TYPE_SIZE_BOX, + "width", width, + "height", height, + "unit", unit, + "xresolution", xres, + "yresolution", yres, + "keep-aspect", FALSE, + "edit-resolution", FALSE, + NULL); + gtk_container_add (GTK_CONTAINER (frame), private->box); + gtk_widget_show (private->box); + + /* offset frame */ + frame = gimp_frame_new (_("Offset")); + gtk_box_pack_start (GTK_BOX (center_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); + + /* the offset sizeentry */ + adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); + spinbutton = gimp_spin_button_new (adjustment, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), SB_WIDTH); + + private->offset = entry = gimp_size_entry_new (1, unit, "%p", + TRUE, FALSE, FALSE, SB_WIDTH, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gtk_table_set_col_spacing (GTK_TABLE (entry), 0, 6); + gtk_table_set_col_spacing (GTK_TABLE (entry), 1, 6); + gtk_table_set_col_spacing (GTK_TABLE (entry), 3, 12); + gtk_table_set_row_spacing (GTK_TABLE (entry), 0, 2); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry), + GTK_SPIN_BUTTON (spinbutton), NULL); + gtk_table_attach_defaults (GTK_TABLE (entry), spinbutton, + 1, 2, 0, 1); + gtk_widget_show (spinbutton); + + gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry), + _("_X:"), 0, 0, 0.0); + gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry),_("_Y:"), 1, 0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); + gtk_widget_show (entry); + + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, xres, FALSE); + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 1, yres, FALSE); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 0, 0, 0); + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 1, 0, 0); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, 0); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, 0); + + g_signal_connect (entry, "value-changed", + G_CALLBACK (offset_update), + private); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (center_right_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + private->area = gimp_offset_area_new (width, height); + gtk_container_add (GTK_CONTAINER (frame), private->area); + gtk_widget_show (private->area); + + gimp_viewable_get_preview_size (viewable, 200, TRUE, TRUE, &width, &height); + pixbuf = gimp_viewable_get_pixbuf (viewable, context, + width, height); + + if (pixbuf) + gimp_offset_area_set_pixbuf (GIMP_OFFSET_AREA (private->area), pixbuf); + + g_signal_connect (private->area, "offsets-changed", + G_CALLBACK (offsets_changed), + private); + + g_signal_connect (private->box, "notify", + G_CALLBACK (size_notify), + private); + + /* Button to center the image on canvas just below the preview. */ + button = gtk_button_new_with_mnemonic (_("C_enter")); + gtk_box_pack_start (GTK_BOX (center_right_vbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (offset_center_clicked), + private); + + frame = gimp_frame_new (layers_title); + 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); + + if (GIMP_IS_IMAGE (viewable)) + { + GtkWidget *label; + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + 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 (_("Resize _layers:")); + 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); + + private->layer_set_combo = combo = + gimp_enum_combo_box_new (GIMP_TYPE_ITEM_SET); + 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), + private->layer_set, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->layer_set); + } + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + private->fill_type_combo = combo = + gimp_enum_combo_box_new (GIMP_TYPE_FILL_TYPE); + gtk_box_pack_end (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + private->fill_type, + G_CALLBACK (gimp_int_combo_box_get_active), + &private->fill_type); + + if (GIMP_IS_IMAGE (viewable)) + { + GtkWidget *label; + + label = gtk_label_new_with_mnemonic (_("_Fill with:")); + 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_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + gtk_size_group_add_widget (size_group, label); + + private->text_layers_button = button = + gtk_check_button_new_with_mnemonic (_("Resize _text layers")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->resize_text_layers); + 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), + &private->resize_text_layers); + + gimp_help_set_help_data (button, + _("Resizing text layers will make them uneditable"), + NULL); + + g_object_unref (size_group); + } + + return dialog; +} + + +/* private functions */ + +static void +resize_dialog_free (ResizeDialog *private) +{ + g_object_unref (private->context); + + g_slice_free (ResizeDialog, private); +} + +static void +resize_dialog_response (GtkWidget *dialog, + gint response_id, + ResizeDialog *private) +{ + GimpSizeEntry *entry = GIMP_SIZE_ENTRY (private->offset); + GimpUnit unit; + gint width; + gint height; + gdouble xres; + gdouble yres; + GimpUnit res_unit; + + switch (response_id) + { + case RESPONSE_RESET: + resize_dialog_reset (private); + break; + + case GTK_RESPONSE_OK: + g_object_get (private->box, + "width", &width, + "height", &height, + "unit", &unit, + "xresolution", &xres, + "yresolution", &yres, + "resolution-unit", &res_unit, + NULL); + + private->callback (dialog, + private->viewable, + private->parent_context, + width, + height, + unit, + gimp_size_entry_get_refval (entry, 0), + gimp_size_entry_get_refval (entry, 1), + xres, + yres, + res_unit, + private->fill_type, + private->layer_set, + private->resize_text_layers, + private->user_data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} + +static void +resize_dialog_reset (ResizeDialog *private) +{ + g_object_set (private->box, + "keep-aspect", FALSE, + NULL); + + g_object_set (private->box, + "width", private->old_width, + "height", private->old_height, + "unit", private->old_unit, + "xresolution", private->old_xres, + "yresolution", private->old_yres, + "resolution-unit", private->old_res_unit, + NULL); + + if (private->layer_set_combo) + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->layer_set_combo), + private->old_layer_set); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->fill_type_combo), + private->old_fill_type); + + if (private->text_layers_button) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->text_layers_button), + private->old_resize_text_layers); + + gimp_context_set_template (private->context, NULL); + + gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->offset), + private->old_unit); +} + +static void +size_notify (GimpSizeBox *box, + GParamSpec *pspec, + ResizeDialog *private) +{ + gint diff_x = box->width - private->old_width; + gint diff_y = box->height - private->old_height; + + gimp_offset_area_set_size (GIMP_OFFSET_AREA (private->area), + box->width, box->height); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset), 0, + MIN (0, diff_x), MAX (0, diff_x)); + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->offset), 1, + MIN (0, diff_y), MAX (0, diff_y)); +} + +static gint +resize_bound_off_x (ResizeDialog *private, + gint offset_x) +{ + GimpSizeBox *box = GIMP_SIZE_BOX (private->box); + + if (private->old_width <= box->width) + return CLAMP (offset_x, 0, (box->width - private->old_width)); + else + return CLAMP (offset_x, (box->width - private->old_width), 0); +} + +static gint +resize_bound_off_y (ResizeDialog *private, + gint off_y) +{ + GimpSizeBox *box = GIMP_SIZE_BOX (private->box); + + if (private->old_height <= box->height) + return CLAMP (off_y, 0, (box->height - private->old_height)); + else + return CLAMP (off_y, (box->height - private->old_height), 0); +} + +static void +offset_update (GtkWidget *widget, + ResizeDialog *private) +{ + GimpSizeEntry *entry = GIMP_SIZE_ENTRY (private->offset); + gint off_x; + gint off_y; + + off_x = resize_bound_off_x (private, + RINT (gimp_size_entry_get_refval (entry, 0))); + off_y = resize_bound_off_y (private, + RINT (gimp_size_entry_get_refval (entry, 1))); + + gimp_offset_area_set_offsets (GIMP_OFFSET_AREA (private->area), off_x, off_y); +} + +static void +offsets_changed (GtkWidget *area, + gint off_x, + gint off_y, + ResizeDialog *private) +{ + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset), 0, off_x); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset), 1, off_y); +} + +static void +offset_center_clicked (GtkWidget *widget, + ResizeDialog *private) +{ + GimpSizeBox *box = GIMP_SIZE_BOX (private->box); + gint off_x; + gint off_y; + + off_x = resize_bound_off_x (private, (box->width - private->old_width) / 2); + off_y = resize_bound_off_y (private, (box->height - private->old_height) / 2); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset), 0, off_x); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset), 1, off_y); + + g_signal_emit_by_name (private->offset, "value-changed", 0); +} + +static void +template_changed (GimpContext *context, + GimpTemplate *template, + ResizeDialog *private) +{ + GimpUnit unit = private->old_unit; + + private->template = template; + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->ppi_image), TRUE); + gtk_widget_hide (private->ppi_box); + + if (template != NULL) + { + gdouble xres; + gdouble yres; + GimpUnit res_unit; + gboolean resolution_mismatch; + + unit = gimp_template_get_unit (template); + xres = gimp_template_get_resolution_x (template); + yres = gimp_template_get_resolution_y (template); + res_unit = gimp_template_get_resolution_unit (template); + + resolution_mismatch = xres != private->old_xres || + yres != private->old_yres || + res_unit != private->old_res_unit; + + if (resolution_mismatch && + unit != GIMP_UNIT_PIXEL) + { + gchar *text; + + text = g_strdup_printf (_("Scale template to %.2f ppi"), + private->old_xres); + gtk_button_set_label (GTK_BUTTON (private->ppi_image), text); + g_free (text); + + text = g_strdup_printf (_("Set image to %.2f ppi"), + xres); + gtk_button_set_label (GTK_BUTTON (private->ppi_template), text); + g_free (text); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (private->ppi_image), + TRUE); + + gtk_widget_show (private->ppi_box); + } + } + + ppi_select_toggled (NULL, private); + + gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->offset), unit); +} + +static void +ppi_select_toggled (GtkWidget *radio, + ResizeDialog *private) +{ + gint width; + gint height; + GimpUnit unit; + gdouble xres; + gdouble yres; + GimpUnit res_unit; + GtkToggleButton *image_button; + gboolean use_image_ppi; + + width = private->old_width; + height = private->old_height; + xres = private->old_xres; + yres = private->old_yres; + res_unit = private->old_res_unit; + unit = private->old_unit; + + image_button = GTK_TOGGLE_BUTTON (private->ppi_image); + use_image_ppi = gtk_toggle_button_get_active (image_button); + + if (private->template != NULL) + { + width = gimp_template_get_width (private->template); + height = gimp_template_get_height (private->template); + unit = gimp_template_get_unit (private->template); + xres = gimp_template_get_resolution_x (private->template); + yres = gimp_template_get_resolution_y (private->template); + res_unit = gimp_template_get_resolution_unit (private->template); + } + + if (private->template != NULL && + unit != GIMP_UNIT_PIXEL) + { + if (use_image_ppi) + { + width = ceil (width * (private->old_xres / xres)); + height = ceil (height * (private->old_yres / yres)); + + xres = private->old_xres; + yres = private->old_yres; + } + + g_object_set (private->box, + "xresolution", xres, + "yresolution", yres, + "resolution-unit", res_unit, + NULL); + } + else + { + g_object_set (private->box, + "xresolution", private->old_xres, + "yresolution", private->old_yres, + "resolution-unit", private->old_res_unit, + NULL); + } + + g_object_set (private->box, + "width", width, + "height", height, + "unit", unit, + NULL); +} + +static void +reset_template_clicked (GtkWidget *button, + ResizeDialog *private) +{ + gimp_context_set_template (private->context, NULL); +} diff --git a/app/dialogs/resize-dialog.h b/app/dialogs/resize-dialog.h new file mode 100644 index 0000000..223b43c --- /dev/null +++ b/app/dialogs/resize-dialog.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 . + */ + +#ifndef __RESIZE_DIALOG_H__ +#define __RESIZE_DIALOG_H__ + + +typedef void (* GimpResizeCallback) (GtkWidget *dialog, + GimpViewable *viewable, + GimpContext *context, + gint width, + gint height, + GimpUnit unit, + gint offset_x, + gint offset_y, + gdouble xres, + gdouble yres, + GimpUnit res_unit, + GimpFillType fill_type, + GimpItemSet layer_set, + gboolean resize_text_layers, + gpointer user_data); + + +GtkWidget * resize_dialog_new (GimpViewable *viewable, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpUnit unit, + GimpFillType fill_type, + GimpItemSet layer_set, + gboolean resize_text_layers, + GimpResizeCallback callback, + gpointer user_data); + + +#endif /* __RESIZE_DIALOG_H__ */ diff --git a/app/dialogs/resolution-calibrate-dialog.c b/app/dialogs/resolution-calibrate-dialog.c new file mode 100644 index 0000000..d4562aa --- /dev/null +++ b/app/dialogs/resolution-calibrate-dialog.c @@ -0,0 +1,204 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "resolution-calibrate-dialog.h" + +#include "gimp-intl.h" + + +static GtkWidget *calibrate_entry = NULL; +static gdouble calibrate_xres = 1.0; +static gdouble calibrate_yres = 1.0; +static gint ruler_width = 1; +static gint ruler_height = 1; + + +/** + * resolution_calibrate_dialog: + * @resolution_entry: a #GimpSizeEntry to connect the dialog to + * @icon_name: an optional icon-name for the upper left corner + * + * Displays a dialog that allows the user to interactively determine + * her monitor resolution. This dialog runs it's own GTK main loop and + * is connected to a #GimpSizeEntry handling the resolution to be set. + **/ +void +resolution_calibrate_dialog (GtkWidget *resolution_entry, + const gchar *icon_name) +{ + GtkWidget *dialog; + GtkWidget *table; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *ruler; + GtkWidget *label; + GdkScreen *screen; + GdkRectangle rect; + gint monitor; + + g_return_if_fail (GIMP_IS_SIZE_ENTRY (resolution_entry)); + g_return_if_fail (gtk_widget_get_realized (resolution_entry)); + + /* this dialog can only exist once */ + if (calibrate_entry) + return; + + dialog = gimp_dialog_new (_("Calibrate Monitor Resolution"), + "gimp-resolution-calibration", + gtk_widget_get_toplevel (resolution_entry), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, NULL, + + _("_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); + + screen = gtk_widget_get_screen (dialog); + monitor = gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (resolution_entry)); + gdk_screen_get_monitor_workarea (screen, monitor, &rect); + + ruler_width = rect.width - 300 - (rect.width % 100); + ruler_height = rect.height - 300 - (rect.height % 100); + + table = gtk_table_new (4, 4, FALSE); + 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); + gtk_widget_show (table); + + if (icon_name) + { + GtkWidget *image = gtk_image_new_from_icon_name (icon_name, + GTK_ICON_SIZE_DIALOG); + + gtk_table_attach (GTK_TABLE (table), image, 0, 1, 0, 1, + GTK_SHRINK, GTK_SHRINK, 4, 4); + gtk_widget_show (image); + } + + ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL); + gtk_widget_set_size_request (ruler, ruler_width, 32); + gimp_ruler_set_range (GIMP_RULER (ruler), 0, ruler_width, ruler_width); + gtk_table_attach (GTK_TABLE (table), ruler, 1, 3, 0, 1, + GTK_SHRINK, GTK_SHRINK, 0, 0); + gtk_widget_show (ruler); + + ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL); + gtk_widget_set_size_request (ruler, 32, ruler_height); + gimp_ruler_set_range (GIMP_RULER (ruler), 0, ruler_height, ruler_height); + gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 3, + GTK_SHRINK, GTK_SHRINK, 0, 0); + gtk_widget_show (ruler); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, 1, 2, + GTK_SHRINK, GTK_SHRINK, 0, 0); + gtk_widget_show (vbox); + + label = + gtk_label_new (_("Measure the rulers and enter their lengths:")); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_SCALE, PANGO_SCALE_LARGE, + PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + calibrate_xres = + gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (resolution_entry), 0); + calibrate_yres = + gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (resolution_entry), 1); + + calibrate_entry = + gimp_coordinates_new (GIMP_UNIT_INCH, "%p", + FALSE, FALSE, 10, + GIMP_SIZE_ENTRY_UPDATE_SIZE, + FALSE, + FALSE, + _("_Horizontal:"), + ruler_width, + calibrate_xres, + 1, GIMP_MAX_IMAGE_SIZE, + 0, 0, + _("_Vertical:"), + ruler_height, + calibrate_yres, + 1, GIMP_MAX_IMAGE_SIZE, + 0, 0); + gtk_widget_hide (GTK_WIDGET (GIMP_COORDINATES_CHAINBUTTON (calibrate_entry))); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_widget_destroyed), + &calibrate_entry); + + gtk_box_pack_end (GTK_BOX (hbox), calibrate_entry, FALSE, FALSE, 0); + gtk_widget_show (calibrate_entry); + + gtk_widget_show (dialog); + + switch (gimp_dialog_run (GIMP_DIALOG (dialog))) + { + case GTK_RESPONSE_OK: + { + GtkWidget *chain_button; + gdouble x, y; + + x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (calibrate_entry), 0); + y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (calibrate_entry), 1); + + calibrate_xres = (gdouble) ruler_width * calibrate_xres / x; + calibrate_yres = (gdouble) ruler_height * calibrate_yres / y; + + chain_button = GIMP_COORDINATES_CHAINBUTTON (resolution_entry); + + if (ABS (x - y) > GIMP_MIN_RESOLUTION) + gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), + FALSE); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (resolution_entry), + 0, calibrate_xres); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (resolution_entry), + 1, calibrate_yres); + } + + default: + break; + } + + gtk_widget_destroy (dialog); +} diff --git a/app/dialogs/resolution-calibrate-dialog.h b/app/dialogs/resolution-calibrate-dialog.h new file mode 100644 index 0000000..84a60dc --- /dev/null +++ b/app/dialogs/resolution-calibrate-dialog.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 . + */ + +#ifndef __RESOLUTION_CALIBRATE_DIALOG_H__ +#define __RESOLUTION_CALIBRATE_DIALOG_H__ + + +void resolution_calibrate_dialog (GtkWidget *resolution_entry, + const gchar *icon_name); + + +#endif /* __RESOLUTION_CALIBRATE_DIALOG_H__ */ diff --git a/app/dialogs/scale-dialog.c b/app/dialogs/scale-dialog.c new file mode 100644 index 0000000..8c4b6da --- /dev/null +++ b/app/dialogs/scale-dialog.c @@ -0,0 +1,311 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpitem.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpsizebox.h" +#include "widgets/gimpviewabledialog.h" + +#include "scale-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + +typedef struct _ScaleDialog ScaleDialog; + +struct _ScaleDialog +{ + GimpViewable *viewable; + GimpUnit unit; + GimpInterpolationType interpolation; + GtkWidget *box; + GtkWidget *combo; + GimpScaleCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void scale_dialog_free (ScaleDialog *private); +static void scale_dialog_response (GtkWidget *dialog, + gint response_id, + ScaleDialog *private); +static void scale_dialog_reset (ScaleDialog *private); + + +/* public function */ + +GtkWidget * +scale_dialog_new (GimpViewable *viewable, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpUnit unit, + GimpInterpolationType interpolation, + GimpScaleCallback callback, + gpointer user_data) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *frame; + GtkWidget *label; + ScaleDialog *private; + GimpImage *image = NULL; + const gchar *text = NULL; + gint width, height; + gdouble xres, yres; + + g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + if (GIMP_IS_IMAGE (viewable)) + { + image = GIMP_IMAGE (viewable); + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + + text = _("Image Size"); + } + else if (GIMP_IS_ITEM (viewable)) + { + GimpItem *item = GIMP_ITEM (viewable); + + image = gimp_item_get_image (item); + + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + + text = _("Layer Size"); + } + else + { + g_return_val_if_reached (NULL); + } + + private = g_slice_new0 (ScaleDialog); + + private->viewable = viewable; + private->interpolation = interpolation; + private->unit = unit; + private->callback = callback; + private->user_data = user_data; + + gimp_image_get_resolution (image, &xres, &yres); + + dialog = gimp_viewable_dialog_new (viewable, context, + title, role, GIMP_ICON_OBJECT_SCALE, title, + parent, + help_func, help_id, + + _("_Reset"), RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Scale"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + RESPONSE_RESET, + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) scale_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (scale_dialog_response), + private); + + private->box = g_object_new (GIMP_TYPE_SIZE_BOX, + "width", width, + "height", height, + "unit", unit, + "xresolution", xres, + "yresolution", yres, + "resolution-unit", gimp_image_get_unit (image), + "keep-aspect", TRUE, + "edit-resolution", GIMP_IS_IMAGE (viewable), + NULL); + + 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 (text); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + gtk_container_add (GTK_CONTAINER (frame), private->box); + gtk_widget_show (private->box); + + frame = gimp_frame_new (_("Quality")); + gtk_box_pack_start (GTK_BOX (vbox), 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); + + 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 (_("I_nterpolation:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + gtk_size_group_add_widget (GIMP_SIZE_BOX (private->box)->size_group, label); + + private->combo = gimp_enum_combo_box_new (GIMP_TYPE_INTERPOLATION_TYPE); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), private->combo); + gtk_box_pack_start (GTK_BOX (hbox), private->combo, TRUE, TRUE, 0); + gtk_widget_show (private->combo); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->combo), + private->interpolation); + + return dialog; +} + + +/* private functions */ + +static void +scale_dialog_free (ScaleDialog *private) +{ + g_slice_free (ScaleDialog, private); +} + +static void +scale_dialog_response (GtkWidget *dialog, + gint response_id, + ScaleDialog *private) +{ + GimpUnit unit = private->unit; + gint interpolation = private->interpolation; + GimpUnit resolution_unit; + gint width, height; + gdouble xres, yres; + + switch (response_id) + { + case RESPONSE_RESET: + scale_dialog_reset (private); + break; + + case GTK_RESPONSE_OK: + g_object_get (private->box, + "width", &width, + "height", &height, + "unit", &unit, + "xresolution", &xres, + "yresolution", &yres, + "resolution-unit", &resolution_unit, + NULL); + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (private->combo), + &interpolation); + + private->callback (dialog, + private->viewable, + width, height, unit, interpolation, + xres, yres, resolution_unit, + private->user_data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} + +static void +scale_dialog_reset (ScaleDialog *private) +{ + GimpImage *image; + gint width, height; + gdouble xres, yres; + + if (GIMP_IS_IMAGE (private->viewable)) + { + image = GIMP_IMAGE (private->viewable); + + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + } + else if (GIMP_IS_ITEM (private->viewable)) + { + GimpItem *item = GIMP_ITEM (private->viewable); + + image = gimp_item_get_image (item); + + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + } + else + { + g_return_if_reached (); + } + + gimp_image_get_resolution (image, &xres, &yres); + + g_object_set (private->box, + "keep-aspect", FALSE, + NULL); + + g_object_set (private->box, + "width", width, + "height", height, + "unit", private->unit, + NULL); + + g_object_set (private->box, + "keep-aspect", TRUE, + "xresolution", xres, + "yresolution", yres, + "resolution-unit", gimp_image_get_unit (image), + NULL); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->combo), + private->interpolation); +} diff --git a/app/dialogs/scale-dialog.h b/app/dialogs/scale-dialog.h new file mode 100644 index 0000000..46e8f9c --- /dev/null +++ b/app/dialogs/scale-dialog.h @@ -0,0 +1,35 @@ +/* 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 . + */ + +#ifndef __SCALE_DIALOG_H__ +#define __SCALE_DIALOG_H__ + + +GtkWidget * scale_dialog_new (GimpViewable *viewable, + GimpContext *context, + const gchar *title, + const gchar *role, + GtkWidget *parent, + GimpHelpFunc help_func, + const gchar *help_id, + GimpUnit unit, + GimpInterpolationType interpolation, + GimpScaleCallback callback, + gpointer user_data); + + +#endif /* __SCALE_DIALOG_H__ */ diff --git a/app/dialogs/stroke-dialog.c b/app/dialogs/stroke-dialog.c new file mode 100644 index 0000000..33c11ce --- /dev/null +++ b/app/dialogs/stroke-dialog.c @@ -0,0 +1,303 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2003 Henrik Brix Andersen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimppaintinfo.h" +#include "core/gimpstrokeoptions.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimpcontainercombobox.h" +#include "widgets/gimpcontainerview.h" +#include "widgets/gimpviewabledialog.h" +#include "widgets/gimpstrokeeditor.h" + +#include "stroke-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + + +typedef struct _StrokeDialog StrokeDialog; + +struct _StrokeDialog +{ + GimpItem *item; + GimpDrawable *drawable; + GimpContext *context; + GimpStrokeOptions *options; + GimpStrokeCallback callback; + gpointer user_data; + + GtkWidget *tool_combo; +}; + + +/* local function prototypes */ + +static void stroke_dialog_free (StrokeDialog *private); +static void stroke_dialog_response (GtkWidget *dialog, + gint response_id, + StrokeDialog *private); + + +/* public function */ + +GtkWidget * +stroke_dialog_new (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + const gchar *title, + const gchar *icon_name, + const gchar *help_id, + GtkWidget *parent, + GimpStrokeOptions *options, + GimpStrokeCallback callback, + gpointer user_data) +{ + StrokeDialog *private; + GimpImage *image; + GtkWidget *dialog; + GtkWidget *main_vbox; + GtkWidget *radio_box; + GtkWidget *cairo_radio; + GtkWidget *paint_radio; + GSList *group; + GtkWidget *frame; + + g_return_val_if_fail (GIMP_IS_ITEM (item), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (callback != NULL, NULL); + + image = gimp_item_get_image (item); + + private = g_slice_new0 (StrokeDialog); + + private->item = item; + private->drawable = drawable; + private->context = context; + private->options = gimp_stroke_options_new (context->gimp, context, TRUE); + private->callback = callback; + private->user_data = user_data; + + gimp_config_sync (G_OBJECT (options), + G_OBJECT (private->options), 0); + + dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (item), context, + title, "gimp-stroke-options", + icon_name, + _("Choose Stroke Style"), + parent, + gimp_standard_help_func, + help_id, + + _("_Reset"), RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Stroke"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + RESPONSE_RESET, + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) stroke_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (stroke_dialog_response), + private); + + 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); + + radio_box = gimp_prop_enum_radio_box_new (G_OBJECT (private->options), + "method", -1, -1); + + group = gtk_radio_button_get_group (g_object_get_data (G_OBJECT (radio_box), + "radio-button")); + + cairo_radio = g_object_ref (group->next->data); + gtk_container_remove (GTK_CONTAINER (radio_box), cairo_radio); + + paint_radio = g_object_ref (group->data); + gtk_container_remove (GTK_CONTAINER (radio_box), paint_radio); + + g_object_ref_sink (radio_box); + g_object_unref (radio_box); + + { + PangoFontDescription *font_desc; + + font_desc = pango_font_description_new (); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); + + gtk_widget_modify_font (gtk_bin_get_child (GTK_BIN (cairo_radio)), + font_desc); + gtk_widget_modify_font (gtk_bin_get_child (GTK_BIN (paint_radio)), + font_desc); + + pango_font_description_free (font_desc); + } + + + /* the stroke frame */ + + frame = gimp_frame_new (NULL); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + gtk_frame_set_label_widget (GTK_FRAME (frame), cairo_radio); + g_object_unref (cairo_radio); + + { + GtkWidget *stroke_editor; + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + stroke_editor = gimp_stroke_editor_new (private->options, yres, FALSE); + gtk_container_add (GTK_CONTAINER (frame), stroke_editor); + gtk_widget_show (stroke_editor); + + g_object_bind_property (cairo_radio, "active", + stroke_editor, "sensitive", + G_BINDING_SYNC_CREATE); + } + + + /* the paint tool frame */ + + frame = gimp_frame_new (NULL); + gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + gtk_frame_set_label_widget (GTK_FRAME (frame), paint_radio); + g_object_unref (paint_radio); + + { + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo; + GtkWidget *button; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + g_object_bind_property (paint_radio, "active", + vbox, "sensitive", + G_BINDING_SYNC_CREATE); + + 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 (_("P_aint tool:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + combo = gimp_container_combo_box_new (image->gimp->paint_info_list, + GIMP_CONTEXT (private->options), + 16, 0); + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + gtk_widget_show (combo); + + private->tool_combo = combo; + + button = gimp_prop_check_button_new (G_OBJECT (private->options), + "emulate-brush-dynamics", + _("_Emulate brush dynamics")); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + } + + return dialog; +} + + +/* private functions */ + +static void +stroke_dialog_free (StrokeDialog *private) +{ + g_object_unref (private->options); + + g_slice_free (StrokeDialog, private); +} + +static void +stroke_dialog_response (GtkWidget *dialog, + gint response_id, + StrokeDialog *private) +{ + switch (response_id) + { + case RESPONSE_RESET: + { + GimpToolInfo *tool_info = gimp_context_get_tool (private->context); + + gimp_config_reset (GIMP_CONFIG (private->options)); + + gimp_container_view_select_item (GIMP_CONTAINER_VIEW (private->tool_combo), + GIMP_VIEWABLE (tool_info->paint_info)); + + } + break; + + case GTK_RESPONSE_OK: + private->callback (dialog, + private->item, + private->drawable, + private->context, + private->options, + private->user_data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} diff --git a/app/dialogs/stroke-dialog.h b/app/dialogs/stroke-dialog.h new file mode 100644 index 0000000..e8e02a1 --- /dev/null +++ b/app/dialogs/stroke-dialog.h @@ -0,0 +1,44 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __STROKE_DIALOG_H__ +#define __STROKE_DIALOG_H__ + + +typedef void (* GimpStrokeCallback) (GtkWidget *dialog, + GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + GimpStrokeOptions *options, + gpointer user_data); + + +GtkWidget * stroke_dialog_new (GimpItem *item, + GimpDrawable *drawable, + GimpContext *context, + const gchar *title, + const gchar *icon_name, + const gchar *help_id, + GtkWidget *parent, + GimpStrokeOptions *options, + GimpStrokeCallback callback, + gpointer user_data); + + +#endif /* __STROKE_DIALOG_H__ */ diff --git a/app/dialogs/template-options-dialog.c b/app/dialogs/template-options-dialog.c new file mode 100644 index 0000000..7025253 --- /dev/null +++ b/app/dialogs/template-options-dialog.c @@ -0,0 +1,180 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimptemplate.h" + +#include "widgets/gimptemplateeditor.h" +#include "widgets/gimpviewabledialog.h" + +#include "template-options-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _TemplateOptionsDialog TemplateOptionsDialog; + +struct _TemplateOptionsDialog +{ + GimpTemplate *template; + GimpContext *context; + GimpTemplateOptionsCallback callback; + gpointer user_data; + + GtkWidget *editor; +}; + + +/* local function prototypes */ + +static void template_options_dialog_free (TemplateOptionsDialog *private); +static void template_options_dialog_response (GtkWidget *dialog, + gint response_id, + TemplateOptionsDialog *private); + + +/* public function */ + +GtkWidget * +template_options_dialog_new (GimpTemplate *template, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + GimpTemplateOptionsCallback callback, + gpointer user_data) +{ + TemplateOptionsDialog *private; + GtkWidget *dialog; + GimpViewable *viewable = NULL; + GtkWidget *vbox; + + g_return_val_if_fail (template == NULL || GIMP_IS_TEMPLATE (template), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (title != NULL, NULL); + g_return_val_if_fail (role != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (desc != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (TemplateOptionsDialog); + + private->template = template; + private->context = context; + private->callback = callback; + private->user_data = user_data; + + if (template) + { + viewable = GIMP_VIEWABLE (template); + template = gimp_config_duplicate (GIMP_CONFIG (template)); + } + else + { + template = + gimp_config_duplicate (GIMP_CONFIG (context->gimp->config->default_image)); + viewable = GIMP_VIEWABLE (template); + + gimp_object_set_static_name (GIMP_OBJECT (template), _("Unnamed")); + } + + dialog = gimp_viewable_dialog_new (viewable, context, + title, role, icon_name, desc, + parent, + gimp_standard_help_func, help_id, + + _("_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); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) template_options_dialog_free, private); + + g_signal_connect (dialog, "response", + G_CALLBACK (template_options_dialog_response), + private); + + 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); + + private->editor = gimp_template_editor_new (template, context->gimp, TRUE); + gtk_box_pack_start (GTK_BOX (vbox), private->editor, FALSE, FALSE, 0); + gtk_widget_show (private->editor); + + g_object_unref (template); + + return dialog; +} + + +/* private functions */ + +static void +template_options_dialog_free (TemplateOptionsDialog *private) +{ + g_slice_free (TemplateOptionsDialog, private); +} + +static void +template_options_dialog_response (GtkWidget *dialog, + gint response_id, + TemplateOptionsDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GimpTemplateEditor *editor = GIMP_TEMPLATE_EDITOR (private->editor); + + private->callback (dialog, + private->template, + gimp_template_editor_get_template (editor), + private->context, + private->user_data); + } + else + { + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/template-options-dialog.h b/app/dialogs/template-options-dialog.h new file mode 100644 index 0000000..633489e --- /dev/null +++ b/app/dialogs/template-options-dialog.h @@ -0,0 +1,41 @@ +/* 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 . + */ + +#ifndef __TEMPLATE_OPTIONS_DIALOG_H__ +#define __TEMPLATE_OPTIONS_DIALOG_H__ + + +typedef void (* GimpTemplateOptionsCallback) (GtkWidget *dialog, + GimpTemplate *template, + GimpTemplate *edit_template, + GimpContext *context, + gpointer user_data); + + +GtkWidget * template_options_dialog_new (GimpTemplate *template, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + GimpTemplateOptionsCallback callback, + gpointer user_data); + + +#endif /* __TEMPLATE_OPTIONS_DIALOG_H__ */ diff --git a/app/dialogs/tips-dialog.c b/app/dialogs/tips-dialog.c new file mode 100644 index 0000000..610938c --- /dev/null +++ b/app/dialogs/tips-dialog.c @@ -0,0 +1,289 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "widgets/gimphelp-ids.h" + +#include "tips-dialog.h" +#include "tips-parser.h" + +#include "gimp-intl.h" + +enum +{ + RESPONSE_PREVIOUS = 1, + RESPONSE_NEXT = 2 +}; + + +/* eek, see bug 762279 */ +GtkLinkButtonUriFunc +gtk_link_button_set_uri_hook (GtkLinkButtonUriFunc func, + gpointer data, + GDestroyNotify destroy); +static void tips_uri_hook (GtkLinkButton *button, + const gchar *link_, + gpointer user_data); + + +static void tips_dialog_set_tip (GimpTip *tip); +static void tips_dialog_response (GtkWidget *dialog, + gint response); +static void tips_dialog_destroy (GtkWidget *widget, + GimpGuiConfig *config); +static void more_button_clicked (GtkWidget *button, + Gimp *gimp); + + +static GtkWidget *tips_dialog = NULL; +static GtkWidget *tip_label = NULL; +static GtkWidget *more_button = NULL; +static GList *tips = NULL; +static GList *current_tip = NULL; + + +GtkWidget * +tips_dialog_create (Gimp *gimp) +{ + GimpGuiConfig *config; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *image; + gint tips_count; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (!tips) + { + GError *error = NULL; + GFile *file; + + file = gimp_data_directory_file ("tips", "gimp-tips.xml", NULL); + + tips = gimp_tips_from_file (file, &error); + + if (! tips) + { + GimpTip *tip; + + if (! error) + { + tip = gimp_tip_new (_("The GIMP tips file is empty!"), NULL); + } + else if (error->code == G_FILE_ERROR_NOENT) + { + tip = gimp_tip_new (_("The GIMP tips file appears to be " + "missing!"), + _("There should be a file called '%s'. " + "Please check your installation."), + gimp_file_get_utf8_name (file)); + } + else + { + tip = gimp_tip_new (_("The GIMP tips file could not be parsed!"), + "%s", error->message); + } + + tips = g_list_prepend (tips, tip); + } + else if (error) + { + g_printerr ("Error while parsing '%s': %s\n", + gimp_file_get_utf8_name (file), error->message); + } + + g_clear_error (&error); + g_object_unref (file); + } + + tips_count = g_list_length (tips); + + config = GIMP_GUI_CONFIG (gimp->config); + + if (config->last_tip_shown >= tips_count || config->last_tip_shown < 0) + config->last_tip_shown = 0; + + current_tip = g_list_nth (tips, config->last_tip_shown); + + if (tips_dialog) + return tips_dialog; + + tips_dialog = gimp_dialog_new (_("GIMP Tip of the Day"), + "gimp-tip-of-the-day", + NULL, 0, NULL, NULL, + NULL); + + button = gtk_dialog_add_button (GTK_DIALOG (tips_dialog), + _("_Previous Tip"), RESPONSE_PREVIOUS); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_icon_name (GIMP_ICON_GO_PREVIOUS, + GTK_ICON_SIZE_BUTTON)); + + button = gtk_dialog_add_button (GTK_DIALOG (tips_dialog), + _("_Next Tip"), RESPONSE_NEXT); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_icon_name (GIMP_ICON_GO_NEXT, + GTK_ICON_SIZE_BUTTON)); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (tips_dialog), + RESPONSE_NEXT, tips_count > 1); + gtk_dialog_set_response_sensitive (GTK_DIALOG (tips_dialog), + RESPONSE_PREVIOUS, tips_count > 1); + + g_signal_connect (tips_dialog, "response", + G_CALLBACK (tips_dialog_response), + NULL); + g_signal_connect (tips_dialog, "destroy", + G_CALLBACK (tips_dialog_destroy), + config); + + 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 (tips_dialog))), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_INFORMATION, + 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); + + gtk_container_set_focus_chain (GTK_CONTAINER (hbox), NULL); + + tip_label = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (tip_label), TRUE); + gtk_label_set_justify (GTK_LABEL (tip_label), GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap (GTK_LABEL (tip_label), TRUE); + gtk_label_set_yalign (GTK_LABEL (tip_label), 0.0); + gtk_box_pack_start (GTK_BOX (vbox), tip_label, TRUE, TRUE, 0); + gtk_widget_show (tip_label); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + more_button = gtk_link_button_new_with_label ("https://docs.gimp.org/", + /* a link to the related section in the user manual */ + _("Learn more")); + gtk_widget_show (more_button); + gtk_box_pack_start (GTK_BOX (hbox), more_button, FALSE, FALSE, 0); + + /* this is deprecated but better than showing two URIs, see bug #762279 */ + gtk_link_button_set_uri_hook (tips_uri_hook, NULL, NULL); + + g_signal_connect (more_button, "clicked", + G_CALLBACK (more_button_clicked), + gimp); + + tips_dialog_set_tip (current_tip->data); + + return tips_dialog; +} + +static void +tips_dialog_destroy (GtkWidget *widget, + GimpGuiConfig *config) +{ + /* the last-shown-tip is saved in sessionrc */ + config->last_tip_shown = g_list_position (tips, current_tip); + + tips_dialog = NULL; + current_tip = NULL; + + gimp_tips_free (tips); + tips = NULL; +} + +static void +tips_dialog_response (GtkWidget *dialog, + gint response) +{ + switch (response) + { + case RESPONSE_PREVIOUS: + current_tip = current_tip->prev ? current_tip->prev : g_list_last (tips); + tips_dialog_set_tip (current_tip->data); + break; + + case RESPONSE_NEXT: + current_tip = current_tip->next ? current_tip->next : tips; + tips_dialog_set_tip (current_tip->data); + break; + + default: + gtk_widget_destroy (dialog); + break; + } +} + +static void +tips_dialog_set_tip (GimpTip *tip) +{ + g_return_if_fail (tip != NULL); + + gtk_label_set_markup (GTK_LABEL (tip_label), tip->text); + + /* set the URI to unset the "visited" state */ + gtk_link_button_set_uri (GTK_LINK_BUTTON (more_button), + "https://docs.gimp.org/"); + + gtk_widget_set_sensitive (more_button, tip->help_id != NULL); +} + +static void +more_button_clicked (GtkWidget *button, + Gimp *gimp) +{ + GimpTip *tip = current_tip->data; + + g_signal_stop_emission_by_name (button, "clicked"); + + if (tip->help_id) + gimp_help (gimp, NULL, NULL, tip->help_id); +} + +static void +tips_uri_hook (GtkLinkButton *button, + const gchar *link_, + gpointer user_data) +{ + /* do nothing */ +} diff --git a/app/dialogs/tips-dialog.h b/app/dialogs/tips-dialog.h new file mode 100644 index 0000000..e594212 --- /dev/null +++ b/app/dialogs/tips-dialog.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 . + */ + +#ifndef __TIPS_DIALOG_H__ +#define __TIPS_DIALOG_H__ + + +GtkWidget * tips_dialog_create (Gimp *gimp); + + +#endif /* __TIPS_DIALOG_H__ */ diff --git a/app/dialogs/tips-parser.c b/app/dialogs/tips-parser.c new file mode 100644 index 0000000..9e94f0f --- /dev/null +++ b/app/dialogs/tips-parser.c @@ -0,0 +1,477 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * tips-parser.c - Parse the gimp-tips.xml file. + * Copyright (C) 2002, 2008 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 . + */ + +#include "config.h" + +#include + +#include + +#include "config/config-types.h" +#include "config/gimpxmlparser.h" + +#include "tips-parser.h" + +#include "gimp-intl.h" + + +typedef enum +{ + TIPS_START, + TIPS_IN_TIPS, + TIPS_IN_TIP, + TIPS_IN_THETIP, + TIPS_IN_UNKNOWN +} TipsParserState; + +typedef enum +{ + TIPS_LOCALE_NONE, + TIPS_LOCALE_MATCH, + TIPS_LOCALE_MISMATCH +} TipsParserLocaleState; + +typedef struct +{ + TipsParserState state; + TipsParserState last_known_state; + const gchar *locale; + const gchar *help_id; + TipsParserLocaleState locale_state; + gint markup_depth; + gint unknown_depth; + GString *value; + GimpTip *current_tip; + GList *tips; +} TipsParser; + + +static void tips_parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void tips_parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void tips_parser_characters (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + +static void tips_parser_start_markup (TipsParser *parser, + const gchar *markup_name); +static void tips_parser_end_markup (TipsParser *parser, + const gchar *markup_name); +static void tips_parser_start_unknown (TipsParser *parser); +static void tips_parser_end_unknown (TipsParser *parser); + +static gchar * tips_parser_parse_help_id (TipsParser *parser, + const gchar **names, + const gchar **values); + +static void tips_parser_parse_locale (TipsParser *parser, + const gchar **names, + const gchar **values); +static void tips_parser_set_by_locale (TipsParser *parser, + gchar **dest); + + +static const GMarkupParser markup_parser = +{ + tips_parser_start_element, + tips_parser_end_element, + tips_parser_characters, + NULL, /* passthrough */ + NULL /* error */ +}; + + +GimpTip * +gimp_tip_new (const gchar *title, + const gchar *format, + ...) +{ + GimpTip *tip = g_slice_new0 (GimpTip); + GString *str = g_string_new (NULL); + + if (title) + { + g_string_append (str, ""); + g_string_append (str, title); + g_string_append (str, ""); + + if (format) + g_string_append (str, "\n\n"); + } + + if (format) + { + va_list args; + + va_start (args, format); + g_string_append_vprintf (str, format, args); + va_end (args); + } + + tip->text = g_string_free (str, FALSE); + + return tip; +} + +void +gimp_tip_free (GimpTip *tip) +{ + if (! tip) + return; + + g_free (tip->text); + g_free (tip->help_id); + + g_slice_free (GimpTip, tip); +} + +/** + * gimp_tips_from_file: + * @file: the tips file to parse + * @error: return location for a #GError + * + * Reads a gimp-tips XML file, creates a new #GimpTip for + * each tip entry and returns a #GList of them. If a parser + * error occurs at some point, the uncompleted list is + * returned and @error is set (unless @error is %NULL). + * The message set in @error contains a detailed description + * of the problem. + * + * Return value: a #Glist of #GimpTips. + **/ +GList * +gimp_tips_from_file (GFile *file, + GError **error) +{ + GimpXmlParser *xml_parser; + TipsParser parser = { 0, }; + const gchar *tips_locale; + GList *tips = NULL; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + parser.value = g_string_new (NULL); + + /* This is a special string to specify the language identifier to + look for in the gimp-tips.xml file. Please translate the C in it + according to the name of the po file used for gimp-tips.xml. + E.g. for the german translation, that would be "tips-locale:de". + */ + tips_locale = _("tips-locale:C"); + + if (g_str_has_prefix (tips_locale, "tips-locale:")) + { + tips_locale += strlen ("tips-locale:"); + + if (*tips_locale && *tips_locale != 'C') + parser.locale = tips_locale; + } + else + { + g_warning ("Wrong translation for 'tips-locale:', fix the translation!"); + } + + xml_parser = gimp_xml_parser_new (&markup_parser, &parser); + + gimp_xml_parser_parse_gfile (xml_parser, file, error); + + gimp_xml_parser_free (xml_parser); + + tips = g_list_reverse (parser.tips); + + gimp_tip_free (parser.current_tip); + g_string_free (parser.value, TRUE); + + return tips; +} + +void +gimp_tips_free (GList *tips) +{ + GList *list; + + for (list = tips; list; list = list->next) + gimp_tip_free (list->data); + + g_list_free (tips); +} + +static void +tips_parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + TipsParser *parser = user_data; + + switch (parser->state) + { + case TIPS_START: + if (strcmp (element_name, "gimp-tips") == 0) + { + parser->state = TIPS_IN_TIPS; + } + else + { + tips_parser_start_unknown (parser); + } + break; + + case TIPS_IN_TIPS: + if (strcmp (element_name, "tip") == 0) + { + parser->state = TIPS_IN_TIP; + parser->current_tip = g_slice_new0 (GimpTip); + parser->current_tip->help_id = tips_parser_parse_help_id (parser, + attribute_names, + attribute_values); + } + else + { + tips_parser_start_unknown (parser); + } + break; + + case TIPS_IN_TIP: + if (strcmp (element_name, "thetip") == 0) + { + parser->state = TIPS_IN_THETIP; + tips_parser_parse_locale (parser, attribute_names, attribute_values); + } + else + { + tips_parser_start_unknown (parser); + } + break; + + case TIPS_IN_THETIP: + if (strcmp (element_name, "b" ) == 0 || + strcmp (element_name, "big") == 0 || + strcmp (element_name, "tt" ) == 0) + { + tips_parser_start_markup (parser, element_name); + } + else + { + tips_parser_start_unknown (parser); + } + break; + + case TIPS_IN_UNKNOWN: + tips_parser_start_unknown (parser); + break; + } +} + +static void +tips_parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + TipsParser *parser = user_data; + + switch (parser->state) + { + case TIPS_START: + g_warning ("%s: shouldn't get here", G_STRLOC); + break; + + case TIPS_IN_TIPS: + parser->state = TIPS_START; + break; + + case TIPS_IN_TIP: + parser->tips = g_list_prepend (parser->tips, parser->current_tip); + parser->current_tip = NULL; + parser->state = TIPS_IN_TIPS; + break; + + case TIPS_IN_THETIP: + if (parser->markup_depth == 0) + { + tips_parser_set_by_locale (parser, &parser->current_tip->text); + g_string_truncate (parser->value, 0); + parser->state = TIPS_IN_TIP; + } + else + tips_parser_end_markup (parser, element_name); + break; + + case TIPS_IN_UNKNOWN: + tips_parser_end_unknown (parser); + break; + } +} + +static void +tips_parser_characters (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + TipsParser *parser = user_data; + + switch (parser->state) + { + case TIPS_IN_THETIP: + if (parser->locale_state != TIPS_LOCALE_MISMATCH) + { + gint i; + + /* strip tabs, newlines and adjacent whitespace */ + for (i = 0; i < text_len; i++) + { + if (text[i] != ' ' && + text[i] != '\t' && text[i] != '\n' && text[i] != '\r') + { + g_string_append_c (parser->value, text[i]); + } + else if (parser->value->len > 0 && + parser->value->str[parser->value->len - 1] != ' ') + { + g_string_append_c (parser->value, ' '); + } + } + } + break; + default: + break; + } +} + +static void +tips_parser_start_markup (TipsParser *parser, + const gchar *markup_name) +{ + parser->markup_depth++; + g_string_append_printf (parser->value, "<%s>", markup_name); +} + +static void +tips_parser_end_markup (TipsParser *parser, + const gchar *markup_name) +{ + gimp_assert (parser->markup_depth > 0); + + parser->markup_depth--; + g_string_append_printf (parser->value, "", markup_name); +} + +static void +tips_parser_start_unknown (TipsParser *parser) +{ + if (parser->unknown_depth == 0) + parser->last_known_state = parser->state; + + parser->state = TIPS_IN_UNKNOWN; + parser->unknown_depth++; +} + +static void +tips_parser_end_unknown (TipsParser *parser) +{ + gimp_assert (parser->unknown_depth > 0 && parser->state == TIPS_IN_UNKNOWN); + + parser->unknown_depth--; + + if (parser->unknown_depth == 0) + parser->state = parser->last_known_state; +} + +static gchar * +tips_parser_parse_help_id (TipsParser *parser, + const gchar **names, + const gchar **values) +{ + while (*names && *values) + { + if (strcmp (*names, "help") == 0 && **values) + return g_strdup (*values); + + names++; + values++; + } + + return NULL; +} + +static void +tips_parser_parse_locale (TipsParser *parser, + const gchar **names, + const gchar **values) +{ + parser->locale_state = TIPS_LOCALE_NONE; + + while (*names && *values) + { + if (strcmp (*names, "xml:lang") == 0 && **values) + { + parser->locale_state = (parser->locale && + strcmp (*values, parser->locale) == 0 ? + TIPS_LOCALE_MATCH : TIPS_LOCALE_MISMATCH); + } + + names++; + values++; + } +} + +static void +tips_parser_set_by_locale (TipsParser *parser, + gchar **dest) +{ + switch (parser->locale_state) + { + case TIPS_LOCALE_NONE: + if (!parser->locale) + { + g_free (*dest); + *dest = g_strdup (parser->value->str); + } + else if (*dest == NULL) + { + *dest = g_strdup (parser->value->str); + } + break; + + case TIPS_LOCALE_MATCH: + g_free (*dest); + *dest = g_strdup (parser->value->str); + break; + + case TIPS_LOCALE_MISMATCH: + break; + } +} + diff --git a/app/dialogs/tips-parser.h b/app/dialogs/tips-parser.h new file mode 100644 index 0000000..4d9fab7 --- /dev/null +++ b/app/dialogs/tips-parser.h @@ -0,0 +1,44 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * tips-parser.h - Parse the gimp-tips.xml file. + * Copyright (C) 2002, 2008 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 . + */ + +#ifndef __TIPS_PARSER_H__ +#define __TIPS_PARSER_H__ + + +typedef struct _GimpTip GimpTip; + +struct _GimpTip +{ + gchar *text; + gchar *help_id; +}; + + +GimpTip * gimp_tip_new (const gchar *title, + const gchar *format, + ...) G_GNUC_PRINTF(2, 3); +void gimp_tip_free (GimpTip *tip); + +GList * gimp_tips_from_file (GFile *file, + GError **error); +void gimp_tips_free (GList *tips); + + +#endif /* __TIPS_PARSER_H__ */ diff --git a/app/dialogs/user-install-dialog.c b/app/dialogs/user-install-dialog.c new file mode 100644 index 0000000..af9edd4 --- /dev/null +++ b/app/dialogs/user-install-dialog.c @@ -0,0 +1,153 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * user-install-dialog.c + * Copyright (C) 2000-2006 Michael Natterer and 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimp-user-install.h" + +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" + +#include "user-install-dialog.h" + +#include "gimp-intl.h" + + +static GtkWidget * user_install_dialog_new (GimpUserInstall *install); +static void user_install_dialog_log (const gchar *message, + gboolean error, + gpointer data); + + +gboolean +user_install_dialog_run (GimpUserInstall *install) +{ + GtkWidget *dialog; + gboolean success; + + g_return_val_if_fail (install != NULL, FALSE); + + dialog = user_install_dialog_new (install); + + success = gimp_user_install_run (install); + + if (! success) + { + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_main_quit), + NULL); + + gtk_widget_show (dialog); + + gtk_main (); + } + + gtk_widget_destroy (dialog); + + return success; +} + +static GtkWidget * +user_install_dialog_new (GimpUserInstall *install) +{ + GtkWidget *dialog; + GtkWidget *frame; + GtkWidget *scrolled; + GtkTextBuffer *buffer; + GtkWidget *view; + + gimp_icons_init (); + + dialog = gimp_message_dialog_new (_("GIMP User Installation"), + GIMP_ICON_WILBER_EEK, + NULL, 0, NULL, NULL, + + _("_Quit"), GTK_RESPONSE_OK, + + NULL); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("User installation failed!")); + gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box, + _("The GIMP user installation failed; " + "see the log for details.")); + + frame = gimp_frame_new (_("Installation Log")); + gtk_container_set_border_width (GTK_CONTAINER (frame), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (frame), scrolled); + gtk_widget_show (scrolled); + + buffer = gtk_text_buffer_new (NULL); + + gtk_text_buffer_create_tag (buffer, "bold", + "weight", PANGO_WEIGHT_BOLD, + NULL); + + view = gtk_text_view_new_with_buffer (buffer); + gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD); + gtk_widget_set_size_request (view, -1, 200); + gtk_container_add (GTK_CONTAINER (scrolled), view); + gtk_widget_show (view); + + g_object_unref (buffer); + + gimp_user_install_set_log_handler (install, user_install_dialog_log, buffer); + + return dialog; +} + +static void +user_install_dialog_log (const gchar *message, + gboolean error, + gpointer data) +{ + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data); + GtkTextIter cursor; + + gtk_text_buffer_get_end_iter (buffer, &cursor); + + if (error && message) + { + gtk_text_buffer_insert_with_tags_by_name (buffer, &cursor, message, -1, + "bold", NULL); + } + else if (message) + { + gtk_text_buffer_insert (buffer, &cursor, message, -1); + } + + gtk_text_buffer_insert (buffer, &cursor, "\n", -1); +} diff --git a/app/dialogs/user-install-dialog.h b/app/dialogs/user-install-dialog.h new file mode 100644 index 0000000..baa2bda --- /dev/null +++ b/app/dialogs/user-install-dialog.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 . + */ + +#ifndef __USER_INSTALL_DIALOG_H__ +#define __USER_INSTALL_DIALOG_H__ + + +gboolean user_install_dialog_run (GimpUserInstall *install); + + +#endif /* __USER_INSTALL_DIALOG_H__ */ diff --git a/app/dialogs/vectors-export-dialog.c b/app/dialogs/vectors-export-dialog.c new file mode 100644 index 0000000..9a3e082 --- /dev/null +++ b/app/dialogs/vectors-export-dialog.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpimage.h" + +#include "vectors-export-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _VectorsExportDialog VectorsExportDialog; + +struct _VectorsExportDialog +{ + GimpImage *image; + gboolean active_only; + GimpVectorsExportCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void vectors_export_dialog_free (VectorsExportDialog *private); +static void vectors_export_dialog_response (GtkWidget *widget, + gint response_id, + VectorsExportDialog *private); + + +/* public function */ + +GtkWidget * +vectors_export_dialog_new (GimpImage *image, + GtkWidget *parent, + GFile *export_folder, + gboolean active_only, + GimpVectorsExportCallback callback, + gpointer user_data) +{ + VectorsExportDialog *private; + GtkWidget *dialog; + GtkWidget *combo; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (export_folder == NULL || G_IS_FILE (export_folder), + NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (VectorsExportDialog); + + private->image = image; + private->active_only = active_only; + private->callback = callback; + private->user_data = user_data; + + dialog = gtk_file_chooser_dialog_new (_("Export Path to SVG"), 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_window_set_role (GTK_WINDOW (dialog), "gimp-vectors-export"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (parent)); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), + TRUE); + + if (export_folder) + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), + export_folder, NULL); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) vectors_export_dialog_free, private); + + g_signal_connect_object (image, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, 0); + + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + g_signal_connect (dialog, "response", + G_CALLBACK (vectors_export_dialog_response), + private); + + combo = gimp_int_combo_box_new (_("Export the active path"), TRUE, + _("Export all paths from this image"), FALSE, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + private->active_only); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), combo); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + &private->active_only); + + return dialog; +} + + +/* private functions */ + +static void +vectors_export_dialog_free (VectorsExportDialog *private) +{ + g_slice_free (VectorsExportDialog, private); +} + +static void +vectors_export_dialog_response (GtkWidget *dialog, + gint response_id, + VectorsExportDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); + GFile *file; + + file = gtk_file_chooser_get_file (chooser); + + if (file) + { + GFile *folder; + + folder = gtk_file_chooser_get_current_folder_file (chooser); + + private->callback (dialog, + private->image, + file, + folder, + private->active_only, + private->user_data); + + if (folder) + g_object_unref (folder); + + g_object_unref (file); + } + } + else + { + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/vectors-export-dialog.h b/app/dialogs/vectors-export-dialog.h new file mode 100644 index 0000000..236b6e5 --- /dev/null +++ b/app/dialogs/vectors-export-dialog.h @@ -0,0 +1,38 @@ +/* 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 . + */ + +#ifndef __VECTORS_EXPORT_DIALOG_H__ +#define __VECTORS_EXPORT_DIALOG_H__ + + +typedef void (* GimpVectorsExportCallback) (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *export_folder, + gboolean active_only, + gpointer user_data); + + +GtkWidget * vectors_export_dialog_new (GimpImage *image, + GtkWidget *parent, + GFile *export_folder, + gboolean active_only, + GimpVectorsExportCallback callback, + gpointer user_data); + + +#endif /* __VECTORS_EXPORT_DIALOG_H__ */ diff --git a/app/dialogs/vectors-import-dialog.c b/app/dialogs/vectors-import-dialog.c new file mode 100644 index 0000000..76ac7cc --- /dev/null +++ b/app/dialogs/vectors-import-dialog.c @@ -0,0 +1,209 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpimage.h" + +#include "vectors-import-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _VectorsImportDialog VectorsImportDialog; + +struct _VectorsImportDialog +{ + GimpImage *image; + gboolean merge_vectors; + gboolean scale_vectors; + GimpVectorsImportCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void vectors_import_dialog_free (VectorsImportDialog *private); +static void vectors_import_dialog_response (GtkWidget *dialog, + gint response_id, + VectorsImportDialog *private); + + +/* public function */ + +GtkWidget * +vectors_import_dialog_new (GimpImage *image, + GtkWidget *parent, + GFile *import_folder, + gboolean merge_vectors, + gboolean scale_vectors, + GimpVectorsImportCallback callback, + gpointer user_data) +{ + VectorsImportDialog *private; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *button; + GtkFileFilter *filter; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (import_folder == NULL || G_IS_FILE (import_folder), + NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (VectorsImportDialog); + + private->image = image; + private->merge_vectors = merge_vectors; + private->scale_vectors = scale_vectors; + private->callback = callback; + private->user_data = user_data; + + dialog = gtk_file_chooser_dialog_new (_("Import Paths from SVG"), 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); + + gtk_window_set_role (GTK_WINDOW (dialog), "gimp-vectors-import"); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); + gtk_window_set_screen (GTK_WINDOW (dialog), + gtk_widget_get_screen (parent)); + + if (import_folder) + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), + import_folder, NULL); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) vectors_import_dialog_free, private); + + g_signal_connect_object (image, "disconnect", + G_CALLBACK (gtk_widget_destroy), + dialog, 0); + + g_signal_connect (dialog, "delete-event", + G_CALLBACK (gtk_true), + NULL); + + g_signal_connect (dialog, "response", + G_CALLBACK (vectors_import_dialog_response), + private); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files (*.*)")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Scalable SVG image (*.svg)")); + gtk_file_filter_add_pattern (filter, "*.[Ss][Vv][Gg]"); + gtk_file_filter_add_mime_type (filter, "image/svg+xml"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), vbox); + gtk_widget_show (vbox); + + button = gtk_check_button_new_with_mnemonic (_("_Merge imported paths")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->merge_vectors); + 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), + &private->merge_vectors); + + button = gtk_check_button_new_with_mnemonic (_("_Scale imported paths " + "to fit image")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + private->scale_vectors); + 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), + &private->scale_vectors); + + return dialog; +} + + +/* private functions */ + +static void +vectors_import_dialog_free (VectorsImportDialog *private) +{ + g_slice_free (VectorsImportDialog, private); +} + +static void +vectors_import_dialog_response (GtkWidget *dialog, + gint response_id, + VectorsImportDialog *private) +{ + if (response_id == GTK_RESPONSE_OK) + { + GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); + GFile *file; + + file = gtk_file_chooser_get_file (chooser); + + if (file) + { + GFile *folder; + + folder = gtk_file_chooser_get_current_folder_file (chooser); + + private->callback (dialog, + private->image, + file, + folder, + private->merge_vectors, + private->scale_vectors, + private->user_data); + + if (folder) + g_object_unref (folder); + + g_object_unref (file); + } + } + else + { + gtk_widget_destroy (dialog); + } +} diff --git a/app/dialogs/vectors-import-dialog.h b/app/dialogs/vectors-import-dialog.h new file mode 100644 index 0000000..b8928d4 --- /dev/null +++ b/app/dialogs/vectors-import-dialog.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 . + */ + +#ifndef __VECTORS_IMPORT_DIALOG_H__ +#define __VECTORS_IMPORT_DIALOG_H__ + + +typedef void (* GimpVectorsImportCallback) (GtkWidget *dialog, + GimpImage *image, + GFile *file, + GFile *import_folder, + gboolean merge_vectors, + gboolean scale_vectors, + gpointer user_data); + + +GtkWidget * vectors_import_dialog_new (GimpImage *image, + GtkWidget *parent, + GFile *import_folder, + gboolean merge_vectors, + gboolean scale_vectors, + GimpVectorsImportCallback callback, + gpointer user_data); + + +#endif /* __VECTORS_IMPORT_DIALOG_H__ */ diff --git a/app/dialogs/vectors-options-dialog.c b/app/dialogs/vectors-options-dialog.c new file mode 100644 index 0000000..3b00be0 --- /dev/null +++ b/app/dialogs/vectors-options-dialog.c @@ -0,0 +1,160 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "dialogs-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "vectors/gimpvectors.h" + +#include "item-options-dialog.h" +#include "vectors-options-dialog.h" + +#include "gimp-intl.h" + + +typedef struct _VectorsOptionsDialog VectorsOptionsDialog; + +struct _VectorsOptionsDialog +{ + GimpVectorsOptionsCallback callback; + gpointer user_data; +}; + + +/* local function prototypes */ + +static void vectors_options_dialog_free (VectorsOptionsDialog *private); +static void vectors_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data); + + +/* public functions */ + +GtkWidget * +vectors_options_dialog_new (GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + GimpVectorsOptionsCallback callback, + gpointer user_data) +{ + VectorsOptionsDialog *private; + GtkWidget *dialog; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); + g_return_val_if_fail (title != NULL, NULL); + g_return_val_if_fail (role != NULL, NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail (desc != NULL, NULL); + g_return_val_if_fail (help_id != NULL, NULL); + g_return_val_if_fail (callback != NULL, NULL); + + private = g_slice_new0 (VectorsOptionsDialog); + + private->callback = callback; + private->user_data = user_data; + + dialog = item_options_dialog_new (image, GIMP_ITEM (vectors), context, + parent, title, role, + icon_name, desc, help_id, + _("Path _name:"), + GIMP_ICON_TOOL_PATH, + _("Lock path _strokes"), + _("Lock path _position"), + vectors_name, + vectors_visible, + vectors_linked, + vectors_color_tag, + vectors_lock_content, + vectors_lock_position, + vectors_options_dialog_callback, + private); + + g_object_weak_ref (G_OBJECT (dialog), + (GWeakNotify) vectors_options_dialog_free, private); + + return dialog; +} + + +/* private functions */ + +static void +vectors_options_dialog_free (VectorsOptionsDialog *private) +{ + g_slice_free (VectorsOptionsDialog, private); +} + +static void +vectors_options_dialog_callback (GtkWidget *dialog, + GimpImage *image, + GimpItem *item, + GimpContext *context, + const gchar *item_name, + gboolean item_visible, + gboolean item_linked, + GimpColorTag item_color_tag, + gboolean item_lock_content, + gboolean item_lock_position, + gpointer user_data) +{ + VectorsOptionsDialog *private = user_data; + + private->callback (dialog, + image, + GIMP_VECTORS (item), + context, + item_name, + item_visible, + item_linked, + item_color_tag, + item_lock_content, + item_lock_position, + private->user_data); +} diff --git a/app/dialogs/vectors-options-dialog.h b/app/dialogs/vectors-options-dialog.h new file mode 100644 index 0000000..00f5a32 --- /dev/null +++ b/app/dialogs/vectors-options-dialog.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 . + */ + +#ifndef __VECTORS_OPTIONS_DIALOG_H__ +#define __VECTORS_OPTIONS_DIALOG_H__ + + +typedef void (* GimpVectorsOptionsCallback) (GtkWidget *dialog, + GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + gpointer user_data); + + +GtkWidget * vectors_options_dialog_new (GimpImage *image, + GimpVectors *vectors, + GimpContext *context, + GtkWidget *parent, + const gchar *title, + const gchar *role, + const gchar *icon_name, + const gchar *desc, + const gchar *help_id, + const gchar *vectors_name, + gboolean vectors_visible, + gboolean vectors_linked, + GimpColorTag vectors_color_tag, + gboolean vectors_lock_content, + gboolean vectors_lock_position, + GimpVectorsOptionsCallback callback, + gpointer user_data); + + +#endif /* __VECTORS_OPTIONS_DIALOG_H__ */ diff --git a/app/display/Makefile.am b/app/display/Makefile.am new file mode 100644 index 0000000..dbee710 --- /dev/null +++ b/app/display/Makefile.am @@ -0,0 +1,247 @@ +## Process this file with automake to produce Makefile.in + +if PLATFORM_OSX +xobjective_c = "-xobjective-c" +xobjective_cxx = "-xobjective-c++" +xnone = "-xnone" +endif + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Display\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappdisplay.a + +libappdisplay_a_sources = \ + display-enums.h \ + display-types.h \ + gimpcanvas.c \ + gimpcanvas.h \ + gimpcanvas-style.c \ + gimpcanvas-style.h \ + gimpcanvasarc.c \ + gimpcanvasarc.h \ + gimpcanvasboundary.c \ + gimpcanvasboundary.h \ + gimpcanvasbufferpreview.c \ + gimpcanvasbufferpreview.h \ + gimpcanvascanvasboundary.c \ + gimpcanvascanvasboundary.h \ + gimpcanvascorner.c \ + gimpcanvascorner.h \ + gimpcanvascursor.c \ + gimpcanvascursor.h \ + gimpcanvasgrid.c \ + gimpcanvasgrid.h \ + gimpcanvasgroup.c \ + gimpcanvasgroup.h \ + gimpcanvasguide.c \ + gimpcanvasguide.h \ + gimpcanvashandle.c \ + gimpcanvashandle.h \ + gimpcanvasitem.c \ + gimpcanvasitem.h \ + gimpcanvasitem-utils.c \ + gimpcanvasitem-utils.h \ + gimpcanvaslayerboundary.c \ + gimpcanvaslayerboundary.h \ + gimpcanvaslimit.c \ + gimpcanvaslimit.h \ + gimpcanvasline.c \ + gimpcanvasline.h \ + gimpcanvaspassepartout.c \ + gimpcanvaspassepartout.h \ + gimpcanvaspath.c \ + gimpcanvaspath.h \ + gimpcanvaspen.c \ + gimpcanvaspen.h \ + gimpcanvaspolygon.c \ + gimpcanvaspolygon.h \ + gimpcanvasprogress.c \ + gimpcanvasprogress.h \ + gimpcanvasproxygroup.c \ + gimpcanvasproxygroup.h \ + gimpcanvasrectangle.c \ + gimpcanvasrectangle.h \ + gimpcanvasrectangleguides.c \ + gimpcanvasrectangleguides.h \ + gimpcanvassamplepoint.c \ + gimpcanvassamplepoint.h \ + gimpcanvastextcursor.c \ + gimpcanvastextcursor.h \ + gimpcanvastransformguides.c \ + gimpcanvastransformguides.h \ + gimpcanvastransformpreview.c \ + gimpcanvastransformpreview.h \ + gimpcursorview.c \ + gimpcursorview.h \ + gimpdisplay.c \ + gimpdisplay.h \ + gimpdisplay-foreach.c \ + gimpdisplay-foreach.h \ + gimpdisplay-handlers.c \ + gimpdisplay-handlers.h \ + gimpdisplayshell.c \ + gimpdisplayshell.h \ + gimpdisplayshell-actions.c \ + gimpdisplayshell-actions.h \ + gimpdisplayshell-appearance.c \ + gimpdisplayshell-appearance.h \ + gimpdisplayshell-autoscroll.c \ + gimpdisplayshell-autoscroll.h \ + gimpdisplayshell-callbacks.c \ + gimpdisplayshell-callbacks.h \ + gimpdisplayshell-close.c \ + gimpdisplayshell-close.h \ + gimpdisplayshell-cursor.c \ + gimpdisplayshell-cursor.h \ + gimpdisplayshell-dnd.c \ + gimpdisplayshell-dnd.h \ + gimpdisplayshell-draw.c \ + gimpdisplayshell-draw.h \ + gimpdisplayshell-expose.c \ + gimpdisplayshell-expose.h \ + gimpdisplayshell-grab.c \ + gimpdisplayshell-grab.h \ + gimpdisplayshell-handlers.c \ + gimpdisplayshell-handlers.h \ + gimpdisplayshell-filter.c \ + gimpdisplayshell-filter.h \ + gimpdisplayshell-filter-dialog.c \ + gimpdisplayshell-filter-dialog.h \ + gimpdisplayshell-layer-select.c \ + gimpdisplayshell-layer-select.h \ + gimpdisplayshell-icon.c \ + gimpdisplayshell-icon.h \ + gimpdisplayshell-items.c \ + gimpdisplayshell-items.h \ + gimpdisplayshell-profile.c \ + gimpdisplayshell-profile.h \ + gimpdisplayshell-progress.c \ + gimpdisplayshell-progress.h \ + gimpdisplayshell-render.c \ + gimpdisplayshell-render.h \ + gimpdisplayshell-rotate.c \ + gimpdisplayshell-rotate.h \ + gimpdisplayshell-rotate-dialog.c \ + gimpdisplayshell-rotate-dialog.h \ + gimpdisplayshell-rulers.c \ + gimpdisplayshell-rulers.h \ + gimpdisplayshell-scale.c \ + gimpdisplayshell-scale.h \ + gimpdisplayshell-scale-dialog.c \ + gimpdisplayshell-scale-dialog.h \ + gimpdisplayshell-scroll.c \ + gimpdisplayshell-scroll.h \ + gimpdisplayshell-scrollbars.c \ + gimpdisplayshell-scrollbars.h \ + gimpdisplayshell-selection.c \ + gimpdisplayshell-selection.h \ + gimpdisplayshell-title.c \ + gimpdisplayshell-title.h \ + gimpdisplayshell-tool-events.c \ + gimpdisplayshell-tool-events.h \ + gimpdisplayshell-transform.c \ + gimpdisplayshell-transform.h \ + gimpdisplayshell-utils.c \ + gimpdisplayshell-utils.h \ + gimpdisplayxfer.c \ + gimpdisplayxfer.h \ + gimpimagewindow.c \ + gimpimagewindow.h \ + gimpmotionbuffer.c \ + gimpmotionbuffer.h \ + gimpmultiwindowstrategy.c \ + gimpmultiwindowstrategy.h \ + gimpnavigationeditor.c \ + gimpnavigationeditor.h \ + gimpscalecombobox.c \ + gimpscalecombobox.h \ + gimpsinglewindowstrategy.c \ + gimpsinglewindowstrategy.h \ + gimpstatusbar.c \ + gimpstatusbar.h \ + gimptooldialog.c \ + gimptooldialog.h \ + gimptoolgui.c \ + gimptoolgui.h \ + gimptoolcompass.c \ + gimptoolcompass.h \ + gimptoolfocus.h \ + gimptoolfocus.c \ + gimptoolgyroscope.c \ + gimptoolgyroscope.h \ + gimptoolhandlegrid.c \ + gimptoolhandlegrid.h \ + gimptoolline.c \ + gimptoolline.h \ + gimptoolpath.c \ + gimptoolpath.h \ + gimptoolpolygon.c \ + gimptoolpolygon.h \ + gimptoolrectangle.c \ + gimptoolrectangle.h \ + gimptoolrotategrid.c \ + gimptoolrotategrid.h \ + gimptoolsheargrid.c \ + gimptoolsheargrid.h \ + gimptooltransform3dgrid.c \ + gimptooltransform3dgrid.h \ + gimptooltransformgrid.c \ + gimptooltransformgrid.h \ + gimptoolwidget.c \ + gimptoolwidget.h \ + gimptoolwidgetgroup.c \ + gimptoolwidgetgroup.h + +libappdisplay_a_built_sources = display-enums.c + +libappdisplay_a_SOURCES = \ + $(libappdisplay_a_built_sources) \ + $(libappdisplay_a_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-dec +CLEANFILES = $(gen_sources) + +xgen-dec: $(srcdir)/display-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"display-enums.h\"\n#include\"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/display-enums.c: xgen-dec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/display/Makefile.in b/app/display/Makefile.in new file mode 100644 index 0000000..943fbe3 --- /dev/null +++ b/app/display/Makefile.in @@ -0,0 +1,1546 @@ +# 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 = app/display +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 = +libappdisplay_a_AR = $(AR) $(ARFLAGS) +libappdisplay_a_LIBADD = +am__objects_1 = display-enums.$(OBJEXT) +am__objects_2 = gimpcanvas.$(OBJEXT) gimpcanvas-style.$(OBJEXT) \ + gimpcanvasarc.$(OBJEXT) gimpcanvasboundary.$(OBJEXT) \ + gimpcanvasbufferpreview.$(OBJEXT) \ + gimpcanvascanvasboundary.$(OBJEXT) gimpcanvascorner.$(OBJEXT) \ + gimpcanvascursor.$(OBJEXT) gimpcanvasgrid.$(OBJEXT) \ + gimpcanvasgroup.$(OBJEXT) gimpcanvasguide.$(OBJEXT) \ + gimpcanvashandle.$(OBJEXT) gimpcanvasitem.$(OBJEXT) \ + gimpcanvasitem-utils.$(OBJEXT) \ + gimpcanvaslayerboundary.$(OBJEXT) gimpcanvaslimit.$(OBJEXT) \ + gimpcanvasline.$(OBJEXT) gimpcanvaspassepartout.$(OBJEXT) \ + gimpcanvaspath.$(OBJEXT) gimpcanvaspen.$(OBJEXT) \ + gimpcanvaspolygon.$(OBJEXT) gimpcanvasprogress.$(OBJEXT) \ + gimpcanvasproxygroup.$(OBJEXT) gimpcanvasrectangle.$(OBJEXT) \ + gimpcanvasrectangleguides.$(OBJEXT) \ + gimpcanvassamplepoint.$(OBJEXT) gimpcanvastextcursor.$(OBJEXT) \ + gimpcanvastransformguides.$(OBJEXT) \ + gimpcanvastransformpreview.$(OBJEXT) gimpcursorview.$(OBJEXT) \ + gimpdisplay.$(OBJEXT) gimpdisplay-foreach.$(OBJEXT) \ + gimpdisplay-handlers.$(OBJEXT) gimpdisplayshell.$(OBJEXT) \ + gimpdisplayshell-actions.$(OBJEXT) \ + gimpdisplayshell-appearance.$(OBJEXT) \ + gimpdisplayshell-autoscroll.$(OBJEXT) \ + gimpdisplayshell-callbacks.$(OBJEXT) \ + gimpdisplayshell-close.$(OBJEXT) \ + gimpdisplayshell-cursor.$(OBJEXT) \ + gimpdisplayshell-dnd.$(OBJEXT) gimpdisplayshell-draw.$(OBJEXT) \ + gimpdisplayshell-expose.$(OBJEXT) \ + gimpdisplayshell-grab.$(OBJEXT) \ + gimpdisplayshell-handlers.$(OBJEXT) \ + gimpdisplayshell-filter.$(OBJEXT) \ + gimpdisplayshell-filter-dialog.$(OBJEXT) \ + gimpdisplayshell-layer-select.$(OBJEXT) \ + gimpdisplayshell-icon.$(OBJEXT) \ + gimpdisplayshell-items.$(OBJEXT) \ + gimpdisplayshell-profile.$(OBJEXT) \ + gimpdisplayshell-progress.$(OBJEXT) \ + gimpdisplayshell-render.$(OBJEXT) \ + gimpdisplayshell-rotate.$(OBJEXT) \ + gimpdisplayshell-rotate-dialog.$(OBJEXT) \ + gimpdisplayshell-rulers.$(OBJEXT) \ + gimpdisplayshell-scale.$(OBJEXT) \ + gimpdisplayshell-scale-dialog.$(OBJEXT) \ + gimpdisplayshell-scroll.$(OBJEXT) \ + gimpdisplayshell-scrollbars.$(OBJEXT) \ + gimpdisplayshell-selection.$(OBJEXT) \ + gimpdisplayshell-title.$(OBJEXT) \ + gimpdisplayshell-tool-events.$(OBJEXT) \ + gimpdisplayshell-transform.$(OBJEXT) \ + gimpdisplayshell-utils.$(OBJEXT) gimpdisplayxfer.$(OBJEXT) \ + gimpimagewindow.$(OBJEXT) gimpmotionbuffer.$(OBJEXT) \ + gimpmultiwindowstrategy.$(OBJEXT) \ + gimpnavigationeditor.$(OBJEXT) gimpscalecombobox.$(OBJEXT) \ + gimpsinglewindowstrategy.$(OBJEXT) gimpstatusbar.$(OBJEXT) \ + gimptooldialog.$(OBJEXT) gimptoolgui.$(OBJEXT) \ + gimptoolcompass.$(OBJEXT) gimptoolfocus.$(OBJEXT) \ + gimptoolgyroscope.$(OBJEXT) gimptoolhandlegrid.$(OBJEXT) \ + gimptoolline.$(OBJEXT) gimptoolpath.$(OBJEXT) \ + gimptoolpolygon.$(OBJEXT) gimptoolrectangle.$(OBJEXT) \ + gimptoolrotategrid.$(OBJEXT) gimptoolsheargrid.$(OBJEXT) \ + gimptooltransform3dgrid.$(OBJEXT) \ + gimptooltransformgrid.$(OBJEXT) gimptoolwidget.$(OBJEXT) \ + gimptoolwidgetgroup.$(OBJEXT) +am_libappdisplay_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappdisplay_a_OBJECTS = $(am_libappdisplay_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)/display-enums.Po \ + ./$(DEPDIR)/gimpcanvas-style.Po ./$(DEPDIR)/gimpcanvas.Po \ + ./$(DEPDIR)/gimpcanvasarc.Po ./$(DEPDIR)/gimpcanvasboundary.Po \ + ./$(DEPDIR)/gimpcanvasbufferpreview.Po \ + ./$(DEPDIR)/gimpcanvascanvasboundary.Po \ + ./$(DEPDIR)/gimpcanvascorner.Po \ + ./$(DEPDIR)/gimpcanvascursor.Po ./$(DEPDIR)/gimpcanvasgrid.Po \ + ./$(DEPDIR)/gimpcanvasgroup.Po ./$(DEPDIR)/gimpcanvasguide.Po \ + ./$(DEPDIR)/gimpcanvashandle.Po \ + ./$(DEPDIR)/gimpcanvasitem-utils.Po \ + ./$(DEPDIR)/gimpcanvasitem.Po \ + ./$(DEPDIR)/gimpcanvaslayerboundary.Po \ + ./$(DEPDIR)/gimpcanvaslimit.Po ./$(DEPDIR)/gimpcanvasline.Po \ + ./$(DEPDIR)/gimpcanvaspassepartout.Po \ + ./$(DEPDIR)/gimpcanvaspath.Po ./$(DEPDIR)/gimpcanvaspen.Po \ + ./$(DEPDIR)/gimpcanvaspolygon.Po \ + ./$(DEPDIR)/gimpcanvasprogress.Po \ + ./$(DEPDIR)/gimpcanvasproxygroup.Po \ + ./$(DEPDIR)/gimpcanvasrectangle.Po \ + ./$(DEPDIR)/gimpcanvasrectangleguides.Po \ + ./$(DEPDIR)/gimpcanvassamplepoint.Po \ + ./$(DEPDIR)/gimpcanvastextcursor.Po \ + ./$(DEPDIR)/gimpcanvastransformguides.Po \ + ./$(DEPDIR)/gimpcanvastransformpreview.Po \ + ./$(DEPDIR)/gimpcursorview.Po \ + ./$(DEPDIR)/gimpdisplay-foreach.Po \ + ./$(DEPDIR)/gimpdisplay-handlers.Po ./$(DEPDIR)/gimpdisplay.Po \ + ./$(DEPDIR)/gimpdisplayshell-actions.Po \ + ./$(DEPDIR)/gimpdisplayshell-appearance.Po \ + ./$(DEPDIR)/gimpdisplayshell-autoscroll.Po \ + ./$(DEPDIR)/gimpdisplayshell-callbacks.Po \ + ./$(DEPDIR)/gimpdisplayshell-close.Po \ + ./$(DEPDIR)/gimpdisplayshell-cursor.Po \ + ./$(DEPDIR)/gimpdisplayshell-dnd.Po \ + ./$(DEPDIR)/gimpdisplayshell-draw.Po \ + ./$(DEPDIR)/gimpdisplayshell-expose.Po \ + ./$(DEPDIR)/gimpdisplayshell-filter-dialog.Po \ + ./$(DEPDIR)/gimpdisplayshell-filter.Po \ + ./$(DEPDIR)/gimpdisplayshell-grab.Po \ + ./$(DEPDIR)/gimpdisplayshell-handlers.Po \ + ./$(DEPDIR)/gimpdisplayshell-icon.Po \ + ./$(DEPDIR)/gimpdisplayshell-items.Po \ + ./$(DEPDIR)/gimpdisplayshell-layer-select.Po \ + ./$(DEPDIR)/gimpdisplayshell-profile.Po \ + ./$(DEPDIR)/gimpdisplayshell-progress.Po \ + ./$(DEPDIR)/gimpdisplayshell-render.Po \ + ./$(DEPDIR)/gimpdisplayshell-rotate-dialog.Po \ + ./$(DEPDIR)/gimpdisplayshell-rotate.Po \ + ./$(DEPDIR)/gimpdisplayshell-rulers.Po \ + ./$(DEPDIR)/gimpdisplayshell-scale-dialog.Po \ + ./$(DEPDIR)/gimpdisplayshell-scale.Po \ + ./$(DEPDIR)/gimpdisplayshell-scroll.Po \ + ./$(DEPDIR)/gimpdisplayshell-scrollbars.Po \ + ./$(DEPDIR)/gimpdisplayshell-selection.Po \ + ./$(DEPDIR)/gimpdisplayshell-title.Po \ + ./$(DEPDIR)/gimpdisplayshell-tool-events.Po \ + ./$(DEPDIR)/gimpdisplayshell-transform.Po \ + ./$(DEPDIR)/gimpdisplayshell-utils.Po \ + ./$(DEPDIR)/gimpdisplayshell.Po ./$(DEPDIR)/gimpdisplayxfer.Po \ + ./$(DEPDIR)/gimpimagewindow.Po ./$(DEPDIR)/gimpmotionbuffer.Po \ + ./$(DEPDIR)/gimpmultiwindowstrategy.Po \ + ./$(DEPDIR)/gimpnavigationeditor.Po \ + ./$(DEPDIR)/gimpscalecombobox.Po \ + ./$(DEPDIR)/gimpsinglewindowstrategy.Po \ + ./$(DEPDIR)/gimpstatusbar.Po ./$(DEPDIR)/gimptoolcompass.Po \ + ./$(DEPDIR)/gimptooldialog.Po ./$(DEPDIR)/gimptoolfocus.Po \ + ./$(DEPDIR)/gimptoolgui.Po ./$(DEPDIR)/gimptoolgyroscope.Po \ + ./$(DEPDIR)/gimptoolhandlegrid.Po ./$(DEPDIR)/gimptoolline.Po \ + ./$(DEPDIR)/gimptoolpath.Po ./$(DEPDIR)/gimptoolpolygon.Po \ + ./$(DEPDIR)/gimptoolrectangle.Po \ + ./$(DEPDIR)/gimptoolrotategrid.Po \ + ./$(DEPDIR)/gimptoolsheargrid.Po \ + ./$(DEPDIR)/gimptooltransform3dgrid.Po \ + ./$(DEPDIR)/gimptooltransformgrid.Po \ + ./$(DEPDIR)/gimptoolwidget.Po \ + ./$(DEPDIR)/gimptoolwidgetgroup.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 = $(libappdisplay_a_SOURCES) +DIST_SOURCES = $(libappdisplay_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 +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@ +@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c" +@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++" +@PLATFORM_OSX_TRUE@xnone = "-xnone" +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Display\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappdisplay.a +libappdisplay_a_sources = \ + display-enums.h \ + display-types.h \ + gimpcanvas.c \ + gimpcanvas.h \ + gimpcanvas-style.c \ + gimpcanvas-style.h \ + gimpcanvasarc.c \ + gimpcanvasarc.h \ + gimpcanvasboundary.c \ + gimpcanvasboundary.h \ + gimpcanvasbufferpreview.c \ + gimpcanvasbufferpreview.h \ + gimpcanvascanvasboundary.c \ + gimpcanvascanvasboundary.h \ + gimpcanvascorner.c \ + gimpcanvascorner.h \ + gimpcanvascursor.c \ + gimpcanvascursor.h \ + gimpcanvasgrid.c \ + gimpcanvasgrid.h \ + gimpcanvasgroup.c \ + gimpcanvasgroup.h \ + gimpcanvasguide.c \ + gimpcanvasguide.h \ + gimpcanvashandle.c \ + gimpcanvashandle.h \ + gimpcanvasitem.c \ + gimpcanvasitem.h \ + gimpcanvasitem-utils.c \ + gimpcanvasitem-utils.h \ + gimpcanvaslayerboundary.c \ + gimpcanvaslayerboundary.h \ + gimpcanvaslimit.c \ + gimpcanvaslimit.h \ + gimpcanvasline.c \ + gimpcanvasline.h \ + gimpcanvaspassepartout.c \ + gimpcanvaspassepartout.h \ + gimpcanvaspath.c \ + gimpcanvaspath.h \ + gimpcanvaspen.c \ + gimpcanvaspen.h \ + gimpcanvaspolygon.c \ + gimpcanvaspolygon.h \ + gimpcanvasprogress.c \ + gimpcanvasprogress.h \ + gimpcanvasproxygroup.c \ + gimpcanvasproxygroup.h \ + gimpcanvasrectangle.c \ + gimpcanvasrectangle.h \ + gimpcanvasrectangleguides.c \ + gimpcanvasrectangleguides.h \ + gimpcanvassamplepoint.c \ + gimpcanvassamplepoint.h \ + gimpcanvastextcursor.c \ + gimpcanvastextcursor.h \ + gimpcanvastransformguides.c \ + gimpcanvastransformguides.h \ + gimpcanvastransformpreview.c \ + gimpcanvastransformpreview.h \ + gimpcursorview.c \ + gimpcursorview.h \ + gimpdisplay.c \ + gimpdisplay.h \ + gimpdisplay-foreach.c \ + gimpdisplay-foreach.h \ + gimpdisplay-handlers.c \ + gimpdisplay-handlers.h \ + gimpdisplayshell.c \ + gimpdisplayshell.h \ + gimpdisplayshell-actions.c \ + gimpdisplayshell-actions.h \ + gimpdisplayshell-appearance.c \ + gimpdisplayshell-appearance.h \ + gimpdisplayshell-autoscroll.c \ + gimpdisplayshell-autoscroll.h \ + gimpdisplayshell-callbacks.c \ + gimpdisplayshell-callbacks.h \ + gimpdisplayshell-close.c \ + gimpdisplayshell-close.h \ + gimpdisplayshell-cursor.c \ + gimpdisplayshell-cursor.h \ + gimpdisplayshell-dnd.c \ + gimpdisplayshell-dnd.h \ + gimpdisplayshell-draw.c \ + gimpdisplayshell-draw.h \ + gimpdisplayshell-expose.c \ + gimpdisplayshell-expose.h \ + gimpdisplayshell-grab.c \ + gimpdisplayshell-grab.h \ + gimpdisplayshell-handlers.c \ + gimpdisplayshell-handlers.h \ + gimpdisplayshell-filter.c \ + gimpdisplayshell-filter.h \ + gimpdisplayshell-filter-dialog.c \ + gimpdisplayshell-filter-dialog.h \ + gimpdisplayshell-layer-select.c \ + gimpdisplayshell-layer-select.h \ + gimpdisplayshell-icon.c \ + gimpdisplayshell-icon.h \ + gimpdisplayshell-items.c \ + gimpdisplayshell-items.h \ + gimpdisplayshell-profile.c \ + gimpdisplayshell-profile.h \ + gimpdisplayshell-progress.c \ + gimpdisplayshell-progress.h \ + gimpdisplayshell-render.c \ + gimpdisplayshell-render.h \ + gimpdisplayshell-rotate.c \ + gimpdisplayshell-rotate.h \ + gimpdisplayshell-rotate-dialog.c \ + gimpdisplayshell-rotate-dialog.h \ + gimpdisplayshell-rulers.c \ + gimpdisplayshell-rulers.h \ + gimpdisplayshell-scale.c \ + gimpdisplayshell-scale.h \ + gimpdisplayshell-scale-dialog.c \ + gimpdisplayshell-scale-dialog.h \ + gimpdisplayshell-scroll.c \ + gimpdisplayshell-scroll.h \ + gimpdisplayshell-scrollbars.c \ + gimpdisplayshell-scrollbars.h \ + gimpdisplayshell-selection.c \ + gimpdisplayshell-selection.h \ + gimpdisplayshell-title.c \ + gimpdisplayshell-title.h \ + gimpdisplayshell-tool-events.c \ + gimpdisplayshell-tool-events.h \ + gimpdisplayshell-transform.c \ + gimpdisplayshell-transform.h \ + gimpdisplayshell-utils.c \ + gimpdisplayshell-utils.h \ + gimpdisplayxfer.c \ + gimpdisplayxfer.h \ + gimpimagewindow.c \ + gimpimagewindow.h \ + gimpmotionbuffer.c \ + gimpmotionbuffer.h \ + gimpmultiwindowstrategy.c \ + gimpmultiwindowstrategy.h \ + gimpnavigationeditor.c \ + gimpnavigationeditor.h \ + gimpscalecombobox.c \ + gimpscalecombobox.h \ + gimpsinglewindowstrategy.c \ + gimpsinglewindowstrategy.h \ + gimpstatusbar.c \ + gimpstatusbar.h \ + gimptooldialog.c \ + gimptooldialog.h \ + gimptoolgui.c \ + gimptoolgui.h \ + gimptoolcompass.c \ + gimptoolcompass.h \ + gimptoolfocus.h \ + gimptoolfocus.c \ + gimptoolgyroscope.c \ + gimptoolgyroscope.h \ + gimptoolhandlegrid.c \ + gimptoolhandlegrid.h \ + gimptoolline.c \ + gimptoolline.h \ + gimptoolpath.c \ + gimptoolpath.h \ + gimptoolpolygon.c \ + gimptoolpolygon.h \ + gimptoolrectangle.c \ + gimptoolrectangle.h \ + gimptoolrotategrid.c \ + gimptoolrotategrid.h \ + gimptoolsheargrid.c \ + gimptoolsheargrid.h \ + gimptooltransform3dgrid.c \ + gimptooltransform3dgrid.h \ + gimptooltransformgrid.c \ + gimptooltransformgrid.h \ + gimptoolwidget.c \ + gimptoolwidget.h \ + gimptoolwidgetgroup.c \ + gimptoolwidgetgroup.h + +libappdisplay_a_built_sources = display-enums.c +libappdisplay_a_SOURCES = \ + $(libappdisplay_a_built_sources) \ + $(libappdisplay_a_sources) + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-dec +CLEANFILES = $(gen_sources) +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 app/display/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/display/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) + +libappdisplay.a: $(libappdisplay_a_OBJECTS) $(libappdisplay_a_DEPENDENCIES) $(EXTRA_libappdisplay_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappdisplay.a + $(AM_V_AR)$(libappdisplay_a_AR) libappdisplay.a $(libappdisplay_a_OBJECTS) $(libappdisplay_a_LIBADD) + $(AM_V_at)$(RANLIB) libappdisplay.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display-enums.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvas-style.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvas.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasarc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasboundary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasbufferpreview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvascanvasboundary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvascorner.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvascursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasgrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasgroup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasguide.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvashandle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasitem-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasitem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaslayerboundary.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaslimit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaspassepartout.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaspath.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaspen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvaspolygon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasprogress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasproxygroup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasrectangle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvasrectangleguides.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvassamplepoint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvastextcursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvastransformguides.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcanvastransformpreview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcursorview.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplay-foreach.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplay-handlers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplay.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-actions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-appearance.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-autoscroll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-callbacks.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-close.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-cursor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-dnd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-draw.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-expose.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-filter-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-filter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-grab.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-handlers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-icon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-items.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-layer-select.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-profile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-progress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-render.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-rotate-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-rotate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-rulers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-scale-dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-scale.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-scroll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-scrollbars.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-selection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-title.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-tool-events.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-transform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayshell.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplayxfer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagewindow.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmotionbuffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmultiwindowstrategy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpnavigationeditor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpscalecombobox.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsinglewindowstrategy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpstatusbar.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolcompass.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptooldialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolfocus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolgui.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolgyroscope.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolhandlegrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolpath.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolpolygon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolrectangle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolrotategrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolsheargrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptooltransform3dgrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptooltransformgrid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolwidget.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptoolwidgetgroup.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: + -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 clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/display-enums.Po + -rm -f ./$(DEPDIR)/gimpcanvas-style.Po + -rm -f ./$(DEPDIR)/gimpcanvas.Po + -rm -f ./$(DEPDIR)/gimpcanvasarc.Po + -rm -f ./$(DEPDIR)/gimpcanvasboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvasbufferpreview.Po + -rm -f ./$(DEPDIR)/gimpcanvascanvasboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvascorner.Po + -rm -f ./$(DEPDIR)/gimpcanvascursor.Po + -rm -f ./$(DEPDIR)/gimpcanvasgrid.Po + -rm -f ./$(DEPDIR)/gimpcanvasgroup.Po + -rm -f ./$(DEPDIR)/gimpcanvasguide.Po + -rm -f ./$(DEPDIR)/gimpcanvashandle.Po + -rm -f ./$(DEPDIR)/gimpcanvasitem-utils.Po + -rm -f ./$(DEPDIR)/gimpcanvasitem.Po + -rm -f ./$(DEPDIR)/gimpcanvaslayerboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvaslimit.Po + -rm -f ./$(DEPDIR)/gimpcanvasline.Po + -rm -f ./$(DEPDIR)/gimpcanvaspassepartout.Po + -rm -f ./$(DEPDIR)/gimpcanvaspath.Po + -rm -f ./$(DEPDIR)/gimpcanvaspen.Po + -rm -f ./$(DEPDIR)/gimpcanvaspolygon.Po + -rm -f ./$(DEPDIR)/gimpcanvasprogress.Po + -rm -f ./$(DEPDIR)/gimpcanvasproxygroup.Po + -rm -f ./$(DEPDIR)/gimpcanvasrectangle.Po + -rm -f ./$(DEPDIR)/gimpcanvasrectangleguides.Po + -rm -f ./$(DEPDIR)/gimpcanvassamplepoint.Po + -rm -f ./$(DEPDIR)/gimpcanvastextcursor.Po + -rm -f ./$(DEPDIR)/gimpcanvastransformguides.Po + -rm -f ./$(DEPDIR)/gimpcanvastransformpreview.Po + -rm -f ./$(DEPDIR)/gimpcursorview.Po + -rm -f ./$(DEPDIR)/gimpdisplay-foreach.Po + -rm -f ./$(DEPDIR)/gimpdisplay-handlers.Po + -rm -f ./$(DEPDIR)/gimpdisplay.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-actions.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-appearance.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-autoscroll.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-callbacks.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-close.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-cursor.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-dnd.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-draw.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-expose.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-filter-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-filter.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-grab.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-handlers.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-icon.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-items.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-layer-select.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-profile.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-progress.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-render.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rotate-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rotate.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rulers.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scale-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scale.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scroll.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scrollbars.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-selection.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-title.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-tool-events.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-transform.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-utils.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell.Po + -rm -f ./$(DEPDIR)/gimpdisplayxfer.Po + -rm -f ./$(DEPDIR)/gimpimagewindow.Po + -rm -f ./$(DEPDIR)/gimpmotionbuffer.Po + -rm -f ./$(DEPDIR)/gimpmultiwindowstrategy.Po + -rm -f ./$(DEPDIR)/gimpnavigationeditor.Po + -rm -f ./$(DEPDIR)/gimpscalecombobox.Po + -rm -f ./$(DEPDIR)/gimpsinglewindowstrategy.Po + -rm -f ./$(DEPDIR)/gimpstatusbar.Po + -rm -f ./$(DEPDIR)/gimptoolcompass.Po + -rm -f ./$(DEPDIR)/gimptooldialog.Po + -rm -f ./$(DEPDIR)/gimptoolfocus.Po + -rm -f ./$(DEPDIR)/gimptoolgui.Po + -rm -f ./$(DEPDIR)/gimptoolgyroscope.Po + -rm -f ./$(DEPDIR)/gimptoolhandlegrid.Po + -rm -f ./$(DEPDIR)/gimptoolline.Po + -rm -f ./$(DEPDIR)/gimptoolpath.Po + -rm -f ./$(DEPDIR)/gimptoolpolygon.Po + -rm -f ./$(DEPDIR)/gimptoolrectangle.Po + -rm -f ./$(DEPDIR)/gimptoolrotategrid.Po + -rm -f ./$(DEPDIR)/gimptoolsheargrid.Po + -rm -f ./$(DEPDIR)/gimptooltransform3dgrid.Po + -rm -f ./$(DEPDIR)/gimptooltransformgrid.Po + -rm -f ./$(DEPDIR)/gimptoolwidget.Po + -rm -f ./$(DEPDIR)/gimptoolwidgetgroup.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)/display-enums.Po + -rm -f ./$(DEPDIR)/gimpcanvas-style.Po + -rm -f ./$(DEPDIR)/gimpcanvas.Po + -rm -f ./$(DEPDIR)/gimpcanvasarc.Po + -rm -f ./$(DEPDIR)/gimpcanvasboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvasbufferpreview.Po + -rm -f ./$(DEPDIR)/gimpcanvascanvasboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvascorner.Po + -rm -f ./$(DEPDIR)/gimpcanvascursor.Po + -rm -f ./$(DEPDIR)/gimpcanvasgrid.Po + -rm -f ./$(DEPDIR)/gimpcanvasgroup.Po + -rm -f ./$(DEPDIR)/gimpcanvasguide.Po + -rm -f ./$(DEPDIR)/gimpcanvashandle.Po + -rm -f ./$(DEPDIR)/gimpcanvasitem-utils.Po + -rm -f ./$(DEPDIR)/gimpcanvasitem.Po + -rm -f ./$(DEPDIR)/gimpcanvaslayerboundary.Po + -rm -f ./$(DEPDIR)/gimpcanvaslimit.Po + -rm -f ./$(DEPDIR)/gimpcanvasline.Po + -rm -f ./$(DEPDIR)/gimpcanvaspassepartout.Po + -rm -f ./$(DEPDIR)/gimpcanvaspath.Po + -rm -f ./$(DEPDIR)/gimpcanvaspen.Po + -rm -f ./$(DEPDIR)/gimpcanvaspolygon.Po + -rm -f ./$(DEPDIR)/gimpcanvasprogress.Po + -rm -f ./$(DEPDIR)/gimpcanvasproxygroup.Po + -rm -f ./$(DEPDIR)/gimpcanvasrectangle.Po + -rm -f ./$(DEPDIR)/gimpcanvasrectangleguides.Po + -rm -f ./$(DEPDIR)/gimpcanvassamplepoint.Po + -rm -f ./$(DEPDIR)/gimpcanvastextcursor.Po + -rm -f ./$(DEPDIR)/gimpcanvastransformguides.Po + -rm -f ./$(DEPDIR)/gimpcanvastransformpreview.Po + -rm -f ./$(DEPDIR)/gimpcursorview.Po + -rm -f ./$(DEPDIR)/gimpdisplay-foreach.Po + -rm -f ./$(DEPDIR)/gimpdisplay-handlers.Po + -rm -f ./$(DEPDIR)/gimpdisplay.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-actions.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-appearance.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-autoscroll.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-callbacks.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-close.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-cursor.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-dnd.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-draw.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-expose.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-filter-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-filter.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-grab.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-handlers.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-icon.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-items.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-layer-select.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-profile.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-progress.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-render.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rotate-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rotate.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-rulers.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scale-dialog.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scale.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scroll.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-scrollbars.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-selection.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-title.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-tool-events.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-transform.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell-utils.Po + -rm -f ./$(DEPDIR)/gimpdisplayshell.Po + -rm -f ./$(DEPDIR)/gimpdisplayxfer.Po + -rm -f ./$(DEPDIR)/gimpimagewindow.Po + -rm -f ./$(DEPDIR)/gimpmotionbuffer.Po + -rm -f ./$(DEPDIR)/gimpmultiwindowstrategy.Po + -rm -f ./$(DEPDIR)/gimpnavigationeditor.Po + -rm -f ./$(DEPDIR)/gimpscalecombobox.Po + -rm -f ./$(DEPDIR)/gimpsinglewindowstrategy.Po + -rm -f ./$(DEPDIR)/gimpstatusbar.Po + -rm -f ./$(DEPDIR)/gimptoolcompass.Po + -rm -f ./$(DEPDIR)/gimptooldialog.Po + -rm -f ./$(DEPDIR)/gimptoolfocus.Po + -rm -f ./$(DEPDIR)/gimptoolgui.Po + -rm -f ./$(DEPDIR)/gimptoolgyroscope.Po + -rm -f ./$(DEPDIR)/gimptoolhandlegrid.Po + -rm -f ./$(DEPDIR)/gimptoolline.Po + -rm -f ./$(DEPDIR)/gimptoolpath.Po + -rm -f ./$(DEPDIR)/gimptoolpolygon.Po + -rm -f ./$(DEPDIR)/gimptoolrectangle.Po + -rm -f ./$(DEPDIR)/gimptoolrotategrid.Po + -rm -f ./$(DEPDIR)/gimptoolsheargrid.Po + -rm -f ./$(DEPDIR)/gimptooltransform3dgrid.Po + -rm -f ./$(DEPDIR)/gimptooltransformgrid.Po + -rm -f ./$(DEPDIR)/gimptoolwidget.Po + -rm -f ./$(DEPDIR)/gimptoolwidgetgroup.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 + + +xgen-dec: $(srcdir)/display-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"display-enums.h\"\n#include\"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/display-enums.c: xgen-dec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/display/display-enums.c b/app/display/display-enums.c new file mode 100644 index 0000000..438fc3e --- /dev/null +++ b/app/display/display-enums.c @@ -0,0 +1,592 @@ + +/* Generated data (by gimp-mkenums) */ + +#include "config.h" +#include +#include "libgimpbase/gimpbase.h" +#include "display-enums.h" +#include"gimp-intl.h" + +/* enumerations from "display-enums.h" */ +GType +gimp_button_press_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_BUTTON_PRESS_NORMAL, "GIMP_BUTTON_PRESS_NORMAL", "normal" }, + { GIMP_BUTTON_PRESS_DOUBLE, "GIMP_BUTTON_PRESS_DOUBLE", "double" }, + { GIMP_BUTTON_PRESS_TRIPLE, "GIMP_BUTTON_PRESS_TRIPLE", "triple" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_BUTTON_PRESS_NORMAL, "GIMP_BUTTON_PRESS_NORMAL", NULL }, + { GIMP_BUTTON_PRESS_DOUBLE, "GIMP_BUTTON_PRESS_DOUBLE", NULL }, + { GIMP_BUTTON_PRESS_TRIPLE, "GIMP_BUTTON_PRESS_TRIPLE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpButtonPressType", values); + gimp_type_set_translation_context (type, "button-press-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_button_release_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_BUTTON_RELEASE_NORMAL, "GIMP_BUTTON_RELEASE_NORMAL", "normal" }, + { GIMP_BUTTON_RELEASE_CANCEL, "GIMP_BUTTON_RELEASE_CANCEL", "cancel" }, + { GIMP_BUTTON_RELEASE_CLICK, "GIMP_BUTTON_RELEASE_CLICK", "click" }, + { GIMP_BUTTON_RELEASE_NO_MOTION, "GIMP_BUTTON_RELEASE_NO_MOTION", "no-motion" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_BUTTON_RELEASE_NORMAL, "GIMP_BUTTON_RELEASE_NORMAL", NULL }, + { GIMP_BUTTON_RELEASE_CANCEL, "GIMP_BUTTON_RELEASE_CANCEL", NULL }, + { GIMP_BUTTON_RELEASE_CLICK, "GIMP_BUTTON_RELEASE_CLICK", NULL }, + { GIMP_BUTTON_RELEASE_NO_MOTION, "GIMP_BUTTON_RELEASE_NO_MOTION", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpButtonReleaseType", values); + gimp_type_set_translation_context (type, "button-release-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_compass_orientation_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_COMPASS_ORIENTATION_AUTO, "GIMP_COMPASS_ORIENTATION_AUTO", "auto" }, + { GIMP_COMPASS_ORIENTATION_HORIZONTAL, "GIMP_COMPASS_ORIENTATION_HORIZONTAL", "horizontal" }, + { GIMP_COMPASS_ORIENTATION_VERTICAL, "GIMP_COMPASS_ORIENTATION_VERTICAL", "vertical" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_COMPASS_ORIENTATION_AUTO, NC_("compass-orientation", "Auto"), NULL }, + { GIMP_COMPASS_ORIENTATION_HORIZONTAL, NC_("compass-orientation", "Horizontal"), NULL }, + { GIMP_COMPASS_ORIENTATION_VERTICAL, NC_("compass-orientation", "Vertical"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCompassOrientation", values); + gimp_type_set_translation_context (type, "compass-orientation"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_cursor_precision_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CURSOR_PRECISION_PIXEL_CENTER, "GIMP_CURSOR_PRECISION_PIXEL_CENTER", "pixel-center" }, + { GIMP_CURSOR_PRECISION_PIXEL_BORDER, "GIMP_CURSOR_PRECISION_PIXEL_BORDER", "pixel-border" }, + { GIMP_CURSOR_PRECISION_SUBPIXEL, "GIMP_CURSOR_PRECISION_SUBPIXEL", "subpixel" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CURSOR_PRECISION_PIXEL_CENTER, "GIMP_CURSOR_PRECISION_PIXEL_CENTER", NULL }, + { GIMP_CURSOR_PRECISION_PIXEL_BORDER, "GIMP_CURSOR_PRECISION_PIXEL_BORDER", NULL }, + { GIMP_CURSOR_PRECISION_SUBPIXEL, "GIMP_CURSOR_PRECISION_SUBPIXEL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCursorPrecision", values); + gimp_type_set_translation_context (type, "cursor-precision"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_guides_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_GUIDES_NONE, "GIMP_GUIDES_NONE", "none" }, + { GIMP_GUIDES_CENTER_LINES, "GIMP_GUIDES_CENTER_LINES", "center-lines" }, + { GIMP_GUIDES_THIRDS, "GIMP_GUIDES_THIRDS", "thirds" }, + { GIMP_GUIDES_FIFTHS, "GIMP_GUIDES_FIFTHS", "fifths" }, + { GIMP_GUIDES_GOLDEN, "GIMP_GUIDES_GOLDEN", "golden" }, + { GIMP_GUIDES_DIAGONALS, "GIMP_GUIDES_DIAGONALS", "diagonals" }, + { GIMP_GUIDES_N_LINES, "GIMP_GUIDES_N_LINES", "n-lines" }, + { GIMP_GUIDES_SPACING, "GIMP_GUIDES_SPACING", "spacing" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_GUIDES_NONE, NC_("guides-type", "No guides"), NULL }, + { GIMP_GUIDES_CENTER_LINES, NC_("guides-type", "Center lines"), NULL }, + { GIMP_GUIDES_THIRDS, NC_("guides-type", "Rule of thirds"), NULL }, + { GIMP_GUIDES_FIFTHS, NC_("guides-type", "Rule of fifths"), NULL }, + { GIMP_GUIDES_GOLDEN, NC_("guides-type", "Golden sections"), NULL }, + { GIMP_GUIDES_DIAGONALS, NC_("guides-type", "Diagonal lines"), NULL }, + { GIMP_GUIDES_N_LINES, NC_("guides-type", "Number of lines"), NULL }, + { GIMP_GUIDES_SPACING, NC_("guides-type", "Line spacing"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpGuidesType", values); + gimp_type_set_translation_context (type, "guides-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_handle_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDLE_SQUARE, "GIMP_HANDLE_SQUARE", "square" }, + { GIMP_HANDLE_DASHED_SQUARE, "GIMP_HANDLE_DASHED_SQUARE", "dashed-square" }, + { GIMP_HANDLE_FILLED_SQUARE, "GIMP_HANDLE_FILLED_SQUARE", "filled-square" }, + { GIMP_HANDLE_CIRCLE, "GIMP_HANDLE_CIRCLE", "circle" }, + { GIMP_HANDLE_DASHED_CIRCLE, "GIMP_HANDLE_DASHED_CIRCLE", "dashed-circle" }, + { GIMP_HANDLE_FILLED_CIRCLE, "GIMP_HANDLE_FILLED_CIRCLE", "filled-circle" }, + { GIMP_HANDLE_DIAMOND, "GIMP_HANDLE_DIAMOND", "diamond" }, + { GIMP_HANDLE_DASHED_DIAMOND, "GIMP_HANDLE_DASHED_DIAMOND", "dashed-diamond" }, + { GIMP_HANDLE_FILLED_DIAMOND, "GIMP_HANDLE_FILLED_DIAMOND", "filled-diamond" }, + { GIMP_HANDLE_CROSS, "GIMP_HANDLE_CROSS", "cross" }, + { GIMP_HANDLE_CROSSHAIR, "GIMP_HANDLE_CROSSHAIR", "crosshair" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDLE_SQUARE, "GIMP_HANDLE_SQUARE", NULL }, + { GIMP_HANDLE_DASHED_SQUARE, "GIMP_HANDLE_DASHED_SQUARE", NULL }, + { GIMP_HANDLE_FILLED_SQUARE, "GIMP_HANDLE_FILLED_SQUARE", NULL }, + { GIMP_HANDLE_CIRCLE, "GIMP_HANDLE_CIRCLE", NULL }, + { GIMP_HANDLE_DASHED_CIRCLE, "GIMP_HANDLE_DASHED_CIRCLE", NULL }, + { GIMP_HANDLE_FILLED_CIRCLE, "GIMP_HANDLE_FILLED_CIRCLE", NULL }, + { GIMP_HANDLE_DIAMOND, "GIMP_HANDLE_DIAMOND", NULL }, + { GIMP_HANDLE_DASHED_DIAMOND, "GIMP_HANDLE_DASHED_DIAMOND", NULL }, + { GIMP_HANDLE_FILLED_DIAMOND, "GIMP_HANDLE_FILLED_DIAMOND", NULL }, + { GIMP_HANDLE_CROSS, "GIMP_HANDLE_CROSS", NULL }, + { GIMP_HANDLE_CROSSHAIR, "GIMP_HANDLE_CROSSHAIR", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpHandleType", values); + gimp_type_set_translation_context (type, "handle-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_handle_anchor_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDLE_ANCHOR_CENTER, "GIMP_HANDLE_ANCHOR_CENTER", "center" }, + { GIMP_HANDLE_ANCHOR_NORTH, "GIMP_HANDLE_ANCHOR_NORTH", "north" }, + { GIMP_HANDLE_ANCHOR_NORTH_WEST, "GIMP_HANDLE_ANCHOR_NORTH_WEST", "north-west" }, + { GIMP_HANDLE_ANCHOR_NORTH_EAST, "GIMP_HANDLE_ANCHOR_NORTH_EAST", "north-east" }, + { GIMP_HANDLE_ANCHOR_SOUTH, "GIMP_HANDLE_ANCHOR_SOUTH", "south" }, + { GIMP_HANDLE_ANCHOR_SOUTH_WEST, "GIMP_HANDLE_ANCHOR_SOUTH_WEST", "south-west" }, + { GIMP_HANDLE_ANCHOR_SOUTH_EAST, "GIMP_HANDLE_ANCHOR_SOUTH_EAST", "south-east" }, + { GIMP_HANDLE_ANCHOR_WEST, "GIMP_HANDLE_ANCHOR_WEST", "west" }, + { GIMP_HANDLE_ANCHOR_EAST, "GIMP_HANDLE_ANCHOR_EAST", "east" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDLE_ANCHOR_CENTER, "GIMP_HANDLE_ANCHOR_CENTER", NULL }, + { GIMP_HANDLE_ANCHOR_NORTH, "GIMP_HANDLE_ANCHOR_NORTH", NULL }, + { GIMP_HANDLE_ANCHOR_NORTH_WEST, "GIMP_HANDLE_ANCHOR_NORTH_WEST", NULL }, + { GIMP_HANDLE_ANCHOR_NORTH_EAST, "GIMP_HANDLE_ANCHOR_NORTH_EAST", NULL }, + { GIMP_HANDLE_ANCHOR_SOUTH, "GIMP_HANDLE_ANCHOR_SOUTH", NULL }, + { GIMP_HANDLE_ANCHOR_SOUTH_WEST, "GIMP_HANDLE_ANCHOR_SOUTH_WEST", NULL }, + { GIMP_HANDLE_ANCHOR_SOUTH_EAST, "GIMP_HANDLE_ANCHOR_SOUTH_EAST", NULL }, + { GIMP_HANDLE_ANCHOR_WEST, "GIMP_HANDLE_ANCHOR_WEST", NULL }, + { GIMP_HANDLE_ANCHOR_EAST, "GIMP_HANDLE_ANCHOR_EAST", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpHandleAnchor", values); + gimp_type_set_translation_context (type, "handle-anchor"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_limit_type_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_LIMIT_CIRCLE, "GIMP_LIMIT_CIRCLE", "circle" }, + { GIMP_LIMIT_SQUARE, "GIMP_LIMIT_SQUARE", "square" }, + { GIMP_LIMIT_DIAMOND, "GIMP_LIMIT_DIAMOND", "diamond" }, + { GIMP_LIMIT_HORIZONTAL, "GIMP_LIMIT_HORIZONTAL", "horizontal" }, + { GIMP_LIMIT_VERTICAL, "GIMP_LIMIT_VERTICAL", "vertical" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_LIMIT_CIRCLE, "GIMP_LIMIT_CIRCLE", NULL }, + { GIMP_LIMIT_SQUARE, "GIMP_LIMIT_SQUARE", NULL }, + { GIMP_LIMIT_DIAMOND, "GIMP_LIMIT_DIAMOND", NULL }, + { GIMP_LIMIT_HORIZONTAL, "GIMP_LIMIT_HORIZONTAL", NULL }, + { GIMP_LIMIT_VERTICAL, "GIMP_LIMIT_VERTICAL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpLimitType", values); + gimp_type_set_translation_context (type, "limit-type"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_path_style_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_PATH_STYLE_DEFAULT, "GIMP_PATH_STYLE_DEFAULT", "default" }, + { GIMP_PATH_STYLE_VECTORS, "GIMP_PATH_STYLE_VECTORS", "vectors" }, + { GIMP_PATH_STYLE_OUTLINE, "GIMP_PATH_STYLE_OUTLINE", "outline" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_PATH_STYLE_DEFAULT, "GIMP_PATH_STYLE_DEFAULT", NULL }, + { GIMP_PATH_STYLE_VECTORS, "GIMP_PATH_STYLE_VECTORS", NULL }, + { GIMP_PATH_STYLE_OUTLINE, "GIMP_PATH_STYLE_OUTLINE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpPathStyle", values); + gimp_type_set_translation_context (type, "path-style"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_rectangle_constraint_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_RECTANGLE_CONSTRAIN_NONE, "GIMP_RECTANGLE_CONSTRAIN_NONE", "none" }, + { GIMP_RECTANGLE_CONSTRAIN_IMAGE, "GIMP_RECTANGLE_CONSTRAIN_IMAGE", "image" }, + { GIMP_RECTANGLE_CONSTRAIN_DRAWABLE, "GIMP_RECTANGLE_CONSTRAIN_DRAWABLE", "drawable" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_RECTANGLE_CONSTRAIN_NONE, "GIMP_RECTANGLE_CONSTRAIN_NONE", NULL }, + { GIMP_RECTANGLE_CONSTRAIN_IMAGE, "GIMP_RECTANGLE_CONSTRAIN_IMAGE", NULL }, + { GIMP_RECTANGLE_CONSTRAIN_DRAWABLE, "GIMP_RECTANGLE_CONSTRAIN_DRAWABLE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpRectangleConstraint", values); + gimp_type_set_translation_context (type, "rectangle-constraint"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_rectangle_fixed_rule_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_RECTANGLE_FIXED_ASPECT, "GIMP_RECTANGLE_FIXED_ASPECT", "aspect" }, + { GIMP_RECTANGLE_FIXED_WIDTH, "GIMP_RECTANGLE_FIXED_WIDTH", "width" }, + { GIMP_RECTANGLE_FIXED_HEIGHT, "GIMP_RECTANGLE_FIXED_HEIGHT", "height" }, + { GIMP_RECTANGLE_FIXED_SIZE, "GIMP_RECTANGLE_FIXED_SIZE", "size" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_RECTANGLE_FIXED_ASPECT, NC_("rectangle-fixed-rule", "Aspect ratio"), NULL }, + { GIMP_RECTANGLE_FIXED_WIDTH, NC_("rectangle-fixed-rule", "Width"), NULL }, + { GIMP_RECTANGLE_FIXED_HEIGHT, NC_("rectangle-fixed-rule", "Height"), NULL }, + { GIMP_RECTANGLE_FIXED_SIZE, NC_("rectangle-fixed-rule", "Size"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpRectangleFixedRule", values); + gimp_type_set_translation_context (type, "rectangle-fixed-rule"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_rectangle_precision_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_RECTANGLE_PRECISION_INT, "GIMP_RECTANGLE_PRECISION_INT", "int" }, + { GIMP_RECTANGLE_PRECISION_DOUBLE, "GIMP_RECTANGLE_PRECISION_DOUBLE", "double" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_RECTANGLE_PRECISION_INT, "GIMP_RECTANGLE_PRECISION_INT", NULL }, + { GIMP_RECTANGLE_PRECISION_DOUBLE, "GIMP_RECTANGLE_PRECISION_DOUBLE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpRectanglePrecision", values); + gimp_type_set_translation_context (type, "rectangle-precision"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_transform_3d_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_TRANSFORM_3D_MODE_CAMERA, "GIMP_TRANSFORM_3D_MODE_CAMERA", "camera" }, + { GIMP_TRANSFORM_3D_MODE_MOVE, "GIMP_TRANSFORM_3D_MODE_MOVE", "move" }, + { GIMP_TRANSFORM_3D_MODE_ROTATE, "GIMP_TRANSFORM_3D_MODE_ROTATE", "rotate" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_TRANSFORM_3D_MODE_CAMERA, "GIMP_TRANSFORM_3D_MODE_CAMERA", NULL }, + { GIMP_TRANSFORM_3D_MODE_MOVE, "GIMP_TRANSFORM_3D_MODE_MOVE", NULL }, + { GIMP_TRANSFORM_3D_MODE_ROTATE, "GIMP_TRANSFORM_3D_MODE_ROTATE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpTransform3DMode", values); + gimp_type_set_translation_context (type, "transform3-dmode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_transform_function_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_TRANSFORM_FUNCTION_NONE, "GIMP_TRANSFORM_FUNCTION_NONE", "none" }, + { GIMP_TRANSFORM_FUNCTION_MOVE, "GIMP_TRANSFORM_FUNCTION_MOVE", "move" }, + { GIMP_TRANSFORM_FUNCTION_SCALE, "GIMP_TRANSFORM_FUNCTION_SCALE", "scale" }, + { GIMP_TRANSFORM_FUNCTION_ROTATE, "GIMP_TRANSFORM_FUNCTION_ROTATE", "rotate" }, + { GIMP_TRANSFORM_FUNCTION_SHEAR, "GIMP_TRANSFORM_FUNCTION_SHEAR", "shear" }, + { GIMP_TRANSFORM_FUNCTION_PERSPECTIVE, "GIMP_TRANSFORM_FUNCTION_PERSPECTIVE", "perspective" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_TRANSFORM_FUNCTION_NONE, "GIMP_TRANSFORM_FUNCTION_NONE", NULL }, + { GIMP_TRANSFORM_FUNCTION_MOVE, "GIMP_TRANSFORM_FUNCTION_MOVE", NULL }, + { GIMP_TRANSFORM_FUNCTION_SCALE, "GIMP_TRANSFORM_FUNCTION_SCALE", NULL }, + { GIMP_TRANSFORM_FUNCTION_ROTATE, "GIMP_TRANSFORM_FUNCTION_ROTATE", NULL }, + { GIMP_TRANSFORM_FUNCTION_SHEAR, "GIMP_TRANSFORM_FUNCTION_SHEAR", NULL }, + { GIMP_TRANSFORM_FUNCTION_PERSPECTIVE, "GIMP_TRANSFORM_FUNCTION_PERSPECTIVE", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpTransformFunction", values); + gimp_type_set_translation_context (type, "transform-function"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_transform_handle_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDLE_MODE_ADD_TRANSFORM, "GIMP_HANDLE_MODE_ADD_TRANSFORM", "add-transform" }, + { GIMP_HANDLE_MODE_MOVE, "GIMP_HANDLE_MODE_MOVE", "move" }, + { GIMP_HANDLE_MODE_REMOVE, "GIMP_HANDLE_MODE_REMOVE", "remove" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDLE_MODE_ADD_TRANSFORM, NC_("transform-handle-mode", "Add / Transform"), NULL }, + { GIMP_HANDLE_MODE_MOVE, NC_("transform-handle-mode", "Move"), NULL }, + { GIMP_HANDLE_MODE_REMOVE, NC_("transform-handle-mode", "Remove"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpTransformHandleMode", values); + gimp_type_set_translation_context (type, "transform-handle-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_vector_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_VECTOR_MODE_DESIGN, "GIMP_VECTOR_MODE_DESIGN", "design" }, + { GIMP_VECTOR_MODE_EDIT, "GIMP_VECTOR_MODE_EDIT", "edit" }, + { GIMP_VECTOR_MODE_MOVE, "GIMP_VECTOR_MODE_MOVE", "move" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_VECTOR_MODE_DESIGN, NC_("vector-mode", "Design"), NULL }, + { GIMP_VECTOR_MODE_EDIT, NC_("vector-mode", "Edit"), NULL }, + { GIMP_VECTOR_MODE_MOVE, NC_("vector-mode", "Move"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpVectorMode", values); + gimp_type_set_translation_context (type, "vector-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_zoom_focus_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_ZOOM_FOCUS_BEST_GUESS, "GIMP_ZOOM_FOCUS_BEST_GUESS", "best-guess" }, + { GIMP_ZOOM_FOCUS_POINTER, "GIMP_ZOOM_FOCUS_POINTER", "pointer" }, + { GIMP_ZOOM_FOCUS_IMAGE_CENTER, "GIMP_ZOOM_FOCUS_IMAGE_CENTER", "image-center" }, + { GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS, "GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS", "retain-centering-else-best-guess" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_ZOOM_FOCUS_BEST_GUESS, "GIMP_ZOOM_FOCUS_BEST_GUESS", NULL }, + { GIMP_ZOOM_FOCUS_POINTER, "GIMP_ZOOM_FOCUS_POINTER", NULL }, + { GIMP_ZOOM_FOCUS_IMAGE_CENTER, "GIMP_ZOOM_FOCUS_IMAGE_CENTER", NULL }, + { GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS, "GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpZoomFocus", values); + gimp_type_set_translation_context (type, "zoom-focus"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + + +/* Generated data ends here */ + diff --git a/app/display/display-enums.h b/app/display/display-enums.h new file mode 100644 index 0000000..4cc84a8 --- /dev/null +++ b/app/display/display-enums.h @@ -0,0 +1,275 @@ +/* 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 . + */ + +#ifndef __DISPLAY_ENUMS_H__ +#define __DISPLAY_ENUMS_H__ + + +#define GIMP_TYPE_BUTTON_PRESS_TYPE (gimp_button_press_type_get_type ()) + +GType gimp_button_press_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_BUTTON_PRESS_NORMAL, + GIMP_BUTTON_PRESS_DOUBLE, + GIMP_BUTTON_PRESS_TRIPLE +} GimpButtonPressType; + + +#define GIMP_TYPE_BUTTON_RELEASE_TYPE (gimp_button_release_type_get_type ()) + +GType gimp_button_release_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_BUTTON_RELEASE_NORMAL, + GIMP_BUTTON_RELEASE_CANCEL, + GIMP_BUTTON_RELEASE_CLICK, + GIMP_BUTTON_RELEASE_NO_MOTION +} GimpButtonReleaseType; + + +#define GIMP_TYPE_COMPASS_ORIENTATION (gimp_compass_orientation_get_type ()) + +GType gimp_compass_orientation_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_COMPASS_ORIENTATION_AUTO, /*< desc="Auto" >*/ + GIMP_COMPASS_ORIENTATION_HORIZONTAL, /*< desc="Horizontal" >*/ + GIMP_COMPASS_ORIENTATION_VERTICAL /*< desc="Vertical" >*/ +} GimpCompassOrientation; + + +#define GIMP_TYPE_CURSOR_PRECISION (gimp_cursor_precision_get_type ()) + +GType gimp_cursor_precision_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CURSOR_PRECISION_PIXEL_CENTER, + GIMP_CURSOR_PRECISION_PIXEL_BORDER, + GIMP_CURSOR_PRECISION_SUBPIXEL +} GimpCursorPrecision; + + +#define GIMP_TYPE_GUIDES_TYPE (gimp_guides_type_get_type ()) + +GType gimp_guides_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_GUIDES_NONE, /*< desc="No guides" >*/ + GIMP_GUIDES_CENTER_LINES, /*< desc="Center lines" >*/ + GIMP_GUIDES_THIRDS, /*< desc="Rule of thirds" >*/ + GIMP_GUIDES_FIFTHS, /*< desc="Rule of fifths" >*/ + GIMP_GUIDES_GOLDEN, /*< desc="Golden sections" >*/ + GIMP_GUIDES_DIAGONALS, /*< desc="Diagonal lines" >*/ + GIMP_GUIDES_N_LINES, /*< desc="Number of lines" >*/ + GIMP_GUIDES_SPACING /*< desc="Line spacing" >*/ +} GimpGuidesType; + + +#define GIMP_TYPE_HANDLE_TYPE (gimp_handle_type_get_type ()) + +GType gimp_handle_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDLE_SQUARE, + GIMP_HANDLE_DASHED_SQUARE, + GIMP_HANDLE_FILLED_SQUARE, + GIMP_HANDLE_CIRCLE, + GIMP_HANDLE_DASHED_CIRCLE, + GIMP_HANDLE_FILLED_CIRCLE, + GIMP_HANDLE_DIAMOND, + GIMP_HANDLE_DASHED_DIAMOND, + GIMP_HANDLE_FILLED_DIAMOND, + GIMP_HANDLE_CROSS, + GIMP_HANDLE_CROSSHAIR +} GimpHandleType; + + +#define GIMP_TYPE_HANDLE_ANCHOR (gimp_handle_anchor_get_type ()) + +GType gimp_handle_anchor_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDLE_ANCHOR_CENTER, + GIMP_HANDLE_ANCHOR_NORTH, + GIMP_HANDLE_ANCHOR_NORTH_WEST, + GIMP_HANDLE_ANCHOR_NORTH_EAST, + GIMP_HANDLE_ANCHOR_SOUTH, + GIMP_HANDLE_ANCHOR_SOUTH_WEST, + GIMP_HANDLE_ANCHOR_SOUTH_EAST, + GIMP_HANDLE_ANCHOR_WEST, + GIMP_HANDLE_ANCHOR_EAST +} GimpHandleAnchor; + + +#define GIMP_TYPE_LIMIT_TYPE (gimp_limit_type_get_type ()) + +GType gimp_limit_type_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_LIMIT_CIRCLE, + GIMP_LIMIT_SQUARE, + GIMP_LIMIT_DIAMOND, + GIMP_LIMIT_HORIZONTAL, + GIMP_LIMIT_VERTICAL +} GimpLimitType; + + +#define GIMP_TYPE_PATH_STYLE (gimp_path_style_get_type ()) + +GType gimp_path_style_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_PATH_STYLE_DEFAULT, + GIMP_PATH_STYLE_VECTORS, + GIMP_PATH_STYLE_OUTLINE +} GimpPathStyle; + + +#define GIMP_TYPE_RECTANGLE_CONSTRAINT (gimp_rectangle_constraint_get_type ()) + +GType gimp_rectangle_constraint_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_RECTANGLE_CONSTRAIN_NONE, + GIMP_RECTANGLE_CONSTRAIN_IMAGE, + GIMP_RECTANGLE_CONSTRAIN_DRAWABLE +} GimpRectangleConstraint; + + +#define GIMP_TYPE_RECTANGLE_FIXED_RULE (gimp_rectangle_fixed_rule_get_type ()) + +GType gimp_rectangle_fixed_rule_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_RECTANGLE_FIXED_ASPECT, /*< desc="Aspect ratio" >*/ + GIMP_RECTANGLE_FIXED_WIDTH, /*< desc="Width" >*/ + GIMP_RECTANGLE_FIXED_HEIGHT, /*< desc="Height" >*/ + GIMP_RECTANGLE_FIXED_SIZE, /*< desc="Size" >*/ +} GimpRectangleFixedRule; + + +#define GIMP_TYPE_RECTANGLE_PRECISION (gimp_rectangle_precision_get_type ()) + +GType gimp_rectangle_precision_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_RECTANGLE_PRECISION_INT, + GIMP_RECTANGLE_PRECISION_DOUBLE, +} GimpRectanglePrecision; + + +#define GIMP_TYPE_TRANSFORM_3D_MODE (gimp_transform_3d_mode_get_type ()) + +GType gimp_transform_3d_mode_get_type (void) G_GNUC_CONST; + +typedef enum /*< lowercase_name=gimp_transform_3d_mode >*/ +{ + GIMP_TRANSFORM_3D_MODE_CAMERA, + GIMP_TRANSFORM_3D_MODE_MOVE, + GIMP_TRANSFORM_3D_MODE_ROTATE +} GimpTransform3DMode; + + +#define GIMP_TYPE_TRANSFORM_FUNCTION (gimp_transform_function_get_type ()) + +GType gimp_transform_function_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_TRANSFORM_FUNCTION_NONE, + GIMP_TRANSFORM_FUNCTION_MOVE, + GIMP_TRANSFORM_FUNCTION_SCALE, + GIMP_TRANSFORM_FUNCTION_ROTATE, + GIMP_TRANSFORM_FUNCTION_SHEAR, + GIMP_TRANSFORM_FUNCTION_PERSPECTIVE +} GimpTransformFunction; + + +#define GIMP_TYPE_TRANSFORM_HANDLE_MODE (gimp_transform_handle_mode_get_type ()) + +GType gimp_transform_handle_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDLE_MODE_ADD_TRANSFORM, /*< desc="Add / Transform" >*/ + GIMP_HANDLE_MODE_MOVE, /*< desc="Move" >*/ + GIMP_HANDLE_MODE_REMOVE /*< desc="Remove" >*/ +} GimpTransformHandleMode; + + +#define GIMP_TYPE_VECTOR_MODE (gimp_vector_mode_get_type ()) + +GType gimp_vector_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_VECTOR_MODE_DESIGN, /*< desc="Design" >*/ + GIMP_VECTOR_MODE_EDIT, /*< desc="Edit" >*/ + GIMP_VECTOR_MODE_MOVE /*< desc="Move" >*/ +} GimpVectorMode; + + +#define GIMP_TYPE_ZOOM_FOCUS (gimp_zoom_focus_get_type ()) + +GType gimp_zoom_focus_get_type (void) G_GNUC_CONST; + +typedef enum +{ + /* Make a best guess */ + GIMP_ZOOM_FOCUS_BEST_GUESS, + + /* Use the mouse cursor (if within canvas) */ + GIMP_ZOOM_FOCUS_POINTER, + + /* Use the image center */ + GIMP_ZOOM_FOCUS_IMAGE_CENTER, + + /* If the image is centered, retain the centering. Else use + * _BEST_GUESS + */ + GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS + +} GimpZoomFocus; + + +/* + * non-registered enums; register them if needed + */ + + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_HIT_NONE, + GIMP_HIT_INDIRECT, + GIMP_HIT_DIRECT +} GimpHit; + + +#endif /* __DISPLAY_ENUMS_H__ */ diff --git a/app/display/display-types.h b/app/display/display-types.h new file mode 100644 index 0000000..6de751d --- /dev/null +++ b/app/display/display-types.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __DISPLAY_TYPES_H__ +#define __DISPLAY_TYPES_H__ + + +#include "propgui/propgui-types.h" + +#include "display/display-enums.h" + + +typedef struct _GimpCanvas GimpCanvas; +typedef struct _GimpCanvasGroup GimpCanvasGroup; +typedef struct _GimpCanvasItem GimpCanvasItem; + +typedef struct _GimpDisplay GimpDisplay; +typedef struct _GimpDisplayShell GimpDisplayShell; +typedef struct _GimpMotionBuffer GimpMotionBuffer; + +typedef struct _GimpImageWindow GimpImageWindow; +typedef struct _GimpMultiWindowStrategy GimpMultiWindowStrategy; +typedef struct _GimpSingleWindowStrategy GimpSingleWindowStrategy; + +typedef struct _GimpCursorView GimpCursorView; +typedef struct _GimpNavigationEditor GimpNavigationEditor; +typedef struct _GimpScaleComboBox GimpScaleComboBox; +typedef struct _GimpStatusbar GimpStatusbar; + +typedef struct _GimpToolDialog GimpToolDialog; +typedef struct _GimpToolGui GimpToolGui; +typedef struct _GimpToolWidget GimpToolWidget; +typedef struct _GimpToolWidgetGroup GimpToolWidgetGroup; + +typedef struct _GimpDisplayXfer GimpDisplayXfer; +typedef struct _Selection Selection; + + +#endif /* __DISPLAY_TYPES_H__ */ diff --git a/app/display/gimpcanvas-style.c b/app/display/gimpcanvas-style.c new file mode 100644 index 0000000..8d47961 --- /dev/null +++ b/app/display/gimpcanvas-style.c @@ -0,0 +1,458 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvas-style.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" +#include "core/gimpgrid.h" +#include "core/gimplayer.h" + +#include "gimpcanvas-style.h" + +/* Styles for common and custom guides. */ +static const GimpRGB guide_normal_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB guide_normal_bg = { 0.0, 0.8, 1.0, 1.0 }; +static const GimpRGB guide_active_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB guide_active_bg = { 1.0, 0.0, 0.0, 1.0 }; + +static const GimpRGB guide_mirror_normal_fg = { 1.0, 1.0, 1.0, 1.0 }; +static const GimpRGB guide_mirror_normal_bg = { 0.0, 1.0, 0.0, 1.0 }; +static const GimpRGB guide_mirror_active_fg = { 0.0, 1.0, 0.0, 1.0 }; +static const GimpRGB guide_mirror_active_bg = { 1.0, 0.0, 0.0, 1.0 }; + +static const GimpRGB guide_mandala_normal_fg = { 1.0, 1.0, 1.0, 1.0 }; +static const GimpRGB guide_mandala_normal_bg = { 0.0, 1.0, 1.0, 1.0 }; +static const GimpRGB guide_mandala_active_fg = { 0.0, 1.0, 1.0, 1.0 }; +static const GimpRGB guide_mandala_active_bg = { 1.0, 0.0, 0.0, 1.0 }; + +static const GimpRGB guide_split_normal_fg = { 1.0, 1.0, 1.0, 1.0 }; +static const GimpRGB guide_split_normal_bg = { 1.0, 0.0, 1.0, 1.0 }; +static const GimpRGB guide_split_active_fg = { 1.0, 0.0, 1.0, 1.0 }; +static const GimpRGB guide_split_active_bg = { 1.0, 0.0, 0.0, 1.0 }; + +/* Styles for other canvas items. */ +static const GimpRGB sample_point_normal = { 0.0, 0.8, 1.0, 1.0 }; +static const GimpRGB sample_point_active = { 1.0, 0.0, 0.0, 1.0 }; + +static const GimpRGB layer_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB layer_bg = { 1.0, 1.0, 0.0, 1.0 }; + +static const GimpRGB layer_group_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB layer_group_bg = { 0.0, 1.0, 1.0, 1.0 }; + +static const GimpRGB layer_mask_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB layer_mask_bg = { 0.0, 1.0, 0.0, 1.0 }; + +static const GimpRGB canvas_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB canvas_bg = { 1.0, 0.5, 0.0, 1.0 }; + +static const GimpRGB selection_out_fg = { 1.0, 1.0, 1.0, 1.0 }; +static const GimpRGB selection_out_bg = { 0.5, 0.5, 0.5, 1.0 }; + +static const GimpRGB selection_in_fg = { 0.0, 0.0, 0.0, 1.0 }; +static const GimpRGB selection_in_bg = { 1.0, 1.0, 1.0, 1.0 }; + +static const GimpRGB vectors_normal_bg = { 1.0, 1.0, 1.0, 0.6 }; +static const GimpRGB vectors_normal_fg = { 0.0, 0.0, 1.0, 0.8 }; + +static const GimpRGB vectors_active_bg = { 1.0, 1.0, 1.0, 0.6 }; +static const GimpRGB vectors_active_fg = { 1.0, 0.0, 0.0, 0.8 }; + +static const GimpRGB outline_bg = { 1.0, 1.0, 1.0, 0.6 }; +static const GimpRGB outline_fg = { 0.0, 0.0, 0.0, 0.8 }; + +static const GimpRGB passe_partout = { 0.0, 0.0, 0.0, 1.0 }; + +static const GimpRGB tool_bg = { 0.0, 0.0, 0.0, 0.4 }; +static const GimpRGB tool_fg = { 1.0, 1.0, 1.0, 0.8 }; +static const GimpRGB tool_fg_highlight = { 1.0, 0.8, 0.2, 0.8 }; + + +/* public functions */ + +void +gimp_canvas_set_guide_style (GtkWidget *canvas, + cairo_t *cr, + GimpGuideStyle style, + gboolean active, + gdouble offset_x, + gdouble offset_y) +{ + cairo_pattern_t *pattern; + GimpRGB normal_fg; + GimpRGB normal_bg; + GimpRGB active_fg; + GimpRGB active_bg; + gdouble line_width; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + switch (style) + { + case GIMP_GUIDE_STYLE_NORMAL: + normal_fg = guide_normal_fg; + normal_bg = guide_normal_bg; + active_fg = guide_active_fg; + active_bg = guide_active_bg; + line_width = 1.0; + break; + + case GIMP_GUIDE_STYLE_MIRROR: + normal_fg = guide_mirror_normal_fg; + normal_bg = guide_mirror_normal_bg; + active_fg = guide_mirror_active_fg; + active_bg = guide_mirror_active_bg; + line_width = 1.0; + break; + + case GIMP_GUIDE_STYLE_MANDALA: + normal_fg = guide_mandala_normal_fg; + normal_bg = guide_mandala_normal_bg; + active_fg = guide_mandala_active_fg; + active_bg = guide_mandala_active_bg; + line_width = 1.0; + break; + + case GIMP_GUIDE_STYLE_SPLIT_VIEW: + normal_fg = guide_split_normal_fg; + normal_bg = guide_split_normal_bg; + active_fg = guide_split_active_fg; + active_bg = guide_split_active_bg; + line_width = 1.0; + break; + + default: /* GIMP_GUIDE_STYLE_NONE */ + /* This should not happen. */ + g_return_if_reached (); + } + + cairo_set_line_width (cr, line_width); + + if (active) + pattern = gimp_cairo_pattern_create_stipple (&active_fg, &active_bg, 0, + offset_x, offset_y); + else + pattern = gimp_cairo_pattern_create_stipple (&normal_fg, &normal_bg, 0, + offset_x, offset_y); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +void +gimp_canvas_set_sample_point_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + + if (active) + gimp_cairo_set_source_rgb (cr, &sample_point_active); + else + gimp_cairo_set_source_rgb (cr, &sample_point_normal); +} + +void +gimp_canvas_set_grid_style (GtkWidget *canvas, + cairo_t *cr, + GimpGrid *grid, + gdouble offset_x, + gdouble offset_y) +{ + GimpRGB fg; + GimpRGB bg; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + g_return_if_fail (GIMP_IS_GRID (grid)); + + cairo_set_line_width (cr, 1.0); + + gimp_grid_get_fgcolor (grid, &fg); + + switch (gimp_grid_get_style (grid)) + { + cairo_pattern_t *pattern; + + case GIMP_GRID_ON_OFF_DASH: + case GIMP_GRID_DOUBLE_DASH: + if (grid->style == GIMP_GRID_DOUBLE_DASH) + { + gimp_grid_get_bgcolor (grid, &bg); + + pattern = gimp_cairo_pattern_create_stipple (&fg, &bg, 0, + offset_x, offset_y); + } + else + { + gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); + + pattern = gimp_cairo_pattern_create_stipple (&fg, &bg, 0, + offset_x, offset_y); + } + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + break; + + case GIMP_GRID_DOTS: + case GIMP_GRID_INTERSECTIONS: + case GIMP_GRID_SOLID: + gimp_cairo_set_source_rgb (cr, &fg); + break; + } +} + +void +gimp_canvas_set_pen_style (GtkWidget *canvas, + cairo_t *cr, + const GimpRGB *color, + gint width) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + g_return_if_fail (color != NULL); + + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (cr, width); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + gimp_cairo_set_source_rgb (cr, color); +} + +void +gimp_canvas_set_layer_style (GtkWidget *canvas, + cairo_t *cr, + GimpLayer *layer, + gdouble offset_x, + gdouble offset_y) +{ + cairo_pattern_t *pattern; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + g_return_if_fail (GIMP_IS_LAYER (layer)); + + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + + if (gimp_layer_get_mask (layer) && + gimp_layer_get_edit_mask (layer)) + { + pattern = gimp_cairo_pattern_create_stipple (&layer_mask_fg, + &layer_mask_bg, + 0, + offset_x, offset_y); + } + else if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) + { + pattern = gimp_cairo_pattern_create_stipple (&layer_group_fg, + &layer_group_bg, + 0, + offset_x, offset_y); + } + else + { + pattern = gimp_cairo_pattern_create_stipple (&layer_fg, + &layer_bg, + 0, + offset_x, offset_y); + } + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +void +gimp_canvas_set_canvas_style (GtkWidget *canvas, + cairo_t *cr, + gdouble offset_x, + gdouble offset_y) +{ + cairo_pattern_t *pattern; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + + pattern = gimp_cairo_pattern_create_stipple (&canvas_fg, + &canvas_bg, + 0, + offset_x, offset_y); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +void +gimp_canvas_set_selection_out_style (GtkWidget *canvas, + cairo_t *cr, + gdouble offset_x, + gdouble offset_y) +{ + cairo_pattern_t *pattern; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + + pattern = gimp_cairo_pattern_create_stipple (&selection_out_fg, + &selection_out_bg, + 0, + offset_x, offset_y); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +void +gimp_canvas_set_selection_in_style (GtkWidget *canvas, + cairo_t *cr, + gint index, + gdouble offset_x, + gdouble offset_y) +{ + cairo_pattern_t *pattern; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + + pattern = gimp_cairo_pattern_create_stipple (&selection_in_fg, + &selection_in_bg, + index, + offset_x, offset_y); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); +} + +void +gimp_canvas_set_vectors_bg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 3.0); + + if (active) + gimp_cairo_set_source_rgba (cr, &vectors_active_bg); + else + gimp_cairo_set_source_rgba (cr, &vectors_normal_bg); +} + +void +gimp_canvas_set_vectors_fg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + + if (active) + gimp_cairo_set_source_rgba (cr, &vectors_active_fg); + else + gimp_cairo_set_source_rgba (cr, &vectors_normal_fg); +} + +void +gimp_canvas_set_outline_bg_style (GtkWidget *canvas, + cairo_t *cr) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + gimp_cairo_set_source_rgba (cr, &outline_bg); +} + +void +gimp_canvas_set_outline_fg_style (GtkWidget *canvas, + cairo_t *cr) +{ + static const double dashes[] = { 4.0, 4.0 }; + + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + gimp_cairo_set_source_rgba (cr, &outline_fg); + cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); +} + +void +gimp_canvas_set_passe_partout_style (GtkWidget *canvas, + cairo_t *cr) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + gimp_cairo_set_source_rgba (cr, &passe_partout); +} + +void +gimp_canvas_set_tool_bg_style (GtkWidget *canvas, + cairo_t *cr) +{ + g_return_if_fail (GTK_IS_WIDGET (canvas)); + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 3.0); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + gimp_cairo_set_source_rgba (cr, &tool_bg); +} + +void +gimp_canvas_set_tool_fg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean highlight) +{ + g_return_if_fail (cr != NULL); + + cairo_set_line_width (cr, 1.0); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + if (highlight) + gimp_cairo_set_source_rgba (cr, &tool_fg_highlight); + else + gimp_cairo_set_source_rgba (cr, &tool_fg); +} diff --git a/app/display/gimpcanvas-style.h b/app/display/gimpcanvas-style.h new file mode 100644 index 0000000..3a37b98 --- /dev/null +++ b/app/display/gimpcanvas-style.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdisplayshell-style.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_STYLE_H__ +#define __GIMP_CANVAS_STYLE_H__ + + +void gimp_canvas_set_guide_style (GtkWidget *canvas, + cairo_t *cr, + GimpGuideStyle style, + gboolean active, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_sample_point_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active); +void gimp_canvas_set_grid_style (GtkWidget *canvas, + cairo_t *cr, + GimpGrid *grid, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_pen_style (GtkWidget *canvas, + cairo_t *cr, + const GimpRGB *color, + gint width); +void gimp_canvas_set_layer_style (GtkWidget *canvas, + cairo_t *cr, + GimpLayer *layer, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_canvas_style (GtkWidget *canvas, + cairo_t *cr, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_selection_out_style (GtkWidget *canvas, + cairo_t *cr, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_selection_in_style (GtkWidget *canvas, + cairo_t *cr, + gint index, + gdouble offset_x, + gdouble offset_y); +void gimp_canvas_set_vectors_bg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active); +void gimp_canvas_set_vectors_fg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean active); +void gimp_canvas_set_outline_bg_style (GtkWidget *canvas, + cairo_t *cr); +void gimp_canvas_set_outline_fg_style (GtkWidget *canvas, + cairo_t *cr); +void gimp_canvas_set_passe_partout_style (GtkWidget *canvas, + cairo_t *cr); + +void gimp_canvas_set_tool_bg_style (GtkWidget *canvas, + cairo_t *cr); +void gimp_canvas_set_tool_fg_style (GtkWidget *canvas, + cairo_t *cr, + gboolean highlight); + + +#endif /* __GIMP_CANVAS_STYLE_H__ */ diff --git a/app/display/gimpcanvas.c b/app/display/gimpcanvas.c new file mode 100644 index 0000000..46b9ff5 --- /dev/null +++ b/app/display/gimpcanvas.c @@ -0,0 +1,298 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvas.h" + +#include "gimp-intl.h" + + +#define MAX_BATCH_SIZE 32000 + + +enum +{ + PROP_0, + PROP_CONFIG +}; + + +/* local function prototypes */ + +static void gimp_canvas_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_canvas_unrealize (GtkWidget *widget); +static void gimp_canvas_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static gboolean gimp_canvas_focus_in_event (GtkWidget *widget, + GdkEventFocus *event); +static gboolean gimp_canvas_focus_out_event (GtkWidget *widget, + GdkEventFocus *event); +static gboolean gimp_canvas_focus (GtkWidget *widget, + GtkDirectionType direction); + + +G_DEFINE_TYPE (GimpCanvas, gimp_canvas, GIMP_TYPE_OVERLAY_BOX) + +#define parent_class gimp_canvas_parent_class + + +static void +gimp_canvas_class_init (GimpCanvasClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->set_property = gimp_canvas_set_property; + object_class->get_property = gimp_canvas_get_property; + + widget_class->unrealize = gimp_canvas_unrealize; + widget_class->style_set = gimp_canvas_style_set; + widget_class->focus_in_event = gimp_canvas_focus_in_event; + widget_class->focus_out_event = gimp_canvas_focus_out_event; + widget_class->focus = gimp_canvas_focus; + + g_object_class_install_property (object_class, PROP_CONFIG, + g_param_spec_object ("config", NULL, NULL, + GIMP_TYPE_DISPLAY_CONFIG, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_canvas_init (GimpCanvas *canvas) +{ + GtkWidget *widget = GTK_WIDGET (canvas); + + gtk_widget_set_can_focus (widget, TRUE); + gtk_widget_add_events (widget, GIMP_CANVAS_EVENT_MASK); + gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_ALL); +} + +static void +gimp_canvas_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvas *canvas = GIMP_CANVAS (object); + + switch (property_id) + { + case PROP_CONFIG: + canvas->config = g_value_get_object (value); /* don't dup */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvas *canvas = GIMP_CANVAS (object); + + switch (property_id) + { + case PROP_CONFIG: + g_value_set_object (value, canvas->config); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_unrealize (GtkWidget *widget) +{ + GimpCanvas *canvas = GIMP_CANVAS (widget); + + g_clear_object (&canvas->layout); + + GTK_WIDGET_CLASS (parent_class)->unrealize (widget); +} + +static void +gimp_canvas_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpCanvas *canvas = GIMP_CANVAS (widget); + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + g_clear_object (&canvas->layout); +} + +static gboolean +gimp_canvas_focus_in_event (GtkWidget *widget, + GdkEventFocus *event) +{ + /* don't allow the default impl to invalidate the whole widget, + * we don't draw a focus indicator anyway. + */ + return FALSE; +} + +static gboolean +gimp_canvas_focus_out_event (GtkWidget *widget, + GdkEventFocus *event) +{ + /* see focus-in-event + */ + return FALSE; +} + +static gboolean +gimp_canvas_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkWidget *focus = gtk_container_get_focus_child (GTK_CONTAINER (widget)); + + /* override GtkContainer's focus() implementation which would always + * give focus to the canvas because it is focussable. Instead, try + * navigating in the focused overlay child first, and use + * GtkContainer's default implementation only if that fails (which + * happens when focus navigation leaves the overlay child). + */ + + if (focus && gtk_widget_child_focus (focus, direction)) + return TRUE; + + return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction); +} + + +/* public functions */ + +/** + * gimp_canvas_new: + * + * Creates a new #GimpCanvas widget. + * + * The #GimpCanvas widget is a #GtkDrawingArea abstraction. It manages + * a set of graphic contexts for drawing on a GIMP display. If you + * draw using a #GimpCanvasStyle, #GimpCanvas makes sure that the + * associated #GdkGC is created. All drawing on the canvas needs to + * happen by means of the #GimpCanvas drawing functions. Besides from + * not needing a #GdkGC pointer, the #GimpCanvas drawing functions + * look and work like their #GdkDrawable counterparts. #GimpCanvas + * gracefully handles attempts to draw on the unrealized widget. + * + * Return value: a new #GimpCanvas widget + **/ +GtkWidget * +gimp_canvas_new (GimpDisplayConfig *config) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_CONFIG (config), NULL); + + return g_object_new (GIMP_TYPE_CANVAS, + "name", "gimp-canvas", + "config", config, + NULL); +} + +/** + * gimp_canvas_get_layout: + * @canvas: a #GimpCanvas widget + * @format: a standard printf() format string. + * @Varargs: the parameters to insert into the format string. + * + * Returns a layout which can be used for + * pango_cairo_show_layout(). The layout belongs to the canvas and + * should not be freed, not should a pointer to it be kept around + * after drawing. + * + * Returns: a #PangoLayout owned by the canvas. + **/ +PangoLayout * +gimp_canvas_get_layout (GimpCanvas *canvas, + const gchar *format, + ...) +{ + va_list args; + gchar *text; + + if (! canvas->layout) + canvas->layout = gtk_widget_create_pango_layout (GTK_WIDGET (canvas), + NULL); + + va_start (args, format); + text = g_strdup_vprintf (format, args); + va_end (args); + + pango_layout_set_text (canvas->layout, text, -1); + g_free (text); + + return canvas->layout; +} + +/** + * gimp_canvas_set_bg_color: + * @canvas: a #GimpCanvas widget + * @color: a color in #GimpRGB format + * + * Sets the background color of the canvas's window. This + * is the color the canvas is set to if it is cleared. + **/ +void +gimp_canvas_set_bg_color (GimpCanvas *canvas, + GimpRGB *color) +{ + GtkWidget *widget = GTK_WIDGET (canvas); + GdkColormap *colormap; + GdkColor gdk_color; + + if (! gtk_widget_get_realized (widget)) + return; + + gimp_rgb_get_gdk_color (color, &gdk_color); + + colormap = gdk_drawable_get_colormap (gtk_widget_get_window (widget)); + g_return_if_fail (colormap != NULL); + gdk_colormap_alloc_color (colormap, &gdk_color, FALSE, TRUE); + + gdk_window_set_background (gtk_widget_get_window (widget), &gdk_color); + + gtk_widget_queue_draw (GTK_WIDGET (canvas)); +} diff --git a/app/display/gimpcanvas.h b/app/display/gimpcanvas.h new file mode 100644 index 0000000..dc52be8 --- /dev/null +++ b/app/display/gimpcanvas.h @@ -0,0 +1,74 @@ +/* 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 . + */ + +#ifndef __GIMP_CANVAS_H__ +#define __GIMP_CANVAS_H__ + + +#include "widgets/gimpoverlaybox.h" + + +#define GIMP_CANVAS_EVENT_MASK (GDK_EXPOSURE_MASK | \ + GDK_POINTER_MOTION_MASK | \ + GDK_BUTTON_PRESS_MASK | \ + GDK_BUTTON_RELEASE_MASK | \ + GDK_STRUCTURE_MASK | \ + GDK_ENTER_NOTIFY_MASK | \ + GDK_LEAVE_NOTIFY_MASK | \ + GDK_FOCUS_CHANGE_MASK | \ + GDK_KEY_PRESS_MASK | \ + GDK_KEY_RELEASE_MASK | \ + GDK_PROXIMITY_OUT_MASK) + + +#define GIMP_TYPE_CANVAS (gimp_canvas_get_type ()) +#define GIMP_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS, GimpCanvas)) +#define GIMP_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS, GimpCanvasClass)) +#define GIMP_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS)) +#define GIMP_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS)) +#define GIMP_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS, GimpCanvasClass)) + + +typedef struct _GimpCanvasClass GimpCanvasClass; + +struct _GimpCanvas +{ + GimpOverlayBox parent_instance; + + GimpDisplayConfig *config; + PangoLayout *layout; +}; + +struct _GimpCanvasClass +{ + GimpOverlayBoxClass parent_class; +}; + + +GType gimp_canvas_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_canvas_new (GimpDisplayConfig *config); + +PangoLayout * gimp_canvas_get_layout (GimpCanvas *canvas, + const gchar *format, + ...) G_GNUC_PRINTF (2, 3); + +void gimp_canvas_set_bg_color (GimpCanvas *canvas, + GimpRGB *color); + + +#endif /* __GIMP_CANVAS_H__ */ diff --git a/app/display/gimpcanvasarc.c b/app/display/gimpcanvasarc.c new file mode 100644 index 0000000..a4041b2 --- /dev/null +++ b/app/display/gimpcanvasarc.c @@ -0,0 +1,369 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasarc.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" + +#include "gimpcanvasarc.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_CENTER_X, + PROP_CENTER_Y, + PROP_RADIUS_X, + PROP_RADIUS_Y, + PROP_START_ANGLE, + PROP_SLICE_ANGLE, + PROP_FILLED +}; + + +typedef struct _GimpCanvasArcPrivate GimpCanvasArcPrivate; + +struct _GimpCanvasArcPrivate +{ + gdouble center_x; + gdouble center_y; + gdouble radius_x; + gdouble radius_y; + gdouble start_angle; + gdouble slice_angle; + gboolean filled; +}; + +#define GET_PRIVATE(arc) \ + ((GimpCanvasArcPrivate *) gimp_canvas_arc_get_instance_private ((GimpCanvasArc *) (arc))) + + +/* local function prototypes */ + +static void gimp_canvas_arc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_arc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_arc_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_arc_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasArc, gimp_canvas_arc, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_arc_parent_class + + +static void +gimp_canvas_arc_class_init (GimpCanvasArcClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_arc_set_property; + object_class->get_property = gimp_canvas_arc_get_property; + + item_class->draw = gimp_canvas_arc_draw; + item_class->get_extents = gimp_canvas_arc_get_extents; + + g_object_class_install_property (object_class, PROP_CENTER_X, + g_param_spec_double ("center-x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CENTER_Y, + g_param_spec_double ("center-y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_RADIUS_X, + g_param_spec_double ("radius-x", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_RADIUS_Y, + g_param_spec_double ("radius-y", NULL, NULL, + 0, GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_START_ANGLE, + g_param_spec_double ("start-angle", NULL, NULL, + -1000, 1000, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SLICE_ANGLE, + g_param_spec_double ("slice-angle", NULL, NULL, + -1000, 1000, 2 * G_PI, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FILLED, + g_param_spec_boolean ("filled", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_arc_init (GimpCanvasArc *arc) +{ +} + +static void +gimp_canvas_arc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasArcPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_CENTER_X: + private->center_x = g_value_get_double (value); + break; + case PROP_CENTER_Y: + private->center_y = g_value_get_double (value); + break; + case PROP_RADIUS_X: + private->radius_x = g_value_get_double (value); + break; + case PROP_RADIUS_Y: + private->radius_y = g_value_get_double (value); + break; + case PROP_START_ANGLE: + private->start_angle = g_value_get_double (value); + break; + case PROP_SLICE_ANGLE: + private->slice_angle = g_value_get_double (value); + break; + case PROP_FILLED: + private->filled = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_arc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasArcPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_CENTER_X: + g_value_set_double (value, private->center_x); + break; + case PROP_CENTER_Y: + g_value_set_double (value, private->center_y); + break; + case PROP_RADIUS_X: + g_value_set_double (value, private->radius_x); + break; + case PROP_RADIUS_Y: + g_value_set_double (value, private->radius_y); + break; + case PROP_START_ANGLE: + g_value_set_double (value, private->start_angle); + break; + case PROP_SLICE_ANGLE: + g_value_set_double (value, private->slice_angle); + break; + case PROP_FILLED: + g_value_set_boolean (value, private->filled); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_arc_transform (GimpCanvasItem *item, + gdouble *center_x, + gdouble *center_y, + gdouble *radius_x, + gdouble *radius_y) +{ + GimpCanvasArcPrivate *private = GET_PRIVATE (item); + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_item_transform_xy_f (item, + private->center_x - private->radius_x, + private->center_y - private->radius_y, + &x1, &y1); + gimp_canvas_item_transform_xy_f (item, + private->center_x + private->radius_x, + private->center_y + private->radius_y, + &x2, &y2); + + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + + *center_x = (x1 + x2) / 2.0; + *center_y = (y1 + y2) / 2.0; + + *radius_x = (x2 - x1) / 2.0; + *radius_y = (y2 - y1) / 2.0; + + if (! private->filled) + { + *radius_x = MAX (*radius_x - 0.5, 0.0); + *radius_y = MAX (*radius_y - 0.5, 0.0); + } + + /* avoid cairo_scale (cr, 0.0, 0.0) */ + if (*radius_x == 0.0) *radius_x = 0.000001; + if (*radius_y == 0.0) *radius_y = 0.000001; +} + +static void +gimp_canvas_arc_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasArcPrivate *private = GET_PRIVATE (item); + gdouble center_x, center_y; + gdouble radius_x, radius_y; + + gimp_canvas_arc_transform (item, + ¢er_x, ¢er_y, + &radius_x, &radius_y); + + cairo_save (cr); + cairo_translate (cr, center_x, center_y); + cairo_scale (cr, radius_x, radius_y); + gimp_cairo_arc (cr, 0.0, 0.0, 1.0, + private->start_angle, private->slice_angle); + cairo_restore (cr); + + if (private->filled) + _gimp_canvas_item_fill (item, cr); + else + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_arc_get_extents (GimpCanvasItem *item) +{ + GimpCanvasArcPrivate *private = GET_PRIVATE (item); + cairo_region_t *region; + cairo_rectangle_int_t rectangle; + gdouble center_x, center_y; + gdouble radius_x, radius_y; + + gimp_canvas_arc_transform (item, + ¢er_x, ¢er_y, + &radius_x, &radius_y); + + rectangle.x = floor (center_x - radius_x - 1.5); + rectangle.y = floor (center_y - radius_y - 1.5); + rectangle.width = ceil (center_x + radius_x + 1.5) - rectangle.x; + rectangle.height = ceil (center_y + radius_y + 1.5) - rectangle.y; + + region = cairo_region_create_rectangle (&rectangle); + + if (! private->filled && + rectangle.width > 64 * 1.43 && + rectangle.height > 64 * 1.43) + { + radius_x *= 0.7; + radius_y *= 0.7; + + rectangle.x = ceil (center_x - radius_x + 1.5); + rectangle.y = ceil (center_y - radius_y + 1.5); + rectangle.width = floor (center_x + radius_x - 1.5) - rectangle.x; + rectangle.height = floor (center_y + radius_y - 1.5) - rectangle.y; + + cairo_region_subtract_rectangle (region, &rectangle); + } + + return region; +} + +GimpCanvasItem * +gimp_canvas_arc_new (GimpDisplayShell *shell, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle, + gboolean filled) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_ARC, + "shell", shell, + "center-x", center_x, + "center-y", center_y, + "radius-x", radius_x, + "radius-y", radius_y, + "start-angle", start_angle, + "slice-angle", slice_angle, + "filled", filled, + NULL); +} + +void +gimp_canvas_arc_set (GimpCanvasItem *arc, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle) +{ + g_return_if_fail (GIMP_IS_CANVAS_ARC (arc)); + + gimp_canvas_item_begin_change (arc); + g_object_set (arc, + "center-x", center_x, + "center-y", center_y, + "radius-x", radius_x, + "radius-y", radius_y, + "start-angle", start_angle, + "slice-angle", slice_angle, + NULL); + gimp_canvas_item_end_change (arc); +} diff --git a/app/display/gimpcanvasarc.h b/app/display/gimpcanvasarc.h new file mode 100644 index 0000000..1896352 --- /dev/null +++ b/app/display/gimpcanvasarc.h @@ -0,0 +1,69 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasarc.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_ARC_H__ +#define __GIMP_CANVAS_ARC_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_ARC (gimp_canvas_arc_get_type ()) +#define GIMP_CANVAS_ARC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_ARC, GimpCanvasArc)) +#define GIMP_CANVAS_ARC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_ARC, GimpCanvasArcClass)) +#define GIMP_IS_CANVAS_ARC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_ARC)) +#define GIMP_IS_CANVAS_ARC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_ARC)) +#define GIMP_CANVAS_ARC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_ARC, GimpCanvasArcClass)) + + +typedef struct _GimpCanvasArc GimpCanvasArc; +typedef struct _GimpCanvasArcClass GimpCanvasArcClass; + +struct _GimpCanvasArc +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasArcClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_arc_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_arc_new (GimpDisplayShell *shell, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle, + gboolean filled); + +void gimp_canvas_arc_set (GimpCanvasItem *arc, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle); + +#endif /* __GIMP_CANVAS_ARC_H__ */ diff --git a/app/display/gimpcanvasboundary.c b/app/display/gimpcanvasboundary.c new file mode 100644 index 0000000..a6cab9d --- /dev/null +++ b/app/display/gimpcanvasboundary.c @@ -0,0 +1,384 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasboundary.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" +#include "core/gimp-transform-utils.h" +#include "core/gimpboundary.h" +#include "core/gimpparamspecs.h" + +#include "gimpcanvasboundary.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_SEGS, + PROP_TRANSFORM, + PROP_OFFSET_X, + PROP_OFFSET_Y +}; + + +typedef struct _GimpCanvasBoundaryPrivate GimpCanvasBoundaryPrivate; + +struct _GimpCanvasBoundaryPrivate +{ + GimpBoundSeg *segs; + gint n_segs; + GimpMatrix3 *transform; + gdouble offset_x; + gdouble offset_y; +}; + +#define GET_PRIVATE(boundary) \ + ((GimpCanvasBoundaryPrivate *) gimp_canvas_boundary_get_instance_private ((GimpCanvasBoundary *) (boundary))) + + +/* local function prototypes */ + +static void gimp_canvas_boundary_finalize (GObject *object); +static void gimp_canvas_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_boundary_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_boundary_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasBoundary, gimp_canvas_boundary, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_boundary_parent_class + + +static void +gimp_canvas_boundary_class_init (GimpCanvasBoundaryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_boundary_finalize; + object_class->set_property = gimp_canvas_boundary_set_property; + object_class->get_property = gimp_canvas_boundary_get_property; + + item_class->draw = gimp_canvas_boundary_draw; + item_class->get_extents = gimp_canvas_boundary_get_extents; + + g_object_class_install_property (object_class, PROP_SEGS, + gimp_param_spec_array ("segs", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TRANSFORM, + g_param_spec_pointer ("transform", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_OFFSET_X, + g_param_spec_double ("offset-x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_OFFSET_Y, + g_param_spec_double ("offset-y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_boundary_init (GimpCanvasBoundary *boundary) +{ + gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (boundary), + CAIRO_LINE_CAP_SQUARE); +} + +static void +gimp_canvas_boundary_finalize (GObject *object) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->segs, g_free); + private->n_segs = 0; + + g_clear_pointer (&private->transform, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_SEGS: + break; + + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_pointer (value); + if (private->transform) + g_free (private->transform); + if (transform) + private->transform = g_memdup (transform, sizeof (GimpMatrix3)); + else + private->transform = NULL; + } + break; + + case PROP_OFFSET_X: + private->offset_x = g_value_get_double (value); + break; + case PROP_OFFSET_Y: + private->offset_y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_SEGS: + break; + + case PROP_TRANSFORM: + g_value_set_pointer (value, private->transform); + break; + + case PROP_OFFSET_X: + g_value_set_double (value, private->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_double (value, private->offset_y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_boundary_transform (GimpCanvasItem *item, + GimpSegment *segs, + gint *n_segs) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); + gint i; + + if (private->transform) + { + gint n = 0; + + for (i = 0; i < private->n_segs; i++) + { + GimpVector2 vertices[2]; + GimpVector2 t_vertices[2]; + gint n_t_vertices; + + vertices[0] = (GimpVector2) { private->segs[i].x1, private->segs[i].y1 }; + vertices[1] = (GimpVector2) { private->segs[i].x2, private->segs[i].y2 }; + + gimp_transform_polygon (private->transform, vertices, 2, FALSE, + t_vertices, &n_t_vertices); + + if (n_t_vertices == 2) + { + gimp_canvas_item_transform_xy (item, + t_vertices[0].x + private->offset_x, + t_vertices[0].y + private->offset_y, + &segs[n].x1, &segs[n].y1); + gimp_canvas_item_transform_xy (item, + t_vertices[1].x + private->offset_x, + t_vertices[1].y + private->offset_y, + &segs[n].x2, &segs[n].y2); + + n++; + } + } + + *n_segs = n; + } + else + { + for (i = 0; i < private->n_segs; i++) + { + gimp_canvas_item_transform_xy (item, + private->segs[i].x1 + private->offset_x, + private->segs[i].y1 + private->offset_y, + &segs[i].x1, + &segs[i].y1); + gimp_canvas_item_transform_xy (item, + private->segs[i].x2 + private->offset_x, + private->segs[i].y2 + private->offset_y, + &segs[i].x2, + &segs[i].y2); + + /* If this segment is a closing segment && the segments lie inside + * the region, OR if this is an opening segment and the segments + * lie outside the region... + * we need to transform it by one display pixel + */ + if (! private->segs[i].open) + { + /* If it is vertical */ + if (segs[i].x1 == segs[i].x2) + { + segs[i].x1 -= 1; + segs[i].x2 -= 1; + } + else + { + segs[i].y1 -= 1; + segs[i].y2 -= 1; + } + } + } + + *n_segs = private->n_segs; + } +} + +static void +gimp_canvas_boundary_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); + GimpSegment *segs; + gint n_segs; + + segs = g_new0 (GimpSegment, private->n_segs); + + gimp_canvas_boundary_transform (item, segs, &n_segs); + + gimp_cairo_segments (cr, segs, n_segs); + + _gimp_canvas_item_stroke (item, cr); + + g_free (segs); +} + +static cairo_region_t * +gimp_canvas_boundary_get_extents (GimpCanvasItem *item) +{ + GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + GimpSegment *segs; + gint n_segs; + gint x1, y1, x2, y2; + gint i; + + segs = g_new0 (GimpSegment, private->n_segs); + + gimp_canvas_boundary_transform (item, segs, &n_segs); + + if (n_segs == 0) + { + g_free (segs); + + return cairo_region_create (); + } + + x1 = MIN (segs[0].x1, segs[0].x2); + y1 = MIN (segs[0].y1, segs[0].y2); + x2 = MAX (segs[0].x1, segs[0].x2); + y2 = MAX (segs[0].y1, segs[0].y2); + + for (i = 1; i < n_segs; i++) + { + gint x3 = MIN (segs[i].x1, segs[i].x2); + gint y3 = MIN (segs[i].y1, segs[i].y2); + gint x4 = MAX (segs[i].x1, segs[i].x2); + gint y4 = MAX (segs[i].y1, segs[i].y2); + + x1 = MIN (x1, x3); + y1 = MIN (y1, y3); + x2 = MAX (x2, x4); + y2 = MAX (y2, y4); + } + + g_free (segs); + + rectangle.x = x1 - 2; + rectangle.y = y1 - 2; + rectangle.width = x2 - x1 + 4; + rectangle.height = y2 - y1 + 4; + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_boundary_new (GimpDisplayShell *shell, + const GimpBoundSeg *segs, + gint n_segs, + GimpMatrix3 *transform, + gdouble offset_x, + gdouble offset_y) +{ + GimpCanvasItem *item; + GimpCanvasBoundaryPrivate *private; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + item = g_object_new (GIMP_TYPE_CANVAS_BOUNDARY, + "shell", shell, + "transform", transform, + "offset-x", offset_x, + "offset-y", offset_y, + NULL); + private = GET_PRIVATE (item); + + /* puke */ + private->segs = g_memdup (segs, n_segs * sizeof (GimpBoundSeg)); + private->n_segs = n_segs; + + return item; +} diff --git a/app/display/gimpcanvasboundary.h b/app/display/gimpcanvasboundary.h new file mode 100644 index 0000000..527f89e --- /dev/null +++ b/app/display/gimpcanvasboundary.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program Copyright (C) 1995 + * Spencer Kimball and Peter Mattis + * + * gimpcanvasboundary.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_BOUNDARY_H__ +#define __GIMP_CANVAS_BOUNDARY_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_BOUNDARY (gimp_canvas_boundary_get_type ()) +#define GIMP_CANVAS_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_BOUNDARY, GimpCanvasBoundary)) +#define GIMP_CANVAS_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_BOUNDARY, GimpCanvasBoundaryClass)) +#define GIMP_IS_CANVAS_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_BOUNDARY)) +#define GIMP_IS_CANVAS_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_BOUNDARY)) +#define GIMP_CANVAS_BOUNDARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_BOUNDARY, GimpCanvasBoundaryClass)) + + +typedef struct _GimpCanvasBoundary GimpCanvasBoundary; +typedef struct _GimpCanvasBoundaryClass GimpCanvasBoundaryClass; + +struct _GimpCanvasBoundary +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasBoundaryClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_boundary_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_boundary_new (GimpDisplayShell *shell, + const GimpBoundSeg *segs, + gint n_segs, + GimpMatrix3 *transform, + gdouble offset_x, + gdouble offset_y); + + +#endif /* __GIMP_CANVAS_BOUNDARY_H__ */ diff --git a/app/display/gimpcanvasbufferpreview.c b/app/display/gimpcanvasbufferpreview.c new file mode 100644 index 0000000..858bab6 --- /dev/null +++ b/app/display/gimpcanvasbufferpreview.c @@ -0,0 +1,263 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasbufferpreview.c + * Copyright (C) 2013 Marek Dvoroznak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display/display-types.h" + +#include "core/gimpimage.h" + +#include "gimpcanvas.h" +#include "gimpcanvasbufferpreview.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scroll.h" + + +enum +{ + PROP_0, + PROP_BUFFER +}; + + +typedef struct _GimpCanvasBufferPreviewPrivate GimpCanvasBufferPreviewPrivate; + +struct _GimpCanvasBufferPreviewPrivate +{ + GeglBuffer *buffer; +}; + + +#define GET_PRIVATE(transform_preview) \ + ((GimpCanvasBufferPreviewPrivate *) gimp_canvas_buffer_preview_get_instance_private ((GimpCanvasBufferPreview *) (transform_preview))) + + +/* local function prototypes */ + +static void gimp_canvas_buffer_preview_dispose (GObject *object); +static void gimp_canvas_buffer_preview_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_buffer_preview_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_canvas_buffer_preview_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_buffer_preview_get_extents (GimpCanvasItem *item); +static void gimp_canvas_buffer_preview_compute_bounds (GimpCanvasItem *item, + cairo_rectangle_int_t *bounds); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasBufferPreview, gimp_canvas_buffer_preview, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_buffer_preview_parent_class + + +static void +gimp_canvas_buffer_preview_class_init (GimpCanvasBufferPreviewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->dispose = gimp_canvas_buffer_preview_dispose; + object_class->set_property = gimp_canvas_buffer_preview_set_property; + object_class->get_property = gimp_canvas_buffer_preview_get_property; + + item_class->draw = gimp_canvas_buffer_preview_draw; + item_class->get_extents = gimp_canvas_buffer_preview_get_extents; + + g_object_class_install_property (object_class, PROP_BUFFER, + g_param_spec_object ("buffer", + NULL, NULL, + GEGL_TYPE_BUFFER, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_buffer_preview_init (GimpCanvasBufferPreview *transform_preview) +{ +} + +static void +gimp_canvas_buffer_preview_dispose (GObject *object) +{ + GimpCanvasBufferPreviewPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->buffer); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_canvas_buffer_preview_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasBufferPreviewPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_BUFFER: + g_set_object (&private->buffer, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_buffer_preview_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasBufferPreviewPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_BUFFER: + g_value_set_object (value, private->buffer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_buffer_preview_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + GeglBuffer *buffer = GET_PRIVATE (item)->buffer; + cairo_surface_t *area; + guchar *data; + cairo_rectangle_int_t rectangle; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + gimp_canvas_buffer_preview_compute_bounds (item, &rectangle); + + area = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + rectangle.width, + rectangle.height); + + data = cairo_image_surface_get_data (area); + gegl_buffer_get (buffer, + GEGL_RECTANGLE (rectangle.x + shell->offset_x, + rectangle.y + shell->offset_y, + rectangle.width, + rectangle.height), + shell->scale_x, + babl_format ("cairo-ARGB32"), + data, + cairo_image_surface_get_stride (area), + GEGL_ABYSS_NONE); + + cairo_surface_flush (area); + cairo_surface_mark_dirty (area); + + cairo_set_source_surface (cr, area, rectangle.x, rectangle.y); + cairo_rectangle (cr, + rectangle.x, rectangle.y, + rectangle.width, rectangle.height); + cairo_fill (cr); + + cairo_surface_destroy (area); +} + +static void +gimp_canvas_buffer_preview_compute_bounds (GimpCanvasItem *item, + cairo_rectangle_int_t *bounds) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + GeglBuffer *buffer = GET_PRIVATE (item)->buffer; + GeglRectangle extent; + gdouble x1, y1; + gdouble x2, y2; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + extent = *gegl_buffer_get_extent (buffer); + + gimp_canvas_item_transform_xy_f (item, + extent.x, + extent.y, + &x1, &y1); + gimp_canvas_item_transform_xy_f (item, + extent.x + extent.width, + extent.y + extent.height, + &x2, &y2); + + extent.x = floor (x1); + extent.y = floor (y1); + extent.width = ceil (x2) - extent.x; + extent.height = ceil (y2) - extent.y; + + gegl_rectangle_intersect (&extent, + &extent, + GEGL_RECTANGLE (0, + 0, + shell->disp_width, + shell->disp_height)); + + bounds->x = extent.x; + bounds->y = extent.y; + bounds->width = extent.width; + bounds->height = extent.height; +} + +static cairo_region_t * +gimp_canvas_buffer_preview_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + + gimp_canvas_buffer_preview_compute_bounds (item, &rectangle); + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_buffer_preview_new (GimpDisplayShell *shell, + GeglBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_BUFFER_PREVIEW, + "shell", shell, + "buffer", buffer, + NULL); +} diff --git a/app/display/gimpcanvasbufferpreview.h b/app/display/gimpcanvasbufferpreview.h new file mode 100644 index 0000000..28d8806 --- /dev/null +++ b/app/display/gimpcanvasbufferpreview.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasbufferpreview.h + * Copyright (C) 2013 Marek Dvoroznak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_BUFFER_PREVIEW_H__ +#define __GIMP_CANVAS_BUFFER_PREVIEW_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_BUFFER_PREVIEW (gimp_canvas_buffer_preview_get_type ()) +#define GIMP_CANVAS_BUFFER_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_BUFFER_PREVIEW, GimpCanvasBufferPreview)) +#define GIMP_CANVAS_BUFFER_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_BUFFER_PREVIEW, GimpCanvasBufferPreviewClass)) +#define GIMP_IS_CANVAS_BUFFER_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_BUFFER_PREVIEW)) +#define GIMP_IS_CANVAS_BUFFER_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_BUFFER_PREVIEW)) +#define GIMP_CANVAS_BUFFER_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_BUFFER_PREVIEW, GimpCanvasBufferPreviewClass)) + + +typedef struct _GimpCanvasBufferPreview GimpCanvasBufferPreview; +typedef struct _GimpCanvasBufferPreviewClass GimpCanvasBufferPreviewClass; + +struct _GimpCanvasBufferPreview +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasBufferPreviewClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_buffer_preview_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_buffer_preview_new (GimpDisplayShell *shell, + GeglBuffer *buffer); + + +#endif /* __GIMP_CANVAS_BUFFER_PREVIEW_H__ */ diff --git a/app/display/gimpcanvascanvasboundary.c b/app/display/gimpcanvascanvasboundary.c new file mode 100644 index 0000000..01ac5a3 --- /dev/null +++ b/app/display/gimpcanvascanvasboundary.c @@ -0,0 +1,270 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascanvasboundary.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvascanvasboundary.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_IMAGE +}; + + +typedef struct _GimpCanvasCanvasBoundaryPrivate GimpCanvasCanvasBoundaryPrivate; + +struct _GimpCanvasCanvasBoundaryPrivate +{ + GimpImage *image; +}; + +#define GET_PRIVATE(canvas_boundary) \ + ((GimpCanvasCanvasBoundaryPrivate *) gimp_canvas_canvas_boundary_get_instance_private ((GimpCanvasCanvasBoundary *) (canvas_boundary))) + + +/* local function prototypes */ + +static void gimp_canvas_canvas_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_canvas_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_canvas_boundary_finalize (GObject *object); +static void gimp_canvas_canvas_boundary_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_canvas_boundary_get_extents (GimpCanvasItem *item); +static void gimp_canvas_canvas_boundary_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasCanvasBoundary, gimp_canvas_canvas_boundary, + GIMP_TYPE_CANVAS_RECTANGLE) + +#define parent_class gimp_canvas_canvas_boundary_parent_class + + +static void +gimp_canvas_canvas_boundary_class_init (GimpCanvasCanvasBoundaryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_canvas_boundary_set_property; + object_class->get_property = gimp_canvas_canvas_boundary_get_property; + object_class->finalize = gimp_canvas_canvas_boundary_finalize; + + item_class->draw = gimp_canvas_canvas_boundary_draw; + item_class->get_extents = gimp_canvas_canvas_boundary_get_extents; + item_class->stroke = gimp_canvas_canvas_boundary_stroke; + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_canvas_boundary_init (GimpCanvasCanvasBoundary *canvas_boundary) +{ +} + +static void +gimp_canvas_canvas_boundary_finalize (GObject *object) +{ + GimpCanvasCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + if (private->image) + g_object_remove_weak_pointer (G_OBJECT (private->image), + (gpointer) &private->image); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_canvas_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_IMAGE: + if (private->image) + g_object_remove_weak_pointer (G_OBJECT (private->image), + (gpointer) &private->image); + private->image = g_value_get_object (value); /* don't ref */ + if (private->image) + g_object_add_weak_pointer (G_OBJECT (private->image), + (gpointer) &private->image); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_canvas_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCanvasBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_IMAGE: + g_value_set_object (value, private->image); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_canvas_boundary_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasCanvasBoundaryPrivate *private = GET_PRIVATE (item); + + if (private->image) + GIMP_CANVAS_ITEM_CLASS (parent_class)->draw (item, cr); +} + +static cairo_region_t * +gimp_canvas_canvas_boundary_get_extents (GimpCanvasItem *item) +{ + GimpCanvasCanvasBoundaryPrivate *private = GET_PRIVATE (item); + + if (private->image) + return GIMP_CANVAS_ITEM_CLASS (parent_class)->get_extents (item); + + return NULL; +} + +static void +gimp_canvas_canvas_boundary_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + + gimp_canvas_set_canvas_style (gimp_canvas_item_get_canvas (item), cr, + shell->offset_x, shell->offset_y); + cairo_stroke (cr); +} + +GimpCanvasItem * +gimp_canvas_canvas_boundary_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_CANVAS_BOUNDARY, + "shell", shell, + NULL); +} + +void +gimp_canvas_canvas_boundary_set_image (GimpCanvasCanvasBoundary *boundary, + GimpImage *image) +{ + GimpCanvasCanvasBoundaryPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_CANVAS_BOUNDARY (boundary)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + + private = GET_PRIVATE (boundary); + + if (image != private->image) + { + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (boundary)); + + if (image) + { + g_object_set (boundary, + "x", (gdouble) 0, + "y", (gdouble) 0, + "width", (gdouble) gimp_image_get_width (image), + "height", (gdouble) gimp_image_get_height (image), + NULL); + } + + g_object_set (boundary, + "image", image, + NULL); + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (boundary)); + } + else if (image && image == private->image) + { + gint lx, ly, lw, lh; + gdouble x, y, w ,h; + + lx = 0; + ly = 0; + lw = gimp_image_get_width (image); + lh = gimp_image_get_height (image); + + g_object_get (boundary, + "x", &x, + "y", &y, + "width", &w, + "height", &h, + NULL); + + if (lx != (gint) x || + ly != (gint) y || + lw != (gint) w || + lh != (gint) h) + { + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (boundary)); + + g_object_set (boundary, + "x", (gdouble) lx, + "y", (gdouble) ly, + "width", (gdouble) lw, + "height", (gdouble) lh, + NULL); + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (boundary)); + } + } +} diff --git a/app/display/gimpcanvascanvasboundary.h b/app/display/gimpcanvascanvasboundary.h new file mode 100644 index 0000000..c8fe16f --- /dev/null +++ b/app/display/gimpcanvascanvasboundary.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascanvasvboundary.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_CANVAS_CANVAS_BOUNDARY_H__ +#define __GIMP_CANVAS_CANVAS_BOUNDARY_H__ + + +#include "gimpcanvasrectangle.h" + + +#define GIMP_TYPE_CANVAS_CANVAS_BOUNDARY (gimp_canvas_canvas_boundary_get_type ()) +#define GIMP_CANVAS_CANVAS_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_CANVAS_BOUNDARY, GimpCanvasCanvasBoundary)) +#define GIMP_CANVAS_CANVAS_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_CANVAS_BOUNDARY, GimpCanvasCanvasBoundaryClass)) +#define GIMP_IS_CANVAS_CANVAS_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_CANVAS_BOUNDARY)) +#define GIMP_IS_CANVAS_CANVAS_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_CANVAS_BOUNDARY)) +#define GIMP_CANVAS_CANVAS_BOUNDARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_CANVAS_BOUNDARY, GimpCanvasCanvasBoundaryClass)) + + +typedef struct _GimpCanvasCanvasBoundary GimpCanvasCanvasBoundary; +typedef struct _GimpCanvasCanvasBoundaryClass GimpCanvasCanvasBoundaryClass; + +struct _GimpCanvasCanvasBoundary +{ + GimpCanvasRectangle parent_instance; +}; + +struct _GimpCanvasCanvasBoundaryClass +{ + GimpCanvasRectangleClass parent_class; +}; + + +GType gimp_canvas_canvas_boundary_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_canvas_boundary_new (GimpDisplayShell *shell); + +void gimp_canvas_canvas_boundary_set_image (GimpCanvasCanvasBoundary *boundary, + GimpImage *image); + + +#endif /* __GIMP_CANVAS_CANVAS_BOUNDARY_H__ */ diff --git a/app/display/gimpcanvascorner.c b/app/display/gimpcanvascorner.c new file mode 100644 index 0000000..50aebfb --- /dev/null +++ b/app/display/gimpcanvascorner.c @@ -0,0 +1,468 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascorner.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvascorner.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_ANCHOR, + PROP_CORNER_WIDTH, + PROP_CORNER_HEIGHT, + PROP_OUTSIDE +}; + + +typedef struct _GimpCanvasCornerPrivate GimpCanvasCornerPrivate; + +struct _GimpCanvasCornerPrivate +{ + gdouble x; + gdouble y; + gdouble width; + gdouble height; + GimpHandleAnchor anchor; + gint corner_width; + gint corner_height; + gboolean outside; +}; + +#define GET_PRIVATE(corner) \ + ((GimpCanvasCornerPrivate *) gimp_canvas_corner_get_instance_private ((GimpCanvasCorner *) (corner))) + + +/* local function prototypes */ + +static void gimp_canvas_corner_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_corner_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_corner_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_corner_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasCorner, gimp_canvas_corner, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_corner_parent_class + + +static void +gimp_canvas_corner_class_init (GimpCanvasCornerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_corner_set_property; + object_class->get_property = gimp_canvas_corner_get_property; + + item_class->draw = gimp_canvas_corner_draw; + item_class->get_extents = gimp_canvas_corner_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_double ("width", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_double ("height", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_ANCHOR, + g_param_spec_enum ("anchor", NULL, NULL, + GIMP_TYPE_HANDLE_ANCHOR, + GIMP_HANDLE_ANCHOR_CENTER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CORNER_WIDTH, + g_param_spec_int ("corner-width", NULL, NULL, + 3, GIMP_MAX_IMAGE_SIZE, 3, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CORNER_HEIGHT, + g_param_spec_int ("corner-height", NULL, NULL, + 3, GIMP_MAX_IMAGE_SIZE, 3, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_OUTSIDE, + g_param_spec_boolean ("outside", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_corner_init (GimpCanvasCorner *corner) +{ +} + +static void +gimp_canvas_corner_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCornerPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_WIDTH: + private->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_double (value); + break; + case PROP_ANCHOR: + private->anchor = g_value_get_enum (value); + break; + case PROP_CORNER_WIDTH: + private->corner_width = g_value_get_int (value); + break; + case PROP_CORNER_HEIGHT: + private->corner_height = g_value_get_int (value); + break; + case PROP_OUTSIDE: + private->outside = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_corner_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCornerPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_WIDTH: + g_value_set_double (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, private->height); + break; + case PROP_ANCHOR: + g_value_set_enum (value, private->anchor); + break; + case PROP_CORNER_WIDTH: + g_value_set_int (value, private->corner_width); + break; + case PROP_CORNER_HEIGHT: + g_value_set_int (value, private->corner_height); + break; + case PROP_OUTSIDE: + g_value_set_boolean (value, private->outside); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_corner_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y, + gdouble *w, + gdouble *h) +{ + GimpCanvasCornerPrivate *private = GET_PRIVATE (item); + gdouble rx, ry; + gdouble rw, rh; + gint top_and_bottom_handle_x_offset; + gint left_and_right_handle_y_offset; + + gimp_canvas_item_transform_xy_f (item, + MIN (private->x, + private->x + private->width), + MIN (private->y, + private->y + private->height), + &rx, &ry); + gimp_canvas_item_transform_xy_f (item, + MAX (private->x, + private->x + private->width), + MAX (private->y, + private->y + private->height), + &rw, &rh); + + rw -= rx; + rh -= ry; + + rx = floor (rx) + 0.5; + ry = floor (ry) + 0.5; + rw = ceil (rw) - 1.0; + rh = ceil (rh) - 1.0; + + top_and_bottom_handle_x_offset = (rw - private->corner_width) / 2; + left_and_right_handle_y_offset = (rh - private->corner_height) / 2; + + *w = private->corner_width; + *h = private->corner_height; + + switch (private->anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + if (private->outside) + { + *x = rx - private->corner_width; + *y = ry - private->corner_height; + } + else + { + *x = rx; + *y = ry; + } + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + if (private->outside) + { + *x = rx + rw; + *y = ry - private->corner_height; + } + else + { + *x = rx + rw - private->corner_width; + *y = ry; + } + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + if (private->outside) + { + *x = rx - private->corner_width; + *y = ry + rh; + } + else + { + *x = rx; + *y = ry + rh - private->corner_height; + } + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + if (private->outside) + { + *x = rx + rw; + *y = ry + rh; + } + else + { + *x = rx + rw - private->corner_width; + *y = ry + rh - private->corner_height; + } + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + if (private->outside) + { + *x = rx; + *y = ry - private->corner_height; + *w = rw; + } + else + { + *x = rx + top_and_bottom_handle_x_offset; + *y = ry; + } + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + if (private->outside) + { + *x = rx; + *y = ry + rh; + *w = rw; + } + else + { + *x = rx + top_and_bottom_handle_x_offset; + *y = ry + rh - private->corner_height; + } + break; + + case GIMP_HANDLE_ANCHOR_WEST: + if (private->outside) + { + *x = rx - private->corner_width; + *y = ry; + *h = rh; + } + else + { + *x = rx; + *y = ry + left_and_right_handle_y_offset; + } + break; + + case GIMP_HANDLE_ANCHOR_EAST: + if (private->outside) + { + *x = rx + rw; + *y = ry; + *h = rh; + } + else + { + *x = rx + rw - private->corner_width; + *y = ry + left_and_right_handle_y_offset; + } + break; + } +} + +static void +gimp_canvas_corner_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + gdouble x, y; + gdouble w, h; + + gimp_canvas_corner_transform (item, &x, &y, &w, &h); + + cairo_rectangle (cr, x, y, w, h); + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_corner_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + gdouble x, y; + gdouble w, h; + + gimp_canvas_corner_transform (item, &x, &y, &w, &h); + + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 1.5); + rectangle.width = ceil (w + 3.0); + rectangle.height = ceil (h + 3.0); + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_corner_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpHandleAnchor anchor, + gint corner_width, + gint corner_height, + gboolean outside) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_CORNER, + "shell", shell, + "x", x, + "y", y, + "width", width, + "height", height, + "anchor", anchor, + "corner-width", corner_width, + "corner-height", corner_height, + "outside", outside, + NULL); +} + +void +gimp_canvas_corner_set (GimpCanvasItem *corner, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gint corner_width, + gint corner_height, + gboolean outside) +{ + g_return_if_fail (GIMP_IS_CANVAS_CORNER (corner)); + + gimp_canvas_item_begin_change (corner); + g_object_set (corner, + "x", x, + "y", y, + "width", width, + "height", height, + "corner-width", corner_width, + "corner-height", corner_height, + "outside", outside, + NULL); + gimp_canvas_item_end_change (corner); +} diff --git a/app/display/gimpcanvascorner.h b/app/display/gimpcanvascorner.h new file mode 100644 index 0000000..47cc53b --- /dev/null +++ b/app/display/gimpcanvascorner.h @@ -0,0 +1,72 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascorner.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_CORNER_H__ +#define __GIMP_CANVAS_CORNER_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_CORNER (gimp_canvas_corner_get_type ()) +#define GIMP_CANVAS_CORNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_CORNER, GimpCanvasCorner)) +#define GIMP_CANVAS_CORNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_CORNER, GimpCanvasCornerClass)) +#define GIMP_IS_CANVAS_CORNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_CORNER)) +#define GIMP_IS_CANVAS_CORNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_CORNER)) +#define GIMP_CANVAS_CORNER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_CORNER, GimpCanvasCornerClass)) + + +typedef struct _GimpCanvasCorner GimpCanvasCorner; +typedef struct _GimpCanvasCornerClass GimpCanvasCornerClass; + +struct _GimpCanvasCorner +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasCornerClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_corner_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_corner_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpHandleAnchor anchor, + gint corner_width, + gint corner_height, + gboolean outside); + +void gimp_canvas_corner_set (GimpCanvasItem *corner, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gint corner_width, + gint corner_height, + gboolean outside); + + +#endif /* __GIMP_CANVAS_CORNER_H__ */ diff --git a/app/display/gimpcanvascursor.c b/app/display/gimpcanvascursor.c new file mode 100644 index 0000000..581fa76 --- /dev/null +++ b/app/display/gimpcanvascursor.c @@ -0,0 +1,228 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascursor.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" + +#include "gimpcanvascursor.h" +#include "gimpdisplayshell.h" + + +#define GIMP_CURSOR_SIZE 7 + + +enum +{ + PROP_0, + PROP_X, + PROP_Y +}; + + +typedef struct _GimpCanvasCursorPrivate GimpCanvasCursorPrivate; + +struct _GimpCanvasCursorPrivate +{ + gdouble x; + gdouble y; +}; + +#define GET_PRIVATE(cursor) \ + ((GimpCanvasCursorPrivate *) gimp_canvas_cursor_get_instance_private ((GimpCanvasCursor *) (cursor))) + + +/* local function prototypes */ + +static void gimp_canvas_cursor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_cursor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_cursor_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_cursor_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasCursor, gimp_canvas_cursor, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_cursor_parent_class + + +static void +gimp_canvas_cursor_class_init (GimpCanvasCursorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_cursor_set_property; + object_class->get_property = gimp_canvas_cursor_get_property; + + item_class->draw = gimp_canvas_cursor_draw; + item_class->get_extents = gimp_canvas_cursor_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_cursor_init (GimpCanvasCursor *cursor) +{ + gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (cursor), + CAIRO_LINE_CAP_SQUARE); +} + +static void +gimp_canvas_cursor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCursorPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_cursor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasCursorPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_cursor_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasCursorPrivate *private = GET_PRIVATE (item); + gdouble x, y; + + x = floor (private->x) + 0.5; + y = floor (private->y) + 0.5; + + cairo_move_to (cr, x - GIMP_CURSOR_SIZE, y); + cairo_line_to (cr, x + GIMP_CURSOR_SIZE, y); + + cairo_move_to (cr, x, y - GIMP_CURSOR_SIZE); + cairo_line_to (cr, x, y + GIMP_CURSOR_SIZE); + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_cursor_get_extents (GimpCanvasItem *item) +{ + GimpCanvasCursorPrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + gdouble x, y; + + x = floor (private->x) + 0.5; + y = floor (private->y) + 0.5; + + rectangle.x = floor (x - GIMP_CURSOR_SIZE - 1.5); + rectangle.y = floor (y - GIMP_CURSOR_SIZE - 1.5); + rectangle.width = ceil (x + GIMP_CURSOR_SIZE + 1.5) - rectangle.x; + rectangle.height = ceil (y + GIMP_CURSOR_SIZE + 1.5) - rectangle.y; + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_cursor_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_CURSOR, + "shell", shell, + NULL); +} + +void +gimp_canvas_cursor_set (GimpCanvasItem *cursor, + gdouble x, + gdouble y) +{ + GimpCanvasCursorPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_CURSOR (cursor)); + + private = GET_PRIVATE (cursor); + + if (private->x != x || private->y != y) + { + gimp_canvas_item_begin_change (cursor); + + g_object_set (cursor, + "x", x, + "y", y, + NULL); + + gimp_canvas_item_end_change (cursor); + } +} diff --git a/app/display/gimpcanvascursor.h b/app/display/gimpcanvascursor.h new file mode 100644 index 0000000..b518563 --- /dev/null +++ b/app/display/gimpcanvascursor.h @@ -0,0 +1,59 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvascursor.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_CURSOR_H__ +#define __GIMP_CANVAS_CURSOR_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_CURSOR (gimp_canvas_cursor_get_type ()) +#define GIMP_CANVAS_CURSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_CURSOR, GimpCanvasCursor)) +#define GIMP_CANVAS_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_CURSOR, GimpCanvasCursorClass)) +#define GIMP_IS_CANVAS_CURSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_CURSOR)) +#define GIMP_IS_CANVAS_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_CURSOR)) +#define GIMP_CANVAS_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_CURSOR, GimpCanvasCursorClass)) + + +typedef struct _GimpCanvasCursor GimpCanvasCursor; +typedef struct _GimpCanvasCursorClass GimpCanvasCursorClass; + +struct _GimpCanvasCursor +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasCursorClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_cursor_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_cursor_new (GimpDisplayShell *shell); + +void gimp_canvas_cursor_set (GimpCanvasItem *cursor, + gdouble x, + gdouble y); + + +#endif /* __GIMP_CANVAS_CURSOR_H__ */ diff --git a/app/display/gimpcanvasgrid.c b/app/display/gimpcanvasgrid.c new file mode 100644 index 0000000..23b46ee --- /dev/null +++ b/app/display/gimpcanvasgrid.c @@ -0,0 +1,414 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasgrid.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "display-types.h" + +#include "core/gimpgrid.h" +#include "core/gimpimage.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvasgrid.h" +#include "gimpcanvasitem-utils.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scale.h" + + +enum +{ + PROP_0, + PROP_GRID, + PROP_GRID_STYLE +}; + + +typedef struct _GimpCanvasGridPrivate GimpCanvasGridPrivate; + +struct _GimpCanvasGridPrivate +{ + GimpGrid *grid; + gboolean grid_style; +}; + +#define GET_PRIVATE(grid) \ + ((GimpCanvasGridPrivate *) gimp_canvas_grid_get_instance_private ((GimpCanvasGrid *) (grid))) + + +/* local function prototypes */ + +static void gimp_canvas_grid_finalize (GObject *object); +static void gimp_canvas_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_grid_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_grid_get_extents (GimpCanvasItem *item); +static void gimp_canvas_grid_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasGrid, gimp_canvas_grid, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_grid_parent_class + + +static void +gimp_canvas_grid_class_init (GimpCanvasGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_grid_finalize; + object_class->set_property = gimp_canvas_grid_set_property; + object_class->get_property = gimp_canvas_grid_get_property; + + item_class->draw = gimp_canvas_grid_draw; + item_class->get_extents = gimp_canvas_grid_get_extents; + item_class->stroke = gimp_canvas_grid_stroke; + + g_object_class_install_property (object_class, PROP_GRID, + g_param_spec_object ("grid", NULL, NULL, + GIMP_TYPE_GRID, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_GRID_STYLE, + g_param_spec_boolean ("grid-style", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_grid_init (GimpCanvasGrid *grid) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (grid); + + private->grid = g_object_new (GIMP_TYPE_GRID, NULL); +} + +static void +gimp_canvas_grid_finalize (GObject *object) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->grid); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GRID: + { + GimpGrid *grid = g_value_get_object (value); + if (grid) + gimp_config_sync (G_OBJECT (grid), G_OBJECT (private->grid), 0); + } + break; + case PROP_GRID_STYLE: + private->grid_style = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_GRID: + g_value_set_object (value, private->grid); + break; + case PROP_GRID_STYLE: + g_value_set_boolean (value, private->grid_style); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_grid_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (item); + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + gdouble xspacing, yspacing; + gdouble xoffset, yoffset; + gboolean vert, horz; + gdouble dx1, dy1, dx2, dy2; + gint x1, y1, x2, y2; + gdouble dx, dy; + gint x, y; + +#define CROSSHAIR 2 + + gimp_grid_get_spacing (private->grid, &xspacing, &yspacing); + gimp_grid_get_offset (private->grid, &xoffset, &yoffset); + + g_return_if_fail (xspacing >= 0.0 && + yspacing >= 0.0); + + xspacing *= shell->scale_x; + yspacing *= shell->scale_y; + + xoffset *= shell->scale_x; + yoffset *= shell->scale_y; + + /* skip grid drawing when the space between grid lines starts + * disappearing, see bug #599267. + */ + vert = (xspacing >= 2.0); + horz = (yspacing >= 2.0); + + if (! vert && ! horz) + return; + + cairo_clip_extents (cr, &dx1, &dy1, &dx2, &dy2); + + x1 = floor (dx1) - 1; + y1 = floor (dy1) - 1; + x2 = ceil (dx2) + 1; + y2 = ceil (dy2) + 1; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + GeglRectangle bounds; + + gimp_display_shell_scale_get_image_unrotated_bounds ( + shell, + &bounds.x, &bounds.y, &bounds.width, &bounds.height); + + if (! gegl_rectangle_intersect (&bounds, + &bounds, + GEGL_RECTANGLE (x1, y1, + x2 - x1, y2 - y1))) + { + return; + } + + x1 = bounds.x; + y1 = bounds.y; + x2 = bounds.x + bounds.width; + y2 = bounds.y + bounds.height; + } + + switch (gimp_grid_get_style (private->grid)) + { + case GIMP_GRID_INTERSECTIONS: + x1 -= CROSSHAIR; + y1 -= CROSSHAIR; + x2 += CROSSHAIR; + y2 += CROSSHAIR; + break; + + case GIMP_GRID_DOTS: + case GIMP_GRID_ON_OFF_DASH: + case GIMP_GRID_DOUBLE_DASH: + case GIMP_GRID_SOLID: + break; + } + + xoffset = fmod (xoffset - shell->offset_x - x1, xspacing); + yoffset = fmod (yoffset - shell->offset_y - y1, yspacing); + + if (xoffset < 0.0) + xoffset += xspacing; + + if (yoffset < 0.0) + yoffset += yspacing; + + switch (gimp_grid_get_style (private->grid)) + { + case GIMP_GRID_DOTS: + if (vert && horz) + { + for (dx = x1 + xoffset; dx <= x2; dx += xspacing) + { + x = RINT (dx); + + for (dy = y1 + yoffset; dy <= y2; dy += yspacing) + { + y = RINT (dy); + + cairo_move_to (cr, x, y + 0.5); + cairo_line_to (cr, x + 1.0, y + 0.5); + } + } + } + break; + + case GIMP_GRID_INTERSECTIONS: + if (vert && horz) + { + for (dx = x1 + xoffset; dx <= x2; dx += xspacing) + { + x = RINT (dx); + + for (dy = y1 + yoffset; dy <= y2; dy += yspacing) + { + y = RINT (dy); + + cairo_move_to (cr, x + 0.5, y - CROSSHAIR); + cairo_line_to (cr, x + 0.5, y + CROSSHAIR + 1.0); + + cairo_move_to (cr, x - CROSSHAIR, y + 0.5); + cairo_line_to (cr, x + CROSSHAIR + 1.0, y + 0.5); + } + } + } + break; + + case GIMP_GRID_ON_OFF_DASH: + case GIMP_GRID_DOUBLE_DASH: + case GIMP_GRID_SOLID: + if (vert) + { + for (dx = x1 + xoffset; dx < x2; dx += xspacing) + { + x = RINT (dx); + + cairo_move_to (cr, x + 0.5, y1); + cairo_line_to (cr, x + 0.5, y2); + } + } + + if (horz) + { + for (dy = y1 + yoffset; dy < y2; dy += yspacing) + { + y = RINT (dy); + + cairo_move_to (cr, x1, y + 0.5); + cairo_line_to (cr, x2, y + 0.5); + } + } + break; + } + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_grid_get_extents (GimpCanvasItem *item) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + GimpImage *image = gimp_canvas_item_get_image (item); + cairo_rectangle_int_t rectangle; + + if (! image) + return NULL; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + + gdouble x1, y1; + gdouble x2, y2; + gint w, h; + + w = gimp_image_get_width (image); + h = gimp_image_get_height (image); + + gimp_canvas_item_transform_xy_f (item, 0, 0, &x1, &y1); + gimp_canvas_item_transform_xy_f (item, w, h, &x2, &y2); + + rectangle.x = floor (x1); + rectangle.y = floor (y1); + rectangle.width = ceil (x2) - rectangle.x; + rectangle.height = ceil (y2) - rectangle.y; + } + else + { + gimp_canvas_item_untransform_viewport (item, + &rectangle.x, + &rectangle.y, + &rectangle.width, + &rectangle.height); + } + + return cairo_region_create_rectangle (&rectangle); +} + +static void +gimp_canvas_grid_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasGridPrivate *private = GET_PRIVATE (item); + + if (private->grid_style) + { + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + + gimp_canvas_set_grid_style (gimp_canvas_item_get_canvas (item), cr, + private->grid, + shell->offset_x, shell->offset_y); + cairo_stroke (cr); + } + else + { + GIMP_CANVAS_ITEM_CLASS (parent_class)->stroke (item, cr); + } +} + +GimpCanvasItem * +gimp_canvas_grid_new (GimpDisplayShell *shell, + GimpGrid *grid) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (grid == NULL || GIMP_IS_GRID (grid), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_GRID, + "shell", shell, + "grid", grid, + NULL); +} diff --git a/app/display/gimpcanvasgrid.h b/app/display/gimpcanvasgrid.h new file mode 100644 index 0000000..391d2ea --- /dev/null +++ b/app/display/gimpcanvasgrid.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasgrid.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_GRID_H__ +#define __GIMP_CANVAS_GRID_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_GRID (gimp_canvas_grid_get_type ()) +#define GIMP_CANVAS_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_GRID, GimpCanvasGrid)) +#define GIMP_CANVAS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_GRID, GimpCanvasGridClass)) +#define GIMP_IS_CANVAS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_GRID)) +#define GIMP_IS_CANVAS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_GRID)) +#define GIMP_CANVAS_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_GRID, GimpCanvasGridClass)) + + +typedef struct _GimpCanvasGrid GimpCanvasGrid; +typedef struct _GimpCanvasGridClass GimpCanvasGridClass; + +struct _GimpCanvasGrid +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasGridClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_grid_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_grid_new (GimpDisplayShell *shell, + GimpGrid *grid); + + +#endif /* __GIMP_CANVAS_GRID_H__ */ diff --git a/app/display/gimpcanvasgroup.c b/app/display/gimpcanvasgroup.c new file mode 100644 index 0000000..13d00ff --- /dev/null +++ b/app/display/gimpcanvasgroup.c @@ -0,0 +1,387 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasgroup.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvasgroup.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_GROUP_STROKING, + PROP_GROUP_FILLING +}; + + +struct _GimpCanvasGroupPrivate +{ + GQueue *items; + gboolean group_stroking; + gboolean group_filling; +}; + + +/* local function prototypes */ + +static void gimp_canvas_group_finalize (GObject *object); +static void gimp_canvas_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_group_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_group_get_extents (GimpCanvasItem *item); +static gboolean gimp_canvas_group_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + +static void gimp_canvas_group_child_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpCanvasGroup *group); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasGroup, gimp_canvas_group, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_group_parent_class + + +static void +gimp_canvas_group_class_init (GimpCanvasGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_group_finalize; + object_class->set_property = gimp_canvas_group_set_property; + object_class->get_property = gimp_canvas_group_get_property; + + item_class->draw = gimp_canvas_group_draw; + item_class->get_extents = gimp_canvas_group_get_extents; + item_class->hit = gimp_canvas_group_hit; + + g_object_class_install_property (object_class, PROP_GROUP_STROKING, + g_param_spec_boolean ("group-stroking", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_GROUP_FILLING, + g_param_spec_boolean ("group-filling", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_group_init (GimpCanvasGroup *group) +{ + group->priv = gimp_canvas_group_get_instance_private (group); + + group->priv->items = g_queue_new (); +} + +static void +gimp_canvas_group_finalize (GObject *object) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (object); + GimpCanvasItem *item; + + while ((item = g_queue_peek_head (group->priv->items))) + gimp_canvas_group_remove_item (group, item); + + g_queue_free (group->priv->items); + group->priv->items = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (object); + + switch (property_id) + { + case PROP_GROUP_STROKING: + group->priv->group_stroking = g_value_get_boolean (value); + break; + case PROP_GROUP_FILLING: + group->priv->group_filling = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (object); + + switch (property_id) + { + case PROP_GROUP_STROKING: + g_value_set_boolean (value, group->priv->group_stroking); + break; + case PROP_GROUP_FILLING: + g_value_set_boolean (value, group->priv->group_filling); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_group_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (item); + GList *list; + + for (list = group->priv->items->head; list; list = g_list_next (list)) + { + GimpCanvasItem *sub_item = list->data; + + gimp_canvas_item_draw (sub_item, cr); + } + + if (group->priv->group_stroking) + _gimp_canvas_item_stroke (item, cr); + + if (group->priv->group_filling) + _gimp_canvas_item_fill (item, cr); +} + +static cairo_region_t * +gimp_canvas_group_get_extents (GimpCanvasItem *item) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (item); + cairo_region_t *region = NULL; + GList *list; + + for (list = group->priv->items->head; list; list = g_list_next (list)) + { + GimpCanvasItem *sub_item = list->data; + cairo_region_t *sub_region = gimp_canvas_item_get_extents (sub_item); + + if (! region) + { + region = sub_region; + } + else if (sub_region) + { + cairo_region_union (region, sub_region); + cairo_region_destroy (sub_region); + } + } + + return region; +} + +static gboolean +gimp_canvas_group_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + GimpCanvasGroup *group = GIMP_CANVAS_GROUP (item); + GList *list; + + for (list = group->priv->items->head; list; list = g_list_next (list)) + { + if (gimp_canvas_item_hit (list->data, x, y)) + return TRUE; + } + + return FALSE; +} + +static void +gimp_canvas_group_child_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpCanvasGroup *group) +{ + if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group))) + _gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region); +} + + +/* public functions */ + +GimpCanvasItem * +gimp_canvas_group_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_GROUP, + "shell", shell, + NULL); +} + +void +gimp_canvas_group_add_item (GimpCanvasGroup *group, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + g_return_if_fail (GIMP_CANVAS_ITEM (group) != item); + + if (group->priv->group_stroking) + gimp_canvas_item_suspend_stroking (item); + + if (group->priv->group_filling) + gimp_canvas_item_suspend_filling (item); + + g_queue_push_tail (group->priv->items, g_object_ref (item)); + + if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group))) + { + cairo_region_t *region = gimp_canvas_item_get_extents (item); + + if (region) + { + _gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region); + cairo_region_destroy (region); + } + } + + g_signal_connect (item, "update", + G_CALLBACK (gimp_canvas_group_child_update), + group); +} + +void +gimp_canvas_group_remove_item (GimpCanvasGroup *group, + GimpCanvasItem *item) +{ + GList *list; + + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + list = g_queue_find (group->priv->items, item); + + g_return_if_fail (list != NULL); + + g_queue_delete_link (group->priv->items, list); + + if (group->priv->group_stroking) + gimp_canvas_item_resume_stroking (item); + + if (group->priv->group_filling) + gimp_canvas_item_resume_filling (item); + + if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group))) + { + cairo_region_t *region = gimp_canvas_item_get_extents (item); + + if (region) + { + _gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region); + cairo_region_destroy (region); + } + } + + g_signal_handlers_disconnect_by_func (item, + gimp_canvas_group_child_update, + group); + + g_object_unref (item); +} + +void +gimp_canvas_group_set_group_stroking (GimpCanvasGroup *group, + gboolean group_stroking) +{ + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + + if (group->priv->group_stroking != group_stroking) + { + GList *list; + + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (group)); + + g_object_set (group, + "group-stroking", group_stroking ? TRUE : FALSE, + NULL); + + for (list = group->priv->items->head; list; list = g_list_next (list)) + { + if (group->priv->group_stroking) + gimp_canvas_item_suspend_stroking (list->data); + else + gimp_canvas_item_resume_stroking (list->data); + } + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (group)); + } +} + +void +gimp_canvas_group_set_group_filling (GimpCanvasGroup *group, + gboolean group_filling) +{ + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + + if (group->priv->group_filling != group_filling) + { + GList *list; + + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (group)); + + g_object_set (group, + "group-filling", group_filling ? TRUE : FALSE, + NULL); + + for (list = group->priv->items->head; list; list = g_list_next (list)) + { + if (group->priv->group_filling) + gimp_canvas_item_suspend_filling (list->data); + else + gimp_canvas_item_resume_filling (list->data); + } + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (group)); + } +} diff --git a/app/display/gimpcanvasgroup.h b/app/display/gimpcanvasgroup.h new file mode 100644 index 0000000..d2fb9cc --- /dev/null +++ b/app/display/gimpcanvasgroup.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasgroup.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_GROUP_H__ +#define __GIMP_CANVAS_GROUP_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_GROUP (gimp_canvas_group_get_type ()) +#define GIMP_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_GROUP, GimpCanvasGroup)) +#define GIMP_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_GROUP, GimpCanvasGroupClass)) +#define GIMP_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_GROUP)) +#define GIMP_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_GROUP)) +#define GIMP_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_GROUP, GimpCanvasGroupClass)) + + +typedef struct _GimpCanvasGroupPrivate GimpCanvasGroupPrivate; +typedef struct _GimpCanvasGroupClass GimpCanvasGroupClass; + +struct _GimpCanvasGroup +{ + GimpCanvasItem parent_instance; + + GimpCanvasGroupPrivate *priv; + +}; + +struct _GimpCanvasGroupClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_group_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_group_new (GimpDisplayShell *shell); + +void gimp_canvas_group_add_item (GimpCanvasGroup *group, + GimpCanvasItem *item); +void gimp_canvas_group_remove_item (GimpCanvasGroup *group, + GimpCanvasItem *item); + +void gimp_canvas_group_set_group_stroking (GimpCanvasGroup *group, + gboolean group_stroking); +void gimp_canvas_group_set_group_filling (GimpCanvasGroup *group, + gboolean group_filling); + + +#endif /* __GIMP_CANVAS_GROUP_H__ */ diff --git a/app/display/gimpcanvasguide.c b/app/display/gimpcanvasguide.c new file mode 100644 index 0000000..5712633 --- /dev/null +++ b/app/display/gimpcanvasguide.c @@ -0,0 +1,295 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasguide.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvasguide.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_POSITION, + PROP_STYLE +}; + + +typedef struct _GimpCanvasGuidePrivate GimpCanvasGuidePrivate; + +struct _GimpCanvasGuidePrivate +{ + GimpOrientationType orientation; + gint position; + + GimpGuideStyle style; +}; + +#define GET_PRIVATE(guide) \ + ((GimpCanvasGuidePrivate *) gimp_canvas_guide_get_instance_private ((GimpCanvasGuide *) (guide))) + + +/* local function prototypes */ + +static void gimp_canvas_guide_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_guide_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_guide_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_guide_get_extents (GimpCanvasItem *item); +static void gimp_canvas_guide_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasGuide, gimp_canvas_guide, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_guide_parent_class + + +static void +gimp_canvas_guide_class_init (GimpCanvasGuideClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_guide_set_property; + object_class->get_property = gimp_canvas_guide_get_property; + + item_class->draw = gimp_canvas_guide_draw; + item_class->get_extents = gimp_canvas_guide_get_extents; + item_class->stroke = gimp_canvas_guide_stroke; + + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", NULL, NULL, + GIMP_TYPE_ORIENTATION_TYPE, + GIMP_ORIENTATION_HORIZONTAL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_POSITION, + g_param_spec_int ("position", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_STYLE, + g_param_spec_enum ("style", NULL, NULL, + GIMP_TYPE_GUIDE_STYLE, + GIMP_GUIDE_STYLE_NONE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_guide_init (GimpCanvasGuide *guide) +{ +} + +static void +gimp_canvas_guide_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGuidePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_ORIENTATION: + private->orientation = g_value_get_enum (value); + break; + case PROP_POSITION: + private->position = g_value_get_int (value); + break; + case PROP_STYLE: + private->style = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_guide_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasGuidePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, private->orientation); + break; + case PROP_POSITION: + g_value_set_int (value, private->position); + break; + case PROP_STYLE: + g_value_set_enum (value, private->style); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_guide_transform (GimpCanvasItem *item, + gdouble *x1, + gdouble *y1, + gdouble *x2, + gdouble *y2) +{ + GimpCanvasGuidePrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + GtkAllocation allocation; + gint max_outside; + gint x, y; + + gtk_widget_get_allocation (canvas, &allocation); + + max_outside = allocation.width + allocation.height; + + *x1 = -max_outside; + *y1 = -max_outside; + *x2 = allocation.width + max_outside; + *y2 = allocation.height + max_outside; + + switch (private->orientation) + { + case GIMP_ORIENTATION_HORIZONTAL: + gimp_canvas_item_transform_xy (item, 0, private->position, &x, &y); + *y1 = *y2 = y + 0.5; + break; + + case GIMP_ORIENTATION_VERTICAL: + gimp_canvas_item_transform_xy (item, private->position, 0, &x, &y); + *x1 = *x2 = x + 0.5; + break; + + case GIMP_ORIENTATION_UNKNOWN: + return; + } +} + +static void +gimp_canvas_guide_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_guide_transform (item, &x1, &y1, &x2, &y2); + + cairo_move_to (cr, x1, y1); + cairo_line_to (cr, x2, y2); + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_guide_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_guide_transform (item, &x1, &y1, &x2, &y2); + + rectangle.x = MIN (x1, x2) - 1.5; + rectangle.y = MIN (y1, y2) - 1.5; + rectangle.width = ABS (x2 - x1) + 3.0; + rectangle.height = ABS (y2 - y1) + 3.0; + + return cairo_region_create_rectangle (&rectangle); +} + +static void +gimp_canvas_guide_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasGuidePrivate *private = GET_PRIVATE (item); + + if (private->style != GIMP_GUIDE_STYLE_NONE) + { + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + + gimp_canvas_set_guide_style (gimp_canvas_item_get_canvas (item), cr, + private->style, + gimp_canvas_item_get_highlight (item), + shell->offset_x, shell->offset_y); + cairo_stroke (cr); + } + else + { + GIMP_CANVAS_ITEM_CLASS (parent_class)->stroke (item, cr); + } +} + +GimpCanvasItem * +gimp_canvas_guide_new (GimpDisplayShell *shell, + GimpOrientationType orientation, + gint position, + GimpGuideStyle style) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_GUIDE, + "shell", shell, + "orientation", orientation, + "position", position, + "style", style, + NULL); +} + +void +gimp_canvas_guide_set (GimpCanvasItem *guide, + GimpOrientationType orientation, + gint position) +{ + g_return_if_fail (GIMP_IS_CANVAS_GUIDE (guide)); + + gimp_canvas_item_begin_change (guide); + + g_object_set (guide, + "orientation", orientation, + "position", position, + NULL); + + gimp_canvas_item_end_change (guide); +} diff --git a/app/display/gimpcanvasguide.h b/app/display/gimpcanvasguide.h new file mode 100644 index 0000000..54b1696 --- /dev/null +++ b/app/display/gimpcanvasguide.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasguide.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_GUIDE_H__ +#define __GIMP_CANVAS_GUIDE_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_GUIDE (gimp_canvas_guide_get_type ()) +#define GIMP_CANVAS_GUIDE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_GUIDE, GimpCanvasGuide)) +#define GIMP_CANVAS_GUIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_GUIDE, GimpCanvasGuideClass)) +#define GIMP_IS_CANVAS_GUIDE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_GUIDE)) +#define GIMP_IS_CANVAS_GUIDE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_GUIDE)) +#define GIMP_CANVAS_GUIDE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_GUIDE, GimpCanvasGuideClass)) + + +typedef struct _GimpCanvasGuide GimpCanvasGuide; +typedef struct _GimpCanvasGuideClass GimpCanvasGuideClass; + +struct _GimpCanvasGuide +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasGuideClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_guide_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_guide_new (GimpDisplayShell *shell, + GimpOrientationType orientation, + gint position, + GimpGuideStyle style); + +void gimp_canvas_guide_set (GimpCanvasItem *guide, + GimpOrientationType orientation, + gint position); + + +#endif /* __GIMP_CANVAS_GUIDE_H__ */ diff --git a/app/display/gimpcanvashandle.c b/app/display/gimpcanvashandle.c new file mode 100644 index 0000000..954eead --- /dev/null +++ b/app/display/gimpcanvashandle.c @@ -0,0 +1,703 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvashandle.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvasitem-utils.h" +#include "gimpdisplayshell.h" + + +#define N_DASHES 8 +#define DASH_ON_RATIO 0.3 +#define DASH_OFF_RATIO 0.7 + + +enum +{ + PROP_0, + PROP_TYPE, + PROP_ANCHOR, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_START_ANGLE, + PROP_SLICE_ANGLE +}; + + +typedef struct _GimpCanvasHandlePrivate GimpCanvasHandlePrivate; + +struct _GimpCanvasHandlePrivate +{ + GimpHandleType type; + GimpHandleAnchor anchor; + gdouble x; + gdouble y; + gint width; + gint height; + gdouble start_angle; + gdouble slice_angle; +}; + +#define GET_PRIVATE(handle) \ + ((GimpCanvasHandlePrivate *) gimp_canvas_handle_get_instance_private ((GimpCanvasHandle *) (handle))) + + +/* local function prototypes */ + +static void gimp_canvas_handle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_handle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_handle_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_handle_get_extents (GimpCanvasItem *item); +static gboolean gimp_canvas_handle_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasHandle, gimp_canvas_handle, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_handle_parent_class + + +static void +gimp_canvas_handle_class_init (GimpCanvasHandleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_handle_set_property; + object_class->get_property = gimp_canvas_handle_get_property; + + item_class->draw = gimp_canvas_handle_draw; + item_class->get_extents = gimp_canvas_handle_get_extents; + item_class->hit = gimp_canvas_handle_hit; + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_HANDLE_TYPE, + GIMP_HANDLE_CROSS, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_ANCHOR, + g_param_spec_enum ("anchor", NULL, NULL, + GIMP_TYPE_HANDLE_ANCHOR, + GIMP_HANDLE_ANCHOR_CENTER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + 3, 1001, 7, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", NULL, NULL, + 3, 1001, 7, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_START_ANGLE, + g_param_spec_double ("start-angle", NULL, NULL, + -1000, 1000, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SLICE_ANGLE, + g_param_spec_double ("slice-angle", NULL, NULL, + -1000, 1000, 2 * G_PI, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_handle_init (GimpCanvasHandle *handle) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (handle); + + gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (handle), + CAIRO_LINE_CAP_SQUARE); + + private->start_angle = 0.0; + private->slice_angle = 2.0 * G_PI; +} + +static void +gimp_canvas_handle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + private->type = g_value_get_enum (value); + break; + case PROP_ANCHOR: + private->anchor = g_value_get_enum (value); + break; + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_WIDTH: + private->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_int (value); + break; + case PROP_START_ANGLE: + private->start_angle = g_value_get_double (value); + break; + case PROP_SLICE_ANGLE: + private->slice_angle = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_handle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, private->type); + break; + case PROP_ANCHOR: + g_value_set_enum (value, private->anchor); + break; + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, private->height); + break; + case PROP_START_ANGLE: + g_value_set_double (value, private->start_angle); + break; + case PROP_SLICE_ANGLE: + g_value_set_double (value, private->slice_angle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_handle_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (item); + + gimp_canvas_item_transform_xy_f (item, + private->x, private->y, + x, y); + + switch (private->type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_DASHED_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + gimp_canvas_item_shift_to_north_west (private->anchor, + *x, *y, + private->width, + private->height, + x, y); + break; + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_DASHED_CIRCLE: + case GIMP_HANDLE_FILLED_CIRCLE: + case GIMP_HANDLE_CROSS: + case GIMP_HANDLE_CROSSHAIR: + case GIMP_HANDLE_DIAMOND: + case GIMP_HANDLE_DASHED_DIAMOND: + case GIMP_HANDLE_FILLED_DIAMOND: + gimp_canvas_item_shift_to_center (private->anchor, + *x, *y, + private->width, + private->height, + x, y); + break; + + default: + break; + } + + *x = floor (*x) + 0.5; + *y = floor (*y) + 0.5; +} + +static void +gimp_canvas_handle_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (item); + gdouble x, y, tx, ty; + + gimp_canvas_handle_transform (item, &x, &y); + + gimp_canvas_item_transform_xy_f (item, + private->x, private->y, + &tx, &ty); + + tx = floor (tx) + 0.5; + ty = floor (ty) + 0.5; + + switch (private->type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_DASHED_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + case GIMP_HANDLE_DIAMOND: + case GIMP_HANDLE_DASHED_DIAMOND: + case GIMP_HANDLE_FILLED_DIAMOND: + case GIMP_HANDLE_CROSS: + cairo_save (cr); + cairo_translate (cr, tx, ty); + cairo_rotate (cr, private->start_angle); + cairo_translate (cr, -tx, -ty); + + switch (private->type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_DASHED_SQUARE: + if (private->type == GIMP_HANDLE_DASHED_SQUARE) + { + gdouble circ; + gdouble dashes[2]; + + cairo_save (cr); + + circ = 2.0 * ((private->width - 1.0) + (private->height - 1.0)); + + dashes[0] = (circ / N_DASHES) * DASH_ON_RATIO; + dashes[1] = (circ / N_DASHES) * DASH_OFF_RATIO; + + cairo_set_dash (cr, dashes, 2, dashes[0] / 2.0); + } + + cairo_rectangle (cr, x, y, private->width - 1.0, private->height - 1.0); + _gimp_canvas_item_stroke (item, cr); + + if (private->type == GIMP_HANDLE_DASHED_SQUARE) + cairo_restore (cr); + break; + + case GIMP_HANDLE_FILLED_SQUARE: + cairo_rectangle (cr, x - 0.5, y - 0.5, private->width, private->height); + _gimp_canvas_item_fill (item, cr); + break; + + case GIMP_HANDLE_DIAMOND: + case GIMP_HANDLE_DASHED_DIAMOND: + case GIMP_HANDLE_FILLED_DIAMOND: + if (private->type == GIMP_HANDLE_DASHED_DIAMOND) + { + gdouble circ; + gdouble dashes[2]; + + cairo_save (cr); + + circ = 4.0 * hypot ((gdouble) private->width / 2.0, + (gdouble) private->height / 2.0); + + dashes[0] = (circ / N_DASHES) * DASH_ON_RATIO; + dashes[1] = (circ / N_DASHES) * DASH_OFF_RATIO; + + cairo_set_dash (cr, dashes, 2, dashes[0] / 2.0); + } + + cairo_move_to (cr, x, y - (gdouble) private->height / 2.0); + cairo_line_to (cr, x + (gdouble) private->width / 2.0, y); + cairo_line_to (cr, x, y + (gdouble) private->height / 2.0); + cairo_line_to (cr, x - (gdouble) private->width / 2.0, y); + cairo_line_to (cr, x, y - (gdouble) private->height / 2.0); + if (private->type == GIMP_HANDLE_DIAMOND || + private->type == GIMP_HANDLE_DASHED_DIAMOND) + _gimp_canvas_item_stroke (item, cr); + else + _gimp_canvas_item_fill (item, cr); + + if (private->type == GIMP_HANDLE_DASHED_SQUARE) + cairo_restore (cr); + break; + + case GIMP_HANDLE_CROSS: + cairo_move_to (cr, x - private->width / 2.0, y); + cairo_line_to (cr, x + private->width / 2.0 - 0.5, y); + cairo_move_to (cr, x, y - private->height / 2.0); + cairo_line_to (cr, x, y + private->height / 2.0 - 0.5); + _gimp_canvas_item_stroke (item, cr); + break; + + default: + gimp_assert_not_reached (); + } + + cairo_restore (cr); + break; + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_DASHED_CIRCLE: + if (private->type == GIMP_HANDLE_DASHED_CIRCLE) + { + gdouble circ; + gdouble dashes[2]; + + cairo_save (cr); + + circ = 2.0 * G_PI * (private->width / 2.0); + + dashes[0] = (circ / N_DASHES) * DASH_ON_RATIO; + dashes[1] = (circ / N_DASHES) * DASH_OFF_RATIO; + + cairo_set_dash (cr, dashes, 2, dashes[0] / 2.0); + } + + gimp_cairo_arc (cr, x, y, private->width / 2.0, + private->start_angle, + private->slice_angle); + + _gimp_canvas_item_stroke (item, cr); + + if (private->type == GIMP_HANDLE_DASHED_CIRCLE) + cairo_restore (cr); + break; + + case GIMP_HANDLE_FILLED_CIRCLE: + cairo_move_to (cr, x, y); + + gimp_cairo_arc (cr, x, y, (gdouble) private->width / 2.0, + private->start_angle, + private->slice_angle); + + _gimp_canvas_item_fill (item, cr); + break; + + case GIMP_HANDLE_CROSSHAIR: + cairo_move_to (cr, x - private->width / 2.0, y); + cairo_line_to (cr, x - private->width * 0.4, y); + + cairo_move_to (cr, x + private->width / 2.0 - 0.5, y); + cairo_line_to (cr, x + private->width * 0.4, y); + + cairo_move_to (cr, x, y - private->height / 2.0); + cairo_line_to (cr, x, y - private->height * 0.4 - 0.5); + + cairo_move_to (cr, x, y + private->height / 2.0 - 0.5); + cairo_line_to (cr, x, y + private->height * 0.4 - 0.5); + + _gimp_canvas_item_stroke (item, cr); + break; + + default: + break; + } +} + +static cairo_region_t * +gimp_canvas_handle_get_extents (GimpCanvasItem *item) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + gdouble x, y; + gdouble w, h; + + gimp_canvas_handle_transform (item, &x, &y); + + switch (private->type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_DASHED_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + w = private->width * (sqrt(2) - 1) / 2; + h = private->height * (sqrt(2) - 1) / 2; + rectangle.x = x - 1.5 - w; + rectangle.y = y - 1.5 - h; + rectangle.width = private->width + 3.0 + w * 2; + rectangle.height = private->height + 3.0 + h * 2; + break; + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_DASHED_CIRCLE: + case GIMP_HANDLE_FILLED_CIRCLE: + case GIMP_HANDLE_CROSS: + case GIMP_HANDLE_CROSSHAIR: + case GIMP_HANDLE_DIAMOND: + case GIMP_HANDLE_DASHED_DIAMOND: + case GIMP_HANDLE_FILLED_DIAMOND: + rectangle.x = x - private->width / 2.0 - 2.0; + rectangle.y = y - private->height / 2.0 - 2.0; + rectangle.width = private->width + 4.0; + rectangle.height = private->height + 4.0; + break; + + default: + break; + } + + return cairo_region_create_rectangle (&rectangle); +} + +static gboolean +gimp_canvas_handle_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + GimpCanvasHandlePrivate *private = GET_PRIVATE (item); + gdouble handle_tx, handle_ty; + gdouble mx, my, tx, ty, mmx, mmy; + gdouble diamond_offset_x = 0.0; + gdouble diamond_offset_y = 0.0; + gdouble angle = -private->start_angle; + + gimp_canvas_handle_transform (item, &handle_tx, &handle_ty); + + gimp_canvas_item_transform_xy_f (item, + x, y, + &mx, &my); + + switch (private->type) + { + case GIMP_HANDLE_DIAMOND: + case GIMP_HANDLE_DASHED_DIAMOND: + case GIMP_HANDLE_FILLED_DIAMOND: + angle -= G_PI / 4.0; + diamond_offset_x = private->width / 2.0; + diamond_offset_y = private->height / 2.0; + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_DASHED_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + gimp_canvas_item_transform_xy_f (item, + private->x, private->y, + &tx, &ty); + mmx = mx - tx; mmy = my - ty; + mx = cos (angle) * mmx - sin (angle) * mmy + tx + diamond_offset_x; + my = sin (angle) * mmx + cos (angle) * mmy + ty + diamond_offset_y; + return mx > handle_tx && mx < handle_tx + private->width && + my > handle_ty && my < handle_ty + private->height; + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_DASHED_CIRCLE: + case GIMP_HANDLE_FILLED_CIRCLE: + case GIMP_HANDLE_CROSS: + case GIMP_HANDLE_CROSSHAIR: + { + gint width = private->width; + + if (width != private->height) + width = (width + private->height) / 2; + + width /= 2; + + return ((SQR (handle_tx - mx) + SQR (handle_ty - my)) < SQR (width)); + } + + default: + break; + } + + return FALSE; +} + +GimpCanvasItem * +gimp_canvas_handle_new (GimpDisplayShell *shell, + GimpHandleType type, + GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_HANDLE, + "shell", shell, + "type", type, + "anchor", anchor, + "x", x, + "y", y, + "width", width, + "height", height, + NULL); +} + +void +gimp_canvas_handle_get_position (GimpCanvasItem *handle, + gdouble *x, + gdouble *y) +{ + g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle)); + g_return_if_fail (x != NULL); + g_return_if_fail (y != NULL); + + g_object_get (handle, + "x", x, + "y", y, + NULL); +} + +void +gimp_canvas_handle_set_position (GimpCanvasItem *handle, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle)); + + gimp_canvas_item_begin_change (handle); + + g_object_set (handle, + "x", x, + "y", y, + NULL); + + gimp_canvas_item_end_change (handle); +} + +gint +gimp_canvas_handle_calc_size (GimpCanvasItem *item, + gdouble mouse_x, + gdouble mouse_y, + gint normal_size, + gint hover_size) +{ + gdouble x, y; + gdouble distance; + gdouble size; + gint full_threshold_sq = SQR (hover_size / 2) * 9; + gint partial_threshold_sq = full_threshold_sq * 5; + + g_return_val_if_fail (GIMP_IS_CANVAS_HANDLE (item), normal_size); + + gimp_canvas_handle_get_position (item, &x, &y); + distance = gimp_canvas_item_transform_distance_square (item, + mouse_x, + mouse_y, + x, y); + + /* calculate the handle size based on distance from the cursor */ + size = (1.0 - (distance - full_threshold_sq) / + (partial_threshold_sq - full_threshold_sq)); + + size = CLAMP (size, 0.0, 1.0); + + return (gint) CLAMP ((size * hover_size), + normal_size, + hover_size); +} + +void +gimp_canvas_handle_get_size (GimpCanvasItem *handle, + gint *width, + gint *height) +{ + g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle)); + g_return_if_fail (width != NULL); + g_return_if_fail (height != NULL); + + g_object_get (handle, + "width", width, + "height", height, + NULL); +} + +void +gimp_canvas_handle_set_size (GimpCanvasItem *handle, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle)); + + gimp_canvas_item_begin_change (handle); + + g_object_set (handle, + "width", width, + "height", height, + NULL); + + gimp_canvas_item_end_change (handle); +} + +void +gimp_canvas_handle_set_angles (GimpCanvasItem *handle, + gdouble start_angle, + gdouble slice_angle) +{ + g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle)); + + gimp_canvas_item_begin_change (handle); + + g_object_set (handle, + "start-angle", start_angle, + "slice-angle", slice_angle, + NULL); + + gimp_canvas_item_end_change (handle); +} diff --git a/app/display/gimpcanvashandle.h b/app/display/gimpcanvashandle.h new file mode 100644 index 0000000..0dea957 --- /dev/null +++ b/app/display/gimpcanvashandle.h @@ -0,0 +1,92 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvashandle.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_HANDLE_H__ +#define __GIMP_CANVAS_HANDLE_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_CANVAS_HANDLE_SIZE_CIRCLE 13 +#define GIMP_CANVAS_HANDLE_SIZE_CROSS 15 +#define GIMP_CANVAS_HANDLE_SIZE_CROSSHAIR 43 +#define GIMP_CANVAS_HANDLE_SIZE_LARGE 25 +#define GIMP_CANVAS_HANDLE_SIZE_SMALL 7 + + +#define GIMP_TYPE_CANVAS_HANDLE (gimp_canvas_handle_get_type ()) +#define GIMP_CANVAS_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_HANDLE, GimpCanvasHandle)) +#define GIMP_CANVAS_HANDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_HANDLE, GimpCanvasHandleClass)) +#define GIMP_IS_CANVAS_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_HANDLE)) +#define GIMP_IS_CANVAS_HANDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_HANDLE)) +#define GIMP_CANVAS_HANDLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_HANDLE, GimpCanvasHandleClass)) + + +typedef struct _GimpCanvasHandle GimpCanvasHandle; +typedef struct _GimpCanvasHandleClass GimpCanvasHandleClass; + +struct _GimpCanvasHandle +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasHandleClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_handle_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_handle_new (GimpDisplayShell *shell, + GimpHandleType type, + GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height); + +void gimp_canvas_handle_get_position (GimpCanvasItem *handle, + gdouble *x, + gdouble *y); +void gimp_canvas_handle_set_position (GimpCanvasItem *handle, + gdouble x, + gdouble y); + +gint gimp_canvas_handle_calc_size (GimpCanvasItem *item, + gdouble mouse_x, + gdouble mouse_y, + gint normal_size, + gint hover_size); + +void gimp_canvas_handle_get_size (GimpCanvasItem *handle, + gint *width, + gint *height); +void gimp_canvas_handle_set_size (GimpCanvasItem *handle, + gint width, + gint height); + +void gimp_canvas_handle_set_angles (GimpCanvasItem *handle, + gdouble start_handle, + gdouble slice_handle); + + +#endif /* __GIMP_CANVAS_HANDLE_H__ */ diff --git a/app/display/gimpcanvasitem-utils.c b/app/display/gimpcanvasitem-utils.c new file mode 100644 index 0000000..4c23592 --- /dev/null +++ b/app/display/gimpcanvasitem-utils.c @@ -0,0 +1,474 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasitem-utils.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "vectors/gimpanchor.h" +#include "vectors/gimpbezierstroke.h" +#include "vectors/gimpvectors.h" + +#include "gimpcanvasitem.h" +#include "gimpcanvasitem-utils.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" + + +gboolean +gimp_canvas_item_on_handle (GimpCanvasItem *item, + gdouble x, + gdouble y, + GimpHandleType type, + gdouble handle_x, + gdouble handle_y, + gint width, + gint height, + GimpHandleAnchor anchor) +{ + GimpDisplayShell *shell; + gdouble tx, ty; + gdouble handle_tx, handle_ty; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + + shell = gimp_canvas_item_get_shell (item); + + gimp_display_shell_zoom_xy_f (shell, + x, y, + &tx, &ty); + gimp_display_shell_zoom_xy_f (shell, + handle_x, handle_y, + &handle_tx, &handle_ty); + + switch (type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + case GIMP_HANDLE_CROSS: + case GIMP_HANDLE_CROSSHAIR: + gimp_canvas_item_shift_to_north_west (anchor, + handle_tx, handle_ty, + width, height, + &handle_tx, &handle_ty); + + return (tx == CLAMP (tx, handle_tx, handle_tx + width) && + ty == CLAMP (ty, handle_ty, handle_ty + height)); + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_FILLED_CIRCLE: + gimp_canvas_item_shift_to_center (anchor, + handle_tx, handle_ty, + width, height, + &handle_tx, &handle_ty); + + /* FIXME */ + if (width != height) + width = (width + height) / 2; + + width /= 2; + + return ((SQR (handle_tx - tx) + SQR (handle_ty - ty)) < SQR (width)); + + default: + g_warning ("%s: invalid handle type %d", G_STRFUNC, type); + break; + } + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors_handle (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpAnchorType preferred, + gboolean exclusive, + GimpAnchor **ret_anchor, + GimpStroke **ret_stroke) +{ + GimpStroke *stroke = NULL; + GimpStroke *pref_stroke = NULL; + GimpAnchor *anchor = NULL; + GimpAnchor *pref_anchor = NULL; + gdouble dx, dy; + gdouble pref_mindist = -1; + gdouble mindist = -1; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (coord != NULL, FALSE); + + if (ret_anchor) *ret_anchor = NULL; + if (ret_stroke) *ret_stroke = NULL; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + GList *anchor_list; + GList *list; + + anchor_list = g_list_concat (gimp_stroke_get_draw_anchors (stroke), + gimp_stroke_get_draw_controls (stroke)); + + for (list = anchor_list; list; list = g_list_next (list)) + { + dx = coord->x - GIMP_ANCHOR (list->data)->position.x; + dy = coord->y - GIMP_ANCHOR (list->data)->position.y; + + if (mindist < 0 || mindist > dx * dx + dy * dy) + { + mindist = dx * dx + dy * dy; + anchor = GIMP_ANCHOR (list->data); + + if (ret_stroke) + *ret_stroke = stroke; + } + + if ((pref_mindist < 0 || pref_mindist > dx * dx + dy * dy) && + GIMP_ANCHOR (list->data)->type == preferred) + { + pref_mindist = dx * dx + dy * dy; + pref_anchor = GIMP_ANCHOR (list->data); + pref_stroke = stroke; + } + } + + g_list_free (anchor_list); + } + + /* If the data passed into ret_anchor is a preferred anchor, return it. */ + if (ret_anchor && *ret_anchor && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + (*ret_anchor)->position.x, + (*ret_anchor)->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER) && + (*ret_anchor)->type == preferred) + { + if (ret_stroke) *ret_stroke = pref_stroke; + + return TRUE; + } + + if (pref_anchor && gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + pref_anchor->position.x, + pref_anchor->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + if (ret_anchor) *ret_anchor = pref_anchor; + if (ret_stroke) *ret_stroke = pref_stroke; + + return TRUE; + } + else if (!exclusive && anchor && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + anchor->position.x, + anchor->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + if (ret_anchor) + *ret_anchor = anchor; + + /* *ret_stroke already set correctly. */ + return TRUE; + } + + if (ret_anchor) + *ret_anchor = NULL; + if (ret_stroke) + *ret_stroke = NULL; + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors_curve (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke) +{ + GimpStroke *stroke = NULL; + GimpAnchor *segment_start; + GimpAnchor *segment_end; + GimpCoords min_coords = GIMP_COORDS_DEFAULT_VALUES; + GimpCoords cur_coords; + gdouble min_dist, cur_dist, cur_pos; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (coord != NULL, FALSE); + + if (ret_coords) *ret_coords = *coord; + if (ret_pos) *ret_pos = -1.0; + if (ret_segment_start) *ret_segment_start = NULL; + if (ret_segment_end) *ret_segment_end = NULL; + if (ret_stroke) *ret_stroke = NULL; + + min_dist = -1.0; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + cur_dist = gimp_stroke_nearest_point_get (stroke, coord, 1.0, + &cur_coords, + &segment_start, + &segment_end, + &cur_pos); + + if (cur_dist >= 0 && (min_dist < 0 || cur_dist < min_dist)) + { + min_dist = cur_dist; + min_coords = cur_coords; + + if (ret_coords) *ret_coords = cur_coords; + if (ret_pos) *ret_pos = cur_pos; + if (ret_segment_start) *ret_segment_start = segment_start; + if (ret_segment_end) *ret_segment_end = segment_end; + if (ret_stroke) *ret_stroke = stroke; + } + } + + if (min_dist >= 0 && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + min_coords.x, + min_coords.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + return TRUE; + } + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors (GimpCanvasItem *item, + const GimpCoords *coords, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke, + GimpVectors **ret_vectors) +{ + GimpDisplayShell *shell; + GimpImage *image; + GList *all_vectors; + GList *list; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (coords != NULL, FALSE); + + shell = gimp_canvas_item_get_shell (item); + image = gimp_display_get_image (shell->display); + + if (ret_coords) *ret_coords = *coords; + if (ret_pos) *ret_pos = -1.0; + if (ret_segment_start) *ret_segment_start = NULL; + if (ret_segment_end) *ret_segment_end = NULL; + if (ret_stroke) *ret_stroke = NULL; + if (ret_vectors) *ret_vectors = NULL; + + all_vectors = gimp_image_get_vectors_list (image); + + for (list = all_vectors; list; list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + + if (! gimp_item_get_visible (GIMP_ITEM (vectors))) + continue; + + if (gimp_canvas_item_on_vectors_curve (item, + vectors, coords, + width, height, + ret_coords, + ret_pos, + ret_segment_start, + ret_segment_end, + ret_stroke)) + { + if (ret_vectors) + *ret_vectors = vectors; + + g_list_free (all_vectors); + + return TRUE; + } + } + + g_list_free (all_vectors); + + return FALSE; +} + +void +gimp_canvas_item_shift_to_north_west (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y) +{ + switch (anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + x -= width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + x -= width / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + /* nothing, this is the default */ + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + x -= width; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + x -= width / 2; + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + x -= width; + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + x -= width; + y -= height / 2; + break; + + default: + break; + } + + if (shifted_x) + *shifted_x = x; + + if (shifted_y) + *shifted_y = y; +} + +void +gimp_canvas_item_shift_to_center (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y) +{ + switch (anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + /* nothing, this is the default */ + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + x += width / 2; + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + x -= width / 2; + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + x += width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + x -= width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + x += width / 2; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + x -= width / 2; + break; + + default: + break; + } + + if (shifted_x) + *shifted_x = x; + + if (shifted_y) + *shifted_y = y; +} diff --git a/app/display/gimpcanvasitem-utils.h b/app/display/gimpcanvasitem-utils.h new file mode 100644 index 0000000..240385e --- /dev/null +++ b/app/display/gimpcanvasitem-utils.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasitem-utils.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_ITEM_UTILS_H__ +#define __GIMP_CANVAS_ITEM_UTILS_H__ + + +gboolean gimp_canvas_item_on_handle (GimpCanvasItem *item, + gdouble x, + gdouble y, + GimpHandleType type, + gdouble handle_x, + gdouble handle_y, + gint width, + gint height, + GimpHandleAnchor anchor); + +gboolean gimp_canvas_item_on_vectors_handle (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpAnchorType preferred, + gboolean exclusive, + GimpAnchor **ret_anchor, + GimpStroke **ret_stroke); +gboolean gimp_canvas_item_on_vectors_curve (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke); +gboolean gimp_canvas_item_on_vectors (GimpCanvasItem *item, + const GimpCoords *coords, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke, + GimpVectors **ret_vectors); + +void gimp_canvas_item_shift_to_north_west (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y); +void gimp_canvas_item_shift_to_center (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y); + + +#endif /* __GIMP_CANVAS_ITEM_UTILS_H__ */ diff --git a/app/display/gimpcanvasitem.c b/app/display/gimpcanvasitem.c new file mode 100644 index 0000000..560210a --- /dev/null +++ b/app/display/gimpcanvasitem.c @@ -0,0 +1,731 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasitem.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpmarshal.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvasitem.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" + + +enum +{ + PROP_0, + PROP_SHELL, + PROP_VISIBLE, + PROP_LINE_CAP, + PROP_HIGHLIGHT +}; + +enum +{ + UPDATE, + LAST_SIGNAL +}; + + +struct _GimpCanvasItemPrivate +{ + GimpDisplayShell *shell; + gboolean visible; + cairo_line_cap_t line_cap; + gboolean highlight; + gint suspend_stroking; + gint suspend_filling; + gint change_count; + cairo_region_t *change_region; +}; + + +/* local function prototypes */ + +static void gimp_canvas_item_dispose (GObject *object); +static void gimp_canvas_item_constructed (GObject *object); +static void gimp_canvas_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_item_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs); + +static void gimp_canvas_item_real_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_item_real_get_extents (GimpCanvasItem *item); +static void gimp_canvas_item_real_stroke (GimpCanvasItem *item, + cairo_t *cr); +static void gimp_canvas_item_real_fill (GimpCanvasItem *item, + cairo_t *cr); +static gboolean gimp_canvas_item_real_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasItem, gimp_canvas_item, GIMP_TYPE_OBJECT) + +#define parent_class gimp_canvas_item_parent_class + +static guint item_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_canvas_item_class_init (GimpCanvasItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_canvas_item_dispose; + object_class->constructed = gimp_canvas_item_constructed; + object_class->set_property = gimp_canvas_item_set_property; + object_class->get_property = gimp_canvas_item_get_property; + object_class->dispatch_properties_changed = gimp_canvas_item_dispatch_properties_changed; + + klass->update = NULL; + klass->draw = gimp_canvas_item_real_draw; + klass->get_extents = gimp_canvas_item_real_get_extents; + klass->stroke = gimp_canvas_item_real_stroke; + klass->fill = gimp_canvas_item_real_fill; + klass->hit = gimp_canvas_item_real_hit; + + item_signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpCanvasItemClass, update), + NULL, NULL, + gimp_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + g_object_class_install_property (object_class, PROP_SHELL, + g_param_spec_object ("shell", + NULL, NULL, + GIMP_TYPE_DISPLAY_SHELL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_VISIBLE, + g_param_spec_boolean ("visible", + NULL, NULL, + TRUE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_LINE_CAP, + g_param_spec_int ("line-cap", + NULL, NULL, + CAIRO_LINE_CAP_BUTT, + CAIRO_LINE_CAP_SQUARE, + CAIRO_LINE_CAP_ROUND, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HIGHLIGHT, + g_param_spec_boolean ("highlight", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_item_init (GimpCanvasItem *item) +{ + GimpCanvasItemPrivate *private; + + item->private = gimp_canvas_item_get_instance_private (item); + private = item->private; + + private->shell = NULL; + private->visible = TRUE; + private->line_cap = CAIRO_LINE_CAP_ROUND; + private->highlight = FALSE; + private->suspend_stroking = 0; + private->suspend_filling = 0; + private->change_count = 1; /* avoid emissions during construction */ + private->change_region = NULL; +} + +static void +gimp_canvas_item_constructed (GObject *object) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); + + gimp_assert (GIMP_IS_DISPLAY_SHELL (item->private->shell)); + + item->private->change_count = 0; /* undo hack from init() */ + + G_OBJECT_CLASS (parent_class)->constructed (object); +} + +static void +gimp_canvas_item_dispose (GObject *object) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); + + item->private->change_count++; /* avoid emissions during destruction */ + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_canvas_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); + GimpCanvasItemPrivate *private = item->private; + + switch (property_id) + { + case PROP_SHELL: + private->shell = g_value_get_object (value); /* don't ref */ + break; + case PROP_VISIBLE: + private->visible = g_value_get_boolean (value); + break; + case PROP_LINE_CAP: + private->line_cap = g_value_get_int (value); + break; + case PROP_HIGHLIGHT: + private->highlight = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); + GimpCanvasItemPrivate *private = item->private; + + switch (property_id) + { + case PROP_SHELL: + g_value_set_object (value, private->shell); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, private->visible); + break; + case PROP_LINE_CAP: + g_value_set_int (value, private->line_cap); + break; + case PROP_HIGHLIGHT: + g_value_set_boolean (value, private->highlight); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_item_dispatch_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); + + G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, + n_pspecs, + pspecs); + + if (_gimp_canvas_item_needs_update (item)) + { + cairo_region_t *region = gimp_canvas_item_get_extents (item); + + if (region) + { + g_signal_emit (object, item_signals[UPDATE], 0, + region); + cairo_region_destroy (region); + } + } +} + +static void +gimp_canvas_item_real_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + g_warn_if_reached (); +} + +static cairo_region_t * +gimp_canvas_item_real_get_extents (GimpCanvasItem *item) +{ + return NULL; +} + +static void +gimp_canvas_item_real_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + cairo_set_line_cap (cr, item->private->line_cap); + + gimp_canvas_set_tool_bg_style (gimp_canvas_item_get_canvas (item), cr); + cairo_stroke_preserve (cr); + + gimp_canvas_set_tool_fg_style (gimp_canvas_item_get_canvas (item), cr, + item->private->highlight); + cairo_stroke (cr); +} + +static void +gimp_canvas_item_real_fill (GimpCanvasItem *item, + cairo_t *cr) +{ + gimp_canvas_set_tool_bg_style (gimp_canvas_item_get_canvas (item), cr); + cairo_set_line_width (cr, 2.0); + cairo_stroke_preserve (cr); + + gimp_canvas_set_tool_fg_style (gimp_canvas_item_get_canvas (item), cr, + item->private->highlight); + cairo_fill (cr); +} + +static gboolean +gimp_canvas_item_real_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + return FALSE; +} + + +/* public functions */ + +GimpDisplayShell * +gimp_canvas_item_get_shell (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), NULL); + + return item->private->shell; +} + +GimpImage * +gimp_canvas_item_get_image (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), NULL); + + return gimp_display_get_image (item->private->shell->display); +} + +GtkWidget * +gimp_canvas_item_get_canvas (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), NULL); + + return item->private->shell->canvas; +} + +void +gimp_canvas_item_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + g_return_if_fail (cr != NULL); + + if (item->private->visible) + { + cairo_save (cr); + GIMP_CANVAS_ITEM_GET_CLASS (item)->draw (item, cr); + cairo_restore (cr); + } +} + +cairo_region_t * +gimp_canvas_item_get_extents (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), NULL); + + if (item->private->visible) + return GIMP_CANVAS_ITEM_GET_CLASS (item)->get_extents (item); + + return NULL; +} + +gboolean +gimp_canvas_item_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + + if (item->private->visible) + return GIMP_CANVAS_ITEM_GET_CLASS (item)->hit (item, x, y); + + return FALSE; +} + +void +gimp_canvas_item_set_visible (GimpCanvasItem *item, + gboolean visible) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + if (item->private->visible != visible) + { + gimp_canvas_item_begin_change (item); + g_object_set (G_OBJECT (item), + "visible", visible, + NULL); + gimp_canvas_item_end_change (item); + } +} + +gboolean +gimp_canvas_item_get_visible (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + + return item->private->visible; +} + +void +gimp_canvas_item_set_line_cap (GimpCanvasItem *item, + cairo_line_cap_t line_cap) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + if (item->private->line_cap != line_cap) + { + gimp_canvas_item_begin_change (item); + g_object_set (G_OBJECT (item), + "line-cap", line_cap, + NULL); + gimp_canvas_item_end_change (item); + } +} + +void +gimp_canvas_item_set_highlight (GimpCanvasItem *item, + gboolean highlight) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + if (item->private->highlight != highlight) + { + g_object_set (G_OBJECT (item), + "highlight", highlight, + NULL); + } +} + +gboolean +gimp_canvas_item_get_highlight (GimpCanvasItem *item) +{ + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + + return item->private->highlight; +} + +void +gimp_canvas_item_begin_change (GimpCanvasItem *item) +{ + GimpCanvasItemPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + private = item->private; + + private->change_count++; + + if (private->change_count == 1 && + g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE)) + { + private->change_region = gimp_canvas_item_get_extents (item); + } +} + +void +gimp_canvas_item_end_change (GimpCanvasItem *item) +{ + GimpCanvasItemPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + private = item->private; + + g_return_if_fail (private->change_count > 0); + + private->change_count--; + + if (private->change_count == 0) + { + if (g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE)) + { + cairo_region_t *region = gimp_canvas_item_get_extents (item); + + if (! region) + { + region = private->change_region; + } + else if (private->change_region) + { + cairo_region_union (region, private->change_region); + cairo_region_destroy (private->change_region); + } + + private->change_region = NULL; + + if (region) + { + g_signal_emit (item, item_signals[UPDATE], 0, + region); + cairo_region_destroy (region); + } + } + else + { + g_clear_pointer (&private->change_region, cairo_region_destroy); + } + } +} + +void +gimp_canvas_item_suspend_stroking (GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + item->private->suspend_stroking++; +} + +void +gimp_canvas_item_resume_stroking (GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + g_return_if_fail (item->private->suspend_stroking > 0); + + item->private->suspend_stroking--; +} + +void +gimp_canvas_item_suspend_filling (GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + item->private->suspend_filling++; +} + +void +gimp_canvas_item_resume_filling (GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + g_return_if_fail (item->private->suspend_filling > 0); + + item->private->suspend_filling--; +} + +void +gimp_canvas_item_transform (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasItemPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + g_return_if_fail (cr != NULL); + + private = item->private; + + cairo_translate (cr, -private->shell->offset_x, -private->shell->offset_y); + cairo_scale (cr, private->shell->scale_x, private->shell->scale_y); +} + +void +gimp_canvas_item_transform_xy (GimpCanvasItem *item, + gdouble x, + gdouble y, + gint *tx, + gint *ty) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_display_shell_zoom_xy (item->private->shell, x, y, tx, ty); +} + +void +gimp_canvas_item_transform_xy_f (GimpCanvasItem *item, + gdouble x, + gdouble y, + gdouble *tx, + gdouble *ty) +{ + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_display_shell_zoom_xy_f (item->private->shell, x, y, tx, ty); +} + +/** + * gimp_canvas_item_transform_distance: + * @item: a #GimpCanvasItem + * @x1: start point X in image coordinates + * @y1: start point Y in image coordinates + * @x2: end point X in image coordinates + * @y2: end point Y in image coordinates + * + * If you just need to compare distances, consider to use + * gimp_canvas_item_transform_distance_square() instead. + * + * Returns: the distance between the given points in display coordinates + **/ +gdouble +gimp_canvas_item_transform_distance (GimpCanvasItem *item, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + return sqrt (gimp_canvas_item_transform_distance_square (item, + x1, y1, x2, y2)); +} + +/** + * gimp_canvas_item_transform_distance_square: + * @item: a #GimpCanvasItem + * @x1: start point X in image coordinates + * @y1: start point Y in image coordinates + * @x2: end point X in image coordinates + * @y2: end point Y in image coordinates + * + * This function is more effective than + * gimp_canvas_item_transform_distance() as it doesn't perform a + * sqrt(). Use this if you just need to compare distances. + * + * Returns: the square of the distance between the given points in + * display coordinates + **/ +gdouble +gimp_canvas_item_transform_distance_square (GimpCanvasItem *item, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + gdouble tx1, ty1; + gdouble tx2, ty2; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), 0.0); + + gimp_canvas_item_transform_xy_f (item, x1, y1, &tx1, &ty1); + gimp_canvas_item_transform_xy_f (item, x2, y2, &tx2, &ty2); + + return SQR (tx2 - tx1) + SQR (ty2 - ty1); +} + +void +gimp_canvas_item_untransform_viewport (GimpCanvasItem *item, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GimpDisplayShell *shell; + gdouble x1, y1; + gdouble x2, y2; + + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + shell = item->private->shell; + + gimp_display_shell_unrotate_bounds (shell, + 0.0, 0.0, + shell->disp_width, shell->disp_height, + &x1, &y1, + &x2, &y2); + + *x = floor (x1); + *y = floor (y1); + *w = ceil (x2) - *x; + *h = ceil (y2) - *y; +} + + +/* protected functions */ + +void +_gimp_canvas_item_update (GimpCanvasItem *item, + cairo_region_t *region) +{ + g_signal_emit (item, item_signals[UPDATE], 0, + region); +} + +gboolean +_gimp_canvas_item_needs_update (GimpCanvasItem *item) +{ + return (item->private->change_count == 0 && + g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE)); +} + +void +_gimp_canvas_item_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + if (item->private->suspend_filling > 0) + g_warning ("_gimp_canvas_item_stroke() on an item that is in a filling group"); + + if (item->private->suspend_stroking == 0) + { + GIMP_CANVAS_ITEM_GET_CLASS (item)->stroke (item, cr); + } + else + { + cairo_new_sub_path (cr); + } +} + +void +_gimp_canvas_item_fill (GimpCanvasItem *item, + cairo_t *cr) +{ + if (item->private->suspend_stroking > 0) + g_warning ("_gimp_canvas_item_fill() on an item that is in a stroking group"); + + if (item->private->suspend_filling == 0) + { + GIMP_CANVAS_ITEM_GET_CLASS (item)->fill (item, cr); + } + else + { + cairo_new_sub_path (cr); + } +} diff --git a/app/display/gimpcanvasitem.h b/app/display/gimpcanvasitem.h new file mode 100644 index 0000000..2004260 --- /dev/null +++ b/app/display/gimpcanvasitem.h @@ -0,0 +1,147 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasitem.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_ITEM_H__ +#define __GIMP_CANVAS_ITEM_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_CANVAS_ITEM (gimp_canvas_item_get_type ()) +#define GIMP_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_ITEM, GimpCanvasItem)) +#define GIMP_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_ITEM, GimpCanvasItemClass)) +#define GIMP_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_ITEM)) +#define GIMP_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_ITEM)) +#define GIMP_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_ITEM, GimpCanvasItemClass)) + + +typedef struct _GimpCanvasItemPrivate GimpCanvasItemPrivate; +typedef struct _GimpCanvasItemClass GimpCanvasItemClass; + +struct _GimpCanvasItem +{ + GimpObject parent_instance; + + GimpCanvasItemPrivate *private; +}; + +struct _GimpCanvasItemClass +{ + GimpObjectClass parent_class; + + /* signals */ + void (* update) (GimpCanvasItem *item, + cairo_region_t *region); + + /* virtual functions */ + void (* draw) (GimpCanvasItem *item, + cairo_t *cr); + cairo_region_t * (* get_extents) (GimpCanvasItem *item); + + void (* stroke) (GimpCanvasItem *item, + cairo_t *cr); + void (* fill) (GimpCanvasItem *item, + cairo_t *cr); + + gboolean (* hit) (GimpCanvasItem *item, + gdouble x, + gdouble y); +}; + + +GType gimp_canvas_item_get_type (void) G_GNUC_CONST; + +GimpDisplayShell * gimp_canvas_item_get_shell (GimpCanvasItem *item); +GimpImage * gimp_canvas_item_get_image (GimpCanvasItem *item); +GtkWidget * gimp_canvas_item_get_canvas (GimpCanvasItem *item); + +void gimp_canvas_item_draw (GimpCanvasItem *item, + cairo_t *cr); +cairo_region_t * gimp_canvas_item_get_extents (GimpCanvasItem *item); + +gboolean gimp_canvas_item_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + +void gimp_canvas_item_set_visible (GimpCanvasItem *item, + gboolean visible); +gboolean gimp_canvas_item_get_visible (GimpCanvasItem *item); + +void gimp_canvas_item_set_line_cap (GimpCanvasItem *item, + cairo_line_cap_t line_cap); + +void gimp_canvas_item_set_highlight (GimpCanvasItem *item, + gboolean highlight); +gboolean gimp_canvas_item_get_highlight (GimpCanvasItem *item); + +void gimp_canvas_item_begin_change (GimpCanvasItem *item); +void gimp_canvas_item_end_change (GimpCanvasItem *item); + +void gimp_canvas_item_suspend_stroking (GimpCanvasItem *item); +void gimp_canvas_item_resume_stroking (GimpCanvasItem *item); + +void gimp_canvas_item_suspend_filling (GimpCanvasItem *item); +void gimp_canvas_item_resume_filling (GimpCanvasItem *item); + +void gimp_canvas_item_transform (GimpCanvasItem *item, + cairo_t *cr); +void gimp_canvas_item_transform_xy (GimpCanvasItem *item, + gdouble x, + gdouble y, + gint *tx, + gint *ty); +void gimp_canvas_item_transform_xy_f (GimpCanvasItem *item, + gdouble x, + gdouble y, + gdouble *tx, + gdouble *ty); +gdouble gimp_canvas_item_transform_distance + (GimpCanvasItem *item, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); +gdouble gimp_canvas_item_transform_distance_square + (GimpCanvasItem *item, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); +void gimp_canvas_item_untransform_viewport + (GimpCanvasItem *item, + gint *x, + gint *y, + gint *w, + gint *h); + + +/* protected */ + +void _gimp_canvas_item_update (GimpCanvasItem *item, + cairo_region_t *region); +gboolean _gimp_canvas_item_needs_update (GimpCanvasItem *item); +void _gimp_canvas_item_stroke (GimpCanvasItem *item, + cairo_t *cr); +void _gimp_canvas_item_fill (GimpCanvasItem *item, + cairo_t *cr); + + +#endif /* __GIMP_CANVAS_ITEM_H__ */ diff --git a/app/display/gimpcanvaslayerboundary.c b/app/display/gimpcanvaslayerboundary.c new file mode 100644 index 0000000..0e4b43b --- /dev/null +++ b/app/display/gimpcanvaslayerboundary.c @@ -0,0 +1,322 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaslayerboundary.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpchannel.h" +#include "core/gimplayer.h" +#include "core/gimplayer-floating-selection.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvaslayerboundary.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_LAYER, + PROP_EDIT_MASK +}; + + +typedef struct _GimpCanvasLayerBoundaryPrivate GimpCanvasLayerBoundaryPrivate; + +struct _GimpCanvasLayerBoundaryPrivate +{ + GimpLayer *layer; + gboolean edit_mask; +}; + +#define GET_PRIVATE(layer_boundary) \ + ((GimpCanvasLayerBoundaryPrivate *) gimp_canvas_layer_boundary_get_instance_private ((GimpCanvasLayerBoundary *) (layer_boundary))) + + +/* local function prototypes */ + +static void gimp_canvas_layer_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_layer_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_layer_boundary_finalize (GObject *object); +static void gimp_canvas_layer_boundary_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_layer_boundary_get_extents (GimpCanvasItem *item); +static void gimp_canvas_layer_boundary_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasLayerBoundary, gimp_canvas_layer_boundary, + GIMP_TYPE_CANVAS_RECTANGLE) + +#define parent_class gimp_canvas_layer_boundary_parent_class + + +static void +gimp_canvas_layer_boundary_class_init (GimpCanvasLayerBoundaryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_layer_boundary_set_property; + object_class->get_property = gimp_canvas_layer_boundary_get_property; + object_class->finalize = gimp_canvas_layer_boundary_finalize; + + item_class->draw = gimp_canvas_layer_boundary_draw; + item_class->get_extents = gimp_canvas_layer_boundary_get_extents; + item_class->stroke = gimp_canvas_layer_boundary_stroke; + + g_object_class_install_property (object_class, PROP_LAYER, + g_param_spec_object ("layer", NULL, NULL, + GIMP_TYPE_LAYER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_EDIT_MASK, + g_param_spec_boolean ("edit-mask", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_layer_boundary_init (GimpCanvasLayerBoundary *layer_boundary) +{ +} + +static void +gimp_canvas_layer_boundary_finalize (GObject *object) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (object); + + if (private->layer) + g_object_remove_weak_pointer (G_OBJECT (private->layer), + (gpointer) &private->layer); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_layer_boundary_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_LAYER: + if (private->layer) + g_object_remove_weak_pointer (G_OBJECT (private->layer), + (gpointer) &private->layer); + private->layer = g_value_get_object (value); /* don't ref */ + if (private->layer) + g_object_add_weak_pointer (G_OBJECT (private->layer), + (gpointer) &private->layer); + break; + case PROP_EDIT_MASK: + private->edit_mask = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_layer_boundary_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_LAYER: + g_value_set_object (value, private->layer); + break; + case PROP_EDIT_MASK: + g_value_set_boolean (value, private->edit_mask); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_layer_boundary_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (item); + + if (private->layer) + GIMP_CANVAS_ITEM_CLASS (parent_class)->draw (item, cr); +} + +static cairo_region_t * +gimp_canvas_layer_boundary_get_extents (GimpCanvasItem *item) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (item); + + if (private->layer) + return GIMP_CANVAS_ITEM_CLASS (parent_class)->get_extents (item); + + return NULL; +} + +static void +gimp_canvas_layer_boundary_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasLayerBoundaryPrivate *private = GET_PRIVATE (item); + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + + gimp_canvas_set_layer_style (gimp_canvas_item_get_canvas (item), cr, + private->layer, + shell->offset_x, shell->offset_y); + cairo_stroke (cr); +} + +GimpCanvasItem * +gimp_canvas_layer_boundary_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_LAYER_BOUNDARY, + "shell", shell, + NULL); +} + +void +gimp_canvas_layer_boundary_set_layer (GimpCanvasLayerBoundary *boundary, + GimpLayer *layer) +{ + GimpCanvasLayerBoundaryPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_LAYER_BOUNDARY (boundary)); + g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer)); + + private = GET_PRIVATE (boundary); + + if (layer && gimp_layer_is_floating_sel (layer)) + { + GimpDrawable *drawable; + + drawable = gimp_layer_get_floating_sel_drawable (layer); + + if (GIMP_IS_CHANNEL (drawable)) + { + /* if the owner drawable is a channel, show no outline */ + + layer = NULL; + } + else + { + /* otherwise, set the layer to the owner drawable */ + + layer = GIMP_LAYER (drawable); + } + } + + if (layer != private->layer) + { + gboolean edit_mask = FALSE; + + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (boundary)); + + if (layer) + { + GimpItem *item = GIMP_ITEM (layer); + + edit_mask = (gimp_layer_get_mask (layer) && + gimp_layer_get_edit_mask (layer)); + + g_object_set (boundary, + "x", (gdouble) gimp_item_get_offset_x (item), + "y", (gdouble) gimp_item_get_offset_y (item), + "width", (gdouble) gimp_item_get_width (item), + "height", (gdouble) gimp_item_get_height (item), + NULL); + } + + g_object_set (boundary, + "layer", layer, + "edit-mask", edit_mask, + NULL); + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (boundary)); + } + else if (layer && layer == private->layer) + { + GimpItem *item = GIMP_ITEM (layer); + gint lx, ly, lw, lh; + gdouble x, y, w ,h; + gboolean edit_mask; + + lx = gimp_item_get_offset_x (item); + ly = gimp_item_get_offset_y (item); + lw = gimp_item_get_width (item); + lh = gimp_item_get_height (item); + + edit_mask = (gimp_layer_get_mask (layer) && + gimp_layer_get_edit_mask (layer)); + + g_object_get (boundary, + "x", &x, + "y", &y, + "width", &w, + "height", &h, + NULL); + + if (lx != (gint) x || + ly != (gint) y || + lw != (gint) w || + lh != (gint) h || + edit_mask != private->edit_mask) + { + gimp_canvas_item_begin_change (GIMP_CANVAS_ITEM (boundary)); + + g_object_set (boundary, + "x", (gdouble) lx, + "y", (gdouble) ly, + "width", (gdouble) lw, + "height", (gdouble) lh, + "edit-mask", edit_mask, + NULL); + + gimp_canvas_item_end_change (GIMP_CANVAS_ITEM (boundary)); + } + } +} diff --git a/app/display/gimpcanvaslayerboundary.h b/app/display/gimpcanvaslayerboundary.h new file mode 100644 index 0000000..5f90131 --- /dev/null +++ b/app/display/gimpcanvaslayerboundary.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaslayerboundary.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_LAYER_BOUNDARY_H__ +#define __GIMP_CANVAS_LAYER_BOUNDARY_H__ + + +#include "gimpcanvasrectangle.h" + + +#define GIMP_TYPE_CANVAS_LAYER_BOUNDARY (gimp_canvas_layer_boundary_get_type ()) +#define GIMP_CANVAS_LAYER_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_LAYER_BOUNDARY, GimpCanvasLayerBoundary)) +#define GIMP_CANVAS_LAYER_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_LAYER_BOUNDARY, GimpCanvasLayerBoundaryClass)) +#define GIMP_IS_CANVAS_LAYER_BOUNDARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_LAYER_BOUNDARY)) +#define GIMP_IS_CANVAS_LAYER_BOUNDARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_LAYER_BOUNDARY)) +#define GIMP_CANVAS_LAYER_BOUNDARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_LAYER_BOUNDARY, GimpCanvasLayerBoundaryClass)) + + +typedef struct _GimpCanvasLayerBoundary GimpCanvasLayerBoundary; +typedef struct _GimpCanvasLayerBoundaryClass GimpCanvasLayerBoundaryClass; + +struct _GimpCanvasLayerBoundary +{ + GimpCanvasRectangle parent_instance; +}; + +struct _GimpCanvasLayerBoundaryClass +{ + GimpCanvasRectangleClass parent_class; +}; + + +GType gimp_canvas_layer_boundary_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_layer_boundary_new (GimpDisplayShell *shell); + +void gimp_canvas_layer_boundary_set_layer (GimpCanvasLayerBoundary *boundary, + GimpLayer *layer); + + +#endif /* __GIMP_CANVAS_LAYER_BOUNDARY_H__ */ diff --git a/app/display/gimpcanvaslimit.c b/app/display/gimpcanvaslimit.c new file mode 100644 index 0000000..05dacbb --- /dev/null +++ b/app/display/gimpcanvaslimit.c @@ -0,0 +1,760 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaslimit.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" + +#include "gimpcanvaslimit.h" +#include "gimpdisplayshell.h" + + +#define DASH_LENGTH 4.0 +#define HIT_DISTANCE 16.0 + + +enum +{ + PROP_0, + PROP_TYPE, + PROP_X, + PROP_Y, + PROP_RADIUS, + PROP_ASPECT_RATIO, + PROP_ANGLE, + PROP_DASHED +}; + + +typedef struct _GimpCanvasLimitPrivate GimpCanvasLimitPrivate; + +struct _GimpCanvasLimitPrivate +{ + GimpLimitType type; + + gdouble x; + gdouble y; + gdouble radius; + gdouble aspect_ratio; + gdouble angle; + + gboolean dashed; +}; + +#define GET_PRIVATE(limit) \ + ((GimpCanvasLimitPrivate *) gimp_canvas_limit_get_instance_private ((GimpCanvasLimit *) (limit))) + + +/* local function prototypes */ + +static void gimp_canvas_limit_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_limit_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_canvas_limit_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_limit_get_extents (GimpCanvasItem *item); +static gboolean gimp_canvas_limit_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasLimit, gimp_canvas_limit, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_limit_parent_class + + +/* private functions */ + +static void +gimp_canvas_limit_class_init (GimpCanvasLimitClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_limit_set_property; + object_class->get_property = gimp_canvas_limit_get_property; + + item_class->draw = gimp_canvas_limit_draw; + item_class->get_extents = gimp_canvas_limit_get_extents; + item_class->hit = gimp_canvas_limit_hit; + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_LIMIT_TYPE, + GIMP_LIMIT_CIRCLE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_RADIUS, + g_param_spec_double ("radius", NULL, NULL, + 0.0, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_ASPECT_RATIO, + g_param_spec_double ("aspect-ratio", NULL, NULL, + -1.0, + +1.0, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_ANGLE, + g_param_spec_double ("angle", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_DASHED, + g_param_spec_boolean ("dashed", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_limit_init (GimpCanvasLimit *limit) +{ +} + +static void +gimp_canvas_limit_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLimitPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + priv->type = g_value_get_enum (value); + break; + + case PROP_X: + priv->x = g_value_get_double (value); + break; + + case PROP_Y: + priv->y = g_value_get_double (value); + break; + + case PROP_RADIUS: + priv->radius = g_value_get_double (value); + break; + + case PROP_ASPECT_RATIO: + priv->aspect_ratio = g_value_get_double (value); + break; + + case PROP_ANGLE: + priv->angle = g_value_get_double (value); + break; + + case PROP_DASHED: + priv->dashed = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_limit_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLimitPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, priv->type); + break; + + case PROP_X: + g_value_set_double (value, priv->x); + break; + + case PROP_Y: + g_value_set_double (value, priv->y); + break; + + case PROP_RADIUS: + g_value_set_double (value, priv->radius); + break; + + case PROP_ASPECT_RATIO: + g_value_set_double (value, priv->aspect_ratio); + break; + + case PROP_ANGLE: + g_value_set_double (value, priv->angle); + break; + + case PROP_DASHED: + g_value_set_boolean (value, priv->dashed); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_limit_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y, + gdouble *rx, + gdouble *ry) +{ + GimpCanvasLimit *limit = GIMP_CANVAS_LIMIT (item); + GimpCanvasLimitPrivate *priv = GET_PRIVATE (item); + gdouble x1, y1; + gdouble x2, y2; + gdouble min_radius = 0.0; + + gimp_canvas_limit_get_radii (limit, rx, ry); + + gimp_canvas_item_transform_xy_f (item, + priv->x - *rx, priv->y - *ry, + &x1, &y1); + gimp_canvas_item_transform_xy_f (item, + priv->x + *rx, priv->y + *ry, + &x2, &y2); + + x1 = floor (x1) + 0.5; + y1 = floor (y1) + 0.5; + + x2 = floor (x2) + 0.5; + y2 = floor (y2) + 0.5; + + *x = (x1 + x2) / 2.0; + *y = (y1 + y2) / 2.0; + + *rx = (x2 - x1) / 2.0; + *ry = (y2 - y1) / 2.0; + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + case GIMP_LIMIT_SQUARE: + min_radius = 2.0; + break; + + case GIMP_LIMIT_DIAMOND: + min_radius = 3.0; + break; + + case GIMP_LIMIT_HORIZONTAL: + case GIMP_LIMIT_VERTICAL: + min_radius = 1.0; + break; + } + + *rx = MAX (*rx, min_radius); + *ry = MAX (*ry, min_radius); +} + +static void +gimp_canvas_limit_paint (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasLimitPrivate *priv = GET_PRIVATE (item); + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + gdouble x, y; + gdouble rx, ry; + gdouble inf; + + gimp_canvas_limit_transform (item, + &x, &y, + &rx, &ry); + + cairo_save (cr); + + cairo_translate (cr, x, y); + cairo_rotate (cr, priv->angle); + cairo_scale (cr, rx, ry); + + inf = MAX (x, shell->disp_width - x) + + MAX (y, shell->disp_height - y); + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2.0 * G_PI); + break; + + case GIMP_LIMIT_SQUARE: + cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0); + break; + + case GIMP_LIMIT_DIAMOND: + cairo_move_to (cr, 0.0, -1.0); + cairo_line_to (cr, +1.0, 0.0); + cairo_line_to (cr, 0.0, +1.0); + cairo_line_to (cr, -1.0, 0.0); + cairo_close_path (cr); + break; + + case GIMP_LIMIT_HORIZONTAL: + cairo_move_to (cr, -inf / rx, -1.0); + cairo_line_to (cr, +inf / rx, -1.0); + + cairo_move_to (cr, -inf / rx, +1.0); + cairo_line_to (cr, +inf / rx, +1.0); + break; + + case GIMP_LIMIT_VERTICAL: + cairo_move_to (cr, -1.0, -inf / ry); + cairo_line_to (cr, -1.0, +inf / ry); + + cairo_move_to (cr, +1.0, -inf / ry); + cairo_line_to (cr, +1.0, +inf / ry); + break; + } + + cairo_restore (cr); +} + +static void +gimp_canvas_limit_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasLimitPrivate *priv = GET_PRIVATE (item); + + gimp_canvas_limit_paint (item, cr); + + cairo_save (cr); + + if (priv->dashed) + cairo_set_dash (cr, (const gdouble[]) {DASH_LENGTH}, 1, 0.0); + + _gimp_canvas_item_stroke (item, cr); + + cairo_restore (cr); +} + +static cairo_region_t * +gimp_canvas_limit_get_extents (GimpCanvasItem *item) +{ + cairo_t *cr; + cairo_surface_t *surface; + cairo_rectangle_int_t rectangle; + gdouble x1, y1; + gdouble x2, y2; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + + cr = cairo_create (surface); + + gimp_canvas_limit_paint (item, cr); + + cairo_path_extents (cr, + &x1, &y1, + &x2, &y2); + + cairo_destroy (cr); + + cairo_surface_destroy (surface); + + rectangle.x = floor (x1 - 1.5); + rectangle.y = floor (y1 - 1.5); + rectangle.width = ceil (x2 + 1.5) - rectangle.x; + rectangle.height = ceil (y2 + 1.5) - rectangle.y; + + return cairo_region_create_rectangle (&rectangle); +} + +static gboolean +gimp_canvas_limit_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + GimpCanvasLimit *limit = GIMP_CANVAS_LIMIT (item); + gdouble bx, by; + + gimp_canvas_limit_boundary_point (limit, + x, y, + &bx, &by); + + return gimp_canvas_item_transform_distance (item, + x, y, + bx, by) <= HIT_DISTANCE; +} + + +/* public functions */ + +GimpCanvasItem * +gimp_canvas_limit_new (GimpDisplayShell *shell, + GimpLimitType type, + gdouble x, + gdouble y, + gdouble radius, + gdouble aspect_ratio, + gdouble angle, + gboolean dashed) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_LIMIT, + "shell", shell, + "type", type, + "x", x, + "y", y, + "radius", radius, + "aspect-ratio", aspect_ratio, + "angle", angle, + "dashed", dashed, + NULL); +} + +void +gimp_canvas_limit_get_radii (GimpCanvasLimit *limit, + gdouble *rx, + gdouble *ry) +{ + GimpCanvasLimitPrivate *priv; + + g_return_if_fail (GIMP_IS_CANVAS_LIMIT (limit)); + + priv = GET_PRIVATE (limit); + + if (priv->aspect_ratio >= 0.0) + { + if (rx) *rx = priv->radius; + if (ry) *ry = priv->radius * (1.0 - priv->aspect_ratio); + } + else + { + if (rx) *rx = priv->radius * (1.0 + priv->aspect_ratio); + if (ry) *ry = priv->radius; + } +} + +gboolean +gimp_canvas_limit_is_inside (GimpCanvasLimit *limit, + gdouble x, + gdouble y) +{ + GimpCanvasLimitPrivate *priv; + GimpVector2 p; + gdouble rx, ry; + + g_return_val_if_fail (GIMP_IS_CANVAS_LIMIT (limit), FALSE); + + priv = GET_PRIVATE (limit); + + gimp_canvas_limit_get_radii (limit, &rx, &ry); + + if (rx == 0.0 || ry == 0.0) + return FALSE; + + p.x = x - priv->x; + p.y = y - priv->y; + + gimp_vector2_rotate (&p, +priv->angle); + + p.x = fabs (p.x / rx); + p.y = fabs (p.y / ry); + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + return gimp_vector2_length (&p) < 1.0; + + case GIMP_LIMIT_SQUARE: + return p.x < 1.0 && p.y < 1.0; + + case GIMP_LIMIT_DIAMOND: + return p.x + p.y < 1.0; + + case GIMP_LIMIT_HORIZONTAL: + return p.y < 1.0; + + case GIMP_LIMIT_VERTICAL: + return p.x < 1.0; + } + + g_return_val_if_reached (FALSE); +} + +void +gimp_canvas_limit_boundary_point (GimpCanvasLimit *limit, + gdouble x, + gdouble y, + gdouble *bx, + gdouble *by) +{ + GimpCanvasLimitPrivate *priv; + GimpVector2 p; + gdouble rx, ry; + gboolean flip_x = FALSE; + gboolean flip_y = FALSE; + + g_return_if_fail (GIMP_IS_CANVAS_LIMIT (limit)); + g_return_if_fail (bx != NULL); + g_return_if_fail (by != NULL); + + priv = GET_PRIVATE (limit); + + gimp_canvas_limit_get_radii (limit, &rx, &ry); + + p.x = x - priv->x; + p.y = y - priv->y; + + gimp_vector2_rotate (&p, +priv->angle); + + if (p.x < 0.0) + { + p.x = -p.x; + + flip_x = TRUE; + } + + if (p.y < 0.0) + { + p.y = -p.y; + + flip_y = TRUE; + } + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + if (rx == ry) + { + gimp_vector2_normalize (&p); + + gimp_vector2_mul (&p, rx); + } + else + { + gdouble a0 = 0.0; + gdouble a1 = G_PI / 2.0; + gdouble a; + gint i; + + for (i = 0; i < 20; i++) + { + GimpVector2 r; + GimpVector2 n; + + a = (a0 + a1) / 2.0; + + r.x = p.x - rx * cos (a); + r.y = p.y - ry * sin (a); + + n.x = 1.0; + n.y = tan (a) * rx / ry; + + if (gimp_vector2_cross_product (&r, &n).x >= 0.0) + a1 = a; + else + a0 = a; + } + + a = (a0 + a1) / 2.0; + + p.x = rx * cos (a); + p.y = ry * sin (a); + } + break; + + case GIMP_LIMIT_SQUARE: + if (p.x <= rx || p.y <= ry) + { + if (rx - p.x <= ry - p.y) + p.x = rx; + else + p.y = ry; + } + else + { + p.x = rx; + p.y = ry; + } + break; + + case GIMP_LIMIT_DIAMOND: + { + GimpVector2 l; + GimpVector2 r; + gdouble t; + + l.x = rx; + l.y = -ry; + + r.x = p.x; + r.y = p.y - ry; + + t = gimp_vector2_inner_product (&r, &l) / + gimp_vector2_inner_product (&l, &l); + t = CLAMP (t, 0.0, 1.0); + + p.x = rx * t; + p.y = ry * (1.0 - t); + } + break; + + case GIMP_LIMIT_HORIZONTAL: + p.y = ry; + break; + + case GIMP_LIMIT_VERTICAL: + p.x = rx; + break; + } + + if (flip_x) + p.x = -p.x; + + if (flip_y) + p.y = -p.y; + + gimp_vector2_rotate (&p, -priv->angle); + + *bx = priv->x + p.x; + *by = priv->y + p.y; +} + +gdouble +gimp_canvas_limit_boundary_radius (GimpCanvasLimit *limit, + gdouble x, + gdouble y) +{ + GimpCanvasLimitPrivate *priv; + GimpVector2 p; + + g_return_val_if_fail (GIMP_IS_CANVAS_LIMIT (limit), 0.0); + + priv = GET_PRIVATE (limit); + + p.x = x - priv->x; + p.y = y - priv->y; + + gimp_vector2_rotate (&p, +priv->angle); + + p.x = fabs (p.x); + p.y = fabs (p.y); + + if (priv->aspect_ratio >= 0.0) + p.y /= 1.0 - priv->aspect_ratio; + else + p.x /= 1.0 + priv->aspect_ratio; + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + return gimp_vector2_length (&p); + + case GIMP_LIMIT_SQUARE: + return MAX (p.x, p.y); + + case GIMP_LIMIT_DIAMOND: + return p.x + p.y; + + case GIMP_LIMIT_HORIZONTAL: + return p.y; + + case GIMP_LIMIT_VERTICAL: + return p.x; + } + + g_return_val_if_reached (0.0); +} + +void +gimp_canvas_limit_center_point (GimpCanvasLimit *limit, + gdouble x, + gdouble y, + gdouble *cx, + gdouble *cy) +{ + GimpCanvasLimitPrivate *priv; + GimpVector2 p; + + g_return_if_fail (GIMP_IS_CANVAS_LIMIT (limit)); + g_return_if_fail (cx != NULL); + g_return_if_fail (cy != NULL); + + priv = GET_PRIVATE (limit); + + p.x = x - priv->x; + p.y = y - priv->y; + + gimp_vector2_rotate (&p, +priv->angle); + + switch (priv->type) + { + case GIMP_LIMIT_CIRCLE: + case GIMP_LIMIT_SQUARE: + case GIMP_LIMIT_DIAMOND: + p.x = 0.0; + p.y = 0.0; + break; + + case GIMP_LIMIT_HORIZONTAL: + p.y = 0.0; + break; + + case GIMP_LIMIT_VERTICAL: + p.x = 0.0; + break; + } + + gimp_vector2_rotate (&p, -priv->angle); + + *cx = priv->x + p.x; + *cy = priv->y + p.y; +} diff --git a/app/display/gimpcanvaslimit.h b/app/display/gimpcanvaslimit.h new file mode 100644 index 0000000..cf6ffc8 --- /dev/null +++ b/app/display/gimpcanvaslimit.h @@ -0,0 +1,84 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaslimit.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_CANVAS_LIMIT_H__ +#define __GIMP_CANVAS_LIMIT_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_LIMIT (gimp_canvas_limit_get_type ()) +#define GIMP_CANVAS_LIMIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_LIMIT, GimpCanvasLimit)) +#define GIMP_CANVAS_LIMIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_LIMIT, GimpCanvasLimitClass)) +#define GIMP_IS_CANVAS_LIMIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_LIMIT)) +#define GIMP_IS_CANVAS_LIMIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_LIMIT)) +#define GIMP_CANVAS_LIMIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_LIMIT, GimpCanvasLimitClass)) + + +typedef struct _GimpCanvasLimit GimpCanvasLimit; +typedef struct _GimpCanvasLimitClass GimpCanvasLimitClass; + +struct _GimpCanvasLimit +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasLimitClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_limit_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_limit_new (GimpDisplayShell *shell, + GimpLimitType type, + gdouble x, + gdouble y, + gdouble radius, + gdouble aspect_ratio, + gdouble angle, + gboolean dashed); + +void gimp_canvas_limit_get_radii (GimpCanvasLimit *limit, + gdouble *rx, + gdouble *ry); + +gboolean gimp_canvas_limit_is_inside (GimpCanvasLimit *limit, + gdouble x, + gdouble y); +void gimp_canvas_limit_boundary_point (GimpCanvasLimit *limit, + gdouble x, + gdouble y, + gdouble *bx, + gdouble *by); +gdouble gimp_canvas_limit_boundary_radius (GimpCanvasLimit *limit, + gdouble x, + gdouble y); + +void gimp_canvas_limit_center_point (GimpCanvasLimit *limit, + gdouble x, + gdouble y, + gdouble *cx, + gdouble *cy); + + +#endif /* __GIMP_CANVAS_LIMIT_H__ */ diff --git a/app/display/gimpcanvasline.c b/app/display/gimpcanvasline.c new file mode 100644 index 0000000..38a00c8 --- /dev/null +++ b/app/display/gimpcanvasline.c @@ -0,0 +1,281 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasline.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvasline.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2 +}; + + +typedef struct _GimpCanvasLinePrivate GimpCanvasLinePrivate; + +struct _GimpCanvasLinePrivate +{ + gdouble x1; + gdouble y1; + gdouble x2; + gdouble y2; +}; + +#define GET_PRIVATE(line) \ + ((GimpCanvasLinePrivate *) gimp_canvas_line_get_instance_private ((GimpCanvasLine *) (line))) + + +/* local function prototypes */ + +static void gimp_canvas_line_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_line_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_line_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_line_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasLine, gimp_canvas_line, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_line_parent_class + + +static void +gimp_canvas_line_class_init (GimpCanvasLineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_line_set_property; + object_class->get_property = gimp_canvas_line_get_property; + + item_class->draw = gimp_canvas_line_draw; + item_class->get_extents = gimp_canvas_line_get_extents; + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_line_init (GimpCanvasLine *line) +{ +} + +static void +gimp_canvas_line_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLinePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_line_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasLinePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X1: + g_value_set_double (value, private->x1); + break; + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + case PROP_X2: + g_value_set_double (value, private->x2); + break; + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_line_transform (GimpCanvasItem *item, + gdouble *x1, + gdouble *y1, + gdouble *x2, + gdouble *y2) +{ + GimpCanvasLinePrivate *private = GET_PRIVATE (item); + + gimp_canvas_item_transform_xy_f (item, + private->x1, private->y1, + x1, y1); + gimp_canvas_item_transform_xy_f (item, + private->x2, private->y2, + x2, y2); + + *x1 = floor (*x1) + 0.5; + *y1 = floor (*y1) + 0.5; + *x2 = floor (*x2) + 0.5; + *y2 = floor (*y2) + 0.5; +} + +static void +gimp_canvas_line_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_line_transform (item, &x1, &y1, &x2, &y2); + + cairo_move_to (cr, x1, y1); + cairo_line_to (cr, x2, y2); + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_line_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_line_transform (item, &x1, &y1, &x2, &y2); + + if (x1 == x2 || y1 == y2) + { + rectangle.x = MIN (x1, x2) - 1.5; + rectangle.y = MIN (y1, y2) - 1.5; + rectangle.width = ABS (x2 - x1) + 3.0; + rectangle.height = ABS (y2 - y1) + 3.0; + } + else + { + rectangle.x = floor (MIN (x1, x2) - 2.5); + rectangle.y = floor (MIN (y1, y2) - 2.5); + rectangle.width = ceil (ABS (x2 - x1) + 5.0); + rectangle.height = ceil (ABS (y2 - y1) + 5.0); + } + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_line_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_LINE, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} + +void +gimp_canvas_line_set (GimpCanvasItem *line, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_if_fail (GIMP_IS_CANVAS_LINE (line)); + + gimp_canvas_item_begin_change (line); + + g_object_set (line, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); + + gimp_canvas_item_end_change (line); +} diff --git a/app/display/gimpcanvasline.h b/app/display/gimpcanvasline.h new file mode 100644 index 0000000..6f3f5f2 --- /dev/null +++ b/app/display/gimpcanvasline.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasline.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_LINE_H__ +#define __GIMP_CANVAS_LINE_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_LINE (gimp_canvas_line_get_type ()) +#define GIMP_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_LINE, GimpCanvasLine)) +#define GIMP_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_LINE, GimpCanvasLineClass)) +#define GIMP_IS_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_LINE)) +#define GIMP_IS_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_LINE)) +#define GIMP_CANVAS_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_LINE, GimpCanvasLineClass)) + + +typedef struct _GimpCanvasLine GimpCanvasLine; +typedef struct _GimpCanvasLineClass GimpCanvasLineClass; + +struct _GimpCanvasLine +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasLineClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_line_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_line_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + +void gimp_canvas_line_set (GimpCanvasItem *line, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + + +#endif /* __GIMP_CANVAS_LINE_H__ */ diff --git a/app/display/gimpcanvaspassepartout.c b/app/display/gimpcanvaspassepartout.c new file mode 100644 index 0000000..21e7307 --- /dev/null +++ b/app/display/gimpcanvaspassepartout.c @@ -0,0 +1,235 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaspassepartout.c + * Copyright (C) 2010 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvasitem-utils.h" +#include "gimpcanvaspassepartout.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scale.h" + + +enum +{ + PROP_0, + PROP_OPACITY, +}; + + +typedef struct _GimpCanvasPassePartoutPrivate GimpCanvasPassePartoutPrivate; + +struct _GimpCanvasPassePartoutPrivate +{ + gdouble opacity; +}; + +#define GET_PRIVATE(item) \ + ((GimpCanvasPassePartoutPrivate *) gimp_canvas_passe_partout_get_instance_private ((GimpCanvasPassePartout *) (item))) + + +/* local function prototypes */ + +static void gimp_canvas_passe_partout_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_passe_partout_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_passe_partout_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_passe_partout_get_extents (GimpCanvasItem *item); +static void gimp_canvas_passe_partout_fill (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasPassePartout, gimp_canvas_passe_partout, + GIMP_TYPE_CANVAS_RECTANGLE) + +#define parent_class gimp_canvas_passe_partout_parent_class + + +static void +gimp_canvas_passe_partout_class_init (GimpCanvasPassePartoutClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_passe_partout_set_property; + object_class->get_property = gimp_canvas_passe_partout_get_property; + + item_class->draw = gimp_canvas_passe_partout_draw; + item_class->get_extents = gimp_canvas_passe_partout_get_extents; + item_class->fill = gimp_canvas_passe_partout_fill; + + g_object_class_install_property (object_class, PROP_OPACITY, + g_param_spec_double ("opacity", NULL, NULL, + 0.0, + 1.0, 0.5, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_passe_partout_init (GimpCanvasPassePartout *passe_partout) +{ +} + +static void +gimp_canvas_passe_partout_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPassePartoutPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_OPACITY: + priv->opacity = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_passe_partout_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPassePartoutPrivate *priv = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_OPACITY: + g_value_set_double (value, priv->opacity); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_passe_partout_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + gint x, y; + gint w, h; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + x = -shell->offset_x; + y = -shell->offset_y; + + gimp_display_shell_scale_get_image_size (shell, &w, &h); + } + else + { + gimp_canvas_item_untransform_viewport (item, &x, &y, &w, &h); + } + + cairo_rectangle (cr, x, y, w, h); + + GIMP_CANVAS_ITEM_CLASS (parent_class)->draw (item, cr); +} + +static cairo_region_t * +gimp_canvas_passe_partout_get_extents (GimpCanvasItem *item) +{ + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + cairo_rectangle_int_t rectangle; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + cairo_region_t *inner; + cairo_region_t *outer; + + rectangle.x = - shell->offset_x; + rectangle.y = - shell->offset_y; + gimp_display_shell_scale_get_image_size (shell, + &rectangle.width, + &rectangle.height); + + outer = cairo_region_create_rectangle (&rectangle); + + inner = GIMP_CANVAS_ITEM_CLASS (parent_class)->get_extents (item); + + cairo_region_xor (outer, inner); + + cairo_region_destroy (inner); + + return outer; + } + else + { + gimp_canvas_item_untransform_viewport (item, + &rectangle.x, + &rectangle.y, + &rectangle.width, + &rectangle.height); + + return cairo_region_create_rectangle (&rectangle); + } +} + +static void +gimp_canvas_passe_partout_fill (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasPassePartoutPrivate *priv = GET_PRIVATE (item); + + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_clip (cr); + + gimp_canvas_set_passe_partout_style (gimp_canvas_item_get_canvas (item), cr); + cairo_paint_with_alpha (cr, priv->opacity); +} + +GimpCanvasItem * +gimp_canvas_passe_partout_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_PASSE_PARTOUT, + "shell", shell, + "x", x, + "y", y, + "width", width, + "height", height, + "filled", TRUE, + NULL); +} diff --git a/app/display/gimpcanvaspassepartout.h b/app/display/gimpcanvaspassepartout.h new file mode 100644 index 0000000..7356e3a --- /dev/null +++ b/app/display/gimpcanvaspassepartout.h @@ -0,0 +1,59 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaspassepartout.h + * Copyright (C) 2010 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 . + */ + +#ifndef __GIMP_CANVAS_PASSE_PARTOUT_H__ +#define __GIMP_CANVAS_PASSE_PARTOUT_H__ + + +#include "gimpcanvasrectangle.h" + + +#define GIMP_TYPE_CANVAS_PASSE_PARTOUT (gimp_canvas_passe_partout_get_type ()) +#define GIMP_CANVAS_PASSE_PARTOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_PASSE_PARTOUT, GimpCanvasPassePartout)) +#define GIMP_CANVAS_PASSE_PARTOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_PASSE_PARTOUT, GimpCanvasPassePartoutClass)) +#define GIMP_IS_CANVAS_PASSE_PARTOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_PASSE_PARTOUT)) +#define GIMP_IS_CANVAS_PASSE_PARTOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_PASSE_PARTOUT)) +#define GIMP_CANVAS_PASSE_PARTOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_PASSE_PARTOUT, GimpCanvasPassePartoutClass)) + + +typedef struct _GimpCanvasPassePartout GimpCanvasPassePartout; +typedef struct _GimpCanvasPassePartoutClass GimpCanvasPassePartoutClass; + +struct _GimpCanvasPassePartout +{ + GimpCanvasRectangle parent_instance; +}; + +struct _GimpCanvasPassePartoutClass +{ + GimpCanvasRectangleClass parent_class; +}; + + +GType gimp_canvas_passe_partout_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_passe_partout_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height); + + +#endif /* __GIMP_CANVAS_PASSE_PARTOUT_H__ */ diff --git a/app/display/gimpcanvaspath.c b/app/display/gimpcanvaspath.c new file mode 100644 index 0000000..71a001f --- /dev/null +++ b/app/display/gimpcanvaspath.c @@ -0,0 +1,352 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaspath.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpbezierdesc.h" +#include "core/gimpparamspecs.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvaspath.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_PATH, + PROP_X, + PROP_Y, + PROP_FILLED, + PROP_PATH_STYLE +}; + + +typedef struct _GimpCanvasPathPrivate GimpCanvasPathPrivate; + +struct _GimpCanvasPathPrivate +{ + cairo_path_t *path; + gdouble x; + gdouble y; + gboolean filled; + GimpPathStyle path_style; +}; + +#define GET_PRIVATE(path) \ + ((GimpCanvasPathPrivate *) gimp_canvas_path_get_instance_private ((GimpCanvasPath *) (path))) + +/* local function prototypes */ + +static void gimp_canvas_path_finalize (GObject *object); +static void gimp_canvas_path_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_path_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_path_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_path_get_extents (GimpCanvasItem *item); +static void gimp_canvas_path_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasPath, gimp_canvas_path, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_path_parent_class + + +static void +gimp_canvas_path_class_init (GimpCanvasPathClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_path_finalize; + object_class->set_property = gimp_canvas_path_set_property; + object_class->get_property = gimp_canvas_path_get_property; + + item_class->draw = gimp_canvas_path_draw; + item_class->get_extents = gimp_canvas_path_get_extents; + item_class->stroke = gimp_canvas_path_stroke; + + g_object_class_install_property (object_class, PROP_PATH, + g_param_spec_boxed ("path", NULL, NULL, + GIMP_TYPE_BEZIER_DESC, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FILLED, + g_param_spec_boolean ("filled", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, PROP_PATH_STYLE, + g_param_spec_enum ("path-style", NULL, NULL, + GIMP_TYPE_PATH_STYLE, + GIMP_PATH_STYLE_DEFAULT, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_path_init (GimpCanvasPath *path) +{ +} + +static void +gimp_canvas_path_finalize (GObject *object) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (object); + + if (private->path) + { + gimp_bezier_desc_free (private->path); + private->path = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_path_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_PATH: + if (private->path) + gimp_bezier_desc_free (private->path); + private->path = g_value_dup_boxed (value); + break; + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_FILLED: + private->filled = g_value_get_boolean (value); + break; + case PROP_PATH_STYLE: + private->path_style = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_path_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_PATH: + g_value_set_boxed (value, private->path); + break; + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_FILLED: + g_value_set_boolean (value, private->filled); + break; + case PROP_PATH_STYLE: + g_value_set_enum (value, private->path_style); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_path_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (item); + + if (private->path) + { + cairo_save (cr); + gimp_canvas_item_transform (item, cr); + cairo_translate (cr, private->x, private->y); + + cairo_append_path (cr, private->path); + cairo_restore (cr); + + if (private->filled) + _gimp_canvas_item_fill (item, cr); + else + _gimp_canvas_item_stroke (item, cr); + } +} + +static cairo_region_t * +gimp_canvas_path_get_extents (GimpCanvasItem *item) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + + if (private->path && gtk_widget_get_realized (canvas)) + { + cairo_t *cr; + cairo_rectangle_int_t rectangle; + gdouble x1, y1, x2, y2; + + cr = gdk_cairo_create (gtk_widget_get_window (canvas)); + + cairo_save (cr); + gimp_canvas_item_transform (item, cr); + cairo_translate (cr, private->x, private->y); + + cairo_append_path (cr, private->path); + cairo_restore (cr); + + cairo_path_extents (cr, &x1, &y1, &x2, &y2); + + cairo_destroy (cr); + + if (private->filled) + { + rectangle.x = floor (x1 - 1.0); + rectangle.y = floor (y1 - 1.0); + rectangle.width = ceil (x2 + 1.0) - rectangle.x; + rectangle.height = ceil (y2 + 1.0) - rectangle.y; + } + else + { + rectangle.x = floor (x1 - 1.5); + rectangle.y = floor (y1 - 1.5); + rectangle.width = ceil (x2 + 1.5) - rectangle.x; + rectangle.height = ceil (y2 + 1.5) - rectangle.y; + } + + return cairo_region_create_rectangle (&rectangle); + } + + return NULL; +} + +static void +gimp_canvas_path_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasPathPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + gboolean active; + + switch (private->path_style) + { + case GIMP_PATH_STYLE_VECTORS: + active = gimp_canvas_item_get_highlight (item); + + gimp_canvas_set_vectors_bg_style (canvas, cr, active); + cairo_stroke_preserve (cr); + + gimp_canvas_set_vectors_fg_style (canvas, cr, active); + cairo_stroke (cr); + break; + + case GIMP_PATH_STYLE_OUTLINE: + gimp_canvas_set_outline_bg_style (canvas, cr); + cairo_stroke_preserve (cr); + + gimp_canvas_set_outline_fg_style (canvas, cr); + cairo_stroke (cr); + break; + + case GIMP_PATH_STYLE_DEFAULT: + GIMP_CANVAS_ITEM_CLASS (parent_class)->stroke (item, cr); + break; + } +} + +GimpCanvasItem * +gimp_canvas_path_new (GimpDisplayShell *shell, + const GimpBezierDesc *bezier, + gdouble x, + gdouble y, + gboolean filled, + GimpPathStyle style) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_PATH, + "shell", shell, + "path", bezier, + "x", x, + "y", y, + "filled", filled, + "path-style", style, + NULL); +} + +void +gimp_canvas_path_set (GimpCanvasItem *path, + const GimpBezierDesc *bezier) +{ + g_return_if_fail (GIMP_IS_CANVAS_PATH (path)); + + gimp_canvas_item_begin_change (path); + + g_object_set (path, + "path", bezier, + NULL); + + gimp_canvas_item_end_change (path); +} diff --git a/app/display/gimpcanvaspath.h b/app/display/gimpcanvaspath.h new file mode 100644 index 0000000..03fbb01 --- /dev/null +++ b/app/display/gimpcanvaspath.h @@ -0,0 +1,63 @@ +/* GIMP - The GNU Image Manipulation Program Copyright (C) 1995 + * Spencer Kimball and Peter Mattis + * + * gimpcanvaspolygon.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_PATH_H__ +#define __GIMP_CANVAS_PATH_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_PATH (gimp_canvas_path_get_type ()) +#define GIMP_CANVAS_PATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_PATH, GimpCanvasPath)) +#define GIMP_CANVAS_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_PATH, GimpCanvasPathClass)) +#define GIMP_IS_CANVAS_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_PATH)) +#define GIMP_IS_CANVAS_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_PATH)) +#define GIMP_CANVAS_PATH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_PATH, GimpCanvasPathClass)) + + +typedef struct _GimpCanvasPath GimpCanvasPath; +typedef struct _GimpCanvasPathClass GimpCanvasPathClass; + +struct _GimpCanvasPath +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasPathClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_path_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_path_new (GimpDisplayShell *shell, + const GimpBezierDesc *bezier, + gdouble x, + gdouble y, + gboolean filled, + GimpPathStyle style); + +void gimp_canvas_path_set (GimpCanvasItem *path, + const GimpBezierDesc *bezier); + + +#endif /* __GIMP_CANVAS_PATH_H__ */ diff --git a/app/display/gimpcanvaspen.c b/app/display/gimpcanvaspen.c new file mode 100644 index 0000000..d0d01f9 --- /dev/null +++ b/app/display/gimpcanvaspen.c @@ -0,0 +1,231 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaspen.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpparamspecs.h" + +#include "gimpcanvas-style.h" +#include "gimpcanvaspen.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_COLOR, + PROP_WIDTH +}; + + +typedef struct _GimpCanvasPenPrivate GimpCanvasPenPrivate; + +struct _GimpCanvasPenPrivate +{ + GimpRGB color; + gint width; +}; + +#define GET_PRIVATE(pen) \ + ((GimpCanvasPenPrivate *) gimp_canvas_pen_get_instance_private ((GimpCanvasPen *) (pen))) + + +/* local function prototypes */ + +static void gimp_canvas_pen_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_pen_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static cairo_region_t * gimp_canvas_pen_get_extents (GimpCanvasItem *item); +static void gimp_canvas_pen_stroke (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasPen, gimp_canvas_pen, + GIMP_TYPE_CANVAS_POLYGON) + +#define parent_class gimp_canvas_pen_parent_class + + +static void +gimp_canvas_pen_class_init (GimpCanvasPenClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_pen_set_property; + object_class->get_property = gimp_canvas_pen_get_property; + + item_class->get_extents = gimp_canvas_pen_get_extents; + item_class->stroke = gimp_canvas_pen_stroke; + + g_object_class_install_property (object_class, PROP_COLOR, + gimp_param_spec_rgb ("color", NULL, NULL, + FALSE, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + 1, G_MAXINT, 1, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_pen_init (GimpCanvasPen *pen) +{ +} + +static void +gimp_canvas_pen_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPenPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_COLOR: + gimp_value_get_rgb (value, &private->color); + break; + case PROP_WIDTH: + private->width = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_pen_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPenPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_COLOR: + gimp_value_set_rgb (value, &private->color); + break; + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static cairo_region_t * +gimp_canvas_pen_get_extents (GimpCanvasItem *item) +{ + GimpCanvasPenPrivate *private = GET_PRIVATE (item); + cairo_region_t *region; + + region = GIMP_CANVAS_ITEM_CLASS (parent_class)->get_extents (item); + + if (region) + { + cairo_rectangle_int_t rectangle; + + cairo_region_get_extents (region, &rectangle); + + rectangle.x -= ceil (private->width / 2.0); + rectangle.y -= ceil (private->width / 2.0); + rectangle.width += private->width + 1; + rectangle.height += private->width + 1; + + cairo_region_union_rectangle (region, &rectangle); + } + + return region; +} + +static void +gimp_canvas_pen_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasPenPrivate *private = GET_PRIVATE (item); + + gimp_canvas_set_pen_style (gimp_canvas_item_get_canvas (item), cr, + &private->color, private->width); + cairo_stroke (cr); +} + +GimpCanvasItem * +gimp_canvas_pen_new (GimpDisplayShell *shell, + const GimpVector2 *points, + gint n_points, + GimpContext *context, + GimpActiveColor color, + gint width) +{ + GimpCanvasItem *item; + GimpArray *array; + GimpRGB rgb; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (points != NULL && n_points > 1, NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + + array = gimp_array_new ((const guint8 *) points, + n_points * sizeof (GimpVector2), TRUE); + + switch (color) + { + case GIMP_ACTIVE_COLOR_FOREGROUND: + gimp_context_get_foreground (context, &rgb); + break; + + case GIMP_ACTIVE_COLOR_BACKGROUND: + gimp_context_get_background (context, &rgb); + break; + } + + item = g_object_new (GIMP_TYPE_CANVAS_PEN, + "shell", shell, + "points", array, + "color", &rgb, + "width", width, + NULL); + + gimp_array_free (array); + + return item; +} diff --git a/app/display/gimpcanvaspen.h b/app/display/gimpcanvaspen.h new file mode 100644 index 0000000..3380790 --- /dev/null +++ b/app/display/gimpcanvaspen.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program Copyright (C) 1995 + * Spencer Kimball and Peter Mattis + * + * gimpcanvaspen.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_PEN_H__ +#define __GIMP_CANVAS_PEN_H__ + + +#include "gimpcanvaspolygon.h" + + +#define GIMP_TYPE_CANVAS_PEN (gimp_canvas_pen_get_type ()) +#define GIMP_CANVAS_PEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_PEN, GimpCanvasPen)) +#define GIMP_CANVAS_PEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_PEN, GimpCanvasPenClass)) +#define GIMP_IS_CANVAS_PEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_PEN)) +#define GIMP_IS_CANVAS_PEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_PEN)) +#define GIMP_CANVAS_PEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_PEN, GimpCanvasPenClass)) + + +typedef struct _GimpCanvasPen GimpCanvasPen; +typedef struct _GimpCanvasPenClass GimpCanvasPenClass; + +struct _GimpCanvasPen +{ + GimpCanvasPolygon parent_instance; +}; + +struct _GimpCanvasPenClass +{ + GimpCanvasPolygonClass parent_class; +}; + + +GType gimp_canvas_pen_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_pen_new (GimpDisplayShell *shell, + const GimpVector2 *points, + gint n_points, + GimpContext *context, + GimpActiveColor color, + gint width); + + +#endif /* __GIMP_CANVAS_PEN_H__ */ diff --git a/app/display/gimpcanvaspolygon.c b/app/display/gimpcanvaspolygon.c new file mode 100644 index 0000000..9c670e7 --- /dev/null +++ b/app/display/gimpcanvaspolygon.c @@ -0,0 +1,505 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvaspolygon.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimpparamspecs.h" + +#include "gimpcanvaspolygon.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_POINTS, + PROP_TRANSFORM, + PROP_FILLED +}; + + +typedef struct _GimpCanvasPolygonPrivate GimpCanvasPolygonPrivate; + +struct _GimpCanvasPolygonPrivate +{ + GimpVector2 *points; + gint n_points; + GimpMatrix3 *transform; + gboolean filled; +}; + +#define GET_PRIVATE(polygon) \ + ((GimpCanvasPolygonPrivate *) gimp_canvas_polygon_get_instance_private ((GimpCanvasPolygon *) (polygon))) + + +/* local function prototypes */ + +static void gimp_canvas_polygon_finalize (GObject *object); +static void gimp_canvas_polygon_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_polygon_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_polygon_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_polygon_get_extents (GimpCanvasItem *item); +static gboolean gimp_canvas_polygon_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasPolygon, gimp_canvas_polygon, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_polygon_parent_class + + +static void +gimp_canvas_polygon_class_init (GimpCanvasPolygonClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_polygon_finalize; + object_class->set_property = gimp_canvas_polygon_set_property; + object_class->get_property = gimp_canvas_polygon_get_property; + + item_class->draw = gimp_canvas_polygon_draw; + item_class->get_extents = gimp_canvas_polygon_get_extents; + item_class->hit = gimp_canvas_polygon_hit; + + g_object_class_install_property (object_class, PROP_POINTS, + gimp_param_spec_array ("points", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TRANSFORM, + g_param_spec_pointer ("transform", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FILLED, + g_param_spec_boolean ("filled", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_polygon_init (GimpCanvasPolygon *polygon) +{ +} + +static void +gimp_canvas_polygon_finalize (GObject *object) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->points, g_free); + private->n_points = 0; + + g_clear_pointer (&private->transform, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_polygon_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_POINTS: + { + GimpArray *array = g_value_get_boxed (value); + + g_clear_pointer (&private->points, g_free); + private->n_points = 0; + + if (array) + { + private->points = g_memdup (array->data, array->length); + private->n_points = array->length / sizeof (GimpVector2); + } + } + break; + + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_pointer (value); + if (private->transform) + g_free (private->transform); + if (transform) + private->transform = g_memdup (transform, sizeof (GimpMatrix3)); + else + private->transform = NULL; + } + break; + + case PROP_FILLED: + private->filled = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_polygon_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_POINTS: + if (private->points) + { + GimpArray *array; + + array = gimp_array_new ((const guint8 *) private->points, + private->n_points * sizeof (GimpVector2), + FALSE); + g_value_take_boxed (value, array); + } + else + { + g_value_set_boxed (value, NULL); + } + break; + + case PROP_TRANSFORM: + g_value_set_pointer (value, private->transform); + break; + + case PROP_FILLED: + g_value_set_boolean (value, private->filled); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_polygon_transform (GimpCanvasItem *item, + GimpVector2 *points, + gint *n_points) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (item); + gint i; + + if (private->transform) + { + gimp_transform_polygon (private->transform, + private->points, private->n_points, FALSE, + points, n_points); + + for (i = 0; i < *n_points; i++) + { + gimp_canvas_item_transform_xy_f (item, + points[i].x, + points[i].y, + &points[i].x, + &points[i].y); + + points[i].x = floor (points[i].x) + 0.5; + points[i].y = floor (points[i].y) + 0.5; + } + } + else + { + for (i = 0; i < private->n_points; i++) + { + gimp_canvas_item_transform_xy_f (item, + private->points[i].x, + private->points[i].y, + &points[i].x, + &points[i].y); + + points[i].x = floor (points[i].x) + 0.5; + points[i].y = floor (points[i].y) + 0.5; + } + + *n_points = private->n_points; + } +} + +static void +gimp_canvas_polygon_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (item); + GimpVector2 *points; + gint n_points; + gint i; + + if (! private->points) + return; + + n_points = private->n_points; + + if (private->transform) + n_points = 3 * n_points / 2; + + points = g_new0 (GimpVector2, n_points); + + gimp_canvas_polygon_transform (item, points, &n_points); + + if (n_points < 2) + { + g_free (points); + + return; + } + + cairo_move_to (cr, points[0].x, points[0].y); + + for (i = 1; i < n_points; i++) + { + cairo_line_to (cr, points[i].x, points[i].y); + } + + if (private->filled) + _gimp_canvas_item_fill (item, cr); + else + _gimp_canvas_item_stroke (item, cr); + + g_free (points); +} + +static cairo_region_t * +gimp_canvas_polygon_get_extents (GimpCanvasItem *item) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + GimpVector2 *points; + gint n_points; + gint x1, y1, x2, y2; + gint i; + + if (! private->points) + return NULL; + + n_points = private->n_points; + + if (private->transform) + n_points = 3 * n_points / 2; + + points = g_new0 (GimpVector2, n_points); + + gimp_canvas_polygon_transform (item, points, &n_points); + + if (n_points < 2) + { + g_free (points); + + return NULL; + } + + x1 = floor (points[0].x - 1.5); + y1 = floor (points[0].y - 1.5); + x2 = x1 + 3; + y2 = y1 + 3; + + for (i = 1; i < n_points; i++) + { + gint x3 = floor (points[i].x - 1.5); + gint y3 = floor (points[i].y - 1.5); + gint x4 = x3 + 3; + gint y4 = y3 + 3; + + x1 = MIN (x1, x3); + y1 = MIN (y1, y3); + x2 = MAX (x2, x4); + y2 = MAX (y2, y4); + } + + g_free (points); + + rectangle.x = x1; + rectangle.y = y1; + rectangle.width = x2 - x1; + rectangle.height = y2 - y1; + + return cairo_region_create_rectangle (&rectangle); +} + +static gboolean +gimp_canvas_polygon_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + GimpCanvasPolygonPrivate *private = GET_PRIVATE (item); + GimpVector2 *points; + gint n_points; + gdouble tx, ty; + cairo_surface_t *surface; + cairo_t *cr; + gboolean hit; + gint i; + + if (! private->points) + return FALSE; + + gimp_canvas_item_transform_xy_f (item, x, y, &tx, &ty); + + n_points = private->n_points; + + if (private->transform) + n_points = 3 * n_points / 2; + + points = g_new0 (GimpVector2, n_points); + + gimp_canvas_polygon_transform (item, points, &n_points); + + if (n_points < 2) + { + g_free (points); + + return FALSE; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1); + cr = cairo_create (surface); + cairo_surface_destroy (surface); + + cairo_move_to (cr, points[0].x, points[0].y); + + for (i = 1; i < private->n_points; i++) + { + cairo_line_to (cr, points[i].x, points[i].y); + } + + g_free (points); + + hit = cairo_in_fill (cr, tx, ty); + + cairo_destroy (cr); + + return hit; +} + +GimpCanvasItem * +gimp_canvas_polygon_new (GimpDisplayShell *shell, + const GimpVector2 *points, + gint n_points, + GimpMatrix3 *transform, + gboolean filled) +{ + GimpCanvasItem *item; + GimpArray *array; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (points == NULL || n_points > 0, NULL); + + array = gimp_array_new ((const guint8 *) points, + n_points * sizeof (GimpVector2), TRUE); + + item = g_object_new (GIMP_TYPE_CANVAS_POLYGON, + "shell", shell, + "transform", transform, + "filled", filled, + "points", array, + NULL); + + gimp_array_free (array); + + return item; +} + +GimpCanvasItem * +gimp_canvas_polygon_new_from_coords (GimpDisplayShell *shell, + const GimpCoords *coords, + gint n_coords, + GimpMatrix3 *transform, + gboolean filled) +{ + GimpCanvasItem *item; + GimpVector2 *points; + GimpArray *array; + gint i; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (coords == NULL || n_coords > 0, NULL); + + points = g_new (GimpVector2, n_coords); + + for (i = 0; i < n_coords; i++) + { + points[i].x = coords[i].x; + points[i].y = coords[i].y; + } + + array = gimp_array_new ((const guint8 *) points, + n_coords * sizeof (GimpVector2), TRUE); + + item = g_object_new (GIMP_TYPE_CANVAS_POLYGON, + "shell", shell, + "transform", transform, + "filled", filled, + "points", array, + NULL); + + gimp_array_free (array); + g_free (points); + + return item; +} + +void +gimp_canvas_polygon_set_points (GimpCanvasItem *polygon, + const GimpVector2 *points, + gint n_points) +{ + GimpArray *array; + + g_return_if_fail (GIMP_IS_CANVAS_POLYGON (polygon)); + g_return_if_fail (points == NULL || n_points > 0); + + array = gimp_array_new ((const guint8 *) points, + n_points * sizeof (GimpVector2), TRUE); + + gimp_canvas_item_begin_change (polygon); + g_object_set (polygon, + "points", array, + NULL); + gimp_canvas_item_end_change (polygon); + + gimp_array_free (array); +} diff --git a/app/display/gimpcanvaspolygon.h b/app/display/gimpcanvaspolygon.h new file mode 100644 index 0000000..f68e36a --- /dev/null +++ b/app/display/gimpcanvaspolygon.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program Copyright (C) 1995 + * Spencer Kimball and Peter Mattis + * + * gimpcanvaspolygon.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_POLYGON_H__ +#define __GIMP_CANVAS_POLYGON_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_POLYGON (gimp_canvas_polygon_get_type ()) +#define GIMP_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_POLYGON, GimpCanvasPolygon)) +#define GIMP_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_POLYGON, GimpCanvasPolygonClass)) +#define GIMP_IS_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_POLYGON)) +#define GIMP_IS_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_POLYGON)) +#define GIMP_CANVAS_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_POLYGON, GimpCanvasPolygonClass)) + + +typedef struct _GimpCanvasPolygon GimpCanvasPolygon; +typedef struct _GimpCanvasPolygonClass GimpCanvasPolygonClass; + +struct _GimpCanvasPolygon +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasPolygonClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_polygon_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_polygon_new (GimpDisplayShell *shell, + const GimpVector2 *points, + gint n_points, + GimpMatrix3 *transform, + gboolean filled); +GimpCanvasItem * gimp_canvas_polygon_new_from_coords (GimpDisplayShell *shell, + const GimpCoords *coords, + gint n_coords, + GimpMatrix3 *transform, + gboolean filled); + +void gimp_canvas_polygon_set_points (GimpCanvasItem *polygon, + const GimpVector2 *points, + gint n_points); + + +#endif /* __GIMP_CANVAS_POLYGON_H__ */ diff --git a/app/display/gimpcanvasprogress.c b/app/display/gimpcanvasprogress.c new file mode 100644 index 0000000..4def1ac --- /dev/null +++ b/app/display/gimpcanvasprogress.c @@ -0,0 +1,459 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasprogress.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpprogress.h" + +#include "gimpcanvas.h" +#include "gimpcanvas-style.h" +#include "gimpcanvasitem-utils.h" +#include "gimpcanvasprogress.h" +#include "gimpdisplayshell.h" + + +#define BORDER 5 +#define RADIUS 20 + +#define MIN_UPDATE_INTERVAL 50000 /* microseconds */ + + +enum +{ + PROP_0, + PROP_ANCHOR, + PROP_X, + PROP_Y +}; + + +typedef struct _GimpCanvasProgressPrivate GimpCanvasProgressPrivate; + +struct _GimpCanvasProgressPrivate +{ + GimpHandleAnchor anchor; + gdouble x; + gdouble y; + + gchar *text; + gdouble value; + + guint64 last_update_time; +}; + +#define GET_PRIVATE(progress) \ + ((GimpCanvasProgressPrivate *) gimp_canvas_progress_get_instance_private ((GimpCanvasProgress *) (progress))) + + +/* local function prototypes */ + +static void gimp_canvas_progress_iface_init (GimpProgressInterface *iface); + +static void gimp_canvas_progress_finalize (GObject *object); +static void gimp_canvas_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_progress_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_progress_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_progress_get_extents (GimpCanvasItem *item); +static gboolean gimp_canvas_progress_hit (GimpCanvasItem *item, + gdouble x, + gdouble y); + +static GimpProgress * gimp_canvas_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message); +static void gimp_canvas_progress_end (GimpProgress *progress); +static gboolean gimp_canvas_progress_is_active (GimpProgress *progress); +static void gimp_canvas_progress_set_text (GimpProgress *progress, + const gchar *message); +static void gimp_canvas_progress_set_value (GimpProgress *progress, + gdouble percentage); +static gdouble gimp_canvas_progress_get_value (GimpProgress *progress); +static void gimp_canvas_progress_pulse (GimpProgress *progress); +static gboolean gimp_canvas_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + + +G_DEFINE_TYPE_WITH_CODE (GimpCanvasProgress, gimp_canvas_progress, + GIMP_TYPE_CANVAS_ITEM, + G_ADD_PRIVATE (GimpCanvasProgress) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS, + gimp_canvas_progress_iface_init)) + +#define parent_class gimp_canvas_progress_parent_class + + +static void +gimp_canvas_progress_class_init (GimpCanvasProgressClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_progress_finalize; + object_class->set_property = gimp_canvas_progress_set_property; + object_class->get_property = gimp_canvas_progress_get_property; + + item_class->draw = gimp_canvas_progress_draw; + item_class->get_extents = gimp_canvas_progress_get_extents; + item_class->hit = gimp_canvas_progress_hit; + + g_object_class_install_property (object_class, PROP_ANCHOR, + g_param_spec_enum ("anchor", NULL, NULL, + GIMP_TYPE_HANDLE_ANCHOR, + GIMP_HANDLE_ANCHOR_CENTER, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_canvas_progress_start; + iface->end = gimp_canvas_progress_end; + iface->is_active = gimp_canvas_progress_is_active; + iface->set_text = gimp_canvas_progress_set_text; + iface->set_value = gimp_canvas_progress_set_value; + iface->get_value = gimp_canvas_progress_get_value; + iface->pulse = gimp_canvas_progress_pulse; + iface->message = gimp_canvas_progress_message; +} + +static void +gimp_canvas_progress_init (GimpCanvasProgress *progress) +{ +} + +static void +gimp_canvas_progress_finalize (GObject *object) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->text, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_progress_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_ANCHOR: + private->anchor = g_value_get_enum (value); + break; + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_progress_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_ANCHOR: + g_value_set_enum (value, private->anchor); + break; + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static PangoLayout * +gimp_canvas_progress_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y, + gint *width, + gint *height) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + PangoLayout *layout; + + layout = gimp_canvas_get_layout (GIMP_CANVAS (canvas), "%s", + private->text); + + pango_layout_get_pixel_size (layout, width, height); + + *width = MAX (*width, 2 * RADIUS); + + *width += 2 * BORDER; + *height += 3 * BORDER + 2 * RADIUS; + + gimp_canvas_item_transform_xy_f (item, + private->x, private->y, + x, y); + + gimp_canvas_item_shift_to_north_west (private->anchor, + *x, *y, + *width, + *height, + x, y); + + *x = floor (*x); + *y = floor (*y); + + return layout; +} + +static void +gimp_canvas_progress_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + gdouble x, y; + gint width, height; + + gimp_canvas_progress_transform (item, &x, &y, &width, &height); + + cairo_move_to (cr, x, y); + cairo_line_to (cr, x + width, y); + cairo_line_to (cr, x + width, y + height - BORDER - 2 * RADIUS); + cairo_line_to (cr, x + 2 * BORDER + 2 * RADIUS, y + height - BORDER - 2 * RADIUS); + cairo_arc (cr, x + BORDER + RADIUS, y + height - BORDER - RADIUS, + BORDER + RADIUS, 0, G_PI); + cairo_close_path (cr); + + _gimp_canvas_item_fill (item, cr); + + cairo_move_to (cr, x + BORDER, y + BORDER); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); + pango_cairo_show_layout (cr, + gimp_canvas_get_layout (GIMP_CANVAS (canvas), + "%s", private->text)); + + gimp_canvas_set_tool_bg_style (gimp_canvas_item_get_canvas (item), cr); + cairo_arc (cr, x + BORDER + RADIUS, y + height - BORDER - RADIUS, + RADIUS, - G_PI / 2.0, 2 * G_PI - G_PI / 2.0); + cairo_fill (cr); + + cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0); + cairo_move_to (cr, x + BORDER + RADIUS, y + height - BORDER - RADIUS); + cairo_arc (cr, x + BORDER + RADIUS, y + height - BORDER - RADIUS, + RADIUS, - G_PI / 2.0, 2 * G_PI * private->value - G_PI / 2.0); + cairo_fill (cr); +} + +static cairo_region_t * +gimp_canvas_progress_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + gdouble x, y; + gint width, height; + + gimp_canvas_progress_transform (item, &x, &y, &width, &height); + + /* add 1px on each side because fill()'s default impl does the same */ + rectangle.x = (gint) x - 1; + rectangle.y = (gint) y - 1; + rectangle.width = width + 2; + rectangle.height = height + 2; + + return cairo_region_create_rectangle (&rectangle); +} + +static gboolean +gimp_canvas_progress_hit (GimpCanvasItem *item, + gdouble x, + gdouble y) +{ + gdouble px, py; + gint pwidth, pheight; + gdouble tx, ty; + + gimp_canvas_progress_transform (item, &px, &py, &pwidth, &pheight); + gimp_canvas_item_transform_xy_f (item, x, y, &tx, &ty); + + pheight -= BORDER + 2 * RADIUS; + + return (tx >= px && tx < (px + pwidth) && + ty >= py && ty < (py + pheight)); +} + +static GimpProgress * +gimp_canvas_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (progress); + + gimp_canvas_progress_set_text (progress, message); + + private->last_update_time = g_get_monotonic_time (); + + return progress; +} + +static void +gimp_canvas_progress_end (GimpProgress *progress) +{ +} + +static gboolean +gimp_canvas_progress_is_active (GimpProgress *progress) +{ + return TRUE; +} + +static void +gimp_canvas_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (progress); + cairo_region_t *old_region; + cairo_region_t *new_region; + + old_region = gimp_canvas_item_get_extents (GIMP_CANVAS_ITEM (progress)); + + if (private->text) + g_free (private->text); + + private->text = g_strdup (message); + + new_region = gimp_canvas_item_get_extents (GIMP_CANVAS_ITEM (progress)); + + cairo_region_union (new_region, old_region); + cairo_region_destroy (old_region); + + _gimp_canvas_item_update (GIMP_CANVAS_ITEM (progress), new_region); + + cairo_region_destroy (new_region); +} + +static void +gimp_canvas_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (progress); + + if (percentage != private->value) + { + guint64 time = g_get_monotonic_time (); + + private->value = percentage; + + if (time - private->last_update_time >= MIN_UPDATE_INTERVAL) + { + cairo_region_t *region; + + private->last_update_time = time; + + region = gimp_canvas_item_get_extents (GIMP_CANVAS_ITEM (progress)); + + _gimp_canvas_item_update (GIMP_CANVAS_ITEM (progress), region); + + cairo_region_destroy (region); + } + } +} + +static gdouble +gimp_canvas_progress_get_value (GimpProgress *progress) +{ + GimpCanvasProgressPrivate *private = GET_PRIVATE (progress); + + return private->value; +} + +static void +gimp_canvas_progress_pulse (GimpProgress *progress) +{ +} + +static gboolean +gimp_canvas_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + return FALSE; +} + +GimpCanvasItem * +gimp_canvas_progress_new (GimpDisplayShell *shell, + GimpHandleAnchor anchor, + gdouble x, + gdouble y) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_PROGRESS, + "shell", shell, + "anchor", anchor, + "x", x, + "y", y, + NULL); +} diff --git a/app/display/gimpcanvasprogress.h b/app/display/gimpcanvasprogress.h new file mode 100644 index 0000000..1f6a9c7 --- /dev/null +++ b/app/display/gimpcanvasprogress.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasprogress.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_PROGRESS_H__ +#define __GIMP_CANVAS_PROGRESS_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_PROGRESS (gimp_canvas_progress_get_type ()) +#define GIMP_CANVAS_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_PROGRESS, GimpCanvasProgress)) +#define GIMP_CANVAS_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_PROGRESS, GimpCanvasProgressClass)) +#define GIMP_IS_CANVAS_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_PROGRESS)) +#define GIMP_IS_CANVAS_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_PROGRESS)) +#define GIMP_CANVAS_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_PROGRESS, GimpCanvasProgressClass)) + + +typedef struct _GimpCanvasProgress GimpCanvasProgress; +typedef struct _GimpCanvasProgressClass GimpCanvasProgressClass; + +struct _GimpCanvasProgress +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasProgressClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_progress_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_progress_new (GimpDisplayShell *shell, + GimpHandleAnchor anchor, + gdouble x, + gdouble y); + + +#endif /* __GIMP_CANVAS_PROGRESS_H__ */ diff --git a/app/display/gimpcanvasproxygroup.c b/app/display/gimpcanvasproxygroup.c new file mode 100644 index 0000000..a979f24 --- /dev/null +++ b/app/display/gimpcanvasproxygroup.c @@ -0,0 +1,197 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasproxygroup.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvas.h" +#include "gimpcanvasproxygroup.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0 +}; + + +typedef struct _GimpCanvasProxyGroupPrivate GimpCanvasProxyGroupPrivate; + +struct _GimpCanvasProxyGroupPrivate +{ + GHashTable *proxy_hash; +}; + +#define GET_PRIVATE(proxy_group) \ + ((GimpCanvasProxyGroupPrivate *) gimp_canvas_proxy_group_get_instance_private ((GimpCanvasProxyGroup *) (proxy_group))) + + +/* local function prototypes */ + +static void gimp_canvas_proxy_group_finalize (GObject *object); +static void gimp_canvas_proxy_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_proxy_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasProxyGroup, gimp_canvas_proxy_group, + GIMP_TYPE_CANVAS_GROUP) + +#define parent_class gimp_canvas_proxy_group_parent_class + + +static void +gimp_canvas_proxy_group_class_init (GimpCanvasProxyGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_canvas_proxy_group_finalize; + object_class->set_property = gimp_canvas_proxy_group_set_property; + object_class->get_property = gimp_canvas_proxy_group_get_property; +} + +static void +gimp_canvas_proxy_group_init (GimpCanvasProxyGroup *proxy_group) +{ + GimpCanvasProxyGroupPrivate *private = GET_PRIVATE (proxy_group); + + private->proxy_hash = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +gimp_canvas_proxy_group_finalize (GObject *object) +{ + GimpCanvasProxyGroupPrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->proxy_hash, g_hash_table_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_canvas_proxy_group_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + /* GimpCanvasProxyGroupPrivate *private = GET_PRIVATE (object); */ + + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_proxy_group_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + /* GimpCanvasProxyGroupPrivate *private = GET_PRIVATE (object); */ + + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +GimpCanvasItem * +gimp_canvas_proxy_group_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_PROXY_GROUP, + "shell", shell, + NULL); +} + +void +gimp_canvas_proxy_group_add_item (GimpCanvasProxyGroup *group, + gpointer object, + GimpCanvasItem *proxy_item) +{ + GimpCanvasProxyGroupPrivate *private; + + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + g_return_if_fail (object != NULL); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (proxy_item)); + g_return_if_fail (GIMP_CANVAS_ITEM (group) != proxy_item); + + private = GET_PRIVATE (group); + + g_return_if_fail (g_hash_table_lookup (private->proxy_hash, object) == + NULL); + + g_hash_table_insert (private->proxy_hash, object, proxy_item); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (group), proxy_item); +} + +void +gimp_canvas_proxy_group_remove_item (GimpCanvasProxyGroup *group, + gpointer object) +{ + GimpCanvasProxyGroupPrivate *private; + GimpCanvasItem *proxy_item; + + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + g_return_if_fail (object != NULL); + + private = GET_PRIVATE (group); + + proxy_item = g_hash_table_lookup (private->proxy_hash, object); + + g_return_if_fail (proxy_item != NULL); + + g_hash_table_remove (private->proxy_hash, object); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (group), proxy_item); +} + +GimpCanvasItem * +gimp_canvas_proxy_group_get_item (GimpCanvasProxyGroup *group, + gpointer object) +{ + GimpCanvasProxyGroupPrivate *private; + + g_return_val_if_fail (GIMP_IS_CANVAS_GROUP (group), NULL); + g_return_val_if_fail (object != NULL, NULL); + + private = GET_PRIVATE (group); + + return g_hash_table_lookup (private->proxy_hash, object); +} diff --git a/app/display/gimpcanvasproxygroup.h b/app/display/gimpcanvasproxygroup.h new file mode 100644 index 0000000..be25787 --- /dev/null +++ b/app/display/gimpcanvasproxygroup.h @@ -0,0 +1,63 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasproxygroup.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_PROXY_GROUP_H__ +#define __GIMP_CANVAS_PROXY_GROUP_H__ + + +#include "gimpcanvasgroup.h" + + +#define GIMP_TYPE_CANVAS_PROXY_GROUP (gimp_canvas_proxy_group_get_type ()) +#define GIMP_CANVAS_PROXY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_PROXY_GROUP, GimpCanvasProxyGroup)) +#define GIMP_CANVAS_PROXY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_PROXY_GROUP, GimpCanvasProxyGroupClass)) +#define GIMP_IS_CANVAS_PROXY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_PROXY_GROUP)) +#define GIMP_IS_CANVAS_PROXY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_PROXY_GROUP)) +#define GIMP_CANVAS_PROXY_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_PROXY_GROUP, GimpCanvasProxyGroupClass)) + + +typedef struct _GimpCanvasProxyGroup GimpCanvasProxyGroup; +typedef struct _GimpCanvasProxyGroupClass GimpCanvasProxyGroupClass; + +struct _GimpCanvasProxyGroup +{ + GimpCanvasGroup parent_instance; +}; + +struct _GimpCanvasProxyGroupClass +{ + GimpCanvasGroupClass parent_class; +}; + + +GType gimp_canvas_proxy_group_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_proxy_group_new (GimpDisplayShell *shell); + +void gimp_canvas_proxy_group_add_item (GimpCanvasProxyGroup *group, + gpointer object, + GimpCanvasItem *proxy_item); +void gimp_canvas_proxy_group_remove_item (GimpCanvasProxyGroup *group, + gpointer object); +GimpCanvasItem * gimp_canvas_proxy_group_get_item (GimpCanvasProxyGroup *group, + gpointer object); + + +#endif /* __GIMP_CANVAS_PROXY_GROUP_H__ */ diff --git a/app/display/gimpcanvasrectangle.c b/app/display/gimpcanvasrectangle.c new file mode 100644 index 0000000..7ef7351 --- /dev/null +++ b/app/display/gimpcanvasrectangle.c @@ -0,0 +1,360 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasrectangle.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvasrectangle.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_FILLED +}; + + +typedef struct _GimpCanvasRectanglePrivate GimpCanvasRectanglePrivate; + +struct _GimpCanvasRectanglePrivate +{ + gdouble x; + gdouble y; + gdouble width; + gdouble height; + gboolean filled; +}; + +#define GET_PRIVATE(rectangle) \ + ((GimpCanvasRectanglePrivate *) gimp_canvas_rectangle_get_instance_private ((GimpCanvasRectangle *) (rectangle))) + + +/* local function prototypes */ + +static void gimp_canvas_rectangle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_rectangle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_rectangle_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_rectangle_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasRectangle, gimp_canvas_rectangle, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_rectangle_parent_class + + +static void +gimp_canvas_rectangle_class_init (GimpCanvasRectangleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_rectangle_set_property; + object_class->get_property = gimp_canvas_rectangle_get_property; + + item_class->draw = gimp_canvas_rectangle_draw; + item_class->get_extents = gimp_canvas_rectangle_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_double ("width", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_double ("height", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FILLED, + g_param_spec_boolean ("filled", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_rectangle_init (GimpCanvasRectangle *rectangle) +{ +} + +static void +gimp_canvas_rectangle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasRectanglePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_WIDTH: + private->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_double (value); + break; + case PROP_FILLED: + private->filled = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_rectangle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasRectanglePrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_WIDTH: + g_value_set_double (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, private->height); + break; + case PROP_FILLED: + g_value_set_boolean (value, private->filled); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_rectangle_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y, + gdouble *w, + gdouble *h) +{ + GimpCanvasRectanglePrivate *private = GET_PRIVATE (item); + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_item_transform_xy_f (item, + MIN (private->x, + private->x + private->width), + MIN (private->y, + private->y + private->height), + &x1, &y1); + gimp_canvas_item_transform_xy_f (item, + MAX (private->x, + private->x + private->width), + MAX (private->y, + private->y + private->height), + &x2, &y2); + + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + + if (private->filled) + { + *x = x1; + *y = y1; + *w = x2 - x1; + *h = y2 - y1; + } + else + { + *x = x1 + 0.5; + *y = y1 + 0.5; + *w = x2 - 0.5 - *x; + *h = y2 - 0.5 - *y; + + *w = MAX (0.0, *w); + *h = MAX (0.0, *h); + } +} + +static void +gimp_canvas_rectangle_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasRectanglePrivate *private = GET_PRIVATE (item); + gdouble x, y; + gdouble w, h; + + gimp_canvas_rectangle_transform (item, &x, &y, &w, &h); + + cairo_rectangle (cr, x, y, w, h); + + if (private->filled) + _gimp_canvas_item_fill (item, cr); + else + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_rectangle_get_extents (GimpCanvasItem *item) +{ + GimpCanvasRectanglePrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + gdouble x, y; + gdouble w, h; + + gimp_canvas_rectangle_transform (item, &x, &y, &w, &h); + + if (private->filled) + { + rectangle.x = floor (x - 1.0); + rectangle.y = floor (y - 1.0); + rectangle.width = ceil (w + 2.0); + rectangle.height = ceil (h + 2.0); + + return cairo_region_create_rectangle (&rectangle); + } + else if (w > 64 && h > 64) + { + cairo_region_t *region; + + /* left */ + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 1.5); + rectangle.width = 3.0; + rectangle.height = ceil (h + 3.0); + + region = cairo_region_create_rectangle (&rectangle); + + /* right */ + rectangle.x = floor (x + w - 1.5); + + cairo_region_union_rectangle (region, &rectangle); + + /* top */ + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 1.5); + rectangle.width = ceil (w + 3.0); + rectangle.height = 3.0; + + cairo_region_union_rectangle (region, &rectangle); + + /* bottom */ + rectangle.y = floor (y + h - 1.5); + + cairo_region_union_rectangle (region, &rectangle); + + return region; + } + else + { + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 1.5); + rectangle.width = ceil (w + 3.0); + rectangle.height = ceil (h + 3.0); + + return cairo_region_create_rectangle (&rectangle); + } +} + +GimpCanvasItem * +gimp_canvas_rectangle_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean filled) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_RECTANGLE, + "shell", shell, + "x", x, + "y", y, + "width", width, + "height", height, + "filled", filled, + NULL); +} + +void +gimp_canvas_rectangle_set (GimpCanvasItem *rectangle, + gdouble x, + gdouble y, + gdouble width, + gdouble height) +{ + g_return_if_fail (GIMP_IS_CANVAS_RECTANGLE (rectangle)); + + gimp_canvas_item_begin_change (rectangle); + + g_object_set (rectangle, + "x", x, + "y", y, + "width", width, + "height", height, + NULL); + + gimp_canvas_item_end_change (rectangle); +} diff --git a/app/display/gimpcanvasrectangle.h b/app/display/gimpcanvasrectangle.h new file mode 100644 index 0000000..6a96c06 --- /dev/null +++ b/app/display/gimpcanvasrectangle.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasrectangle.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_RECTANGLE_H__ +#define __GIMP_CANVAS_RECTANGLE_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_RECTANGLE (gimp_canvas_rectangle_get_type ()) +#define GIMP_CANVAS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_RECTANGLE, GimpCanvasRectangle)) +#define GIMP_CANVAS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_RECTANGLE, GimpCanvasRectangleClass)) +#define GIMP_IS_CANVAS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_RECTANGLE)) +#define GIMP_IS_CANVAS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_RECTANGLE)) +#define GIMP_CANVAS_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_RECTANGLE, GimpCanvasRectangleClass)) + + +typedef struct _GimpCanvasRectangle GimpCanvasRectangle; +typedef struct _GimpCanvasRectangleClass GimpCanvasRectangleClass; + +struct _GimpCanvasRectangle +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasRectangleClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_rectangle_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_rectangle_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean filled); + +void gimp_canvas_rectangle_set (GimpCanvasItem *rectangle, + gdouble x, + gdouble y, + gdouble width, + gdouble height); + + +#endif /* __GIMP_CANVAS_RECTANGLE_H__ */ diff --git a/app/display/gimpcanvasrectangleguides.c b/app/display/gimpcanvasrectangleguides.c new file mode 100644 index 0000000..b7cc07d --- /dev/null +++ b/app/display/gimpcanvasrectangleguides.c @@ -0,0 +1,422 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasrectangleguides.c + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvasrectangleguides.h" +#include "gimpdisplayshell.h" + + +#define SQRT5 2.236067977 + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_TYPE, + PROP_N_GUIDES +}; + + +typedef struct _GimpCanvasRectangleGuidesPrivate GimpCanvasRectangleGuidesPrivate; + +struct _GimpCanvasRectangleGuidesPrivate +{ + gdouble x; + gdouble y; + gdouble width; + gdouble height; + GimpGuidesType type; + gint n_guides; +}; + +#define GET_PRIVATE(rectangle) \ + ((GimpCanvasRectangleGuidesPrivate *) gimp_canvas_rectangle_guides_get_instance_private ((GimpCanvasRectangleGuides *) (rectangle))) + + +/* local function prototypes */ + +static void gimp_canvas_rectangle_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_rectangle_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_rectangle_guides_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_rectangle_guides_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasRectangleGuides, + gimp_canvas_rectangle_guides, GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_rectangle_guides_parent_class + + +static void +gimp_canvas_rectangle_guides_class_init (GimpCanvasRectangleGuidesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_rectangle_guides_set_property; + object_class->get_property = gimp_canvas_rectangle_guides_get_property; + + item_class->draw = gimp_canvas_rectangle_guides_draw; + item_class->get_extents = gimp_canvas_rectangle_guides_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_double ("width", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_double ("height", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_GUIDES_TYPE, + GIMP_GUIDES_NONE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_N_GUIDES, + g_param_spec_int ("n-guides", NULL, NULL, + 1, 128, 4, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_rectangle_guides_init (GimpCanvasRectangleGuides *rectangle) +{ +} + +static void +gimp_canvas_rectangle_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasRectangleGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_WIDTH: + private->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_double (value); + break; + case PROP_TYPE: + private->type = g_value_get_enum (value); + break; + case PROP_N_GUIDES: + private->n_guides = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_rectangle_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasRectangleGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_WIDTH: + g_value_set_double (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, private->height); + break; + case PROP_TYPE: + g_value_set_enum (value, private->type); + break; + case PROP_N_GUIDES: + g_value_set_int (value, private->n_guides); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_rectangle_guides_transform (GimpCanvasItem *item, + gdouble *x1, + gdouble *y1, + gdouble *x2, + gdouble *y2) +{ + GimpCanvasRectangleGuidesPrivate *private = GET_PRIVATE (item); + + gimp_canvas_item_transform_xy_f (item, + MIN (private->x, + private->x + private->width), + MIN (private->y, + private->y + private->height), + x1, y1); + gimp_canvas_item_transform_xy_f (item, + MAX (private->x, + private->x + private->width), + MAX (private->y, + private->y + private->height), + x2, y2); + + *x1 = floor (*x1) + 0.5; + *y1 = floor (*y1) + 0.5; + *x2 = ceil (*x2) - 0.5; + *y2 = ceil (*y2) - 0.5; + + *x2 = MAX (*x1, *x2); + *y2 = MAX (*y1, *y2); +} + +static void +draw_hline (cairo_t *cr, + gdouble x1, + gdouble x2, + gdouble y) +{ + y = floor (y) + 0.5; + + cairo_move_to (cr, x1, y); + cairo_line_to (cr, x2, y); +} + +static void +draw_vline (cairo_t *cr, + gdouble y1, + gdouble y2, + gdouble x) +{ + x = floor (x) + 0.5; + + cairo_move_to (cr, x, y1); + cairo_line_to (cr, x, y2); +} + +static void +gimp_canvas_rectangle_guides_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasRectangleGuidesPrivate *private = GET_PRIVATE (item); + gdouble x1, y1; + gdouble x2, y2; + gint i; + + gimp_canvas_rectangle_guides_transform (item, &x1, &y1, &x2, &y2); + + switch (private->type) + { + case GIMP_GUIDES_NONE: + break; + + case GIMP_GUIDES_CENTER_LINES: + draw_hline (cr, x1, x2, (y1 + y2) / 2); + draw_vline (cr, y1, y2, (x1 + x2) / 2); + break; + + case GIMP_GUIDES_THIRDS: + draw_hline (cr, x1, x2, (2 * y1 + y2) / 3); + draw_hline (cr, x1, x2, ( y1 + 2 * y2) / 3); + + draw_vline (cr, y1, y2, (2 * x1 + x2) / 3); + draw_vline (cr, y1, y2, ( x1 + 2 * x2) / 3); + break; + + case GIMP_GUIDES_FIFTHS: + for (i = 0; i < 5; i++) + { + draw_hline (cr, x1, x2, y1 + i * (y2 - y1) / 5); + draw_vline (cr, y1, y2, x1 + i * (x2 - x1) / 5); + } + break; + + case GIMP_GUIDES_GOLDEN: + draw_hline (cr, x1, x2, (2 * y1 + (1 + SQRT5) * y2) / (3 + SQRT5)); + draw_hline (cr, x1, x2, ((1 + SQRT5) * y1 + 2 * y2) / (3 + SQRT5)); + + draw_vline (cr, y1, y2, (2 * x1 + (1 + SQRT5) * x2) / (3 + SQRT5)); + draw_vline (cr, y1, y2, ((1 + SQRT5) * x1 + 2 * x2) / (3 + SQRT5)); + break; + + /* This code implements the method of diagonals discovered by + * Edwin Westhoff - see http://www.diagonalmethod.info/ + */ + case GIMP_GUIDES_DIAGONALS: + { + /* the side of the largest square that can be + * fitted in whole into the rectangle (x1, y1), (x2, y2) + */ + const gdouble square_side = MIN (x2 - x1, y2 - y1); + + /* diagonal from the top-left edge */ + cairo_move_to (cr, x1, y1); + cairo_line_to (cr, x1 + square_side, y1 + square_side); + + /* diagonal from the top-right edge */ + cairo_move_to (cr, x2, y1); + cairo_line_to (cr, x2 - square_side, y1 + square_side); + + /* diagonal from the bottom-left edge */ + cairo_move_to (cr, x1, y2); + cairo_line_to (cr, x1 + square_side, y2 - square_side); + + /* diagonal from the bottom-right edge */ + cairo_move_to (cr, x2, y2); + cairo_line_to (cr, x2 - square_side, y2 - square_side); + } + break; + + case GIMP_GUIDES_N_LINES: + for (i = 0; i < private->n_guides; i++) + { + draw_hline (cr, x1, x2, y1 + i * (y2 - y1) / private->n_guides); + draw_vline (cr, y1, y2, x1 + i * (x2 - x1) / private->n_guides); + } + break; + + case GIMP_GUIDES_SPACING: + break; + } + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_rectangle_guides_get_extents (GimpCanvasItem *item) +{ + GimpCanvasRectangleGuidesPrivate *private = GET_PRIVATE (item); + + if (private->type != GIMP_GUIDES_NONE) + { + cairo_rectangle_int_t rectangle; + gdouble x1, y1; + gdouble x2, y2; + + gimp_canvas_rectangle_guides_transform (item, &x1, &y1, &x2, &y2); + + rectangle.x = floor (x1 - 1.5); + rectangle.y = floor (y1 - 1.5); + rectangle.width = ceil (x2 - x1 + 3.0); + rectangle.height = ceil (y2 - y1 + 3.0); + + return cairo_region_create_rectangle (&rectangle); + } + + return NULL; +} + +GimpCanvasItem * +gimp_canvas_rectangle_guides_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type, + gint n_guides) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_RECTANGLE_GUIDES, + "shell", shell, + "x", x, + "y", y, + "width", width, + "height", height, + "type", type, + "n-guides", n_guides, + NULL); +} + +void +gimp_canvas_rectangle_guides_set (GimpCanvasItem *rectangle, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type, + gint n_guides) +{ + g_return_if_fail (GIMP_IS_CANVAS_RECTANGLE_GUIDES (rectangle)); + + gimp_canvas_item_begin_change (rectangle); + + g_object_set (rectangle, + "x", x, + "y", y, + "width", width, + "height", height, + "type", type, + "n-guides", n_guides, + NULL); + + gimp_canvas_item_end_change (rectangle); +} diff --git a/app/display/gimpcanvasrectangleguides.h b/app/display/gimpcanvasrectangleguides.h new file mode 100644 index 0000000..1d37d15 --- /dev/null +++ b/app/display/gimpcanvasrectangleguides.h @@ -0,0 +1,69 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasrectangleguides.h + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_RECTANGLE_GUIDES_H__ +#define __GIMP_CANVAS_RECTANGLE_GUIDES_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_RECTANGLE_GUIDES (gimp_canvas_rectangle_guides_get_type ()) +#define GIMP_CANVAS_RECTANGLE_GUIDES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_RECTANGLE_GUIDES, GimpCanvasRectangleGuides)) +#define GIMP_CANVAS_RECTANGLE_GUIDES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_RECTANGLE_GUIDES, GimpCanvasRectangleGuidesClass)) +#define GIMP_IS_CANVAS_RECTANGLE_GUIDES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_RECTANGLE_GUIDES)) +#define GIMP_IS_CANVAS_RECTANGLE_GUIDES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_RECTANGLE_GUIDES)) +#define GIMP_CANVAS_RECTANGLE_GUIDES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_RECTANGLE_GUIDES, GimpCanvasRectangleGuidesClass)) + + +typedef struct _GimpCanvasRectangleGuides GimpCanvasRectangleGuides; +typedef struct _GimpCanvasRectangleGuidesClass GimpCanvasRectangleGuidesClass; + +struct _GimpCanvasRectangleGuides +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasRectangleGuidesClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_rectangle_guides_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_rectangle_guides_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type, + gint n_guides); + +void gimp_canvas_rectangle_guides_set (GimpCanvasItem *rectangle, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type, + gint n_guides); + + +#endif /* __GIMP_CANVAS_RECTANGLE_GUIDES_H__ */ diff --git a/app/display/gimpcanvassamplepoint.c b/app/display/gimpcanvassamplepoint.c new file mode 100644 index 0000000..913a5b7 --- /dev/null +++ b/app/display/gimpcanvassamplepoint.c @@ -0,0 +1,356 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvassamplepoint.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvas.h" +#include "gimpcanvas-style.h" +#include "gimpcanvassamplepoint.h" +#include "gimpdisplayshell.h" + + +#define GIMP_SAMPLE_POINT_DRAW_SIZE 14 + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_INDEX, + PROP_SAMPLE_POINT_STYLE +}; + + +typedef struct _GimpCanvasSamplePointPrivate GimpCanvasSamplePointPrivate; + +struct _GimpCanvasSamplePointPrivate +{ + gint x; + gint y; + gint index; + gboolean sample_point_style; +}; + +#define GET_PRIVATE(sample_point) \ + ((GimpCanvasSamplePointPrivate *) gimp_canvas_sample_point_get_instance_private ((GimpCanvasSamplePoint *) (sample_point))) + + +/* local function prototypes */ + +static void gimp_canvas_sample_point_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_sample_point_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_sample_point_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_sample_point_get_extents (GimpCanvasItem *item); +static void gimp_canvas_sample_point_stroke (GimpCanvasItem *item, + cairo_t *cr); +static void gimp_canvas_sample_point_fill (GimpCanvasItem *item, + cairo_t *cr); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasSamplePoint, gimp_canvas_sample_point, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_sample_point_parent_class + + +static void +gimp_canvas_sample_point_class_init (GimpCanvasSamplePointClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_sample_point_set_property; + object_class->get_property = gimp_canvas_sample_point_get_property; + + item_class->draw = gimp_canvas_sample_point_draw; + item_class->get_extents = gimp_canvas_sample_point_get_extents; + item_class->stroke = gimp_canvas_sample_point_stroke; + item_class->fill = gimp_canvas_sample_point_fill; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_int ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_int ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_INDEX, + g_param_spec_int ("index", NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SAMPLE_POINT_STYLE, + g_param_spec_boolean ("sample-point-style", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_sample_point_init (GimpCanvasSamplePoint *sample_point) +{ +} + +static void +gimp_canvas_sample_point_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_int (value); + break; + case PROP_Y: + private->y = g_value_get_int (value); + break; + case PROP_INDEX: + private->index = g_value_get_int (value); + break; + case PROP_SAMPLE_POINT_STYLE: + private->sample_point_style = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_sample_point_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_int (value, private->x); + break; + case PROP_Y: + g_value_set_int (value, private->x); + break; + case PROP_INDEX: + g_value_set_int (value, private->x); + break; + case PROP_SAMPLE_POINT_STYLE: + g_value_set_boolean (value, private->sample_point_style); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_sample_point_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (item); + + gimp_canvas_item_transform_xy_f (item, + private->x + 0.5, + private->y + 0.5, + x, y); + + *x = floor (*x) + 0.5; + *y = floor (*y) + 0.5; +} + +#define HALF_SIZE (GIMP_SAMPLE_POINT_DRAW_SIZE / 2) + +static void +gimp_canvas_sample_point_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + PangoLayout *layout; + gdouble x, y; + gint x1, x2, y1, y2; + + gimp_canvas_sample_point_transform (item, &x, &y); + + x1 = x - GIMP_SAMPLE_POINT_DRAW_SIZE; + x2 = x + GIMP_SAMPLE_POINT_DRAW_SIZE; + y1 = y - GIMP_SAMPLE_POINT_DRAW_SIZE; + y2 = y + GIMP_SAMPLE_POINT_DRAW_SIZE; + + cairo_move_to (cr, x, y1); + cairo_line_to (cr, x, y1 + HALF_SIZE); + + cairo_move_to (cr, x, y2); + cairo_line_to (cr, x, y2 - HALF_SIZE); + + cairo_move_to (cr, x1, y); + cairo_line_to (cr, x1 + HALF_SIZE, y); + + cairo_move_to (cr, x2, y); + cairo_line_to (cr, x2 - HALF_SIZE, y); + + cairo_arc_negative (cr, x, y, HALF_SIZE, 0.0, 0.5 * G_PI); + + _gimp_canvas_item_stroke (item, cr); + + layout = gimp_canvas_get_layout (GIMP_CANVAS (canvas), + "%d", private->index); + + cairo_move_to (cr, x + 3, y + 3); + pango_cairo_show_layout (cr, layout); + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_sample_point_get_extents (GimpCanvasItem *item) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (item); + GtkWidget *canvas = gimp_canvas_item_get_canvas (item); + cairo_rectangle_int_t rectangle; + PangoLayout *layout; + PangoRectangle ink; + gdouble x, y; + gint x1, x2, y1, y2; + + gimp_canvas_sample_point_transform (item, &x, &y); + + x1 = floor (x - GIMP_SAMPLE_POINT_DRAW_SIZE); + x2 = ceil (x + GIMP_SAMPLE_POINT_DRAW_SIZE); + y1 = floor (y - GIMP_SAMPLE_POINT_DRAW_SIZE); + y2 = ceil (y + GIMP_SAMPLE_POINT_DRAW_SIZE); + + layout = gimp_canvas_get_layout (GIMP_CANVAS (canvas), + "%d", private->index); + + pango_layout_get_extents (layout, &ink, NULL); + + x2 = MAX (x2, 3 + ink.width); + y2 = MAX (y2, 3 + ink.height); + + rectangle.x = x1 - 1.5; + rectangle.y = y1 - 1.5; + rectangle.width = x2 - x1 + 3.0; + rectangle.height = y2 - y1 + 3.0; + + return cairo_region_create_rectangle (&rectangle); +} + +static void +gimp_canvas_sample_point_stroke (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (item); + + if (private->sample_point_style) + { + gimp_canvas_set_tool_bg_style (gimp_canvas_item_get_canvas (item), cr); + cairo_stroke_preserve (cr); + + gimp_canvas_set_sample_point_style (gimp_canvas_item_get_canvas (item), cr, + gimp_canvas_item_get_highlight (item)); + cairo_stroke (cr); + } + else + { + GIMP_CANVAS_ITEM_CLASS (parent_class)->stroke (item, cr); + } +} + +static void +gimp_canvas_sample_point_fill (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasSamplePointPrivate *private = GET_PRIVATE (item); + + if (private->sample_point_style) + { + gimp_canvas_set_sample_point_style (gimp_canvas_item_get_canvas (item), cr, + gimp_canvas_item_get_highlight (item)); + cairo_fill (cr); + } + else + { + GIMP_CANVAS_ITEM_CLASS (parent_class)->fill (item, cr); + } +} + +GimpCanvasItem * +gimp_canvas_sample_point_new (GimpDisplayShell *shell, + gint x, + gint y, + gint index, + gboolean sample_point_style) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_SAMPLE_POINT, + "shell", shell, + "x", x, + "y", y, + "index", index, + "sample-point-style", sample_point_style, + NULL); +} + +void +gimp_canvas_sample_point_set (GimpCanvasItem *sample_point, + gint x, + gint y) +{ + g_return_if_fail (GIMP_IS_CANVAS_SAMPLE_POINT (sample_point)); + + gimp_canvas_item_begin_change (sample_point); + + g_object_set (sample_point, + "x", x, + "y", y, + NULL); + + gimp_canvas_item_end_change (sample_point); +} diff --git a/app/display/gimpcanvassamplepoint.h b/app/display/gimpcanvassamplepoint.h new file mode 100644 index 0000000..00ac0d5 --- /dev/null +++ b/app/display/gimpcanvassamplepoint.h @@ -0,0 +1,63 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvassamplepoint.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_SAMPLE_POINT_H__ +#define __GIMP_CANVAS_SAMPLE_POINT_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_SAMPLE_POINT (gimp_canvas_sample_point_get_type ()) +#define GIMP_CANVAS_SAMPLE_POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_SAMPLE_POINT, GimpCanvasSamplePoint)) +#define GIMP_CANVAS_SAMPLE_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_SAMPLE_POINT, GimpCanvasSamplePointClass)) +#define GIMP_IS_CANVAS_SAMPLE_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_SAMPLE_POINT)) +#define GIMP_IS_CANVAS_SAMPLE_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_SAMPLE_POINT)) +#define GIMP_CANVAS_SAMPLE_POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_SAMPLE_POINT, GimpCanvasSamplePointClass)) + + +typedef struct _GimpCanvasSamplePoint GimpCanvasSamplePoint; +typedef struct _GimpCanvasSamplePointClass GimpCanvasSamplePointClass; + +struct _GimpCanvasSamplePoint +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasSamplePointClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_sample_point_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_sample_point_new (GimpDisplayShell *shell, + gint x, + gint y, + gint index, + gboolean sample_point_style); + +void gimp_canvas_sample_point_set (GimpCanvasItem *sample_point, + gint x, + gint y); + + +#endif /* __GIMP_CANVAS_SAMPLE_POINT_H__ */ diff --git a/app/display/gimpcanvastextcursor.c b/app/display/gimpcanvastextcursor.c new file mode 100644 index 0000000..aaa8c5f --- /dev/null +++ b/app/display/gimpcanvastextcursor.c @@ -0,0 +1,386 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastextcursor.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpcanvastextcursor.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_OVERWRITE, + PROP_DIRECTION +}; + + +typedef struct _GimpCanvasTextCursorPrivate GimpCanvasTextCursorPrivate; + +struct _GimpCanvasTextCursorPrivate +{ + gint x; + gint y; + gint width; + gint height; + gboolean overwrite; + GimpTextDirection direction; +}; + +#define GET_PRIVATE(text_cursor) \ + ((GimpCanvasTextCursorPrivate *) gimp_canvas_text_cursor_get_instance_private ((GimpCanvasTextCursor *) (text_cursor))) + + +/* local function prototypes */ + +static void gimp_canvas_text_cursor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_text_cursor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_text_cursor_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_text_cursor_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasTextCursor, gimp_canvas_text_cursor, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_text_cursor_parent_class + + +static void +gimp_canvas_text_cursor_class_init (GimpCanvasTextCursorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_text_cursor_set_property; + object_class->get_property = gimp_canvas_text_cursor_get_property; + + item_class->draw = gimp_canvas_text_cursor_draw; + item_class->get_extents = gimp_canvas_text_cursor_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_int ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_int ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_OVERWRITE, + g_param_spec_boolean ("overwrite", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_DIRECTION, + g_param_spec_enum ("direction", NULL, NULL, + gimp_text_direction_get_type(), + GIMP_TEXT_DIRECTION_LTR, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_text_cursor_init (GimpCanvasTextCursor *text_cursor) +{ +} + +static void +gimp_canvas_text_cursor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTextCursorPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_int (value); + break; + case PROP_Y: + private->y = g_value_get_int (value); + break; + case PROP_WIDTH: + private->width = g_value_get_int (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_int (value); + break; + case PROP_OVERWRITE: + private->overwrite = g_value_get_boolean (value); + break; + case PROP_DIRECTION: + private->direction = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_text_cursor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTextCursorPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_int (value, private->x); + break; + case PROP_Y: + g_value_set_int (value, private->y); + break; + case PROP_WIDTH: + g_value_set_int (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_int (value, private->height); + break; + case PROP_OVERWRITE: + g_value_set_boolean (value, private->overwrite); + break; + case PROP_DIRECTION: + g_value_set_enum (value, private->direction); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_text_cursor_transform (GimpCanvasItem *item, + gdouble *x, + gdouble *y, + gdouble *w, + gdouble *h) +{ + GimpCanvasTextCursorPrivate *private = GET_PRIVATE (item); + + gimp_canvas_item_transform_xy_f (item, + MIN (private->x, + private->x + private->width), + MIN (private->y, + private->y + private->height), + x, y); + gimp_canvas_item_transform_xy_f (item, + MAX (private->x, + private->x + private->width), + MAX (private->y, + private->y + private->height), + w, h); + + *w -= *x; + *h -= *y; + + *x = floor (*x) + 0.5; + *y = floor (*y) + 0.5; + switch (private->direction) + { + case GIMP_TEXT_DIRECTION_LTR: + case GIMP_TEXT_DIRECTION_RTL: + break; + case GIMP_TEXT_DIRECTION_TTB_RTL: + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + *x = *x - *w; + break; + case GIMP_TEXT_DIRECTION_TTB_LTR: + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + *y = *y + *h; + break; + } + + if (private->overwrite) + { + *w = ceil (*w) - 1.0; + *h = ceil (*h) - 1.0; + } + else + { + switch (private->direction) + { + case GIMP_TEXT_DIRECTION_LTR: + case GIMP_TEXT_DIRECTION_RTL: + *w = 0; + *h = ceil (*h) - 1.0; + break; + case GIMP_TEXT_DIRECTION_TTB_RTL: + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + *w = ceil (*w) - 1.0; + *h = 0; + break; + case GIMP_TEXT_DIRECTION_TTB_LTR: + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + *w = ceil (*w) - 1.0; + *h = 0; + break; + } + } +} + +static void +gimp_canvas_text_cursor_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasTextCursorPrivate *private = GET_PRIVATE (item); + gdouble x, y; + gdouble w, h; + + gimp_canvas_text_cursor_transform (item, &x, &y, &w, &h); + + if (private->overwrite) + { + cairo_rectangle (cr, x, y, w, h); + } + else + { + switch (private->direction) + { + case GIMP_TEXT_DIRECTION_LTR: + case GIMP_TEXT_DIRECTION_RTL: + cairo_move_to (cr, x, y); + cairo_line_to (cr, x, y + h); + + cairo_move_to (cr, x - 3.0, y); + cairo_line_to (cr, x + 3.0, y); + + cairo_move_to (cr, x - 3.0, y + h); + cairo_line_to (cr, x + 3.0, y + h); + break; + case GIMP_TEXT_DIRECTION_TTB_RTL: + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + case GIMP_TEXT_DIRECTION_TTB_LTR: + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + cairo_move_to (cr, x, y); + cairo_line_to (cr, x + w, y); + + cairo_move_to (cr, x, y - 3.0); + cairo_line_to (cr, x, y + 3.0); + + cairo_move_to (cr, x + w, y - 3.0); + cairo_line_to (cr, x + w, y + 3.0); + break; + } + } + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_text_cursor_get_extents (GimpCanvasItem *item) +{ + GimpCanvasTextCursorPrivate *private = GET_PRIVATE (item); + cairo_rectangle_int_t rectangle; + gdouble x, y; + gdouble w, h; + + gimp_canvas_text_cursor_transform (item, &x, &y, &w, &h); + + if (private->overwrite) + { + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 1.5); + rectangle.width = ceil (w + 3.0); + rectangle.height = ceil (h + 3.0); + } + else + { + switch (private->direction) + { + case GIMP_TEXT_DIRECTION_LTR: + case GIMP_TEXT_DIRECTION_RTL: + rectangle.x = floor (x - 4.5); + rectangle.y = floor (y - 1.5); + rectangle.width = ceil (9.0); + rectangle.height = ceil (h + 3.0); + break; + case GIMP_TEXT_DIRECTION_TTB_RTL: + case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: + case GIMP_TEXT_DIRECTION_TTB_LTR: + case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: + rectangle.x = floor (x - 1.5); + rectangle.y = floor (y - 4.5); + rectangle.width = ceil (w + 3.0); + rectangle.height = ceil (9.0); + break; + } + } + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_text_cursor_new (GimpDisplayShell *shell, + PangoRectangle *cursor, + gboolean overwrite, + GimpTextDirection direction) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (cursor != NULL, NULL); + + return g_object_new (GIMP_TYPE_CANVAS_TEXT_CURSOR, + "shell", shell, + "x", cursor->x, + "y", cursor->y, + "width", cursor->width, + "height", cursor->height, + "overwrite", overwrite, + "direction", direction, + NULL); +} diff --git a/app/display/gimpcanvastextcursor.h b/app/display/gimpcanvastextcursor.h new file mode 100644 index 0000000..210b2a2 --- /dev/null +++ b/app/display/gimpcanvastextcursor.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastextcursor.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_TEXT_CURSOR_H__ +#define __GIMP_CANVAS_TEXT_CURSOR_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_TEXT_CURSOR (gimp_canvas_text_cursor_get_type ()) +#define GIMP_CANVAS_TEXT_CURSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_TEXT_CURSOR, GimpCanvasTextCursor)) +#define GIMP_CANVAS_TEXT_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_TEXT_CURSOR, GimpCanvasTextCursorClass)) +#define GIMP_IS_CANVAS_TEXT_CURSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_TEXT_CURSOR)) +#define GIMP_IS_CANVAS_TEXT_CURSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_TEXT_CURSOR)) +#define GIMP_CANVAS_TEXT_CURSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_TEXT_CURSOR, GimpCanvasTextCursorClass)) + + +typedef struct _GimpCanvasTextCursor GimpCanvasTextCursor; +typedef struct _GimpCanvasTextCursorClass GimpCanvasTextCursorClass; + +struct _GimpCanvasTextCursor +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasTextCursorClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_text_cursor_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_text_cursor_new (GimpDisplayShell *shell, + PangoRectangle *cursor, + gboolean overwrite, + GimpTextDirection direction); + + +#endif /* __GIMP_CANVAS_RECTANGLE_H__ */ diff --git a/app/display/gimpcanvastransformguides.c b/app/display/gimpcanvastransformguides.c new file mode 100644 index 0000000..ba6c4cb --- /dev/null +++ b/app/display/gimpcanvastransformguides.c @@ -0,0 +1,683 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastransformguides.c + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "gimpcanvastransformguides.h" +#include "gimpdisplayshell.h" + + +#define SQRT5 2.236067977 + + +enum +{ + PROP_0, + PROP_TRANSFORM, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_TYPE, + PROP_N_GUIDES, + PROP_CLIP +}; + + +typedef struct _GimpCanvasTransformGuidesPrivate GimpCanvasTransformGuidesPrivate; + +struct _GimpCanvasTransformGuidesPrivate +{ + GimpMatrix3 transform; + gdouble x1, y1; + gdouble x2, y2; + GimpGuidesType type; + gint n_guides; + gboolean clip; +}; + +#define GET_PRIVATE(transform) \ + ((GimpCanvasTransformGuidesPrivate *) gimp_canvas_transform_guides_get_instance_private ((GimpCanvasTransformGuides *) (transform))) + + +/* local function prototypes */ + +static void gimp_canvas_transform_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_transform_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_transform_guides_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_transform_guides_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasTransformGuides, + gimp_canvas_transform_guides, GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_transform_guides_parent_class + + +static void +gimp_canvas_transform_guides_class_init (GimpCanvasTransformGuidesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_transform_guides_set_property; + object_class->get_property = gimp_canvas_transform_guides_get_property; + + item_class->draw = gimp_canvas_transform_guides_draw; + item_class->get_extents = gimp_canvas_transform_guides_get_extents; + + g_object_class_install_property (object_class, PROP_TRANSFORM, + gimp_param_spec_matrix3 ("transform", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_GUIDES_TYPE, + GIMP_GUIDES_NONE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_N_GUIDES, + g_param_spec_int ("n-guides", NULL, NULL, + 1, 128, 4, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CLIP, + g_param_spec_boolean ("clip", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_transform_guides_init (GimpCanvasTransformGuides *transform) +{ +} + +static void +gimp_canvas_transform_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_boxed (value); + + if (transform) + private->transform = *transform; + else + gimp_matrix3_identity (&private->transform); + } + break; + + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + case PROP_TYPE: + private->type = g_value_get_enum (value); + break; + + case PROP_N_GUIDES: + private->n_guides = g_value_get_int (value); + break; + + case PROP_CLIP: + private->clip = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_transform_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TRANSFORM: + g_value_set_boxed (value, &private->transform); + break; + + case PROP_X1: + g_value_set_double (value, private->x1); + break; + + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + + case PROP_X2: + g_value_set_double (value, private->x2); + break; + + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_TYPE: + g_value_set_enum (value, private->type); + break; + + case PROP_N_GUIDES: + g_value_set_int (value, private->n_guides); + break; + + case PROP_CLIP: + g_value_set_boolean (value, private->clip); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_canvas_transform_guides_transform (GimpCanvasItem *item, + GimpVector2 *t_vertices, + gint *n_t_vertices) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 vertices[4]; + + vertices[0] = (GimpVector2) { private->x1, private->y1 }; + vertices[1] = (GimpVector2) { private->x2, private->y1 }; + vertices[2] = (GimpVector2) { private->x2, private->y2 }; + vertices[3] = (GimpVector2) { private->x1, private->y2 }; + + if (private->clip) + { + gimp_transform_polygon (&private->transform, vertices, 4, TRUE, + t_vertices, n_t_vertices); + + return TRUE; + } + else + { + gint i; + + for (i = 0; i < 4; i++) + { + gimp_matrix3_transform_point (&private->transform, + vertices[i].x, vertices[i].y, + &t_vertices[i].x, &t_vertices[i].y); + } + + *n_t_vertices = 4; + + return gimp_transform_polygon_is_convex (t_vertices[0].x, t_vertices[0].y, + t_vertices[1].x, t_vertices[1].y, + t_vertices[3].x, t_vertices[3].y, + t_vertices[2].x, t_vertices[2].y); + } +} + +static void +draw_line (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 vertices[2]; + GimpVector2 t_vertices[2]; + gint n_t_vertices; + + vertices[0] = (GimpVector2) { x1, y1 }; + vertices[1] = (GimpVector2) { x2, y2 }; + + if (private->clip) + { + gimp_transform_polygon (transform, vertices, 2, FALSE, + t_vertices, &n_t_vertices); + } + else + { + gint i; + + for (i = 0; i < 2; i++) + { + gimp_matrix3_transform_point (transform, + vertices[i].x, vertices[i].y, + &t_vertices[i].x, &t_vertices[i].y); + } + + n_t_vertices = 2; + } + + if (n_t_vertices == 2) + { + gint i; + + for (i = 0; i < 2; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, + t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + v.x = floor (v.x) + 0.5; + v.y = floor (v.y) + 0.5; + + if (i == 0) + cairo_move_to (cr, v.x, v.y); + else + cairo_line_to (cr, v.x, v.y); + } + } +} + +static void +draw_hline (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble x1, + gdouble x2, + gdouble y) +{ + draw_line (cr, item, transform, x1, y, x2, y); +} + +static void +draw_vline (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble y1, + gdouble y2, + gdouble x) +{ + draw_line (cr, item, transform, x, y1, x, y2); +} + +static void +gimp_canvas_transform_guides_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 t_vertices[5]; + gint n_t_vertices; + gboolean convex; + gint i; + + convex = gimp_canvas_transform_guides_transform (item, + t_vertices, &n_t_vertices); + + if (n_t_vertices < 2) + return; + + for (i = 0; i < n_t_vertices; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + v.x = floor (v.x) + 0.5; + v.y = floor (v.y) + 0.5; + + if (i == 0) + cairo_move_to (cr, v.x, v.y); + else + cairo_line_to (cr, v.x, v.y); + } + + cairo_close_path (cr); + + if (! convex || n_t_vertices < 3) + { + _gimp_canvas_item_stroke (item, cr); + return; + } + + switch (private->type) + { + case GIMP_GUIDES_NONE: + break; + + case GIMP_GUIDES_CENTER_LINES: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (private->y1 + private->y2) / 2); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (private->x1 + private->x2) / 2); + break; + + case GIMP_GUIDES_THIRDS: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (2 * private->y1 + private->y2) / 3); + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (private->y1 + 2 * private->y2) / 3); + + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (2 * private->x1 + private->x2) / 3); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (private->x1 + 2 * private->x2) / 3); + break; + + case GIMP_GUIDES_FIFTHS: + for (i = 0; i < 5; i++) + { + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + private->y1 + i * (private->y2 - private->y1) / 5); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + private->x1 + i * (private->x2 - private->x1) / 5); + } + break; + + case GIMP_GUIDES_GOLDEN: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + (2 * private->y1 + (1 + SQRT5) * private->y2) / (3 + SQRT5)); + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + ((1 + SQRT5) * private->y1 + 2 * private->y2) / (3 + SQRT5)); + + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + (2 * private->x1 + (1 + SQRT5) * private->x2) / (3 + SQRT5)); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + ((1 + SQRT5) * private->x1 + 2 * private->x2) / (3 + SQRT5)); + break; + + /* This code implements the method of diagonals discovered by + * Edwin Westhoff - see http://www.diagonalmethod.info/ + */ + case GIMP_GUIDES_DIAGONALS: + { + /* the side of the largest square that can be + * fitted in whole into the rectangle (x1, y1), (x2, y2) + */ + const gdouble square_side = MIN (private->x2 - private->x1, + private->y2 - private->y1); + + /* diagonal from the top-left edge */ + draw_line (cr, item, &private->transform, + private->x1, private->y1, + private->x1 + square_side, + private->y1 + square_side); + + /* diagonal from the top-right edge */ + draw_line (cr, item, &private->transform, + private->x2, private->y1, + private->x2 - square_side, + private->y1 + square_side); + + /* diagonal from the bottom-left edge */ + draw_line (cr, item, &private->transform, + private->x1, private->y2, + private->x1 + square_side, + private->y2 - square_side); + + /* diagonal from the bottom-right edge */ + draw_line (cr, item, &private->transform, + private->x2, private->y2, + private->x2 - square_side, + private->y2 - square_side); + } + break; + + case GIMP_GUIDES_N_LINES: + case GIMP_GUIDES_SPACING: + { + gint width, height; + gint ngx, ngy; + + width = MAX (1, private->x2 - private->x1); + height = MAX (1, private->y2 - private->y1); + + /* the MIN() in the code below limits the grid to one line + * every 5 image pixels, see bug 772667. + */ + + if (private->type == GIMP_GUIDES_N_LINES) + { + if (width <= height) + { + ngx = private->n_guides; + ngx = MIN (ngx, width / 5); + + ngy = ngx * MAX (1, height / width); + ngy = MIN (ngy, height / 5); + } + else + { + ngy = private->n_guides; + ngy = MIN (ngy, height / 5); + + ngx = ngy * MAX (1, width / height); + ngx = MIN (ngx, width / 5); + } + } + else /* GIMP_GUIDES_SPACING */ + { + gint grid_size = MAX (2, private->n_guides); + + ngx = width / grid_size; + ngx = MIN (ngx, width / 5); + + ngy = height / grid_size; + ngy = MIN (ngy, height / 5); + } + + for (i = 1; i <= ngx; i++) + { + gdouble x = private->x1 + (((gdouble) i) / (ngx + 1) * + (private->x2 - private->x1)); + + draw_line (cr, item, &private->transform, + x, private->y1, + x, private->y2); + } + + for (i = 1; i <= ngy; i++) + { + gdouble y = private->y1 + (((gdouble) i) / (ngy + 1) * + (private->y2 - private->y1)); + + draw_line (cr, item, &private->transform, + private->x1, y, + private->x2, y); + } + } + } + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_transform_guides_get_extents (GimpCanvasItem *item) +{ + GimpVector2 t_vertices[5]; + gint n_t_vertices; + GimpVector2 top_left; + GimpVector2 bottom_right; + cairo_rectangle_int_t extents; + gint i; + + gimp_canvas_transform_guides_transform (item, t_vertices, &n_t_vertices); + + if (n_t_vertices < 2) + return cairo_region_create (); + + for (i = 0; i < n_t_vertices; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, + t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + if (i == 0) + { + top_left = bottom_right = v; + } + else + { + top_left.x = MIN (top_left.x, v.x); + top_left.y = MIN (top_left.y, v.y); + + bottom_right.x = MAX (bottom_right.x, v.x); + bottom_right.y = MAX (bottom_right.y, v.y); + } + } + + extents.x = (gint) floor (top_left.x - 1.5); + extents.y = (gint) floor (top_left.y - 1.5); + extents.width = (gint) ceil (bottom_right.x + 1.5) - extents.x; + extents.height = (gint) ceil (bottom_right.y + 1.5) - extents.y; + + return cairo_region_create_rectangle (&extents); +} + +GimpCanvasItem * +gimp_canvas_transform_guides_new (GimpDisplayShell *shell, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_GUIDES, + "shell", shell, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "type", type, + "n-guides", n_guides, + "clip", clip, + NULL); +} + +void +gimp_canvas_transform_guides_set (GimpCanvasItem *guides, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip) +{ + g_return_if_fail (GIMP_IS_CANVAS_TRANSFORM_GUIDES (guides)); + + gimp_canvas_item_begin_change (guides); + + g_object_set (guides, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "type", type, + "n-guides", n_guides, + "clip", clip, + NULL); + + gimp_canvas_item_end_change (guides); +} diff --git a/app/display/gimpcanvastransformguides.h b/app/display/gimpcanvastransformguides.h new file mode 100644 index 0000000..4358501 --- /dev/null +++ b/app/display/gimpcanvastransformguides.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastransformguides.h + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_TRANSFORM_GUIDES_H__ +#define __GIMP_CANVAS_TRANSFORM_GUIDES_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_TRANSFORM_GUIDES (gimp_canvas_transform_guides_get_type ()) +#define GIMP_CANVAS_TRANSFORM_GUIDES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_TRANSFORM_GUIDES, GimpCanvasTransformGuides)) +#define GIMP_CANVAS_TRANSFORM_GUIDES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_TRANSFORM_GUIDES, GimpCanvasTransformGuidesClass)) +#define GIMP_IS_CANVAS_TRANSFORM_GUIDES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_TRANSFORM_GUIDES)) +#define GIMP_IS_CANVAS_TRANSFORM_GUIDES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_TRANSFORM_GUIDES)) +#define GIMP_CANVAS_TRANSFORM_GUIDES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_TRANSFORM_GUIDES, GimpCanvasTransformGuidesClass)) + + +typedef struct _GimpCanvasTransformGuides GimpCanvasTransformGuides; +typedef struct _GimpCanvasTransformGuidesClass GimpCanvasTransformGuidesClass; + +struct _GimpCanvasTransformGuides +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasTransformGuidesClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_transform_guides_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_transform_guides_new (GimpDisplayShell *shell, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip); + +void gimp_canvas_transform_guides_set (GimpCanvasItem *guides, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip); + + +#endif /* __GIMP_CANVAS_TRANSFORM_GUIDES_H__ */ diff --git a/app/display/gimpcanvastransformpreview.c b/app/display/gimpcanvastransformpreview.c new file mode 100644 index 0000000..8c8d950 --- /dev/null +++ b/app/display/gimpcanvastransformpreview.c @@ -0,0 +1,791 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastransformpreview.c + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display/display-types.h" + +#include "gegl/gimp-gegl-nodes.h" +#include "gegl/gimp-gegl-utils.h" +#include "gegl/gimptilehandlervalidate.h" + +#include "core/gimp-transform-resize.h" +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" +#include "core/gimppickable.h" + +#include "gimpcanvas.h" +#include "gimpcanvastransformpreview.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_PICKABLE, + PROP_TRANSFORM, + PROP_CLIP, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_OPACITY +}; + + +typedef struct _GimpCanvasTransformPreviewPrivate GimpCanvasTransformPreviewPrivate; + +struct _GimpCanvasTransformPreviewPrivate +{ + GimpPickable *pickable; + GimpMatrix3 transform; + GimpTransformResize clip; + gdouble x1, y1; + gdouble x2, y2; + gdouble opacity; + + GeglNode *node; + GeglNode *source_node; + GeglNode *convert_format_node; + GeglNode *layer_mask_source_node; + GeglNode *layer_mask_opacity_node; + GeglNode *mask_source_node; + GeglNode *mask_translate_node; + GeglNode *mask_crop_node; + GeglNode *opacity_node; + GeglNode *cache_node; + GeglNode *transform_node; + + GimpPickable *node_pickable; + GimpDrawable *node_layer_mask; + GimpDrawable *node_mask; + GeglRectangle node_rect; + gdouble node_opacity; + GimpMatrix3 node_matrix; + GeglNode *node_output; +}; + +#define GET_PRIVATE(transform_preview) \ + ((GimpCanvasTransformPreviewPrivate *) gimp_canvas_transform_preview_get_instance_private ((GimpCanvasTransformPreview *) (transform_preview))) + + +/* local function prototypes */ + +static void gimp_canvas_transform_preview_dispose (GObject *object); +static void gimp_canvas_transform_preview_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_transform_preview_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_canvas_transform_preview_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item); + +static void gimp_canvas_transform_preview_layer_changed (GimpLayer *layer, + GimpCanvasTransformPreview *transform_preview); + +static void gimp_canvas_transform_preview_set_pickable (GimpCanvasTransformPreview *transform_preview, + GimpPickable *pickable); +static void gimp_canvas_transform_preview_sync_node (GimpCanvasTransformPreview *transform_preview); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasTransformPreview, + gimp_canvas_transform_preview, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_transform_preview_parent_class + + +/* private functions */ + + +static void +gimp_canvas_transform_preview_class_init (GimpCanvasTransformPreviewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->dispose = gimp_canvas_transform_preview_dispose; + object_class->set_property = gimp_canvas_transform_preview_set_property; + object_class->get_property = gimp_canvas_transform_preview_get_property; + + item_class->draw = gimp_canvas_transform_preview_draw; + item_class->get_extents = gimp_canvas_transform_preview_get_extents; + + g_object_class_install_property (object_class, PROP_PICKABLE, + g_param_spec_object ("pickable", + NULL, NULL, + GIMP_TYPE_PICKABLE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TRANSFORM, + gimp_param_spec_matrix3 ("transform", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CLIP, + g_param_spec_enum ("clip", + NULL, NULL, + GIMP_TYPE_TRANSFORM_RESIZE, + GIMP_TRANSFORM_RESIZE_ADJUST, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_OPACITY, + g_param_spec_double ("opacity", + NULL, NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_transform_preview_init (GimpCanvasTransformPreview *transform_preview) +{ + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview); + + private->clip = GIMP_TRANSFORM_RESIZE_ADJUST; + private->opacity = 1.0; +} + +static void +gimp_canvas_transform_preview_dispose (GObject *object) +{ + GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object); + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->node); + + gimp_canvas_transform_preview_set_pickable (transform_preview, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_canvas_transform_preview_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object); + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_PICKABLE: + gimp_canvas_transform_preview_set_pickable (transform_preview, + g_value_get_object (value)); + break; + + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_boxed (value); + + if (transform) + private->transform = *transform; + else + gimp_matrix3_identity (&private->transform); + } + break; + + case PROP_CLIP: + private->clip = g_value_get_enum (value); + break; + + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + case PROP_OPACITY: + private->opacity = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_transform_preview_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_PICKABLE: + g_value_set_object (value, private->pickable); + break; + + case PROP_TRANSFORM: + g_value_set_boxed (value, &private->transform); + break; + + case PROP_CLIP: + g_value_set_enum (value, private->clip); + break; + + case PROP_X1: + g_value_set_double (value, private->x1); + break; + + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + + case PROP_X2: + g_value_set_double (value, private->x2); + break; + + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_OPACITY: + g_value_set_double (value, private->opacity); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_canvas_transform_preview_transform (GimpCanvasItem *item, + cairo_rectangle_int_t *extents) +{ + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item); + gint x1, y1; + gint x2, y2; + gdouble tx1, ty1; + gdouble tx2, ty2; + + if (! gimp_transform_resize_boundary (&private->transform, + private->clip, + private->x1, private->y1, + private->x2, private->y2, + &x1, &y1, + &x2, &y2)) + { + return FALSE; + } + + gimp_canvas_item_transform_xy_f (item, x1, y1, &tx1, &ty1); + gimp_canvas_item_transform_xy_f (item, x2, y2, &tx2, &ty2); + + extents->x = (gint) floor (tx1); + extents->y = (gint) floor (ty1); + extents->width = (gint) ceil (tx2) - extents->x; + extents->height = (gint) ceil (ty2) - extents->y; + + return TRUE; +} + +static void +gimp_canvas_transform_preview_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (item); + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item); + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + cairo_rectangle_int_t extents; + gdouble clip_x1, clip_y1; + gdouble clip_x2, clip_y2; + GeglRectangle bounds; + cairo_surface_t *surface; + guchar *surface_data; + gint surface_stride; + + if (! gimp_canvas_transform_preview_transform (item, &extents)) + return; + + cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2); + + clip_x1 = floor (clip_x1); + clip_y1 = floor (clip_y1); + clip_x2 = ceil (clip_x2); + clip_y2 = ceil (clip_y2); + + if (! gegl_rectangle_intersect (&bounds, + GEGL_RECTANGLE (extents.x, + extents.y, + extents.width, + extents.height), + GEGL_RECTANGLE (clip_x1, + clip_y1, + clip_x2 - clip_x1, + clip_y2 - clip_y1))) + { + return; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + bounds.width, bounds.height); + + g_return_if_fail (surface != NULL); + + surface_data = cairo_image_surface_get_data (surface); + surface_stride = cairo_image_surface_get_stride (surface); + + gimp_canvas_transform_preview_sync_node (transform_preview); + + gegl_node_blit (private->node_output, 1.0, + GEGL_RECTANGLE (bounds.x + shell->offset_x, + bounds.y + shell->offset_y, + bounds.width, + bounds.height), + babl_format ("cairo-ARGB32"), surface_data, surface_stride, + GEGL_BLIT_CACHE); + + cairo_surface_mark_dirty (surface); + + cairo_set_source_surface (cr, surface, bounds.x, bounds.y); + cairo_rectangle (cr, bounds.x, bounds.y, bounds.width, bounds.height); + cairo_fill (cr); + + cairo_surface_destroy (surface); +} + +static cairo_region_t * +gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + + if (gimp_canvas_transform_preview_transform (item, &rectangle)) + return cairo_region_create_rectangle (&rectangle); + + return NULL; +} + +static void +gimp_canvas_transform_preview_layer_changed (GimpLayer *layer, + GimpCanvasTransformPreview *transform_preview) +{ + GimpCanvasItem *item = GIMP_CANVAS_ITEM (transform_preview); + + gimp_canvas_item_begin_change (item); + gimp_canvas_item_end_change (item); +} + +static void +gimp_canvas_transform_preview_set_pickable (GimpCanvasTransformPreview *transform_preview, + GimpPickable *pickable) +{ + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview); + + if (private->pickable && GIMP_IS_LAYER (private->pickable)) + { + g_signal_handlers_disconnect_by_func ( + private->pickable, + gimp_canvas_transform_preview_layer_changed, + transform_preview); + } + + g_set_object (&private->pickable, pickable); + + if (pickable && GIMP_IS_LAYER (pickable)) + { + g_signal_connect (pickable, "opacity-changed", + G_CALLBACK (gimp_canvas_transform_preview_layer_changed), + transform_preview); + g_signal_connect (pickable, "mask-changed", + G_CALLBACK (gimp_canvas_transform_preview_layer_changed), + transform_preview); + g_signal_connect (pickable, "apply-mask-changed", + G_CALLBACK (gimp_canvas_transform_preview_layer_changed), + transform_preview); + g_signal_connect (pickable, "show-mask-changed", + G_CALLBACK (gimp_canvas_transform_preview_layer_changed), + transform_preview); + } +} + +static void +gimp_canvas_transform_preview_sync_node (GimpCanvasTransformPreview *transform_preview) +{ + GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview); + GimpCanvasItem *item = GIMP_CANVAS_ITEM (transform_preview); + GimpDisplayShell *shell = gimp_canvas_item_get_shell (item); + GimpImage *image = gimp_canvas_item_get_image (item); + GimpPickable *pickable = private->pickable; + GimpDrawable *layer_mask = NULL; + GimpDrawable *mask = NULL; + gdouble opacity = private->opacity; + gint offset_x = 0; + gint offset_y = 0; + GimpMatrix3 matrix; + + if (! private->node) + { + private->node = gegl_node_new (); + + private->source_node = + gegl_node_new_child (private->node, + "operation", "gimp:buffer-source-validate", + NULL); + + private->convert_format_node = + gegl_node_new_child (private->node, + "operation", "gegl:convert-format", + NULL); + + private->layer_mask_source_node = + gegl_node_new_child (private->node, + "operation", "gimp:buffer-source-validate", + NULL); + + private->layer_mask_opacity_node = + gegl_node_new_child (private->node, + "operation", "gegl:opacity", + NULL); + + private->mask_source_node = + gegl_node_new_child (private->node, + "operation", "gimp:buffer-source-validate", + NULL); + + private->mask_translate_node = + gegl_node_new_child (private->node, + "operation", "gegl:translate", + NULL); + + private->mask_crop_node = + gegl_node_new_child (private->node, + "operation", "gegl:crop", + "width", 0.0, + "height", 0.0, + NULL); + + private->opacity_node = + gegl_node_new_child (private->node, + "operation", "gegl:opacity", + NULL); + + private->cache_node = + gegl_node_new_child (private->node, + "operation", "gegl:cache", + NULL); + + private->transform_node = + gegl_node_new_child (private->node, + "operation", "gegl:transform", + "near-z", GIMP_TRANSFORM_NEAR_Z, + "sampler", GIMP_INTERPOLATION_NONE, + NULL); + + gegl_node_link_many (private->source_node, + private->convert_format_node, + private->transform_node, + NULL); + + gegl_node_connect_to (private->layer_mask_source_node, "output", + private->layer_mask_opacity_node, "aux"); + + gegl_node_link_many (private->mask_source_node, + private->mask_translate_node, + private->mask_crop_node, + NULL); + + private->node_pickable = NULL; + private->node_layer_mask = NULL; + private->node_mask = NULL; + private->node_rect = *GEGL_RECTANGLE (0, 0, 0, 0); + private->node_opacity = 1.0; + gimp_matrix3_identity (&private->node_matrix); + private->node_output = private->transform_node; + } + + if (GIMP_IS_ITEM (pickable)) + { + gimp_item_get_offset (GIMP_ITEM (private->pickable), + &offset_x, &offset_y); + + if (gimp_item_mask_bounds (GIMP_ITEM (pickable), + NULL, NULL, NULL, NULL)) + { + mask = GIMP_DRAWABLE (gimp_image_get_mask (image)); + } + + if (GIMP_IS_LAYER (pickable)) + { + GimpLayer *layer = GIMP_LAYER (pickable); + + opacity *= gimp_layer_get_opacity (layer); + + layer_mask = GIMP_DRAWABLE (gimp_layer_get_mask (layer)); + + if (layer_mask) + { + if (gimp_layer_get_show_mask (layer) && ! mask) + { + pickable = GIMP_PICKABLE (layer_mask); + layer_mask = NULL; + } + else if (! gimp_layer_get_apply_mask (layer)) + { + layer_mask = NULL; + } + } + } + } + + gimp_matrix3_identity (&matrix); + gimp_matrix3_translate (&matrix, offset_x, offset_y); + gimp_matrix3_mult (&private->transform, &matrix); + gimp_matrix3_scale (&matrix, shell->scale_x, shell->scale_y); + + if (pickable != private->node_pickable) + { + GeglBuffer *buffer; + + gimp_pickable_flush (pickable); + + buffer = gimp_pickable_get_buffer (pickable); + + if (gimp_tile_handler_validate_get_assigned (buffer)) + buffer = gimp_gegl_buffer_dup (buffer); + else + buffer = g_object_ref (buffer); + + gegl_node_set (private->source_node, + "buffer", buffer, + NULL); + gegl_node_set (private->convert_format_node, + "format", gimp_pickable_get_format_with_alpha (pickable), + NULL); + + g_object_unref (buffer); + } + + if (layer_mask != private->node_layer_mask) + { + gegl_node_set (private->layer_mask_source_node, + "buffer", layer_mask ? + gimp_drawable_get_buffer (layer_mask) : + NULL, + NULL); + } + + if (mask) + { + GeglRectangle rect; + + rect.x = offset_x; + rect.y = offset_y; + rect.width = gimp_item_get_width (GIMP_ITEM (private->pickable)); + rect.height = gimp_item_get_height (GIMP_ITEM (private->pickable)); + + if (mask != private->node_mask) + { + gegl_node_set (private->mask_source_node, + "buffer", gimp_drawable_get_buffer (mask), + NULL); + } + + if (! gegl_rectangle_equal (&rect, &private->node_rect)) + { + private->node_rect = rect; + + gegl_node_set (private->mask_translate_node, + "x", (gdouble) -rect.x, + "y", (gdouble) -rect.y, + NULL); + + gegl_node_set (private->mask_crop_node, + "width", (gdouble) rect.width, + "height", (gdouble) rect.height, + NULL); + } + + if (! private->node_mask) + { + gegl_node_connect_to (private->mask_crop_node, "output", + private->opacity_node, "aux"); + } + } + else if (private->node_mask) + { + gegl_node_disconnect (private->opacity_node, "aux"); + } + + if (opacity != private->node_opacity) + { + gegl_node_set (private->opacity_node, + "value", opacity, + NULL); + } + + if (layer_mask != private->node_layer_mask || + mask != private->node_mask || + (opacity != 1.0) != (private->node_opacity != 1.0)) + { + GeglNode *output = private->source_node; + + if (layer_mask && ! mask) + { + gegl_node_link (output, private->layer_mask_opacity_node); + output = private->layer_mask_opacity_node; + } + else + { + gegl_node_disconnect (private->layer_mask_opacity_node, "input"); + } + + if (mask || (opacity != 1.0)) + { + gegl_node_link (output, private->opacity_node); + output = private->opacity_node; + } + else + { + gegl_node_disconnect (private->opacity_node, "input"); + } + + if (output == private->source_node) + { + gegl_node_disconnect (private->cache_node, "input"); + + gegl_node_link (output, private->convert_format_node); + output = private->convert_format_node; + } + else + { + gegl_node_disconnect (private->convert_format_node, "input"); + + gegl_node_link (output, private->cache_node); + output = private->cache_node; + } + + gegl_node_link (output, private->transform_node); + output = private->transform_node; + + if (layer_mask && mask) + { + gegl_node_link (output, private->layer_mask_opacity_node); + output = private->layer_mask_opacity_node; + } + + private->node_output = output; + } + + if (memcmp (&matrix, &private->node_matrix, sizeof (matrix))) + { + private->node_matrix = matrix; + + gimp_gegl_node_set_matrix (private->transform_node, &matrix); + } + + private->node_pickable = pickable; + private->node_layer_mask = layer_mask; + private->node_mask = mask; + private->node_opacity = opacity; +} + + +/* public functions */ + + +GimpCanvasItem * +gimp_canvas_transform_preview_new (GimpDisplayShell *shell, + GimpPickable *pickable, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); + g_return_val_if_fail (transform != NULL, NULL); + + return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW, + "shell", shell, + "pickable", pickable, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} diff --git a/app/display/gimpcanvastransformpreview.h b/app/display/gimpcanvastransformpreview.h new file mode 100644 index 0000000..a82d50c --- /dev/null +++ b/app/display/gimpcanvastransformpreview.h @@ -0,0 +1,61 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastransformpreview.h + * Copyright (C) 2011 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CANVAS_TRANSFORM_PREVIEW_H__ +#define __GIMP_CANVAS_TRANSFORM_PREVIEW_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW (gimp_canvas_transform_preview_get_type ()) +#define GIMP_CANVAS_TRANSFORM_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW, GimpCanvasTransformPreview)) +#define GIMP_CANVAS_TRANSFORM_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW, GimpCanvasTransformPreviewClass)) +#define GIMP_IS_CANVAS_TRANSFORM_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW)) +#define GIMP_IS_CANVAS_TRANSFORM_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW)) +#define GIMP_CANVAS_TRANSFORM_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW, GimpCanvasTransformPreviewClass)) + + +typedef struct _GimpCanvasTransformPreview GimpCanvasTransformPreview; +typedef struct _GimpCanvasTransformPreviewClass GimpCanvasTransformPreviewClass; + +struct _GimpCanvasTransformPreview +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasTransformPreviewClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_transform_preview_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_transform_preview_new (GimpDisplayShell *shell, + GimpPickable *pickable, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + + +#endif /* __GIMP_CANVAS_TRANSFORM_PREVIEW_H__ */ diff --git a/app/display/gimpcursorview.c b/app/display/gimpcursorview.c new file mode 100644 index 0000000..846df91 --- /dev/null +++ b/app/display/gimpcursorview.c @@ -0,0 +1,887 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcursorview.c + * Copyright (C) 2005-2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-pick-color.h" +#include "core/gimpitem.h" + +#include "widgets/gimpcolorframe.h" +#include "widgets/gimpdocked.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpsessioninfo-aux.h" + +#include "gimpcursorview.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_SAMPLE_MERGED +}; + + +struct _GimpCursorViewPrivate +{ + GimpEditor parent_instance; + + GtkWidget *coord_hbox; + GtkWidget *selection_hbox; + GtkWidget *color_hbox; + + GtkWidget *pixel_x_label; + GtkWidget *pixel_y_label; + GtkWidget *unit_x_label; + GtkWidget *unit_y_label; + GtkWidget *selection_x_label; + GtkWidget *selection_y_label; + GtkWidget *selection_width_label; + GtkWidget *selection_height_label; + GtkWidget *color_frame_1; + GtkWidget *color_frame_2; + + gboolean sample_merged; + + GimpContext *context; + GimpDisplayShell *shell; + GimpImage *image; + GimpUnit unit; + + guint cursor_idle_id; + GimpImage *cursor_image; + GimpUnit cursor_unit; + gdouble cursor_x; + gdouble cursor_y; +}; + + +static void gimp_cursor_view_docked_iface_init (GimpDockedInterface *iface); + +static void gimp_cursor_view_dispose (GObject *object); +static void gimp_cursor_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_cursor_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_cursor_view_style_set (GtkWidget *widget, + GtkStyle *prev_style); + +static void gimp_cursor_view_set_aux_info (GimpDocked *docked, + GList *aux_info); +static GList * gimp_cursor_view_get_aux_info (GimpDocked *docked); + +static void gimp_cursor_view_set_context (GimpDocked *docked, + GimpContext *context); +static void gimp_cursor_view_image_changed (GimpCursorView *view, + GimpImage *image, + GimpContext *context); +static void gimp_cursor_view_mask_changed (GimpCursorView *view, + GimpImage *image); +static void gimp_cursor_view_diplay_changed (GimpCursorView *view, + GimpDisplay *display, + GimpContext *context); +static void gimp_cursor_view_shell_unit_changed (GimpCursorView *view, + GParamSpec *pspec, + GimpDisplayShell *shell); +static void gimp_cursor_view_format_as_unit (GimpUnit unit, + gchar *output_buf, + gint output_buf_size, + gdouble pixel_value, + gdouble image_res); +static void gimp_cursor_view_set_label_italic (GtkWidget *label, + gboolean italic); +static void gimp_cursor_view_update_selection_info (GimpCursorView *view, + GimpImage *image, + GimpUnit unit); +static gboolean gimp_cursor_view_cursor_idle (GimpCursorView *view); + + +G_DEFINE_TYPE_WITH_CODE (GimpCursorView, gimp_cursor_view, GIMP_TYPE_EDITOR, + G_ADD_PRIVATE (GimpCursorView) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED, + gimp_cursor_view_docked_iface_init)) + +#define parent_class gimp_cursor_view_parent_class + +static GimpDockedInterface *parent_docked_iface = NULL; + + +static void +gimp_cursor_view_class_init (GimpCursorViewClass* klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gimp_cursor_view_dispose; + object_class->get_property = gimp_cursor_view_get_property; + object_class->set_property = gimp_cursor_view_set_property; + + widget_class->style_set = gimp_cursor_view_style_set; + + g_object_class_install_property (object_class, PROP_SAMPLE_MERGED, + g_param_spec_boolean ("sample-merged", + NULL, NULL, + TRUE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_cursor_view_init (GimpCursorView *view) +{ + GtkWidget *frame; + GtkWidget *table; + GtkWidget *toggle; + gint content_spacing; + + view->priv = gimp_cursor_view_get_instance_private (view); + + view->priv->sample_merged = TRUE; + view->priv->context = NULL; + view->priv->shell = NULL; + view->priv->image = NULL; + view->priv->unit = GIMP_UNIT_PIXEL; + view->priv->cursor_idle_id = 0; + + gtk_widget_style_get (GTK_WIDGET (view), + "content-spacing", &content_spacing, + NULL); + + + /* cursor information */ + + view->priv->coord_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, + content_spacing); + gtk_box_set_homogeneous (GTK_BOX (view->priv->coord_hbox), TRUE); + gtk_box_pack_start (GTK_BOX (view), view->priv->coord_hbox, + FALSE, FALSE, 0); + gtk_widget_show (view->priv->coord_hbox); + + view->priv->selection_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, + content_spacing); + gtk_box_set_homogeneous (GTK_BOX (view->priv->selection_hbox), TRUE); + gtk_box_pack_start (GTK_BOX (view), view->priv->selection_hbox, + FALSE, FALSE, 0); + gtk_widget_show (view->priv->selection_hbox); + + + /* Pixels */ + + frame = gimp_frame_new (_("Pixels")); + gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 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), 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + view->priv->pixel_x_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_x_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("X"), 0.5, 0.5, + view->priv->pixel_x_label, 1, FALSE); + + view->priv->pixel_y_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_y_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Y"), 0.5, 0.5, + view->priv->pixel_y_label, 1, FALSE); + + + /* Units */ + + frame = gimp_frame_new (_("Units")); + gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + view->priv->unit_x_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->unit_x_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("X"), 0.5, 0.5, + view->priv->unit_x_label, 1, FALSE); + + view->priv->unit_y_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->unit_y_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Y"), 0.5, 0.5, + view->priv->unit_y_label, 1, FALSE); + + + /* Selection Bounding Box */ + + frame = gimp_frame_new (_("Selection")); + gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + gimp_help_set_help_data (frame, _("The selection's bounding box"), NULL); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + view->priv->selection_x_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->selection_x_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("X"), 0.5, 0.5, + view->priv->selection_x_label, 1, FALSE); + + view->priv->selection_y_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->selection_y_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Y"), 0.5, 0.5, + view->priv->selection_y_label, 1, FALSE); + + frame = gimp_frame_new (""); + gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_table_set_row_spacings (GTK_TABLE (table), 2); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + view->priv->selection_width_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->selection_width_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + /* Width */ + _("W"), 0.5, 0.5, + view->priv->selection_width_label, 1, FALSE); + + view->priv->selection_height_label = gtk_label_new (_("n/a")); + gtk_label_set_xalign (GTK_LABEL (view->priv->selection_height_label), 1.0); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + /* Height */ + _("H"), 0.5, 0.5, + view->priv->selection_height_label, 1, FALSE); + + + /* color information */ + + view->priv->color_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, + content_spacing); + gtk_box_set_homogeneous (GTK_BOX (view->priv->color_hbox), TRUE); + gtk_box_pack_start (GTK_BOX (view), view->priv->color_hbox, FALSE, FALSE, 0); + gtk_widget_show (view->priv->color_hbox); + + view->priv->color_frame_1 = gimp_color_frame_new (); + gimp_color_frame_set_mode (GIMP_COLOR_FRAME (view->priv->color_frame_1), + GIMP_COLOR_PICK_MODE_PIXEL); + gimp_color_frame_set_ellipsize (GIMP_COLOR_FRAME (view->priv->color_frame_1), + PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_1, + TRUE, TRUE, 0); + gtk_widget_show (view->priv->color_frame_1); + + view->priv->color_frame_2 = gimp_color_frame_new (); + gimp_color_frame_set_mode (GIMP_COLOR_FRAME (view->priv->color_frame_2), + GIMP_COLOR_PICK_MODE_RGB_PERCENT); + gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_2, + TRUE, TRUE, 0); + gtk_widget_show (view->priv->color_frame_2); + + /* sample merged toggle */ + + toggle = gimp_prop_check_button_new (G_OBJECT (view), "sample-merged", + _("_Sample Merged")); + gtk_box_pack_start (GTK_BOX (view), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); +} + +static void +gimp_cursor_view_docked_iface_init (GimpDockedInterface *iface) +{ + parent_docked_iface = g_type_interface_peek_parent (iface); + + if (! parent_docked_iface) + parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED); + + iface->set_aux_info = gimp_cursor_view_set_aux_info; + iface->get_aux_info = gimp_cursor_view_get_aux_info; + iface->set_context = gimp_cursor_view_set_context; +} + +static void +gimp_cursor_view_dispose (GObject *object) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (object); + + if (view->priv->context) + gimp_docked_set_context (GIMP_DOCKED (view), NULL); + + if (view->priv->cursor_idle_id) + { + g_source_remove (view->priv->cursor_idle_id); + view->priv->cursor_idle_id = 0; + } + + g_clear_object (&view->priv->cursor_image); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_cursor_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (object); + + switch (property_id) + { + case PROP_SAMPLE_MERGED: + view->priv->sample_merged = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_cursor_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (object); + + switch (property_id) + { + case PROP_SAMPLE_MERGED: + g_value_set_boolean (value, view->priv->sample_merged); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +#define AUX_INFO_FRAME_1_MODE "frame-1-mode" +#define AUX_INFO_FRAME_2_MODE "frame-2-mode" + +static void +gimp_cursor_view_set_aux_info (GimpDocked *docked, + GList *aux_info) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (docked); + GList *list; + + parent_docked_iface->set_aux_info (docked, aux_info); + + for (list = aux_info; list; list = g_list_next (list)) + { + GimpSessionInfoAux *aux = list->data; + GtkWidget *frame = NULL; + + if (! strcmp (aux->name, AUX_INFO_FRAME_1_MODE)) + frame = view->priv->color_frame_1; + else if (! strcmp (aux->name, AUX_INFO_FRAME_2_MODE)) + frame = view->priv->color_frame_2; + + if (frame) + { + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_peek (GIMP_TYPE_COLOR_PICK_MODE); + enum_value = g_enum_get_value_by_nick (enum_class, aux->value); + + if (enum_value) + gimp_color_frame_set_mode (GIMP_COLOR_FRAME (frame), + enum_value->value); + } + } +} + +static GList * +gimp_cursor_view_get_aux_info (GimpDocked *docked) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (docked); + GList *aux_info; + const gchar *nick; + GimpSessionInfoAux *aux; + + aux_info = parent_docked_iface->get_aux_info (docked); + + if (gimp_enum_get_value (GIMP_TYPE_COLOR_PICK_MODE, + GIMP_COLOR_FRAME (view->priv->color_frame_1)->pick_mode, + NULL, &nick, NULL, NULL)) + { + aux = gimp_session_info_aux_new (AUX_INFO_FRAME_1_MODE, nick); + aux_info = g_list_append (aux_info, aux); + } + + if (gimp_enum_get_value (GIMP_TYPE_COLOR_PICK_MODE, + GIMP_COLOR_FRAME (view->priv->color_frame_2)->pick_mode, + NULL, &nick, NULL, NULL)) + { + aux = gimp_session_info_aux_new (AUX_INFO_FRAME_2_MODE, nick); + aux_info = g_list_append (aux_info, aux); + } + + return aux_info; +} + +static void +gimp_cursor_view_format_as_unit (GimpUnit unit, + gchar *output_buf, + gint output_buf_size, + gdouble pixel_value, + gdouble image_res) +{ + gchar format_buf[32]; + gdouble value; + gint unit_digits = 0; + const gchar *unit_str = ""; + + value = gimp_pixels_to_units (pixel_value, unit, image_res); + + if (unit != GIMP_UNIT_PIXEL) + { + unit_digits = gimp_unit_get_scaled_digits (unit, image_res); + unit_str = gimp_unit_get_abbreviation (unit); + } + + g_snprintf (format_buf, sizeof (format_buf), + "%%.%df %s", unit_digits, unit_str); + + g_snprintf (output_buf, output_buf_size, format_buf, value); +} + +static void +gimp_cursor_view_set_label_italic (GtkWidget *label, + gboolean italic) +{ + PangoStyle attribute = italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL; + + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_STYLE, attribute, + -1); +} + +static void +gimp_cursor_view_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (widget); + gint content_spacing; + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + gtk_widget_style_get (GTK_WIDGET (view), + "content-spacing", &content_spacing, + NULL); + + gtk_box_set_spacing (GTK_BOX (view->priv->coord_hbox), content_spacing); + gtk_box_set_spacing (GTK_BOX (view->priv->selection_hbox), content_spacing); + gtk_box_set_spacing (GTK_BOX (view->priv->color_hbox), content_spacing); +} + +static void +gimp_cursor_view_set_context (GimpDocked *docked, + GimpContext *context) +{ + GimpCursorView *view = GIMP_CURSOR_VIEW (docked); + GimpColorConfig *config = NULL; + GimpDisplay *display = NULL; + GimpImage *image = NULL; + + if (context == view->priv->context) + return; + + if (view->priv->context) + { + g_signal_handlers_disconnect_by_func (view->priv->context, + gimp_cursor_view_diplay_changed, + view); + g_signal_handlers_disconnect_by_func (view->priv->context, + gimp_cursor_view_image_changed, + view); + + g_object_unref (view->priv->context); + } + + view->priv->context = context; + + if (view->priv->context) + { + g_object_ref (view->priv->context); + + g_signal_connect_swapped (view->priv->context, "display-changed", + G_CALLBACK (gimp_cursor_view_diplay_changed), + view); + + g_signal_connect_swapped (view->priv->context, "image-changed", + G_CALLBACK (gimp_cursor_view_image_changed), + view); + + config = context->gimp->config->color_management; + display = gimp_context_get_display (context); + image = gimp_context_get_image (context); + } + + gimp_color_frame_set_color_config (GIMP_COLOR_FRAME (view->priv->color_frame_1), + config); + gimp_color_frame_set_color_config (GIMP_COLOR_FRAME (view->priv->color_frame_2), + config); + + gimp_cursor_view_diplay_changed (view, display, view->priv->context); + gimp_cursor_view_image_changed (view, image, view->priv->context); +} + +static void +gimp_cursor_view_image_changed (GimpCursorView *view, + GimpImage *image, + GimpContext *context) +{ + g_return_if_fail (GIMP_IS_CURSOR_VIEW (view)); + + if (image == view->priv->image) + return; + + if (view->priv->image) + { + g_signal_handlers_disconnect_by_func (view->priv->image, + gimp_cursor_view_mask_changed, + view); + } + + view->priv->image = image; + + if (view->priv->image) + { + g_signal_connect_swapped (view->priv->image, "mask-changed", + G_CALLBACK (gimp_cursor_view_mask_changed), + view); + } + + gimp_cursor_view_mask_changed (view, view->priv->image); +} + +static void +gimp_cursor_view_mask_changed (GimpCursorView *view, + GimpImage *image) +{ + gimp_cursor_view_update_selection_info (view, + view->priv->image, + view->priv->unit); +} + +static void +gimp_cursor_view_diplay_changed (GimpCursorView *view, + GimpDisplay *display, + GimpContext *context) +{ + GimpDisplayShell *shell = NULL; + + if (display) + shell = gimp_display_get_shell (display); + + if (view->priv->shell) + { + g_signal_handlers_disconnect_by_func (view->priv->shell, + gimp_cursor_view_shell_unit_changed, + view); + } + + view->priv->shell = shell; + + if (view->priv->shell) + { + g_signal_connect_swapped (view->priv->shell, "notify::unit", + G_CALLBACK (gimp_cursor_view_shell_unit_changed), + view); + } + + gimp_cursor_view_shell_unit_changed (view, + NULL, + view->priv->shell); +} + +static void +gimp_cursor_view_shell_unit_changed (GimpCursorView *view, + GParamSpec *pspec, + GimpDisplayShell *shell) +{ + GimpUnit new_unit = GIMP_UNIT_PIXEL; + + if (shell) + { + new_unit = gimp_display_shell_get_unit (shell); + } + + if (view->priv->unit != new_unit) + { + gimp_cursor_view_update_selection_info (view, view->priv->image, new_unit); + view->priv->unit = new_unit; + } +} + +static void +gimp_cursor_view_update_selection_info (GimpCursorView *view, + GimpImage *image, + GimpUnit unit) +{ + gint x, y, width, height; + + if (image && + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height)) + { + gdouble xres, yres; + gchar buf[32]; + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres); + gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label), buf); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres); + gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label), buf); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), width, xres); + gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label), buf); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), height, yres); + gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label), buf); + } + else + { + gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label), + _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label), + _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label), + _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label), + _("n/a")); + } +} + +static gboolean +gimp_cursor_view_cursor_idle (GimpCursorView *view) +{ + + if (view->priv->cursor_image) + { + GimpImage *image = view->priv->cursor_image; + GimpUnit unit = view->priv->cursor_unit; + gdouble x = view->priv->cursor_x; + gdouble y = view->priv->cursor_y; + gboolean in_image; + gchar buf[32]; + const Babl *sample_format; + gdouble pixel[4]; + GimpRGB color; + gdouble xres; + gdouble yres; + gint int_x; + gint int_y; + + if (unit == GIMP_UNIT_PIXEL) + unit = gimp_image_get_unit (image); + + gimp_image_get_resolution (image, &xres, &yres); + + in_image = (x >= 0.0 && x < gimp_image_get_width (image) && + y >= 0.0 && y < gimp_image_get_height (image)); + + g_snprintf (buf, sizeof (buf), "%d", (gint) floor (x)); + gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), buf); + gimp_cursor_view_set_label_italic (view->priv->pixel_x_label, ! in_image); + + g_snprintf (buf, sizeof (buf), "%d", (gint) floor (y)); + gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), buf); + gimp_cursor_view_set_label_italic (view->priv->pixel_y_label, ! in_image); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres); + gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label), buf); + gimp_cursor_view_set_label_italic (view->priv->unit_x_label, ! in_image); + + gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres); + gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label), buf); + gimp_cursor_view_set_label_italic (view->priv->unit_y_label, ! in_image); + + int_x = (gint) floor (x); + int_y = (gint) floor (y); + + if (gimp_image_pick_color (image, NULL, + int_x, int_y, + view->priv->shell->show_all, + view->priv->sample_merged, + FALSE, 0.0, + &sample_format, pixel, &color)) + { + gimp_color_frame_set_color (GIMP_COLOR_FRAME (view->priv->color_frame_1), + FALSE, sample_format, pixel, &color, + int_x, int_y); + gimp_color_frame_set_color (GIMP_COLOR_FRAME (view->priv->color_frame_2), + FALSE, sample_format, pixel, &color, + int_x, int_y); + } + else + { + gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_1)); + gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_2)); + } + + /* Show the selection info from the image under the cursor if any */ + gimp_cursor_view_update_selection_info (view, + image, + view->priv->cursor_unit); + + g_clear_object (&view->priv->cursor_image); + } + else + { + gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label), _("n/a")); + gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label), _("n/a")); + + gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_1)); + gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_2)); + + /* Start showing selection info from the active image again */ + gimp_cursor_view_update_selection_info (view, + view->priv->image, + view->priv->unit); + } + + view->priv->cursor_idle_id = 0; + + return G_SOURCE_REMOVE; +} + + +/* public functions */ + +GtkWidget * +gimp_cursor_view_new (GimpMenuFactory *menu_factory) +{ + g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL); + + return g_object_new (GIMP_TYPE_CURSOR_VIEW, + "menu-factory", menu_factory, + "menu-identifier", "", + "ui-path", "/cursor-info-popup", + NULL); +} + +void +gimp_cursor_view_set_sample_merged (GimpCursorView *view, + gboolean sample_merged) +{ + g_return_if_fail (GIMP_IS_CURSOR_VIEW (view)); + + sample_merged = sample_merged ? TRUE : FALSE; + + if (view->priv->sample_merged != sample_merged) + { + view->priv->sample_merged = sample_merged; + + g_object_notify (G_OBJECT (view), "sample-merged"); + } +} + +gboolean +gimp_cursor_view_get_sample_merged (GimpCursorView *view) +{ + g_return_val_if_fail (GIMP_IS_CURSOR_VIEW (view), FALSE); + + return view->priv->sample_merged; +} + +void +gimp_cursor_view_update_cursor (GimpCursorView *view, + GimpImage *image, + GimpUnit shell_unit, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CURSOR_VIEW (view)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_clear_object (&view->priv->cursor_image); + + view->priv->cursor_image = g_object_ref (image); + view->priv->cursor_unit = shell_unit; + view->priv->cursor_x = x; + view->priv->cursor_y = y; + + if (view->priv->cursor_idle_id == 0) + { + view->priv->cursor_idle_id = + g_idle_add ((GSourceFunc) gimp_cursor_view_cursor_idle, view); + } +} + +void +gimp_cursor_view_clear_cursor (GimpCursorView *view) +{ + g_return_if_fail (GIMP_IS_CURSOR_VIEW (view)); + + g_clear_object (&view->priv->cursor_image); + + if (view->priv->cursor_idle_id == 0) + { + view->priv->cursor_idle_id = + g_idle_add ((GSourceFunc) gimp_cursor_view_cursor_idle, view); + } +} diff --git a/app/display/gimpcursorview.h b/app/display/gimpcursorview.h new file mode 100644 index 0000000..f538041 --- /dev/null +++ b/app/display/gimpcursorview.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcursorview.h + * Copyright (C) 2005-2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CURSOR_VIEW_H__ +#define __GIMP_CURSOR_VIEW_H__ + + +#include "widgets/gimpeditor.h" + + +#define GIMP_TYPE_CURSOR_VIEW (gimp_cursor_view_get_type ()) +#define GIMP_CURSOR_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CURSOR_VIEW, GimpCursorView)) +#define GIMP_CURSOR_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CURSOR_VIEW, GimpCursorViewClass)) +#define GIMP_IS_CURSOR_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CURSOR_VIEW)) +#define GIMP_IS_CURSOR_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CURSOR_VIEW)) +#define GIMP_CURSOR_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CURSOR_VIEW, GimpCursorViewClass)) + + +typedef struct _GimpCursorViewClass GimpCursorViewClass; +typedef struct _GimpCursorViewPrivate GimpCursorViewPrivate; + +struct _GimpCursorView +{ + GimpEditor parent_instance; + + GimpCursorViewPrivate *priv; +}; + +struct _GimpCursorViewClass +{ + GimpEditorClass parent_class; +}; + + +GType gimp_cursor_view_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_cursor_view_new (GimpMenuFactory *menu_factory); + +void gimp_cursor_view_set_sample_merged (GimpCursorView *view, + gboolean sample_merged); +gboolean gimp_cursor_view_get_sample_merged (GimpCursorView *view); + +void gimp_cursor_view_update_cursor (GimpCursorView *view, + GimpImage *image, + GimpUnit shell_unit, + gdouble x, + gdouble y); +void gimp_cursor_view_clear_cursor (GimpCursorView *view); + + +#endif /* __GIMP_CURSOR_VIEW_H__ */ diff --git a/app/display/gimpdisplay-foreach.c b/app/display/gimpdisplay-foreach.c new file mode 100644 index 0000000..440113e --- /dev/null +++ b/app/display/gimpdisplay-foreach.c @@ -0,0 +1,308 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimplist.h" + +#include "gimpdisplay.h" +#include "gimpdisplay-foreach.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-cursor.h" + + +gboolean +gimp_displays_dirty (Gimp *gimp) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + GimpImage *image = gimp_display_get_image (display); + + if (image && gimp_image_is_dirty (image)) + return TRUE; + } + + return FALSE; +} + +static void +gimp_displays_image_dirty_callback (GimpImage *image, + GimpDirtyMask dirty_mask, + GimpContainer *container) +{ + if (gimp_image_is_dirty (image) && + gimp_image_get_display_count (image) > 0 && + ! gimp_container_have (container, GIMP_OBJECT (image))) + gimp_container_add (container, GIMP_OBJECT (image)); +} + +static void +gimp_displays_dirty_images_disconnect (GimpContainer *dirty_container, + GimpContainer *global_container) +{ + GQuark handler; + + handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dirty_container), + "clean-handler")); + gimp_container_remove_handler (global_container, handler); + + handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dirty_container), + "dirty-handler")); + gimp_container_remove_handler (global_container, handler); +} + +static void +gimp_displays_image_clean_callback (GimpImage *image, + GimpDirtyMask dirty_mask, + GimpContainer *container) +{ + if (! gimp_image_is_dirty (image)) + gimp_container_remove (container, GIMP_OBJECT (image)); +} + +GimpContainer * +gimp_displays_get_dirty_images (Gimp *gimp) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (gimp_displays_dirty (gimp)) + { + GimpContainer *container = gimp_list_new_weak (GIMP_TYPE_IMAGE, FALSE); + GList *list; + GQuark handler; + + handler = + gimp_container_add_handler (gimp->images, "clean", + G_CALLBACK (gimp_displays_image_dirty_callback), + container); + g_object_set_data (G_OBJECT (container), "clean-handler", + GINT_TO_POINTER (handler)); + + handler = + gimp_container_add_handler (gimp->images, "dirty", + G_CALLBACK (gimp_displays_image_dirty_callback), + container); + g_object_set_data (G_OBJECT (container), "dirty-handler", + GINT_TO_POINTER (handler)); + + g_signal_connect_object (container, "disconnect", + G_CALLBACK (gimp_displays_dirty_images_disconnect), + G_OBJECT (gimp->images), 0); + + gimp_container_add_handler (container, "clean", + G_CALLBACK (gimp_displays_image_clean_callback), + container); + gimp_container_add_handler (container, "dirty", + G_CALLBACK (gimp_displays_image_clean_callback), + container); + + for (list = gimp_get_image_iter (gimp); + list; + list = g_list_next (list)) + { + GimpImage *image = list->data; + + if (gimp_image_is_dirty (image) && + gimp_image_get_display_count (image) > 0) + gimp_container_add (container, GIMP_OBJECT (image)); + } + + return container; + } + + return NULL; +} + +/** + * gimp_displays_delete: + * @gimp: + * + * Calls gimp_display_delete() an all displays in the display list. + * This closes all displays, including the first one which is usually + * kept open. + */ +void +gimp_displays_delete (Gimp *gimp) +{ + /* this removes the GimpDisplay from the list, so do a while loop + * "around" the first element to get them all + */ + while (! gimp_container_is_empty (gimp->displays)) + { + GimpDisplay *display = gimp_get_display_iter (gimp)->data; + + gimp_display_delete (display); + } +} + +/** + * gimp_displays_close: + * @gimp: + * + * Calls gimp_display_close() an all displays in the display list. The + * first display will remain open without an image. + */ +void +gimp_displays_close (Gimp *gimp) +{ + GList *list; + GList *iter; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + list = g_list_copy (gimp_get_display_iter (gimp)); + + for (iter = list; iter; iter = g_list_next (iter)) + { + GimpDisplay *display = iter->data; + + gimp_display_close (display); + } + + g_list_free (list); +} + +void +gimp_displays_reconnect (Gimp *gimp, + GimpImage *old, + GimpImage *new) +{ + GList *contexts = NULL; + GList *list; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_IMAGE (old)); + g_return_if_fail (GIMP_IS_IMAGE (new)); + + /* check which contexts refer to old_image */ + for (list = gimp->context_list; list; list = g_list_next (list)) + { + GimpContext *context = list->data; + + if (gimp_context_get_image (context) == old) + contexts = g_list_prepend (contexts, list->data); + } + + /* set the new_image on the remembered contexts (in reverse order, + * since older contexts are usually the parents of newer + * ones). Also, update the contexts before the displays, or we + * might run into menu update functions that would see an + * inconsistent state (display = new, context = old), and thus + * inadvertently call actions as if the user had selected a menu + * item. + */ + g_list_foreach (contexts, (GFunc) gimp_context_set_image, new); + g_list_free (contexts); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + if (gimp_display_get_image (display) == old) + gimp_display_set_image (display, new); + } +} + +gint +gimp_displays_get_num_visible (Gimp *gimp) +{ + GList *list; + gint visible = 0; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + GimpDisplayShell *shell = gimp_display_get_shell (display); + + if (gtk_widget_is_drawable (GTK_WIDGET (shell))) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + if (GTK_IS_WINDOW (toplevel)) + { + GdkWindow *window = gtk_widget_get_window (toplevel); + GdkWindowState state = gdk_window_get_state (window); + + if ((state & (GDK_WINDOW_STATE_WITHDRAWN | + GDK_WINDOW_STATE_ICONIFIED)) == 0) + { + visible++; + } + } + } + } + + return visible; +} + +void +gimp_displays_set_busy (Gimp *gimp) +{ + GList *list; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplayShell *shell = + gimp_display_get_shell (GIMP_DISPLAY (list->data)); + + gimp_display_shell_set_override_cursor (shell, (GimpCursorType) GDK_WATCH); + } +} + +void +gimp_displays_unset_busy (Gimp *gimp) +{ + GList *list; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplayShell *shell = + gimp_display_get_shell (GIMP_DISPLAY (list->data)); + + gimp_display_shell_unset_override_cursor (shell); + } +} diff --git a/app/display/gimpdisplay-foreach.h b/app/display/gimpdisplay-foreach.h new file mode 100644 index 0000000..09fae7c --- /dev/null +++ b/app/display/gimpdisplay-foreach.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 . + */ + +#ifndef __GIMP_DISPLAY_FOREACH_H__ +#define __GIMP_DISPLAY_FOREACH_H__ + + +gboolean gimp_displays_dirty (Gimp *gimp); +GimpContainer * gimp_displays_get_dirty_images (Gimp *gimp); +void gimp_displays_delete (Gimp *gimp); +void gimp_displays_close (Gimp *gimp); +void gimp_displays_reconnect (Gimp *gimp, + GimpImage *old, + GimpImage *new); + +gint gimp_displays_get_num_visible (Gimp *gimp); + +void gimp_displays_set_busy (Gimp *gimp); +void gimp_displays_unset_busy (Gimp *gimp); + + +#endif /* __GIMP_DISPLAY_FOREACH_H__ */ diff --git a/app/display/gimpdisplay-handlers.c b/app/display/gimpdisplay-handlers.c new file mode 100644 index 0000000..94cb863 --- /dev/null +++ b/app/display/gimpdisplay-handlers.c @@ -0,0 +1,128 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "gimpdisplay.h" +#include "gimpdisplay-handlers.h" + + +/* local function prototypes */ + +static void gimp_display_update_handler (GimpProjection *projection, + gboolean now, + gint x, + gint y, + gint w, + gint h, + GimpDisplay *display); + +static void gimp_display_bounds_changed_handler (GimpImage *image, + gint old_x, + gint old_y, + GimpDisplay *display); +static void gimp_display_flush_handler (GimpImage *image, + gboolean invalidate_preview, + GimpDisplay *display); + + +/* public functions */ + +void +gimp_display_connect (GimpDisplay *display) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + image = gimp_display_get_image (display); + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_connect (gimp_image_get_projection (image), "update", + G_CALLBACK (gimp_display_update_handler), + display); + + g_signal_connect (image, "bounds-changed", + G_CALLBACK (gimp_display_bounds_changed_handler), + display); + g_signal_connect (image, "flush", + G_CALLBACK (gimp_display_flush_handler), + display); +} + +void +gimp_display_disconnect (GimpDisplay *display) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + image = gimp_display_get_image (display); + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + g_signal_handlers_disconnect_by_func (image, + gimp_display_flush_handler, + display); + g_signal_handlers_disconnect_by_func (image, + gimp_display_bounds_changed_handler, + display); + + g_signal_handlers_disconnect_by_func (gimp_image_get_projection (image), + gimp_display_update_handler, + display); +} + + +/* private functions */ + +static void +gimp_display_update_handler (GimpProjection *projection, + gboolean now, + gint x, + gint y, + gint w, + gint h, + GimpDisplay *display) +{ + gimp_display_update_area (display, now, x, y, w, h); +} + +static void +gimp_display_bounds_changed_handler (GimpImage *image, + gint old_x, + gint old_y, + GimpDisplay *display) +{ + gimp_display_update_bounding_box (display); +} + +static void +gimp_display_flush_handler (GimpImage *image, + gboolean invalidate_preview, + GimpDisplay *display) +{ + gimp_display_flush (display); +} diff --git a/app/display/gimpdisplay-handlers.h b/app/display/gimpdisplay-handlers.h new file mode 100644 index 0000000..34c547c --- /dev/null +++ b/app/display/gimpdisplay-handlers.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 . + */ + +#ifndef __GIMP_DISPLAY_HANDLERS_H__ +#define __GIMP_DISPLAY_HANDLERS_H__ + + +void gimp_display_connect (GimpDisplay *display); +void gimp_display_disconnect (GimpDisplay *display); + + +#endif /* __GIMP_DISPLAY_HANDLERS_H__ */ diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c new file mode 100644 index 0000000..bf8a238 --- /dev/null +++ b/app/display/gimpdisplay.c @@ -0,0 +1,985 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" +#include "tools/tools-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpprogress.h" + +#include "widgets/gimpdialogfactory.h" + +#include "tools/gimptool.h" +#include "tools/tool_manager.h" + +#include "gimpdisplay.h" +#include "gimpdisplay-handlers.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-handlers.h" +#include "gimpdisplayshell-icon.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-scrollbars.h" +#include "gimpdisplayshell-title.h" +#include "gimpdisplayshell-transform.h" +#include "gimpimagewindow.h" + +#include "gimp-intl.h" + + +#define FLUSH_NOW_INTERVAL (G_TIME_SPAN_SECOND / 60) + +#define PAINT_AREA_CHUNK_WIDTH 32 +#define PAINT_AREA_CHUNK_HEIGHT 32 + + +enum +{ + PROP_0, + PROP_ID, + PROP_GIMP, + PROP_IMAGE, + PROP_SHELL +}; + + +typedef struct _GimpDisplayPrivate GimpDisplayPrivate; + +struct _GimpDisplayPrivate +{ + gint ID; /* unique identifier for this display */ + + GimpImage *image; /* pointer to the associated image */ + gint instance; /* the instance # of this display as + * taken from the image at creation */ + + GeglRectangle bounding_box; + + GtkWidget *shell; + cairo_region_t *update_region; + + guint64 last_flush_now; +}; + +#define GIMP_DISPLAY_GET_PRIVATE(display) \ + ((GimpDisplayPrivate *) gimp_display_get_instance_private ((GimpDisplay *) (display))) + + +/* local function prototypes */ + +static void gimp_display_progress_iface_init (GimpProgressInterface *iface); + +static void gimp_display_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_display_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static GimpProgress * gimp_display_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message); +static void gimp_display_progress_end (GimpProgress *progress); +static gboolean gimp_display_progress_is_active (GimpProgress *progress); +static void gimp_display_progress_set_text (GimpProgress *progress, + const gchar *message); +static void gimp_display_progress_set_value (GimpProgress *progress, + gdouble percentage); +static gdouble gimp_display_progress_get_value (GimpProgress *progress); +static void gimp_display_progress_pulse (GimpProgress *progress); +static guint32 gimp_display_progress_get_window_id (GimpProgress *progress); +static gboolean gimp_display_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); +static void gimp_display_progress_canceled (GimpProgress *progress, + GimpDisplay *display); + +static void gimp_display_flush_whenever (GimpDisplay *display, + gboolean now); +static void gimp_display_paint_area (GimpDisplay *display, + gint x, + gint y, + gint w, + gint h); + + +G_DEFINE_TYPE_WITH_CODE (GimpDisplay, gimp_display, GIMP_TYPE_OBJECT, + G_ADD_PRIVATE (GimpDisplay) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS, + gimp_display_progress_iface_init)) + +#define parent_class gimp_display_parent_class + + +static void +gimp_display_class_init (GimpDisplayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_display_set_property; + object_class->get_property = gimp_display_get_property; + + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_int ("id", + NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", + NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_IMAGE, + g_param_spec_object ("image", + NULL, NULL, + GIMP_TYPE_IMAGE, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_SHELL, + g_param_spec_object ("shell", + NULL, NULL, + GIMP_TYPE_DISPLAY_SHELL, + GIMP_PARAM_READABLE)); +} + +static void +gimp_display_init (GimpDisplay *display) +{ +} + +static void +gimp_display_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_display_progress_start; + iface->end = gimp_display_progress_end; + iface->is_active = gimp_display_progress_is_active; + iface->set_text = gimp_display_progress_set_text; + iface->set_value = gimp_display_progress_set_value; + iface->get_value = gimp_display_progress_get_value; + iface->pulse = gimp_display_progress_pulse; + iface->get_window_id = gimp_display_progress_get_window_id; + iface->message = gimp_display_progress_message; +} + +static void +gimp_display_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDisplay *display = GIMP_DISPLAY (object); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + switch (property_id) + { + case PROP_GIMP: + { + gint ID; + + display->gimp = g_value_get_object (value); /* don't ref the gimp */ + display->config = GIMP_DISPLAY_CONFIG (display->gimp->config); + + do + { + ID = display->gimp->next_display_ID++; + + if (display->gimp->next_display_ID == G_MAXINT) + display->gimp->next_display_ID = 1; + } + while (gimp_display_get_by_ID (display->gimp, ID)); + + private->ID = ID; + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDisplay *display = GIMP_DISPLAY (object); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + switch (property_id) + { + case PROP_ID: + g_value_set_int (value, private->ID); + break; + + case PROP_GIMP: + g_value_set_object (value, display->gimp); + break; + + case PROP_IMAGE: + g_value_set_object (value, private->image); + break; + + case PROP_SHELL: + g_value_set_object (value, private->shell); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GimpProgress * +gimp_display_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + return gimp_progress_start (GIMP_PROGRESS (private->shell), cancellable, + "%s", message); + + return NULL; +} + +static void +gimp_display_progress_end (GimpProgress *progress) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + gimp_progress_end (GIMP_PROGRESS (private->shell)); +} + +static gboolean +gimp_display_progress_is_active (GimpProgress *progress) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + return gimp_progress_is_active (GIMP_PROGRESS (private->shell)); + + return FALSE; +} + +static void +gimp_display_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + gimp_progress_set_text_literal (GIMP_PROGRESS (private->shell), message); +} + +static void +gimp_display_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + gimp_progress_set_value (GIMP_PROGRESS (private->shell), percentage); +} + +static gdouble +gimp_display_progress_get_value (GimpProgress *progress) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + return gimp_progress_get_value (GIMP_PROGRESS (private->shell)); + + return 0.0; +} + +static void +gimp_display_progress_pulse (GimpProgress *progress) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + gimp_progress_pulse (GIMP_PROGRESS (private->shell)); +} + +static guint32 +gimp_display_progress_get_window_id (GimpProgress *progress) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + return gimp_progress_get_window_id (GIMP_PROGRESS (private->shell)); + + return 0; +} + +static gboolean +gimp_display_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GimpDisplay *display = GIMP_DISPLAY (progress); + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->shell) + return gimp_progress_message (GIMP_PROGRESS (private->shell), gimp, + severity, domain, message); + + return FALSE; +} + +static void +gimp_display_progress_canceled (GimpProgress *progress, + GimpDisplay *display) +{ + gimp_progress_cancel (GIMP_PROGRESS (display)); +} + + +/* public functions */ + +GimpDisplay * +gimp_display_new (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GimpUIManager *popup_manager, + GimpDialogFactory *dialog_factory, + GdkScreen *screen, + gint monitor) +{ + GimpDisplay *display; + GimpDisplayPrivate *private; + GimpImageWindow *window = NULL; + GimpDisplayShell *shell; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + /* If there isn't an interface, never create a display */ + if (gimp->no_interface) + return NULL; + + display = g_object_new (GIMP_TYPE_DISPLAY, + "gimp", gimp, + NULL); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + /* refs the image */ + if (image) + gimp_display_set_image (display, image); + + /* get an image window */ + if (GIMP_GUI_CONFIG (display->config)->single_window_mode) + { + GimpDisplay *active_display; + + active_display = gimp_context_get_display (gimp_get_user_context (gimp)); + + if (! active_display) + { + active_display = + GIMP_DISPLAY (gimp_container_get_first_child (gimp->displays)); + } + + if (active_display) + { + GimpDisplayShell *shell = gimp_display_get_shell (active_display); + + window = gimp_display_shell_get_window (shell); + } + } + + if (! window) + { + window = gimp_image_window_new (gimp, + private->image, + dialog_factory, + screen, + monitor); + } + + /* create the shell for the image */ + private->shell = gimp_display_shell_new (display, unit, scale, + popup_manager, + screen, + monitor); + + shell = gimp_display_get_shell (display); + + gimp_display_update_bounding_box (display); + + gimp_image_window_add_shell (window, shell); + gimp_display_shell_present (shell); + + /* make sure the docks are visible, in case all other image windows + * are iconified, see bug #686544. + */ + gimp_dialog_factory_show_with_display (dialog_factory); + + g_signal_connect (gimp_display_shell_get_statusbar (shell), "cancel", + G_CALLBACK (gimp_display_progress_canceled), + display); + + /* add the display to the list */ + gimp_container_add (gimp->displays, GIMP_OBJECT (display)); + + return display; +} + +/** + * gimp_display_delete: + * @display: + * + * Closes the display and removes it from the display list. You should + * not call this function directly, use gimp_display_close() instead. + */ +void +gimp_display_delete (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + GimpTool *active_tool; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + /* remove the display from the list */ + gimp_container_remove (display->gimp->displays, GIMP_OBJECT (display)); + + /* unrefs the image */ + gimp_display_set_image (display, NULL); + + active_tool = tool_manager_get_active (display->gimp); + + if (active_tool && active_tool->focus_display == display) + tool_manager_focus_display_active (display->gimp, NULL); + + if (private->shell) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + /* set private->shell to NULL *before* destroying the shell. + * all callbacks in gimpdisplayshell-callbacks.c will check + * this pointer and do nothing if the shell is in destruction. + */ + private->shell = NULL; + + if (window) + { + if (gimp_image_window_get_n_shells (window) > 1) + { + g_object_ref (shell); + + gimp_image_window_remove_shell (window, shell); + gtk_widget_destroy (GTK_WIDGET (shell)); + + g_object_unref (shell); + } + else + { + gimp_image_window_destroy (window); + } + } + else + { + g_object_unref (shell); + } + } + + g_object_unref (display); +} + +/** + * gimp_display_close: + * @display: + * + * Closes the display. If this is the last display, it will remain + * open, but without an image. + */ +void +gimp_display_close (GimpDisplay *display) +{ + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + if (gimp_container_get_n_children (display->gimp->displays) > 1) + { + gimp_display_delete (display); + } + else + { + gimp_display_empty (display); + } +} + +gint +gimp_display_get_ID (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + + g_return_val_if_fail (GIMP_IS_DISPLAY (display), -1); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + return private->ID; +} + +GimpDisplay * +gimp_display_get_by_ID (Gimp *gimp, + gint ID) +{ + GList *list; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + if (gimp_display_get_ID (display) == ID) + return display; + } + + return NULL; +} + +/** + * gimp_display_get_action_name: + * @display: + * + * Returns: The action name for the given display. The action name + * depends on the display ID. The result must be freed with g_free(). + **/ +gchar * +gimp_display_get_action_name (GimpDisplay *display) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); + + return g_strdup_printf ("windows-display-%04d", + gimp_display_get_ID (display)); +} + +Gimp * +gimp_display_get_gimp (GimpDisplay *display) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); + + return display->gimp; +} + +GimpImage * +gimp_display_get_image (GimpDisplay *display) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); + + return GIMP_DISPLAY_GET_PRIVATE (display)->image; +} + +void +gimp_display_set_image (GimpDisplay *display, + GimpImage *image) +{ + GimpDisplayPrivate *private; + GimpImage *old_image = NULL; + GimpDisplayShell *shell; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + shell = gimp_display_get_shell (display); + + if (private->image) + { + /* stop any active tool */ + tool_manager_control_active (display->gimp, GIMP_TOOL_ACTION_HALT, + display); + + gimp_display_shell_disconnect (shell); + + gimp_display_disconnect (display); + + g_clear_pointer (&private->update_region, cairo_region_destroy); + + gimp_image_dec_display_count (private->image); + + /* set private->image before unrefing because there may be code + * that listens for image removals and then iterates the + * display list to find a valid display. + */ + old_image = private->image; + +#if 0 + g_print ("%s: image->ref_count before unrefing: %d\n", + G_STRFUNC, G_OBJECT (old_image)->ref_count); +#endif + } + + private->image = image; + + if (image) + { +#if 0 + g_print ("%s: image->ref_count before refing: %d\n", + G_STRFUNC, G_OBJECT (image)->ref_count); +#endif + + g_object_ref (image); + + private->instance = gimp_image_get_instance_count (image); + gimp_image_inc_instance_count (image); + + gimp_image_inc_display_count (image); + + gimp_display_connect (display); + + if (shell) + gimp_display_shell_connect (shell); + } + + if (old_image) + g_object_unref (old_image); + + gimp_display_update_bounding_box (display); + + if (shell) + { + if (image) + { + gimp_display_shell_reconnect (shell); + } + else + { + gimp_display_shell_title_update (shell); + gimp_display_shell_icon_update (shell); + } + } + + if (old_image != image) + g_object_notify (G_OBJECT (display), "image"); +} + +gint +gimp_display_get_instance (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + + g_return_val_if_fail (GIMP_IS_DISPLAY (display), 0); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + return private->instance; +} + +GimpDisplayShell * +gimp_display_get_shell (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + return GIMP_DISPLAY_SHELL (private->shell); +} + +void +gimp_display_empty (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + GList *iter; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + g_return_if_fail (GIMP_IS_IMAGE (private->image)); + + for (iter = display->gimp->context_list; iter; iter = g_list_next (iter)) + { + GimpContext *context = iter->data; + + if (gimp_context_get_display (context) == display) + gimp_context_set_image (context, NULL); + } + + gimp_display_set_image (display, NULL); + + gimp_display_shell_empty (gimp_display_get_shell (display)); +} + +void +gimp_display_fill (GimpDisplay *display, + GimpImage *image, + GimpUnit unit, + gdouble scale) +{ + GimpDisplayPrivate *private; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + g_return_if_fail (private->image == NULL); + + gimp_display_set_image (display, image); + + gimp_display_shell_fill (gimp_display_get_shell (display), + image, unit, scale); +} + +void +gimp_display_update_bounding_box (GimpDisplay *display) +{ + GimpDisplayPrivate *private; + GimpDisplayShell *shell; + GeglRectangle bounding_box = {}; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + shell = gimp_display_get_shell (display); + + if (shell) + { + bounding_box = gimp_display_shell_get_bounding_box (shell); + + if (! gegl_rectangle_equal (&bounding_box, &private->bounding_box)) + { + GeglRectangle diff_rects[4]; + gint n_diff_rects; + gint i; + + n_diff_rects = gegl_rectangle_subtract (diff_rects, + &private->bounding_box, + &bounding_box); + + for (i = 0; i < n_diff_rects; i++) + { + gimp_display_paint_area (display, + diff_rects[i].x, diff_rects[i].y, + diff_rects[i].width, diff_rects[i].height); + } + + private->bounding_box = bounding_box; + + gimp_display_shell_scroll_clamp_and_update (shell); + gimp_display_shell_scrollbars_update (shell); + } + } + else + { + private->bounding_box = bounding_box; + } +} + +void +gimp_display_update_area (GimpDisplay *display, + gboolean now, + gint x, + gint y, + gint w, + gint h) +{ + GimpDisplayPrivate *private; + + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (now) + { + gimp_display_paint_area (display, x, y, w, h); + } + else + { + cairo_rectangle_int_t rect; + gint image_width; + gint image_height; + + image_width = gimp_image_get_width (private->image); + image_height = gimp_image_get_height (private->image); + + rect.x = CLAMP (x, 0, image_width); + rect.y = CLAMP (y, 0, image_height); + rect.width = CLAMP (x + w, 0, image_width) - rect.x; + rect.height = CLAMP (y + h, 0, image_height) - rect.y; + + if (private->update_region) + cairo_region_union_rectangle (private->update_region, &rect); + else + private->update_region = cairo_region_create_rectangle (&rect); + } +} + +void +gimp_display_flush (GimpDisplay *display) +{ + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + /* FIXME: we can end up being called during shell construction if "show all" + * is enabled by default, in which case the shell's display pointer is still + * NULL + */ + if (gimp_display_get_shell (display)) + gimp_display_flush_whenever (display, FALSE); +} + +void +gimp_display_flush_now (GimpDisplay *display) +{ + g_return_if_fail (GIMP_IS_DISPLAY (display)); + + gimp_display_flush_whenever (display, TRUE); +} + + +/* private functions */ + +static void +gimp_display_flush_whenever (GimpDisplay *display, + gboolean now) +{ + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + + if (private->update_region) + { + gint n_rects = cairo_region_num_rectangles (private->update_region); + gint i; + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (private->update_region, + i, &rect); + + gimp_display_paint_area (display, + rect.x, + rect.y, + rect.width, + rect.height); + } + + g_clear_pointer (&private->update_region, cairo_region_destroy); + } + + if (now) + { + guint64 now = g_get_monotonic_time (); + + if ((now - private->last_flush_now) > FLUSH_NOW_INTERVAL) + { + gimp_display_shell_flush (gimp_display_get_shell (display), TRUE); + + private->last_flush_now = now; + } + } + else + { + gimp_display_shell_flush (gimp_display_get_shell (display), now); + } +} + +static void +gimp_display_paint_area (GimpDisplay *display, + gint x, + gint y, + gint w, + gint h) +{ + GimpDisplayPrivate *private = GIMP_DISPLAY_GET_PRIVATE (display); + GimpDisplayShell *shell = gimp_display_get_shell (display); + GeglRectangle rect; + gint x1, y1, x2, y2; + gdouble x1_f, y1_f, x2_f, y2_f; + + if (! gegl_rectangle_intersect (&rect, + &private->bounding_box, + GEGL_RECTANGLE (x, y, w, h))) + { + return; + } + + /* display the area */ + gimp_display_shell_transform_bounds (shell, + rect.x, + rect.y, + rect.x + rect.width, + rect.y + rect.height, + &x1_f, &y1_f, &x2_f, &y2_f); + + /* make sure to expose a superset of the transformed sub-pixel expose + * area, not a subset. bug #126942. --mitch + * + * also accommodate for spill introduced by potential box filtering. + * (bug #474509). --simon + */ + x1 = floor (x1_f - 0.5); + y1 = floor (y1_f - 0.5); + x2 = ceil (x2_f + 0.5); + y2 = ceil (y2_f + 0.5); + + /* align transformed area to a coarse grid, to simplify the + * invalidated area + */ + x1 = floor ((gdouble) x1 / PAINT_AREA_CHUNK_WIDTH) * PAINT_AREA_CHUNK_WIDTH; + y1 = floor ((gdouble) y1 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT; + x2 = ceil ((gdouble) x2 / PAINT_AREA_CHUNK_WIDTH) * PAINT_AREA_CHUNK_WIDTH; + y2 = ceil ((gdouble) y2 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT; + + gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1); +} diff --git a/app/display/gimpdisplay.h b/app/display/gimpdisplay.h new file mode 100644 index 0000000..7e676b0 --- /dev/null +++ b/app/display/gimpdisplay.h @@ -0,0 +1,99 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_H__ +#define __GIMP_DISPLAY_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_DISPLAY (gimp_display_get_type ()) +#define GIMP_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DISPLAY, GimpDisplay)) +#define GIMP_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DISPLAY, GimpDisplayClass)) +#define GIMP_IS_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DISPLAY)) +#define GIMP_IS_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DISPLAY)) +#define GIMP_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DISPLAY, GimpDisplayClass)) + + +typedef struct _GimpDisplayClass GimpDisplayClass; + +struct _GimpDisplay +{ + GimpObject parent_instance; + + Gimp *gimp; + GimpDisplayConfig *config; + +}; + +struct _GimpDisplayClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_display_get_type (void) G_GNUC_CONST; + +GimpDisplay * gimp_display_new (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GimpUIManager *popup_manager, + GimpDialogFactory *dialog_factory, + GdkScreen *screen, + gint monitor); +void gimp_display_delete (GimpDisplay *display); +void gimp_display_close (GimpDisplay *display); + +gint gimp_display_get_ID (GimpDisplay *display); +GimpDisplay * gimp_display_get_by_ID (Gimp *gimp, + gint ID); + +gchar * gimp_display_get_action_name (GimpDisplay *display); + +Gimp * gimp_display_get_gimp (GimpDisplay *display); + +GimpImage * gimp_display_get_image (GimpDisplay *display); +void gimp_display_set_image (GimpDisplay *display, + GimpImage *image); + +gint gimp_display_get_instance (GimpDisplay *display); + +GimpDisplayShell * gimp_display_get_shell (GimpDisplay *display); + +void gimp_display_empty (GimpDisplay *display); +void gimp_display_fill (GimpDisplay *display, + GimpImage *image, + GimpUnit unit, + gdouble scale); + +void gimp_display_update_bounding_box + (GimpDisplay *display); + +void gimp_display_update_area (GimpDisplay *display, + gboolean now, + gint x, + gint y, + gint w, + gint h); + +void gimp_display_flush (GimpDisplay *display); +void gimp_display_flush_now (GimpDisplay *display); + + +#endif /* __GIMP_DISPLAY_H__ */ diff --git a/app/display/gimpdisplayshell-actions.c b/app/display/gimpdisplayshell-actions.c new file mode 100644 index 0000000..fa0b5ef --- /dev/null +++ b/app/display/gimpdisplayshell-actions.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpuimanager.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-actions.h" +#include "gimpimagewindow.h" + + +void +gimp_display_shell_set_action_sensitive (GimpDisplayShell *shell, + const gchar *action, + gboolean sensitive) +{ + GimpImageWindow *window; + GimpContext *context; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (action != NULL); + + window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (manager, "view"); + + if (action_group) + gimp_action_group_set_action_sensitive (action_group, action, sensitive); + } + + context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (context)) + { + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (shell->popup_manager, + "view"); + + if (action_group) + gimp_action_group_set_action_sensitive (action_group, action, sensitive); + } +} + +void +gimp_display_shell_set_action_active (GimpDisplayShell *shell, + const gchar *action, + gboolean active) +{ + GimpImageWindow *window; + GimpContext *context; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (action != NULL); + + window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (manager, "view"); + + if (action_group) + gimp_action_group_set_action_active (action_group, action, active); + } + + context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (context)) + { + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (shell->popup_manager, + "view"); + + if (action_group) + gimp_action_group_set_action_active (action_group, action, active); + } +} + +void +gimp_display_shell_set_action_color (GimpDisplayShell *shell, + const gchar *action, + const GimpRGB *color) +{ + GimpImageWindow *window; + GimpContext *context; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (action != NULL); + + window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (manager, "view"); + + if (action_group) + gimp_action_group_set_action_color (action_group, action, color, FALSE); + } + + context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (context)) + { + GimpActionGroup *action_group; + + action_group = gimp_ui_manager_get_action_group (shell->popup_manager, + "view"); + + if (action_group) + gimp_action_group_set_action_color (action_group, action, color, FALSE); + } +} diff --git a/app/display/gimpdisplayshell-actions.h b/app/display/gimpdisplayshell-actions.h new file mode 100644 index 0000000..5d4a4da --- /dev/null +++ b/app/display/gimpdisplayshell-actions.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_ACTIONS_H__ +#define __GIMP_DISPLAY_SHELL_ACTIONS_H__ + + +void gimp_display_shell_set_action_sensitive (GimpDisplayShell *shell, + const gchar *action, + gboolean sensitive); +void gimp_display_shell_set_action_active (GimpDisplayShell *shell, + const gchar *action, + gboolean active); +void gimp_display_shell_set_action_color (GimpDisplayShell *shell, + const gchar *action, + const GimpRGB *color); + + +#endif /* __GIMP_DISPLAY_SHELL_ACTIONS_H__ */ diff --git a/app/display/gimpdisplayshell-appearance.c b/app/display/gimpdisplayshell-appearance.c new file mode 100644 index 0000000..1080acf --- /dev/null +++ b/app/display/gimpdisplayshell-appearance.c @@ -0,0 +1,606 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "config/gimpdisplayoptions.h" + +#include "core/gimpimage.h" + +#include "widgets/gimpdockcolumns.h" +#include "widgets/gimprender.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvas.h" +#include "gimpcanvasitem.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-actions.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-selection.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-scrollbars.h" +#include "gimpimagewindow.h" +#include "gimpstatusbar.h" + + +/* local function prototypes */ + +static GimpDisplayOptions * appearance_get_options (GimpDisplayShell *shell); + + +/* public functions */ + +void +gimp_display_shell_appearance_update (GimpDisplayShell *shell) +{ + GimpDisplayOptions *options; + GimpImageWindow *window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpDockColumns *left_docks; + GimpDockColumns *right_docks; + gboolean fullscreen; + gboolean has_grip; + + fullscreen = gimp_image_window_get_fullscreen (window); + + gimp_display_shell_set_action_active (shell, "view-fullscreen", + fullscreen); + + left_docks = gimp_image_window_get_left_docks (window); + right_docks = gimp_image_window_get_right_docks (window); + + has_grip = (! fullscreen && + ! (left_docks && gimp_dock_columns_get_docks (left_docks)) && + ! (right_docks && gimp_dock_columns_get_docks (right_docks))); + + gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (shell->statusbar), + has_grip); + } + + gimp_display_shell_set_show_menubar (shell, + options->show_menubar); + gimp_display_shell_set_show_statusbar (shell, + options->show_statusbar); + + gimp_display_shell_set_show_rulers (shell, + options->show_rulers); + gimp_display_shell_set_show_scrollbars (shell, + options->show_scrollbars); + gimp_display_shell_set_show_selection (shell, + options->show_selection); + gimp_display_shell_set_show_layer (shell, + options->show_layer_boundary); + gimp_display_shell_set_show_canvas (shell, + options->show_canvas_boundary); + gimp_display_shell_set_show_guides (shell, + options->show_guides); + gimp_display_shell_set_show_grid (shell, + options->show_grid); + gimp_display_shell_set_show_sample_points (shell, + options->show_sample_points); + gimp_display_shell_set_padding (shell, + options->padding_mode, + &options->padding_color); + gimp_display_shell_set_padding_in_show_all (shell, + options->padding_in_show_all); +} + +void +gimp_display_shell_set_show_menubar (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + GimpImageWindow *window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + window = gimp_display_shell_get_window (shell); + + g_object_set (options, "show-menubar", show, NULL); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + gimp_image_window_keep_canvas_pos (gimp_display_shell_get_window (shell)); + gimp_image_window_set_show_menubar (window, show); + } + + gimp_display_shell_set_action_active (shell, "view-show-menubar", show); +} + +gboolean +gimp_display_shell_get_show_menubar (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_menubar; +} + +void +gimp_display_shell_set_show_statusbar (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-statusbar", show, NULL); + + gimp_image_window_keep_canvas_pos (gimp_display_shell_get_window (shell)); + gimp_statusbar_set_visible (GIMP_STATUSBAR (shell->statusbar), show); + + gimp_display_shell_set_action_active (shell, "view-show-statusbar", show); +} + +gboolean +gimp_display_shell_get_show_statusbar (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_statusbar; +} + +void +gimp_display_shell_set_show_rulers (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-rulers", show, NULL); + + gimp_image_window_keep_canvas_pos (gimp_display_shell_get_window (shell)); + gtk_widget_set_visible (shell->origin, show); + gtk_widget_set_visible (shell->hrule, show); + gtk_widget_set_visible (shell->vrule, show); + + gimp_display_shell_set_action_active (shell, "view-show-rulers", show); +} + +gboolean +gimp_display_shell_get_show_rulers (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_rulers; +} + +void +gimp_display_shell_set_show_scrollbars (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-scrollbars", show, NULL); + + gimp_image_window_keep_canvas_pos (gimp_display_shell_get_window (shell)); + gtk_widget_set_visible (shell->nav_ebox, show); + gtk_widget_set_visible (shell->hsb, show); + gtk_widget_set_visible (shell->vsb, show); + gtk_widget_set_visible (shell->quick_mask_button, show); + gtk_widget_set_visible (shell->zoom_button, show); + + gimp_display_shell_set_action_active (shell, "view-show-scrollbars", show); +} + +gboolean +gimp_display_shell_get_show_scrollbars (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_scrollbars; +} + +void +gimp_display_shell_set_show_selection (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-selection", show, NULL); + + gimp_display_shell_selection_set_show (shell, show); + + gimp_display_shell_set_action_active (shell, "view-show-selection", show); +} + +gboolean +gimp_display_shell_get_show_selection (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_selection; +} + +void +gimp_display_shell_set_show_layer (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-layer-boundary", show, NULL); + + gimp_canvas_item_set_visible (shell->layer_boundary, show); + + gimp_display_shell_set_action_active (shell, "view-show-layer-boundary", show); +} + +gboolean +gimp_display_shell_get_show_layer (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_layer_boundary; +} + +void +gimp_display_shell_set_show_canvas (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-canvas-boundary", show, NULL); + + gimp_canvas_item_set_visible (shell->canvas_boundary, + show && shell->show_all); + + gimp_display_shell_set_action_active (shell, "view-show-canvas-boundary", show); +} + +gboolean +gimp_display_shell_get_show_canvas (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_canvas_boundary; +} + +void +gimp_display_shell_update_show_canvas (GimpDisplayShell *shell) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + gimp_canvas_item_set_visible (shell->canvas_boundary, + options->show_canvas_boundary && + shell->show_all); +} + +void +gimp_display_shell_set_show_guides (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-guides", show, NULL); + + gimp_canvas_item_set_visible (shell->guides, show); + + gimp_display_shell_set_action_active (shell, "view-show-guides", show); +} + +gboolean +gimp_display_shell_get_show_guides (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_guides; +} + +void +gimp_display_shell_set_show_grid (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-grid", show, NULL); + + gimp_canvas_item_set_visible (shell->grid, show); + + gimp_display_shell_set_action_active (shell, "view-show-grid", show); +} + +gboolean +gimp_display_shell_get_show_grid (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_grid; +} + +void +gimp_display_shell_set_show_sample_points (GimpDisplayShell *shell, + gboolean show) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "show-sample-points", show, NULL); + + gimp_canvas_item_set_visible (shell->sample_points, show); + + gimp_display_shell_set_action_active (shell, "view-show-sample-points", show); +} + +gboolean +gimp_display_shell_get_show_sample_points (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->show_sample_points; +} + +void +gimp_display_shell_set_snap_to_grid (GimpDisplayShell *shell, + gboolean snap) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "snap-to-grid", snap, NULL); +} + +gboolean +gimp_display_shell_get_snap_to_grid (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->snap_to_grid; +} + +void +gimp_display_shell_set_snap_to_guides (GimpDisplayShell *shell, + gboolean snap) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "snap-to-guides", snap, NULL); +} + +gboolean +gimp_display_shell_get_snap_to_guides (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->snap_to_guides; +} + +void +gimp_display_shell_set_snap_to_canvas (GimpDisplayShell *shell, + gboolean snap) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "snap-to-canvas", snap, NULL); +} + +gboolean +gimp_display_shell_get_snap_to_canvas (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->snap_to_canvas; +} + +void +gimp_display_shell_set_snap_to_vectors (GimpDisplayShell *shell, + gboolean snap) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + g_object_set (options, "snap-to-path", snap, NULL); +} + +gboolean +gimp_display_shell_get_snap_to_vectors (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->snap_to_path; +} + +void +gimp_display_shell_set_padding (GimpDisplayShell *shell, + GimpCanvasPaddingMode padding_mode, + const GimpRGB *padding_color) +{ + GimpDisplayOptions *options; + GimpRGB color; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (padding_color != NULL); + + options = appearance_get_options (shell); + color = *padding_color; + + switch (padding_mode) + { + case GIMP_CANVAS_PADDING_MODE_DEFAULT: + if (shell->canvas) + { + GtkStyle *style; + + gtk_widget_ensure_style (shell->canvas); + + style = gtk_widget_get_style (shell->canvas); + + gimp_rgb_set_gdk_color (&color, style->bg + GTK_STATE_NORMAL); + } + break; + + case GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK: + color = *gimp_render_light_check_color (); + break; + + case GIMP_CANVAS_PADDING_MODE_DARK_CHECK: + color = *gimp_render_dark_check_color (); + break; + + case GIMP_CANVAS_PADDING_MODE_CUSTOM: + case GIMP_CANVAS_PADDING_MODE_RESET: + break; + } + + g_object_set (options, + "padding-mode", padding_mode, + "padding-color", &color, + NULL); + + gimp_canvas_set_bg_color (GIMP_CANVAS (shell->canvas), &color); + + gimp_display_shell_set_action_color (shell, "view-padding-color-menu", + &options->padding_color); +} + +void +gimp_display_shell_get_padding (GimpDisplayShell *shell, + GimpCanvasPaddingMode *padding_mode, + GimpRGB *padding_color) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + if (padding_mode) + *padding_mode = options->padding_mode; + + if (padding_color) + *padding_color = options->padding_color; +} + +void +gimp_display_shell_set_padding_in_show_all (GimpDisplayShell *shell, + gboolean keep) +{ + GimpDisplayOptions *options; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + options = appearance_get_options (shell); + + if (options->padding_in_show_all != keep) + { + g_object_set (options, "padding-in-show-all", keep, NULL); + + if (shell->display) + { + gimp_display_shell_scroll_clamp_and_update (shell); + gimp_display_shell_scrollbars_update (shell); + + gimp_display_shell_expose_full (shell); + } + + gimp_display_shell_set_action_active (shell, + "view-padding-color-in-show-all", + keep); + + g_object_notify (G_OBJECT (shell), "infinite-canvas"); + } +} + +gboolean +gimp_display_shell_get_padding_in_show_all (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return appearance_get_options (shell)->padding_in_show_all; +} + + +/* private functions */ + +static GimpDisplayOptions * +appearance_get_options (GimpDisplayShell *shell) +{ + if (gimp_display_get_image (shell->display)) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_fullscreen (window)) + return shell->fullscreen_options; + else + return shell->options; + } + + return shell->no_image_options; +} diff --git a/app/display/gimpdisplayshell-appearance.h b/app/display/gimpdisplayshell-appearance.h new file mode 100644 index 0000000..0c67649 --- /dev/null +++ b/app/display/gimpdisplayshell-appearance.h @@ -0,0 +1,92 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_APPEARANCE_H__ +#define __GIMP_DISPLAY_SHELL_APPEARANCE_H__ + + +void gimp_display_shell_appearance_update (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_menubar (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_menubar (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_statusbar (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_statusbar (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_rulers (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_rulers (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_scrollbars (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_scrollbars (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_selection (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_selection (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_layer (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_layer (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_canvas (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_canvas (GimpDisplayShell *shell); +void gimp_display_shell_update_show_canvas (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_grid (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_grid (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_guides (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_guides (GimpDisplayShell *shell); + +void gimp_display_shell_set_snap_to_grid (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_grid (GimpDisplayShell *shell); + +void gimp_display_shell_set_show_sample_points (GimpDisplayShell *shell, + gboolean show); +gboolean gimp_display_shell_get_show_sample_points (GimpDisplayShell *shell); + +void gimp_display_shell_set_snap_to_guides (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_guides (GimpDisplayShell *shell); + +void gimp_display_shell_set_snap_to_canvas (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_canvas (GimpDisplayShell *shell); + +void gimp_display_shell_set_snap_to_vectors (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_vectors (GimpDisplayShell *shell); + +void gimp_display_shell_set_padding (GimpDisplayShell *shell, + GimpCanvasPaddingMode mode, + const GimpRGB *color); +void gimp_display_shell_get_padding (GimpDisplayShell *shell, + GimpCanvasPaddingMode *mode, + GimpRGB *color); +void gimp_display_shell_set_padding_in_show_all (GimpDisplayShell *shell, + gboolean keep); +gboolean gimp_display_shell_get_padding_in_show_all (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_APPEARANCE_H__ */ diff --git a/app/display/gimpdisplayshell-autoscroll.c b/app/display/gimpdisplayshell-autoscroll.c new file mode 100644 index 0000000..39cf252 --- /dev/null +++ b/app/display/gimpdisplayshell-autoscroll.c @@ -0,0 +1,184 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "widgets/gimpdeviceinfo.h" +#include "widgets/gimpdeviceinfo-coords.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-autoscroll.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-transform.h" + +#include "tools/tools-types.h" +#include "tools/tool_manager.h" +#include "tools/gimptool.h" +#include "tools/gimptoolcontrol.h" + + +#define AUTOSCROLL_DT 20 +#define AUTOSCROLL_DX 0.1 + + +typedef struct +{ + GdkEventMotion *mevent; + GimpDeviceInfo *device; + guint32 time; + GdkModifierType state; + guint timeout_id; +} ScrollInfo; + + +/* local function prototypes */ + +static gboolean gimp_display_shell_autoscroll_timeout (gpointer data); + + +/* public functions */ + +void +gimp_display_shell_autoscroll_start (GimpDisplayShell *shell, + GdkModifierType state, + GdkEventMotion *mevent) +{ + ScrollInfo *info; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->scroll_info) + return; + + info = g_slice_new0 (ScrollInfo); + + info->mevent = mevent; + info->device = gimp_device_info_get_by_device (mevent->device); + info->time = gdk_event_get_time ((GdkEvent *) mevent); + info->state = state; + info->timeout_id = g_timeout_add (AUTOSCROLL_DT, + gimp_display_shell_autoscroll_timeout, + shell); + + shell->scroll_info = info; +} + +void +gimp_display_shell_autoscroll_stop (GimpDisplayShell *shell) +{ + ScrollInfo *info; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->scroll_info) + return; + + info = shell->scroll_info; + + if (info->timeout_id) + { + g_source_remove (info->timeout_id); + info->timeout_id = 0; + } + + g_slice_free (ScrollInfo, info); + shell->scroll_info = NULL; +} + + +/* private functions */ + +static gboolean +gimp_display_shell_autoscroll_timeout (gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + ScrollInfo *info = shell->scroll_info; + GimpCoords device_coords; + GimpCoords image_coords; + gint dx = 0; + gint dy = 0; + + gimp_device_info_get_device_coords (info->device, + gtk_widget_get_window (shell->canvas), + &device_coords); + + if (device_coords.x < 0) + dx = device_coords.x; + else if (device_coords.x > shell->disp_width) + dx = device_coords.x - shell->disp_width; + + if (device_coords.y < 0) + dy = device_coords.y; + else if (device_coords.y > shell->disp_height) + dy = device_coords.y - shell->disp_height; + + if (dx || dy) + { + GimpDisplay *display = shell->display; + GimpTool *active_tool = tool_manager_get_active (display->gimp); + gint scroll_amount_x = AUTOSCROLL_DX * dx; + gint scroll_amount_y = AUTOSCROLL_DX * dy; + + info->time += AUTOSCROLL_DT; + + gimp_display_shell_scroll_unoverscrollify (shell, + scroll_amount_x, + scroll_amount_y, + &scroll_amount_x, + &scroll_amount_y); + + gimp_display_shell_scroll (shell, + scroll_amount_x, + scroll_amount_y); + + gimp_display_shell_untransform_coords (shell, + &device_coords, + &image_coords); + + if (gimp_tool_control_get_snap_to (active_tool->control)) + { + gint x, y, width, height; + + gimp_tool_control_get_snap_offsets (active_tool->control, + &x, &y, &width, &height); + + gimp_display_shell_snap_coords (shell, + &image_coords, + x, y, width, height); + } + + tool_manager_motion_active (display->gimp, + &image_coords, + info->time, info->state, + display); + + return TRUE; + } + else + { + g_slice_free (ScrollInfo, info); + shell->scroll_info = NULL; + + return FALSE; + } +} diff --git a/app/display/gimpdisplayshell-autoscroll.h b/app/display/gimpdisplayshell-autoscroll.h new file mode 100644 index 0000000..c030759 --- /dev/null +++ b/app/display/gimpdisplayshell-autoscroll.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_AUTOSCROLL_H__ +#define __GIMP_DISPLAY_SHELL_AUTOSCROLL_H__ + + +void gimp_display_shell_autoscroll_start (GimpDisplayShell *shell, + GdkModifierType state, + GdkEventMotion *mevent); +void gimp_display_shell_autoscroll_stop (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_AUTOSCROLL_H__ */ diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c new file mode 100644 index 0000000..4b87898 --- /dev/null +++ b/app/display/gimpdisplayshell-callbacks.c @@ -0,0 +1,652 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimpimage-quick-mask.h" + +#include "widgets/gimpcairo-wilber.h" +#include "widgets/gimpuimanager.h" + +#include "gimpcanvasitem.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-callbacks.h" +#include "gimpdisplayshell-draw.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-scrollbars.h" +#include "gimpdisplayshell-selection.h" +#include "gimpdisplayshell-title.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayxfer.h" +#include "gimpimagewindow.h" +#include "gimpnavigationeditor.h" + +#include "git-version.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_display_shell_vadjustment_changed (GtkAdjustment *adjustment, + GimpDisplayShell *shell); +static void gimp_display_shell_hadjustment_changed (GtkAdjustment *adjustment, + GimpDisplayShell *shell); +static gboolean gimp_display_shell_vscrollbar_change_value (GtkRange *range, + GtkScrollType scroll, + gdouble value, + GimpDisplayShell *shell); + +static gboolean gimp_display_shell_hscrollbar_change_value (GtkRange *range, + GtkScrollType scroll, + gdouble value, + GimpDisplayShell *shell); + +static void gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell, + cairo_t *cr); +static void gimp_display_shell_canvas_draw_drop_zone (GimpDisplayShell *shell, + cairo_t *cr); + + +/* public functions */ + +void +gimp_display_shell_canvas_realize (GtkWidget *canvas, + GimpDisplayShell *shell) +{ + GimpCanvasPaddingMode padding_mode; + GimpRGB padding_color; + GtkAllocation allocation; + + gtk_widget_grab_focus (canvas); + + gimp_display_shell_get_padding (shell, &padding_mode, &padding_color); + gimp_display_shell_set_padding (shell, padding_mode, &padding_color); + + gtk_widget_get_allocation (canvas, &allocation); + + gimp_display_shell_title_update (shell); + + shell->disp_width = allocation.width; + shell->disp_height = allocation.height; + + /* set up the scrollbar observers */ + g_signal_connect (shell->hsbdata, "value-changed", + G_CALLBACK (gimp_display_shell_hadjustment_changed), + shell); + g_signal_connect (shell->vsbdata, "value-changed", + G_CALLBACK (gimp_display_shell_vadjustment_changed), + shell); + + g_signal_connect (shell->hsb, "change-value", + G_CALLBACK (gimp_display_shell_hscrollbar_change_value), + shell); + + g_signal_connect (shell->vsb, "change-value", + G_CALLBACK (gimp_display_shell_vscrollbar_change_value), + shell); + + /* allow shrinking */ + gtk_widget_set_size_request (GTK_WIDGET (shell), 0, 0); + + shell->xfer = gimp_display_xfer_realize (GTK_WIDGET(shell)); + + /* HACK: remove with GTK+ 3.x: this unconditionally maps the + * rulers, if configured to be hidden they are never visible to the + * user because they will be hidden again right away. + * + * For some obscure reason, having the rulers mapped once prevents + * crashes with tablets and on-canvas dialogs. See bug #784480 and + * all its duplicates. + */ + gtk_widget_show (shell->hrule); + gtk_widget_show (shell->vrule); +} + +void +gimp_display_shell_canvas_realize_after (GtkWidget *canvas, + GimpDisplayShell *shell) +{ + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + /* HACK: see above: must go with GTK+ 3.x too. Restore the rulers' + * intended visibility again. + */ + gimp_image_window_suspend_keep_pos (window); + gimp_display_shell_appearance_update (shell); + gimp_image_window_resume_keep_pos (window); +} + +void +gimp_display_shell_canvas_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + GimpDisplayShell *shell) +{ + /* are we in destruction? */ + if (! shell->display || ! gimp_display_get_shell (shell->display)) + return; + + if ((shell->disp_width != allocation->width) || + (shell->disp_height != allocation->height)) + { + if (shell->zoom_on_resize && + shell->disp_width > 64 && + shell->disp_height > 64 && + allocation->width > 64 && + allocation->height > 64) + { + gdouble scale = gimp_zoom_model_get_factor (shell->zoom); + gint offset_x; + gint offset_y; + + /* FIXME: The code is a bit of a mess */ + + /* multiply the zoom_factor with the ratio of the new and + * old canvas diagonals + */ + scale *= (sqrt (SQR (allocation->width) + + SQR (allocation->height)) / + sqrt (SQR (shell->disp_width) + + SQR (shell->disp_height))); + + offset_x = UNSCALEX (shell, shell->offset_x); + offset_y = UNSCALEX (shell, shell->offset_y); + + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale); + + shell->offset_x = SCALEX (shell, offset_x); + shell->offset_y = SCALEY (shell, offset_y); + } + + shell->disp_width = allocation->width; + shell->disp_height = allocation->height; + + /* When we size-allocate due to resize of the top level window, + * we want some additional logic. Don't apply it on + * zoom_on_resize though. + */ + if (shell->size_allocate_from_configure_event && + ! shell->zoom_on_resize) + { + gboolean center_horizontally; + gboolean center_vertically; + gint target_offset_x; + gint target_offset_y; + gint sw; + gint sh; + + gimp_display_shell_scale_get_image_size (shell, &sw, &sh); + + center_horizontally = sw <= shell->disp_width; + center_vertically = sh <= shell->disp_height; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + gimp_display_shell_scroll_center_image (shell, + center_horizontally, + center_vertically); + } + else + { + gimp_display_shell_scroll_center_content (shell, + center_horizontally, + center_vertically); + } + + /* This is basically the best we can do before we get an + * API for storing the image offset at the start of an + * image window resize using the mouse + */ + target_offset_x = shell->offset_x; + target_offset_y = shell->offset_y; + + if (! center_horizontally) + { + target_offset_x = MAX (shell->offset_x, 0); + } + + if (! center_vertically) + { + target_offset_y = MAX (shell->offset_y, 0); + } + + gimp_display_shell_scroll_set_offset (shell, + target_offset_x, + target_offset_y); + } + + gimp_display_shell_scroll_clamp_and_update (shell); + gimp_display_shell_scaled (shell); + + shell->size_allocate_from_configure_event = FALSE; + } + + if (shell->size_allocate_center_image) + { + gimp_display_shell_scroll_center_image (shell, TRUE, TRUE); + + shell->size_allocate_center_image = FALSE; + } +} + +gboolean +gimp_display_shell_canvas_expose (GtkWidget *widget, + GdkEventExpose *eevent, + GimpDisplayShell *shell) +{ + /* are we in destruction? */ + if (! shell->display || ! gimp_display_get_shell (shell->display)) + return TRUE; + + /* we will scroll around in the next tick anyway, so we just can as + * well skip the drawing of this frame and wait for the next + */ + if (shell->size_allocate_center_image) + return TRUE; + + /* ignore events on overlays */ + if (eevent->window == gtk_widget_get_window (widget)) + { + GimpImage *image = gimp_display_get_image (shell->display); + cairo_t *cr; + + cr = gdk_cairo_create (gtk_widget_get_window (shell->canvas)); + gdk_cairo_region (cr, eevent->region); + cairo_clip (cr); + + /* If we are currently converting the image, it might be in inconsistent + * state and should not be redrawn. + */ + if (image != NULL && ! gimp_image_get_converting (image)) + { + gimp_display_shell_canvas_draw_image (shell, cr); + } + else if (image == NULL) + { + gimp_display_shell_canvas_draw_drop_zone (shell, cr); + } + + cairo_destroy (cr); + } + + return FALSE; +} + +gboolean +gimp_display_shell_origin_button_press (GtkWidget *widget, + GdkEventButton *event, + GimpDisplayShell *shell) +{ + if (! shell->display->gimp->busy) + { + if (event->type == GDK_BUTTON_PRESS && event->button == 1) + { + gboolean unused; + + g_signal_emit_by_name (shell, "popup-menu", &unused); + } + } + + /* Return TRUE to stop signal emission so the button doesn't grab the + * pointer away from us. + */ + return TRUE; +} + +gboolean +gimp_display_shell_quick_mask_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell) +{ + if (! gimp_display_get_image (shell->display)) + return TRUE; + + if (gdk_event_triggers_context_menu ((GdkEvent *) bevent)) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_ui_popup (manager, + "/quick-mask-popup", + GTK_WIDGET (shell), + NULL, NULL, NULL, NULL); + } + + return TRUE; + } + + return FALSE; +} + +void +gimp_display_shell_quick_mask_toggled (GtkWidget *widget, + GimpDisplayShell *shell) +{ + GimpImage *image = gimp_display_get_image (shell->display); + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + if (active != gimp_image_get_quick_mask_state (image)) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_toggle_action (manager, + "quick-mask", "quick-mask-toggle", + active); + } + } +} + +gboolean +gimp_display_shell_navigation_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell) +{ + if (! gimp_display_get_image (shell->display)) + return TRUE; + + if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 1) + { + gimp_navigation_editor_popup (shell, widget, bevent->x, bevent->y); + } + + return TRUE; +} + + +/* private functions */ + +static void +gimp_display_shell_vadjustment_changed (GtkAdjustment *adjustment, + GimpDisplayShell *shell) +{ + /* If we are panning with mouse, scrollbars are to be ignored or + * they will cause jitter in motion + */ + if (! shell->scrolling) + gimp_display_shell_scroll (shell, + 0, + gtk_adjustment_get_value (adjustment) - + shell->offset_y); +} + +static void +gimp_display_shell_hadjustment_changed (GtkAdjustment *adjustment, + GimpDisplayShell *shell) +{ + /* If we are panning with mouse, scrollbars are to be ignored or + * they will cause jitter in motion + */ + if (! shell->scrolling) + gimp_display_shell_scroll (shell, + gtk_adjustment_get_value (adjustment) - + shell->offset_x, + 0); +} + +static gboolean +gimp_display_shell_hscrollbar_change_value (GtkRange *range, + GtkScrollType scroll, + gdouble value, + GimpDisplayShell *shell) +{ + if (! shell->display) + return TRUE; + + if ((scroll == GTK_SCROLL_JUMP) || + (scroll == GTK_SCROLL_PAGE_BACKWARD) || + (scroll == GTK_SCROLL_PAGE_FORWARD)) + return FALSE; + + g_object_freeze_notify (G_OBJECT (shell->hsbdata)); + + gimp_display_shell_scrollbars_setup_horizontal (shell, value); + + g_object_thaw_notify (G_OBJECT (shell->hsbdata)); /* emits "changed" */ + + return FALSE; +} + +static gboolean +gimp_display_shell_vscrollbar_change_value (GtkRange *range, + GtkScrollType scroll, + gdouble value, + GimpDisplayShell *shell) +{ + if (! shell->display) + return TRUE; + + if ((scroll == GTK_SCROLL_JUMP) || + (scroll == GTK_SCROLL_PAGE_BACKWARD) || + (scroll == GTK_SCROLL_PAGE_FORWARD)) + return FALSE; + + g_object_freeze_notify (G_OBJECT (shell->vsbdata)); + + gimp_display_shell_scrollbars_setup_vertical (shell, value); + + g_object_thaw_notify (G_OBJECT (shell->vsbdata)); /* emits "changed" */ + + return FALSE; +} + +static void +gimp_display_shell_canvas_draw_image (GimpDisplayShell *shell, + cairo_t *cr) +{ + cairo_rectangle_list_t *clip_rectangles; + GeglRectangle image_rect; + GeglRectangle rotated_image_rect; + GeglRectangle canvas_rect; + cairo_matrix_t matrix; + gdouble x1, y1; + gdouble x2, y2; + + gimp_display_shell_scale_get_image_unrotated_bounding_box ( + shell, + &image_rect.x, + &image_rect.y, + &image_rect.width, + &image_rect.height); + + gimp_display_shell_scale_get_image_unrotated_bounds ( + shell, + &canvas_rect.x, + &canvas_rect.y, + &canvas_rect.width, + &canvas_rect.height); + + /* the background has already been cleared by GdkWindow + */ + + + /* on top, draw the exposed part of the region that is inside the + * image + */ + + cairo_save (cr); + clip_rectangles = cairo_copy_clip_rectangle_list (cr); + cairo_get_matrix (cr, &matrix); + + if (shell->rotate_transform) + cairo_transform (cr, shell->rotate_transform); + + if (shell->show_all) + { + cairo_save (cr); + + if (gimp_display_shell_get_padding_in_show_all (shell)) + { + cairo_rectangle (cr, + canvas_rect.x, + canvas_rect.y, + canvas_rect.width, + canvas_rect.height); + cairo_clip (cr); + } + + gimp_display_shell_draw_checkerboard (shell, cr); + + cairo_restore (cr); + } + + cairo_rectangle (cr, + image_rect.x, + image_rect.y, + image_rect.width, + image_rect.height); + cairo_clip (cr); + + gimp_display_shell_rotate_bounds (shell, + image_rect.x, + image_rect.y, + image_rect.x + image_rect.width, + image_rect.y + image_rect.height, + &x1, &y1, &x2, &y2); + + rotated_image_rect.x = floor (x1); + rotated_image_rect.y = floor (y1); + rotated_image_rect.width = ceil (x2) - rotated_image_rect.x; + rotated_image_rect.height = ceil (y2) - rotated_image_rect.y; + + if (gdk_cairo_get_clip_rectangle (cr, NULL)) + { + gint i; + + if (! shell->show_all) + { + cairo_save (cr); + gimp_display_shell_draw_checkerboard (shell, cr); + cairo_restore (cr); + } + + if (shell->show_image) + { + cairo_set_matrix (cr, &matrix); + + for (i = 0; i < clip_rectangles->num_rectangles; i++) + { + cairo_rectangle_t clip_rect = clip_rectangles->rectangles[i]; + GeglRectangle rect; + + rect.x = floor (clip_rect.x); + rect.y = floor (clip_rect.y); + rect.width = ceil (clip_rect.x + clip_rect.width) - rect.x; + rect.height = ceil (clip_rect.y + clip_rect.height) - rect.y; + + if (gegl_rectangle_intersect (&rect, &rect, &rotated_image_rect)) + { + gimp_display_shell_draw_image (shell, cr, + rect.x, rect.y, + rect.width, rect.height); + } + } + } + } + + cairo_rectangle_list_destroy (clip_rectangles); + cairo_restore (cr); + + + /* finally, draw all the remaining image window stuff on top + */ + + /* draw canvas items */ + cairo_save (cr); + + if (shell->rotate_transform) + cairo_transform (cr, shell->rotate_transform); + + gimp_canvas_item_draw (shell->canvas_item, cr); + + cairo_restore (cr); + + gimp_canvas_item_draw (shell->unrotated_item, cr); + + /* restart (and recalculate) the selection boundaries */ + gimp_display_shell_selection_draw (shell, cr); + gimp_display_shell_selection_restart (shell); +} + +static void +gimp_display_shell_canvas_draw_drop_zone (GimpDisplayShell *shell, + cairo_t *cr) +{ + cairo_save (cr); + + gimp_cairo_draw_drop_wilber (shell->canvas, cr, shell->blink); + + cairo_restore (cr); + +#ifdef GIMP_UNSTABLE + { + PangoLayout *layout; + gchar *msg; + GtkAllocation allocation; + gint width; + gint height; + gdouble scale; + + layout = gtk_widget_create_pango_layout (shell->canvas, NULL); + + msg = g_strdup_printf (_("Unstable Development Version\n\n" + "commit %s\n\n" + "Please test bugs against " + "latest git master branch\n" + "before reporting them."), + GIMP_GIT_VERSION_ABBREV); + pango_layout_set_markup (layout, msg, -1); + g_free (msg); + pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); + + pango_layout_get_pixel_size (layout, &width, &height); + gtk_widget_get_allocation (shell->canvas, &allocation); + + scale = MIN (((gdouble) allocation.width / 2.0) / (gdouble) width, + ((gdouble) allocation.height / 2.0) / (gdouble) height); + + cairo_move_to (cr, + (allocation.width - (width * scale)) / 2, + (allocation.height - (height * scale)) / 2); + + cairo_scale (cr, scale, scale); + + pango_cairo_show_layout (cr, layout); + + g_object_unref (layout); + } +#endif /* GIMP_UNSTABLE */ +} diff --git a/app/display/gimpdisplayshell-callbacks.h b/app/display/gimpdisplayshell-callbacks.h new file mode 100644 index 0000000..de1861f --- /dev/null +++ b/app/display/gimpdisplayshell-callbacks.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_CALLBACKS_H__ +#define __GIMP_DISPLAY_SHELL_CALLBACKS_H__ + + +void gimp_display_shell_canvas_realize (GtkWidget *widget, + GimpDisplayShell *shell); +void gimp_display_shell_canvas_realize_after (GtkWidget *widget, + GimpDisplayShell *shell); +void gimp_display_shell_canvas_size_allocate (GtkWidget *widget, + GtkAllocation *alloc, + GimpDisplayShell *shell); +gboolean gimp_display_shell_canvas_expose (GtkWidget *widget, + GdkEventExpose *eevent, + GimpDisplayShell *shell); + +gboolean gimp_display_shell_origin_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); + +gboolean gimp_display_shell_quick_mask_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); +void gimp_display_shell_quick_mask_toggled (GtkWidget *widget, + GimpDisplayShell *shell); + +gboolean gimp_display_shell_navigation_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_CALLBACKS_H__ */ diff --git a/app/display/gimpdisplayshell-close.c b/app/display/gimpdisplayshell-close.c new file mode 100644 index 0000000..b7d7c16 --- /dev/null +++ b/app/display/gimpdisplayshell-close.c @@ -0,0 +1,447 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpmessagedialog.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-close.h" +#include "gimpimagewindow.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_display_shell_close_dialog (GimpDisplayShell *shell, + GimpImage *image); +static void gimp_display_shell_close_name_changed (GimpImage *image, + GimpMessageBox *box); +static void gimp_display_shell_close_exported (GimpImage *image, + GFile *file, + GimpMessageBox *box); +static gboolean gimp_display_shell_close_time_changed (GimpMessageBox *box); +static void gimp_display_shell_close_response (GtkWidget *widget, + gboolean close, + GimpDisplayShell *shell); +static void gimp_display_shell_close_accel_marshal(GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +static void gimp_time_since (gint64 then, + gint *hours, + gint *minutes); + + +/* public functions */ + +void +gimp_display_shell_close (GimpDisplayShell *shell, + gboolean kill_it) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + /* FIXME: gimp_busy HACK not really appropriate here because we only + * want to prevent the busy image and display to be closed. --Mitch + */ + if (shell->display->gimp->busy) + return; + + /* If the image has been modified, give the user a chance to save + * it before nuking it--this only applies if its the last view + * to an image canvas. (a image with disp_count = 1) + */ + if (! kill_it && + image && + gimp_image_get_display_count (image) == 1 && + gimp_image_is_dirty (image)) + { + /* If there's a save dialog active for this image, then raise it. + * (see bug #511965) + */ + GtkWidget *dialog = g_object_get_data (G_OBJECT (image), + "gimp-file-save-dialog"); + if (dialog) + { + gtk_window_present (GTK_WINDOW (dialog)); + } + else + { + gimp_display_shell_close_dialog (shell, image); + } + } + else if (image) + { + gimp_display_close (shell->display); + } + else + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + /* Activate the action instead of simply calling gimp_exit(), so + * the quit action's sensitivity is taken into account. + */ + gimp_ui_manager_activate_action (manager, "file", "file-quit"); + } + } +} + + +/* private functions */ + +#define RESPONSE_SAVE 1 + + +static void +gimp_display_shell_close_dialog (GimpDisplayShell *shell, + GimpImage *image) +{ + GtkWidget *dialog; + GimpMessageBox *box; + GtkWidget *label; + GtkAccelGroup *accel_group; + GClosure *closure; + GSource *source; + guint accel_key; + GdkModifierType accel_mods; + gchar *title; + gchar *accel_string; + gchar *hint; + gchar *markup; + GFile *file; + + if (shell->close_dialog) + { + gtk_window_present (GTK_WINDOW (shell->close_dialog)); + return; + } + + file = gimp_image_get_file (image); + + title = g_strdup_printf (_("Close %s"), gimp_image_get_display_name (image)); + + shell->close_dialog = + dialog = gimp_message_dialog_new (title, GIMP_ICON_DOCUMENT_SAVE, + GTK_WIDGET (shell), + GTK_DIALOG_DESTROY_WITH_PARENT, + gimp_standard_help_func, NULL, + + file ? + _("_Save") : + _("Save _As"), RESPONSE_SAVE, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Discard Changes"), GTK_RESPONSE_CLOSE, + NULL); + + g_free (title); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + RESPONSE_SAVE, + GTK_RESPONSE_CLOSE, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_widget_destroyed), + &shell->close_dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (gimp_display_shell_close_response), + shell); + + /* connect D to the quit/close button */ + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (shell->close_dialog), accel_group); + g_object_unref (accel_group); + + closure = g_closure_new_object (sizeof (GClosure), + G_OBJECT (shell->close_dialog)); + g_closure_set_marshal (closure, gimp_display_shell_close_accel_marshal); + gtk_accelerator_parse ("D", &accel_key, &accel_mods); + gtk_accel_group_connect (accel_group, accel_key, accel_mods, 0, closure); + + box = GIMP_MESSAGE_DIALOG (dialog)->box; + + accel_string = gtk_accelerator_get_label (accel_key, accel_mods); + hint = g_strdup_printf (_("Press %s to discard all changes and close the image."), + accel_string); + markup = g_strdup_printf ("%s", hint); + + label = gtk_label_new (NULL); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_markup (GTK_LABEL (label), markup); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + g_free (markup); + g_free (hint); + g_free (accel_string); + + g_signal_connect_object (image, "name-changed", + G_CALLBACK (gimp_display_shell_close_name_changed), + box, 0); + g_signal_connect_object (image, "exported", + G_CALLBACK (gimp_display_shell_close_exported), + box, 0); + + gimp_display_shell_close_name_changed (image, box); + + closure = + g_cclosure_new_object (G_CALLBACK (gimp_display_shell_close_time_changed), + G_OBJECT (box)); + + /* update every 10 seconds */ + source = g_timeout_source_new_seconds (10); + g_source_set_closure (source, closure); + g_source_attach (source, NULL); + g_source_unref (source); + + /* The dialog is destroyed with the shell, so it should be safe + * to hold an image pointer for the lifetime of the dialog. + */ + g_object_set_data (G_OBJECT (box), "gimp-image", image); + + gimp_display_shell_close_time_changed (box); + + gtk_widget_show (dialog); +} + +static void +gimp_display_shell_close_name_changed (GimpImage *image, + GimpMessageBox *box) +{ + GtkWidget *window = gtk_widget_get_toplevel (GTK_WIDGET (box)); + + if (GTK_IS_WINDOW (window)) + { + gchar *title = g_strdup_printf (_("Close %s"), + gimp_image_get_display_name (image)); + + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + } + + gimp_message_box_set_primary_text (box, + _("Save the changes to image '%s' " + "before closing?"), + gimp_image_get_display_name (image)); +} + +static void +gimp_display_shell_close_exported (GimpImage *image, + GFile *file, + GimpMessageBox *box) +{ + gimp_display_shell_close_time_changed (box); +} + +static gboolean +gimp_display_shell_close_time_changed (GimpMessageBox *box) +{ + GimpImage *image = g_object_get_data (G_OBJECT (box), "gimp-image"); + gint64 dirty_time = gimp_image_get_dirty_time (image); + gchar *time_text = NULL; + gchar *export_text = NULL; + + if (dirty_time) + { + gint hours = 0; + gint minutes = 0; + + gimp_time_since (dirty_time, &hours, &minutes); + + if (hours > 0) + { + if (hours > 1 || minutes == 0) + { + time_text = + g_strdup_printf (ngettext ("If you don't save the image, " + "changes from the last hour " + "will be lost.", + "If you don't save the image, " + "changes from the last %d " + "hours will be lost.", + hours), hours); + } + else + { + time_text = + g_strdup_printf (ngettext ("If you don't save the image, " + "changes from the last hour " + "and %d minute will be lost.", + "If you don't save the image, " + "changes from the last hour " + "and %d minutes will be lost.", + minutes), minutes); + } + } + else + { + time_text = + g_strdup_printf (ngettext ("If you don't save the image, " + "changes from the last minute " + "will be lost.", + "If you don't save the image, " + "changes from the last %d " + "minutes will be lost.", + minutes), minutes); + } + } + + if (! gimp_image_is_export_dirty (image)) + { + GFile *file; + + file = gimp_image_get_exported_file (image); + if (! file) + file = gimp_image_get_imported_file (image); + + export_text = g_strdup_printf (_("The image has been exported to '%s'."), + gimp_file_get_utf8_name (file)); + } + + if (time_text && export_text) + gimp_message_box_set_text (box, "%s\n\n%s", time_text, export_text); + else if (time_text || export_text) + gimp_message_box_set_text (box, "%s", time_text ? time_text : export_text); + else + gimp_message_box_set_text (box, "%s", time_text); + + g_free (time_text); + g_free (export_text); + + return TRUE; +} + +static void +gimp_display_shell_close_response (GtkWidget *widget, + gint response_id, + GimpDisplayShell *shell) +{ + gtk_widget_destroy (widget); + + switch (response_id) + { + case GTK_RESPONSE_CLOSE: + gimp_display_close (shell->display); + break; + + case RESPONSE_SAVE: + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_image_window_set_active_shell (window, shell); + + gimp_ui_manager_activate_action (manager, + "file", "file-save-and-close"); + } + } + break; + + default: + break; + } +} + +static void +gimp_display_shell_close_accel_marshal (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + gtk_dialog_response (GTK_DIALOG (closure->data), GTK_RESPONSE_CLOSE); + + /* we handled the accelerator */ + g_value_set_boolean (return_value, TRUE); +} + +static void +gimp_time_since (gint64 then, + gint *hours, + gint *minutes) +{ + gint64 now = time (NULL); + gint64 diff = 1 + now - then; + + g_return_if_fail (now >= then); + + /* first round up to the nearest minute */ + diff = (diff + 59) / 60; + + /* then optionally round minutes to multiples of 5 or 10 */ + if (diff > 50) + diff = ((diff + 8) / 10) * 10; + else if (diff > 20) + diff = ((diff + 3) / 5) * 5; + + /* determine full hours */ + if (diff >= 60) + { + *hours = diff / 60; + diff = (diff % 60); + } + + /* round up to full hours for 2 and more */ + if (*hours > 1 && diff > 0) + { + *hours += 1; + diff = 0; + } + + *minutes = diff; +} diff --git a/app/display/gimpdisplayshell-close.h b/app/display/gimpdisplayshell-close.h new file mode 100644 index 0000000..3d55650 --- /dev/null +++ b/app/display/gimpdisplayshell-close.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_CLOSE_H__ +#define __GIMP_DISPLAY_SHELL_CLOSE_H__ + + +void gimp_display_shell_close (GimpDisplayShell *shell, + gboolean kill_it); + + +#endif /* __GIMP_DISPLAY_SHELL_CLOSE_H__ */ diff --git a/app/display/gimpdisplayshell-cursor.c b/app/display/gimpdisplayshell-cursor.c new file mode 100644 index 0000000..5a536d5 --- /dev/null +++ b/app/display/gimpdisplayshell-cursor.c @@ -0,0 +1,295 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimpimage.h" + +#include "widgets/gimpcursor.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdockcontainer.h" +#include "widgets/gimpsessioninfo.h" + +#include "gimpcanvascursor.h" +#include "gimpdisplay.h" +#include "gimpcursorview.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-cursor.h" +#include "gimpdisplayshell-transform.h" +#include "gimpstatusbar.h" + + +static void gimp_display_shell_real_set_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type, + GimpToolCursorType tool_cursor, + GimpCursorModifier modifier, + gboolean always_install); + + +/* public functions */ + +void +gimp_display_shell_set_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type, + GimpToolCursorType tool_cursor, + GimpCursorModifier modifier) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->using_override_cursor) + { + gimp_display_shell_real_set_cursor (shell, + cursor_type, + tool_cursor, + modifier, + FALSE); + } +} + +void +gimp_display_shell_unset_cursor (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->using_override_cursor) + { + gimp_display_shell_real_set_cursor (shell, + (GimpCursorType) -1, 0, 0, FALSE); + } +} + +void +gimp_display_shell_set_override_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->using_override_cursor || + (shell->using_override_cursor && + shell->override_cursor != cursor_type)) + { + shell->override_cursor = cursor_type; + shell->using_override_cursor = TRUE; + + gimp_cursor_set (shell->canvas, + shell->cursor_handedness, + cursor_type, + GIMP_TOOL_CURSOR_NONE, + GIMP_CURSOR_MODIFIER_NONE); + } +} + +void +gimp_display_shell_unset_override_cursor (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->using_override_cursor) + { + shell->using_override_cursor = FALSE; + + gimp_display_shell_real_set_cursor (shell, + shell->current_cursor, + shell->tool_cursor, + shell->cursor_modifier, + TRUE); + } +} + +void +gimp_display_shell_update_software_cursor (GimpDisplayShell *shell, + GimpCursorPrecision precision, + gint display_x, + gint display_y, + gdouble image_x, + gdouble image_y) +{ + GimpImageWindow *image_window; + GimpDialogFactory *factory; + GimpStatusbar *statusbar; + GtkWidget *widget; + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + if (shell->draw_cursor && + shell->proximity && + display_x >= 0 && + display_y >= 0) + { + gimp_canvas_item_begin_change (shell->cursor); + + gimp_canvas_cursor_set (shell->cursor, + display_x, + display_y); + gimp_canvas_item_set_visible (shell->cursor, TRUE); + + gimp_canvas_item_end_change (shell->cursor); + } + else + { + gimp_canvas_item_set_visible (shell->cursor, FALSE); + } + + /* use the passed image_coords for the statusbar because they are + * possibly snapped... + */ + statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_update_cursor (statusbar, precision, image_x, image_y); + + image_window = gimp_display_shell_get_window (shell); + factory = gimp_dock_container_get_dialog_factory (GIMP_DOCK_CONTAINER (image_window)); + + widget = gimp_dialog_factory_find_widget (factory, "gimp-cursor-view"); + + if (widget) + { + GtkWidget *cursor_view = gtk_bin_get_child (GTK_BIN (widget)); + + if (cursor_view) + { + gint t_x = -1; + gint t_y = -1; + + /* ...but use the unsnapped display_coords for the info window */ + if (display_x >= 0 && display_y >= 0) + gimp_display_shell_untransform_xy (shell, display_x, display_y, + &t_x, &t_y, FALSE); + + gimp_cursor_view_update_cursor (GIMP_CURSOR_VIEW (cursor_view), + image, shell->unit, + t_x, t_y); + } + } +} + +void +gimp_display_shell_clear_software_cursor (GimpDisplayShell *shell) +{ + GimpImageWindow *image_window; + GimpDialogFactory *factory; + GimpStatusbar *statusbar; + GtkWidget *widget; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_canvas_item_set_visible (shell->cursor, FALSE); + + statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_clear_cursor (statusbar); + + image_window = gimp_display_shell_get_window (shell); + factory = gimp_dock_container_get_dialog_factory (GIMP_DOCK_CONTAINER (image_window)); + + widget = gimp_dialog_factory_find_widget (factory, "gimp-cursor-view"); + + if (widget) + { + GtkWidget *cursor_view = gtk_bin_get_child (GTK_BIN (widget)); + + if (cursor_view) + gimp_cursor_view_clear_cursor (GIMP_CURSOR_VIEW (cursor_view)); + } +} + + +/* private functions */ + +static void +gimp_display_shell_real_set_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type, + GimpToolCursorType tool_cursor, + GimpCursorModifier modifier, + gboolean always_install) +{ + GimpHandedness cursor_handedness; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (cursor_type == (GimpCursorType) -1) + { + shell->current_cursor = cursor_type; + + if (gtk_widget_is_drawable (shell->canvas)) + gdk_window_set_cursor (gtk_widget_get_window (shell->canvas), NULL); + + return; + } + + if (cursor_type != GIMP_CURSOR_NONE && + cursor_type != GIMP_CURSOR_BAD) + { + switch (shell->display->config->cursor_mode) + { + case GIMP_CURSOR_MODE_TOOL_ICON: + break; + + case GIMP_CURSOR_MODE_TOOL_CROSSHAIR: + if (cursor_type < GIMP_CURSOR_CORNER_TOP || + cursor_type > GIMP_CURSOR_SIDE_TOP_LEFT) + { + /* the corner and side cursors count as crosshair, so leave + * them and override everything else + */ + cursor_type = GIMP_CURSOR_CROSSHAIR_SMALL; + } + break; + + case GIMP_CURSOR_MODE_CROSSHAIR: + cursor_type = GIMP_CURSOR_CROSSHAIR; + tool_cursor = GIMP_TOOL_CURSOR_NONE; + + if (modifier != GIMP_CURSOR_MODIFIER_BAD) + { + /* the bad modifier is always shown */ + modifier = GIMP_CURSOR_MODIFIER_NONE; + } + break; + } + } + + cursor_type = gimp_cursor_rotate (cursor_type, shell->rotate_angle); + + cursor_handedness = GIMP_GUI_CONFIG (shell->display->config)->cursor_handedness; + + if (shell->cursor_handedness != cursor_handedness || + shell->current_cursor != cursor_type || + shell->tool_cursor != tool_cursor || + shell->cursor_modifier != modifier || + always_install) + { + shell->cursor_handedness = cursor_handedness; + shell->current_cursor = cursor_type; + shell->tool_cursor = tool_cursor; + shell->cursor_modifier = modifier; + + gimp_cursor_set (shell->canvas, + cursor_handedness, + cursor_type, tool_cursor, modifier); + } +} diff --git a/app/display/gimpdisplayshell-cursor.h b/app/display/gimpdisplayshell-cursor.h new file mode 100644 index 0000000..8f434c6 --- /dev/null +++ b/app/display/gimpdisplayshell-cursor.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_CURSOR_H__ +#define __GIMP_DISPLAY_SHELL_CURSOR_H__ + + +/* functions dealing with the normal windowing system cursor */ + +void gimp_display_shell_set_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type, + GimpToolCursorType tool_cursor, + GimpCursorModifier modifier); +void gimp_display_shell_unset_cursor (GimpDisplayShell *shell); +void gimp_display_shell_set_override_cursor (GimpDisplayShell *shell, + GimpCursorType cursor_type); +void gimp_display_shell_unset_override_cursor (GimpDisplayShell *shell); + + +/* functions dealing with the software cursor that is drawn to the + * canvas by GIMP + */ + +void gimp_display_shell_update_software_cursor (GimpDisplayShell *shell, + GimpCursorPrecision precision, + gint display_x, + gint display_y, + gdouble image_x, + gdouble image_y); +void gimp_display_shell_clear_software_cursor (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_CURSOR_H__ */ diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c new file mode 100644 index 0000000..152ca45 --- /dev/null +++ b/app/display/gimpdisplayshell-dnd.c @@ -0,0 +1,770 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimp-edit.h" +#include "core/gimpbuffer.h" +#include "core/gimpdrawable-edit.h" +#include "core/gimpfilloptions.h" +#include "core/gimpimage.h" +#include "core/gimpimage-new.h" +#include "core/gimpimage-undo.h" +#include "core/gimplayer.h" +#include "core/gimplayer-new.h" +#include "core/gimplayermask.h" +#include "core/gimppattern.h" +#include "core/gimpprogress.h" + +#include "file/file-open.h" + +#include "text/gimptext.h" +#include "text/gimptextlayer.h" + +#include "vectors/gimpvectors.h" +#include "vectors/gimpvectors-import.h" + +#include "widgets/gimpdnd.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-dnd.h" +#include "gimpdisplayshell-transform.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_display_shell_drop_drawable (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data); +static void gimp_display_shell_drop_vectors (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data); +static void gimp_display_shell_drop_svg (GtkWidget *widget, + gint x, + gint y, + const guchar *svg_data, + gsize svg_data_length, + gpointer data); +static void gimp_display_shell_drop_pattern (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data); +static void gimp_display_shell_drop_color (GtkWidget *widget, + gint x, + gint y, + const GimpRGB *color, + gpointer data); +static void gimp_display_shell_drop_buffer (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data); +static void gimp_display_shell_drop_uri_list (GtkWidget *widget, + gint x, + gint y, + GList *uri_list, + gpointer data); +static void gimp_display_shell_drop_component (GtkWidget *widget, + gint x, + gint y, + GimpImage *image, + GimpChannelType component, + gpointer data); +static void gimp_display_shell_drop_pixbuf (GtkWidget *widget, + gint x, + gint y, + GdkPixbuf *pixbuf, + gpointer data); + + +/* public functions */ + +void +gimp_display_shell_dnd_init (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_LAYER, + gimp_display_shell_drop_drawable, + shell); + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_LAYER_MASK, + gimp_display_shell_drop_drawable, + shell); + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_CHANNEL, + gimp_display_shell_drop_drawable, + shell); + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_VECTORS, + gimp_display_shell_drop_vectors, + shell); + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_PATTERN, + gimp_display_shell_drop_pattern, + shell); + gimp_dnd_viewable_dest_add (shell->canvas, GIMP_TYPE_BUFFER, + gimp_display_shell_drop_buffer, + shell); + gimp_dnd_color_dest_add (shell->canvas, + gimp_display_shell_drop_color, + shell); + gimp_dnd_component_dest_add (shell->canvas, + gimp_display_shell_drop_component, + shell); + gimp_dnd_uri_list_dest_add (shell->canvas, + gimp_display_shell_drop_uri_list, + shell); + gimp_dnd_svg_dest_add (shell->canvas, + gimp_display_shell_drop_svg, + shell); + gimp_dnd_pixbuf_dest_add (shell->canvas, + gimp_display_shell_drop_pixbuf, + shell); +} + + +/* private functions */ + +/* + * Position the dropped item in the middle of the viewport. + */ +static void +gimp_display_shell_dnd_position_item (GimpDisplayShell *shell, + GimpImage *image, + GimpItem *item) +{ + gint item_width = gimp_item_get_width (item); + gint item_height = gimp_item_get_height (item); + gint off_x, off_y; + + if (item_width >= gimp_image_get_width (image) && + item_height >= gimp_image_get_height (image)) + { + off_x = (gimp_image_get_width (image) - item_width) / 2; + off_y = (gimp_image_get_height (image) - item_height) / 2; + } + else + { + gint x, y; + gint width, height; + + gimp_display_shell_untransform_viewport ( + shell, + ! gimp_display_shell_get_infinite_canvas (shell), + &x, &y, &width, &height); + + off_x = x + (width - item_width) / 2; + off_y = y + (height - item_height) / 2; + } + + gimp_item_translate (item, + off_x - gimp_item_get_offset_x (item), + off_y - gimp_item_get_offset_y (item), + FALSE); +} + +static void +gimp_display_shell_dnd_flush (GimpDisplayShell *shell, + GimpImage *image) +{ + gimp_display_shell_present (shell); + + gimp_image_flush (image); + + gimp_context_set_display (gimp_get_user_context (shell->display->gimp), + shell->display); +} + +static void +gimp_display_shell_drop_drawable (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GType new_type; + GimpItem *new_item; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! image) + { + image = gimp_image_new_from_drawable (shell->display->gimp, + GIMP_DRAWABLE (viewable)); + gimp_create_display (shell->display->gimp, image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (image); + + return; + } + + if (GIMP_IS_LAYER (viewable)) + new_type = G_TYPE_FROM_INSTANCE (viewable); + else + new_type = GIMP_TYPE_LAYER; + + new_item = gimp_item_convert (GIMP_ITEM (viewable), image, new_type); + + if (new_item) + { + GimpLayer *new_layer = GIMP_LAYER (new_item); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, + _("Drop New Layer")); + + gimp_display_shell_dnd_position_item (shell, image, new_item); + + gimp_item_set_visible (new_item, TRUE, FALSE); + gimp_item_set_linked (new_item, FALSE, FALSE); + + gimp_image_add_layer (image, new_layer, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + + gimp_image_undo_group_end (image); + + gimp_display_shell_dnd_flush (shell, image); + } +} + +static void +gimp_display_shell_drop_vectors (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GimpItem *new_item; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! image) + return; + + new_item = gimp_item_convert (GIMP_ITEM (viewable), + image, G_TYPE_FROM_INSTANCE (viewable)); + + if (new_item) + { + GimpVectors *new_vectors = GIMP_VECTORS (new_item); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, + _("Drop New Path")); + + gimp_image_add_vectors (image, new_vectors, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + + gimp_image_undo_group_end (image); + + gimp_display_shell_dnd_flush (shell, image); + } +} + +static void +gimp_display_shell_drop_svg (GtkWidget *widget, + gint x, + gint y, + const guchar *svg_data, + gsize svg_data_len, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GError *error = NULL; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! image) + return; + + if (! gimp_vectors_import_buffer (image, + (const gchar *) svg_data, svg_data_len, + TRUE, FALSE, + GIMP_IMAGE_ACTIVE_PARENT, -1, + NULL, &error)) + { + gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + } + else + { + gimp_display_shell_dnd_flush (shell, image); + } +} + +static void +gimp_display_shell_dnd_fill (GimpDisplayShell *shell, + GimpFillOptions *options, + const gchar *undo_desc) +{ + GimpImage *image = gimp_display_get_image (shell->display); + GimpDrawable *drawable; + + if (shell->display->gimp->busy) + return; + + if (! image) + return; + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + return; + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + { + gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_ERROR, + _("Cannot modify the pixels of layer groups.")); + return; + } + + if (gimp_item_is_content_locked (GIMP_ITEM (drawable))) + { + gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_ERROR, + _("The active layer's pixels are locked.")); + return; + } + + /* FIXME: there should be a virtual method for this that the + * GimpTextLayer can override. + */ + if (gimp_fill_options_get_style (options) == GIMP_FILL_STYLE_SOLID && + gimp_item_is_text_layer (GIMP_ITEM (drawable))) + { + GimpRGB color; + + gimp_context_get_foreground (GIMP_CONTEXT (options), &color); + + gimp_text_layer_set (GIMP_TEXT_LAYER (drawable), NULL, + "color", &color, + NULL); + } + else + { + gimp_drawable_edit_fill (drawable, options, undo_desc); + } + + gimp_display_shell_dnd_flush (shell, image); +} + +static void +gimp_display_shell_drop_pattern (GtkWidget *widget, + gint x, + gint y, + GimpViewable *viewable, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpFillOptions *options = gimp_fill_options_new (shell->display->gimp, + NULL, FALSE); + + GIMP_LOG (DND, NULL); + + gimp_fill_options_set_style (options, GIMP_FILL_STYLE_PATTERN); + gimp_context_set_pattern (GIMP_CONTEXT (options), GIMP_PATTERN (viewable)); + + gimp_display_shell_dnd_fill (shell, options, + C_("undo-type", "Drop pattern to layer")); + + g_object_unref (options); +} + +static void +gimp_display_shell_drop_color (GtkWidget *widget, + gint x, + gint y, + const GimpRGB *color, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpFillOptions *options = gimp_fill_options_new (shell->display->gimp, + NULL, FALSE); + + GIMP_LOG (DND, NULL); + + gimp_fill_options_set_style (options, GIMP_FILL_STYLE_SOLID); + gimp_context_set_foreground (GIMP_CONTEXT (options), color); + + gimp_display_shell_dnd_fill (shell, options, + C_("undo-type", "Drop color to layer")); + + g_object_unref (options); +} + +static void +gimp_display_shell_drop_buffer (GtkWidget *widget, + gint drop_x, + gint drop_y, + GimpViewable *viewable, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GimpDrawable *drawable; + GimpBuffer *buffer; + GimpPasteType paste_type; + gint x, y, width, height; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! image) + { + image = gimp_image_new_from_buffer (shell->display->gimp, + GIMP_BUFFER (viewable)); + gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (image); + + return; + } + + paste_type = GIMP_PASTE_TYPE_FLOATING; + + drawable = gimp_image_get_active_drawable (image); + + if (drawable) + { + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + { + gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_INFO, + _("Pasted as new layer because the " + "target is a layer group.")); + + paste_type = GIMP_PASTE_TYPE_NEW_LAYER; + } + else if (gimp_item_is_content_locked (GIMP_ITEM (drawable))) + { + gimp_message_literal (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_ERROR, + _("Pasted as new layer because the " + "target's pixels are locked.")); + + paste_type = GIMP_PASTE_TYPE_NEW_LAYER; + } + } + + buffer = GIMP_BUFFER (viewable); + + gimp_display_shell_untransform_viewport ( + shell, + ! gimp_display_shell_get_infinite_canvas (shell), + &x, &y, &width, &height); + + /* FIXME: popup a menu for selecting "Paste Into" */ + + gimp_edit_paste (image, drawable, GIMP_OBJECT (buffer), + paste_type, x, y, width, height); + + gimp_display_shell_dnd_flush (shell, image); +} + +static void +gimp_display_shell_drop_uri_list (GtkWidget *widget, + gint x, + gint y, + GList *uri_list, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image; + GimpContext *context; + GList *list; + gboolean open_as_layers; + + /* If the app is already being torn down, shell->display might be + * NULL here. Play it safe. + */ + if (! shell->display) + return; + + image = gimp_display_get_image (shell->display); + context = gimp_get_user_context (shell->display->gimp); + + GIMP_LOG (DND, NULL); + + open_as_layers = (image != NULL); + + if (image) + g_object_ref (image); + + for (list = uri_list; list; list = g_list_next (list)) + { + GFile *file = g_file_new_for_uri (list->data); + GimpPDBStatusType status; + GError *error = NULL; + gboolean warn = FALSE; + + if (! shell->display) + { + /* It seems as if GIMP is being torn down for quitting. Bail out. */ + g_object_unref (file); + g_clear_object (&image); + return; + } + + if (open_as_layers) + { + GList *new_layers; + + new_layers = file_open_layers (shell->display->gimp, context, + GIMP_PROGRESS (shell->display), + image, FALSE, + file, GIMP_RUN_INTERACTIVE, NULL, + &status, &error); + + if (new_layers) + { + gint x = 0; + gint y = 0; + gint width = gimp_image_get_width (image); + gint height = gimp_image_get_height (image); + + if (gimp_display_get_image (shell->display)) + { + gimp_display_shell_untransform_viewport ( + shell, + ! gimp_display_shell_get_infinite_canvas (shell), + &x, &y, &width, &height); + } + + gimp_image_add_layers (image, new_layers, + GIMP_IMAGE_ACTIVE_PARENT, -1, + x, y, width, height, + _("Drop layers")); + + g_list_free (new_layers); + } + else if (status != GIMP_PDB_CANCEL) + { + warn = TRUE; + } + } + else if (gimp_display_get_image (shell->display)) + { + /* open any subsequent images in a new display */ + GimpImage *new_image; + + new_image = file_open_with_display (shell->display->gimp, context, + NULL, + file, FALSE, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget), + &status, &error); + + if (! new_image && status != GIMP_PDB_CANCEL) + warn = TRUE; + } + else + { + /* open the first image in the empty display */ + image = file_open_with_display (shell->display->gimp, context, + GIMP_PROGRESS (shell->display), + file, FALSE, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget), + &status, &error); + + if (image) + { + g_object_ref (image); + } + else if (status != GIMP_PDB_CANCEL) + { + warn = TRUE; + } + } + + /* Something above might have run a few rounds of the main loop. Check + * that shell->display is still there, otherwise ignore this as the app + * is being torn down for quitting. + */ + if (warn && shell->display) + { + gimp_message (shell->display->gimp, G_OBJECT (shell->display), + GIMP_MESSAGE_ERROR, + _("Opening '%s' failed:\n\n%s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + g_object_unref (file); + } + + if (image) + gimp_display_shell_dnd_flush (shell, image); + + g_clear_object (&image); +} + +static void +gimp_display_shell_drop_component (GtkWidget *widget, + gint x, + gint y, + GimpImage *image, + GimpChannelType component, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *dest_image = gimp_display_get_image (shell->display); + GimpChannel *channel; + GimpItem *new_item; + const gchar *desc; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! dest_image) + { + dest_image = gimp_image_new_from_component (image->gimp, + image, component); + gimp_create_display (dest_image->gimp, dest_image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (dest_image); + + return; + } + + channel = gimp_channel_new_from_component (image, component, NULL, NULL); + + new_item = gimp_item_convert (GIMP_ITEM (channel), + dest_image, GIMP_TYPE_LAYER); + g_object_unref (channel); + + if (new_item) + { + GimpLayer *new_layer = GIMP_LAYER (new_item); + + gimp_enum_get_value (GIMP_TYPE_CHANNEL_TYPE, component, + NULL, NULL, &desc, NULL); + gimp_object_take_name (GIMP_OBJECT (new_layer), + g_strdup_printf (_("%s Channel Copy"), desc)); + + gimp_image_undo_group_start (dest_image, GIMP_UNDO_GROUP_EDIT_PASTE, + _("Drop New Layer")); + + gimp_display_shell_dnd_position_item (shell, image, new_item); + + gimp_image_add_layer (dest_image, new_layer, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + + gimp_image_undo_group_end (dest_image); + + gimp_display_shell_dnd_flush (shell, dest_image); + } +} + +static void +gimp_display_shell_drop_pixbuf (GtkWidget *widget, + gint x, + gint y, + GdkPixbuf *pixbuf, + gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GimpLayer *new_layer; + gboolean has_alpha = FALSE; + + GIMP_LOG (DND, NULL); + + if (shell->display->gimp->busy) + return; + + if (! image) + { + image = gimp_image_new_from_pixbuf (shell->display->gimp, pixbuf, + _("Dropped Buffer")); + gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (gtk_widget_get_screen (widget)), + gimp_widget_get_monitor (widget)); + g_object_unref (image); + + return; + } + + if (gdk_pixbuf_get_n_channels (pixbuf) == 2 || + gdk_pixbuf_get_n_channels (pixbuf) == 4) + { + has_alpha = TRUE; + } + + new_layer = + gimp_layer_new_from_pixbuf (pixbuf, image, + gimp_image_get_layer_format (image, has_alpha), + _("Dropped Buffer"), + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + + if (new_layer) + { + GimpItem *new_item = GIMP_ITEM (new_layer); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, + _("Drop New Layer")); + + gimp_display_shell_dnd_position_item (shell, image, new_item); + + gimp_image_add_layer (image, new_layer, + GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); + + gimp_image_undo_group_end (image); + + gimp_display_shell_dnd_flush (shell, image); + } +} diff --git a/app/display/gimpdisplayshell-dnd.h b/app/display/gimpdisplayshell-dnd.h new file mode 100644 index 0000000..d1f6e99 --- /dev/null +++ b/app/display/gimpdisplayshell-dnd.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_DND_H__ +#define __GIMP_DISPLAY_SHELL_DND_H__ + + +void gimp_display_shell_dnd_init (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_DND_H__ */ diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c new file mode 100644 index 0000000..d6a63d5 --- /dev/null +++ b/app/display/gimpdisplayshell-draw.c @@ -0,0 +1,256 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp-cairo.h" +#include "core/gimp-utils.h" +#include "core/gimpimage.h" + +#include "gimpcanvas.h" +#include "gimpcanvas-style.h" +#include "gimpcanvaspath.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-draw.h" +#include "gimpdisplayshell-render.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayxfer.h" + +#ifdef GDK_WINDOWING_QUARTZ +#import +#endif + +/* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */ + + +/* public functions */ + +void +gimp_display_shell_draw_selection_out (GimpDisplayShell *shell, + cairo_t *cr, + GimpSegment *segs, + gint n_segs) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (cr != NULL); + g_return_if_fail (segs != NULL && n_segs > 0); + + gimp_canvas_set_selection_out_style (shell->canvas, cr, + shell->offset_x, shell->offset_y); + + gimp_cairo_segments (cr, segs, n_segs); + cairo_stroke (cr); +} + +void +gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, + cairo_t *cr, + cairo_pattern_t *mask, + gint index) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (cr != NULL); + g_return_if_fail (mask != NULL); + + gimp_canvas_set_selection_in_style (shell->canvas, cr, index, + shell->offset_x, shell->offset_y); + + cairo_mask (cr, mask); +} + +void +gimp_display_shell_draw_checkerboard (GimpDisplayShell *shell, + cairo_t *cr) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (cr != NULL); + + image = gimp_display_get_image (shell->display); + + if (G_UNLIKELY (! shell->checkerboard)) + { + GimpCheckSize check_size; + GimpCheckType check_type; + guchar check_light; + guchar check_dark; + GimpRGB light; + GimpRGB dark; + + g_object_get (shell->display->config, + "transparency-size", &check_size, + "transparency-type", &check_type, + NULL); + + gimp_checks_get_shades (check_type, &check_light, &check_dark); + gimp_rgb_set_uchar (&light, check_light, check_light, check_light); + gimp_rgb_set_uchar (&dark, check_dark, check_dark, check_dark); + + shell->checkerboard = + gimp_cairo_checkerboard_create (cr, + 1 << (check_size + 2), &light, &dark); + } + + cairo_translate (cr, - shell->offset_x, - shell->offset_y); + + if (gimp_image_get_component_visible (image, GIMP_CHANNEL_ALPHA)) + cairo_set_source (cr, shell->checkerboard); + else + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + + cairo_paint (cr); +} + +void +gimp_display_shell_draw_image (GimpDisplayShell *shell, + cairo_t *cr, + gint x, + gint y, + gint w, + gint h) +{ + gdouble chunk_width; + gdouble chunk_height; + gdouble scale = 1.0; + gint n_rows; + gint n_cols; + gint r, c; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (gimp_display_get_image (shell->display)); + g_return_if_fail (cr != NULL); + + /* display the image in RENDER_BUF_WIDTH x RENDER_BUF_HEIGHT + * maximally-sized image-space chunks. adjust the screen-space + * chunk size as necessary, to accommodate for the display + * transform and window scale factor. + */ + chunk_width = GIMP_DISPLAY_RENDER_BUF_WIDTH; + chunk_height = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + +#ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING + /* if we had this future API, things would look pretty on hires (retina) */ + scale *= + gdk_window_get_scale_factor ( + gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (shell)))); +#elif defined(GDK_WINDOWING_QUARTZ) + /* gtk2/osx retina support */ + if ([ + [NSScreen mainScreen] + respondsToSelector: @selector(backingScaleFactor) + ]) { + for (NSScreen * screen in [NSScreen screens]) { + float s = [screen backingScaleFactor]; + if (s > scale) scale = s; + } + } +#endif + + scale = MIN (scale, GIMP_DISPLAY_RENDER_MAX_SCALE); + scale *= MAX (shell->scale_x, shell->scale_y); + + if (scale != shell->scale_x) + chunk_width = (chunk_width - 1.0) * (shell->scale_x / scale); + if (scale != shell->scale_y) + chunk_height = (chunk_height - 1.0) * (shell->scale_y / scale); + + if (shell->rotate_untransform) + { + gdouble a = shell->rotate_angle * G_PI / 180.0; + + chunk_width = chunk_height = (MIN (chunk_width, chunk_height) - 1.0) / + (fabs (sin (a)) + fabs (cos (a))); + } + + /* divide the painted area to evenly-sized chunks */ + n_rows = ceil (h / floor (chunk_height)); + n_cols = ceil (w / floor (chunk_width)); + + for (r = 0; r < n_rows; r++) + { + gint y1 = y + (2 * r * h + n_rows) / (2 * n_rows); + gint y2 = y + (2 * (r + 1) * h + n_rows) / (2 * n_rows); + + for (c = 0; c < n_cols; c++) + { + gint x1 = x + (2 * c * w + n_cols) / (2 * n_cols); + gint x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols); + gdouble ix1, iy1; + gdouble ix2, iy2; + gint ix, iy; + gint iw, ih; + + /* map chunk from screen space to scaled image space */ + gimp_display_shell_untransform_bounds_with_scale ( + shell, scale, + x1, y1, x2, y2, + &ix1, &iy1, &ix2, &iy2); + + ix = floor (ix1); + iy = floor (iy1); + iw = ceil (ix2) - ix; + ih = ceil (iy2) - iy; + + cairo_save (cr); + + /* clip to chunk bounds, in screen space */ + cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1); + cairo_clip (cr); + + /* transform to scaled image space, and apply uneven scaling */ + if (shell->rotate_transform) + cairo_transform (cr, shell->rotate_transform); + cairo_translate (cr, -shell->offset_x, -shell->offset_y); + cairo_scale (cr, shell->scale_x / scale, shell->scale_y / scale); + + /* render image */ + gimp_display_shell_render (shell, cr, ix, iy, iw, ih, scale); + + cairo_restore (cr); + + /* if the GIMP_BRICK_WALL environment variable is defined, + * show chunk bounds + */ + { + static gint brick_wall = -1; + + if (brick_wall < 0) + brick_wall = (g_getenv ("GIMP_BRICK_WALL") != NULL); + + if (brick_wall) + { + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1); + cairo_stroke (cr); + } + } + } + } +} diff --git a/app/display/gimpdisplayshell-draw.h b/app/display/gimpdisplayshell-draw.h new file mode 100644 index 0000000..05dd2c1 --- /dev/null +++ b/app/display/gimpdisplayshell-draw.h @@ -0,0 +1,41 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_DRAW_H__ +#define __GIMP_DISPLAY_SHELL_DRAW_H__ + + +void gimp_display_shell_draw_selection_out (GimpDisplayShell *shell, + cairo_t *cr, + GimpSegment *segs, + gint n_segs); +void gimp_display_shell_draw_selection_in (GimpDisplayShell *shell, + cairo_t *cr, + cairo_pattern_t *mask, + gint index); + +void gimp_display_shell_draw_checkerboard (GimpDisplayShell *shell, + cairo_t *cr); +void gimp_display_shell_draw_image (GimpDisplayShell *shell, + cairo_t *cr, + gint x, + gint y, + gint w, + gint h); + + +#endif /* __GIMP_DISPLAY_SHELL_DRAW_H__ */ diff --git a/app/display/gimpdisplayshell-expose.c b/app/display/gimpdisplayshell-expose.c new file mode 100644 index 0000000..2320a90 --- /dev/null +++ b/app/display/gimpdisplayshell-expose.c @@ -0,0 +1,76 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" + + +void +gimp_display_shell_expose_area (GimpDisplayShell *shell, + gint x, + gint y, + gint w, + gint h) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gtk_widget_queue_draw_area (shell->canvas, x, y, w, h); +} + +void +gimp_display_shell_expose_region (GimpDisplayShell *shell, + cairo_region_t *region) +{ + GdkWindow *window; + gint n_rectangles; + gint i; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (region != NULL); + + if (! gtk_widget_get_realized (shell->canvas)) + return; + + window = gtk_widget_get_window (shell->canvas); + n_rectangles = cairo_region_num_rectangles (region); + + for (i = 0; i < n_rectangles; i++) + { + cairo_rectangle_int_t rectangle; + + cairo_region_get_rectangle (region, i, &rectangle); + + gdk_window_invalidate_rect (window, + (GdkRectangle *) &rectangle, + TRUE); + } +} + +void +gimp_display_shell_expose_full (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gtk_widget_queue_draw (shell->canvas); +} diff --git a/app/display/gimpdisplayshell-expose.h b/app/display/gimpdisplayshell-expose.h new file mode 100644 index 0000000..36d3519 --- /dev/null +++ b/app/display/gimpdisplayshell-expose.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_EXPOSE_H__ +#define __GIMP_DISPLAY_SHELL_EXPOSE_H__ + + +void gimp_display_shell_expose_area (GimpDisplayShell *shell, + gint x, + gint y, + gint w, + gint h); +void gimp_display_shell_expose_region (GimpDisplayShell *shell, + cairo_region_t *region); +void gimp_display_shell_expose_full (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_EXPOSE_H__ */ diff --git a/app/display/gimpdisplayshell-filter-dialog.c b/app/display/gimpdisplayshell-filter-dialog.c new file mode 100644 index 0000000..720529c --- /dev/null +++ b/app/display/gimpdisplayshell-filter-dialog.c @@ -0,0 +1,151 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1999 Manish Singh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpviewable.h" + +#include "widgets/gimpcolordisplayeditor.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-filter.h" +#include "gimpdisplayshell-filter-dialog.h" + +#include "gimp-intl.h" + + +typedef struct +{ + GimpDisplayShell *shell; + GtkWidget *dialog; + + GimpColorDisplayStack *old_stack; +} ColorDisplayDialog; + + +/* local function prototypes */ + +static void gimp_display_shell_filter_dialog_response (GtkWidget *widget, + gint response_id, + ColorDisplayDialog *cdd); + +static void gimp_display_shell_filter_dialog_free (ColorDisplayDialog *cdd); + + +/* public functions */ + +GtkWidget * +gimp_display_shell_filter_dialog_new (GimpDisplayShell *shell) +{ + GimpImage *image; + ColorDisplayDialog *cdd; + GtkWidget *editor; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + image = gimp_display_get_image (shell->display); + + cdd = g_slice_new0 (ColorDisplayDialog); + + cdd->shell = shell; + cdd->dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), + gimp_get_user_context (shell->display->gimp), + _("Color Display Filters"), + "gimp-display-filters", + GIMP_ICON_DISPLAY_FILTER, + _("Configure Color Display Filters"), + GTK_WIDGET (cdd->shell), + gimp_standard_help_func, + GIMP_HELP_DISPLAY_FILTER_DIALOG, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (cdd->dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_window_set_destroy_with_parent (GTK_WINDOW (cdd->dialog), TRUE); + + g_object_weak_ref (G_OBJECT (cdd->dialog), + (GWeakNotify) gimp_display_shell_filter_dialog_free, cdd); + + g_signal_connect (cdd->dialog, "response", + G_CALLBACK (gimp_display_shell_filter_dialog_response), + cdd); + + if (shell->filter_stack) + { + cdd->old_stack = gimp_color_display_stack_clone (shell->filter_stack); + + g_object_weak_ref (G_OBJECT (cdd->dialog), + (GWeakNotify) g_object_unref, cdd->old_stack); + } + else + { + GimpColorDisplayStack *stack = gimp_color_display_stack_new (); + + gimp_display_shell_filter_set (shell, stack); + g_object_unref (stack); + } + + editor = gimp_color_display_editor_new (shell->display->gimp, + shell->filter_stack, + gimp_display_shell_get_color_config (shell), + GIMP_COLOR_MANAGED (shell)); + gtk_container_set_border_width (GTK_CONTAINER (editor), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (cdd->dialog))), + editor, TRUE, TRUE, 0); + gtk_widget_show (editor); + + return cdd->dialog; +} + + +/* private functions */ + +static void +gimp_display_shell_filter_dialog_response (GtkWidget *widget, + gint response_id, + ColorDisplayDialog *cdd) +{ + if (response_id != GTK_RESPONSE_OK) + gimp_display_shell_filter_set (cdd->shell, cdd->old_stack); + + gtk_widget_destroy (GTK_WIDGET (cdd->dialog)); +} + +static void +gimp_display_shell_filter_dialog_free (ColorDisplayDialog *cdd) +{ + g_slice_free (ColorDisplayDialog, cdd); +} diff --git a/app/display/gimpdisplayshell-filter-dialog.h b/app/display/gimpdisplayshell-filter-dialog.h new file mode 100644 index 0000000..9f73a8b --- /dev/null +++ b/app/display/gimpdisplayshell-filter-dialog.h @@ -0,0 +1,25 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1999 Manish Singh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DISPLAY_SHELL_FILTER_DIALOG_H__ +#define __GIMP_DISPLAY_SHELL_FILTER_DIALOG_H__ + + +GtkWidget * gimp_display_shell_filter_dialog_new (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_FILTER_DIALOG_H__ */ diff --git a/app/display/gimpdisplayshell-filter.c b/app/display/gimpdisplayshell-filter.c new file mode 100644 index 0000000..1cf60e8 --- /dev/null +++ b/app/display/gimpdisplayshell-filter.c @@ -0,0 +1,116 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1999 Manish Singh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-filter.h" +#include "gimpdisplayshell-profile.h" + + +/* local function prototypes */ + +static void gimp_display_shell_filter_changed (GimpColorDisplayStack *stack, + GimpDisplayShell *shell); + + +/* public functions */ + +void +gimp_display_shell_filter_set (GimpDisplayShell *shell, + GimpColorDisplayStack *stack) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (stack == NULL || GIMP_IS_COLOR_DISPLAY_STACK (stack)); + + if (stack == shell->filter_stack) + return; + + if (shell->filter_stack) + { + g_signal_handlers_disconnect_by_func (shell->filter_stack, + gimp_display_shell_filter_changed, + shell); + } + + g_set_object (&shell->filter_stack, stack); + + if (shell->filter_stack) + { + g_signal_connect (shell->filter_stack, "changed", + G_CALLBACK (gimp_display_shell_filter_changed), + shell); + } + + gimp_display_shell_filter_changed (NULL, shell); +} + +gboolean +gimp_display_shell_has_filter (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + if (shell->filter_stack) + { + GList *iter; + + for (iter = shell->filter_stack->filters; iter; iter = g_list_next (iter)) + { + if (gimp_color_display_get_enabled (GIMP_COLOR_DISPLAY (iter->data))) + return TRUE; + } + } + + return FALSE; +} + + +/* private functions */ + +static gboolean +gimp_display_shell_filter_changed_idle (gpointer data) +{ + GimpDisplayShell *shell = data; + + gimp_display_shell_profile_update (shell); + gimp_display_shell_expose_full (shell); + + shell->filter_idle_id = 0; + + return FALSE; +} + +static void +gimp_display_shell_filter_changed (GimpColorDisplayStack *stack, + GimpDisplayShell *shell) +{ + if (shell->filter_idle_id) + g_source_remove (shell->filter_idle_id); + + shell->filter_idle_id = + g_idle_add_full (G_PRIORITY_LOW, + gimp_display_shell_filter_changed_idle, + shell, NULL); +} diff --git a/app/display/gimpdisplayshell-filter.h b/app/display/gimpdisplayshell-filter.h new file mode 100644 index 0000000..7543a8b --- /dev/null +++ b/app/display/gimpdisplayshell-filter.h @@ -0,0 +1,28 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1999 Manish Singh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DISPLAY_SHELL_FILTER_H__ +#define __GIMP_DISPLAY_SHELL_FILTER_H__ + + +void gimp_display_shell_filter_set (GimpDisplayShell *shell, + GimpColorDisplayStack *stack); + +gboolean gimp_display_shell_has_filter (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_FILTER_H__ */ diff --git a/app/display/gimpdisplayshell-grab.c b/app/display/gimpdisplayshell-grab.c new file mode 100644 index 0000000..d3bb550 --- /dev/null +++ b/app/display/gimpdisplayshell-grab.c @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdisplayshell-grab.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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-grab.h" + + +gboolean +gimp_display_shell_pointer_grab (GimpDisplayShell *shell, + const GdkEvent *event, + GdkEventMask event_mask) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + g_return_val_if_fail (shell->pointer_grabbed == FALSE, FALSE); + + if (event) + { + GdkGrabStatus status; + + status = gdk_pointer_grab (gtk_widget_get_window (shell->canvas), + FALSE, event_mask, NULL, NULL, + gdk_event_get_time (event)); + + if (status != GDK_GRAB_SUCCESS) + { + g_printerr ("%s: gdk_pointer_grab failed with status %d\n", + G_STRFUNC, status); + return FALSE; + } + + shell->pointer_grab_time = gdk_event_get_time (event); + } + + gtk_grab_add (shell->canvas); + + shell->pointer_grabbed = TRUE; + + return TRUE; +} + +void +gimp_display_shell_pointer_ungrab (GimpDisplayShell *shell, + const GdkEvent *event) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->pointer_grabbed == TRUE); + + gtk_grab_remove (shell->canvas); + + if (event) + { + gdk_display_pointer_ungrab (gtk_widget_get_display (shell->canvas), + shell->pointer_grab_time); + + shell->pointer_grab_time = 0; + } + + shell->pointer_grabbed = FALSE; +} + +gboolean +gimp_display_shell_keyboard_grab (GimpDisplayShell *shell, + const GdkEvent *event) +{ + GdkGrabStatus status; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + g_return_val_if_fail (shell->keyboard_grabbed == FALSE, FALSE); + + status = gdk_keyboard_grab (gtk_widget_get_window (shell->canvas), + FALSE, + gdk_event_get_time (event)); + + if (status != GDK_GRAB_SUCCESS) + { + g_printerr ("%s: gdk_keyboard_grab failed with status %d\n", + G_STRFUNC, status); + return FALSE; + } + + shell->keyboard_grabbed = TRUE; + shell->keyboard_grab_time = gdk_event_get_time (event); + + return TRUE; +} + +void +gimp_display_shell_keyboard_ungrab (GimpDisplayShell *shell, + const GdkEvent *event) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (event != NULL); + g_return_if_fail (shell->keyboard_grabbed == TRUE); + + gdk_display_keyboard_ungrab (gtk_widget_get_display (shell->canvas), + shell->keyboard_grab_time); + + shell->keyboard_grabbed = FALSE; + shell->keyboard_grab_time = 0; +} diff --git a/app/display/gimpdisplayshell-grab.h b/app/display/gimpdisplayshell-grab.h new file mode 100644 index 0000000..915cf34 --- /dev/null +++ b/app/display/gimpdisplayshell-grab.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdisplayshell-grab.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_GRAB_H__ +#define __GIMP_DISPLAY_SHELL_GRAB_H__ + + +gboolean gimp_display_shell_pointer_grab (GimpDisplayShell *shell, + const GdkEvent *event, + GdkEventMask event_mask); +void gimp_display_shell_pointer_ungrab (GimpDisplayShell *shell, + const GdkEvent *event); + +gboolean gimp_display_shell_keyboard_grab (GimpDisplayShell *shell, + const GdkEvent *event); +void gimp_display_shell_keyboard_ungrab (GimpDisplayShell *shell, + const GdkEvent *event); + + +#endif /* __GIMP_DISPLAY_SHELL_GRAB_H__ */ diff --git a/app/display/gimpdisplayshell-handlers.c b/app/display/gimpdisplayshell-handlers.c new file mode 100644 index 0000000..7680467 --- /dev/null +++ b/app/display/gimpdisplayshell-handlers.c @@ -0,0 +1,1239 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayoptions.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimp-cairo.h" +#include "core/gimpguide.h" +#include "core/gimpimage.h" +#include "core/gimpimage-grid.h" +#include "core/gimpimage-guides.h" +#include "core/gimpimage-quick-mask.h" +#include "core/gimpimage-sample-points.h" +#include "core/gimpitem.h" +#include "core/gimpitemstack.h" +#include "core/gimpsamplepoint.h" +#include "core/gimptreehandler.h" + +#include "vectors/gimpvectors.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvascanvasboundary.h" +#include "gimpcanvasguide.h" +#include "gimpcanvaslayerboundary.h" +#include "gimpcanvaspath.h" +#include "gimpcanvasproxygroup.h" +#include "gimpcanvassamplepoint.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-callbacks.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-handlers.h" +#include "gimpdisplayshell-icon.h" +#include "gimpdisplayshell-profile.h" +#include "gimpdisplayshell-rulers.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-selection.h" +#include "gimpdisplayshell-title.h" +#include "gimpimagewindow.h" +#include "gimpstatusbar.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_display_shell_clean_dirty_handler (GimpImage *image, + GimpDirtyMask dirty_mask, + GimpDisplayShell *shell); +static void gimp_display_shell_undo_event_handler (GimpImage *image, + GimpUndoEvent event, + GimpUndo *undo, + GimpDisplayShell *shell); +static void gimp_display_shell_grid_notify_handler (GimpGrid *grid, + GParamSpec *pspec, + GimpDisplayShell *shell); +static void gimp_display_shell_name_changed_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_selection_invalidate_handler + (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_component_visibility_changed_handler + (GimpImage *image, + GimpChannelType channel, + GimpDisplayShell *shell); +static void gimp_display_shell_size_changed_detailed_handler + (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpDisplayShell *shell); +static void gimp_display_shell_resolution_changed_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_quick_mask_changed_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_guide_add_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell); +static void gimp_display_shell_guide_remove_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell); +static void gimp_display_shell_guide_move_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell); +static void gimp_display_shell_sample_point_add_handler (GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell); +static void gimp_display_shell_sample_point_remove_handler(GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell); +static void gimp_display_shell_sample_point_move_handler (GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell); +static void gimp_display_shell_invalidate_preview_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_mode_changed_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_precision_changed_handler (GimpImage *image, + GimpDisplayShell *shell); +static void gimp_display_shell_profile_changed_handler (GimpColorManaged *image, + GimpDisplayShell *shell); +static void gimp_display_shell_saved_handler (GimpImage *image, + GFile *file, + GimpDisplayShell *shell); +static void gimp_display_shell_exported_handler (GimpImage *image, + GFile *file, + GimpDisplayShell *shell); + +static void gimp_display_shell_active_vectors_handler (GimpImage *image, + GimpDisplayShell *shell); + +static void gimp_display_shell_vectors_freeze_handler (GimpVectors *vectors, + GimpDisplayShell *shell); +static void gimp_display_shell_vectors_thaw_handler (GimpVectors *vectors, + GimpDisplayShell *shell); +static void gimp_display_shell_vectors_visible_handler (GimpVectors *vectors, + GimpDisplayShell *shell); +static void gimp_display_shell_vectors_add_handler (GimpContainer *container, + GimpVectors *vectors, + GimpDisplayShell *shell); +static void gimp_display_shell_vectors_remove_handler (GimpContainer *container, + GimpVectors *vectors, + GimpDisplayShell *shell); + +static void gimp_display_shell_check_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_title_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_nav_size_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_monitor_res_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_padding_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_ants_speed_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_quality_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_color_config_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell); +static void gimp_display_shell_display_changed_handler (GimpContext *context, + GimpDisplay *display, + GimpDisplayShell *shell); + + +/* public functions */ + +void +gimp_display_shell_connect (GimpDisplayShell *shell) +{ + GimpImage *image; + GimpContainer *vectors; + GimpDisplayConfig *config; + GimpColorConfig *color_config; + GimpContext *user_context; + GList *list; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_DISPLAY (shell->display)); + + image = gimp_display_get_image (shell->display); + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + vectors = gimp_image_get_vectors (image); + + config = shell->display->config; + color_config = GIMP_CORE_CONFIG (config)->color_management; + + user_context = gimp_get_user_context (shell->display->gimp); + + g_signal_connect (image, "clean", + G_CALLBACK (gimp_display_shell_clean_dirty_handler), + shell); + g_signal_connect (image, "dirty", + G_CALLBACK (gimp_display_shell_clean_dirty_handler), + shell); + g_signal_connect (image, "undo-event", + G_CALLBACK (gimp_display_shell_undo_event_handler), + shell); + + g_signal_connect (gimp_image_get_grid (image), "notify", + G_CALLBACK (gimp_display_shell_grid_notify_handler), + shell); + g_object_set (shell->grid, "grid", gimp_image_get_grid (image), NULL); + + g_signal_connect (image, "name-changed", + G_CALLBACK (gimp_display_shell_name_changed_handler), + shell); + g_signal_connect (image, "selection-invalidate", + G_CALLBACK (gimp_display_shell_selection_invalidate_handler), + shell); + g_signal_connect (image, "component-visibility-changed", + G_CALLBACK (gimp_display_shell_component_visibility_changed_handler), + shell); + g_signal_connect (image, "size-changed-detailed", + G_CALLBACK (gimp_display_shell_size_changed_detailed_handler), + shell); + g_signal_connect (image, "resolution-changed", + G_CALLBACK (gimp_display_shell_resolution_changed_handler), + shell); + g_signal_connect (image, "quick-mask-changed", + G_CALLBACK (gimp_display_shell_quick_mask_changed_handler), + shell); + + g_signal_connect (image, "guide-added", + G_CALLBACK (gimp_display_shell_guide_add_handler), + shell); + g_signal_connect (image, "guide-removed", + G_CALLBACK (gimp_display_shell_guide_remove_handler), + shell); + g_signal_connect (image, "guide-moved", + G_CALLBACK (gimp_display_shell_guide_move_handler), + shell); + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + gimp_display_shell_guide_add_handler (image, list->data, shell); + } + + g_signal_connect (image, "sample-point-added", + G_CALLBACK (gimp_display_shell_sample_point_add_handler), + shell); + g_signal_connect (image, "sample-point-removed", + G_CALLBACK (gimp_display_shell_sample_point_remove_handler), + shell); + g_signal_connect (image, "sample-point-moved", + G_CALLBACK (gimp_display_shell_sample_point_move_handler), + shell); + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + gimp_display_shell_sample_point_add_handler (image, list->data, shell); + } + + g_signal_connect (image, "invalidate-preview", + G_CALLBACK (gimp_display_shell_invalidate_preview_handler), + shell); + g_signal_connect (image, "mode-changed", + G_CALLBACK (gimp_display_shell_mode_changed_handler), + shell); + g_signal_connect (image, "precision-changed", + G_CALLBACK (gimp_display_shell_precision_changed_handler), + shell); + g_signal_connect (image, "profile-changed", + G_CALLBACK (gimp_display_shell_profile_changed_handler), + shell); + g_signal_connect (image, "saved", + G_CALLBACK (gimp_display_shell_saved_handler), + shell); + g_signal_connect (image, "exported", + G_CALLBACK (gimp_display_shell_exported_handler), + shell); + + g_signal_connect (image, "active-vectors-changed", + G_CALLBACK (gimp_display_shell_active_vectors_handler), + shell); + + shell->vectors_freeze_handler = + gimp_tree_handler_connect (vectors, "freeze", + G_CALLBACK (gimp_display_shell_vectors_freeze_handler), + shell); + shell->vectors_thaw_handler = + gimp_tree_handler_connect (vectors, "thaw", + G_CALLBACK (gimp_display_shell_vectors_thaw_handler), + shell); + shell->vectors_visible_handler = + gimp_tree_handler_connect (vectors, "visibility-changed", + G_CALLBACK (gimp_display_shell_vectors_visible_handler), + shell); + + g_signal_connect (vectors, "add", + G_CALLBACK (gimp_display_shell_vectors_add_handler), + shell); + g_signal_connect (vectors, "remove", + G_CALLBACK (gimp_display_shell_vectors_remove_handler), + shell); + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (vectors)); + list; + list = g_list_next (list)) + { + gimp_display_shell_vectors_add_handler (vectors, list->data, shell); + } + + g_signal_connect (config, + "notify::transparency-size", + G_CALLBACK (gimp_display_shell_check_notify_handler), + shell); + g_signal_connect (config, + "notify::transparency-type", + G_CALLBACK (gimp_display_shell_check_notify_handler), + shell); + + g_signal_connect (config, + "notify::image-title-format", + G_CALLBACK (gimp_display_shell_title_notify_handler), + shell); + g_signal_connect (config, + "notify::image-status-format", + G_CALLBACK (gimp_display_shell_title_notify_handler), + shell); + g_signal_connect (config, + "notify::navigation-preview-size", + G_CALLBACK (gimp_display_shell_nav_size_notify_handler), + shell); + g_signal_connect (config, + "notify::monitor-resolution-from-windowing-system", + G_CALLBACK (gimp_display_shell_monitor_res_notify_handler), + shell); + g_signal_connect (config, + "notify::monitor-xresolution", + G_CALLBACK (gimp_display_shell_monitor_res_notify_handler), + shell); + g_signal_connect (config, + "notify::monitor-yresolution", + G_CALLBACK (gimp_display_shell_monitor_res_notify_handler), + shell); + + g_signal_connect (config->default_view, + "notify::padding-mode", + G_CALLBACK (gimp_display_shell_padding_notify_handler), + shell); + g_signal_connect (config->default_view, + "notify::padding-color", + G_CALLBACK (gimp_display_shell_padding_notify_handler), + shell); + g_signal_connect (config->default_fullscreen_view, + "notify::padding-mode", + G_CALLBACK (gimp_display_shell_padding_notify_handler), + shell); + g_signal_connect (config->default_fullscreen_view, + "notify::padding-color", + G_CALLBACK (gimp_display_shell_padding_notify_handler), + shell); + + g_signal_connect (config, + "notify::marching-ants-speed", + G_CALLBACK (gimp_display_shell_ants_speed_notify_handler), + shell); + + g_signal_connect (config, + "notify::zoom-quality", + G_CALLBACK (gimp_display_shell_quality_notify_handler), + shell); + + g_signal_connect (color_config, "notify", + G_CALLBACK (gimp_display_shell_color_config_notify_handler), + shell); + + g_signal_connect (user_context, "display-changed", + G_CALLBACK (gimp_display_shell_display_changed_handler), + shell); + + gimp_display_shell_active_vectors_handler (image, shell); + gimp_display_shell_invalidate_preview_handler (image, shell); + gimp_display_shell_quick_mask_changed_handler (image, shell); + gimp_display_shell_profile_changed_handler (GIMP_COLOR_MANAGED (image), + shell); + gimp_display_shell_color_config_notify_handler (G_OBJECT (color_config), + NULL, /* sync all */ + shell); + + gimp_canvas_layer_boundary_set_layer (GIMP_CANVAS_LAYER_BOUNDARY (shell->layer_boundary), + gimp_image_get_active_layer (image)); + + gimp_canvas_canvas_boundary_set_image (GIMP_CANVAS_CANVAS_BOUNDARY (shell->canvas_boundary), + image); + + if (shell->show_all) + { + gimp_image_inc_show_all_count (image); + + gimp_image_flush (image); + } +} + +void +gimp_display_shell_disconnect (GimpDisplayShell *shell) +{ + GimpImage *image; + GimpContainer *vectors; + GimpDisplayConfig *config; + GimpColorConfig *color_config; + GimpContext *user_context; + GList *list; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_DISPLAY (shell->display)); + + image = gimp_display_get_image (shell->display); + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + vectors = gimp_image_get_vectors (image); + + config = shell->display->config; + color_config = GIMP_CORE_CONFIG (config)->color_management; + + user_context = gimp_get_user_context (shell->display->gimp); + + gimp_display_shell_icon_update_stop (shell); + + gimp_canvas_layer_boundary_set_layer (GIMP_CANVAS_LAYER_BOUNDARY (shell->layer_boundary), + NULL); + + gimp_canvas_canvas_boundary_set_image (GIMP_CANVAS_CANVAS_BOUNDARY (shell->canvas_boundary), + NULL); + + g_signal_handlers_disconnect_by_func (user_context, + gimp_display_shell_display_changed_handler, + shell); + + g_signal_handlers_disconnect_by_func (color_config, + gimp_display_shell_color_config_notify_handler, + shell); + shell->color_config_set = FALSE; + + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_quality_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_ants_speed_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config->default_fullscreen_view, + gimp_display_shell_padding_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config->default_view, + gimp_display_shell_padding_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_monitor_res_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_nav_size_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_title_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (config, + gimp_display_shell_check_notify_handler, + shell); + + g_signal_handlers_disconnect_by_func (vectors, + gimp_display_shell_vectors_remove_handler, + shell); + g_signal_handlers_disconnect_by_func (vectors, + gimp_display_shell_vectors_add_handler, + shell); + + gimp_tree_handler_disconnect (shell->vectors_visible_handler); + shell->vectors_visible_handler = NULL; + + gimp_tree_handler_disconnect (shell->vectors_thaw_handler); + shell->vectors_thaw_handler = NULL; + + gimp_tree_handler_disconnect (shell->vectors_freeze_handler); + shell->vectors_freeze_handler = NULL; + + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_active_vectors_handler, + shell); + + for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (vectors)); + list; + list = g_list_next (list)) + { + gimp_canvas_proxy_group_remove_item (GIMP_CANVAS_PROXY_GROUP (shell->vectors), + list->data); + } + + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_exported_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_saved_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_profile_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_precision_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_mode_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_invalidate_preview_handler, + shell); + + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_guide_add_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_guide_remove_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_guide_move_handler, + shell); + for (list = gimp_image_get_guides (image); + list; + list = g_list_next (list)) + { + gimp_canvas_proxy_group_remove_item (GIMP_CANVAS_PROXY_GROUP (shell->guides), + list->data); + } + + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_sample_point_add_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_sample_point_remove_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_sample_point_move_handler, + shell); + for (list = gimp_image_get_sample_points (image); + list; + list = g_list_next (list)) + { + gimp_canvas_proxy_group_remove_item (GIMP_CANVAS_PROXY_GROUP (shell->sample_points), + list->data); + } + + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_quick_mask_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_resolution_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_component_visibility_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_size_changed_detailed_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_selection_invalidate_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_name_changed_handler, + shell); + g_signal_handlers_disconnect_by_func (gimp_image_get_grid (image), + gimp_display_shell_grid_notify_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_undo_event_handler, + shell); + g_signal_handlers_disconnect_by_func (image, + gimp_display_shell_clean_dirty_handler, + shell); + + if (shell->show_all) + { + gimp_image_dec_show_all_count (image); + + gimp_image_flush (image); + } +} + + +/* private functions */ + +static void +gimp_display_shell_clean_dirty_handler (GimpImage *image, + GimpDirtyMask dirty_mask, + GimpDisplayShell *shell) +{ + gimp_display_shell_title_update (shell); +} + +static void +gimp_display_shell_undo_event_handler (GimpImage *image, + GimpUndoEvent event, + GimpUndo *undo, + GimpDisplayShell *shell) +{ + gimp_display_shell_title_update (shell); +} + +static void +gimp_display_shell_grid_notify_handler (GimpGrid *grid, + GParamSpec *pspec, + GimpDisplayShell *shell) +{ + g_object_set (shell->grid, "grid", grid, NULL); +} + +static void +gimp_display_shell_name_changed_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_title_update (shell); +} + +static void +gimp_display_shell_selection_invalidate_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_selection_undraw (shell); +} + +static void +gimp_display_shell_resolution_changed_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_scale_update (shell); + + if (shell->dot_for_dot) + { + if (shell->unit != GIMP_UNIT_PIXEL) + { + gimp_display_shell_rulers_update (shell); + } + + gimp_display_shell_scaled (shell); + } + else + { + /* A resolution change has the same effect as a size change from + * a display shell point of view. Force a redraw of the display + * so that we don't get any display garbage. + */ + + GimpDisplayConfig *config = shell->display->config; + gboolean resize_window; + + /* Resize windows only in multi-window mode */ + resize_window = (config->resize_windows_on_resize && + ! GIMP_GUI_CONFIG (config)->single_window_mode); + + gimp_display_shell_scale_resize (shell, resize_window, FALSE); + } +} + +static void +gimp_display_shell_quick_mask_changed_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + GtkImage *gtk_image; + gboolean quick_mask_state; + + gtk_image = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (shell->quick_mask_button))); + + g_signal_handlers_block_by_func (shell->quick_mask_button, + gimp_display_shell_quick_mask_toggled, + shell); + + quick_mask_state = gimp_image_get_quick_mask_state (image); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shell->quick_mask_button), + quick_mask_state); + + if (quick_mask_state) + gtk_image_set_from_icon_name (gtk_image, GIMP_ICON_QUICK_MASK_ON, + GTK_ICON_SIZE_MENU); + else + gtk_image_set_from_icon_name (gtk_image, GIMP_ICON_QUICK_MASK_OFF, + GTK_ICON_SIZE_MENU); + + g_signal_handlers_unblock_by_func (shell->quick_mask_button, + gimp_display_shell_quick_mask_toggled, + shell); +} + +static void +gimp_display_shell_guide_add_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->guides); + GimpCanvasItem *item; + GimpGuideStyle style; + + style = gimp_guide_get_style (guide); + item = gimp_canvas_guide_new (shell, + gimp_guide_get_orientation (guide), + gimp_guide_get_position (guide), + style); + + gimp_canvas_proxy_group_add_item (group, guide, item); + g_object_unref (item); +} + +static void +gimp_display_shell_guide_remove_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->guides); + + gimp_canvas_proxy_group_remove_item (group, guide); +} + +static void +gimp_display_shell_guide_move_handler (GimpImage *image, + GimpGuide *guide, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->guides); + GimpCanvasItem *item; + + item = gimp_canvas_proxy_group_get_item (group, guide); + + gimp_canvas_guide_set (item, + gimp_guide_get_orientation (guide), + gimp_guide_get_position (guide)); +} + +static void +gimp_display_shell_sample_point_add_handler (GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->sample_points); + GimpCanvasItem *item; + GList *list; + gint x; + gint y; + gint i; + + gimp_sample_point_get_position (sample_point, &x, &y); + + item = gimp_canvas_sample_point_new (shell, x, y, 0, TRUE); + + gimp_canvas_proxy_group_add_item (group, sample_point, item); + g_object_unref (item); + + for (list = gimp_image_get_sample_points (image), i = 1; + list; + list = g_list_next (list), i++) + { + GimpSamplePoint *sample_point = list->data; + + item = gimp_canvas_proxy_group_get_item (group, sample_point); + + if (item) + g_object_set (item, + "index", i, + NULL); + } +} + +static void +gimp_display_shell_sample_point_remove_handler (GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->sample_points); + GList *list; + gint i; + + gimp_canvas_proxy_group_remove_item (group, sample_point); + + for (list = gimp_image_get_sample_points (image), i = 1; + list; + list = g_list_next (list), i++) + { + GimpSamplePoint *sample_point = list->data; + GimpCanvasItem *item; + + item = gimp_canvas_proxy_group_get_item (group, sample_point); + + if (item) + g_object_set (item, + "index", i, + NULL); + } +} + +static void +gimp_display_shell_sample_point_move_handler (GimpImage *image, + GimpSamplePoint *sample_point, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->sample_points); + GimpCanvasItem *item; + gint x; + gint y; + + item = gimp_canvas_proxy_group_get_item (group, sample_point); + + gimp_sample_point_get_position (sample_point, &x, &y); + + gimp_canvas_sample_point_set (item, x, y); +} + +static void +gimp_display_shell_component_visibility_changed_handler (GimpImage *image, + GimpChannelType channel, + GimpDisplayShell *shell) +{ + if (channel == GIMP_CHANNEL_ALPHA && shell->show_all) + gimp_display_shell_expose_full (shell); +} + +static void +gimp_display_shell_size_changed_detailed_handler (GimpImage *image, + gint previous_origin_x, + gint previous_origin_y, + gint previous_width, + gint previous_height, + GimpDisplayShell *shell) +{ + GimpDisplayConfig *config = shell->display->config; + gboolean resize_window; + + /* Resize windows only in multi-window mode */ + resize_window = (config->resize_windows_on_resize && + ! GIMP_GUI_CONFIG (config)->single_window_mode); + + if (resize_window) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + /* If the window is resized just center the image in it when it + * has change size + */ + gimp_image_window_shrink_wrap (window, FALSE); + } + } + else + { + GimpImage *image = gimp_display_get_image (shell->display); + gint new_width = gimp_image_get_width (image); + gint new_height = gimp_image_get_height (image); + gint scaled_previous_origin_x; + gint scaled_previous_origin_y; + gboolean horizontally; + gboolean vertically; + + scaled_previous_origin_x = SCALEX (shell, previous_origin_x); + scaled_previous_origin_y = SCALEY (shell, previous_origin_y); + + horizontally = (SCALEX (shell, previous_width) > shell->disp_width && + SCALEX (shell, new_width) <= shell->disp_width); + vertically = (SCALEY (shell, previous_height) > shell->disp_height && + SCALEY (shell, new_height) <= shell->disp_height); + + gimp_display_shell_scroll_set_offset (shell, + shell->offset_x + scaled_previous_origin_x, + shell->offset_y + scaled_previous_origin_y); + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + gimp_display_shell_scroll_center_image (shell, + horizontally, vertically); + } + + /* The above calls might not lead to a call to + * gimp_display_shell_scroll_clamp_and_update() and + * gimp_display_shell_expose_full() in all cases because when + * scaling the old and new scroll offset might be the same. + * + * We need them to be called in all cases, so simply call them + * explicitly here at the end + */ + gimp_display_shell_scroll_clamp_and_update (shell); + + gimp_display_shell_expose_full (shell); + } +} + +static void +gimp_display_shell_invalidate_preview_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_icon_update (shell); +} + +static void +gimp_display_shell_mode_changed_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_profile_update (shell); +} + +static void +gimp_display_shell_precision_changed_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + gimp_display_shell_profile_update (shell); +} + +static void +gimp_display_shell_profile_changed_handler (GimpColorManaged *image, + GimpDisplayShell *shell) +{ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (shell)); +} + +static void +gimp_display_shell_saved_handler (GimpImage *image, + GFile *file, + GimpDisplayShell *shell) +{ + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_push_temp (statusbar, GIMP_MESSAGE_INFO, + GIMP_ICON_DOCUMENT_SAVE, + _("Image saved to '%s'"), + gimp_file_get_utf8_name (file)); +} + +static void +gimp_display_shell_exported_handler (GimpImage *image, + GFile *file, + GimpDisplayShell *shell) +{ + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_push_temp (statusbar, GIMP_MESSAGE_INFO, + GIMP_ICON_DOCUMENT_SAVE, + _("Image exported to '%s'"), + gimp_file_get_utf8_name (file)); +} + +static void +gimp_display_shell_active_vectors_handler (GimpImage *image, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->vectors); + GimpVectors *active = gimp_image_get_active_vectors (image); + GList *list; + + for (list = gimp_image_get_vectors_iter (image); + list; + list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + GimpCanvasItem *item; + + item = gimp_canvas_proxy_group_get_item (group, vectors); + + gimp_canvas_item_set_highlight (item, vectors == active); + } +} + +static void +gimp_display_shell_vectors_freeze_handler (GimpVectors *vectors, + GimpDisplayShell *shell) +{ + /* do nothing */ +} + +static void +gimp_display_shell_vectors_thaw_handler (GimpVectors *vectors, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->vectors); + GimpCanvasItem *item; + + item = gimp_canvas_proxy_group_get_item (group, vectors); + + gimp_canvas_path_set (item, gimp_vectors_get_bezier (vectors)); +} + +static void +gimp_display_shell_vectors_visible_handler (GimpVectors *vectors, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->vectors); + GimpCanvasItem *item; + + item = gimp_canvas_proxy_group_get_item (group, vectors); + + gimp_canvas_item_set_visible (item, + gimp_item_get_visible (GIMP_ITEM (vectors))); +} + +static void +gimp_display_shell_vectors_add_handler (GimpContainer *container, + GimpVectors *vectors, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->vectors); + GimpCanvasItem *item; + + item = gimp_canvas_path_new (shell, + gimp_vectors_get_bezier (vectors), + 0, 0, + FALSE, + GIMP_PATH_STYLE_VECTORS); + gimp_canvas_item_set_visible (item, + gimp_item_get_visible (GIMP_ITEM (vectors))); + + gimp_canvas_proxy_group_add_item (group, vectors, item); + g_object_unref (item); +} + +static void +gimp_display_shell_vectors_remove_handler (GimpContainer *container, + GimpVectors *vectors, + GimpDisplayShell *shell) +{ + GimpCanvasProxyGroup *group = GIMP_CANVAS_PROXY_GROUP (shell->vectors); + + gimp_canvas_proxy_group_remove_item (group, vectors); +} + +static void +gimp_display_shell_check_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + GimpCanvasPaddingMode padding_mode; + GimpRGB padding_color; + + g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy); + + gimp_display_shell_get_padding (shell, &padding_mode, &padding_color); + + switch (padding_mode) + { + case GIMP_CANVAS_PADDING_MODE_LIGHT_CHECK: + case GIMP_CANVAS_PADDING_MODE_DARK_CHECK: + gimp_display_shell_set_padding (shell, padding_mode, &padding_color); + break; + + default: + break; + } + + gimp_display_shell_expose_full (shell); +} + +static void +gimp_display_shell_title_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + gimp_display_shell_title_update (shell); +} + +static void +gimp_display_shell_nav_size_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + g_clear_pointer (&shell->nav_popup, gtk_widget_destroy); +} + +static void +gimp_display_shell_monitor_res_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + if (GIMP_DISPLAY_CONFIG (config)->monitor_res_from_gdk) + { + gimp_get_monitor_resolution (gtk_widget_get_screen (GTK_WIDGET (shell)), + gimp_widget_get_monitor (GTK_WIDGET (shell)), + &shell->monitor_xres, + &shell->monitor_yres); + } + else + { + shell->monitor_xres = GIMP_DISPLAY_CONFIG (config)->monitor_xres; + shell->monitor_yres = GIMP_DISPLAY_CONFIG (config)->monitor_yres; + } + + gimp_display_shell_scale_update (shell); + + if (! shell->dot_for_dot) + { + gimp_display_shell_scroll_clamp_and_update (shell); + + gimp_display_shell_scaled (shell); + + gimp_display_shell_expose_full (shell); + } +} + +static void +gimp_display_shell_padding_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + GimpDisplayConfig *display_config; + GimpImageWindow *window; + gboolean fullscreen; + GimpCanvasPaddingMode padding_mode; + GimpRGB padding_color; + + display_config = shell->display->config; + + window = gimp_display_shell_get_window (shell); + + if (window) + fullscreen = gimp_image_window_get_fullscreen (window); + else + fullscreen = FALSE; + + /* if the user did not set the padding mode for this display explicitly */ + if (! shell->fullscreen_options->padding_mode_set) + { + padding_mode = display_config->default_fullscreen_view->padding_mode; + padding_color = display_config->default_fullscreen_view->padding_color; + + if (fullscreen) + { + gimp_display_shell_set_padding (shell, padding_mode, &padding_color); + } + else + { + shell->fullscreen_options->padding_mode = padding_mode; + shell->fullscreen_options->padding_color = padding_color; + } + } + + /* if the user did not set the padding mode for this display explicitly */ + if (! shell->options->padding_mode_set) + { + padding_mode = display_config->default_view->padding_mode; + padding_color = display_config->default_view->padding_color; + + if (fullscreen) + { + shell->options->padding_mode = padding_mode; + shell->options->padding_color = padding_color; + } + else + { + gimp_display_shell_set_padding (shell, padding_mode, &padding_color); + } + } +} + +static void +gimp_display_shell_ants_speed_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + gimp_display_shell_selection_pause (shell); + gimp_display_shell_selection_resume (shell); +} + +static void +gimp_display_shell_quality_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + gimp_display_shell_expose_full (shell); +} + +static void +gimp_display_shell_color_config_notify_handler (GObject *config, + GParamSpec *param_spec, + GimpDisplayShell *shell) +{ + if (param_spec) + { + gboolean copy = TRUE; + + if (! strcmp (param_spec->name, "mode") || + ! strcmp (param_spec->name, "display-rendering-intent") || + ! strcmp (param_spec->name, "display-use-black-point-compensation") || + ! strcmp (param_spec->name, "printer-profile") || + ! strcmp (param_spec->name, "simulation-rendering-intent") || + ! strcmp (param_spec->name, "simulation-use-black-point-compensation") || + ! strcmp (param_spec->name, "simulation-gamut-check")) + { + if (shell->color_config_set) + copy = FALSE; + } + + if (copy) + { + GValue value = G_VALUE_INIT; + + g_value_init (&value, param_spec->value_type); + + g_object_get_property (config, + param_spec->name, &value); + g_object_set_property (G_OBJECT (shell->color_config), + param_spec->name, &value); + + g_value_unset (&value); + } + } + else + { + gimp_config_copy (GIMP_CONFIG (config), + GIMP_CONFIG (shell->color_config), + 0); + shell->color_config_set = FALSE; + } +} + +static void +gimp_display_shell_display_changed_handler (GimpContext *context, + GimpDisplay *display, + GimpDisplayShell *shell) +{ + if (shell->display == display) + gimp_display_shell_update_priority_rect (shell); +} diff --git a/app/display/gimpdisplayshell-handlers.h b/app/display/gimpdisplayshell-handlers.h new file mode 100644 index 0000000..49d743e --- /dev/null +++ b/app/display/gimpdisplayshell-handlers.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_HANDLERS_H__ +#define __GIMP_DISPLAY_SHELL_HANDLERS_H__ + + +void gimp_display_shell_connect (GimpDisplayShell *shell); +void gimp_display_shell_disconnect (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_HANDLERS_H__ */ diff --git a/app/display/gimpdisplayshell-icon.c b/app/display/gimpdisplayshell-icon.c new file mode 100644 index 0000000..6168820 --- /dev/null +++ b/app/display/gimpdisplayshell-icon.c @@ -0,0 +1,140 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-icon.h" + + +#define GIMP_DISPLAY_UPDATE_ICON_TIMEOUT 1000 + +static gboolean gimp_display_shell_icon_update_idle (gpointer data); + + +/* public functions */ + +void +gimp_display_shell_icon_update (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_icon_update_stop (shell); + + if (gimp_display_get_image (shell->display)) + shell->icon_idle_id = g_timeout_add_full (G_PRIORITY_LOW, + GIMP_DISPLAY_UPDATE_ICON_TIMEOUT, + gimp_display_shell_icon_update_idle, + shell, + NULL); + else + gimp_display_shell_icon_update_idle (shell); +} + +void +gimp_display_shell_icon_update_stop (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->icon_idle_id) + { + g_source_remove (shell->icon_idle_id); + shell->icon_idle_id = 0; + } +} + + +/* private functions */ + +static gboolean +gimp_display_shell_icon_update_idle (gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + GimpImage *image = gimp_display_get_image (shell->display); + GdkPixbuf *icon = NULL; + + shell->icon_idle_id = 0; + + if (image) + { + Gimp *gimp = gimp_display_get_gimp (shell->display); + GdkPixbuf *pixbuf; + gint width; + gint height; + gdouble factor = ((gdouble) gimp_image_get_height (image) / + (gdouble) gimp_image_get_width (image)); + + if (factor >= 1) + { + height = MAX (shell->icon_size, 1); + width = MAX (((gdouble) shell->icon_size) / factor, 1); + } + else + { + height = MAX (((gdouble) shell->icon_size) * factor, 1); + width = MAX (shell->icon_size, 1); + } + + pixbuf = gimp_viewable_get_pixbuf (GIMP_VIEWABLE (image), + gimp_get_user_context (gimp), + width, height); + + icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + shell->icon_size, shell->icon_size); + + memset (gdk_pixbuf_get_pixels (icon), 0, + gdk_pixbuf_get_height (icon) * + gdk_pixbuf_get_rowstride (icon)); + + gdk_pixbuf_copy_area (pixbuf, 0, 0, width, height, + icon, + 0, shell->icon_size - height); + + pixbuf = gimp_widget_load_icon (GTK_WIDGET (shell), "gimp-wilber-outline", + shell->icon_size_small); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + gdk_pixbuf_composite (pixbuf, icon, + shell->icon_size - width, 0, + width, height, + shell->icon_size - width, 0.0, 1.0, 1.0, + GDK_INTERP_NEAREST, 255); + g_object_unref (pixbuf); + } + + g_object_set (shell, "icon", icon, NULL); + + if (icon) + g_object_unref (icon); + + return FALSE; +} diff --git a/app/display/gimpdisplayshell-icon.h b/app/display/gimpdisplayshell-icon.h new file mode 100644 index 0000000..6d2a673 --- /dev/null +++ b/app/display/gimpdisplayshell-icon.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_ICON_H__ +#define __GIMP_DISPLAY_SHELL_ICON_H__ + + +void gimp_display_shell_icon_update (GimpDisplayShell *shell); +void gimp_display_shell_icon_update_stop (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_ICON_H__ */ diff --git a/app/display/gimpdisplayshell-items.c b/app/display/gimpdisplayshell-items.c new file mode 100644 index 0000000..caa381a --- /dev/null +++ b/app/display/gimpdisplayshell-items.c @@ -0,0 +1,284 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdisplayshell-items.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include + +#include "display-types.h" + +#include "gimpcanvascanvasboundary.h" +#include "gimpcanvascursor.h" +#include "gimpcanvasgrid.h" +#include "gimpcanvaslayerboundary.h" +#include "gimpcanvaspassepartout.h" +#include "gimpcanvasproxygroup.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-items.h" +#include "gimpdisplayshell-transform.h" + + +/* local function prototypes */ + +static void gimp_display_shell_item_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpDisplayShell *shell); +static void gimp_display_shell_unrotated_item_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpDisplayShell *shell); + + +/* public functions */ + +void +gimp_display_shell_items_init (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + shell->canvas_item = gimp_canvas_group_new (shell); + + shell->passe_partout = gimp_canvas_passe_partout_new (shell, 0, 0, 0, 0); + gimp_canvas_item_set_visible (shell->passe_partout, FALSE); + gimp_display_shell_add_item (shell, shell->passe_partout); + g_object_unref (shell->passe_partout); + + shell->preview_items = gimp_canvas_group_new (shell); + gimp_display_shell_add_item (shell, shell->preview_items); + g_object_unref (shell->preview_items); + + shell->vectors = gimp_canvas_proxy_group_new (shell); + gimp_display_shell_add_item (shell, shell->vectors); + g_object_unref (shell->vectors); + + shell->grid = gimp_canvas_grid_new (shell, NULL); + gimp_canvas_item_set_visible (shell->grid, FALSE); + g_object_set (shell->grid, "grid-style", TRUE, NULL); + gimp_display_shell_add_item (shell, shell->grid); + g_object_unref (shell->grid); + + shell->guides = gimp_canvas_proxy_group_new (shell); + gimp_display_shell_add_item (shell, shell->guides); + g_object_unref (shell->guides); + + shell->sample_points = gimp_canvas_proxy_group_new (shell); + gimp_display_shell_add_item (shell, shell->sample_points); + g_object_unref (shell->sample_points); + + shell->canvas_boundary = gimp_canvas_canvas_boundary_new (shell); + gimp_canvas_item_set_visible (shell->canvas_boundary, FALSE); + gimp_display_shell_add_item (shell, shell->canvas_boundary); + g_object_unref (shell->canvas_boundary); + + shell->layer_boundary = gimp_canvas_layer_boundary_new (shell); + gimp_canvas_item_set_visible (shell->layer_boundary, FALSE); + gimp_display_shell_add_item (shell, shell->layer_boundary); + g_object_unref (shell->layer_boundary); + + shell->tool_items = gimp_canvas_group_new (shell); + gimp_display_shell_add_item (shell, shell->tool_items); + g_object_unref (shell->tool_items); + + g_signal_connect (shell->canvas_item, "update", + G_CALLBACK (gimp_display_shell_item_update), + shell); + + shell->unrotated_item = gimp_canvas_group_new (shell); + + shell->cursor = gimp_canvas_cursor_new (shell); + gimp_canvas_item_set_visible (shell->cursor, FALSE); + gimp_display_shell_add_unrotated_item (shell, shell->cursor); + g_object_unref (shell->cursor); + + g_signal_connect (shell->unrotated_item, "update", + G_CALLBACK (gimp_display_shell_unrotated_item_update), + shell); +} + +void +gimp_display_shell_items_free (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->canvas_item) + { + g_signal_handlers_disconnect_by_func (shell->canvas_item, + gimp_display_shell_item_update, + shell); + + g_clear_object (&shell->canvas_item); + + shell->passe_partout = NULL; + shell->preview_items = NULL; + shell->vectors = NULL; + shell->grid = NULL; + shell->guides = NULL; + shell->sample_points = NULL; + shell->canvas_boundary = NULL; + shell->layer_boundary = NULL; + shell->tool_items = NULL; + } + + if (shell->unrotated_item) + { + g_signal_handlers_disconnect_by_func (shell->unrotated_item, + gimp_display_shell_unrotated_item_update, + shell); + + g_clear_object (&shell->unrotated_item); + + shell->cursor = NULL; + } +} + +void +gimp_display_shell_add_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (shell->canvas_item), item); +} + +void +gimp_display_shell_remove_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (shell->canvas_item), item); +} + +void +gimp_display_shell_add_preview_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (shell->preview_items), item); +} + +void +gimp_display_shell_remove_preview_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (shell->preview_items), item); +} + +void +gimp_display_shell_add_unrotated_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (shell->unrotated_item), item); +} + +void +gimp_display_shell_remove_unrotated_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (shell->unrotated_item), item); +} + +void +gimp_display_shell_add_tool_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (shell->tool_items), item); +} + +void +gimp_display_shell_remove_tool_item (GimpDisplayShell *shell, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (shell->tool_items), item); +} + + +/* private functions */ + +static void +gimp_display_shell_item_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpDisplayShell *shell) +{ + if (shell->rotate_transform) + { + gint n_rects; + gint i; + + n_rects = cairo_region_num_rectangles (region); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + gdouble tx1, ty1; + gdouble tx2, ty2; + gint x1, y1, x2, y2; + + cairo_region_get_rectangle (region, i, &rect); + + gimp_display_shell_rotate_bounds (shell, + rect.x, rect.y, + rect.x + rect.width, + rect.y + rect.height, + &tx1, &ty1, &tx2, &ty2); + + x1 = floor (tx1 - 0.5); + y1 = floor (ty1 - 0.5); + x2 = ceil (tx2 + 0.5); + y2 = ceil (ty2 + 0.5); + + gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1); + } + } + else + { + gimp_display_shell_expose_region (shell, region); + } +} + +static void +gimp_display_shell_unrotated_item_update (GimpCanvasItem *item, + cairo_region_t *region, + GimpDisplayShell *shell) +{ + gimp_display_shell_expose_region (shell, region); +} diff --git a/app/display/gimpdisplayshell-items.h b/app/display/gimpdisplayshell-items.h new file mode 100644 index 0000000..84f169e --- /dev/null +++ b/app/display/gimpdisplayshell-items.h @@ -0,0 +1,49 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdisplayshell-items.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DISPLAY_SHELL_ITEMS_H__ +#define __GIMP_DISPLAY_SHELL_ITEMS_H__ + + +void gimp_display_shell_items_init (GimpDisplayShell *shell); +void gimp_display_shell_items_free (GimpDisplayShell *shell); + +void gimp_display_shell_add_item (GimpDisplayShell *shell, + GimpCanvasItem *item); +void gimp_display_shell_remove_item (GimpDisplayShell *shell, + GimpCanvasItem *item); + +void gimp_display_shell_add_preview_item (GimpDisplayShell *shell, + GimpCanvasItem *item); +void gimp_display_shell_remove_preview_item (GimpDisplayShell *shell, + GimpCanvasItem *item); + +void gimp_display_shell_add_unrotated_item (GimpDisplayShell *shell, + GimpCanvasItem *item); +void gimp_display_shell_remove_unrotated_item (GimpDisplayShell *shell, + GimpCanvasItem *item); + +void gimp_display_shell_add_tool_item (GimpDisplayShell *shell, + GimpCanvasItem *item); +void gimp_display_shell_remove_tool_item (GimpDisplayShell *shell, + GimpCanvasItem *item); + + +#endif /* __GIMP_DISPLAY_SHELL_ITEMS_H__ */ diff --git a/app/display/gimpdisplayshell-layer-select.c b/app/display/gimpdisplayshell-layer-select.c new file mode 100644 index 0000000..0618eea --- /dev/null +++ b/app/display/gimpdisplayshell-layer-select.c @@ -0,0 +1,305 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpimage.h" +#include "core/gimplayer.h" + +#include "widgets/gimpview.h" +#include "widgets/gimpviewrenderer.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-layer-select.h" + +#include "gimp-intl.h" + + +typedef struct +{ + GtkWidget *window; + GtkWidget *view; + GtkWidget *label; + + GimpImage *image; + GimpLayer *orig_layer; +} LayerSelect; + + +/* local function prototypes */ + +static LayerSelect * layer_select_new (GimpDisplayShell *shell, + GimpImage *image, + GimpLayer *layer, + gint view_size); +static void layer_select_destroy (LayerSelect *layer_select, + guint32 time); +static void layer_select_advance (LayerSelect *layer_select, + gint move); +static gboolean layer_select_events (GtkWidget *widget, + GdkEvent *event, + LayerSelect *layer_select); + + +/* public functions */ + +void +gimp_display_shell_layer_select_init (GimpDisplayShell *shell, + gint move, + guint32 time) +{ + LayerSelect *layer_select; + GimpImage *image; + GimpLayer *layer; + GdkGrabStatus status; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + layer = gimp_image_get_active_layer (image); + + if (! layer) + return; + + layer_select = layer_select_new (shell, image, layer, + image->gimp->config->layer_preview_size); + layer_select_advance (layer_select, move); + + gtk_window_set_screen (GTK_WINDOW (layer_select->window), + gtk_widget_get_screen (GTK_WIDGET (shell))); + + gtk_widget_show (layer_select->window); + + status = gdk_keyboard_grab (gtk_widget_get_window (layer_select->window), FALSE, time); + if (status != GDK_GRAB_SUCCESS) + g_printerr ("gdk_keyboard_grab failed with status %d\n", status); +} + + +/* private functions */ + +static LayerSelect * +layer_select_new (GimpDisplayShell *shell, + GimpImage *image, + GimpLayer *layer, + gint view_size) +{ + LayerSelect *layer_select; + GtkWidget *frame1; + GtkWidget *frame2; + GtkWidget *hbox; + GtkWidget *alignment; + + layer_select = g_slice_new0 (LayerSelect); + + layer_select->image = image; + layer_select->orig_layer = layer; + + layer_select->window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_window_set_role (GTK_WINDOW (layer_select->window), "gimp-layer-select"); + gtk_window_set_title (GTK_WINDOW (layer_select->window), _("Layer Select")); + gtk_window_set_position (GTK_WINDOW (layer_select->window), GTK_WIN_POS_MOUSE); + gtk_widget_set_events (layer_select->window, + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK); + + g_signal_connect (layer_select->window, "event", + G_CALLBACK (layer_select_events), + layer_select); + + frame1 = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_OUT); + gtk_container_add (GTK_CONTAINER (layer_select->window), frame1); + gtk_widget_show (frame1); + + frame2 = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (frame1), frame2); + gtk_widget_show (frame2); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); + gtk_container_add (GTK_CONTAINER (frame2), hbox); + gtk_widget_show (hbox); + + /* The view */ + alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 0); + gtk_widget_show (alignment); + + layer_select->view = + gimp_view_new_by_types (gimp_get_user_context (image->gimp), + GIMP_TYPE_VIEW, + GIMP_TYPE_LAYER, + view_size, 1, FALSE); + gimp_view_renderer_set_color_config (GIMP_VIEW (layer_select->view)->renderer, + gimp_display_shell_get_color_config (shell)); + gimp_view_set_viewable (GIMP_VIEW (layer_select->view), + GIMP_VIEWABLE (layer)); + gtk_container_add (GTK_CONTAINER (alignment), layer_select->view); + gtk_widget_show (layer_select->view); + gtk_widget_show (alignment); + + /* the layer name label */ + layer_select->label = gtk_label_new (gimp_object_get_name (layer)); + gtk_box_pack_start (GTK_BOX (hbox), layer_select->label, FALSE, FALSE, 0); + gtk_widget_show (layer_select->label); + + return layer_select; +} + +static void +layer_select_destroy (LayerSelect *layer_select, + guint32 time) +{ + gdk_display_keyboard_ungrab (gtk_widget_get_display (layer_select->window), + time); + + gtk_widget_destroy (layer_select->window); + + if (layer_select->orig_layer != + gimp_image_get_active_layer (layer_select->image)) + { + gimp_image_flush (layer_select->image); + } + + g_slice_free (LayerSelect, layer_select); +} + +static void +layer_select_advance (LayerSelect *layer_select, + gint move) +{ + GimpLayer *active_layer; + GimpLayer *next_layer; + GList *layers; + gint n_layers; + gint index; + + if (move == 0) + return; + + /* If there is a floating selection, allow no advancement */ + if (gimp_image_get_floating_selection (layer_select->image)) + return; + + active_layer = gimp_image_get_active_layer (layer_select->image); + + layers = gimp_image_get_layer_list (layer_select->image); + n_layers = g_list_length (layers); + + index = g_list_index (layers, active_layer); + index += move; + + if (index < 0) + index = n_layers - 1; + else if (index >= n_layers) + index = 0; + + next_layer = g_list_nth_data (layers, index); + + g_list_free (layers); + + if (next_layer && next_layer != active_layer) + { + active_layer = gimp_image_set_active_layer (layer_select->image, + next_layer); + + if (active_layer) + { + gimp_view_set_viewable (GIMP_VIEW (layer_select->view), + GIMP_VIEWABLE (active_layer)); + gtk_label_set_text (GTK_LABEL (layer_select->label), + gimp_object_get_name (active_layer)); + } + } +} + +static gboolean +layer_select_events (GtkWidget *widget, + GdkEvent *event, + LayerSelect *layer_select) +{ + GdkEventKey *kevent; + GdkEventButton *bevent; + + switch (event->type) + { + case GDK_BUTTON_PRESS: + bevent = (GdkEventButton *) event; + + layer_select_destroy (layer_select, bevent->time); + break; + + case GDK_KEY_PRESS: + kevent = (GdkEventKey *) event; + + switch (kevent->keyval) + { + case GDK_KEY_Tab: + layer_select_advance (layer_select, 1); + break; + case GDK_KEY_ISO_Left_Tab: + layer_select_advance (layer_select, -1); + break; + } + return TRUE; + break; + + case GDK_KEY_RELEASE: + kevent = (GdkEventKey *) event; + + switch (kevent->keyval) + { + case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: + kevent->state &= ~GDK_MOD1_MASK; + break; + case GDK_KEY_Control_L: case GDK_KEY_Control_R: + kevent->state &= ~GDK_CONTROL_MASK; + break; + case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: + kevent->state &= ~GDK_SHIFT_MASK; + break; + } + + if (! (kevent->state & GDK_CONTROL_MASK)) + layer_select_destroy (layer_select, kevent->time); + + return TRUE; + break; + + default: + break; + } + + return FALSE; +} diff --git a/app/display/gimpdisplayshell-layer-select.h b/app/display/gimpdisplayshell-layer-select.h new file mode 100644 index 0000000..20546c6 --- /dev/null +++ b/app/display/gimpdisplayshell-layer-select.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_LAYER_SELECT_H__ +#define __GIMP_DISPLAY_SHELL_LAYER_SELECT_H__ + + +void gimp_display_shell_layer_select_init (GimpDisplayShell *shell, + gint move, + guint32 time); + + +#endif /* __GIMP_DISPLAY_SHELL_LAYER_SELECT_H__ */ diff --git a/app/display/gimpdisplayshell-profile.c b/app/display/gimpdisplayshell-profile.c new file mode 100644 index 0000000..71a6c94 --- /dev/null +++ b/app/display/gimpdisplayshell-profile.c @@ -0,0 +1,336 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpcoreconfig.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimpimage.h" +#include "core/gimpprojectable.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-actions.h" +#include "gimpdisplayshell-filter.h" +#include "gimpdisplayshell-profile.h" +#include "gimpdisplayxfer.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_display_shell_profile_free (GimpDisplayShell *shell); + +static void gimp_display_shell_color_config_notify (GimpColorConfig *config, + const GParamSpec *pspec, + GimpDisplayShell *shell); + + +/* public functions */ + +void +gimp_display_shell_profile_init (GimpDisplayShell *shell) +{ + GimpColorConfig *color_config; + + color_config = GIMP_CORE_CONFIG (shell->display->config)->color_management; + + shell->color_config = gimp_config_duplicate (GIMP_CONFIG (color_config)); + + /* use after so we are called after the profile cache is invalidated + * in gimp_widget_get_color_transform() + */ + g_signal_connect_after (shell->color_config, "notify", + G_CALLBACK (gimp_display_shell_color_config_notify), + shell); +} + +void +gimp_display_shell_profile_finalize (GimpDisplayShell *shell) +{ + g_clear_object (&shell->color_config); + + gimp_display_shell_profile_free (shell); +} + +void +gimp_display_shell_profile_update (GimpDisplayShell *shell) +{ + GimpImage *image; + GimpColorProfile *src_profile; + const Babl *src_format; + GimpColorProfile *filter_profile; + const Babl *filter_format; + const Babl *dest_format; + + gimp_display_shell_profile_free (shell); + + image = gimp_display_get_image (shell->display); + + if (! image) + return; + + src_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (shell)); + + if (! src_profile) + return; + + src_format = gimp_projectable_get_format (GIMP_PROJECTABLE (image)); + + if (gimp_display_shell_has_filter (shell)) + { + filter_format = shell->filter_format; + filter_profile = gimp_babl_format_get_color_profile (filter_format); + } + else + { + filter_format = src_format; + filter_profile = src_profile; + } + + if (! gimp_display_shell_profile_can_convert_to_u8 (shell)) + { + dest_format = shell->filter_format; + } + else + { + dest_format = babl_format ("R'G'B'A u8"); + } + +#if 0 + g_printerr ("src_profile: %s\n" + "src_format: %s\n" + "filter_profile: %s\n" + "filter_format: %s\n" + "dest_format: %s\n", + gimp_color_profile_get_label (src_profile), + babl_get_name (src_format), + gimp_color_profile_get_label (filter_profile), + babl_get_name (filter_format), + babl_get_name (dest_format)); +#endif + + if (! gimp_color_transform_can_gegl_copy (src_profile, filter_profile)) + { + shell->filter_transform = + gimp_color_transform_new (src_profile, + src_format, + filter_profile, + filter_format, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION | + GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE); + } + + shell->profile_transform = + gimp_widget_get_color_transform (gtk_widget_get_toplevel (GTK_WIDGET (shell)), + gimp_display_shell_get_color_config (shell), + filter_profile, + filter_format, + dest_format); + + if (shell->filter_transform || shell->profile_transform) + { + gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH; + gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + + shell->profile_data = + gegl_malloc (w * h * babl_format_get_bytes_per_pixel (src_format)); + + shell->profile_stride = + w * babl_format_get_bytes_per_pixel (src_format); + + shell->profile_buffer = + gegl_buffer_linear_new_from_data (shell->profile_data, + src_format, + GEGL_RECTANGLE (0, 0, w, h), + GEGL_AUTO_ROWSTRIDE, + (GDestroyNotify) gegl_free, + shell->profile_data); + } +} + +gboolean +gimp_display_shell_profile_can_convert_to_u8 (GimpDisplayShell *shell) +{ + GimpImage *image = gimp_display_get_image (shell->display); + + if (image) + { + GimpComponentType component_type; + + if (! gimp_display_shell_has_filter (shell)) + component_type = gimp_image_get_component_type (image); + else + component_type = gimp_babl_format_get_component_type (shell->filter_format); + + switch (component_type) + { + case GIMP_COMPONENT_TYPE_U8: +#if 0 + /* would like to convert directly for these too, but it + * produces inferior results, see bug 750874 + */ + case GIMP_COMPONENT_TYPE_U16: + case GIMP_COMPONENT_TYPE_U32: +#endif + return TRUE; + + default: + break; + } + } + + return FALSE; +} + + +/* private functions */ + +static void +gimp_display_shell_profile_free (GimpDisplayShell *shell) +{ + g_clear_object (&shell->profile_transform); + g_clear_object (&shell->filter_transform); + g_clear_object (&shell->profile_buffer); + shell->profile_data = NULL; + shell->profile_stride = 0; +} + +static void +gimp_display_shell_color_config_notify (GimpColorConfig *config, + const GParamSpec *pspec, + GimpDisplayShell *shell) +{ + if (! strcmp (pspec->name, "mode") || + ! strcmp (pspec->name, "display-rendering-intent") || + ! strcmp (pspec->name, "display-use-black-point-compensation") || + ! strcmp (pspec->name, "simulation-rendering-intent") || + ! strcmp (pspec->name, "simulation-use-black-point-compensation") || + ! strcmp (pspec->name, "simulation-gamut-check")) + { + gboolean managed = FALSE; + gboolean softproof = FALSE; + const gchar *action = NULL; + +#define SET_SENSITIVE(action, sensitive) \ + gimp_display_shell_set_action_sensitive (shell, action, sensitive); + +#define SET_ACTIVE(action, active) \ + gimp_display_shell_set_action_active (shell, action, active); + + switch (gimp_color_config_get_mode (config)) + { + case GIMP_COLOR_MANAGEMENT_OFF: + break; + + case GIMP_COLOR_MANAGEMENT_DISPLAY: + managed = TRUE; + break; + + case GIMP_COLOR_MANAGEMENT_SOFTPROOF: + managed = TRUE; + softproof = TRUE; + break; + } + + SET_ACTIVE ("view-color-management-enable", managed); + SET_ACTIVE ("view-color-management-softproof", softproof); + + switch (gimp_color_config_get_display_intent (config)) + { + case GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL: + action = "view-display-intent-perceptual"; + break; + + case GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC: + action = "view-display-intent-relative-colorimetric"; + break; + + case GIMP_COLOR_RENDERING_INTENT_SATURATION: + action = "view-display-intent-saturation"; + break; + + case GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: + action = "view-display-intent-absolute-colorimetric"; + break; + } + + SET_SENSITIVE ("view-display-intent-perceptual", managed); + SET_SENSITIVE ("view-display-intent-relative-colorimetric", managed); + SET_SENSITIVE ("view-display-intent-saturation", managed); + SET_SENSITIVE ("view-display-intent-absolute-colorimetric", managed); + + SET_ACTIVE (action, TRUE); + + SET_SENSITIVE ("view-display-black-point-compensation", managed); + SET_ACTIVE ("view-display-black-point-compensation", + gimp_color_config_get_display_bpc (config)); + + SET_SENSITIVE ("view-softproof-profile", softproof); + + switch (gimp_color_config_get_simulation_intent (config)) + { + case GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL: + action = "view-softproof-intent-perceptual"; + break; + + case GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC: + action = "view-softproof-intent-relative-colorimetric"; + break; + + case GIMP_COLOR_RENDERING_INTENT_SATURATION: + action = "view-softproof-intent-saturation"; + break; + + case GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: + action = "view-softproof-intent-absolute-colorimetric"; + break; + } + + SET_SENSITIVE ("view-softproof-intent-perceptual", softproof); + SET_SENSITIVE ("view-softproof-intent-relative-colorimetric", softproof); + SET_SENSITIVE ("view-softproof-intent-saturation", softproof); + SET_SENSITIVE ("view-softproof-intent-absolute-colorimetric", softproof); + + SET_ACTIVE (action, TRUE); + + SET_SENSITIVE ("view-softproof-black-point-compensation", softproof); + SET_ACTIVE ("view-softproof-black-point-compensation", + gimp_color_config_get_simulation_bpc (config)); + + SET_SENSITIVE ("view-softproof-gamut-check", softproof); + SET_ACTIVE ("view-softproof-gamut-check", + gimp_color_config_get_simulation_gamut_check (config)); + } + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (shell)); +} diff --git a/app/display/gimpdisplayshell-profile.h b/app/display/gimpdisplayshell-profile.h new file mode 100644 index 0000000..e390cac --- /dev/null +++ b/app/display/gimpdisplayshell-profile.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_PROFILE_H__ +#define __GIMP_DISPLAY_SHELL_PROFILE_H__ + + +void gimp_display_shell_profile_init (GimpDisplayShell *shell); +void gimp_display_shell_profile_finalize (GimpDisplayShell *shell); + +void gimp_display_shell_profile_update (GimpDisplayShell *shell); + +gboolean gimp_display_shell_profile_can_convert_to_u8 (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_PROFILE_H__ */ diff --git a/app/display/gimpdisplayshell-progress.c b/app/display/gimpdisplayshell-progress.c new file mode 100644 index 0000000..4314a84 --- /dev/null +++ b/app/display/gimpdisplayshell-progress.c @@ -0,0 +1,163 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimpprogress.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-progress.h" +#include "gimpstatusbar.h" + + +static GimpProgress * +gimp_display_shell_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + return gimp_progress_start (GIMP_PROGRESS (statusbar), cancellable, + "%s", message); +} + +static void +gimp_display_shell_progress_end (GimpProgress *progress) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_progress_end (GIMP_PROGRESS (statusbar)); +} + +static gboolean +gimp_display_shell_progress_is_active (GimpProgress *progress) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + return gimp_progress_is_active (GIMP_PROGRESS (statusbar)); +} + +static void +gimp_display_shell_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_progress_set_text_literal (GIMP_PROGRESS (statusbar), message); +} + +static void +gimp_display_shell_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_progress_set_value (GIMP_PROGRESS (statusbar), percentage); +} + +static gdouble +gimp_display_shell_progress_get_value (GimpProgress *progress) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + return gimp_progress_get_value (GIMP_PROGRESS (statusbar)); +} + +static void +gimp_display_shell_progress_pulse (GimpProgress *progress) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_progress_pulse (GIMP_PROGRESS (statusbar)); +} + +static guint32 +gimp_display_shell_progress_get_window_id (GimpProgress *progress) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress)); + + if (GTK_IS_WINDOW (toplevel)) + return gimp_window_get_native_id (GTK_WINDOW (toplevel)); + + return 0; +} + +static gboolean +gimp_display_shell_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + switch (severity) + { + case GIMP_MESSAGE_ERROR: + case GIMP_MESSAGE_BUG_WARNING: + case GIMP_MESSAGE_BUG_CRITICAL: + /* error messages are never handled here */ + break; + + case GIMP_MESSAGE_WARNING: + /* warning messages go to the statusbar, if it's visible */ + if (! gimp_statusbar_get_visible (statusbar)) + break; + else + return gimp_progress_message (GIMP_PROGRESS (statusbar), gimp, + severity, domain, message); + + case GIMP_MESSAGE_INFO: + /* info messages go to the statusbar; + * if they are not handled there, they are swallowed + */ + gimp_progress_message (GIMP_PROGRESS (statusbar), gimp, + severity, domain, message); + return TRUE; + } + + return FALSE; +} + +void +gimp_display_shell_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_display_shell_progress_start; + iface->end = gimp_display_shell_progress_end; + iface->is_active = gimp_display_shell_progress_is_active; + iface->set_text = gimp_display_shell_progress_set_text; + iface->set_value = gimp_display_shell_progress_set_value; + iface->get_value = gimp_display_shell_progress_get_value; + iface->pulse = gimp_display_shell_progress_pulse; + iface->get_window_id = gimp_display_shell_progress_get_window_id; + iface->message = gimp_display_shell_progress_message; +} diff --git a/app/display/gimpdisplayshell-progress.h b/app/display/gimpdisplayshell-progress.h new file mode 100644 index 0000000..d697fa1 --- /dev/null +++ b/app/display/gimpdisplayshell-progress.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_PROGRESS_H__ +#define __GIMP_DISPLAY_SHELL_PROGRESS_H__ + + +#include "core/gimpprogress.h" + + +void gimp_display_shell_progress_iface_init (GimpProgressInterface *iface); + + +#endif /* __GIMP_DISPLAY_SHELL_PROGRESS_H__ */ diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c new file mode 100644 index 0000000..48518ac --- /dev/null +++ b/app/display/gimpdisplayshell-render.c @@ -0,0 +1,360 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimpprojectable.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayshell-filter.h" +#include "gimpdisplayshell-profile.h" +#include "gimpdisplayshell-render.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayxfer.h" + + +void +gimp_display_shell_render (GimpDisplayShell *shell, + cairo_t *cr, + gint x, + gint y, + gint w, + gint h, + gdouble scale) +{ + GimpImage *image; + GeglBuffer *buffer; +#ifdef USE_NODE_BLIT + GeglNode *node; +#endif + GeglAbyssPolicy abyss_policy; + cairo_surface_t *xfer; + gint xfer_src_x; + gint xfer_src_y; + gint mask_src_x = 0; + gint mask_src_y = 0; + gint cairo_stride; + guchar *cairo_data; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (cr != NULL); + g_return_if_fail (w > 0 && w <= GIMP_DISPLAY_RENDER_BUF_WIDTH); + g_return_if_fail (h > 0 && h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT); + g_return_if_fail (scale > 0.0); + + image = gimp_display_get_image (shell->display); + + /* While converting, the render can be wrong; but worse, we rely on allocated + * data which might be the wrong size and this was a crash we had which was + * hard to diagnose as it doesn't always crash immediately (see discussions in + * #9136). This is why this assert is important. We want to make sure we never + * call this when the shell's image is in the inconsistent "converting" state. + */ + g_return_if_fail (! gimp_image_get_converting (image)); + + buffer = gimp_pickable_get_buffer ( + gimp_display_shell_get_pickable (shell)); +#ifdef USE_NODE_BLIT + node = gimp_projectable_get_graph (GIMP_PROJECTABLE (image)); + + gimp_projectable_begin_render (GIMP_PROJECTABLE (image)); +#endif + + if (shell->show_all) + abyss_policy = GEGL_ABYSS_NONE; + else + abyss_policy = GEGL_ABYSS_CLAMP; + + xfer = gimp_display_xfer_get_surface (shell->xfer, w, h, + &xfer_src_x, &xfer_src_y); + + cairo_stride = cairo_image_surface_get_stride (xfer); + cairo_data = cairo_image_surface_get_data (xfer) + + xfer_src_y * cairo_stride + xfer_src_x * 4; + + if (shell->profile_transform || + gimp_display_shell_has_filter (shell)) + { + gboolean can_convert_to_u8; + + /* if there is a profile transform or a display filter, we need + * to use temp buffers + */ + + can_convert_to_u8 = gimp_display_shell_profile_can_convert_to_u8 (shell); + + /* create the filter buffer if we have filters, or can't convert + * to u8 directly + */ + if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) && + ! shell->filter_buffer) + { + gint fw = GIMP_DISPLAY_RENDER_BUF_WIDTH; + gint fh = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + + shell->filter_data = + gegl_malloc (fw * fh * + babl_format_get_bytes_per_pixel (shell->filter_format)); + + shell->filter_stride = + fw * babl_format_get_bytes_per_pixel (shell->filter_format); + + shell->filter_buffer = + gegl_buffer_linear_new_from_data (shell->filter_data, + shell->filter_format, + GEGL_RECTANGLE (0, 0, fw, fh), + GEGL_AUTO_ROWSTRIDE, + (GDestroyNotify) gegl_free, + shell->filter_data); + } + + if (! gimp_display_shell_has_filter (shell) || shell->filter_transform) + { + /* if there are no filters, or there is a filter transform, + * load the projection pixels into the profile_buffer + */ +#ifndef USE_NODE_BLIT + gegl_buffer_get (buffer, + GEGL_RECTANGLE (x, y, w, h), scale, + gimp_projectable_get_format (GIMP_PROJECTABLE (image)), + shell->profile_data, shell->profile_stride, + abyss_policy); +#else + gegl_node_blit (node, + scale, GEGL_RECTANGLE (x, y, w, h), + gimp_projectable_get_format (GIMP_PROJECTABLE (image)), + shell->profile_data, shell->profile_stride, + GEGL_BLIT_CACHE); +#endif + } + else + { + /* otherwise, load the pixels directly into the filter_buffer + */ +#ifndef USE_NODE_BLIT + gegl_buffer_get (buffer, + GEGL_RECTANGLE (x, y, w, h), scale, + shell->filter_format, + shell->filter_data, shell->filter_stride, + abyss_policy); +#else + gegl_node_blit (node, + scale, GEGL_RECTANGLE (x, y, w, h), + shell->filter_format, + shell->filter_data, shell->filter_stride, + GEGL_BLIT_CACHE); +#endif + } + + /* if there is a filter transform, convert the pixels from + * the profile_buffer to the filter_buffer + */ + if (shell->filter_transform) + { + gimp_color_transform_process_buffer (shell->filter_transform, + shell->profile_buffer, + GEGL_RECTANGLE (0, 0, w, h), + shell->filter_buffer, + GEGL_RECTANGLE (0, 0, w, h)); + } + + /* if there are filters, apply them + */ + if (gimp_display_shell_has_filter (shell)) + { + GeglBuffer *filter_buffer; + + /* shift the filter_buffer so that the area passed to + * the filters is the real render area, allowing for + * position-dependent filters + */ + filter_buffer = g_object_new (GEGL_TYPE_BUFFER, + "source", shell->filter_buffer, + "shift-x", -x, + "shift-y", -y, + NULL); + + /* convert the filter_buffer in place + */ + gimp_color_display_stack_convert_buffer (shell->filter_stack, + filter_buffer, + GEGL_RECTANGLE (x, y, w, h)); + + g_object_unref (filter_buffer); + } + + /* if there is a profile transform... + */ + if (shell->profile_transform) + { + if (gimp_display_shell_has_filter (shell)) + { + /* if we have filters, convert the pixels in the filter_buffer + * in-place + */ + gimp_color_transform_process_buffer (shell->profile_transform, + shell->filter_buffer, + GEGL_RECTANGLE (0, 0, w, h), + shell->filter_buffer, + GEGL_RECTANGLE (0, 0, w, h)); + } + else if (! can_convert_to_u8) + { + /* otherwise, if we can't convert to u8 directly, convert + * the pixels from the profile_buffer to the filter_buffer + */ + gimp_color_transform_process_buffer (shell->profile_transform, + shell->profile_buffer, + GEGL_RECTANGLE (0, 0, w, h), + shell->filter_buffer, + GEGL_RECTANGLE (0, 0, w, h)); + } + else + { + GeglBuffer *buffer = + gegl_buffer_linear_new_from_data (cairo_data, + babl_format ("cairo-ARGB32"), + GEGL_RECTANGLE (0, 0, w, h), + cairo_stride, + NULL, NULL); + + /* otherwise, convert the profile_buffer directly into + * the cairo_buffer + */ + gimp_color_transform_process_buffer (shell->profile_transform, + shell->profile_buffer, + GEGL_RECTANGLE (0, 0, w, h), + buffer, + GEGL_RECTANGLE (0, 0, w, h)); + g_object_unref (buffer); + } + } + + /* finally, copy the filter buffer to the cairo-ARGB32 buffer, + * if necessary + */ + if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) + { + gegl_buffer_get (shell->filter_buffer, + GEGL_RECTANGLE (0, 0, w, h), 1.0, + babl_format ("cairo-ARGB32"), + cairo_data, cairo_stride, + GEGL_ABYSS_NONE); + } + } + else + { + /* otherwise we can copy the projection pixels straight to the + * cairo-ARGB32 buffer + */ +#ifndef USE_NODE_BLIT + gegl_buffer_get (buffer, + GEGL_RECTANGLE (x, y, w, h), scale, + babl_format ("cairo-ARGB32"), + cairo_data, cairo_stride, + abyss_policy); +#else + gegl_node_blit (node, + scale, GEGL_RECTANGLE (x, y, w, h), + babl_format ("cairo-ARGB32"), + cairo_data, cairo_stride, + GEGL_BLIT_CACHE); +#endif + } + +#ifdef USE_NODE_BLIT + gimp_projectable_end_render (GIMP_PROJECTABLE (image)); +#endif + + if (shell->mask) + { + if (! shell->mask_surface) + { + shell->mask_surface = + cairo_image_surface_create (CAIRO_FORMAT_A8, + GIMP_DISPLAY_RENDER_BUF_WIDTH, + GIMP_DISPLAY_RENDER_BUF_HEIGHT); + } + + cairo_surface_mark_dirty (shell->mask_surface); + + cairo_stride = cairo_image_surface_get_stride (shell->mask_surface); + cairo_data = cairo_image_surface_get_data (shell->mask_surface) + + mask_src_y * cairo_stride + mask_src_x; + + gegl_buffer_get (shell->mask, + GEGL_RECTANGLE (x - floor (shell->mask_offset_x * scale), + y - floor (shell->mask_offset_y * scale), + w, h), + scale, + babl_format ("Y u8"), + cairo_data, cairo_stride, + GEGL_ABYSS_NONE); + + if (shell->mask_inverted) + { + gint mask_height = h; + + while (mask_height--) + { + gint mask_width = w; + guchar *d = cairo_data; + + while (mask_width--) + { + guchar inv = 255 - *d; + + *d++ = inv; + } + + cairo_data += cairo_stride; + } + } + } + + /* put it to the screen */ + cairo_set_source_surface (cr, xfer, + x - xfer_src_x, + y - xfer_src_y); + cairo_paint (cr); + + if (shell->mask) + { + gimp_cairo_set_source_rgba (cr, &shell->mask_color); + cairo_mask_surface (cr, shell->mask_surface, + x - mask_src_x, + y - mask_src_y); + } +} diff --git a/app/display/gimpdisplayshell-render.h b/app/display/gimpdisplayshell-render.h new file mode 100644 index 0000000..7b4a644 --- /dev/null +++ b/app/display/gimpdisplayshell-render.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_RENDER_H__ +#define __GIMP_DISPLAY_SHELL_RENDER_H__ + +void gimp_display_shell_render (GimpDisplayShell *shell, + cairo_t *cr, + gint x, + gint y, + gint w, + gint h, + gdouble scale); + +#endif /* __GIMP_DISPLAY_SHELL_RENDER_H__ */ diff --git a/app/display/gimpdisplayshell-rotate-dialog.c b/app/display/gimpdisplayshell-rotate-dialog.c new file mode 100644 index 0000000..c899235 --- /dev/null +++ b/app/display/gimpdisplayshell-rotate-dialog.c @@ -0,0 +1,293 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpviewable.h" + +#include "widgets/gimpdial.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-rotate-dialog.h" + +#include "gimp-intl.h" + + +#define RESPONSE_RESET 1 + + +typedef struct +{ + GimpDisplayShell *shell; + GtkAdjustment *rotate_adj; + gdouble old_angle; +} RotateDialogData; + + +/* local function prototypes */ + +static void gimp_display_shell_rotate_dialog_response (GtkWidget *widget, + gint response_id, + RotateDialogData *dialog); +static void gimp_display_shell_rotate_dialog_free (RotateDialogData *dialog); + +static void rotate_adjustment_changed (GtkAdjustment *adj, + RotateDialogData *dialog); +static void display_shell_rotated (GimpDisplayShell *shell, + RotateDialogData *dialog); + +static gboolean deg_to_rad (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data); +static gboolean rad_to_deg (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data); + + +/* public functions */ + +/** + * gimp_display_shell_rotate_dialog: + * @shell: the #GimpDisplayShell + * + * Constructs and displays a dialog allowing the user to enter a + * custom display rotate. + **/ +void +gimp_display_shell_rotate_dialog (GimpDisplayShell *shell) +{ + RotateDialogData *data; + GimpImage *image; + GtkWidget *toplevel; + GtkWidget *hbox; + GtkWidget *spin; + GtkWidget *dial; + GtkWidget *label; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->rotate_dialog) + { + gtk_window_present (GTK_WINDOW (shell->rotate_dialog)); + return; + } + + image = gimp_display_get_image (shell->display); + + data = g_slice_new (RotateDialogData); + + data->shell = shell; + data->old_angle = shell->rotate_angle; + + shell->rotate_dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), + gimp_get_user_context (shell->display->gimp), + _("Rotate View"), "display-rotate", + GIMP_ICON_OBJECT_ROTATE_180, + _("Select Rotation Angle"), + GTK_WIDGET (shell), + gimp_standard_help_func, + GIMP_HELP_VIEW_ROTATE_OTHER, + + _("_Reset"), RESPONSE_RESET, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (shell->rotate_dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_object_weak_ref (G_OBJECT (shell->rotate_dialog), + (GWeakNotify) gimp_display_shell_rotate_dialog_free, data); + + g_object_add_weak_pointer (G_OBJECT (shell->rotate_dialog), + (gpointer) &shell->rotate_dialog); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + gtk_window_set_transient_for (GTK_WINDOW (shell->rotate_dialog), + GTK_WINDOW (toplevel)); + gtk_window_set_destroy_with_parent (GTK_WINDOW (shell->rotate_dialog), TRUE); + + g_signal_connect (shell->rotate_dialog, "response", + G_CALLBACK (gimp_display_shell_rotate_dialog_response), + data); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell->rotate_dialog))), + hbox, TRUE, TRUE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new (_("Angle:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + data->rotate_adj = (GtkAdjustment *) + gtk_adjustment_new (shell->rotate_angle, 0.0, 360.0, 1, 15, 0); + spin = gimp_spin_button_new (data->rotate_adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE); + gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spin), TRUE); + gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0); + gtk_widget_show (spin); + + label = gtk_label_new (_("degrees")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + dial = gimp_dial_new (); + g_object_set (dial, + "size", 32, + "background", GIMP_CIRCLE_BACKGROUND_PLAIN, + "draw-beta", FALSE, + NULL); + gtk_box_pack_start (GTK_BOX (hbox), dial, FALSE, FALSE, 0); + gtk_widget_show (dial); + + g_object_bind_property_full (data->rotate_adj, "value", + dial, "alpha", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + deg_to_rad, + rad_to_deg, + NULL, NULL); + + g_signal_connect (data->rotate_adj, "value-changed", + G_CALLBACK (rotate_adjustment_changed), + data); + g_signal_connect (shell, "rotated", + G_CALLBACK (display_shell_rotated), + data); + + gtk_widget_show (shell->rotate_dialog); +} + +static void +gimp_display_shell_rotate_dialog_response (GtkWidget *widget, + gint response_id, + RotateDialogData *dialog) +{ + switch (response_id) + { + case RESPONSE_RESET: + gtk_adjustment_set_value (dialog->rotate_adj, 0.0); + break; + + case GTK_RESPONSE_CANCEL: + gtk_adjustment_set_value (dialog->rotate_adj, dialog->old_angle); + /* fall thru */ + + default: + gtk_widget_destroy (dialog->shell->rotate_dialog); + break; + } +} + +static void +gimp_display_shell_rotate_dialog_free (RotateDialogData *dialog) +{ + g_signal_handlers_disconnect_by_func (dialog->shell, + display_shell_rotated, + dialog); + + g_slice_free (RotateDialogData, dialog); +} + +static void +rotate_adjustment_changed (GtkAdjustment *adj, + RotateDialogData *dialog) +{ + gdouble angle = gtk_adjustment_get_value (dialog->rotate_adj); + + g_signal_handlers_block_by_func (dialog->shell, + display_shell_rotated, + dialog); + + gimp_display_shell_rotate_to (dialog->shell, angle); + + g_signal_handlers_unblock_by_func (dialog->shell, + display_shell_rotated, + dialog); +} + +static void +display_shell_rotated (GimpDisplayShell *shell, + RotateDialogData *dialog) +{ + g_signal_handlers_block_by_func (dialog->rotate_adj, + rotate_adjustment_changed, + dialog); + + gtk_adjustment_set_value (dialog->rotate_adj, shell->rotate_angle); + + g_signal_handlers_unblock_by_func (dialog->rotate_adj, + rotate_adjustment_changed, + dialog); +} + +static gboolean +deg_to_rad (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + gdouble value = g_value_get_double (from_value); + + value = 360.0 - value; + + value *= G_PI / 180.0; + + g_value_set_double (to_value, value); + + return TRUE; +} + +static gboolean +rad_to_deg (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + gdouble value = g_value_get_double (from_value); + + value *= 180.0 / G_PI; + + value = 360.0 - value; + + g_value_set_double (to_value, value); + + return TRUE; +} diff --git a/app/display/gimpdisplayshell-rotate-dialog.h b/app/display/gimpdisplayshell-rotate-dialog.h new file mode 100644 index 0000000..5222d13 --- /dev/null +++ b/app/display/gimpdisplayshell-rotate-dialog.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_ROTATE_DIALOG_H__ +#define __GIMP_DISPLAY_SHELL_ROTATE_DIALOG_H__ + + +void gimp_display_shell_rotate_dialog (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_ROTATE_DIALOG_H__ */ diff --git a/app/display/gimpdisplayshell-rotate.c b/app/display/gimpdisplayshell-rotate.c new file mode 100644 index 0000000..d14f2a5 --- /dev/null +++ b/app/display/gimpdisplayshell-rotate.c @@ -0,0 +1,251 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-transform.h" + + +#define ANGLE_EPSILON 1e-3 + + +/* local function prototypes */ + +static void gimp_display_shell_save_viewport_center (GimpDisplayShell *shell, + gdouble *x, + gdouble *y); +static void gimp_display_shell_restore_viewport_center (GimpDisplayShell *shell, + gdouble x, + gdouble y); + + +/* public functions */ + +void +gimp_display_shell_flip (GimpDisplayShell *shell, + gboolean flip_horizontally, + gboolean flip_vertically) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + flip_horizontally = flip_horizontally ? TRUE : FALSE; + flip_vertically = flip_vertically ? TRUE : FALSE; + + if (flip_horizontally != shell->flip_horizontally || + flip_vertically != shell->flip_vertically) + { + gdouble cx, cy; + + /* Maintain the current center of the viewport. */ + gimp_display_shell_save_viewport_center (shell, &cx, &cy); + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + /* Adjust the rotation angle so that the image gets reflected across the + * horizontal, and/or vertical, axes in screen space, regardless of the + * current rotation. + */ + if (flip_horizontally == shell->flip_horizontally || + flip_vertically == shell->flip_vertically) + { + if (shell->rotate_angle != 0.0) + shell->rotate_angle = 360.0 - shell->rotate_angle; + } + + shell->flip_horizontally = flip_horizontally; + shell->flip_vertically = flip_vertically; + + gimp_display_shell_rotated (shell); + + gimp_display_shell_restore_viewport_center (shell, cx, cy); + + gimp_display_shell_expose_full (shell); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); + } +} + +void +gimp_display_shell_rotate (GimpDisplayShell *shell, + gdouble delta) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_rotate_to (shell, shell->rotate_angle + delta); +} + +void +gimp_display_shell_rotate_to (GimpDisplayShell *shell, + gdouble value) +{ + gdouble cx, cy; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + /* Maintain the current center of the viewport. */ + gimp_display_shell_save_viewport_center (shell, &cx, &cy); + + /* Make sure the angle is within the range [0, 360). */ + value = fmod (value, 360.0); + if (value < 0.0) + value += 360.0; + + shell->rotate_angle = value; + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + gimp_display_shell_scroll_clamp_and_update (shell); + + gimp_display_shell_rotated (shell); + + gimp_display_shell_restore_viewport_center (shell, cx, cy); + + gimp_display_shell_expose_full (shell); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +void +gimp_display_shell_rotate_drag (GimpDisplayShell *shell, + gdouble last_x, + gdouble last_y, + gdouble cur_x, + gdouble cur_y, + gboolean constrain) +{ + gdouble pivot_x, pivot_y; + gdouble src_x, src_y, src_angle; + gdouble dest_x, dest_y, dest_angle; + gdouble delta_angle; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + /* Rotate the image around the center of the viewport. */ + pivot_x = shell->disp_width / 2.0; + pivot_y = shell->disp_height / 2.0; + + src_x = last_x - pivot_x; + src_y = last_y - pivot_y; + src_angle = atan2 (src_y, src_x); + + dest_x = cur_x - pivot_x; + dest_y = cur_y - pivot_y; + dest_angle = atan2 (dest_y, dest_x); + + delta_angle = dest_angle - src_angle; + + shell->rotate_drag_angle += 180.0 * delta_angle / G_PI; + + gimp_display_shell_rotate_to (shell, + constrain ? + RINT (shell->rotate_drag_angle / 15.0) * 15.0 : + shell->rotate_drag_angle); +} + +void +gimp_display_shell_rotate_update_transform (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + g_clear_pointer (&shell->rotate_transform, g_free); + g_clear_pointer (&shell->rotate_untransform, g_free); + + if (fabs (shell->rotate_angle) < ANGLE_EPSILON || + fabs (360.0 - shell->rotate_angle) < ANGLE_EPSILON) + shell->rotate_angle = 0.0; + + if ((shell->rotate_angle != 0.0 || + shell->flip_horizontally || + shell->flip_vertically) && + gimp_display_get_image (shell->display)) + { + gint image_width, image_height; + gdouble cx, cy; + + shell->rotate_transform = g_new (cairo_matrix_t, 1); + shell->rotate_untransform = g_new (cairo_matrix_t, 1); + + gimp_display_shell_scale_get_image_size (shell, + &image_width, &image_height); + + cx = -shell->offset_x + image_width / 2; + cy = -shell->offset_y + image_height / 2; + + cairo_matrix_init_translate (shell->rotate_transform, cx, cy); + + if (shell->rotate_angle != 0.0) + cairo_matrix_rotate (shell->rotate_transform, + shell->rotate_angle / 180.0 * G_PI); + + if (shell->flip_horizontally) + cairo_matrix_scale (shell->rotate_transform, -1.0, 1.0); + + if (shell->flip_vertically) + cairo_matrix_scale (shell->rotate_transform, 1.0, -1.0); + + cairo_matrix_translate (shell->rotate_transform, -cx, -cy); + + *shell->rotate_untransform = *shell->rotate_transform; + cairo_matrix_invert (shell->rotate_untransform); + } +} + + +/* private functions */ + +static void +gimp_display_shell_save_viewport_center (GimpDisplayShell *shell, + gdouble *x, + gdouble *y) +{ + gimp_display_shell_unrotate_xy_f (shell, + shell->disp_width / 2, + shell->disp_height / 2, + x, y); +} + +static void +gimp_display_shell_restore_viewport_center (GimpDisplayShell *shell, + gdouble x, + gdouble y) +{ + gimp_display_shell_rotate_xy_f (shell, x, y, &x, &y); + + x += shell->offset_x - shell->disp_width / 2; + y += shell->offset_y - shell->disp_height / 2; + + gimp_display_shell_scroll_set_offset (shell, RINT (x), RINT (y)); +} diff --git a/app/display/gimpdisplayshell-rotate.h b/app/display/gimpdisplayshell-rotate.h new file mode 100644 index 0000000..d1ffa05 --- /dev/null +++ b/app/display/gimpdisplayshell-rotate.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_ROTATE_H__ +#define __GIMP_DISPLAY_SHELL_ROTATE_H__ + + +void gimp_display_shell_flip (GimpDisplayShell *shell, + gboolean flip_horizontally, + gboolean flip_vertically); + +void gimp_display_shell_rotate (GimpDisplayShell *shell, + gdouble delta); +void gimp_display_shell_rotate_to (GimpDisplayShell *shell, + gdouble value); +void gimp_display_shell_rotate_drag (GimpDisplayShell *shell, + gdouble last_x, + gdouble last_y, + gdouble cur_x, + gdouble cur_y, + gboolean constrain); + +void gimp_display_shell_rotate_update_transform (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_ROTATE_H__ */ diff --git a/app/display/gimpdisplayshell-rulers.c b/app/display/gimpdisplayshell-rulers.c new file mode 100644 index 0000000..79253c8 --- /dev/null +++ b/app/display/gimpdisplayshell-rulers.c @@ -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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-rulers.h" +#include "gimpdisplayshell-scale.h" + + +/** + * gimp_display_shell_rulers_update: + * @shell: + * + **/ +void +gimp_display_shell_rulers_update (GimpDisplayShell *shell) +{ + GimpImage *image; + gint image_width; + gint image_height; + gdouble offset_x = 0.0; + gdouble offset_y = 0.0; + gdouble scale_x = 1.0; + gdouble scale_y = 1.0; + gdouble resolution_x = 1.0; + gdouble resolution_y = 1.0; + gdouble horizontal_lower; + gdouble horizontal_upper; + gdouble horizontal_max_size; + gdouble vertical_lower; + gdouble vertical_upper; + gdouble vertical_max_size; + + if (! shell->display) + return; + + image = gimp_display_get_image (shell->display); + + if (image) + { + gint image_x, image_y; + gdouble res_x, res_y; + + gimp_display_shell_scale_get_image_bounds (shell, + &image_x, &image_y, + &image_width, &image_height); + + gimp_display_shell_get_rotated_scale (shell, &scale_x, &scale_y); + + image_width /= scale_x; + image_height /= scale_y; + + offset_x = shell->offset_x - image_x; + offset_y = shell->offset_y - image_y; + + gimp_image_get_resolution (image, &res_x, &res_y); + + if (shell->rotate_angle == 0.0 || res_x == res_y) + { + resolution_x = res_x; + resolution_y = res_y; + } + else + { + gdouble cos_a = cos (G_PI * shell->rotate_angle / 180.0); + gdouble sin_a = sin (G_PI * shell->rotate_angle / 180.0); + + if (shell->dot_for_dot) + { + resolution_x = 1.0 / sqrt (SQR (cos_a / res_x) + + SQR (sin_a / res_y)); + resolution_y = 1.0 / sqrt (SQR (cos_a / res_y) + + SQR (sin_a / res_x)); + } + else + { + resolution_x = sqrt (SQR (res_x * cos_a) + SQR (res_y * sin_a)); + resolution_y = sqrt (SQR (res_y * cos_a) + SQR (res_x * sin_a)); + } + } + } + else + { + image_width = shell->disp_width; + image_height = shell->disp_height; + } + + /* Initialize values */ + + horizontal_lower = 0; + vertical_lower = 0; + + if (image) + { + horizontal_upper = gimp_pixels_to_units (shell->disp_width / scale_x, + shell->unit, + resolution_x); + horizontal_max_size = gimp_pixels_to_units (MAX (image_width, + image_height), + shell->unit, + resolution_x); + + vertical_upper = gimp_pixels_to_units (shell->disp_height / scale_y, + shell->unit, + resolution_y); + vertical_max_size = gimp_pixels_to_units (MAX (image_width, + image_height), + shell->unit, + resolution_y); + } + else + { + horizontal_upper = image_width; + horizontal_max_size = MAX (image_width, image_height); + + vertical_upper = image_height; + vertical_max_size = MAX (image_width, image_height); + } + + + /* Adjust due to scrolling */ + + if (image) + { + offset_x *= horizontal_upper / shell->disp_width; + offset_y *= vertical_upper / shell->disp_height; + + horizontal_lower += offset_x; + horizontal_upper += offset_x; + + vertical_lower += offset_y; + vertical_upper += offset_y; + } + + /* Finally setup the actual rulers */ + + gimp_ruler_set_range (GIMP_RULER (shell->hrule), + horizontal_lower, + horizontal_upper, + horizontal_max_size); + + gimp_ruler_set_unit (GIMP_RULER (shell->hrule), + shell->unit); + + gimp_ruler_set_range (GIMP_RULER (shell->vrule), + vertical_lower, + vertical_upper, + vertical_max_size); + + gimp_ruler_set_unit (GIMP_RULER (shell->vrule), + shell->unit); +} diff --git a/app/display/gimpdisplayshell-rulers.h b/app/display/gimpdisplayshell-rulers.h new file mode 100644 index 0000000..117a8f0 --- /dev/null +++ b/app/display/gimpdisplayshell-rulers.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_RULERS_H__ +#define __GIMP_DISPLAY_SHELL_RULERS_H__ + + +void gimp_display_shell_rulers_update (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_RULERS_H__ */ diff --git a/app/display/gimpdisplayshell-scale-dialog.c b/app/display/gimpdisplayshell-scale-dialog.c new file mode 100644 index 0000000..f3d2eab --- /dev/null +++ b/app/display/gimpdisplayshell-scale-dialog.c @@ -0,0 +1,293 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpviewable.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpviewabledialog.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scale-dialog.h" + +#include "gimp-intl.h" + + +#define SCALE_EPSILON 0.0001 +#define SCALE_EQUALS(a,b) (fabs ((a) - (b)) < SCALE_EPSILON) + + +typedef struct +{ + GimpDisplayShell *shell; + GimpZoomModel *model; + GtkAdjustment *scale_adj; + GtkAdjustment *num_adj; + GtkAdjustment *denom_adj; +} ScaleDialogData; + + +/* local function prototypes */ + +static void gimp_display_shell_scale_dialog_response (GtkWidget *widget, + gint response_id, + ScaleDialogData *dialog); +static void gimp_display_shell_scale_dialog_free (ScaleDialogData *dialog); + +static void update_zoom_values (GtkAdjustment *adj, + ScaleDialogData *dialog); + + + +/* public functions */ + +/** + * gimp_display_shell_scale_dialog: + * @shell: the #GimpDisplayShell + * + * Constructs and displays a dialog allowing the user to enter a + * custom display scale. + **/ +void +gimp_display_shell_scale_dialog (GimpDisplayShell *shell) +{ + ScaleDialogData *data; + GimpImage *image; + GtkWidget *toplevel; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *spin; + GtkWidget *label; + gint num, denom, row; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->scale_dialog) + { + gtk_window_present (GTK_WINDOW (shell->scale_dialog)); + return; + } + + if (SCALE_EQUALS (shell->other_scale, 0.0)) + { + /* other_scale not yet initialized */ + shell->other_scale = gimp_zoom_model_get_factor (shell->zoom); + } + + image = gimp_display_get_image (shell->display); + + data = g_slice_new (ScaleDialogData); + + data->shell = shell; + data->model = g_object_new (GIMP_TYPE_ZOOM_MODEL, + "value", fabs (shell->other_scale), + NULL); + + shell->scale_dialog = + gimp_viewable_dialog_new (GIMP_VIEWABLE (image), + gimp_get_user_context (shell->display->gimp), + _("Zoom Ratio"), "display_scale", + "zoom-original", + _("Select Zoom Ratio"), + GTK_WIDGET (shell), + gimp_standard_help_func, + GIMP_HELP_VIEW_ZOOM_OTHER, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (shell->scale_dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_object_weak_ref (G_OBJECT (shell->scale_dialog), + (GWeakNotify) gimp_display_shell_scale_dialog_free, data); + g_object_weak_ref (G_OBJECT (shell->scale_dialog), + (GWeakNotify) g_object_unref, data->model); + + g_object_add_weak_pointer (G_OBJECT (shell->scale_dialog), + (gpointer) &shell->scale_dialog); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + gtk_window_set_transient_for (GTK_WINDOW (shell->scale_dialog), + GTK_WINDOW (toplevel)); + gtk_window_set_destroy_with_parent (GTK_WINDOW (shell->scale_dialog), TRUE); + + g_signal_connect (shell->scale_dialog, "response", + G_CALLBACK (gimp_display_shell_scale_dialog_response), + data); + + table = gtk_table_new (2, 2, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), 12); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell->scale_dialog))), + table, TRUE, TRUE, 0); + gtk_widget_show (table); + + row = 0; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gimp_table_attach_aligned (GTK_TABLE (table), 0, row++, + _("Zoom ratio:"), 0.0, 0.5, + hbox, 1, FALSE); + + gimp_zoom_model_get_fraction (data->model, &num, &denom); + + data->num_adj = (GtkAdjustment *) + gtk_adjustment_new (num, 1, 256, 1, 8, 0); + spin = gimp_spin_button_new (data->num_adj, 1.0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE); + gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0); + gtk_widget_show (spin); + + label = gtk_label_new (":"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + data->denom_adj = (GtkAdjustment *) + gtk_adjustment_new (denom, 1, 256, 1, 8, 0); + spin = gimp_spin_button_new (data->denom_adj, 1.0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE); + gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0); + gtk_widget_show (spin); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gimp_table_attach_aligned (GTK_TABLE (table), 0, row++, + _("Zoom:"), 0.0, 0.5, + hbox, 1, FALSE); + + data->scale_adj = (GtkAdjustment *) + gtk_adjustment_new (fabs (shell->other_scale) * 100, + 100.0 / 256.0, 25600.0, + 10, 50, 0); + spin = gimp_spin_button_new (data->scale_adj, 1.0, 2); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE); + gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0); + gtk_widget_show (spin); + + label = gtk_label_new ("%"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + g_signal_connect (data->scale_adj, "value-changed", + G_CALLBACK (update_zoom_values), data); + g_signal_connect (data->num_adj, "value-changed", + G_CALLBACK (update_zoom_values), data); + g_signal_connect (data->denom_adj, "value-changed", + G_CALLBACK (update_zoom_values), data); + + gtk_widget_show (shell->scale_dialog); +} + +static void +gimp_display_shell_scale_dialog_response (GtkWidget *widget, + gint response_id, + ScaleDialogData *dialog) +{ + if (response_id == GTK_RESPONSE_OK) + { + gdouble scale; + + scale = gtk_adjustment_get_value (dialog->scale_adj); + + gimp_display_shell_scale (dialog->shell, + GIMP_ZOOM_TO, + scale / 100.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + } + else + { + /* need to emit "scaled" to get the menu updated */ + gimp_display_shell_scaled (dialog->shell); + } + + dialog->shell->other_scale = - fabs (dialog->shell->other_scale); + + gtk_widget_destroy (dialog->shell->scale_dialog); +} + +static void +gimp_display_shell_scale_dialog_free (ScaleDialogData *dialog) +{ + g_slice_free (ScaleDialogData, dialog); +} + +static void +update_zoom_values (GtkAdjustment *adj, + ScaleDialogData *dialog) +{ + gint num, denom; + gdouble scale; + + g_signal_handlers_block_by_func (dialog->scale_adj, + update_zoom_values, + dialog); + g_signal_handlers_block_by_func (dialog->num_adj, + update_zoom_values, + dialog); + g_signal_handlers_block_by_func (dialog->denom_adj, + update_zoom_values, + dialog); + + if (adj == dialog->scale_adj) + { + scale = gtk_adjustment_get_value (dialog->scale_adj); + + gimp_zoom_model_zoom (dialog->model, GIMP_ZOOM_TO, scale / 100.0); + gimp_zoom_model_get_fraction (dialog->model, &num, &denom); + + gtk_adjustment_set_value (dialog->num_adj, num); + gtk_adjustment_set_value (dialog->denom_adj, denom); + } + else /* fraction adjustments */ + { + scale = (gtk_adjustment_get_value (dialog->num_adj) / + gtk_adjustment_get_value (dialog->denom_adj)); + + gtk_adjustment_set_value (dialog->scale_adj, scale * 100); + } + + g_signal_handlers_unblock_by_func (dialog->scale_adj, + update_zoom_values, + dialog); + g_signal_handlers_unblock_by_func (dialog->num_adj, + update_zoom_values, + dialog); + g_signal_handlers_unblock_by_func (dialog->denom_adj, + update_zoom_values, + dialog); +} diff --git a/app/display/gimpdisplayshell-scale-dialog.h b/app/display/gimpdisplayshell-scale-dialog.h new file mode 100644 index 0000000..eb83065 --- /dev/null +++ b/app/display/gimpdisplayshell-scale-dialog.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_SCALE_DIALOG_H__ +#define __GIMP_DISPLAY_SHELL_SCALE_DIALOG_H__ + + +void gimp_display_shell_scale_dialog (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_SCALE_DIALOG_H__ */ diff --git a/app/display/gimpdisplayshell-scale.c b/app/display/gimpdisplayshell-scale.c new file mode 100644 index 0000000..482a9fe --- /dev/null +++ b/app/display/gimpdisplayshell-scale.c @@ -0,0 +1,1449 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-transform.h" +#include "gimpimagewindow.h" + + +#define SCALE_TIMEOUT 2 +#define SCALE_EPSILON 0.0001 +#define ALMOST_CENTERED_THRESHOLD 2 + +#define SCALE_EQUALS(a,b) (fabs ((a) - (b)) < SCALE_EPSILON) + + +/* local function prototypes */ + +static void gimp_display_shell_scale_get_screen_resolution + (GimpDisplayShell *shell, + gdouble *xres, + gdouble *yres); +static void gimp_display_shell_scale_get_image_size_for_scale + (GimpDisplayShell *shell, + gdouble scale, + gint *w, + gint *h); +static void gimp_display_shell_calculate_scale_x_and_y + (GimpDisplayShell *shell, + gdouble scale, + gdouble *scale_x, + gdouble *scale_y); + +static void gimp_display_shell_scale_to (GimpDisplayShell *shell, + gdouble scale, + gdouble viewport_x, + gdouble viewport_y); +static void gimp_display_shell_scale_fit_or_fill (GimpDisplayShell *shell, + gboolean fill); + +static gboolean gimp_display_shell_scale_image_starts_to_fit + (GimpDisplayShell *shell, + gdouble new_scale, + gdouble current_scale, + gboolean *vertically, + gboolean *horizontally); +static gboolean gimp_display_shell_scale_viewport_coord_almost_centered + (GimpDisplayShell *shell, + gint x, + gint y, + gboolean *horizontally, + gboolean *vertically); + +static void gimp_display_shell_scale_get_image_center_viewport + (GimpDisplayShell *shell, + gint *image_center_x, + gint *image_center_y); + + +static void gimp_display_shell_scale_get_zoom_focus (GimpDisplayShell *shell, + gdouble new_scale, + gdouble current_scale, + gdouble *x, + gdouble *y, + GimpZoomFocus zoom_focus); + + +/* public functions */ + +/** + * gimp_display_shell_scale_revert: + * @shell: the #GimpDisplayShell + * + * Reverts the display to the previously used scale. If no previous + * scale exist, then the call does nothing. + * + * Return value: %TRUE if the scale was reverted, otherwise %FALSE. + **/ +gboolean +gimp_display_shell_scale_revert (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + /* don't bother if no scale has been set */ + if (shell->last_scale < SCALE_EPSILON) + return FALSE; + + shell->last_scale_time = 0; + + gimp_display_shell_scale_by_values (shell, + shell->last_scale, + shell->last_offset_x, + shell->last_offset_y, + FALSE); /* don't resize the window */ + + return TRUE; +} + +/** + * gimp_display_shell_scale_can_revert: + * @shell: the #GimpDisplayShell + * + * Return value: %TRUE if a previous display scale exists, otherwise %FALSE. + **/ +gboolean +gimp_display_shell_scale_can_revert (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return (shell->last_scale > SCALE_EPSILON); +} + +/** + * gimp_display_shell_scale_save_revert_values: + * @shell: + * + * Handle the updating of the Revert Zoom variables. + **/ +void +gimp_display_shell_scale_save_revert_values (GimpDisplayShell *shell) +{ + guint now; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + now = time (NULL); + + if (now - shell->last_scale_time >= SCALE_TIMEOUT) + { + shell->last_scale = gimp_zoom_model_get_factor (shell->zoom); + shell->last_offset_x = shell->offset_x; + shell->last_offset_y = shell->offset_y; + } + + shell->last_scale_time = now; +} + +/** + * gimp_display_shell_scale_set_dot_for_dot: + * @shell: the #GimpDisplayShell + * @dot_for_dot: whether "Dot for Dot" should be enabled + * + * If @dot_for_dot is set to %TRUE then the "Dot for Dot" mode (where image and + * screen pixels are of the same size) is activated. Dually, the mode is + * disabled if @dot_for_dot is %FALSE. + **/ +void +gimp_display_shell_scale_set_dot_for_dot (GimpDisplayShell *shell, + gboolean dot_for_dot) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (dot_for_dot != shell->dot_for_dot) + { + GimpDisplayConfig *config = shell->display->config; + gboolean resize_window; + + /* Resize windows only in multi-window mode */ + resize_window = (config->resize_windows_on_zoom && + ! GIMP_GUI_CONFIG (config)->single_window_mode); + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + shell->dot_for_dot = dot_for_dot; + + gimp_display_shell_scale_update (shell); + + gimp_display_shell_scale_resize (shell, resize_window, FALSE); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); + } +} + +/** + * gimp_display_shell_scale_get_image_size: + * @shell: + * @w: + * @h: + * + * Gets the size of the rendered image after it has been scaled. + * + **/ +void +gimp_display_shell_scale_get_image_size (GimpDisplayShell *shell, + gint *w, + gint *h) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_scale_get_image_size_for_scale (shell, + gimp_zoom_model_get_factor (shell->zoom), + w, h); +} + +/** + * gimp_display_shell_scale_get_image_bounds: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the screen-space boudning box of the image, after it has + * been transformed (i.e., scaled, rotated, and scrolled). + **/ +void +gimp_display_shell_scale_get_image_bounds (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GimpImage *image; + gdouble x1, y1; + gdouble x2, y2; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + gimp_display_shell_transform_bounds (shell, + 0, 0, + gimp_image_get_width (image), + gimp_image_get_height (image), + &x1, &y1, + &x2, &y2); + + x1 = ceil (x1); + y1 = ceil (y1); + x2 = floor (x2); + y2 = floor (y2); + + if (x) *x = x1 + shell->offset_x; + if (y) *y = y1 + shell->offset_y; + if (w) *w = x2 - x1; + if (h) *h = y2 - y1; +} + +/** + * gimp_display_shell_scale_get_image_unrotated_bounds: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the screen-space boudning box of the image, after it has + * been scaled and scrolled, but before it has been rotated. + **/ +void +gimp_display_shell_scale_get_image_unrotated_bounds (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + if (x) *x = -shell->offset_x; + if (y) *y = -shell->offset_y; + if (w) *w = floor (gimp_image_get_width (image) * shell->scale_x); + if (h) *h = floor (gimp_image_get_height (image) * shell->scale_y); +} + +/** + * gimp_display_shell_scale_get_image_bounding_box: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the screen-space boudning box of the image content, after it has + * been transformed (i.e., scaled, rotated, and scrolled). + **/ +void +gimp_display_shell_scale_get_image_bounding_box (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GeglRectangle bounding_box; + gdouble x1, y1; + gdouble x2, y2; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + bounding_box = gimp_display_shell_get_bounding_box (shell); + + gimp_display_shell_transform_bounds (shell, + bounding_box.x, + bounding_box.y, + bounding_box.x + bounding_box.width, + bounding_box.y + bounding_box.height, + &x1, &y1, + &x2, &y2); + + if (! shell->show_all) + { + x1 = ceil (x1); + y1 = ceil (y1); + x2 = floor (x2); + y2 = floor (y2); + } + else + { + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + } + + if (x) *x = x1 + shell->offset_x; + if (y) *y = y1 + shell->offset_y; + if (w) *w = x2 - x1; + if (h) *h = y2 - y1; +} + +/** + * gimp_display_shell_scale_get_image_unrotated_bounding_box: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the screen-space boudning box of the image content, after it has + * been scaled and scrolled, but before it has been rotated. + **/ +void +gimp_display_shell_scale_get_image_unrotated_bounding_box (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GeglRectangle bounding_box; + gdouble x1, y1; + gdouble x2, y2; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + bounding_box = gimp_display_shell_get_bounding_box (shell); + + x1 = bounding_box.x * shell->scale_x - + shell->offset_x; + y1 = bounding_box.y * shell->scale_y - + shell->offset_y; + + x2 = (bounding_box.x + bounding_box.width) * shell->scale_x - + shell->offset_x; + y2 = (bounding_box.y + bounding_box.height) * shell->scale_y - + shell->offset_y; + + if (! shell->show_all) + { + x1 = ceil (x1); + y1 = ceil (y1); + x2 = floor (x2); + y2 = floor (y2); + } + else + { + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + } + + if (x) *x = x1; + if (y) *y = y1; + if (w) *w = x2 - x1; + if (h) *h = y2 - y1; +} + +/** + * gimp_display_shell_scale_image_is_within_viewport: + * @shell: + * + * Returns: %TRUE if the (scaled) image is smaller than and within the + * viewport. + **/ +gboolean +gimp_display_shell_scale_image_is_within_viewport (GimpDisplayShell *shell, + gboolean *horizontally, + gboolean *vertically) +{ + gboolean horizontally_dummy, vertically_dummy; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + if (! horizontally) horizontally = &horizontally_dummy; + if (! vertically) vertically = &vertically_dummy; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + gint sx, sy; + gint sw, sh; + + gimp_display_shell_scale_get_image_bounding_box (shell, + &sx, &sy, &sw, &sh); + + sx -= shell->offset_x; + sy -= shell->offset_y; + + *horizontally = sx >= 0 && sx + sw <= shell->disp_width; + *vertically = sy >= 0 && sy + sh <= shell->disp_height; + } + else + { + *horizontally = FALSE; + *vertically = FALSE; + } + + return *vertically && *horizontally; +} + +/* We used to calculate the scale factor in the SCALEFACTOR_X() and + * SCALEFACTOR_Y() macros. But since these are rather frequently + * called and the values rarely change, we now store them in the + * shell and call this function whenever they need to be recalculated. + */ +void +gimp_display_shell_scale_update (GimpDisplayShell *shell) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + if (image) + { + gimp_display_shell_calculate_scale_x_and_y (shell, + gimp_zoom_model_get_factor (shell->zoom), + &shell->scale_x, + &shell->scale_y); + } + else + { + shell->scale_x = 1.0; + shell->scale_y = 1.0; + } +} + +/** + * gimp_display_shell_scale: + * @shell: the #GimpDisplayShell + * @zoom_type: whether to zoom in, out or to a specific scale + * @scale: ignored unless @zoom_type == %GIMP_ZOOM_TO + * + * This function figures out the context of the zoom and behaves + * appropriately thereafter. + * + **/ +void +gimp_display_shell_scale (GimpDisplayShell *shell, + GimpZoomType zoom_type, + gdouble new_scale, + GimpZoomFocus zoom_focus) +{ + GimpDisplayConfig *config; + gdouble current_scale; + gboolean resize_window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->canvas != NULL); + + current_scale = gimp_zoom_model_get_factor (shell->zoom); + + if (zoom_type != GIMP_ZOOM_TO) + new_scale = gimp_zoom_model_zoom_step (zoom_type, current_scale); + + if (SCALE_EQUALS (new_scale, current_scale)) + return; + + config = shell->display->config; + + /* Resize windows only in multi-window mode */ + resize_window = (config->resize_windows_on_zoom && + ! GIMP_GUI_CONFIG (config)->single_window_mode); + + if (resize_window) + { + /* If the window is resized on zoom, simply do the zoom and get + * things rolling + */ + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, new_scale); + + gimp_display_shell_scale_resize (shell, TRUE, FALSE); + } + else + { + gdouble x, y; + gint image_center_x; + gint image_center_y; + + gimp_display_shell_scale_get_zoom_focus (shell, + new_scale, + current_scale, + &x, + &y, + zoom_focus); + gimp_display_shell_scale_get_image_center_viewport (shell, + &image_center_x, + &image_center_y); + + gimp_display_shell_scale_to (shell, new_scale, x, y); + + /* skip centering magic if pointer focus was requested */ + if (zoom_focus != GIMP_ZOOM_FOCUS_POINTER) + { + gboolean starts_fitting_horiz; + gboolean starts_fitting_vert; + gboolean zoom_focus_almost_centered_horiz; + gboolean zoom_focus_almost_centered_vert; + gboolean image_center_almost_centered_horiz; + gboolean image_center_almost_centered_vert; + + /* If an image axis started to fit due to zooming out or if + * the focus point is as good as in the center, center on + * that axis + */ + gimp_display_shell_scale_image_starts_to_fit (shell, + new_scale, + current_scale, + &starts_fitting_horiz, + &starts_fitting_vert); + + gimp_display_shell_scale_viewport_coord_almost_centered (shell, + x, + y, + &zoom_focus_almost_centered_horiz, + &zoom_focus_almost_centered_vert); + gimp_display_shell_scale_viewport_coord_almost_centered (shell, + image_center_x, + image_center_y, + &image_center_almost_centered_horiz, + &image_center_almost_centered_vert); + + gimp_display_shell_scroll_center_image (shell, + starts_fitting_horiz || + (zoom_focus_almost_centered_horiz && + image_center_almost_centered_horiz), + starts_fitting_vert || + (zoom_focus_almost_centered_vert && + image_center_almost_centered_vert)); + } + } +} + +/** + * gimp_display_shell_scale_to_rectangle: + * @shell: the #GimpDisplayShell + * @zoom_type: whether to zoom in or out + * @x: retangle's x in image coordinates + * @y: retangle's y in image coordinates + * @width: retangle's width in image coordinates + * @height: retangle's height in image coordinates + * @resize_window: whether the display window should be resized + * + * Scales and scrolls to a specific image rectangle + **/ +void +gimp_display_shell_scale_to_rectangle (GimpDisplayShell *shell, + GimpZoomType zoom_type, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean resize_window) +{ + gdouble current_scale; + gdouble new_scale; + gdouble factor = 1.0; + gint offset_x = 0; + gint offset_y = 0; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_transform_bounds (shell, + x, y, + x + width, y + height, + &x, &y, + &width, &height); + + /* Convert scrolled (x1, y1, x2, y2) to unscrolled (x, y, width, height). */ + width -= x; + height -= y; + x += shell->offset_x; + y += shell->offset_y; + + width = MAX (1.0, width); + height = MAX (1.0, height); + + current_scale = gimp_zoom_model_get_factor (shell->zoom); + + switch (zoom_type) + { + case GIMP_ZOOM_IN: + factor = MIN ((shell->disp_width / width), + (shell->disp_height / height)); + break; + + case GIMP_ZOOM_OUT: + factor = MAX ((width / shell->disp_width), + (height / shell->disp_height)); + break; + + default: + g_return_if_reached (); + break; + } + + new_scale = current_scale * factor; + + switch (zoom_type) + { + case GIMP_ZOOM_IN: + /* move the center of the rectangle to the center of the + * viewport: + * + * new_offset = center of rectangle in new scale screen coords + * including offset + * - + * center of viewport in screen coords without + * offset + */ + offset_x = RINT (factor * (x + width / 2.0) - (shell->disp_width / 2)); + offset_y = RINT (factor * (y + height / 2.0) - (shell->disp_height / 2)); + break; + + case GIMP_ZOOM_OUT: + /* move the center of the viewport to the center of the + * rectangle: + * + * new_offset = center of viewport in new scale screen coords + * including offset + * - + * center of rectangle in screen coords without + * offset + */ + offset_x = RINT (factor * (shell->offset_x + shell->disp_width / 2) - + ((x + width / 2.0) - shell->offset_x)); + + offset_y = RINT (factor * (shell->offset_y + shell->disp_height / 2) - + ((y + height / 2.0) - shell->offset_y)); + break; + + default: + break; + } + + if (new_scale != current_scale || + offset_x != shell->offset_x || + offset_y != shell->offset_y) + { + gimp_display_shell_scale_by_values (shell, + new_scale, + offset_x, offset_y, + resize_window); + } +} + +/** + * gimp_display_shell_scale_fit_in: + * @shell: the #GimpDisplayShell + * + * Sets the scale such that the entire image precisely fits in the + * display area. + **/ +void +gimp_display_shell_scale_fit_in (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_scale_fit_or_fill (shell, + /* fill = */ FALSE); + } + +/** + * gimp_display_shell_scale_fill: + * @shell: the #GimpDisplayShell + * + * Sets the scale such that the entire display area is precisely + * filled by the image. + **/ +void +gimp_display_shell_scale_fill (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_scale_fit_or_fill (shell, + /* fill = */ TRUE); +} + +/** + * gimp_display_shell_scale_by_values: + * @shell: the #GimpDisplayShell + * @scale: the new scale + * @offset_x: the new X offset + * @offset_y: the new Y offset + * @resize_window: whether the display window should be resized + * + * Directly sets the image scale and image offsets used by the display. If + * @resize_window is %TRUE then the display window is resized to better + * accommodate the image, see gimp_display_shell_shrink_wrap(). + **/ +void +gimp_display_shell_scale_by_values (GimpDisplayShell *shell, + gdouble scale, + gint offset_x, + gint offset_y, + gboolean resize_window) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + /* Abort early if the values are all setup already. We don't + * want to inadvertently resize the window (bug #164281). + */ + if (SCALE_EQUALS (gimp_zoom_model_get_factor (shell->zoom), scale) && + shell->offset_x == offset_x && + shell->offset_y == offset_y) + return; + + gimp_display_shell_scale_save_revert_values (shell); + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale); + + shell->offset_x = offset_x; + shell->offset_y = offset_y; + + gimp_display_shell_rotate_update_transform (shell); + + gimp_display_shell_scale_resize (shell, resize_window, FALSE); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +void +gimp_display_shell_scale_drag (GimpDisplayShell *shell, + gdouble start_x, + gdouble start_y, + gdouble delta_x, + gdouble delta_y) +{ + gdouble scale; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + scale = gimp_zoom_model_get_factor (shell->zoom); + + gimp_display_shell_push_zoom_focus_pointer_pos (shell, start_x, start_y); + + if (delta_y > 0) + { + gimp_display_shell_scale (shell, + GIMP_ZOOM_TO, + scale * 1.1, + GIMP_ZOOM_FOCUS_POINTER); + } + else if (delta_y < 0) + { + gimp_display_shell_scale (shell, + GIMP_ZOOM_TO, + scale * 0.9, + GIMP_ZOOM_FOCUS_POINTER); + } +} + +/** + * gimp_display_shell_scale_shrink_wrap: + * @shell: the #GimpDisplayShell + * + * Convenience function with the same functionality as + * gimp_display_shell_scale_resize(@shell, TRUE, grow_only). + **/ +void +gimp_display_shell_scale_shrink_wrap (GimpDisplayShell *shell, + gboolean grow_only) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_scale_resize (shell, TRUE, grow_only); +} + +/** + * gimp_display_shell_scale_resize: + * @shell: the #GimpDisplayShell + * @resize_window: whether the display window should be resized + * @grow_only: whether shrinking of the window is allowed or not + * + * Function commonly called after a change in display scale to make the changes + * visible to the user. If @resize_window is %TRUE then the display window is + * resized to accommodate the display image as per + * gimp_display_shell_shrink_wrap(). + **/ +void +gimp_display_shell_scale_resize (GimpDisplayShell *shell, + gboolean resize_window, + gboolean grow_only) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + if (resize_window) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + gimp_image_window_shrink_wrap (window, grow_only); + } + } + + gimp_display_shell_scroll_clamp_and_update (shell); + gimp_display_shell_scaled (shell); + + gimp_display_shell_expose_full (shell); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +void +gimp_display_shell_set_initial_scale (GimpDisplayShell *shell, + gdouble scale, + gint *display_width, + gint *display_height) +{ + GimpImage *image; + GdkScreen *screen; + gint image_width; + gint image_height; + gint shell_width; + gint shell_height; + gint screen_width; + gint screen_height; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + screen = gtk_widget_get_screen (GTK_WIDGET (shell)); + + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + + screen_width = gdk_screen_get_width (screen) * 0.75; + screen_height = gdk_screen_get_height (screen) * 0.75; + + /* We need to zoom before we use SCALE[XY] */ + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale); + + shell_width = SCALEX (shell, image_width); + shell_height = SCALEY (shell, image_height); + + if (shell->display->config->initial_zoom_to_fit) + { + /* Limit to the size of the screen... */ + if (shell_width > screen_width || shell_height > screen_height) + { + gdouble new_scale; + gdouble current = gimp_zoom_model_get_factor (shell->zoom); + + new_scale = current * MIN (((gdouble) screen_height) / shell_height, + ((gdouble) screen_width) / shell_width); + + new_scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, new_scale); + + /* Since zooming out might skip a zoom step we zoom in + * again and test if we are small enough. + */ + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, + gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, + new_scale)); + + if (SCALEX (shell, image_width) > screen_width || + SCALEY (shell, image_height) > screen_height) + gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, new_scale); + + shell_width = SCALEX (shell, image_width); + shell_height = SCALEY (shell, image_height); + } + } + else + { + /* Set up size like above, but do not zoom to fit. Useful when + * working on large images. + */ + if (shell_width > screen_width) + shell_width = screen_width; + + if (shell_height > screen_height) + shell_height = screen_height; + } + + if (display_width) + *display_width = shell_width; + + if (display_height) + *display_height = shell_height; +} + +/** + * gimp_display_shell_get_rotated_scale: + * @shell: the #GimpDisplayShell + * @scale_x: horizontal scale output + * @scale_y: vertical scale output + * + * Returns the screen space horizontal and vertical scaling + * factors, taking rotation into account. + **/ +void +gimp_display_shell_get_rotated_scale (GimpDisplayShell *shell, + gdouble *scale_x, + gdouble *scale_y) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->rotate_angle == 0.0 || shell->scale_x == shell->scale_y) + { + if (scale_x) *scale_x = shell->scale_x; + if (scale_y) *scale_y = shell->scale_y; + } + else + { + gdouble a = G_PI * shell->rotate_angle / 180.0; + gdouble cos_a = cos (a); + gdouble sin_a = sin (a); + + if (scale_x) *scale_x = 1.0 / sqrt (SQR (cos_a / shell->scale_x) + + SQR (sin_a / shell->scale_y)); + + if (scale_y) *scale_y = 1.0 / sqrt (SQR (cos_a / shell->scale_y) + + SQR (sin_a / shell->scale_x)); + } +} + +/** + * gimp_display_shell_push_zoom_focus_pointer_pos: + * @shell: + * @x: + * @y: + * + * When the zoom focus mechanism asks for the pointer the next time, + * use @x and @y. + **/ +void +gimp_display_shell_push_zoom_focus_pointer_pos (GimpDisplayShell *shell, + gint x, + gint y) +{ + GdkPoint *point = g_slice_new (GdkPoint); + point->x = x; + point->y = y; + + g_queue_push_head (shell->zoom_focus_pointer_queue, + point); +} + + +/* private functions */ + +static void +gimp_display_shell_scale_get_screen_resolution (GimpDisplayShell *shell, + gdouble *xres, + gdouble *yres) +{ + gdouble x, y; + + if (shell->dot_for_dot) + { + gimp_image_get_resolution (gimp_display_get_image (shell->display), + &x, &y); + } + else + { + x = shell->monitor_xres; + y = shell->monitor_yres; + } + + if (xres) *xres = x; + if (yres) *yres = y; +} + +/** + * gimp_display_shell_scale_get_image_size_for_scale: + * @shell: + * @scale: + * @w: + * @h: + * + **/ +static void +gimp_display_shell_scale_get_image_size_for_scale (GimpDisplayShell *shell, + gdouble scale, + gint *w, + gint *h) +{ + GimpImage *image = gimp_display_get_image (shell->display); + gdouble scale_x; + gdouble scale_y; + + gimp_display_shell_calculate_scale_x_and_y (shell, scale, &scale_x, &scale_y); + + if (w) *w = scale_x * gimp_image_get_width (image); + if (h) *h = scale_y * gimp_image_get_height (image); +} + +/** + * gimp_display_shell_calculate_scale_x_and_y: + * @shell: + * @scale: + * @scale_x: + * @scale_y: + * + **/ +static void +gimp_display_shell_calculate_scale_x_and_y (GimpDisplayShell *shell, + gdouble scale, + gdouble *scale_x, + gdouble *scale_y) +{ + GimpImage *image = gimp_display_get_image (shell->display); + gdouble xres; + gdouble yres; + gdouble screen_xres; + gdouble screen_yres; + + gimp_image_get_resolution (image, &xres, &yres); + gimp_display_shell_scale_get_screen_resolution (shell, + &screen_xres, &screen_yres); + + if (scale_x) *scale_x = scale * screen_xres / xres; + if (scale_y) *scale_y = scale * screen_yres / yres; +} + +/** + * gimp_display_shell_scale_to: + * @shell: + * @scale: + * @viewport_x: + * @viewport_y: + * + * Zooms. The display offsets are adjusted so that the point specified + * by @x and @y doesn't change it's position on screen. + **/ +static void +gimp_display_shell_scale_to (GimpDisplayShell *shell, + gdouble scale, + gdouble viewport_x, + gdouble viewport_y) +{ + gdouble image_x, image_y; + gdouble new_viewport_x, new_viewport_y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display) + return; + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + gimp_display_shell_untransform_xy_f (shell, + viewport_x, + viewport_y, + &image_x, + &image_y); + + /* Note that we never come here if we need to resize_windows_on_zoom + */ + gimp_display_shell_scale_by_values (shell, + scale, + shell->offset_x, + shell->offset_y, + FALSE); + + gimp_display_shell_transform_xy_f (shell, + image_x, + image_y, + &new_viewport_x, + &new_viewport_y); + + gimp_display_shell_scroll (shell, + new_viewport_x - viewport_x, + new_viewport_y - viewport_y); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +/** + * gimp_display_shell_scale_fit_or_fill: + * @shell: the #GimpDisplayShell + * @fill: whether to scale the image to fill the viewport, + * or fit inside the viewport + * + * A common implementation for gimp_display_shell_scale_{fit_in,fill}(). + **/ +static void +gimp_display_shell_scale_fit_or_fill (GimpDisplayShell *shell, + gboolean fill) +{ + GeglRectangle bounding_box; + gdouble image_x; + gdouble image_y; + gdouble image_width; + gdouble image_height; + gdouble current_scale; + gdouble zoom_factor; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + GimpImage *image = gimp_display_get_image (shell->display); + + bounding_box.x = 0; + bounding_box.y = 0; + bounding_box.width = gimp_image_get_width (image); + bounding_box.height = gimp_image_get_height (image); + } + else + { + bounding_box = gimp_display_shell_get_bounding_box (shell); + } + + gimp_display_shell_transform_bounds (shell, + bounding_box.x, + bounding_box.y, + bounding_box.x + bounding_box.width, + bounding_box.y + bounding_box.height, + &image_x, + &image_y, + &image_width, + &image_height); + + image_width -= image_x; + image_height -= image_y; + + current_scale = gimp_zoom_model_get_factor (shell->zoom); + + if (fill) + { + zoom_factor = MAX (shell->disp_width / image_width, + shell->disp_height / image_height); + } + else + { + zoom_factor = MIN (shell->disp_width / image_width, + shell->disp_height / image_height); + } + + gimp_display_shell_scale (shell, + GIMP_ZOOM_TO, + zoom_factor * current_scale, + GIMP_ZOOM_FOCUS_BEST_GUESS); + + gimp_display_shell_scroll_center_content (shell, TRUE, TRUE); +} + +static gboolean +gimp_display_shell_scale_image_starts_to_fit (GimpDisplayShell *shell, + gdouble new_scale, + gdouble current_scale, + gboolean *vertically, + gboolean *horizontally) +{ + gboolean vertically_dummy; + gboolean horizontally_dummy; + + if (! vertically) vertically = &vertically_dummy; + if (! horizontally) horizontally = &horizontally_dummy; + + /* The image can only start to fit if we zoom out */ + if (new_scale > current_scale || + gimp_display_shell_get_infinite_canvas (shell)) + { + *vertically = FALSE; + *horizontally = FALSE; + } + else + { + gint current_scale_width; + gint current_scale_height; + gint new_scale_width; + gint new_scale_height; + + gimp_display_shell_scale_get_image_size_for_scale (shell, + current_scale, + ¤t_scale_width, + ¤t_scale_height); + + gimp_display_shell_scale_get_image_size_for_scale (shell, + new_scale, + &new_scale_width, + &new_scale_height); + + *vertically = (current_scale_width > shell->disp_width && + new_scale_width <= shell->disp_width); + *horizontally = (current_scale_height > shell->disp_height && + new_scale_height <= shell->disp_height); + } + + return *vertically && *horizontally; +} + +static gboolean +gimp_display_shell_scale_image_stops_to_fit (GimpDisplayShell *shell, + gdouble new_scale, + gdouble current_scale, + gboolean *vertically, + gboolean *horizontally) +{ + return gimp_display_shell_scale_image_starts_to_fit (shell, + current_scale, + new_scale, + vertically, + horizontally); +} + +/** + * gimp_display_shell_scale_viewport_coord_almost_centered: + * @shell: + * @x: + * @y: + * @horizontally: + * @vertically: + * + **/ +static gboolean +gimp_display_shell_scale_viewport_coord_almost_centered (GimpDisplayShell *shell, + gint x, + gint y, + gboolean *horizontally, + gboolean *vertically) +{ + gboolean local_horizontally = FALSE; + gboolean local_vertically = FALSE; + gint center_x = shell->disp_width / 2; + gint center_y = shell->disp_height / 2; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + local_horizontally = (x > center_x - ALMOST_CENTERED_THRESHOLD && + x < center_x + ALMOST_CENTERED_THRESHOLD); + + local_vertically = (y > center_y - ALMOST_CENTERED_THRESHOLD && + y < center_y + ALMOST_CENTERED_THRESHOLD); + } + + if (horizontally) *horizontally = local_horizontally; + if (vertically) *vertically = local_vertically; + + return local_horizontally && local_vertically; +} + +static void +gimp_display_shell_scale_get_image_center_viewport (GimpDisplayShell *shell, + gint *image_center_x, + gint *image_center_y) +{ + gint sw, sh; + + gimp_display_shell_scale_get_image_size (shell, &sw, &sh); + + if (image_center_x) *image_center_x = -shell->offset_x + sw / 2; + if (image_center_y) *image_center_y = -shell->offset_y + sh / 2; +} + +/** + * gimp_display_shell_scale_get_zoom_focus: + * @shell: + * @new_scale: + * @x: + * @y: + * + * Calculates the viewport coordinate to focus on when zooming + * independently for each axis. + **/ +static void +gimp_display_shell_scale_get_zoom_focus (GimpDisplayShell *shell, + gdouble new_scale, + gdouble current_scale, + gdouble *x, + gdouble *y, + GimpZoomFocus zoom_focus) +{ + GtkWidget *window = GTK_WIDGET (gimp_display_shell_get_window (shell)); + GdkEvent *event; + gint image_center_x; + gint image_center_y; + gint other_x; + gint other_y; + + /* Calculate stops-to-fit focus point */ + gimp_display_shell_scale_get_image_center_viewport (shell, + &image_center_x, + &image_center_y); + + /* Calculate other focus point, default is the canvas center */ + other_x = shell->disp_width / 2; + other_y = shell->disp_height / 2; + + /* Center on the mouse position instead of the display center if + * one of the following conditions are fulfilled and pointer is + * within the canvas: + * + * (1) there's no current event (the action was triggered by an + * input controller) + * (2) the event originates from the canvas (a scroll event) + * (3) the event originates from the window (a key press event) + * + * Basically the only situation where we don't want to center on + * mouse position is if the action is being called from a menu. + */ + event = gtk_get_current_event (); + + if (! event || + gtk_get_event_widget (event) == shell->canvas || + gtk_get_event_widget (event) == window) + { + GdkPoint *point = g_queue_pop_head (shell->zoom_focus_pointer_queue); + gint canvas_pointer_x; + gint canvas_pointer_y; + + if (point) + { + canvas_pointer_x = point->x; + canvas_pointer_y = point->y; + + g_slice_free (GdkPoint, point); + } + else + { + gtk_widget_get_pointer (shell->canvas, + &canvas_pointer_x, + &canvas_pointer_y); + } + + if (canvas_pointer_x >= 0 && + canvas_pointer_y >= 0 && + canvas_pointer_x < shell->disp_width && + canvas_pointer_y < shell->disp_height) + { + other_x = canvas_pointer_x; + other_y = canvas_pointer_y; + } + } + + if (zoom_focus == GIMP_ZOOM_FOCUS_RETAIN_CENTERING_ELSE_BEST_GUESS) + { + if (gimp_display_shell_scale_viewport_coord_almost_centered (shell, + image_center_x, + image_center_y, + NULL, + NULL)) + { + zoom_focus = GIMP_ZOOM_FOCUS_IMAGE_CENTER; + } + else + { + zoom_focus = GIMP_ZOOM_FOCUS_BEST_GUESS; + } + } + + switch (zoom_focus) + { + case GIMP_ZOOM_FOCUS_POINTER: + *x = other_x; + *y = other_y; + break; + + case GIMP_ZOOM_FOCUS_IMAGE_CENTER: + *x = image_center_x; + *y = image_center_y; + break; + + case GIMP_ZOOM_FOCUS_BEST_GUESS: + default: + { + gboolean within_horizontally, within_vertically; + gboolean stops_horizontally, stops_vertically; + + gimp_display_shell_scale_image_is_within_viewport (shell, + &within_horizontally, + &within_vertically); + + gimp_display_shell_scale_image_stops_to_fit (shell, + new_scale, + current_scale, + &stops_horizontally, + &stops_vertically); + + *x = within_horizontally && ! stops_horizontally ? image_center_x : other_x; + *y = within_vertically && ! stops_vertically ? image_center_y : other_y; + } + break; + } +} diff --git a/app/display/gimpdisplayshell-scale.h b/app/display/gimpdisplayshell-scale.h new file mode 100644 index 0000000..b97f9fb --- /dev/null +++ b/app/display/gimpdisplayshell-scale.h @@ -0,0 +1,108 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_SCALE_H__ +#define __GIMP_DISPLAY_SHELL_SCALE_H__ + + +gboolean gimp_display_shell_scale_revert (GimpDisplayShell *shell); +gboolean gimp_display_shell_scale_can_revert (GimpDisplayShell *shell); +void gimp_display_shell_scale_save_revert_values (GimpDisplayShell *shell); + +void gimp_display_shell_scale_set_dot_for_dot (GimpDisplayShell *shell, + gboolean dot_for_dot); + +void gimp_display_shell_scale_get_image_size (GimpDisplayShell *shell, + gint *w, + gint *h); +void gimp_display_shell_scale_get_image_bounds (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h); +void gimp_display_shell_scale_get_image_unrotated_bounds + (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h); +void gimp_display_shell_scale_get_image_bounding_box + (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h); +void gimp_display_shell_scale_get_image_unrotated_bounding_box + (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h); +gboolean gimp_display_shell_scale_image_is_within_viewport + (GimpDisplayShell *shell, + gboolean *horizontally, + gboolean *vertically); + +void gimp_display_shell_scale_update (GimpDisplayShell *shell); + +void gimp_display_shell_scale (GimpDisplayShell *shell, + GimpZoomType zoom_type, + gdouble scale, + GimpZoomFocus zoom_focus); +void gimp_display_shell_scale_to_rectangle (GimpDisplayShell *shell, + GimpZoomType zoom_type, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean resize_window); +void gimp_display_shell_scale_fit_in (GimpDisplayShell *shell); +void gimp_display_shell_scale_fill (GimpDisplayShell *shell); +void gimp_display_shell_scale_by_values (GimpDisplayShell *shell, + gdouble scale, + gint offset_x, + gint offset_y, + gboolean resize_window); + +void gimp_display_shell_scale_drag (GimpDisplayShell *shell, + gdouble start_x, + gdouble start_y, + gdouble delta_x, + gdouble delta_y); + +void gimp_display_shell_scale_shrink_wrap (GimpDisplayShell *shell, + gboolean grow_only); +void gimp_display_shell_scale_resize (GimpDisplayShell *shell, + gboolean resize_window, + gboolean grow_only); +void gimp_display_shell_set_initial_scale (GimpDisplayShell *shell, + gdouble scale, + gint *display_width, + gint *display_height); + +void gimp_display_shell_get_rotated_scale (GimpDisplayShell *shell, + gdouble *scale_x, + gdouble *scale_y); + +/* debug API for testing */ + +void gimp_display_shell_push_zoom_focus_pointer_pos (GimpDisplayShell *shell, + gint x, + gint y); + + +#endif /* __GIMP_DISPLAY_SHELL_SCALE_H__ */ diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c new file mode 100644 index 0000000..ec5084d --- /dev/null +++ b/app/display/gimpdisplayshell-scroll.c @@ -0,0 +1,529 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimpimage.h" + +#include "gimpcanvas.h" +#include "gimpdisplay.h" +#include "gimpdisplay-foreach.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-rulers.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-scrollbars.h" +#include "gimpdisplayshell-transform.h" + + +#define OVERPAN_FACTOR 0.5 + + +/** + * gimp_display_shell_scroll: + * @shell: + * @x_offset: + * @y_offset: + * + * This function scrolls the image in the shell's viewport. It does + * actual scrolling of the pixels, so only the newly scrolled-in parts + * are freshly redrawn. + * + * Use it for incremental actual panning. + **/ +void +gimp_display_shell_scroll (GimpDisplayShell *shell, + gint x_offset, + gint y_offset) +{ + gint old_x; + gint old_y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (x_offset == 0 && y_offset == 0) + return; + + old_x = shell->offset_x; + old_y = shell->offset_y; + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + shell->offset_x += x_offset; + shell->offset_y += y_offset; + + gimp_display_shell_scroll_clamp_and_update (shell); + + /* the actual changes in offset */ + x_offset = (shell->offset_x - old_x); + y_offset = (shell->offset_y - old_y); + + if (x_offset || y_offset) + { + gimp_display_shell_scrolled (shell); + + gimp_overlay_box_scroll (GIMP_OVERLAY_BOX (shell->canvas), + -x_offset, -y_offset); + + } + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +/** + * gimp_display_shell_scroll_set_offsets: + * @shell: + * @offset_x: + * @offset_y: + * + * This function scrolls the image in the shell's viewport. It redraws + * the entire canvas. + * + * Use it for setting the scroll offset on freshly scaled images or + * when the window is resized. For panning, use + * gimp_display_shell_scroll(). + **/ +void +gimp_display_shell_scroll_set_offset (GimpDisplayShell *shell, + gint offset_x, + gint offset_y) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->offset_x == offset_x && + shell->offset_y == offset_y) + return; + + gimp_display_shell_scale_save_revert_values (shell); + + /* freeze the active tool */ + gimp_display_shell_pause (shell); + + shell->offset_x = offset_x; + shell->offset_y = offset_y; + + gimp_display_shell_scroll_clamp_and_update (shell); + + gimp_display_shell_scrolled (shell); + + gimp_display_shell_expose_full (shell); + + /* re-enable the active tool */ + gimp_display_shell_resume (shell); +} + +/** + * gimp_display_shell_scroll_clamp_and_update: + * @shell: + * + * Helper function for calling two functions that are commonly called + * in pairs. + **/ +void +gimp_display_shell_scroll_clamp_and_update (GimpDisplayShell *shell) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + if (image) + { + if (! shell->show_all) + { + gint bounds_x; + gint bounds_y; + gint bounds_width; + gint bounds_height; + gint min_offset_x; + gint max_offset_x; + gint min_offset_y; + gint max_offset_y; + gint offset_x; + gint offset_y; + + gimp_display_shell_rotate_update_transform (shell); + + gimp_display_shell_scale_get_image_bounds (shell, + &bounds_x, + &bounds_y, + &bounds_width, + &bounds_height); + + if (shell->disp_width < bounds_width) + { + min_offset_x = bounds_x - + shell->disp_width * OVERPAN_FACTOR; + max_offset_x = bounds_x + bounds_width - + shell->disp_width * (1.0 - OVERPAN_FACTOR); + } + else + { + gint overpan_amount; + + overpan_amount = shell->disp_width - + bounds_width * (1.0 - OVERPAN_FACTOR); + + min_offset_x = bounds_x - + overpan_amount; + max_offset_x = bounds_x + bounds_width - shell->disp_width + + overpan_amount; + } + + if (shell->disp_height < bounds_height) + { + min_offset_y = bounds_y - + shell->disp_height * OVERPAN_FACTOR; + max_offset_y = bounds_y + bounds_height - + shell->disp_height * (1.0 - OVERPAN_FACTOR); + } + else + { + gint overpan_amount; + + overpan_amount = shell->disp_height - + bounds_height * (1.0 - OVERPAN_FACTOR); + + min_offset_y = bounds_y - + overpan_amount; + max_offset_y = bounds_y + bounds_height + + overpan_amount - shell->disp_height; + } + + /* Clamp */ + + offset_x = CLAMP (shell->offset_x, min_offset_x, max_offset_x); + offset_y = CLAMP (shell->offset_y, min_offset_y, max_offset_y); + + if (offset_x != shell->offset_x || offset_y != shell->offset_y) + { + shell->offset_x = offset_x; + shell->offset_y = offset_y; + + gimp_display_shell_rotate_update_transform (shell); + } + + /* Set scrollbar stepper sensitiity */ + + gimp_display_shell_scrollbars_update_steppers (shell, + min_offset_x, + max_offset_x, + min_offset_y, + max_offset_y); + } + else + { + /* Set scrollbar stepper sensitiity */ + + gimp_display_shell_scrollbars_update_steppers (shell, + G_MININT, + G_MAXINT, + G_MININT, + G_MAXINT); + } + } + else + { + shell->offset_x = 0; + shell->offset_y = 0; + } + + gimp_display_shell_scrollbars_update (shell); + gimp_display_shell_rulers_update (shell); +} + +/** + * gimp_display_shell_scroll_unoverscrollify: + * @shell: + * @in_offset_x: + * @in_offset_y: + * @out_offset_x: + * @out_offset_y: + * + * Takes a scroll offset and returns the offset that will not result + * in a scroll beyond the image border. If the image is already + * overscrolled, the return value is 0 for that given axis. + **/ +void +gimp_display_shell_scroll_unoverscrollify (GimpDisplayShell *shell, + gint in_offset_x, + gint in_offset_y, + gint *out_offset_x, + gint *out_offset_y) +{ + gint sw, sh; + gint out_offset_x_dummy, out_offset_y_dummy; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! out_offset_x) out_offset_x = &out_offset_x_dummy; + if (! out_offset_y) out_offset_y = &out_offset_y_dummy; + + *out_offset_x = in_offset_x; + *out_offset_y = in_offset_y; + + if (! shell->show_all) + { + gimp_display_shell_scale_get_image_size (shell, &sw, &sh); + + if (in_offset_x < 0) + { + *out_offset_x = MAX (in_offset_x, + MIN (0, 0 - shell->offset_x)); + } + else if (in_offset_x > 0) + { + gint min_offset = sw - shell->disp_width; + + *out_offset_x = MIN (in_offset_x, + MAX (0, min_offset - shell->offset_x)); + } + + if (in_offset_y < 0) + { + *out_offset_y = MAX (in_offset_y, + MIN (0, 0 - shell->offset_y)); + } + else if (in_offset_y > 0) + { + gint min_offset = sh - shell->disp_height; + + *out_offset_y = MIN (in_offset_y, + MAX (0, min_offset - shell->offset_y)); + } + } +} + +/** + * gimp_display_shell_scroll_center_image_xy: + * @shell: + * @image_x: + * @image_y: + * + * Center the viewport around the passed image coordinate + **/ +void +gimp_display_shell_scroll_center_image_xy (GimpDisplayShell *shell, + gdouble image_x, + gdouble image_y) +{ + gint viewport_x; + gint viewport_y; + + gimp_display_shell_transform_xy (shell, + image_x, image_y, + &viewport_x, &viewport_y); + + gimp_display_shell_scroll (shell, + viewport_x - shell->disp_width / 2, + viewport_y - shell->disp_height / 2); +} + +/** + * gimp_display_shell_scroll_center_image: + * @shell: + * @horizontally: + * @vertically: + * + * Centers the image in the display shell on the desired axes. + **/ +void +gimp_display_shell_scroll_center_image (GimpDisplayShell *shell, + gboolean horizontally, + gboolean vertically) +{ + gint image_x; + gint image_y; + gint image_width; + gint image_height; + gint center_x; + gint center_y; + gint offset_x = 0; + gint offset_y = 0; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display || + ! gimp_display_get_image (shell->display) || + (! vertically && ! horizontally)) + return; + + gimp_display_shell_scale_get_image_bounds (shell, + &image_x, &image_y, + &image_width, &image_height); + + if (shell->disp_width > image_width) + { + image_x -= (shell->disp_width - image_width) / 2; + image_width = shell->disp_width; + } + + if (shell->disp_height > image_height) + { + image_y -= (shell->disp_height - image_height) / 2; + image_height = shell->disp_height; + } + + center_x = image_x + image_width / 2; + center_y = image_y + image_height / 2; + + if (horizontally) + offset_x = center_x - shell->disp_width / 2 - shell->offset_x; + + if (vertically) + offset_y = center_y - shell->disp_height / 2 - shell->offset_y; + + gimp_display_shell_scroll (shell, offset_x, offset_y); +} + +/** + * gimp_display_shell_scroll_center_image: + * @shell: + * @horizontally: + * @vertically: + * + * Centers the image content in the display shell on the desired axes. + **/ +void +gimp_display_shell_scroll_center_content (GimpDisplayShell *shell, + gboolean horizontally, + gboolean vertically) +{ + gint content_x; + gint content_y; + gint content_width; + gint content_height; + gint center_x; + gint center_y; + gint offset_x = 0; + gint offset_y = 0; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display || + ! gimp_display_get_image (shell->display) || + (! vertically && ! horizontally)) + return; + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + gimp_display_shell_scale_get_image_bounds (shell, + &content_x, + &content_y, + &content_width, + &content_height); + } + else + { + gimp_display_shell_scale_get_image_bounding_box (shell, + &content_x, + &content_y, + &content_width, + &content_height); + } + + if (shell->disp_width > content_width) + { + content_x -= (shell->disp_width - content_width) / 2; + content_width = shell->disp_width; + } + + if (shell->disp_height > content_height) + { + content_y -= (shell->disp_height - content_height) / 2; + content_height = shell->disp_height; + } + + center_x = content_x + content_width / 2; + center_y = content_y + content_height / 2; + + if (horizontally) + offset_x = center_x - shell->disp_width / 2 - shell->offset_x; + + if (vertically) + offset_y = center_y - shell->disp_height / 2 - shell->offset_y; + + gimp_display_shell_scroll (shell, offset_x, offset_y); +} + +/** + * gimp_display_shell_scroll_get_scaled_viewport: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the viewport in screen coordinates, with origin at (0, 0) in + * the image. + **/ +void +gimp_display_shell_scroll_get_scaled_viewport (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + *x = shell->offset_x; + *y = shell->offset_y; + *w = shell->disp_width; + *h = shell->disp_height; +} + +/** + * gimp_display_shell_scroll_get_viewport: + * @shell: + * @x: + * @y: + * @w: + * @h: + * + * Gets the viewport in image coordinates. + **/ +void +gimp_display_shell_scroll_get_viewport (GimpDisplayShell *shell, + gdouble *x, + gdouble *y, + gdouble *w, + gdouble *h) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + *x = shell->offset_x / shell->scale_x; + *y = shell->offset_y / shell->scale_y; + *w = shell->disp_width / shell->scale_x; + *h = shell->disp_height / shell->scale_y; +} diff --git a/app/display/gimpdisplayshell-scroll.h b/app/display/gimpdisplayshell-scroll.h new file mode 100644 index 0000000..fba444b --- /dev/null +++ b/app/display/gimpdisplayshell-scroll.h @@ -0,0 +1,59 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_SCROLL_H__ +#define __GIMP_DISPLAY_SHELL_SCROLL_H__ + + +void gimp_display_shell_scroll (GimpDisplayShell *shell, + gint x_offset, + gint y_offset); +void gimp_display_shell_scroll_set_offset (GimpDisplayShell *shell, + gint offset_x, + gint offset_y); + +void gimp_display_shell_scroll_clamp_and_update (GimpDisplayShell *shell); + +void gimp_display_shell_scroll_unoverscrollify (GimpDisplayShell *shell, + gint in_offset_x, + gint in_offset_y, + gint *out_offset_x, + gint *out_offset_y); + +void gimp_display_shell_scroll_center_image_xy (GimpDisplayShell *shell, + gdouble image_x, + gdouble image_y); +void gimp_display_shell_scroll_center_image (GimpDisplayShell *shell, + gboolean horizontally, + gboolean vertically); +void gimp_display_shell_scroll_center_content (GimpDisplayShell *shell, + gboolean horizontally, + gboolean vertically); + +void gimp_display_shell_scroll_get_scaled_viewport (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *w, + gint *h); +void gimp_display_shell_scroll_get_viewport (GimpDisplayShell *shell, + gdouble *x, + gdouble *y, + gdouble *w, + gdouble *h); + + +#endif /* __GIMP_DISPLAY_SHELL_SCROLL_H__ */ diff --git a/app/display/gimpdisplayshell-scrollbars.c b/app/display/gimpdisplayshell-scrollbars.c new file mode 100644 index 0000000..3696e01 --- /dev/null +++ b/app/display/gimpdisplayshell-scrollbars.c @@ -0,0 +1,244 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scrollbars.h" + + +#define MINIMUM_STEP_AMOUNT 1.0 + + +/** + * gimp_display_shell_scrollbars_update: + * @shell: + * + **/ +void +gimp_display_shell_scrollbars_update (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display) + return; + + /* Horizontal scrollbar */ + + g_object_freeze_notify (G_OBJECT (shell->hsbdata)); + + /* Update upper and lower value before we set the new value */ + gimp_display_shell_scrollbars_setup_horizontal (shell, shell->offset_x); + + g_object_set (shell->hsbdata, + "value", (gdouble) shell->offset_x, + "page-size", (gdouble) shell->disp_width, + "page-increment", (gdouble) shell->disp_width / 2, + NULL); + + g_object_thaw_notify (G_OBJECT (shell->hsbdata)); /* emits "changed" */ + + + /* Vertcal scrollbar */ + + g_object_freeze_notify (G_OBJECT (shell->vsbdata)); + + /* Update upper and lower value before we set the new value */ + gimp_display_shell_scrollbars_setup_vertical (shell, shell->offset_y); + + g_object_set (shell->vsbdata, + "value", (gdouble) shell->offset_y, + "page-size", (gdouble) shell->disp_height, + "page-increment", (gdouble) shell->disp_height / 2, + NULL); + + g_object_thaw_notify (G_OBJECT (shell->vsbdata)); /* emits "changed" */ +} + +/** + * gimp_display_shell_scrollbars_setup_horizontal: + * @shell: + * @value: + * + * Setup the limits of the horizontal scrollbar + **/ +void +gimp_display_shell_scrollbars_setup_horizontal (GimpDisplayShell *shell, + gdouble value) +{ + gint bounds_x; + gint bounds_width; + gint bounding_box_x; + gint bounding_box_width; + gint x1; + gint x2; + gdouble lower; + gdouble upper; + gdouble scale_x; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display || ! gimp_display_get_image (shell->display)) + return; + + gimp_display_shell_scale_get_image_bounds (shell, + &bounds_x, NULL, + &bounds_width, NULL); + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + bounding_box_x = bounds_x; + bounding_box_width = bounds_width; + } + else + { + gimp_display_shell_scale_get_image_bounding_box ( + shell, + &bounding_box_x, NULL, + &bounding_box_width, NULL); + } + + x1 = bounding_box_x; + x2 = bounding_box_x + bounding_box_width; + + x1 = MIN (x1, bounds_x + bounds_width / 2 - shell->disp_width / 2); + x2 = MAX (x2, bounds_x + bounds_width / 2 + (shell->disp_width + 1) / 2); + + lower = MIN (value, x1); + upper = MAX (value + shell->disp_width, x2); + + gimp_display_shell_get_rotated_scale (shell, &scale_x, NULL); + + g_object_set (shell->hsbdata, + "lower", lower, + "upper", upper, + "step-increment", (gdouble) MAX (scale_x, MINIMUM_STEP_AMOUNT), + NULL); +} + +/** + * gimp_display_shell_scrollbars_setup_vertical: + * @shell: + * @value: + * + * Setup the limits of the vertical scrollbar + **/ +void +gimp_display_shell_scrollbars_setup_vertical (GimpDisplayShell *shell, + gdouble value) +{ + gint bounds_y; + gint bounds_height; + gint bounding_box_y; + gint bounding_box_height; + gint y1; + gint y2; + gdouble lower; + gdouble upper; + gdouble scale_y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (! shell->display || ! gimp_display_get_image (shell->display)) + return; + + gimp_display_shell_scale_get_image_bounds (shell, + NULL, &bounds_y, + NULL, &bounds_height); + + if (! gimp_display_shell_get_infinite_canvas (shell)) + { + bounding_box_y = bounds_y; + bounding_box_height = bounds_height; + } + else + { + gimp_display_shell_scale_get_image_bounding_box ( + shell, + NULL, &bounding_box_y, + NULL, &bounding_box_height); + } + + y1 = bounding_box_y; + y2 = bounding_box_y + bounding_box_height; + + y1 = MIN (y1, bounds_y + bounds_height / 2 - shell->disp_height / 2); + y2 = MAX (y2, bounds_y + bounds_height / 2 + (shell->disp_height + 1) / 2); + + lower = MIN (value, y1); + upper = MAX (value + shell->disp_height, y2); + + gimp_display_shell_get_rotated_scale (shell, NULL, &scale_y); + + g_object_set (shell->vsbdata, + "lower", lower, + "upper", upper, + "step-increment", (gdouble) MAX (scale_y, MINIMUM_STEP_AMOUNT), + NULL); +} + +/** + * gimp_display_shell_scrollbars_update_steppers: + * @shell: + * @min_offset_x: + * @max_offset_x: + * @min_offset_y: + * @max_offset_y: + * + * Sets the scrollbars' stepper sensitivity which is set differently + * from its adjustment limits because we support overscrolling. + **/ +void +gimp_display_shell_scrollbars_update_steppers (GimpDisplayShell *shell, + gint min_offset_x, + gint max_offset_x, + gint min_offset_y, + gint max_offset_y) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gtk_range_set_lower_stepper_sensitivity (GTK_RANGE (shell->hsb), + min_offset_x < shell->offset_x ? + GTK_SENSITIVITY_ON : + GTK_SENSITIVITY_OFF); + + gtk_range_set_upper_stepper_sensitivity (GTK_RANGE (shell->hsb), + max_offset_x > shell->offset_x ? + GTK_SENSITIVITY_ON : + GTK_SENSITIVITY_OFF); + + gtk_range_set_lower_stepper_sensitivity (GTK_RANGE (shell->vsb), + min_offset_y < shell->offset_y ? + GTK_SENSITIVITY_ON : + GTK_SENSITIVITY_OFF); + + gtk_range_set_upper_stepper_sensitivity (GTK_RANGE (shell->vsb), + max_offset_y > shell->offset_y ? + GTK_SENSITIVITY_ON : + GTK_SENSITIVITY_OFF); +} diff --git a/app/display/gimpdisplayshell-scrollbars.h b/app/display/gimpdisplayshell-scrollbars.h new file mode 100644 index 0000000..c15f802 --- /dev/null +++ b/app/display/gimpdisplayshell-scrollbars.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_SCROLLBARS_H__ +#define __GIMP_DISPLAY_SHELL_SCROLLBARS_H__ + + +void gimp_display_shell_scrollbars_update (GimpDisplayShell *shell); + +void gimp_display_shell_scrollbars_setup_horizontal (GimpDisplayShell *shell, + gdouble value); +void gimp_display_shell_scrollbars_setup_vertical (GimpDisplayShell *shell, + gdouble value); + +void gimp_display_shell_scrollbars_update_steppers (GimpDisplayShell *shell, + gint min_offset_x, + gint max_offset_x, + gint min_offset_y, + gint max_offset_y); + + +#endif /* __GIMP_DISPLAY_SHELL_SCROLLBARS_H__ */ diff --git a/app/display/gimpdisplayshell-selection.c b/app/display/gimpdisplayshell-selection.c new file mode 100644 index 0000000..3cfacd3 --- /dev/null +++ b/app/display/gimpdisplayshell-selection.c @@ -0,0 +1,504 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimp.h" +#include "core/gimp-cairo.h" +#include "core/gimpboundary.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-draw.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-selection.h" +#include "gimpdisplayshell-transform.h" + + +struct _Selection +{ + GimpDisplayShell *shell; /* shell that owns the selection */ + + GimpSegment *segs_in; /* gdk segments of area boundary */ + gint n_segs_in; /* number of segments in segs_in */ + + GimpSegment *segs_out; /* gdk segments of area boundary */ + gint n_segs_out; /* number of segments in segs_out */ + + guint index; /* index of current stipple pattern */ + gint paused; /* count of pause requests */ + gboolean shell_visible; /* visility of the display shell */ + gboolean show_selection; /* is the selection visible? */ + guint timeout; /* timer for successive draws */ + cairo_pattern_t *segs_in_mask; /* cache for rendered segments */ +}; + + +/* local function prototypes */ + +static void selection_start (Selection *selection); +static void selection_stop (Selection *selection); + +static void selection_undraw (Selection *selection); + +static void selection_render_mask (Selection *selection); + +static void selection_zoom_segs (Selection *selection, + const GimpBoundSeg *src_segs, + GimpSegment *dest_segs, + gint n_segs, + gint canvas_offset_x, + gint canvas_offset_y); +static void selection_generate_segs (Selection *selection); +static void selection_free_segs (Selection *selection); + +static gboolean selection_timeout (Selection *selection); + +static gboolean selection_window_state_event (GtkWidget *shell, + GdkEventWindowState *event, + Selection *selection); +static gboolean selection_visibility_notify_event (GtkWidget *shell, + GdkEventVisibility *event, + Selection *selection); + + +/* public functions */ + +void +gimp_display_shell_selection_init (GimpDisplayShell *shell) +{ + Selection *selection; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection == NULL); + + selection = g_slice_new0 (Selection); + + selection->shell = shell; + selection->shell_visible = TRUE; + selection->show_selection = gimp_display_shell_get_show_selection (shell); + + shell->selection = selection; + shell->selection_update = g_get_monotonic_time (); + + g_signal_connect (shell, "window-state-event", + G_CALLBACK (selection_window_state_event), + selection); + g_signal_connect (shell, "visibility-notify-event", + G_CALLBACK (selection_visibility_notify_event), + selection); +} + +void +gimp_display_shell_selection_free (GimpDisplayShell *shell) +{ + Selection *selection; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + selection = shell->selection; + + selection_stop (selection); + + g_signal_handlers_disconnect_by_func (shell, + selection_window_state_event, + selection); + g_signal_handlers_disconnect_by_func (shell, + selection_visibility_notify_event, + selection); + + selection_free_segs (selection); + + g_slice_free (Selection, selection); + + shell->selection = NULL; +} + +void +gimp_display_shell_selection_undraw (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + if (gimp_display_get_image (shell->display)) + { + selection_undraw (shell->selection); + } + else + { + selection_stop (shell->selection); + selection_free_segs (shell->selection); + } +} + +void +gimp_display_shell_selection_restart (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + if (gimp_display_get_image (shell->display)) + { + selection_start (shell->selection); + } +} + +void +gimp_display_shell_selection_pause (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + if (gimp_display_get_image (shell->display)) + { + if (shell->selection->paused == 0) + selection_stop (shell->selection); + + shell->selection->paused++; + } +} + +void +gimp_display_shell_selection_resume (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + if (gimp_display_get_image (shell->display)) + { + shell->selection->paused--; + + if (shell->selection->paused == 0) + selection_start (shell->selection); + } +} + +void +gimp_display_shell_selection_set_show (GimpDisplayShell *shell, + gboolean show) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->selection != NULL); + + if (gimp_display_get_image (shell->display)) + { + Selection *selection = shell->selection; + + if (show != selection->show_selection) + { + selection_undraw (selection); + + selection->show_selection = show; + + selection_start (selection); + } + } +} + + +/* private functions */ + +static void +selection_start (Selection *selection) +{ + selection_stop (selection); + + /* If this selection is paused, do not start it */ + if (selection->paused == 0 && + gimp_display_get_image (selection->shell->display) && + selection->show_selection) + { + /* Draw the ants once */ + selection_timeout (selection); + + if (selection->segs_in && selection->shell_visible) + { + GimpDisplayConfig *config = selection->shell->display->config; + + selection->timeout = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, + config->marching_ants_speed, + (GSourceFunc) selection_timeout, + selection, NULL); + } + } +} + +static void +selection_stop (Selection *selection) +{ + if (selection->timeout) + { + g_source_remove (selection->timeout); + selection->timeout = 0; + } +} + +void +gimp_display_shell_selection_draw (GimpDisplayShell *shell, + cairo_t *cr) +{ + if (gimp_display_get_image (shell->display) && + shell->selection && shell->selection->show_selection) + { + GimpDisplayConfig *config = shell->display->config; + gint64 time = g_get_monotonic_time (); + + if ((time - shell->selection_update) / 1000 > config->marching_ants_speed && + shell->selection->paused == 0) + { + shell->selection_update = time; + shell->selection->index++; + } + + selection_generate_segs (shell->selection); + + if (shell->selection->segs_in) + { + gimp_display_shell_draw_selection_in (shell->selection->shell, cr, + shell->selection->segs_in_mask, + shell->selection->index % 8); + } + + if (shell->selection->segs_out) + { + if (shell->selection->shell->rotate_transform) + cairo_transform (cr, shell->selection->shell->rotate_transform); + + gimp_display_shell_draw_selection_out (shell->selection->shell, cr, + shell->selection->segs_out, + shell->selection->n_segs_out); + } + } +} + +static void +selection_undraw (Selection *selection) +{ + gint x, y, w, h; + + selection_stop (selection); + + if (gimp_display_shell_mask_bounds (selection->shell, &x, &y, &w, &h)) + { + /* expose will restart the selection */ + gimp_display_shell_expose_area (selection->shell, x, y, w, h); + } + else + { + selection_start (selection); + } +} + +static void +selection_render_mask (Selection *selection) +{ + GdkWindow *window; + cairo_surface_t *surface; + cairo_t *cr; + + window = gtk_widget_get_window (GTK_WIDGET (selection->shell)); + surface = gdk_window_create_similar_surface (window, CAIRO_CONTENT_ALPHA, + gdk_window_get_width (window), + gdk_window_get_height (window)); + cr = cairo_create (surface); + + cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); + cairo_set_line_width (cr, 1.0); + + if (selection->shell->rotate_transform) + cairo_transform (cr, selection->shell->rotate_transform); + + gimp_cairo_segments (cr, + selection->segs_in, + selection->n_segs_in); + cairo_stroke (cr); + + selection->segs_in_mask = cairo_pattern_create_for_surface (surface); + + cairo_destroy (cr); + cairo_surface_destroy (surface); +} + +static void +selection_zoom_segs (Selection *selection, + const GimpBoundSeg *src_segs, + GimpSegment *dest_segs, + gint n_segs, + gint canvas_offset_x, + gint canvas_offset_y) +{ + const gint xclamp = selection->shell->disp_width + 1; + const gint yclamp = selection->shell->disp_height + 1; + gint i; + + gimp_display_shell_zoom_segments (selection->shell, + src_segs, dest_segs, n_segs, + 0.0, 0.0); + + for (i = 0; i < n_segs; i++) + { + if (! selection->shell->rotate_transform) + { + dest_segs[i].x1 = CLAMP (dest_segs[i].x1, -1, xclamp) + canvas_offset_x; + dest_segs[i].y1 = CLAMP (dest_segs[i].y1, -1, yclamp) + canvas_offset_y; + + dest_segs[i].x2 = CLAMP (dest_segs[i].x2, -1, xclamp) + canvas_offset_x; + dest_segs[i].y2 = CLAMP (dest_segs[i].y2, -1, yclamp) + canvas_offset_y; + } + + /* If this segment is a closing segment && the segments lie inside + * the region, OR if this is an opening segment and the segments + * lie outside the region... + * we need to transform it by one display pixel + */ + if (! src_segs[i].open) + { + /* If it is vertical */ + if (dest_segs[i].x1 == dest_segs[i].x2) + { + dest_segs[i].x1 -= 1; + dest_segs[i].x2 -= 1; + } + else + { + dest_segs[i].y1 -= 1; + dest_segs[i].y2 -= 1; + } + } + } +} + +static void +selection_generate_segs (Selection *selection) +{ + GimpImage *image = gimp_display_get_image (selection->shell->display); + const GimpBoundSeg *segs_in; + const GimpBoundSeg *segs_out; + gint canvas_offset_x = 0; + gint canvas_offset_y = 0; + + selection_free_segs (selection); + + /* Ask the image for the boundary of its selected region... + * Then transform that information into a new buffer of GimpSegments + */ + gimp_channel_boundary (gimp_image_get_mask (image), + &segs_in, &segs_out, + &selection->n_segs_in, &selection->n_segs_out, + 0, 0, 0, 0); + + if (selection->n_segs_in) + { + selection->segs_in = g_new (GimpSegment, selection->n_segs_in); + selection_zoom_segs (selection, segs_in, + selection->segs_in, selection->n_segs_in, + canvas_offset_x, canvas_offset_y); + + selection_render_mask (selection); + } + + /* Possible secondary boundary representation */ + if (selection->n_segs_out) + { + selection->segs_out = g_new (GimpSegment, selection->n_segs_out); + selection_zoom_segs (selection, segs_out, + selection->segs_out, selection->n_segs_out, + canvas_offset_x, canvas_offset_y); + } +} + +static void +selection_free_segs (Selection *selection) +{ + g_clear_pointer (&selection->segs_in, g_free); + selection->n_segs_in = 0; + + g_clear_pointer (&selection->segs_out, g_free); + selection->n_segs_out = 0; + + g_clear_pointer (&selection->segs_in_mask, cairo_pattern_destroy); +} + +static gboolean +selection_timeout (Selection *selection) +{ + GimpDisplayConfig *config = selection->shell->display->config; + gint64 time = g_get_monotonic_time (); + + if ((time - selection->shell->selection_update) / 1000 > config->marching_ants_speed) + { + GdkWindow *window; + + window = gtk_widget_get_window (GTK_WIDGET (selection->shell)); + + gtk_widget_queue_draw_area (GTK_WIDGET (selection->shell), + 0, 0, + gdk_window_get_width (window), + gdk_window_get_height (window)); + } + + return G_SOURCE_CONTINUE; +} + +static void +selection_set_shell_visible (Selection *selection, + gboolean shell_visible) +{ + if (selection->shell_visible != shell_visible) + { + selection->shell_visible = shell_visible; + + if (shell_visible) + selection_start (selection); + else + selection_stop (selection); + } +} + +static gboolean +selection_window_state_event (GtkWidget *shell, + GdkEventWindowState *event, + Selection *selection) +{ + selection_set_shell_visible (selection, + (event->new_window_state & (GDK_WINDOW_STATE_WITHDRAWN | + GDK_WINDOW_STATE_ICONIFIED)) == 0); + + return FALSE; +} + +static gboolean +selection_visibility_notify_event (GtkWidget *shell, + GdkEventVisibility *event, + Selection *selection) +{ + selection_set_shell_visible (selection, + event->state != GDK_VISIBILITY_FULLY_OBSCURED); + + return FALSE; +} diff --git a/app/display/gimpdisplayshell-selection.h b/app/display/gimpdisplayshell-selection.h new file mode 100644 index 0000000..c7b0a5f --- /dev/null +++ b/app/display/gimpdisplayshell-selection.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_SELECTION_H__ +#define __GIMP_DISPLAY_SHELL_SELECTION_H__ + + +void gimp_display_shell_selection_init (GimpDisplayShell *shell); +void gimp_display_shell_selection_free (GimpDisplayShell *shell); + +void gimp_display_shell_selection_undraw (GimpDisplayShell *shell); +void gimp_display_shell_selection_restart (GimpDisplayShell *shell); + +void gimp_display_shell_selection_pause (GimpDisplayShell *shell); +void gimp_display_shell_selection_resume (GimpDisplayShell *shell); + +void gimp_display_shell_selection_set_show (GimpDisplayShell *shell, + gboolean show); + +void gimp_display_shell_selection_draw (GimpDisplayShell *shell, + cairo_t *cr); + +#endif /* __GIMP_DISPLAY_SHELL_SELECTION_H__ */ diff --git a/app/display/gimpdisplayshell-title.c b/app/display/gimpdisplayshell-title.c new file mode 100644 index 0000000..ff356ce --- /dev/null +++ b/app/display/gimpdisplayshell-title.c @@ -0,0 +1,564 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "libgimpbase/gimpbase.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimpcontainer.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimpimage-color-profile.h" +#include "core/gimpitem.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-title.h" +#include "gimpstatusbar.h" + +#include "about.h" + +#include "gimp-intl.h" + + +#define MAX_TITLE_BUF 512 + + +static gboolean gimp_display_shell_update_title_idle (gpointer data); +static gint gimp_display_shell_format_title (GimpDisplayShell *display, + gchar *title, + gint title_len, + const gchar *format); + + +/* public functions */ + +void +gimp_display_shell_title_update (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->title_idle_id) + g_source_remove (shell->title_idle_id); + + shell->title_idle_id = g_idle_add (gimp_display_shell_update_title_idle, + shell); +} + + +/* private functions */ + +static gboolean +gimp_display_shell_update_title_idle (gpointer data) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data); + + shell->title_idle_id = 0; + + if (gimp_display_get_image (shell->display)) + { + GimpDisplayConfig *config = shell->display->config; + gchar title[MAX_TITLE_BUF]; + gchar status[MAX_TITLE_BUF]; + gint len; + + /* format the title */ + len = gimp_display_shell_format_title (shell, title, sizeof (title), + config->image_title_format); + + if (len) /* U+2013 EN DASH */ + len += g_strlcpy (title + len, " \342\200\223 ", sizeof (title) - len); + + g_strlcpy (title + len, GIMP_ACRONYM, sizeof (title) - len); + + /* format the statusbar */ + gimp_display_shell_format_title (shell, status, sizeof (status), + config->image_status_format); + + g_object_set (shell, + "title", title, + "status", status, + NULL); + } + else + { + g_object_set (shell, + "title", GIMP_NAME, + "status", " ", + NULL); + } + + return FALSE; +} + +static const gchar * +gimp_display_shell_title_image_type (GimpImage *image) +{ + const gchar *name = ""; + + gimp_enum_get_value (GIMP_TYPE_IMAGE_BASE_TYPE, + gimp_image_get_base_type (image), NULL, NULL, &name, NULL); + + return name; +} + +static const gchar * +gimp_display_shell_title_image_precision (GimpImage *image) +{ + const gchar *name = ""; + + gimp_enum_get_value (GIMP_TYPE_PRECISION, + gimp_image_get_precision (image), NULL, NULL, &name, NULL); + + return name; +} + +static gint print (gchar *buf, + gint len, + gint start, + const gchar *fmt, + ...) G_GNUC_PRINTF (4, 5); + +static gint +print (gchar *buf, + gint len, + gint start, + const gchar *fmt, + ...) +{ + va_list args; + gint printed; + + va_start (args, fmt); + + printed = g_vsnprintf (buf + start, len - start, fmt, args); + if (printed < 0) + printed = len - start; + + va_end (args); + + return printed; +} + +static gint +gimp_display_shell_format_title (GimpDisplayShell *shell, + gchar *title, + gint title_len, + const gchar *format) +{ + GimpImage *image; + GimpDrawable *drawable; + gint num, denom; + gint i = 0; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), 0); + + image = gimp_display_get_image (shell->display); + + if (! image) + { + title[0] = '\n'; + return 0; + } + + drawable = gimp_image_get_active_drawable (image); + + gimp_zoom_model_get_fraction (shell->zoom, &num, &denom); + + while (i < title_len && *format) + { + switch (*format) + { + case '%': + format++; + switch (*format) + { + case 0: + /* format string ends within %-sequence, print literal '%' */ + + case '%': + title[i++] = '%'; + break; + + case 'f': /* base filename */ + i += print (title, title_len, i, "%s", + gimp_image_get_display_name (image)); + break; + + case 'F': /* full filename */ + i += print (title, title_len, i, "%s", + gimp_image_get_display_path (image)); + break; + + case 'p': /* PDB id */ + i += print (title, title_len, i, "%d", gimp_image_get_ID (image)); + break; + + case 'i': /* instance */ + i += print (title, title_len, i, "%d", + gimp_display_get_instance (shell->display)); + break; + + case 't': /* image type */ + i += print (title, title_len, i, "%s %s", + gimp_display_shell_title_image_type (image), + gimp_display_shell_title_image_precision (image)); + break; + + case 'T': /* drawable type */ + if (drawable) + { + const Babl *format = gimp_drawable_get_format (drawable); + + i += print (title, title_len, i, "%s", + gimp_babl_format_get_description (format)); + } + break; + + case 's': /* user source zoom factor */ + i += print (title, title_len, i, "%d", denom); + break; + + case 'd': /* user destination zoom factor */ + i += print (title, title_len, i, "%d", num); + break; + + case 'z': /* user zoom factor (percentage) */ + { + gdouble scale = gimp_zoom_model_get_factor (shell->zoom); + + i += print (title, title_len, i, + scale >= 0.15 ? "%.0f" : "%.2f", 100.0 * scale); + } + break; + + case 'D': /* dirty flag */ + if (format[1] == 0) + { + /* format string ends within %D-sequence, print literal '%D' */ + i += print (title, title_len, i, "%%D"); + break; + } + if (gimp_image_is_dirty (image)) + title[i++] = format[1]; + format++; + break; + + case 'C': /* clean flag */ + if (format[1] == 0) + { + /* format string ends within %C-sequence, print literal '%C' */ + i += print (title, title_len, i, "%%C"); + break; + } + if (! gimp_image_is_dirty (image)) + title[i++] = format[1]; + format++; + break; + + case 'B': /* dirty flag (long) */ + if (gimp_image_is_dirty (image)) + i += print (title, title_len, i, "%s", _("(modified)")); + break; + + case 'A': /* clean flag (long) */ + if (! gimp_image_is_dirty (image)) + i += print (title, title_len, i, "%s", _("(clean)")); + break; + + case 'N': /* not-exported flag */ + if (format[1] == 0) + { + /* format string ends within %E-sequence, print literal '%E' */ + i += print (title, title_len, i, "%%N"); + break; + } + if (gimp_image_is_export_dirty (image)) + title[i++] = format[1]; + format++; + break; + + case 'E': /* exported flag */ + if (format[1] == 0) + { + /* format string ends within %E-sequence, print literal '%E' */ + i += print (title, title_len, i, "%%E"); + break; + } + if (! gimp_image_is_export_dirty (image)) + title[i++] = format[1]; + format++; + break; + + case 'm': /* memory used by image */ + { + GimpObject *object = GIMP_OBJECT (image); + gchar *str; + + str = g_format_size (gimp_object_get_memsize (object, NULL)); + i += print (title, title_len, i, "%s", str); + g_free (str); + } + break; + + case 'M': /* image size in megapixels */ + i += print (title, title_len, i, "%.1f", + (gdouble) gimp_image_get_width (image) * + (gdouble) gimp_image_get_height (image) / 1000000.0); + break; + + case 'l': /* number of layers */ + i += print (title, title_len, i, "%d", + gimp_image_get_n_layers (image)); + break; + + case 'L': /* number of layers (long) */ + { + gint num = gimp_image_get_n_layers (image); + + i += print (title, title_len, i, + ngettext ("%d layer", "%d layers", num), num); + } + break; + + case 'n': /* active drawable name */ + if (drawable) + { + gchar *desc; + + desc = gimp_viewable_get_description (GIMP_VIEWABLE (drawable), + NULL); + i += print (title, title_len, i, "%s", desc); + g_free (desc); + } + else + { + i += print (title, title_len, i, "%s", _("(none)")); + } + break; + + case 'P': /* active drawable PDB id */ + if (drawable) + i += print (title, title_len, i, "%d", + gimp_item_get_ID (GIMP_ITEM (drawable))); + else + i += print (title, title_len, i, "%s", _("(none)")); + break; + + case 'W': /* width in real-world units */ + if (shell->unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gchar unit_format[8]; + + gimp_image_get_resolution (image, &xres, &yres); + + g_snprintf (unit_format, sizeof (unit_format), "%%.%df", + gimp_unit_get_scaled_digits (shell->unit, xres)); + i += print (title, title_len, i, unit_format, + gimp_pixels_to_units (gimp_image_get_width (image), + shell->unit, xres)); + break; + } + /* else fallthru */ + + case 'w': /* width in pixels */ + i += print (title, title_len, i, "%d", + gimp_image_get_width (image)); + break; + + case 'H': /* height in real-world units */ + if (shell->unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gchar unit_format[8]; + + gimp_image_get_resolution (image, &xres, &yres); + + g_snprintf (unit_format, sizeof (unit_format), "%%.%df", + gimp_unit_get_scaled_digits (shell->unit, yres)); + i += print (title, title_len, i, unit_format, + gimp_pixels_to_units (gimp_image_get_height (image), + shell->unit, yres)); + break; + } + /* else fallthru */ + + case 'h': /* height in pixels */ + i += print (title, title_len, i, "%d", + gimp_image_get_height (image)); + break; + + case 'u': /* unit symbol */ + i += print (title, title_len, i, "%s", + gimp_unit_get_symbol (shell->unit)); + break; + + case 'U': /* unit abbreviation */ + i += print (title, title_len, i, "%s", + gimp_unit_get_abbreviation (shell->unit)); + break; + + case 'X': /* drawable width in real world units */ + if (drawable && shell->unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gchar unit_format[8]; + + gimp_image_get_resolution (image, &xres, &yres); + + g_snprintf (unit_format, sizeof (unit_format), "%%.%df", + gimp_unit_get_scaled_digits (shell->unit, xres)); + i += print (title, title_len, i, unit_format, + gimp_pixels_to_units (gimp_item_get_width + (GIMP_ITEM (drawable)), + shell->unit, xres)); + break; + } + /* else fallthru */ + + case 'x': /* drawable width in pixels */ + if (drawable) + i += print (title, title_len, i, "%d", + gimp_item_get_width (GIMP_ITEM (drawable))); + break; + + case 'Y': /* drawable height in real world units */ + if (drawable && shell->unit != GIMP_UNIT_PIXEL) + { + gdouble xres; + gdouble yres; + gchar unit_format[8]; + + gimp_image_get_resolution (image, &xres, &yres); + + g_snprintf (unit_format, sizeof (unit_format), "%%.%df", + gimp_unit_get_scaled_digits (shell->unit, yres)); + i += print (title, title_len, i, unit_format, + gimp_pixels_to_units (gimp_item_get_height + (GIMP_ITEM (drawable)), + shell->unit, yres)); + break; + } + /* else fallthru */ + + case 'y': /* drawable height in pixels */ + if (drawable) + i += print (title, title_len, i, "%d", + gimp_item_get_height (GIMP_ITEM (drawable))); + break; + + case 'o': /* image's color profile name */ + if (gimp_image_get_is_color_managed (image)) + { + GimpColorManaged *managed = GIMP_COLOR_MANAGED (image); + GimpColorProfile *profile; + + profile = gimp_color_managed_get_color_profile (managed); + + i += print (title, title_len, i, "%s", + gimp_color_profile_get_label (profile)); + } + else + { + i += print (title, title_len, i, "%s", + _("not color managed")); + } + break; + + case 'e': /* display's offsets in pixels */ + { + gdouble scale = gimp_zoom_model_get_factor (shell->zoom); + gdouble offset_x = shell->offset_x / scale; + gdouble offset_y = shell->offset_y / scale; + + i += print (title, title_len, i, + scale >= 0.15 ? "%.0fx%.0f" : "%.2fx%.2f", + offset_x, offset_y); + } + break; + + case 'r': /* view rotation angle in degrees */ + { + i += print (title, title_len, i, "%.1f", shell->rotate_angle); + } + break; + + case '\xc3': /* utf-8 extended char */ + { + format ++; + switch (*format) + { + case '\xbe': + /* line actually written at 23:55 on an Easter Sunday */ + i += print (title, title_len, i, "42"); + break; + + default: + /* in the case of an unhandled utf-8 extended char format + * leave the format string parsing as it was + */ + format--; + break; + } + } + break; + + /* Other cool things to be added: + * %r = xresolution + * %R = yresolution + * %ø = image's fractal dimension + * %þ = the answer to everything - (implemented) + */ + + default: + /* format string contains unknown %-sequence, print it literally */ + i += print (title, title_len, i, "%%%c", *format); + break; + } + break; + + default: + title[i++] = *format; + break; + } + + format++; + } + + title[MIN (i, title_len - 1)] = '\0'; + + return i; +} diff --git a/app/display/gimpdisplayshell-title.h b/app/display/gimpdisplayshell-title.h new file mode 100644 index 0000000..0a3ed21 --- /dev/null +++ b/app/display/gimpdisplayshell-title.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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_TITLE_H__ +#define __GIMP_DISPLAY_SHELL_TITLE_H__ + + +void gimp_display_shell_title_update (GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_TITLE_H__ */ diff --git a/app/display/gimpdisplayshell-tool-events.c b/app/display/gimpdisplayshell-tool-events.c new file mode 100644 index 0000000..714f39f --- /dev/null +++ b/app/display/gimpdisplayshell-tool-events.c @@ -0,0 +1,2144 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" +#include "tools/tools-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimp.h" +#include "core/gimp-filter-history.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-pick-item.h" +#include "core/gimpitem.h" + +#include "widgets/gimpcontrollers.h" +#include "widgets/gimpcontrollerkeyboard.h" +#include "widgets/gimpcontrollermouse.h" +#include "widgets/gimpcontrollerwheel.h" +#include "widgets/gimpdeviceinfo.h" +#include "widgets/gimpdeviceinfo-coords.h" +#include "widgets/gimpdevicemanager.h" +#include "widgets/gimpdevices.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "tools/gimpguidetool.h" +#include "tools/gimpmovetool.h" +#include "tools/gimpsamplepointtool.h" +#include "tools/gimptoolcontrol.h" +#include "tools/tool_manager.h" + +#include "gimpcanvas.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-autoscroll.h" +#include "gimpdisplayshell-cursor.h" +#include "gimpdisplayshell-grab.h" +#include "gimpdisplayshell-layer-select.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-tool-events.h" +#include "gimpdisplayshell-transform.h" +#include "gimpimagewindow.h" +#include "gimpmotionbuffer.h" +#include "gimpstatusbar.h" + +#include "gimp-intl.h" +#include "gimp-log.h" + + +/* local function prototypes */ + +static gboolean gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas, + GdkEvent *event, + GimpDisplayShell *shell, + GdkEvent **next_event); + +static GdkModifierType + gimp_display_shell_key_to_state (gint key); +static GdkModifierType + gimp_display_shell_button_to_state (gint button); + +static void gimp_display_shell_proximity_in (GimpDisplayShell *shell); +static void gimp_display_shell_proximity_out (GimpDisplayShell *shell); + +static void gimp_display_shell_check_device_cursor (GimpDisplayShell *shell); + +static void gimp_display_shell_start_scrolling (GimpDisplayShell *shell, + const GdkEvent *event, + GdkModifierType state, + gint x, + gint y); +static void gimp_display_shell_stop_scrolling (GimpDisplayShell *shell, + const GdkEvent *event); +static void gimp_display_shell_handle_scrolling (GimpDisplayShell *shell, + GdkModifierType state, + gint x, + gint y); + +static void gimp_display_shell_space_pressed (GimpDisplayShell *shell, + const GdkEvent *event); +static void gimp_display_shell_released (GimpDisplayShell *shell, + const GdkEvent *event, + const GimpCoords *image_coords); + +static gboolean gimp_display_shell_tab_pressed (GimpDisplayShell *shell, + const GdkEventKey *event); + +static void gimp_display_shell_update_focus (GimpDisplayShell *shell, + gboolean focus_in, + const GimpCoords *image_coords, + GdkModifierType state); +static void gimp_display_shell_update_cursor (GimpDisplayShell *shell, + const GimpCoords *display_coords, + const GimpCoords *image_coords, + GdkModifierType state, + gboolean update_software_cursor); + +static gboolean gimp_display_shell_initialize_tool (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GdkModifierType state); + +static void gimp_display_shell_get_event_coords (GimpDisplayShell *shell, + const GdkEvent *event, + GimpCoords *display_coords, + GdkModifierType *state, + guint32 *time); +static void gimp_display_shell_untransform_event_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords, + gboolean *update_software_cursor); + +static GdkEvent * gimp_display_shell_compress_motion (GdkEvent *initial_event, + GdkEvent **next_event); + + +/* public functions */ + +gboolean +gimp_display_shell_events (GtkWidget *widget, + GdkEvent *event, + GimpDisplayShell *shell) +{ + Gimp *gimp; + gboolean set_display = FALSE; + + /* are we in destruction? */ + if (! shell->display || ! gimp_display_get_shell (shell->display)) + return TRUE; + + gimp = gimp_display_get_gimp (shell->display); + + switch (event->type) + { + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + { + GdkEventKey *kevent = (GdkEventKey *) event; + + if (gimp->busy) + return TRUE; + + /* do not process most key events while BUTTON1 is down. We do this + * so tools keep the modifier state they were in when BUTTON1 was + * pressed and to prevent accelerators from being invoked. + */ + if (kevent->state & GDK_BUTTON1_MASK) + { + if (kevent->keyval == GDK_KEY_Shift_L || + kevent->keyval == GDK_KEY_Shift_R || + kevent->keyval == GDK_KEY_Control_L || + kevent->keyval == GDK_KEY_Control_R || + kevent->keyval == GDK_KEY_Alt_L || + kevent->keyval == GDK_KEY_Alt_R || + kevent->keyval == GDK_KEY_Meta_L || + kevent->keyval == GDK_KEY_Meta_R || + kevent->keyval == GDK_KEY_space || + kevent->keyval == GDK_KEY_KP_Space) + { + break; + } + + return TRUE; + } + + switch (kevent->keyval) + { + case GDK_KEY_Left: case GDK_KEY_Right: + case GDK_KEY_Up: case GDK_KEY_Down: + case GDK_KEY_space: + case GDK_KEY_KP_Space: + case GDK_KEY_Tab: + case GDK_KEY_KP_Tab: + case GDK_KEY_ISO_Left_Tab: + case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: + case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: + case GDK_KEY_Control_L: case GDK_KEY_Control_R: + case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + case GDK_KEY_BackSpace: + case GDK_KEY_Escape: + break; + + default: + if (shell->space_release_pending || + shell->button1_release_pending || + shell->scrolling) + return TRUE; + break; + } + + set_display = TRUE; + break; + } + + case GDK_BUTTON_PRESS: + case GDK_SCROLL: + set_display = TRUE; + break; + + case GDK_FOCUS_CHANGE: + { + GdkEventFocus *fevent = (GdkEventFocus *) event; + + if (fevent->in && shell->display->config->activate_on_focus) + set_display = TRUE; + } + break; + + default: + break; + } + + /* Setting the context's display automatically sets the image, too */ + if (set_display) + gimp_context_set_display (gimp_get_user_context (gimp), shell->display); + + return FALSE; +} + +static gboolean +gimp_display_shell_canvas_no_image_events (GtkWidget *canvas, + GdkEvent *event, + GimpDisplayShell *shell) +{ + switch (event->type) + { + case GDK_2BUTTON_PRESS: + { + GdkEventButton *bevent = (GdkEventButton *) event; + if (bevent->button == 1) + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_activate_action (manager, "file", "file-open"); + } + return TRUE; + } + break; + + case GDK_BUTTON_PRESS: + if (gdk_event_triggers_context_menu (event)) + { + gimp_ui_manager_ui_popup (shell->popup_manager, + "/dummy-menubar/image-popup", + GTK_WIDGET (shell), + NULL, NULL, NULL, NULL); + return TRUE; + } + break; + + case GDK_KEY_PRESS: + { + GdkEventKey *kevent = (GdkEventKey *) event; + + if (kevent->keyval == GDK_KEY_Tab || + kevent->keyval == GDK_KEY_KP_Tab || + kevent->keyval == GDK_KEY_ISO_Left_Tab) + { + return gimp_display_shell_tab_pressed (shell, kevent); + } + } + break; + + default: + break; + } + + return FALSE; +} + +gboolean +gimp_display_shell_canvas_tool_events (GtkWidget *canvas, + GdkEvent *event, + GimpDisplayShell *shell) +{ + GdkEvent *next_event = NULL; + gboolean return_val; + + g_return_val_if_fail (gtk_widget_get_realized (canvas), FALSE); + + return_val = gimp_display_shell_canvas_tool_events_internal (canvas, + event, shell, + &next_event); + + if (next_event) + { + gtk_main_do_event (next_event); + + gdk_event_free (next_event); + } + + return return_val; +} + +void +gimp_display_shell_canvas_grab_notify (GtkWidget *canvas, + gboolean was_grabbed, + GimpDisplayShell *shell) +{ + GimpDisplay *display; + GimpImage *image; + Gimp *gimp; + + /* are we in destruction? */ + if (! shell->display || ! gimp_display_get_shell (shell->display)) + return; + + display = shell->display; + gimp = gimp_display_get_gimp (display); + image = gimp_display_get_image (display); + + if (! image) + return; + + GIMP_LOG (TOOL_EVENTS, "grab_notify (display %p): was_grabbed = %s", + display, was_grabbed ? "TRUE" : "FALSE"); + + if (! was_grabbed) + { + if (! gimp_image_is_empty (image)) + { + GimpTool *active_tool = tool_manager_get_active (gimp); + + if (active_tool && active_tool->focus_display == display) + { + tool_manager_modifier_state_active (gimp, 0, display); + } + } + } +} + +void +gimp_display_shell_buffer_stroke (GimpMotionBuffer *buffer, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplayShell *shell) +{ + GimpDisplay *display = shell->display; + Gimp *gimp = gimp_display_get_gimp (display); + GimpTool *active_tool; + + active_tool = tool_manager_get_active (gimp); + + if (active_tool && + gimp_tool_control_is_active (active_tool->control)) + { + tool_manager_motion_active (gimp, + coords, time, state, + display); + } +} + +void +gimp_display_shell_buffer_hover (GimpMotionBuffer *buffer, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplayShell *shell) +{ + GimpDisplay *display = shell->display; + Gimp *gimp = gimp_display_get_gimp (display); + GimpTool *active_tool; + + active_tool = tool_manager_get_active (gimp); + + if (active_tool && + ! gimp_tool_control_is_active (active_tool->control)) + { + tool_manager_oper_update_active (gimp, + coords, state, proximity, + display); + } +} + +static gboolean +gimp_display_shell_ruler_button_press (GtkWidget *widget, + GdkEventButton *event, + GimpDisplayShell *shell, + GimpOrientationType orientation) +{ + GimpDisplay *display = shell->display; + + if (display->gimp->busy) + return TRUE; + + if (! gimp_display_get_image (display)) + return TRUE; + + if (event->type == GDK_BUTTON_PRESS && event->button == 1) + { + GimpTool *active_tool = tool_manager_get_active (display->gimp); + + if (active_tool) + { + gimp_display_shell_update_focus (shell, TRUE, + NULL, event->state); + + if (gimp_display_shell_pointer_grab (shell, NULL, 0)) + { + if (gimp_display_shell_keyboard_grab (shell, + (GdkEvent *) event)) + { + if (event->state & gimp_get_toggle_behavior_mask ()) + { + gimp_sample_point_tool_start_new (active_tool, display); + } + else + { + gimp_guide_tool_start_new (active_tool, display, + orientation); + } + + return TRUE; + } + else + { + gimp_display_shell_pointer_ungrab (shell, NULL); + } + } + } + } + + return FALSE; +} + +gboolean +gimp_display_shell_hruler_button_press (GtkWidget *widget, + GdkEventButton *event, + GimpDisplayShell *shell) +{ + return gimp_display_shell_ruler_button_press (widget, event, shell, + GIMP_ORIENTATION_HORIZONTAL); +} + +gboolean +gimp_display_shell_vruler_button_press (GtkWidget *widget, + GdkEventButton *event, + GimpDisplayShell *shell) +{ + return gimp_display_shell_ruler_button_press (widget, event, shell, + GIMP_ORIENTATION_VERTICAL); +} + + +/* private functions */ + +static gboolean +gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas, + GdkEvent *event, + GimpDisplayShell *shell, + GdkEvent **next_event) +{ + GimpDisplay *display; + GimpImage *image; + Gimp *gimp; + GimpCoords display_coords; + GimpCoords image_coords; + GdkModifierType state; + guint32 time; + gboolean device_changed = FALSE; + gboolean return_val = FALSE; + gboolean update_sw_cursor = FALSE; + + *next_event = NULL; + + /* are we in destruction? */ + if (! shell->display || ! gimp_display_get_shell (shell->display)) + return TRUE; + + /* set the active display before doing any other canvas event processing */ + if (gimp_display_shell_events (canvas, event, shell)) + return TRUE; + + /* events on overlays have a different window, but these windows' + * user_data can still be the canvas, we need to check manually if + * the event's window and the canvas' window are different. + */ + if (event->any.window != gtk_widget_get_window (canvas)) + { + GtkWidget *event_widget; + + gdk_window_get_user_data (event->any.window, (gpointer) &event_widget); + + /* if the event came from a different window than the canvas', + * check if it came from a canvas child and bail out. + */ + if (gtk_widget_get_ancestor (event_widget, GIMP_TYPE_CANVAS)) + return FALSE; + } + + display = shell->display; + gimp = gimp_display_get_gimp (display); + image = gimp_display_get_image (display); + + if (! image) + return gimp_display_shell_canvas_no_image_events (canvas, event, shell); + + GIMP_LOG (TOOL_EVENTS, "event (display %p): %s", + display, gimp_print_event (event)); + + /* See bug 771444 */ + if (shell->pointer_grabbed && + event->type == GDK_MOTION_NOTIFY) + { + GimpDeviceManager *manager = gimp_devices_get_manager (gimp); + GimpDeviceInfo *info; + + info = gimp_device_manager_get_current_device (manager); + + if (info->device != event->motion.device) + return FALSE; + } + + /* Find out what device the event occurred upon */ + if (! gimp->busy && + ! shell->inferior_ignore_mode && + gimp_devices_check_change (gimp, event)) + { + gimp_display_shell_check_device_cursor (shell); + device_changed = TRUE; + } + + gimp_display_shell_get_event_coords (shell, event, + &display_coords, + &state, &time); + gimp_display_shell_untransform_event_coords (shell, + &display_coords, &image_coords, + &update_sw_cursor); + + /* If the device (and maybe the tool) has changed, update the new + * tool's state + */ + if (device_changed && gtk_widget_has_focus (canvas)) + { + gimp_display_shell_update_focus (shell, TRUE, + &image_coords, state); + } + + switch (event->type) + { + case GDK_ENTER_NOTIFY: + { + GdkEventCrossing *cevent = (GdkEventCrossing *) event; + + if (shell->inferior_ignore_mode && + cevent->subwindow == NULL && + cevent->mode == GDK_CROSSING_NORMAL) + { + shell->inferior_ignore_mode = FALSE; + gtk_widget_set_extension_events (shell->canvas, + GDK_EXTENSION_EVENTS_ALL); + } + + if (cevent->mode != GDK_CROSSING_NORMAL) + return TRUE; + + /* ignore enter notify while we have a grab */ + if (shell->pointer_grabbed) + return TRUE; + + gimp_display_shell_proximity_in (shell); + update_sw_cursor = TRUE; + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + } + break; + + case GDK_LEAVE_NOTIFY: + { + GdkEventCrossing *cevent = (GdkEventCrossing *) event; + + if (! shell->inferior_ignore_mode && + cevent->subwindow == NULL && + cevent->mode == GDK_CROSSING_NORMAL && + cevent->detail == GDK_NOTIFY_INFERIOR) + { + shell->inferior_ignore_mode = TRUE; + gtk_widget_set_extension_events (shell->canvas, + GDK_EXTENSION_EVENTS_NONE); + } + + if (cevent->mode != GDK_CROSSING_NORMAL) + return TRUE; + + /* ignore leave notify while we have a grab */ + if (shell->pointer_grabbed) + return TRUE; + + gimp_display_shell_proximity_out (shell); + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + } + break; + + case GDK_PROXIMITY_IN: + gimp_display_shell_proximity_in (shell); + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + break; + + case GDK_PROXIMITY_OUT: + gimp_display_shell_proximity_out (shell); + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + break; + + case GDK_FOCUS_CHANGE: + { + GdkEventFocus *fevent = (GdkEventFocus *) event; + + if (fevent->in) + { + if (G_UNLIKELY (! gtk_widget_has_focus (canvas))) + g_warning ("%s: FOCUS_IN but canvas has no focus", G_STRFUNC); + + /* ignore focus changes while we have a grab */ + if (shell->pointer_grabbed) + return TRUE; + + /* press modifier keys when the canvas gets the focus */ + gimp_display_shell_update_focus (shell, TRUE, + &image_coords, state); + } + else + { + if (G_UNLIKELY (gtk_widget_has_focus (canvas))) + g_warning ("%s: FOCUS_OUT but canvas has focus", G_STRFUNC); + + /* ignore focus changes while we have a grab */ + if (shell->pointer_grabbed) + return TRUE; + + /* release modifier keys when the canvas loses the focus */ + gimp_display_shell_update_focus (shell, FALSE, + &image_coords, 0); + } + } + break; + + case GDK_BUTTON_PRESS: + { + GdkEventButton *bevent = (GdkEventButton *) event; + GdkModifierType button_state; + + /* ignore new mouse events */ + if (gimp->busy || shell->scrolling || + shell->pointer_grabbed || + shell->button1_release_pending) + return TRUE; + + button_state = gimp_display_shell_button_to_state (bevent->button); + + state |= button_state; + + /* ignore new buttons while another button is down */ + if (((state & (GDK_BUTTON1_MASK)) && (state & (GDK_BUTTON2_MASK | + GDK_BUTTON3_MASK))) || + ((state & (GDK_BUTTON2_MASK)) && (state & (GDK_BUTTON1_MASK | + GDK_BUTTON3_MASK))) || + ((state & (GDK_BUTTON3_MASK)) && (state & (GDK_BUTTON1_MASK | + GDK_BUTTON2_MASK)))) + return TRUE; + + /* focus the widget if it isn't; if the toplevel window + * already has focus, this will generate a FOCUS_IN on the + * canvas immediately, therefore we do this before logging + * the BUTTON_PRESS. + */ + if (! gtk_widget_has_focus (canvas)) + gtk_widget_grab_focus (canvas); + + /* if the toplevel window didn't have focus, the above + * gtk_widget_grab_focus() didn't set the canvas' HAS_FOCUS + * flags, and didn't trigger a FOCUS_IN, but the tool needs + * to be set up correctly regardless, so simply do the + * same things here, it's safe to do them redundantly. + */ + gimp_display_shell_update_focus (shell, TRUE, + &image_coords, state); + gimp_display_shell_update_cursor (shell, &display_coords, + &image_coords, state & ~button_state, + FALSE); + + if (gdk_event_triggers_context_menu (event)) + { + GimpUIManager *ui_manager; + const gchar *ui_path; + + ui_manager = tool_manager_get_popup_active (gimp, + &image_coords, state, + display, + &ui_path); + + if (ui_manager) + { + gimp_ui_manager_ui_popup (ui_manager, + ui_path, + GTK_WIDGET (shell), + NULL, NULL, NULL, NULL); + } + else + { + gimp_ui_manager_ui_popup (shell->popup_manager, + "/dummy-menubar/image-popup", + GTK_WIDGET (shell), + NULL, NULL, NULL, NULL); + } + } + else if (bevent->button == 1) + { + if (! gimp_display_shell_pointer_grab (shell, NULL, 0)) + return TRUE; + + if (! shell->space_release_pending) + if (! gimp_display_shell_keyboard_grab (shell, event)) + { + gimp_display_shell_pointer_ungrab (shell, NULL); + return TRUE; + } + + if (gimp_display_shell_initialize_tool (shell, + &image_coords, state)) + { + GimpCoords last_motion; + + /* Use the last evaluated velocity&direction instead of the + * button_press event's ones because the click is + * usually at the same spot as the last motion event + * which would give us bogus derivate dynamics. + */ + gimp_motion_buffer_begin_stroke (shell->motion_buffer, time, + &last_motion); + + image_coords.velocity = last_motion.velocity; + image_coords.direction = last_motion.direction; + + tool_manager_button_press_active (gimp, + &image_coords, + time, state, + GIMP_BUTTON_PRESS_NORMAL, + display); + } + } + else if (bevent->button == 2) + { + gimp_display_shell_start_scrolling (shell, NULL, state, + bevent->x, bevent->y); + } + + return_val = TRUE; + } + break; + + case GDK_2BUTTON_PRESS: + { + GdkEventButton *bevent = (GdkEventButton *) event; + GimpTool *active_tool; + + if (gimp->busy) + return TRUE; + + active_tool = tool_manager_get_active (gimp); + + if (bevent->button == 1 && + active_tool && + gimp_tool_control_is_active (active_tool->control) && + gimp_tool_control_get_wants_double_click (active_tool->control)) + { + tool_manager_button_press_active (gimp, + &image_coords, + time, state, + GIMP_BUTTON_PRESS_DOUBLE, + display); + } + + /* don't update the cursor again on double click */ + return TRUE; + } + break; + + case GDK_3BUTTON_PRESS: + { + GdkEventButton *bevent = (GdkEventButton *) event; + GimpTool *active_tool; + + if (gimp->busy) + return TRUE; + + active_tool = tool_manager_get_active (gimp); + + if (bevent->button == 1 && + active_tool && + gimp_tool_control_is_active (active_tool->control) && + gimp_tool_control_get_wants_triple_click (active_tool->control)) + { + tool_manager_button_press_active (gimp, + &image_coords, + time, state, + GIMP_BUTTON_PRESS_TRIPLE, + display); + } + + /* don't update the cursor again on triple click */ + return TRUE; + } + break; + + case GDK_BUTTON_RELEASE: + { + GdkEventButton *bevent = (GdkEventButton *) event; + GimpTool *active_tool; + + gimp_display_shell_autoscroll_stop (shell); + + if (bevent->button == 1 && shell->button1_release_pending) + { + gimp_display_shell_released (shell, event, NULL); + return TRUE; + } + + if (gimp->busy) + return TRUE; + + active_tool = tool_manager_get_active (gimp); + + state &= ~gimp_display_shell_button_to_state (bevent->button); + + if (bevent->button == 1) + { + if (! shell->pointer_grabbed || shell->scrolling) + return TRUE; + + if (! shell->space_release_pending) + gimp_display_shell_keyboard_ungrab (shell, event); + + if (active_tool && + (! gimp_image_is_empty (image) || + gimp_tool_control_get_handle_empty_image (active_tool->control))) + { + gimp_motion_buffer_end_stroke (shell->motion_buffer); + + if (gimp_tool_control_is_active (active_tool->control)) + { + tool_manager_button_release_active (gimp, + &image_coords, + time, state, + display); + } + } + + /* update the tool's modifier state because it didn't get + * key events while BUTTON1 was down + */ + if (gtk_widget_has_focus (canvas)) + gimp_display_shell_update_focus (shell, TRUE, + &image_coords, state); + else + gimp_display_shell_update_focus (shell, FALSE, + &image_coords, 0); + + gimp_display_shell_pointer_ungrab (shell, NULL); + } + else if (bevent->button == 2) + { + if (shell->scrolling) + gimp_display_shell_stop_scrolling (shell, NULL); + } + else if (bevent->button == 3) + { + /* nop */ + } + else + { + GdkEventButton *bevent = (GdkEventButton *) event; + GimpController *mouse = gimp_controllers_get_mouse (gimp); + + if (!(shell->scrolling || shell->pointer_grabbed) && + mouse && gimp_controller_mouse_button (GIMP_CONTROLLER_MOUSE (mouse), + bevent)) + { + return TRUE; + } + } + + return_val = TRUE; + } + break; + + case GDK_SCROLL: + { + GdkEventScroll *sevent = (GdkEventScroll *) event; + GimpController *wheel = gimp_controllers_get_wheel (gimp); + + if (! wheel || + ! gimp_controller_wheel_scroll (GIMP_CONTROLLER_WHEEL (wheel), + sevent)) + { + GdkScrollDirection direction = sevent->direction; + + if (state & gimp_get_toggle_behavior_mask ()) + { + switch (direction) + { + case GDK_SCROLL_UP: + gimp_display_shell_scale (shell, + GIMP_ZOOM_IN, + 0.0, + GIMP_ZOOM_FOCUS_POINTER); + break; + + case GDK_SCROLL_DOWN: + gimp_display_shell_scale (shell, + GIMP_ZOOM_OUT, + 0.0, + GIMP_ZOOM_FOCUS_POINTER); + break; + + default: + break; + } + } + else + { + GtkAdjustment *adj = NULL; + gdouble value; + + if (state & GDK_SHIFT_MASK) + switch (direction) + { + case GDK_SCROLL_UP: direction = GDK_SCROLL_LEFT; break; + case GDK_SCROLL_DOWN: direction = GDK_SCROLL_RIGHT; break; + case GDK_SCROLL_LEFT: direction = GDK_SCROLL_UP; break; + case GDK_SCROLL_RIGHT: direction = GDK_SCROLL_DOWN; break; + } + + switch (direction) + { + case GDK_SCROLL_LEFT: + case GDK_SCROLL_RIGHT: + adj = shell->hsbdata; + break; + + case GDK_SCROLL_UP: + case GDK_SCROLL_DOWN: + adj = shell->vsbdata; + break; + } + + value = (gtk_adjustment_get_value (adj) + + ((direction == GDK_SCROLL_UP || + direction == GDK_SCROLL_LEFT) ? + -gtk_adjustment_get_page_increment (adj) / 2 : + gtk_adjustment_get_page_increment (adj) / 2)); + value = CLAMP (value, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + + gtk_adjustment_set_value (adj, value); + } + } + + gimp_display_shell_untransform_event_coords (shell, + &display_coords, + &image_coords, + &update_sw_cursor); + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + + return_val = TRUE; + } + break; + + case GDK_MOTION_NOTIFY: + { + GdkEventMotion *mevent = (GdkEventMotion *) event; + GdkEvent *compressed_motion = NULL; + GimpMotionMode motion_mode = GIMP_MOTION_MODE_EXACT; + GimpTool *active_tool; + + if (gimp->busy) + return TRUE; + + active_tool = tool_manager_get_active (gimp); + + if (active_tool) + motion_mode = gimp_tool_control_get_motion_mode (active_tool->control); + + if (shell->scrolling || + motion_mode == GIMP_MOTION_MODE_COMPRESS) + { + compressed_motion = gimp_display_shell_compress_motion (event, + next_event); + + if (compressed_motion && ! shell->scrolling) + { + gimp_display_shell_get_event_coords (shell, + compressed_motion, + &display_coords, + &state, &time); + gimp_display_shell_untransform_event_coords (shell, + &display_coords, + &image_coords, + NULL); + } + } + + /* call proximity_in() here because the pointer might already + * be in proximity when the canvas starts to receive events, + * like when a new image has been created into an empty + * display + */ + gimp_display_shell_proximity_in (shell); + update_sw_cursor = TRUE; + + if (shell->scrolling) + { + GdkEventMotion *me = (compressed_motion ? + (GdkEventMotion *) compressed_motion : + mevent); + + gimp_display_shell_handle_scrolling (shell, state, me->x, me->y); + } + else if (state & GDK_BUTTON1_MASK) + { + if (active_tool && + gimp_tool_control_is_active (active_tool->control) && + (! gimp_image_is_empty (image) || + gimp_tool_control_get_handle_empty_image (active_tool->control))) + { + GdkTimeCoord **history_events; + gint n_history_events; + guint32 last_motion_time; + + /* if the first mouse button is down, check for automatic + * scrolling... + */ + if ((mevent->x < 0 || + mevent->y < 0 || + mevent->x > shell->disp_width || + mevent->y > shell->disp_height) && + ! gimp_tool_control_get_scroll_lock (active_tool->control)) + { + gimp_display_shell_autoscroll_start (shell, state, mevent); + } + + /* gdk_device_get_history() has several quirks. First + * is that events with borderline timestamps at both + * ends are included. Because of that we need to add 1 + * to lower border. The second is due to poor X event + * resolution. We need to do -1 to ensure that the + * amount of events between timestamps is final or + * risk losing some. + */ + last_motion_time = + gimp_motion_buffer_get_last_motion_time (shell->motion_buffer); + + if (motion_mode == GIMP_MOTION_MODE_EXACT && + shell->display->config->use_event_history && + gdk_device_get_history (mevent->device, mevent->window, + last_motion_time + 1, + mevent->time - 1, + &history_events, + &n_history_events)) + { + GimpDeviceInfo *device; + gint i; + + device = gimp_device_info_get_by_device (mevent->device); + + for (i = 0; i < n_history_events; i++) + { + gimp_device_info_get_time_coords (device, + history_events[i], + &display_coords); + + gimp_display_shell_untransform_event_coords (shell, + &display_coords, + &image_coords, + NULL); + + /* Early removal of useless events saves CPU time. + */ + if (gimp_motion_buffer_motion_event (shell->motion_buffer, + &image_coords, + history_events[i]->time, + TRUE)) + { + gimp_motion_buffer_request_stroke (shell->motion_buffer, + state, + history_events[i]->time); + } + } + + gdk_device_free_history (history_events, n_history_events); + } + else + { + gboolean event_fill = (motion_mode == GIMP_MOTION_MODE_EXACT); + + /* Early removal of useless events saves CPU time. + */ + if (gimp_motion_buffer_motion_event (shell->motion_buffer, + &image_coords, + time, + event_fill)) + { + gimp_motion_buffer_request_stroke (shell->motion_buffer, + state, + time); + } + } + } + } + + if (! (state & + (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))) + { + /* Early removal of useless events saves CPU time. + * Pass event_fill = FALSE since we are only hovering. + */ + if (gimp_motion_buffer_motion_event (shell->motion_buffer, + &image_coords, + time, + FALSE)) + { + gimp_motion_buffer_request_hover (shell->motion_buffer, + state, + shell->proximity); + } + } + + if (compressed_motion) + gdk_event_free (compressed_motion); + + return_val = TRUE; + } + break; + + case GDK_KEY_PRESS: + { + GdkEventKey *kevent = (GdkEventKey *) event; + GimpTool *active_tool; + + active_tool = tool_manager_get_active (gimp); + + if (state & GDK_BUTTON1_MASK) + { + if (kevent->keyval == GDK_KEY_Alt_L || + kevent->keyval == GDK_KEY_Alt_R || + kevent->keyval == GDK_KEY_Shift_L || + kevent->keyval == GDK_KEY_Shift_R || + kevent->keyval == GDK_KEY_Control_L || + kevent->keyval == GDK_KEY_Control_R || + kevent->keyval == GDK_KEY_Meta_L || + kevent->keyval == GDK_KEY_Meta_R) + { + GdkModifierType key; + + key = gimp_display_shell_key_to_state (kevent->keyval); + state |= key; + + if (active_tool && + gimp_tool_control_is_active (active_tool->control) && + ! gimp_image_is_empty (image)) + { + tool_manager_active_modifier_state_active (gimp, state, + display); + } + } + } + else + { + gboolean arrow_key = FALSE; + + tool_manager_focus_display_active (gimp, display); + + if (gimp_tool_control_get_wants_all_key_events (active_tool->control)) + { + if (tool_manager_key_press_active (gimp, kevent, display)) + { + /* FIXME: need to do some of the stuff below, like + * calling oper_update() + */ + + return TRUE; + } + } + + if (! gtk_widget_has_focus (shell->canvas)) + { + /* The event was in an overlay widget and not handled + * there, make sure the overlay widgets are keyboard + * navigatable by letting the generic widget handlers + * deal with the event. + */ + return FALSE; + } + + if (gimp_display_shell_key_to_state (kevent->keyval) == GDK_MOD1_MASK) + /* Make sure the picked layer is reset. */ + shell->picked_layer = NULL; + + switch (kevent->keyval) + { + case GDK_KEY_Left: + case GDK_KEY_Right: + case GDK_KEY_Up: + case GDK_KEY_Down: + arrow_key = TRUE; + + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + case GDK_KEY_BackSpace: + case GDK_KEY_Escape: + if (! gimp_image_is_empty (image)) + return_val = tool_manager_key_press_active (gimp, + kevent, + display); + + if (! return_val) + { + GimpController *keyboard = gimp_controllers_get_keyboard (gimp); + + if (keyboard) + return_val = + gimp_controller_keyboard_key_press (GIMP_CONTROLLER_KEYBOARD (keyboard), + kevent); + } + + /* always swallow arrow keys, we don't want focus keynav */ + if (! return_val) + return_val = arrow_key; + break; + + case GDK_KEY_space: + case GDK_KEY_KP_Space: + if (shell->button1_release_pending) + shell->space_release_pending = TRUE; + else + gimp_display_shell_space_pressed (shell, event); + return_val = TRUE; + break; + + case GDK_KEY_Tab: + case GDK_KEY_KP_Tab: + case GDK_KEY_ISO_Left_Tab: + gimp_display_shell_tab_pressed (shell, kevent); + return_val = TRUE; + break; + + /* Update the state based on modifiers being pressed */ + case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: + case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: + case GDK_KEY_Control_L: case GDK_KEY_Control_R: + case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: + { + GdkModifierType key; + + key = gimp_display_shell_key_to_state (kevent->keyval); + state |= key; + + if (! gimp_image_is_empty (image)) + tool_manager_modifier_state_active (gimp, state, display); + } + break; + } + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + } + } + break; + + case GDK_KEY_RELEASE: + { + GdkEventKey *kevent = (GdkEventKey *) event; + GimpTool *active_tool; + + active_tool = tool_manager_get_active (gimp); + + if (gimp_display_shell_key_to_state (kevent->keyval) == GDK_MOD1_MASK && + shell->picked_layer) + { + GimpStatusbar *statusbar; + + statusbar = gimp_display_shell_get_statusbar (shell); + gimp_statusbar_pop_temp (statusbar); + + shell->picked_layer = NULL; + } + + if ((state & GDK_BUTTON1_MASK) && + (! shell->space_release_pending || + (kevent->keyval != GDK_KEY_space && + kevent->keyval != GDK_KEY_KP_Space))) + { + if (kevent->keyval == GDK_KEY_Alt_L || + kevent->keyval == GDK_KEY_Alt_R || + kevent->keyval == GDK_KEY_Shift_L || + kevent->keyval == GDK_KEY_Shift_R || + kevent->keyval == GDK_KEY_Control_L || + kevent->keyval == GDK_KEY_Control_R || + kevent->keyval == GDK_KEY_Meta_L || + kevent->keyval == GDK_KEY_Meta_R) + { + GdkModifierType key; + + key = gimp_display_shell_key_to_state (kevent->keyval); + state &= ~key; + + if (active_tool && + gimp_tool_control_is_active (active_tool->control) && + ! gimp_image_is_empty (image)) + { + tool_manager_active_modifier_state_active (gimp, state, + display); + } + } + } + else + { + tool_manager_focus_display_active (gimp, display); + + if (gimp_tool_control_get_wants_all_key_events (active_tool->control)) + { + if (tool_manager_key_release_active (gimp, kevent, display)) + { + /* FIXME: need to do some of the stuff below, like + * calling oper_update() + */ + + return TRUE; + } + } + + if (! gtk_widget_has_focus (shell->canvas)) + { + /* The event was in an overlay widget and not handled + * there, make sure the overlay widgets are keyboard + * navigatable by letting the generic widget handlers + * deal with the event. + */ + return FALSE; + } + + switch (kevent->keyval) + { + case GDK_KEY_space: + case GDK_KEY_KP_Space: + if ((state & GDK_BUTTON1_MASK)) + { + shell->button1_release_pending = TRUE; + shell->space_release_pending = FALSE; + /* We need to ungrab the pointer in order to catch + * button release events. + */ + if (shell->pointer_grabbed) + gimp_display_shell_pointer_ungrab (shell, event); + } + else + { + gimp_display_shell_released (shell, event, NULL); + } + return_val = TRUE; + break; + + /* Update the state based on modifiers being pressed */ + case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: + case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: + case GDK_KEY_Control_L: case GDK_KEY_Control_R: + case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: + { + GdkModifierType key; + + key = gimp_display_shell_key_to_state (kevent->keyval); + state &= ~key; + + /* For all modifier keys: call the tools + * modifier_state *and* oper_update method so tools + * can choose if they are interested in the press + * itself or only in the resulting state + */ + if (! gimp_image_is_empty (image)) + tool_manager_modifier_state_active (gimp, state, display); + } + break; + } + + tool_manager_oper_update_active (gimp, + &image_coords, state, + shell->proximity, + display); + } + } + break; + + default: + break; + } + + /* if we reached this point in gimp_busy mode, return now */ + if (gimp->busy) + return return_val; + + /* cursor update */ + gimp_display_shell_update_cursor (shell, &display_coords, &image_coords, + state, update_sw_cursor); + + return return_val; +} + +static GdkModifierType +gimp_display_shell_key_to_state (gint key) +{ + /* FIXME: need some proper GDK API to figure this */ + + switch (key) + { + case GDK_KEY_Alt_L: + case GDK_KEY_Alt_R: + return GDK_MOD1_MASK; + + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + return GDK_SHIFT_MASK; + + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + return GDK_CONTROL_MASK; + +#ifdef GDK_WINDOWING_QUARTZ + case GDK_KEY_Meta_L: + case GDK_KEY_Meta_R: + return GDK_MOD2_MASK; +#endif + + default: + return 0; + } +} + +static GdkModifierType +gimp_display_shell_button_to_state (gint button) +{ + if (button == 1) + return GDK_BUTTON1_MASK; + else if (button == 2) + return GDK_BUTTON2_MASK; + else if (button == 3) + return GDK_BUTTON3_MASK; + + return 0; +} + +static void +gimp_display_shell_proximity_in (GimpDisplayShell *shell) +{ + if (! shell->proximity) + { + shell->proximity = TRUE; + + gimp_display_shell_check_device_cursor (shell); + } +} + +static void +gimp_display_shell_proximity_out (GimpDisplayShell *shell) +{ + if (shell->proximity) + { + shell->proximity = FALSE; + + gimp_display_shell_clear_software_cursor (shell); + } +} + +static void +gimp_display_shell_check_device_cursor (GimpDisplayShell *shell) +{ + GimpDeviceManager *manager; + GimpDeviceInfo *current_device; + + manager = gimp_devices_get_manager (shell->display->gimp); + + current_device = gimp_device_manager_get_current_device (manager); + + shell->draw_cursor = ! gimp_device_info_has_cursor (current_device); +} + +static void +gimp_display_shell_start_scrolling (GimpDisplayShell *shell, + const GdkEvent *event, + GdkModifierType state, + gint x, + gint y) +{ + g_return_if_fail (! shell->scrolling); + + gimp_display_shell_pointer_grab (shell, event, GDK_POINTER_MOTION_MASK); + + shell->scrolling = TRUE; + shell->scroll_start_x = x; + shell->scroll_start_y = y; + shell->scroll_last_x = x; + shell->scroll_last_y = y; + shell->rotating = (state & gimp_get_extend_selection_mask ()) ? TRUE : FALSE; + shell->rotate_drag_angle = shell->rotate_angle; + shell->scaling = (state & gimp_get_toggle_behavior_mask ()) ? TRUE : FALSE; + shell->layer_picking = (state & GDK_MOD1_MASK) ? TRUE : FALSE; + + if (shell->rotating) + { + gimp_display_shell_set_override_cursor (shell, + (GimpCursorType) GDK_EXCHANGE); + } + else if (shell->scaling) + { + gimp_display_shell_set_override_cursor (shell, + (GimpCursorType) GIMP_CURSOR_ZOOM); + } + else if (shell->layer_picking) + { + GimpImage *image = gimp_display_get_image (shell->display); + GimpLayer *layer; + GimpCoords image_coords; + GimpCoords display_coords; + guint32 time; + + gimp_display_shell_set_override_cursor (shell, + (GimpCursorType) GIMP_CURSOR_CROSSHAIR); + + gimp_display_shell_get_event_coords (shell, event, + &display_coords, + &state, &time); + gimp_display_shell_untransform_event_coords (shell, + &display_coords, &image_coords, + NULL); + layer = gimp_image_pick_layer (image, + (gint) image_coords.x, + (gint) image_coords.y, + shell->picked_layer); + + if (layer && ! gimp_image_get_floating_selection (image)) + { + if (layer != gimp_image_get_active_layer (image)) + { + GimpStatusbar *statusbar; + + gimp_image_set_active_layer (image, layer); + + statusbar = gimp_display_shell_get_statusbar (shell); + gimp_statusbar_push_temp (statusbar, GIMP_MESSAGE_INFO, + GIMP_ICON_LAYER, + _("Layer picked: '%s'"), + gimp_object_get_name (layer)); + } + shell->picked_layer = layer; + } + } + else + gimp_display_shell_set_override_cursor (shell, + (GimpCursorType) GDK_FLEUR); +} + +static void +gimp_display_shell_stop_scrolling (GimpDisplayShell *shell, + const GdkEvent *event) +{ + g_return_if_fail (shell->scrolling); + + gimp_display_shell_unset_override_cursor (shell); + + shell->scrolling = FALSE; + shell->scroll_start_x = 0; + shell->scroll_start_y = 0; + shell->scroll_last_x = 0; + shell->scroll_last_y = 0; + shell->rotating = FALSE; + shell->rotate_drag_angle = 0.0; + shell->scaling = FALSE; + shell->layer_picking = FALSE; + + /* We may have ungrabbed the pointer when space was released while + * mouse was down, to be able to catch a GDK_BUTTON_RELEASE event. + */ + if (shell->pointer_grabbed) + gimp_display_shell_pointer_ungrab (shell, event); +} + +static void +gimp_display_shell_handle_scrolling (GimpDisplayShell *shell, + GdkModifierType state, + gint x, + gint y) +{ + g_return_if_fail (shell->scrolling); + + if (shell->rotating) + { + gboolean constrain = (state & GDK_CONTROL_MASK) ? TRUE : FALSE; + + gimp_display_shell_rotate_drag (shell, + shell->scroll_last_x, + shell->scroll_last_y, + x, + y, + constrain); + } + else if (shell->scaling) + { + gimp_display_shell_scale_drag (shell, + shell->scroll_start_x, + shell->scroll_start_y, + shell->scroll_last_x - x, + shell->scroll_last_y - y); + } + else if (shell->layer_picking) + { + /* Do nothing. We only pick the layer on click. */ + } + else + { + gimp_display_shell_scroll (shell, + shell->scroll_last_x - x, + shell->scroll_last_y - y); + } + + shell->scroll_last_x = x; + shell->scroll_last_y = y; +} + +static void +gimp_display_shell_space_pressed (GimpDisplayShell *shell, + const GdkEvent *event) +{ + Gimp *gimp = gimp_display_get_gimp (shell->display); + + if (shell->space_release_pending || shell->scrolling) + return; + + if (! gimp_display_shell_keyboard_grab (shell, event)) + return; + + switch (shell->display->config->space_bar_action) + { + case GIMP_SPACE_BAR_ACTION_NONE: + break; + + case GIMP_SPACE_BAR_ACTION_PAN: + { + GimpDeviceManager *manager; + GimpDeviceInfo *current_device; + GimpCoords coords; + GdkModifierType state = 0; + + manager = gimp_devices_get_manager (gimp); + current_device = gimp_device_manager_get_current_device (manager); + + gimp_device_info_get_device_coords (current_device, + gtk_widget_get_window (shell->canvas), + &coords); + gdk_event_get_state (event, &state); + + gimp_display_shell_start_scrolling (shell, event, state, + coords.x, coords.y); + } + break; + + case GIMP_SPACE_BAR_ACTION_MOVE: + { + GimpTool *active_tool = tool_manager_get_active (gimp); + + if (active_tool || ! GIMP_IS_MOVE_TOOL (active_tool)) + { + GdkModifierType state; + + shell->space_shaded_tool = + gimp_object_get_name (active_tool->tool_info); + + gimp_context_set_tool (gimp_get_user_context (gimp), + gimp_get_tool_info (gimp, "gimp-move-tool")); + + gdk_event_get_state (event, &state); + + gimp_display_shell_update_focus (shell, TRUE, + NULL, state); + } + } + break; + } + + shell->space_release_pending = TRUE; +} + +static void +gimp_display_shell_released (GimpDisplayShell *shell, + const GdkEvent *event, + const GimpCoords *image_coords) +{ + Gimp *gimp = gimp_display_get_gimp (shell->display); + + if (! shell->space_release_pending && + ! shell->button1_release_pending) + return; + + switch (shell->display->config->space_bar_action) + { + case GIMP_SPACE_BAR_ACTION_NONE: + break; + + case GIMP_SPACE_BAR_ACTION_PAN: + gimp_display_shell_stop_scrolling (shell, event); + break; + + case GIMP_SPACE_BAR_ACTION_MOVE: + if (shell->space_shaded_tool) + { + gimp_context_set_tool (gimp_get_user_context (gimp), + gimp_get_tool_info (gimp, + shell->space_shaded_tool)); + shell->space_shaded_tool = NULL; + + if (gtk_widget_has_focus (shell->canvas)) + { + GdkModifierType state; + + gdk_event_get_state (event, &state); + + gimp_display_shell_update_focus (shell, TRUE, + image_coords, state); + } + else + { + gimp_display_shell_update_focus (shell, FALSE, + image_coords, 0); + } + } + break; + } + + gimp_display_shell_keyboard_ungrab (shell, event); + + shell->space_release_pending = FALSE; + shell->button1_release_pending = FALSE; +} + +static gboolean +gimp_display_shell_tab_pressed (GimpDisplayShell *shell, + const GdkEventKey *kevent) +{ + GimpImageWindow *window = gimp_display_shell_get_window (shell); + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + GimpImage *image = gimp_display_get_image (shell->display); + + if (kevent->state & GDK_CONTROL_MASK) + { + if (image && ! gimp_image_is_empty (image)) + { + if (kevent->keyval == GDK_KEY_Tab || + kevent->keyval == GDK_KEY_KP_Tab) + gimp_display_shell_layer_select_init (shell, + 1, kevent->time); + else + gimp_display_shell_layer_select_init (shell, + -1, kevent->time); + + return TRUE; + } + } + else if (kevent->state & GDK_MOD1_MASK) + { + if (image) + { + if (kevent->keyval == GDK_KEY_Tab || + kevent->keyval == GDK_KEY_KP_Tab) + gimp_ui_manager_activate_action (manager, "windows", + "windows-show-display-next"); + else + gimp_ui_manager_activate_action (manager, "windows", + "windows-show-display-previous"); + + return TRUE; + } + } + else + { + gimp_ui_manager_activate_action (manager, "windows", + "windows-hide-docks"); + + return TRUE; + } + + return FALSE; +} + +static void +gimp_display_shell_update_focus (GimpDisplayShell *shell, + gboolean focus_in, + const GimpCoords *image_coords, + GdkModifierType state) +{ + Gimp *gimp = gimp_display_get_gimp (shell->display); + + if (focus_in) + { + tool_manager_focus_display_active (gimp, shell->display); + tool_manager_modifier_state_active (gimp, state, shell->display); + } + else + { + tool_manager_focus_display_active (gimp, NULL); + } + + if (image_coords) + tool_manager_oper_update_active (gimp, + image_coords, state, + shell->proximity, + shell->display); +} + +static void +gimp_display_shell_update_cursor (GimpDisplayShell *shell, + const GimpCoords *display_coords, + const GimpCoords *image_coords, + GdkModifierType state, + gboolean update_software_cursor) +{ + GimpDisplay *display = shell->display; + Gimp *gimp = gimp_display_get_gimp (display); + GimpImage *image = gimp_display_get_image (display); + GimpTool *active_tool; + + if (! shell->display->config->cursor_updating) + return; + + active_tool = tool_manager_get_active (gimp); + + if (active_tool) + { + if ((! gimp_image_is_empty (image) || + gimp_tool_control_get_handle_empty_image (active_tool->control)) && + ! (state & (GDK_BUTTON1_MASK | + GDK_BUTTON2_MASK | + GDK_BUTTON3_MASK))) + { + tool_manager_cursor_update_active (gimp, + image_coords, state, + display); + } + else if (gimp_image_is_empty (image) && + ! gimp_tool_control_get_handle_empty_image (active_tool->control)) + { + gimp_display_shell_set_cursor (shell, + GIMP_CURSOR_MOUSE, + gimp_tool_control_get_tool_cursor (active_tool->control), + GIMP_CURSOR_MODIFIER_BAD); + } + } + else + { + gimp_display_shell_set_cursor (shell, + GIMP_CURSOR_MOUSE, + GIMP_TOOL_CURSOR_NONE, + GIMP_CURSOR_MODIFIER_BAD); + } + + if (update_software_cursor) + { + GimpCursorPrecision precision = GIMP_CURSOR_PRECISION_PIXEL_CENTER; + + if (active_tool) + precision = gimp_tool_control_get_precision (active_tool->control); + + gimp_display_shell_update_software_cursor (shell, + precision, + (gint) display_coords->x, + (gint) display_coords->y, + image_coords->x, + image_coords->y); + } +} + +static gboolean +gimp_display_shell_initialize_tool (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GdkModifierType state) +{ + GimpDisplay *display = shell->display; + GimpImage *image = gimp_display_get_image (display); + Gimp *gimp = gimp_display_get_gimp (display); + gboolean initialized = FALSE; + GimpTool *active_tool; + + active_tool = tool_manager_get_active (gimp); + + if (active_tool && + (! gimp_image_is_empty (image) || + gimp_tool_control_get_handle_empty_image (active_tool->control))) + { + /* initialize the current tool if it has no drawable */ + if (! active_tool->drawable) + { + initialized = tool_manager_initialize_active (gimp, display); + } + else if ((active_tool->drawable != + gimp_image_get_active_drawable (image)) && + (! gimp_tool_control_get_preserve (active_tool->control) && + (gimp_tool_control_get_dirty_mask (active_tool->control) & + GIMP_DIRTY_ACTIVE_DRAWABLE))) + { + GimpProcedure *procedure = g_object_get_data (G_OBJECT (active_tool), + "gimp-gegl-procedure"); + + if (image == gimp_item_get_image (GIMP_ITEM (active_tool->drawable))) + { + /* When changing between drawables if the *same* image, + * stop the tool using its dirty action, so it doesn't + * get committed on tool change, in case its dirty action + * is HALT. This is a pure "probably better this way" + * decision because the user is likely changing their + * mind or was simply on the wrong layer. See bug #776370. + * + * See also issues #1180 and #1202 for cases where we + * actually *don't* want to halt the tool here, but rather + * commit it, hence the use of the tool's dirty action. + */ + tool_manager_control_active ( + gimp, + gimp_tool_control_get_dirty_action (active_tool->control), + active_tool->display); + } + + if (procedure) + { + /* We can't just recreate an operation tool, we must + * make sure the right stuff gets set on it, so + * re-activate the procedure that created it instead of + * just calling gimp_context_tool_changed(). See + * GimpGeglProcedure and bug #776370. + */ + GimpImageWindow *window; + GimpUIManager *manager; + + window = gimp_display_shell_get_window (shell); + manager = gimp_image_window_get_ui_manager (window); + + gimp_filter_history_add (gimp, procedure); + gimp_ui_manager_activate_action (manager, "filters", + "filters-reshow"); + + /* the procedure already initialized the tool; don't + * reinitialize it below, since this can lead to errors. + */ + initialized = TRUE; + } + else + { + /* create a new one, deleting the current */ + gimp_context_tool_changed (gimp_get_user_context (gimp)); + } + + /* make sure the newly created tool has the right state */ + gimp_display_shell_update_focus (shell, TRUE, image_coords, state); + + if (! initialized) + initialized = tool_manager_initialize_active (gimp, display); + } + else + { + initialized = TRUE; + } + } + + return initialized; +} + +static void +gimp_display_shell_get_event_coords (GimpDisplayShell *shell, + const GdkEvent *event, + GimpCoords *display_coords, + GdkModifierType *state, + guint32 *time) +{ + Gimp *gimp = gimp_display_get_gimp (shell->display); + GimpDeviceManager *manager; + GimpDeviceInfo *current_device; + + manager = gimp_devices_get_manager (gimp); + current_device = gimp_device_manager_get_current_device (manager); + + gimp_device_info_get_event_coords (current_device, + gtk_widget_get_window (shell->canvas), + event, + display_coords); + + gimp_device_info_get_event_state (current_device, + gtk_widget_get_window (shell->canvas), + event, + state); + + *time = gdk_event_get_time (event); +} + +static void +gimp_display_shell_untransform_event_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords, + gboolean *update_software_cursor) +{ + Gimp *gimp = gimp_display_get_gimp (shell->display); + GimpTool *active_tool; + + /* GimpCoords passed to tools are ALWAYS in image coordinates */ + gimp_display_shell_untransform_coords (shell, + display_coords, + image_coords); + + active_tool = tool_manager_get_active (gimp); + + if (active_tool && gimp_tool_control_get_snap_to (active_tool->control)) + { + gint x, y, width, height; + + gimp_tool_control_get_snap_offsets (active_tool->control, + &x, &y, &width, &height); + + if (gimp_display_shell_snap_coords (shell, + image_coords, + x, y, width, height)) + { + if (update_software_cursor) + *update_software_cursor = TRUE; + } + } +} + +/* gimp_display_shell_compress_motion: + * + * This function walks the GDK event queue, seeking motion events at the + * front of the queue corresponding to the same widget as, and having + * similar characteristics to, `initial_event`. If it finds any it will + * remove them from the queue, and return the most recent motion event. + * Otherwise it will return NULL. + * + * If `*next_event` is non-NULL upon return, the caller must dispatch and + * free this event after handling the motion event. + * + * The gimp_display_shell_compress_motion function source may be re-used under + * the XFree86-style license. + */ +static GdkEvent * +gimp_display_shell_compress_motion (GdkEvent *initial_event, + GdkEvent **next_event) +{ + GdkEvent *last_motion = NULL; + GtkWidget *widget; + + *next_event = NULL; + + if (initial_event->any.type != GDK_MOTION_NOTIFY) + return NULL; + + widget = gtk_get_event_widget (initial_event); + + while (gdk_events_pending ()) + { + GdkEvent *event = gdk_event_get (); + + if (!event) + { + /* Do nothing */ + } + else if ((gtk_get_event_widget (event) == widget) && + (event->any.type == GDK_MOTION_NOTIFY) && + (event->any.window == initial_event->any.window) && + (event->motion.state == initial_event->motion.state) && + (event->motion.device == initial_event->motion.device)) + { + /* Discard previous motion event */ + if (last_motion) + gdk_event_free (last_motion); + + last_motion = event; + } + else + { + /* Let the caller dispatch the event */ + *next_event = event; + + break; + } + } + + return last_motion; +} diff --git a/app/display/gimpdisplayshell-tool-events.h b/app/display/gimpdisplayshell-tool-events.h new file mode 100644 index 0000000..4458a92 --- /dev/null +++ b/app/display/gimpdisplayshell-tool-events.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_TOOL_EVENTS_H__ +#define __GIMP_DISPLAY_SHELL_TOOL_EVENTS_H__ + + +gboolean gimp_display_shell_events (GtkWidget *widget, + GdkEvent *event, + GimpDisplayShell *shell); + +gboolean gimp_display_shell_canvas_tool_events (GtkWidget *widget, + GdkEvent *event, + GimpDisplayShell *shell); +void gimp_display_shell_canvas_grab_notify (GtkWidget *widget, + gboolean was_grabbed, + GimpDisplayShell *shell); + +void gimp_display_shell_buffer_stroke (GimpMotionBuffer *buffer, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplayShell *shell); +void gimp_display_shell_buffer_hover (GimpMotionBuffer *buffer, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplayShell *shell); + +gboolean gimp_display_shell_hruler_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); +gboolean gimp_display_shell_vruler_button_press (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); + + +#endif /* __GIMP_DISPLAY_SHELL_TOOL_EVENT_H__ */ diff --git a/app/display/gimpdisplayshell-transform.c b/app/display/gimpdisplayshell-transform.c new file mode 100644 index 0000000..f9cd2e5 --- /dev/null +++ b/app/display/gimpdisplayshell-transform.c @@ -0,0 +1,1025 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpboundary.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimp-utils.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-transform.h" + + +/* local function prototypes */ + +static void gimp_display_shell_transform_xy_f_noround (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); + +/* public functions */ + +/** + * gimp_display_shell_zoom_coords: + * @shell: a #GimpDisplayShell + * @image_coords: image coordinates + * @display_coords: returns the corresponding display coordinates + * + * Zooms from image coordinates to display coordinates, so that + * objects can be rendered at the correct points on the display. + **/ +void +gimp_display_shell_zoom_coords (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GimpCoords *display_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (image_coords != NULL); + g_return_if_fail (display_coords != NULL); + + *display_coords = *image_coords; + + display_coords->x = SCALEX (shell, image_coords->x); + display_coords->y = SCALEY (shell, image_coords->y); + + display_coords->x -= shell->offset_x; + display_coords->y -= shell->offset_y; +} + +/** + * gimp_display_shell_unzoom_coords: + * @shell: a #GimpDisplayShell + * @display_coords: display coordinates + * @image_coords: returns the corresponding image coordinates + * + * Zooms from display coordinates to image coordinates, so that + * points on the display can be mapped to points in the image. + **/ +void +gimp_display_shell_unzoom_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (display_coords != NULL); + g_return_if_fail (image_coords != NULL); + + *image_coords = *display_coords; + + image_coords->x += shell->offset_x; + image_coords->y += shell->offset_y; + + image_coords->x /= shell->scale_x; + image_coords->y /= shell->scale_y; +} + +/** + * gimp_display_shell_zoom_xy: + * @shell: + * @x: + * @y: + * @nx: + * @ny: + * + * Zooms an image coordinate to a shell coordinate. + **/ +void +gimp_display_shell_zoom_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny) +{ + gint64 tx; + gint64 ty; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + tx = x * shell->scale_x; + ty = y * shell->scale_y; + + tx -= shell->offset_x; + ty -= shell->offset_y; + + /* The projected coordinates might overflow a gint in the case of + * big images at high zoom levels, so we clamp them here to avoid + * problems. + */ + *nx = CLAMP (tx, G_MININT, G_MAXINT); + *ny = CLAMP (ty, G_MININT, G_MAXINT); +} + +/** + * gimp_display_shell_unzoom_xy: + * @shell: a #GimpDisplayShell + * @x: x coordinate in display coordinates + * @y: y coordinate in display coordinates + * @nx: returns x oordinate in image coordinates + * @ny: returns y coordinate in image coordinates + * @round: if %TRUE, round the results to the nearest integer; + * if %FALSE, simply cast them to @gint. + * + * Zoom from display coordinates to image coordinates, so that + * points on the display can be mapped to the corresponding points + * in the image. + **/ +void +gimp_display_shell_unzoom_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny, + gboolean round) +{ + gint64 tx; + gint64 ty; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + if (round) + { + tx = SIGNED_ROUND (((gdouble) x + shell->offset_x) / shell->scale_x); + ty = SIGNED_ROUND (((gdouble) y + shell->offset_y) / shell->scale_y); + } + else + { + tx = ((gint64) x + shell->offset_x) / shell->scale_x; + ty = ((gint64) y + shell->offset_y) / shell->scale_y; + } + + *nx = CLAMP (tx, G_MININT, G_MAXINT); + *ny = CLAMP (ty, G_MININT, G_MAXINT); +} + +/** + * gimp_display_shell_zoom_xy_f: + * @shell: a #GimpDisplayShell + * @x: image x coordinate of point + * @y: image y coordinate of point + * @nx: returned shell canvas x coordinate + * @ny: returned shell canvas y coordinate + * + * Zooms from image coordinates to display shell canvas + * coordinates. + **/ +void +gimp_display_shell_zoom_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + *nx = SCALEX (shell, x) - shell->offset_x; + *ny = SCALEY (shell, y) - shell->offset_y; +} + +/** + * gimp_display_shell_unzoom_xy_f: + * @shell: a #GimpDisplayShell + * @x: x coordinate in display coordinates + * @y: y coordinate in display coordinates + * @nx: place to return x coordinate in image coordinates + * @ny: place to return y coordinate in image coordinates + * + * This function is identical to gimp_display_shell_unzoom_xy(), + * except that the input and output coordinates are doubles rather than + * ints, and consequently there is no option related to rounding. + **/ +void +gimp_display_shell_unzoom_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + *nx = (x + shell->offset_x) / shell->scale_x; + *ny = (y + shell->offset_y) / shell->scale_y; +} + +/** + * gimp_display_shell_zoom_segments: + * @shell: a #GimpDisplayShell + * @src_segs: array of segments in image coordinates + * @dest_segs: returns the corresponding segments in display coordinates + * @n_segs: number of segments + * + * Zooms from image coordinates to display coordinates, so that + * objects can be rendered at the correct points on the display. + **/ +void +gimp_display_shell_zoom_segments (GimpDisplayShell *shell, + const GimpBoundSeg *src_segs, + GimpSegment *dest_segs, + gint n_segs, + gdouble offset_x, + gdouble offset_y) +{ + gint i; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + for (i = 0; i < n_segs ; i++) + { + gdouble x1, x2; + gdouble y1, y2; + + x1 = src_segs[i].x1 + offset_x; + x2 = src_segs[i].x2 + offset_x; + y1 = src_segs[i].y1 + offset_y; + y2 = src_segs[i].y2 + offset_y; + + dest_segs[i].x1 = SCALEX (shell, x1) - shell->offset_x; + dest_segs[i].x2 = SCALEX (shell, x2) - shell->offset_x; + dest_segs[i].y1 = SCALEY (shell, y1) - shell->offset_y; + dest_segs[i].y2 = SCALEY (shell, y2) - shell->offset_y; + } +} + +/** + * gimp_display_shell_rotate_coords: + * @shell: a #GimpDisplayShell + * @image_coords: unrotated display coordinates + * @display_coords: returns the corresponding rotated display coordinates + * + * Rotates from unrotated display coordinates to rotated display + * coordinates, so that objects can be rendered at the correct points + * on the display. + **/ +void +gimp_display_shell_rotate_coords (GimpDisplayShell *shell, + const GimpCoords *unrotated_coords, + GimpCoords *rotated_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (unrotated_coords != NULL); + g_return_if_fail (rotated_coords != NULL); + + *rotated_coords = *unrotated_coords; + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, + &rotated_coords->x, + &rotated_coords->y); +} + +/** + * gimp_display_shell_unrotate_coords: + * @shell: a #GimpDisplayShell + * @display_coords: rotated display coordinates + * @image_coords: returns the corresponding unrotated display coordinates + * + * Rotates from rotated display coordinates to unrotated display coordinates. + **/ +void +gimp_display_shell_unrotate_coords (GimpDisplayShell *shell, + const GimpCoords *rotated_coords, + GimpCoords *unrotated_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (rotated_coords != NULL); + g_return_if_fail (unrotated_coords != NULL); + + *unrotated_coords = *rotated_coords; + + if (shell->rotate_untransform) + cairo_matrix_transform_point (shell->rotate_untransform, + &unrotated_coords->x, + &unrotated_coords->y); +} + +/** + * gimp_display_shell_rotate_xy: + * @shell: + * @x: + * @y: + * @nx: + * @ny: + * + * Rotates an unrotated display coordinate to a rotated shell coordinate. + **/ +void +gimp_display_shell_rotate_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny) +{ + gint64 tx; + gint64 ty; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, &x, &y); + + tx = x; + ty = y; + + /* The projected coordinates might overflow a gint in the case of + * big images at high zoom levels, so we clamp them here to avoid + * problems. + */ + *nx = CLAMP (tx, G_MININT, G_MAXINT); + *ny = CLAMP (ty, G_MININT, G_MAXINT); +} + +/** + * gimp_display_shell_unrotate_xy: + * @shell: a #GimpDisplayShell + * @x: x coordinate in rotated display coordinates + * @y: y coordinate in rotated display coordinates + * @nx: returns x oordinate in unrotated display coordinates + * @ny: returns y coordinate in unrotated display coordinates + * + * Rotate from rotated display coordinates to unrotated display + * coordinates. + **/ +void +gimp_display_shell_unrotate_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + if (shell->rotate_untransform) + { + gdouble fx = x; + gdouble fy = y; + + cairo_matrix_transform_point (shell->rotate_untransform, &fx, &fy); + + *nx = CLAMP (fx, G_MININT, G_MAXINT); + *ny = CLAMP (fy, G_MININT, G_MAXINT); + } + else + { + *nx = x; + *ny = y; + } +} + +/** + * gimp_display_shell_rotate_xy_f: + * @shell: a #GimpDisplayShell + * @x: image x coordinate of point + * @y: image y coordinate of point + * @nx: returned shell canvas x coordinate + * @ny: returned shell canvas y coordinate + * + * Rotates from untransformed display coordinates to rotated display + * coordinates. + **/ +void +gimp_display_shell_rotate_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + *nx = x; + *ny = y; + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, nx, ny); +} + +/** + * gimp_display_shell_unrotate_xy_f: + * @shell: a #GimpDisplayShell + * @x: x coordinate in rotated display coordinates + * @y: y coordinate in rotated display coordinates + * @nx: place to return x coordinate in unrotated display coordinates + * @ny: place to return y coordinate in unrotated display coordinates + * + * This function is identical to gimp_display_shell_unrotate_xy(), + * except that the input and output coordinates are doubles rather + * than ints. + **/ +void +gimp_display_shell_unrotate_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + *nx = x; + *ny = y; + + if (shell->rotate_untransform) + cairo_matrix_transform_point (shell->rotate_untransform, nx, ny); +} + +void +gimp_display_shell_rotate_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->rotate_transform) + { + gdouble tx1 = x1; + gdouble ty1 = y1; + gdouble tx2 = x1; + gdouble ty2 = y2; + gdouble tx3 = x2; + gdouble ty3 = y1; + gdouble tx4 = x2; + gdouble ty4 = y2; + + cairo_matrix_transform_point (shell->rotate_transform, &tx1, &ty1); + cairo_matrix_transform_point (shell->rotate_transform, &tx2, &ty2); + cairo_matrix_transform_point (shell->rotate_transform, &tx3, &ty3); + cairo_matrix_transform_point (shell->rotate_transform, &tx4, &ty4); + + *nx1 = MIN4 (tx1, tx2, tx3, tx4); + *ny1 = MIN4 (ty1, ty2, ty3, ty4); + *nx2 = MAX4 (tx1, tx2, tx3, tx4); + *ny2 = MAX4 (ty1, ty2, ty3, ty4); + } + else + { + *nx1 = x1; + *ny1 = y1; + *nx2 = x2; + *ny2 = y2; + } +} + +void +gimp_display_shell_unrotate_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->rotate_untransform) + { + gdouble tx1 = x1; + gdouble ty1 = y1; + gdouble tx2 = x1; + gdouble ty2 = y2; + gdouble tx3 = x2; + gdouble ty3 = y1; + gdouble tx4 = x2; + gdouble ty4 = y2; + + cairo_matrix_transform_point (shell->rotate_untransform, &tx1, &ty1); + cairo_matrix_transform_point (shell->rotate_untransform, &tx2, &ty2); + cairo_matrix_transform_point (shell->rotate_untransform, &tx3, &ty3); + cairo_matrix_transform_point (shell->rotate_untransform, &tx4, &ty4); + + *nx1 = MIN4 (tx1, tx2, tx3, tx4); + *ny1 = MIN4 (ty1, ty2, ty3, ty4); + *nx2 = MAX4 (tx1, tx2, tx3, tx4); + *ny2 = MAX4 (ty1, ty2, ty3, ty4); + } + else + { + *nx1 = x1; + *ny1 = y1; + *nx2 = x2; + *ny2 = y2; + } +} + +/** + * gimp_display_shell_transform_coords: + * @shell: a #GimpDisplayShell + * @image_coords: image coordinates + * @display_coords: returns the corresponding display coordinates + * + * Transforms from image coordinates to display coordinates, so that + * objects can be rendered at the correct points on the display. + **/ +void +gimp_display_shell_transform_coords (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GimpCoords *display_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (image_coords != NULL); + g_return_if_fail (display_coords != NULL); + + *display_coords = *image_coords; + + display_coords->x = SCALEX (shell, image_coords->x); + display_coords->y = SCALEY (shell, image_coords->y); + + display_coords->x -= shell->offset_x; + display_coords->y -= shell->offset_y; + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, + &display_coords->x, + &display_coords->y); +} + +/** + * gimp_display_shell_untransform_coords: + * @shell: a #GimpDisplayShell + * @display_coords: display coordinates + * @image_coords: returns the corresponding image coordinates + * + * Transforms from display coordinates to image coordinates, so that + * points on the display can be mapped to points in the image. + **/ +void +gimp_display_shell_untransform_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (display_coords != NULL); + g_return_if_fail (image_coords != NULL); + + *image_coords = *display_coords; + + if (shell->rotate_untransform) + cairo_matrix_transform_point (shell->rotate_untransform, + &image_coords->x, + &image_coords->y); + + image_coords->x += shell->offset_x; + image_coords->y += shell->offset_y; + + image_coords->x /= shell->scale_x; + image_coords->y /= shell->scale_y; + + image_coords->xscale = shell->scale_x; + image_coords->yscale = shell->scale_y; + image_coords->angle = shell->rotate_angle / 360.0; + image_coords->reflect = shell->flip_horizontally ^ shell->flip_vertically; + + if (shell->flip_vertically) + image_coords->angle += 0.5; +} + +/** + * gimp_display_shell_transform_xy: + * @shell: + * @x: + * @y: + * @nx: + * @ny: + * + * Transforms an image coordinate to a shell coordinate. + **/ +void +gimp_display_shell_transform_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny) +{ + gint64 tx; + gint64 ty; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + tx = x * shell->scale_x; + ty = y * shell->scale_y; + + tx -= shell->offset_x; + ty -= shell->offset_y; + + if (shell->rotate_transform) + { + gdouble fx = tx; + gdouble fy = ty; + + cairo_matrix_transform_point (shell->rotate_transform, &fx, &fy); + + tx = fx; + ty = fy; + } + + /* The projected coordinates might overflow a gint in the case of + * big images at high zoom levels, so we clamp them here to avoid + * problems. + */ + *nx = CLAMP (tx, G_MININT, G_MAXINT); + *ny = CLAMP (ty, G_MININT, G_MAXINT); +} + +/** + * gimp_display_shell_untransform_xy: + * @shell: a #GimpDisplayShell + * @x: x coordinate in display coordinates + * @y: y coordinate in display coordinates + * @nx: returns x oordinate in image coordinates + * @ny: returns y coordinate in image coordinates + * @round: if %TRUE, round the results to the nearest integer; + * if %FALSE, simply cast them to @gint. + * + * Transform from display coordinates to image coordinates, so that + * points on the display can be mapped to the corresponding points + * in the image. + **/ +void +gimp_display_shell_untransform_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny, + gboolean round) +{ + gint64 tx; + gint64 ty; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + if (shell->rotate_untransform) + { + gdouble fx = x; + gdouble fy = y; + + cairo_matrix_transform_point (shell->rotate_untransform, &fx, &fy); + + x = fx; + y = fy; + } + + if (round) + { + tx = SIGNED_ROUND (((gdouble) x + shell->offset_x) / shell->scale_x); + ty = SIGNED_ROUND (((gdouble) y + shell->offset_y) / shell->scale_y); + } + else + { + tx = ((gint64) x + shell->offset_x) / shell->scale_x; + ty = ((gint64) y + shell->offset_y) / shell->scale_y; + } + + *nx = CLAMP (tx, G_MININT, G_MAXINT); + *ny = CLAMP (ty, G_MININT, G_MAXINT); +} + +/** + * gimp_display_shell_transform_xy_f: + * @shell: a #GimpDisplayShell + * @x: image x coordinate of point + * @y: image y coordinate of point + * @nx: returned shell canvas x coordinate + * @ny: returned shell canvas y coordinate + * + * Transforms from image coordinates to display shell canvas + * coordinates. + **/ +void +gimp_display_shell_transform_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + *nx = SCALEX (shell, x) - shell->offset_x; + *ny = SCALEY (shell, y) - shell->offset_y; + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, nx, ny); +} + +/** + * gimp_display_shell_untransform_xy_f: + * @shell: a #GimpDisplayShell + * @x: x coordinate in display coordinates + * @y: y coordinate in display coordinates + * @nx: place to return x coordinate in image coordinates + * @ny: place to return y coordinate in image coordinates + * + * This function is identical to gimp_display_shell_untransform_xy(), + * except that the input and output coordinates are doubles rather than + * ints, and consequently there is no option related to rounding. + **/ +void +gimp_display_shell_untransform_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx != NULL); + g_return_if_fail (ny != NULL); + + if (shell->rotate_untransform) + cairo_matrix_transform_point (shell->rotate_untransform, &x, &y); + + *nx = (x + shell->offset_x) / shell->scale_x; + *ny = (y + shell->offset_y) / shell->scale_y; +} + +void +gimp_display_shell_transform_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx1 != NULL); + g_return_if_fail (ny1 != NULL); + g_return_if_fail (nx2 != NULL); + g_return_if_fail (ny2 != NULL); + + if (shell->rotate_transform) + { + gdouble tx1, ty1; + gdouble tx2, ty2; + gdouble tx3, ty3; + gdouble tx4, ty4; + + gimp_display_shell_transform_xy_f_noround (shell, x1, y1, &tx1, &ty1); + gimp_display_shell_transform_xy_f_noround (shell, x1, y2, &tx2, &ty2); + gimp_display_shell_transform_xy_f_noround (shell, x2, y1, &tx3, &ty3); + gimp_display_shell_transform_xy_f_noround (shell, x2, y2, &tx4, &ty4); + + *nx1 = MIN4 (tx1, tx2, tx3, tx4); + *ny1 = MIN4 (ty1, ty2, ty3, ty4); + *nx2 = MAX4 (tx1, tx2, tx3, tx4); + *ny2 = MAX4 (ty1, ty2, ty3, ty4); + } + else + { + gimp_display_shell_transform_xy_f_noround (shell, x1, y1, nx1, ny1); + gimp_display_shell_transform_xy_f_noround (shell, x2, y2, nx2, ny2); + } +} + +void +gimp_display_shell_untransform_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (nx1 != NULL); + g_return_if_fail (ny1 != NULL); + g_return_if_fail (nx2 != NULL); + g_return_if_fail (ny2 != NULL); + + if (shell->rotate_untransform) + { + gdouble tx1, ty1; + gdouble tx2, ty2; + gdouble tx3, ty3; + gdouble tx4, ty4; + + gimp_display_shell_untransform_xy_f (shell, x1, y1, &tx1, &ty1); + gimp_display_shell_untransform_xy_f (shell, x1, y2, &tx2, &ty2); + gimp_display_shell_untransform_xy_f (shell, x2, y1, &tx3, &ty3); + gimp_display_shell_untransform_xy_f (shell, x2, y2, &tx4, &ty4); + + *nx1 = MIN4 (tx1, tx2, tx3, tx4); + *ny1 = MIN4 (ty1, ty2, ty3, ty4); + *nx2 = MAX4 (tx1, tx2, tx3, tx4); + *ny2 = MAX4 (ty1, ty2, ty3, ty4); + } + else + { + gimp_display_shell_untransform_xy_f (shell, x1, y1, nx1, ny1); + gimp_display_shell_untransform_xy_f (shell, x2, y2, nx2, ny2); + } +} + +/* transforms a bounding box from image-space, uniformly scaled by a factor of + * 'scale', to display-space. this is equivalent to, but more accurate than, + * dividing the input by 'scale', and using + * gimp_display_shell_transform_bounds(), in particular, in that if 'scale' + * equals 'shell->scale_x' or 'shell->scale_y', there is no loss in accuracy + * in the corresponding dimension due to scaling (although there might be loss + * of accuracy due to rotation or translation.) + */ +void +gimp_display_shell_transform_bounds_with_scale (GimpDisplayShell *shell, + gdouble scale, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + gdouble factor_x; + gdouble factor_y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (scale > 0.0); + g_return_if_fail (nx1 != NULL); + g_return_if_fail (ny1 != NULL); + g_return_if_fail (nx2 != NULL); + g_return_if_fail (ny2 != NULL); + + factor_x = shell->scale_x / scale; + factor_y = shell->scale_y / scale; + + x1 = x1 * factor_x - shell->offset_x; + y1 = y1 * factor_y - shell->offset_y; + x2 = x2 * factor_x - shell->offset_x; + y2 = y2 * factor_y - shell->offset_y; + + gimp_display_shell_rotate_bounds (shell, + x1, y1, x2, y2, + nx1, ny1, nx2, ny2); +} + +/* transforms a bounding box from display-space to image-space, uniformly + * scaled by a factor of 'scale'. this is equivalent to, but more accurate + * than, using gimp_display_shell_untransform_bounds(), and multiplying the + * output by 'scale', in particular, in that if 'scale' equals 'shell->scale_x' + * or 'shell->scale_y', there is no loss in accuracy in the corresponding + * dimension due to scaling (although there might be loss of accuracy due to + * rotation or translation.) + */ +void +gimp_display_shell_untransform_bounds_with_scale (GimpDisplayShell *shell, + gdouble scale, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2) +{ + gdouble factor_x; + gdouble factor_y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (scale > 0.0); + g_return_if_fail (nx1 != NULL); + g_return_if_fail (ny1 != NULL); + g_return_if_fail (nx2 != NULL); + g_return_if_fail (ny2 != NULL); + + factor_x = scale / shell->scale_x; + factor_y = scale / shell->scale_y; + + gimp_display_shell_unrotate_bounds (shell, + x1, y1, x2, y2, + nx1, ny1, nx2, ny2); + + *nx1 = (*nx1 + shell->offset_x) * factor_x; + *ny1 = (*ny1 + shell->offset_y) * factor_y; + *nx2 = (*nx2 + shell->offset_x) * factor_x; + *ny2 = (*ny2 + shell->offset_y) * factor_y; +} + +/** + * gimp_display_shell_untransform_viewport: + * @shell: a #GimpDisplayShell + * @clip: whether to clip the result to the image bounds + * @x: returns image x coordinate of display upper left corner + * @y: returns image y coordinate of display upper left corner + * @width: returns width of display measured in image coordinates + * @height: returns height of display measured in image coordinates + * + * This function calculates the part of the image, in image coordinates, + * that corresponds to the display viewport. + **/ +void +gimp_display_shell_untransform_viewport (GimpDisplayShell *shell, + gboolean clip, + gint *x, + gint *y, + gint *width, + gint *height) +{ + gdouble x1, y1, x2, y2; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_untransform_bounds (shell, + 0, 0, + shell->disp_width, shell->disp_height, + &x1, &y1, + &x2, &y2); + + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + + if (clip) + { + GimpImage *image = gimp_display_get_image (shell->display); + + x1 = MAX (x1, 0); + y1 = MAX (y1, 0); + x2 = MIN (x2, gimp_image_get_width (image)); + y2 = MIN (y2, gimp_image_get_height (image)); + } + + if (x) *x = x1; + if (y) *y = y1; + if (width) *width = x2 - x1; + if (height) *height = y2 - y1; +} + + +/* private functions */ + +/* Same as gimp_display_shell_transform_xy_f(), but doesn't do any rounding + * for the transformed coordinates. + */ +static void +gimp_display_shell_transform_xy_f_noround (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny) +{ + *nx = shell->scale_x * x - shell->offset_x; + *ny = shell->scale_y * y - shell->offset_y; + + if (shell->rotate_transform) + cairo_matrix_transform_point (shell->rotate_transform, nx, ny); +} diff --git a/app/display/gimpdisplayshell-transform.h b/app/display/gimpdisplayshell-transform.h new file mode 100644 index 0000000..1877b13 --- /dev/null +++ b/app/display/gimpdisplayshell-transform.h @@ -0,0 +1,200 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_TRANSFORM_H__ +#define __GIMP_DISPLAY_SHELL_TRANSFORM_H__ + + +/* zoom: functions to transform from image space to unrotated display + * space and back, taking into account scroll offset and scale + */ + +void gimp_display_shell_zoom_coords (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GimpCoords *display_coords); +void gimp_display_shell_unzoom_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords); + +void gimp_display_shell_zoom_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny); +void gimp_display_shell_unzoom_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny, + gboolean round); + +void gimp_display_shell_zoom_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); +void gimp_display_shell_unzoom_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); + +void gimp_display_shell_zoom_segments (GimpDisplayShell *shell, + const GimpBoundSeg *src_segs, + GimpSegment *dest_segs, + gint n_segs, + gdouble offset_x, + gdouble offset_y); + + +/* rotate: functions to transform from unrotated and unflipped but + * zoomed display space to rotated and filpped display space and back + */ + +void gimp_display_shell_rotate_coords (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GimpCoords *display_coords); +void gimp_display_shell_unrotate_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords); + +void gimp_display_shell_rotate_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny); +void gimp_display_shell_unrotate_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny); + +void gimp_display_shell_rotate_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); +void gimp_display_shell_unrotate_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); + +void gimp_display_shell_rotate_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); +void gimp_display_shell_unrotate_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); + + +/* transform: functions to transform from image space to rotated + * display space and back, taking into account scroll offset, scale, + * rotation and flipping + */ + +void gimp_display_shell_transform_coords (GimpDisplayShell *shell, + const GimpCoords *image_coords, + GimpCoords *display_coords); +void gimp_display_shell_untransform_coords (GimpDisplayShell *shell, + const GimpCoords *display_coords, + GimpCoords *image_coords); + +void gimp_display_shell_transform_xy (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gint *nx, + gint *ny); +void gimp_display_shell_untransform_xy (GimpDisplayShell *shell, + gint x, + gint y, + gint *nx, + gint *ny, + gboolean round); + +void gimp_display_shell_transform_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); +void gimp_display_shell_untransform_xy_f (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble *nx, + gdouble *ny); + +void gimp_display_shell_transform_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); +void gimp_display_shell_untransform_bounds (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); + +void gimp_display_shell_transform_bounds_with_scale (GimpDisplayShell *shell, + gdouble scale, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); +void gimp_display_shell_untransform_bounds_with_scale (GimpDisplayShell *shell, + gdouble scale, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *nx1, + gdouble *ny1, + gdouble *nx2, + gdouble *ny2); + +void gimp_display_shell_untransform_viewport (GimpDisplayShell *shell, + gboolean clip, + gint *x, + gint *y, + gint *width, + gint *height); + + +#endif /* __GIMP_DISPLAY_SHELL_TRANSFORM_H__ */ diff --git a/app/display/gimpdisplayshell-utils.c b/app/display/gimpdisplayshell-utils.c new file mode 100644 index 0000000..8e85e71 --- /dev/null +++ b/app/display/gimpdisplayshell-utils.c @@ -0,0 +1,220 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpimage.h" +#include "core/gimpunit.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-utils.h" + +#include "gimp-intl.h" + +void +gimp_display_shell_get_constrained_line_params (GimpDisplayShell *shell, + gdouble *offset_angle, + gdouble *xres, + gdouble *yres) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (offset_angle != NULL); + g_return_if_fail (xres != NULL); + g_return_if_fail (yres != NULL); + + if (shell->flip_horizontally ^ shell->flip_vertically) + *offset_angle = +shell->rotate_angle; + else + *offset_angle = -shell->rotate_angle; + + *xres = 1.0; + *yres = 1.0; + + if (! shell->dot_for_dot) + { + GimpImage *image = gimp_display_get_image (shell->display); + + if (image) + gimp_image_get_resolution (image, xres, yres); + } +} + +void +gimp_display_shell_constrain_line (GimpDisplayShell *shell, + gdouble start_x, + gdouble start_y, + gdouble *end_x, + gdouble *end_y, + gint n_snap_lines) +{ + gdouble offset_angle; + gdouble xres, yres; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (end_x != NULL); + g_return_if_fail (end_y != NULL); + + gimp_display_shell_get_constrained_line_params (shell, + &offset_angle, + &xres, &yres); + + gimp_constrain_line (start_x, start_y, + end_x, end_y, + n_snap_lines, + offset_angle, + xres, yres); +} + +gdouble +gimp_display_shell_constrain_angle (GimpDisplayShell *shell, + gdouble angle, + gint n_snap_lines) +{ + gdouble x, y; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), 0.0); + + x = cos (angle); + y = sin (angle); + + gimp_display_shell_constrain_line (shell, + 0.0, 0.0, + &x, &y, + n_snap_lines); + + return atan2 (y, x); +} + +/** + * gimp_display_shell_get_line_status: + * @status: initial status text. + * @separator: separator text between the line information and @status. + * @shell: #GimpDisplayShell this status text will be displayed for. + * @x1: abscissa of first point. + * @y1: ordinate of first point. + * @x2: abscissa of second point. + * @y2: ordinate of second point. + * + * Utility function to prepend the status message with a distance and + * angle value. Obviously this is only to be used for tools when it + * makes sense, and in particular when there is a concept of line. For + * instance when shift-clicking a painting tool or in the blend tool, + * etc. + * This utility prevents code duplication but also ensures a common + * display for every tool where such a status is needed. It will take + * into account the shell unit settings and will use the ideal digit + * precision according to current image resolution. + * + * Return value: a newly allocated string containing the enhanced status. + **/ +gchar * +gimp_display_shell_get_line_status (GimpDisplayShell *shell, + const gchar *status, + const gchar *separator, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + GimpImage *image; + gchar *enhanced_status; + gdouble xres; + gdouble yres; + gdouble dx, dy, pixel_dist; + gdouble angle; + + image = gimp_display_get_image (shell->display); + if (! image) + { + /* This makes no sense to add line information when no image is + * attached to the display. */ + return g_strdup (status); + } + + if (shell->unit == GIMP_UNIT_PIXEL) + xres = yres = 1.0; + else + gimp_image_get_resolution (image, &xres, &yres); + + dx = x2 - x1; + dy = y2 - y1; + pixel_dist = sqrt (SQR (dx) + SQR (dy)); + + if (dx) + { + angle = gimp_rad_to_deg (atan ((dy/yres) / (dx/xres))); + if (dx > 0) + { + if (dy > 0) + angle = 360.0 - angle; + else if (dy < 0) + angle = -angle; + } + else + { + angle = 180.0 - angle; + } + } + else if (dy) + { + angle = dy > 0 ? 270.0 : 90.0; + } + else + { + angle = 0.0; + } + + if (shell->unit == GIMP_UNIT_PIXEL) + { + enhanced_status = g_strdup_printf ("%.1f %s, %.2f\302\260%s%s", + pixel_dist, _("pixels"), angle, + separator, status); + } + else + { + gdouble inch_dist; + gdouble unit_dist; + gint digits = 0; + + /* The distance in unit. */ + inch_dist = sqrt (SQR (dx / xres) + SQR (dy / yres)); + unit_dist = gimp_unit_get_factor (shell->unit) * inch_dist; + + /* The ideal digit precision for unit in current resolution. */ + if (inch_dist) + digits = gimp_unit_get_scaled_digits (shell->unit, + pixel_dist / inch_dist); + + enhanced_status = g_strdup_printf ("%.*f %s, %.2f\302\260%s%s", + digits, unit_dist, + gimp_unit_get_symbol (shell->unit), + angle, separator, status); + + } + + return enhanced_status; +} diff --git a/app/display/gimpdisplayshell-utils.h b/app/display/gimpdisplayshell-utils.h new file mode 100644 index 0000000..3eb52e3 --- /dev/null +++ b/app/display/gimpdisplayshell-utils.h @@ -0,0 +1,45 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_UTILS_H__ +#define __GIMP_DISPLAY_SHELL_UTILS_H__ + + +void gimp_display_shell_get_constrained_line_params (GimpDisplayShell *shell, + gdouble *offset_angle, + gdouble *xres, + gdouble *yres); +void gimp_display_shell_constrain_line (GimpDisplayShell *shell, + gdouble start_x, + gdouble start_y, + gdouble *end_x, + gdouble *end_y, + gint n_snap_lines); +gdouble gimp_display_shell_constrain_angle (GimpDisplayShell *shell, + gdouble angle, + gint n_snap_lines); + +gchar * gimp_display_shell_get_line_status (GimpDisplayShell *shell, + const gchar *status, + const gchar *separator, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + + +#endif /* __GIMP_DISPLAY_SHELL_UTILS_H__ */ diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c new file mode 100644 index 0000000..9603e4f --- /dev/null +++ b/app/display/gimpdisplayshell.c @@ -0,0 +1,2141 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" +#include "tools/tools-types.h" + +#include "config/gimpcoreconfig.h" +#include "config/gimpdisplayconfig.h" +#include "config/gimpdisplayoptions.h" + +#include "core/gimp.h" +#include "core/gimp-utils.h" +#include "core/gimpchannel.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-grid.h" +#include "core/gimpimage-guides.h" +#include "core/gimpimage-snap.h" +#include "core/gimppickable.h" +#include "core/gimpprojectable.h" +#include "core/gimpprojection.h" +#include "core/gimpmarshal.h" +#include "core/gimptemplate.h" + +#include "widgets/gimpdevices.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "tools/tool_manager.h" + +#include "gimpcanvas.h" +#include "gimpcanvascanvasboundary.h" +#include "gimpcanvaslayerboundary.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-callbacks.h" +#include "gimpdisplayshell-cursor.h" +#include "gimpdisplayshell-dnd.h" +#include "gimpdisplayshell-expose.h" +#include "gimpdisplayshell-filter.h" +#include "gimpdisplayshell-handlers.h" +#include "gimpdisplayshell-items.h" +#include "gimpdisplayshell-profile.h" +#include "gimpdisplayshell-progress.h" +#include "gimpdisplayshell-render.h" +#include "gimpdisplayshell-rotate.h" +#include "gimpdisplayshell-rulers.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-scrollbars.h" +#include "gimpdisplayshell-selection.h" +#include "gimpdisplayshell-title.h" +#include "gimpdisplayshell-tool-events.h" +#include "gimpdisplayshell-transform.h" +#include "gimpimagewindow.h" +#include "gimpmotionbuffer.h" +#include "gimpstatusbar.h" + +#include "about.h" +#include "gimp-log.h" +#include "gimp-priorities.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_POPUP_MANAGER, + PROP_INITIAL_SCREEN, + PROP_INITIAL_MONITOR, + PROP_DISPLAY, + PROP_UNIT, + PROP_TITLE, + PROP_STATUS, + PROP_ICON, + PROP_SHOW_ALL, + PROP_INFINITE_CANVAS +}; + +enum +{ + SCALED, + SCROLLED, + ROTATED, + RECONNECT, + LAST_SIGNAL +}; + + +typedef struct _GimpDisplayShellOverlay GimpDisplayShellOverlay; + +struct _GimpDisplayShellOverlay +{ + gdouble image_x; + gdouble image_y; + GimpHandleAnchor anchor; + gint spacing_x; + gint spacing_y; +}; + + +/* local function prototypes */ + +static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface); + +static void gimp_display_shell_constructed (GObject *object); +static void gimp_display_shell_dispose (GObject *object); +static void gimp_display_shell_finalize (GObject *object); +static void gimp_display_shell_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_display_shell_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_display_shell_unrealize (GtkWidget *widget); +static void gimp_display_shell_unmap (GtkWidget *widget); +static void gimp_display_shell_screen_changed (GtkWidget *widget, + GdkScreen *previous); +static gboolean gimp_display_shell_popup_menu (GtkWidget *widget); + +static void gimp_display_shell_real_scaled (GimpDisplayShell *shell); +static void gimp_display_shell_real_scrolled (GimpDisplayShell *shell); +static void gimp_display_shell_real_rotated (GimpDisplayShell *shell); + +static const guint8 * + gimp_display_shell_get_icc_profile(GimpColorManaged *managed, + gsize *len); +static GimpColorProfile * + gimp_display_shell_get_color_profile(GimpColorManaged *managed); +static void gimp_display_shell_profile_changed(GimpColorManaged *managed); + +static void gimp_display_shell_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gpointer data); +static void gimp_display_shell_zoom_button_callback + (GimpDisplayShell *shell, + GtkWidget *zoom_button); +static void gimp_display_shell_sync_config (GimpDisplayShell *shell, + GimpDisplayConfig *config); + +static void gimp_display_shell_remove_overlay (GtkWidget *canvas, + GtkWidget *child, + GimpDisplayShell *shell); +static void gimp_display_shell_transform_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble *x, + gdouble *y); + + +G_DEFINE_TYPE_WITH_CODE (GimpDisplayShell, gimp_display_shell, + GTK_TYPE_EVENT_BOX, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS, + gimp_display_shell_progress_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED, + gimp_color_managed_iface_init)) + + +#define parent_class gimp_display_shell_parent_class + +static guint display_shell_signals[LAST_SIGNAL] = { 0 }; + + +static const gchar display_rc_style[] = + "style \"check-button-style\"\n" + "{\n" + " GtkToggleButton::child-displacement-x = 0\n" + " GtkToggleButton::child-displacement-y = 0\n" + "}\n" + "widget \"*\" style \"check-button-style\""; + +static void +gimp_display_shell_class_init (GimpDisplayShellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + display_shell_signals[SCALED] = + g_signal_new ("scaled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDisplayShellClass, scaled), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + display_shell_signals[SCROLLED] = + g_signal_new ("scrolled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDisplayShellClass, scrolled), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + display_shell_signals[ROTATED] = + g_signal_new ("rotated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDisplayShellClass, rotated), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + display_shell_signals[RECONNECT] = + g_signal_new ("reconnect", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDisplayShellClass, reconnect), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->constructed = gimp_display_shell_constructed; + object_class->dispose = gimp_display_shell_dispose; + object_class->finalize = gimp_display_shell_finalize; + object_class->set_property = gimp_display_shell_set_property; + object_class->get_property = gimp_display_shell_get_property; + + widget_class->unrealize = gimp_display_shell_unrealize; + widget_class->unmap = gimp_display_shell_unmap; + widget_class->screen_changed = gimp_display_shell_screen_changed; + widget_class->popup_menu = gimp_display_shell_popup_menu; + + klass->scaled = gimp_display_shell_real_scaled; + klass->scrolled = gimp_display_shell_real_scrolled; + klass->rotated = gimp_display_shell_real_rotated; + klass->reconnect = NULL; + + g_object_class_install_property (object_class, PROP_POPUP_MANAGER, + g_param_spec_object ("popup-manager", + NULL, NULL, + GIMP_TYPE_UI_MANAGER, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_INITIAL_SCREEN, + g_param_spec_object ("initial-screen", + NULL, NULL, + GDK_TYPE_SCREEN, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_INITIAL_MONITOR, + g_param_spec_int ("initial-monitor", + NULL, NULL, + 0, 16, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_DISPLAY, + g_param_spec_object ("display", NULL, NULL, + GIMP_TYPE_DISPLAY, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_UNIT, + gimp_param_spec_unit ("unit", NULL, NULL, + TRUE, FALSE, + GIMP_UNIT_PIXEL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TITLE, + g_param_spec_string ("title", NULL, NULL, + GIMP_NAME, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_STATUS, + g_param_spec_string ("status", NULL, NULL, + NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_ICON, + g_param_spec_object ("icon", NULL, NULL, + GDK_TYPE_PIXBUF, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SHOW_ALL, + g_param_spec_boolean ("show-all", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_INFINITE_CANVAS, + g_param_spec_boolean ("infinite-canvas", + NULL, NULL, + FALSE, + GIMP_PARAM_READABLE)); + + gtk_rc_parse_string (display_rc_style); +} + +static void +gimp_color_managed_iface_init (GimpColorManagedInterface *iface) +{ + iface->get_icc_profile = gimp_display_shell_get_icc_profile; + iface->get_color_profile = gimp_display_shell_get_color_profile; + iface->profile_changed = gimp_display_shell_profile_changed; +} + +static void +gimp_display_shell_init (GimpDisplayShell *shell) +{ + shell->options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS, NULL); + shell->fullscreen_options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS_FULLSCREEN, NULL); + shell->no_image_options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS_NO_IMAGE, NULL); + + shell->zoom = gimp_zoom_model_new (); + shell->dot_for_dot = TRUE; + shell->scale_x = 1.0; + shell->scale_y = 1.0; + + shell->show_image = TRUE; + + shell->show_all = FALSE; + + gimp_display_shell_items_init (shell); + + shell->icon_size = 128; + shell->icon_size_small = 96; + + shell->cursor_handedness = GIMP_HANDEDNESS_RIGHT; + shell->current_cursor = (GimpCursorType) -1; + shell->tool_cursor = GIMP_TOOL_CURSOR_NONE; + shell->cursor_modifier = GIMP_CURSOR_MODIFIER_NONE; + shell->override_cursor = (GimpCursorType) -1; + + shell->filter_format = babl_format ("R'G'B'A float"); + + shell->motion_buffer = gimp_motion_buffer_new (); + + g_signal_connect (shell->motion_buffer, "stroke", + G_CALLBACK (gimp_display_shell_buffer_stroke), + shell); + g_signal_connect (shell->motion_buffer, "hover", + G_CALLBACK (gimp_display_shell_buffer_hover), + shell); + + shell->zoom_focus_pointer_queue = g_queue_new (); + + gtk_widget_set_events (GTK_WIDGET (shell), (GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_FOCUS_CHANGE_MASK | + GDK_VISIBILITY_NOTIFY_MASK | + GDK_SCROLL_MASK)); + + /* zoom model callback */ + g_signal_connect_swapped (shell->zoom, "zoomed", + G_CALLBACK (gimp_display_shell_scale_update), + shell); + + /* active display callback */ + g_signal_connect (shell, "button-press-event", + G_CALLBACK (gimp_display_shell_events), + shell); + g_signal_connect (shell, "button-release-event", + G_CALLBACK (gimp_display_shell_events), + shell); + g_signal_connect (shell, "key-press-event", + G_CALLBACK (gimp_display_shell_events), + shell); + + gimp_help_connect (GTK_WIDGET (shell), gimp_standard_help_func, + GIMP_HELP_IMAGE_WINDOW, NULL); +} + +static void +gimp_display_shell_constructed (GObject *object) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object); + GimpDisplayConfig *config; + GimpImage *image; + GtkWidget *main_vbox; + GtkWidget *upper_hbox; + GtkWidget *right_vbox; + GtkWidget *lower_hbox; + GtkWidget *inner_table; + GtkWidget *gtk_image; + GimpAction *action; + gint image_width; + gint image_height; + gint shell_width; + gint shell_height; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_UI_MANAGER (shell->popup_manager)); + gimp_assert (GIMP_IS_DISPLAY (shell->display)); + + config = shell->display->config; + image = gimp_display_get_image (shell->display); + + gimp_display_shell_profile_init (shell); + + if (image) + { + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + } + else + { + /* These values are arbitrary. The width is determined by the + * menubar and the height is chosen to give a window aspect + * ratio of roughly 3:1 (as requested by the UI team). + */ + image_width = GIMP_DEFAULT_IMAGE_WIDTH; + image_height = GIMP_DEFAULT_IMAGE_HEIGHT / 3; + } + + shell->dot_for_dot = config->default_dot_for_dot; + + if (config->monitor_res_from_gdk) + { + gimp_get_monitor_resolution (shell->initial_screen, + shell->initial_monitor, + &shell->monitor_xres, &shell->monitor_yres); + } + else + { + shell->monitor_xres = config->monitor_xres; + shell->monitor_yres = config->monitor_yres; + } + + /* adjust the initial scale -- so that window fits on screen. */ + if (image) + { + gimp_display_shell_set_initial_scale (shell, 1.0, //scale, + &shell_width, &shell_height); + } + else + { + shell_width = -1; + shell_height = image_height; + } + + gimp_display_shell_sync_config (shell, config); + + /* GtkTable widgets are not able to shrink a row/column correctly if + * widgets are attached with GTK_EXPAND even if those widgets have + * other rows/columns in their rowspan/colspan where they could + * nicely expand without disturbing the row/column which is supposed + * to shrink. --Mitch + * + * Changed the packing to use hboxes and vboxes which behave nicer: + * + * shell + * | + * +-- main_vbox + * | + * +-- upper_hbox + * | | + * | +-- inner_table + * | | | + * | | +-- origin + * | | +-- hruler + * | | +-- vruler + * | | +-- canvas + * | | + * | +-- right_vbox + * | | + * | +-- zoom_on_resize_button + * | +-- vscrollbar + * | + * +-- lower_hbox + * | | + * | +-- quick_mask + * | +-- hscrollbar + * | +-- navbutton + * | + * +-- statusbar + * + * Note that we separate "shell" and "main_vbox", so that we can make + * "shell" a GtkEventBox, giving it its own window. This isolates our + * events from those of our ancestors, avoiding some potential slowdowns, + * and making things generally smoother. See bug #778966. + */ + + /* first, set up the container hierarchy *********************************/ + + /* the root vbox */ + + main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (shell), main_vbox); + gtk_widget_show (main_vbox); + + /* a hbox for the inner_table and the vertical scrollbar */ + upper_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (main_vbox), upper_hbox, TRUE, TRUE, 0); + gtk_widget_show (upper_hbox); + + /* the table containing origin, rulers and the canvas */ + inner_table = gtk_table_new (2, 2, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (inner_table), 0, 0); + gtk_table_set_row_spacing (GTK_TABLE (inner_table), 0, 0); + gtk_box_pack_start (GTK_BOX (upper_hbox), inner_table, TRUE, TRUE, 0); + gtk_widget_show (inner_table); + + /* the vbox containing the color button and the vertical scrollbar */ + right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1); + gtk_box_pack_start (GTK_BOX (upper_hbox), right_vbox, FALSE, FALSE, 0); + gtk_widget_show (right_vbox); + + /* the hbox containing the quickmask button, vertical scrollbar and + * the navigation button + */ + lower_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1); + gtk_box_pack_start (GTK_BOX (main_vbox), lower_hbox, FALSE, FALSE, 0); + gtk_widget_show (lower_hbox); + + /* create the scrollbars *************************************************/ + + /* the horizontal scrollbar */ + shell->hsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_width, + 1, 1, image_width)); + shell->hsb = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, shell->hsbdata); + gtk_widget_set_can_focus (shell->hsb, FALSE); + + /* the vertical scrollbar */ + shell->vsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_height, + 1, 1, image_height)); + shell->vsb = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, shell->vsbdata); + gtk_widget_set_can_focus (shell->vsb, FALSE); + + /* create the contents of the inner_table ********************************/ + + /* the menu popup button */ + shell->origin = gtk_event_box_new (); + + gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_MENU_RIGHT, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (shell->origin), gtk_image); + gtk_widget_show (gtk_image); + + g_signal_connect (shell->origin, "button-press-event", + G_CALLBACK (gimp_display_shell_origin_button_press), + shell); + + gimp_help_set_help_data (shell->origin, + _("Access the image menu"), + GIMP_HELP_IMAGE_WINDOW_ORIGIN); + + shell->canvas = gimp_canvas_new (config); + gtk_widget_set_size_request (shell->canvas, shell_width, shell_height); + gtk_container_set_border_width (GTK_CONTAINER (shell->canvas), 10); + + g_signal_connect (shell->canvas, "remove", + G_CALLBACK (gimp_display_shell_remove_overlay), + shell); + + gimp_display_shell_dnd_init (shell); + gimp_display_shell_selection_init (shell); + + /* the horizontal ruler */ + shell->hrule = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL); + gtk_widget_set_events (GTK_WIDGET (shell->hrule), + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + gimp_ruler_add_track_widget (GIMP_RULER (shell->hrule), shell->canvas); + g_signal_connect (shell->hrule, "button-press-event", + G_CALLBACK (gimp_display_shell_hruler_button_press), + shell); + + gimp_help_set_help_data (shell->hrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER); + + /* the vertical ruler */ + shell->vrule = gimp_ruler_new (GTK_ORIENTATION_VERTICAL); + gtk_widget_set_events (GTK_WIDGET (shell->vrule), + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + gimp_ruler_add_track_widget (GIMP_RULER (shell->vrule), shell->canvas); + g_signal_connect (shell->vrule, "button-press-event", + G_CALLBACK (gimp_display_shell_vruler_button_press), + shell); + + gimp_help_set_help_data (shell->vrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER); + + /* set the rulers as track widgets for each other, so we don't end up + * with one ruler wrongly being stuck a few pixels off while we are + * hovering the other + */ + gimp_ruler_add_track_widget (GIMP_RULER (shell->hrule), shell->vrule); + gimp_ruler_add_track_widget (GIMP_RULER (shell->vrule), shell->hrule); + + gimp_devices_add_widget (shell->display->gimp, shell->hrule); + gimp_devices_add_widget (shell->display->gimp, shell->vrule); + + g_signal_connect (shell->canvas, "grab-notify", + G_CALLBACK (gimp_display_shell_canvas_grab_notify), + shell); + + g_signal_connect (shell->canvas, "realize", + G_CALLBACK (gimp_display_shell_canvas_realize), + shell); + g_signal_connect (shell->canvas, "realize", + G_CALLBACK (gimp_display_shell_canvas_realize_after), + shell); + g_signal_connect (shell->canvas, "size-allocate", + G_CALLBACK (gimp_display_shell_canvas_size_allocate), + shell); + g_signal_connect (shell->canvas, "expose-event", + G_CALLBACK (gimp_display_shell_canvas_expose), + shell); + + g_signal_connect (shell->canvas, "enter-notify-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "leave-notify-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "proximity-in-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "proximity-out-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "focus-in-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "focus-out-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "button-press-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "button-release-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "scroll-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "motion-notify-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "key-press-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + g_signal_connect (shell->canvas, "key-release-event", + G_CALLBACK (gimp_display_shell_canvas_tool_events), + shell); + + /* create the contents of the right_vbox *********************************/ + + shell->zoom_button = g_object_new (GTK_TYPE_CHECK_BUTTON, + "draw-indicator", FALSE, + "relief", GTK_RELIEF_NONE, + "width-request", 18, + "height-request", 18, + NULL); + gtk_widget_set_can_focus (shell->zoom_button, FALSE); + + gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_ZOOM_FOLLOW_WINDOW, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (shell->zoom_button), gtk_image); + gtk_widget_show (gtk_image); + + gimp_help_set_help_data (shell->zoom_button, + _("Zoom image when window size changes"), + GIMP_HELP_IMAGE_WINDOW_ZOOM_FOLLOW_BUTTON); + + g_signal_connect_swapped (shell->zoom_button, "toggled", + G_CALLBACK (gimp_display_shell_zoom_button_callback), + shell); + + /* create the contents of the lower_hbox *********************************/ + + /* the quick mask button */ + shell->quick_mask_button = g_object_new (GTK_TYPE_CHECK_BUTTON, + "draw-indicator", FALSE, + "relief", GTK_RELIEF_NONE, + "width-request", 18, + "height-request", 18, + NULL); + gtk_widget_set_can_focus (shell->quick_mask_button, FALSE); + + gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_QUICK_MASK_OFF, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (shell->quick_mask_button), gtk_image); + gtk_widget_show (gtk_image); + + action = gimp_ui_manager_find_action (shell->popup_manager, + "quick-mask", "quick-mask-toggle"); + if (action) + gimp_widget_set_accel_help (shell->quick_mask_button, action); + else + gimp_help_set_help_data (shell->quick_mask_button, + _("Toggle Quick Mask"), + GIMP_HELP_IMAGE_WINDOW_QUICK_MASK_BUTTON); + + g_signal_connect (shell->quick_mask_button, "toggled", + G_CALLBACK (gimp_display_shell_quick_mask_toggled), + shell); + g_signal_connect (shell->quick_mask_button, "button-press-event", + G_CALLBACK (gimp_display_shell_quick_mask_button_press), + shell); + + /* the navigation window button */ + shell->nav_ebox = gtk_event_box_new (); + + gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_NAVIGATION, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (shell->nav_ebox), gtk_image); + gtk_widget_show (gtk_image); + + g_signal_connect (shell->nav_ebox, "button-press-event", + G_CALLBACK (gimp_display_shell_navigation_button_press), + shell); + + gimp_help_set_help_data (shell->nav_ebox, + _("Navigate the image display"), + GIMP_HELP_IMAGE_WINDOW_NAV_BUTTON); + + /* the statusbar ********************************************************/ + + shell->statusbar = gimp_statusbar_new (); + gimp_statusbar_set_shell (GIMP_STATUSBAR (shell->statusbar), shell); + gimp_help_set_help_data (shell->statusbar, NULL, + GIMP_HELP_IMAGE_WINDOW_STATUS_BAR); + gtk_box_pack_end (GTK_BOX (main_vbox), shell->statusbar, FALSE, FALSE, 0); + + /* pack all the widgets **************************************************/ + + /* fill the inner_table */ + gtk_table_attach (GTK_TABLE (inner_table), shell->origin, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach (GTK_TABLE (inner_table), shell->hrule, 1, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach (GTK_TABLE (inner_table), shell->vrule, 0, 1, 1, 2, + GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_table_attach (GTK_TABLE (inner_table), shell->canvas, 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + + /* fill the right_vbox */ + gtk_box_pack_start (GTK_BOX (right_vbox), + shell->zoom_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (right_vbox), + shell->vsb, TRUE, TRUE, 0); + + /* fill the lower_hbox */ + gtk_box_pack_start (GTK_BOX (lower_hbox), + shell->quick_mask_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (lower_hbox), + shell->hsb, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (lower_hbox), + shell->nav_ebox, FALSE, FALSE, 0); + + /* show everything that is always shown ***********************************/ + + gtk_widget_show (GTK_WIDGET (shell->canvas)); + + if (image) + { + gimp_display_shell_connect (shell); + + /* After connecting to the image we want to center it. Since we + * not even finished creating the display shell, we can safely + * assume we will get a size-allocate later. + */ + shell->size_allocate_center_image = TRUE; + } + else + { +#if 0 + /* Disabled because it sets GDK_POINTER_MOTION_HINT on + * shell->canvas. For info see Bug 677375 + */ + gimp_help_set_help_data (shell->canvas, + _("Drop image files here to open them"), + NULL); +#endif + + gimp_statusbar_empty (GIMP_STATUSBAR (shell->statusbar)); + } + + /* make sure the information is up-to-date */ + gimp_display_shell_scale_update (shell); + + gimp_display_shell_set_show_all (shell, config->default_show_all); +} + +static void +gimp_display_shell_dispose (GObject *object) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object); + + if (shell->display && gimp_display_get_shell (shell->display)) + gimp_display_shell_disconnect (shell); + + shell->popup_manager = NULL; + + if (shell->selection) + gimp_display_shell_selection_free (shell); + + gimp_display_shell_filter_set (shell, NULL); + + if (shell->filter_idle_id) + { + g_source_remove (shell->filter_idle_id); + shell->filter_idle_id = 0; + } + + g_clear_pointer (&shell->mask_surface, cairo_surface_destroy); + g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy); + + gimp_display_shell_profile_finalize (shell); + + g_clear_object (&shell->filter_buffer); + shell->filter_data = NULL; + shell->filter_stride = 0; + + g_clear_object (&shell->mask); + + gimp_display_shell_items_free (shell); + + g_clear_object (&shell->motion_buffer); + + g_clear_pointer (&shell->zoom_focus_pointer_queue, g_queue_free); + + if (shell->title_idle_id) + { + g_source_remove (shell->title_idle_id); + shell->title_idle_id = 0; + } + + if (shell->fill_idle_id) + { + g_source_remove (shell->fill_idle_id); + shell->fill_idle_id = 0; + } + + g_clear_pointer (&shell->nav_popup, gtk_widget_destroy); + + if (shell->blink_timeout_id) + { + g_source_remove (shell->blink_timeout_id); + shell->blink_timeout_id = 0; + } + + shell->display = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_display_shell_finalize (GObject *object) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object); + + g_clear_object (&shell->zoom); + g_clear_pointer (&shell->rotate_transform, g_free); + g_clear_pointer (&shell->rotate_untransform, g_free); + g_clear_object (&shell->options); + g_clear_object (&shell->fullscreen_options); + g_clear_object (&shell->no_image_options); + g_clear_pointer (&shell->title, g_free); + g_clear_pointer (&shell->status, g_free); + g_clear_object (&shell->icon); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_display_shell_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object); + + switch (property_id) + { + case PROP_POPUP_MANAGER: + shell->popup_manager = g_value_get_object (value); + break; + case PROP_INITIAL_SCREEN: + shell->initial_screen = g_value_get_object (value); + break; + case PROP_INITIAL_MONITOR: + shell->initial_monitor = g_value_get_int (value); + break; + case PROP_DISPLAY: + shell->display = g_value_get_object (value); + break; + case PROP_UNIT: + gimp_display_shell_set_unit (shell, g_value_get_int (value)); + break; + case PROP_TITLE: + g_free (shell->title); + shell->title = g_value_dup_string (value); + break; + case PROP_STATUS: + g_free (shell->status); + shell->status = g_value_dup_string (value); + break; + case PROP_ICON: + if (shell->icon) + g_object_unref (shell->icon); + shell->icon = g_value_dup_object (value); + break; + case PROP_SHOW_ALL: + gimp_display_shell_set_show_all (shell, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_shell_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object); + + switch (property_id) + { + case PROP_POPUP_MANAGER: + g_value_set_object (value, shell->popup_manager); + break; + case PROP_INITIAL_SCREEN: + g_value_set_object (value, shell->initial_screen); + break; + case PROP_INITIAL_MONITOR: + g_value_set_int (value, shell->initial_monitor); + break; + case PROP_DISPLAY: + g_value_set_object (value, shell->display); + break; + case PROP_UNIT: + g_value_set_int (value, shell->unit); + break; + case PROP_TITLE: + g_value_set_string (value, shell->title); + break; + case PROP_STATUS: + g_value_set_string (value, shell->status); + break; + case PROP_ICON: + g_value_set_object (value, shell->icon); + break; + case PROP_SHOW_ALL: + g_value_set_boolean (value, shell->show_all); + break; + case PROP_INFINITE_CANVAS: + g_value_set_boolean (value, + gimp_display_shell_get_infinite_canvas (shell)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_display_shell_unrealize (GtkWidget *widget) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget); + + if (shell->nav_popup) + gtk_widget_unrealize (shell->nav_popup); + + GTK_WIDGET_CLASS (parent_class)->unrealize (widget); +} + +static void +gimp_display_shell_unmap (GtkWidget *widget) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget); + + gimp_display_shell_selection_undraw (shell); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static void +gimp_display_shell_screen_changed (GtkWidget *widget, + GdkScreen *previous) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget); + + if (GTK_WIDGET_CLASS (parent_class)->screen_changed) + GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous); + + if (shell->display->config->monitor_res_from_gdk) + { + gimp_get_monitor_resolution (gtk_widget_get_screen (widget), + gimp_widget_get_monitor (widget), + &shell->monitor_xres, + &shell->monitor_yres); + } + else + { + shell->monitor_xres = shell->display->config->monitor_xres; + shell->monitor_yres = shell->display->config->monitor_yres; + } +} + +static gboolean +gimp_display_shell_popup_menu (GtkWidget *widget) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget); + + gimp_context_set_display (gimp_get_user_context (shell->display->gimp), + shell->display); + + gimp_ui_manager_ui_popup (shell->popup_manager, "/dummy-menubar/image-popup", + GTK_WIDGET (shell), + gimp_display_shell_menu_position, + shell->origin, + NULL, NULL); + + return TRUE; +} + +static void +gimp_display_shell_real_scaled (GimpDisplayShell *shell) +{ + GimpContext *user_context; + + if (! shell->display) + return; + + gimp_display_shell_title_update (shell); + + user_context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (user_context)) + { + gimp_display_shell_update_priority_rect (shell); + + gimp_ui_manager_update (shell->popup_manager, shell->display); + } +} + +static void +gimp_display_shell_real_scrolled (GimpDisplayShell *shell) +{ + GimpContext *user_context; + + if (! shell->display) + return; + + gimp_display_shell_title_update (shell); + + user_context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (user_context)) + { + gimp_display_shell_update_priority_rect (shell); + + } +} + +static void +gimp_display_shell_real_rotated (GimpDisplayShell *shell) +{ + GimpContext *user_context; + + if (! shell->display) + return; + + gimp_display_shell_title_update (shell); + + user_context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (user_context)) + { + gimp_display_shell_update_priority_rect (shell); + + gimp_ui_manager_update (shell->popup_manager, shell->display); + } +} + +static const guint8 * +gimp_display_shell_get_icc_profile (GimpColorManaged *managed, + gsize *len) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed); + GimpImage *image = gimp_display_get_image (shell->display); + + if (image) + return gimp_color_managed_get_icc_profile (GIMP_COLOR_MANAGED (image), len); + + return NULL; +} + +static GimpColorProfile * +gimp_display_shell_get_color_profile (GimpColorManaged *managed) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed); + GimpImage *image = gimp_display_get_image (shell->display); + + if (image) + return gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + return NULL; +} + +static void +gimp_display_shell_profile_changed (GimpColorManaged *managed) +{ + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed); + + gimp_display_shell_profile_update (shell); + gimp_display_shell_expose_full (shell); +} + +static void +gimp_display_shell_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gpointer data) +{ + gimp_button_menu_position (GTK_WIDGET (data), menu, GTK_POS_RIGHT, x, y); +} + +static void +gimp_display_shell_zoom_button_callback (GimpDisplayShell *shell, + GtkWidget *zoom_button) +{ + shell->zoom_on_resize = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (zoom_button)); + + if (shell->zoom_on_resize && + gimp_display_shell_scale_image_is_within_viewport (shell, NULL, NULL)) + { + /* Implicitly make a View -> Fit Image in Window */ + gimp_display_shell_scale_fit_in (shell); + } +} + +static void +gimp_display_shell_sync_config (GimpDisplayShell *shell, + GimpDisplayConfig *config) +{ + gimp_config_sync (G_OBJECT (config->default_view), + G_OBJECT (shell->options), 0); + gimp_config_sync (G_OBJECT (config->default_fullscreen_view), + G_OBJECT (shell->fullscreen_options), 0); +} + +static void +gimp_display_shell_remove_overlay (GtkWidget *canvas, + GtkWidget *child, + GimpDisplayShell *shell) +{ + shell->children = g_list_remove (shell->children, child); +} + +static void +gimp_display_shell_transform_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble *x, + gdouble *y) +{ + GimpDisplayShellOverlay *overlay; + GtkRequisition requisition; + + overlay = g_object_get_data (G_OBJECT (child), "image-coords-overlay"); + + gimp_display_shell_transform_xy_f (shell, + overlay->image_x, + overlay->image_y, + x, y); + + gtk_widget_size_request (child, &requisition); + + switch (overlay->anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + *x -= requisition.width / 2; + *y -= requisition.height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + *x -= requisition.width / 2; + *y += overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + *x += overlay->spacing_x; + *y += overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + *x -= requisition.width + overlay->spacing_x; + *y += overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + *x -= requisition.width / 2; + *y -= requisition.height + overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + *x += overlay->spacing_x; + *y -= requisition.height + overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + *x -= requisition.width + overlay->spacing_x; + *y -= requisition.height + overlay->spacing_y; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + *x += overlay->spacing_x; + *y -= requisition.height / 2; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + *x -= requisition.width + overlay->spacing_x; + *y -= requisition.height / 2; + break; + } +} + + +/* public functions */ + +GtkWidget * +gimp_display_shell_new (GimpDisplay *display, + GimpUnit unit, + gdouble scale, + GimpUIManager *popup_manager, + GdkScreen *screen, + gint monitor) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); + g_return_val_if_fail (GIMP_IS_UI_MANAGER (popup_manager), NULL); + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + return g_object_new (GIMP_TYPE_DISPLAY_SHELL, + "popup-manager", popup_manager, + "initial-screen", screen, + "initial-monitor", monitor, + "display", display, + "unit", unit, + NULL); +} + +void +gimp_display_shell_add_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble image_x, + gdouble image_y, + GimpHandleAnchor anchor, + gint spacing_x, + gint spacing_y) +{ + GimpDisplayShellOverlay *overlay; + gdouble x, y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GTK_IS_WIDGET (shell)); + + overlay = g_new0 (GimpDisplayShellOverlay, 1); + + overlay->image_x = image_x; + overlay->image_y = image_y; + overlay->anchor = anchor; + overlay->spacing_x = spacing_x; + overlay->spacing_y = spacing_y; + + g_object_set_data_full (G_OBJECT (child), "image-coords-overlay", overlay, + (GDestroyNotify) g_free); + + shell->children = g_list_prepend (shell->children, child); + + gimp_display_shell_transform_overlay (shell, child, &x, &y); + + gimp_overlay_box_add_child (GIMP_OVERLAY_BOX (shell->canvas), child, 0.0, 0.0); + gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (shell->canvas), + child, x, y); +} + +void +gimp_display_shell_move_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble image_x, + gdouble image_y, + GimpHandleAnchor anchor, + gint spacing_x, + gint spacing_y) +{ + GimpDisplayShellOverlay *overlay; + gdouble x, y; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GTK_IS_WIDGET (shell)); + + overlay = g_object_get_data (G_OBJECT (child), "image-coords-overlay"); + + g_return_if_fail (overlay != NULL); + + overlay->image_x = image_x; + overlay->image_y = image_y; + overlay->anchor = anchor; + overlay->spacing_x = spacing_x; + overlay->spacing_y = spacing_y; + + gimp_display_shell_transform_overlay (shell, child, &x, &y); + + gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (shell->canvas), + child, x, y); +} + +GimpImageWindow * +gimp_display_shell_get_window (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return GIMP_IMAGE_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (shell), + GIMP_TYPE_IMAGE_WINDOW)); +} + +GimpStatusbar * +gimp_display_shell_get_statusbar (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return GIMP_STATUSBAR (shell->statusbar); +} + +GimpColorConfig * +gimp_display_shell_get_color_config (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return shell->color_config; +} + +void +gimp_display_shell_present (GimpDisplayShell *shell) +{ + GimpImageWindow *window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + window = gimp_display_shell_get_window (shell); + + if (window) + { + gimp_image_window_set_active_shell (window, shell); + + gtk_window_present (GTK_WINDOW (window)); + } +} + +void +gimp_display_shell_reconnect (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_DISPLAY (shell->display)); + g_return_if_fail (gimp_display_get_image (shell->display) != NULL); + + if (shell->fill_idle_id) + { + g_source_remove (shell->fill_idle_id); + shell->fill_idle_id = 0; + } + + g_signal_emit (shell, display_shell_signals[RECONNECT], 0); + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (shell)); + + gimp_display_shell_scroll_clamp_and_update (shell); + + gimp_display_shell_scaled (shell); + + gimp_display_shell_expose_full (shell); +} + +static gboolean +gimp_display_shell_blink (GimpDisplayShell *shell) +{ + shell->blink_timeout_id = 0; + + if (shell->blink) + { + shell->blink = FALSE; + } + else + { + shell->blink = TRUE; + + shell->blink_timeout_id = + g_timeout_add (100, (GSourceFunc) gimp_display_shell_blink, shell); + } + + gimp_display_shell_expose_full (shell); + + return FALSE; +} + +void +gimp_display_shell_empty (GimpDisplayShell *shell) +{ + GimpContext *user_context; + GimpImageWindow *window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_DISPLAY (shell->display)); + g_return_if_fail (gimp_display_get_image (shell->display) == NULL); + + window = gimp_display_shell_get_window (shell); + + if (shell->fill_idle_id) + { + g_source_remove (shell->fill_idle_id); + shell->fill_idle_id = 0; + } + + gimp_display_shell_selection_undraw (shell); + + gimp_display_shell_unset_cursor (shell); + + gimp_display_shell_filter_set (shell, NULL); + + gimp_display_shell_sync_config (shell, shell->display->config); + + gimp_display_shell_appearance_update (shell); + gimp_image_window_update_tabs (window); +#if 0 + gimp_help_set_help_data (shell->canvas, + _("Drop image files here to open them"), NULL); +#endif + + gimp_statusbar_empty (GIMP_STATUSBAR (shell->statusbar)); + + shell->flip_horizontally = FALSE; + shell->flip_vertically = FALSE; + shell->rotate_angle = 0.0; + gimp_display_shell_rotate_update_transform (shell); + + gimp_display_shell_expose_full (shell); + + user_context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (user_context)) + gimp_ui_manager_update (shell->popup_manager, shell->display); + + shell->blink_timeout_id = + g_timeout_add (1403230, (GSourceFunc) gimp_display_shell_blink, shell); +} + +static gboolean +gimp_display_shell_fill_idle (GimpDisplayShell *shell) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + shell->fill_idle_id = 0; + + if (GTK_IS_WINDOW (toplevel)) + { + gimp_display_shell_scale_shrink_wrap (shell, TRUE); + + gtk_window_present (GTK_WINDOW (toplevel)); + } + + return FALSE; +} + +void +gimp_display_shell_fill (GimpDisplayShell *shell, + GimpImage *image, + GimpUnit unit, + gdouble scale) +{ + GimpDisplayConfig *config; + GimpImageWindow *window; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GIMP_IS_DISPLAY (shell->display)); + g_return_if_fail (GIMP_IS_IMAGE (image)); + + config = shell->display->config; + window = gimp_display_shell_get_window (shell); + + shell->show_image = TRUE; + + shell->dot_for_dot = config->default_dot_for_dot; + + gimp_display_shell_set_unit (shell, unit); + gimp_display_shell_set_initial_scale (shell, scale, NULL, NULL); + gimp_display_shell_scale_update (shell); + + gimp_display_shell_sync_config (shell, config); + + gimp_image_window_suspend_keep_pos (window); + gimp_display_shell_appearance_update (shell); + gimp_image_window_resume_keep_pos (window); + + gimp_image_window_update_tabs (window); +#if 0 + gimp_help_set_help_data (shell->canvas, NULL, NULL); +#endif + + gimp_statusbar_fill (GIMP_STATUSBAR (shell->statusbar)); + + /* make sure a size-allocate always occurs, even when the rulers and + * scrollbars are hidden. see issue #4968. + */ + shell->size_allocate_center_image = TRUE; + gtk_widget_queue_resize (GTK_WIDGET (shell->canvas)); + + if (shell->blink_timeout_id) + { + g_source_remove (shell->blink_timeout_id); + shell->blink_timeout_id = 0; + } + + shell->fill_idle_id = + g_idle_add_full (GIMP_PRIORITY_DISPLAY_SHELL_FILL_IDLE, + (GSourceFunc) gimp_display_shell_fill_idle, shell, + NULL); + + gimp_display_shell_set_show_all (shell, config->default_show_all); +} + +void +gimp_display_shell_scaled (GimpDisplayShell *shell) +{ + GList *list; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_rotate_update_transform (shell); + + for (list = shell->children; list; list = g_list_next (list)) + { + GtkWidget *child = list->data; + gdouble x, y; + + gimp_display_shell_transform_overlay (shell, child, &x, &y); + + gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (shell->canvas), + child, x, y); + } + + g_signal_emit (shell, display_shell_signals[SCALED], 0); +} + +void +gimp_display_shell_scrolled (GimpDisplayShell *shell) +{ + GList *list; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_rotate_update_transform (shell); + + for (list = shell->children; list; list = g_list_next (list)) + { + GtkWidget *child = list->data; + gdouble x, y; + + gimp_display_shell_transform_overlay (shell, child, &x, &y); + + gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (shell->canvas), + child, x, y); + } + + g_signal_emit (shell, display_shell_signals[SCROLLED], 0); +} + +void +gimp_display_shell_rotated (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + gimp_display_shell_rotate_update_transform (shell); + + g_signal_emit (shell, display_shell_signals[ROTATED], 0); +} + +void +gimp_display_shell_set_unit (GimpDisplayShell *shell, + GimpUnit unit) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell->unit != unit) + { + shell->unit = unit; + + gimp_display_shell_rulers_update (shell); + + gimp_display_shell_scaled (shell); + + g_object_notify (G_OBJECT (shell), "unit"); + } +} + +GimpUnit +gimp_display_shell_get_unit (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), GIMP_UNIT_PIXEL); + + return shell->unit; +} + +gboolean +gimp_display_shell_snap_coords (GimpDisplayShell *shell, + GimpCoords *coords, + gint snap_offset_x, + gint snap_offset_y, + gint snap_width, + gint snap_height) +{ + GimpImage *image; + gboolean snap_to_guides = FALSE; + gboolean snap_to_grid = FALSE; + gboolean snap_to_canvas = FALSE; + gboolean snap_to_vectors = FALSE; + gboolean snapped = FALSE; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + g_return_val_if_fail (coords != NULL, FALSE); + + image = gimp_display_get_image (shell->display); + + if (gimp_display_shell_get_snap_to_guides (shell) && + gimp_image_get_guides (image)) + { + snap_to_guides = TRUE; + } + + if (gimp_display_shell_get_snap_to_grid (shell) && + gimp_image_get_grid (image)) + { + snap_to_grid = TRUE; + } + + snap_to_canvas = gimp_display_shell_get_snap_to_canvas (shell); + + if (gimp_display_shell_get_snap_to_vectors (shell) && + gimp_image_get_active_vectors (image)) + { + snap_to_vectors = TRUE; + } + + if (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors) + { + gint snap_distance; + gdouble tx, ty; + + snap_distance = shell->display->config->snap_distance; + + if (snap_width > 0 && snap_height > 0) + { + snapped = gimp_image_snap_rectangle (image, + coords->x + snap_offset_x, + coords->y + snap_offset_y, + coords->x + snap_offset_x + + snap_width, + coords->y + snap_offset_y + + snap_height, + &tx, + &ty, + FUNSCALEX (shell, snap_distance), + FUNSCALEY (shell, snap_distance), + snap_to_guides, + snap_to_grid, + snap_to_canvas, + snap_to_vectors); + } + else + { + snapped = gimp_image_snap_point (image, + coords->x + snap_offset_x, + coords->y + snap_offset_y, + &tx, + &ty, + FUNSCALEX (shell, snap_distance), + FUNSCALEY (shell, snap_distance), + snap_to_guides, + snap_to_grid, + snap_to_canvas, + snap_to_vectors, + shell->show_all); + } + + if (snapped) + { + coords->x = tx - snap_offset_x; + coords->y = ty - snap_offset_y; + } + } + + return snapped; +} + +gboolean +gimp_display_shell_mask_bounds (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GimpImage *image; + GimpLayer *layer; + gint x1, y1; + gint x2, y2; + gdouble x1_f, y1_f; + gdouble x2_f, y2_f; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + g_return_val_if_fail (x != NULL, FALSE); + g_return_val_if_fail (y != NULL, FALSE); + g_return_val_if_fail (width != NULL, FALSE); + g_return_val_if_fail (height != NULL, FALSE); + + image = gimp_display_get_image (shell->display); + + /* If there is a floating selection, handle things differently */ + if ((layer = gimp_image_get_floating_selection (image))) + { + gint fs_x; + gint fs_y; + gint fs_width; + gint fs_height; + + gimp_item_get_offset (GIMP_ITEM (layer), &fs_x, &fs_y); + fs_width = gimp_item_get_width (GIMP_ITEM (layer)); + fs_height = gimp_item_get_height (GIMP_ITEM (layer)); + + if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + x, y, width, height)) + { + *x = fs_x; + *y = fs_y; + *width = fs_width; + *height = fs_height; + } + else + { + gimp_rectangle_union (*x, *y, *width, *height, + fs_x, fs_y, fs_width, fs_height, + x, y, width, height); + } + } + else if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + x, y, width, height)) + { + return FALSE; + } + + x1 = *x; + y1 = *y; + x2 = *x + *width; + y2 = *y + *height; + + gimp_display_shell_transform_bounds (shell, + x1, y1, x2, y2, + &x1_f, &y1_f, &x2_f, &y2_f); + + /* Make sure the extents are within bounds */ + x1 = CLAMP (floor (x1_f), 0, shell->disp_width); + y1 = CLAMP (floor (y1_f), 0, shell->disp_height); + x2 = CLAMP (ceil (x2_f), 0, shell->disp_width); + y2 = CLAMP (ceil (y2_f), 0, shell->disp_height); + + *x = x1; + *y = y1; + *width = x2 - x1; + *height = y2 - y1; + + return (*width > 0) && (*height > 0); +} + +void +gimp_display_shell_set_show_image (GimpDisplayShell *shell, + gboolean show_image) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (show_image != shell->show_image) + { + shell->show_image = show_image; + + gimp_display_shell_expose_full (shell); + } +} + +void +gimp_display_shell_set_show_all (GimpDisplayShell *shell, + gboolean show_all) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (show_all != shell->show_all) + { + shell->show_all = show_all; + + if (shell->display && gimp_display_get_image (shell->display)) + { + GimpImage *image = gimp_display_get_image (shell->display); + GimpContext *user_context; + + if (show_all) + gimp_image_inc_show_all_count (image); + else + gimp_image_dec_show_all_count (image); + + gimp_image_flush (image); + + gimp_display_update_bounding_box (shell->display); + + gimp_display_shell_update_show_canvas (shell); + + gimp_display_shell_scroll_clamp_and_update (shell); + gimp_display_shell_scrollbars_update (shell); + + gimp_display_shell_expose_full (shell); + + user_context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (user_context)) + { + gimp_display_shell_update_priority_rect (shell); + + gimp_ui_manager_update (shell->popup_manager, shell->display); + } + } + + g_object_notify (G_OBJECT (shell), "show-all"); + g_object_notify (G_OBJECT (shell), "infinite-canvas"); + } +} + + +GimpPickable * +gimp_display_shell_get_pickable (GimpDisplayShell *shell) +{ + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + image = gimp_display_get_image (shell->display); + + if (image) + { + if (! shell->show_all) + return GIMP_PICKABLE (image); + else + return GIMP_PICKABLE (gimp_image_get_projection (image)); + } + + return NULL; +} + +GimpPickable * +gimp_display_shell_get_canvas_pickable (GimpDisplayShell *shell) +{ + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + image = gimp_display_get_image (shell->display); + + if (image) + { + if (! gimp_display_shell_get_infinite_canvas (shell)) + return GIMP_PICKABLE (image); + else + return GIMP_PICKABLE (gimp_image_get_projection (image)); + } + + return NULL; +} + +GeglRectangle +gimp_display_shell_get_bounding_box (GimpDisplayShell *shell) +{ + GeglRectangle bounding_box = {}; + GimpImage *image; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), bounding_box); + + image = gimp_display_get_image (shell->display); + + if (image) + { + if (! shell->show_all) + { + bounding_box.width = gimp_image_get_width (image); + bounding_box.height = gimp_image_get_height (image); + } + else + { + bounding_box = gimp_projectable_get_bounding_box ( + GIMP_PROJECTABLE (image)); + } + } + + return bounding_box; +} + +gboolean +gimp_display_shell_get_infinite_canvas (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return shell->show_all && + ! gimp_display_shell_get_padding_in_show_all (shell); +} + +void +gimp_display_shell_update_priority_rect (GimpDisplayShell *shell) +{ + GimpImage *image; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + image = gimp_display_get_image (shell->display); + + if (image) + { + GimpProjection *projection = gimp_image_get_projection (image); + gint x, y; + gint width, height; + + gimp_display_shell_untransform_viewport (shell, ! shell->show_all, + &x, &y, &width, &height); + gimp_projection_set_priority_rect (projection, x, y, width, height); + } +} + +void +gimp_display_shell_flush (GimpDisplayShell *shell, + gboolean now) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (now) + { + gdk_window_process_updates (gtk_widget_get_window (shell->canvas), + FALSE); + } + else + { + GimpImageWindow *window = gimp_display_shell_get_window (shell); + GimpContext *context; + + gimp_display_shell_title_update (shell); + + gimp_canvas_layer_boundary_set_layer (GIMP_CANVAS_LAYER_BOUNDARY (shell->layer_boundary), + gimp_image_get_active_layer (gimp_display_get_image (shell->display))); + + gimp_canvas_canvas_boundary_set_image (GIMP_CANVAS_CANVAS_BOUNDARY (shell->canvas_boundary), + gimp_display_get_image (shell->display)); + + if (window && gimp_image_window_get_active_shell (window) == shell) + { + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_update (manager, shell->display); + } + + context = gimp_get_user_context (shell->display->gimp); + + if (shell->display == gimp_context_get_display (context)) + { + gimp_ui_manager_update (shell->popup_manager, shell->display); + } + } +} + +/** + * gimp_display_shell_pause: + * @shell: a display shell + * + * This function increments the pause count or the display shell. + * If it was zero coming in, then the function pauses the active tool, + * so that operations on the display can take place without corrupting + * anything that the tool has drawn. It "undraws" the current tool + * drawing, and must be followed by gimp_display_shell_resume() after + * the operation in question is completed. + **/ +void +gimp_display_shell_pause (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + shell->paused_count++; + + if (shell->paused_count == 1) + { + /* pause the currently active tool */ + tool_manager_control_active (shell->display->gimp, + GIMP_TOOL_ACTION_PAUSE, + shell->display); + } +} + +/** + * gimp_display_shell_resume: + * @shell: a display shell + * + * This function decrements the pause count for the display shell. + * If this brings it to zero, then the current tool is resumed. + * It is an error to call this function without having previously + * called gimp_display_shell_pause(). + **/ +void +gimp_display_shell_resume (GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (shell->paused_count > 0); + + shell->paused_count--; + + if (shell->paused_count == 0) + { + /* start the currently active tool */ + tool_manager_control_active (shell->display->gimp, + GIMP_TOOL_ACTION_RESUME, + shell->display); + } +} + +/** + * gimp_display_shell_set_highlight: + * @shell: a #GimpDisplayShell + * @highlight: a rectangle in image coordinates that should be brought out + * @opacity: how much to hide the unselected area + * + * This function sets an area of the image that should be + * accentuated. The actual implementation is to dim all pixels outside + * this rectangle. Passing %NULL for @highlight unsets the rectangle. + **/ +void +gimp_display_shell_set_highlight (GimpDisplayShell *shell, + const GdkRectangle *highlight, + gdouble opacity) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (highlight) + { + gimp_canvas_item_begin_change (shell->passe_partout); + + gimp_canvas_rectangle_set (shell->passe_partout, + highlight->x, + highlight->y, + highlight->width, + highlight->height); + g_object_set (shell->passe_partout, "opacity", opacity, NULL); + + gimp_canvas_item_set_visible (shell->passe_partout, TRUE); + + gimp_canvas_item_end_change (shell->passe_partout); + } + else + { + gimp_canvas_item_set_visible (shell->passe_partout, FALSE); + } +} + +/** + * gimp_display_shell_set_mask: + * @shell: a #GimpDisplayShell + * @mask: a #GimpDrawable (1 byte per pixel) + * @color: the color to use for drawing the mask + * @inverted: #TRUE if the mask should be drawn inverted + * + * Previews a mask originating at offset_x, offset_x. Depending on + * @inverted, pixels that are selected or not selected are tinted with + * the given color. + **/ +void +gimp_display_shell_set_mask (GimpDisplayShell *shell, + GeglBuffer *mask, + gint offset_x, + gint offset_y, + const GimpRGB *color, + gboolean inverted) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (mask == NULL || GEGL_IS_BUFFER (mask)); + g_return_if_fail (mask == NULL || color != NULL); + + if (mask) + g_object_ref (mask); + + if (shell->mask) + g_object_unref (shell->mask); + + shell->mask = mask; + + shell->mask_offset_x = offset_x; + shell->mask_offset_y = offset_y; + + if (mask) + shell->mask_color = *color; + + shell->mask_inverted = inverted; + + gimp_display_shell_expose_full (shell); +} diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h new file mode 100644 index 0000000..b38123c --- /dev/null +++ b/app/display/gimpdisplayshell.h @@ -0,0 +1,341 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_SHELL_H__ +#define __GIMP_DISPLAY_SHELL_H__ + + +/* Apply to a float the same rounding mode used in the renderer */ +#define PROJ_ROUND(coord) ((gint) RINT (coord)) +#define PROJ_ROUND64(coord) ((gint64) RINT (coord)) + +/* scale values */ +#define SCALEX(s,x) PROJ_ROUND ((x) * (s)->scale_x) +#define SCALEY(s,y) PROJ_ROUND ((y) * (s)->scale_y) + +/* unscale values */ +#define UNSCALEX(s,x) ((gint) ((x) / (s)->scale_x)) +#define UNSCALEY(s,y) ((gint) ((y) / (s)->scale_y)) +/* (and float-returning versions) */ +#define FUNSCALEX(s,x) ((x) / (s)->scale_x) +#define FUNSCALEY(s,y) ((y) / (s)->scale_y) + + +#define GIMP_TYPE_DISPLAY_SHELL (gimp_display_shell_get_type ()) +#define GIMP_DISPLAY_SHELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DISPLAY_SHELL, GimpDisplayShell)) +#define GIMP_DISPLAY_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DISPLAY_SHELL, GimpDisplayShellClass)) +#define GIMP_IS_DISPLAY_SHELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DISPLAY_SHELL)) +#define GIMP_IS_DISPLAY_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DISPLAY_SHELL)) +#define GIMP_DISPLAY_SHELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DISPLAY_SHELL, GimpDisplayShellClass)) + + +typedef struct _GimpDisplayShellClass GimpDisplayShellClass; + +struct _GimpDisplayShell +{ + GtkEventBox parent_instance; + + GimpDisplay *display; + + GimpUIManager *popup_manager; + GdkScreen *initial_screen; + gint initial_monitor; + + GimpDisplayOptions *options; + GimpDisplayOptions *fullscreen_options; + GimpDisplayOptions *no_image_options; + + GimpUnit unit; + + gint offset_x; /* offset of display image */ + gint offset_y; + + gdouble scale_x; /* horizontal scale factor */ + gdouble scale_y; /* vertical scale factor */ + + gboolean flip_horizontally; + gboolean flip_vertically; + gdouble rotate_angle; + cairo_matrix_t *rotate_transform; + cairo_matrix_t *rotate_untransform; + + gdouble monitor_xres; + gdouble monitor_yres; + gboolean dot_for_dot; /* ignore monitor resolution */ + + GimpZoomModel *zoom; + + gdouble last_scale; /* scale used when reverting zoom */ + guint last_scale_time; /* time when last_scale was set */ + gint last_offset_x; /* offsets used when reverting zoom */ + gint last_offset_y; + + gdouble other_scale; /* scale factor entered in Zoom->Other*/ + + gint disp_width; /* width of drawing area */ + gint disp_height; /* height of drawing area */ + + gboolean proximity; /* is a device in proximity */ + + gboolean show_image; /* whether to show the image */ + + gboolean show_all; /* show the entire image */ + + Selection *selection; /* Selection (marching ants) */ + gint64 selection_update; /* Last selection index update */ + + GList *children; + + GtkWidget *canvas; /* GimpCanvas widget */ + + GtkAdjustment *hsbdata; /* adjustments */ + GtkAdjustment *vsbdata; + GtkWidget *hsb; /* scroll bars */ + GtkWidget *vsb; + + GtkWidget *hrule; /* rulers */ + GtkWidget *vrule; + + GtkWidget *origin; /* NW: origin */ + GtkWidget *quick_mask_button;/* SW: quick mask button */ + GtkWidget *zoom_button; /* NE: zoom toggle button */ + GtkWidget *nav_ebox; /* SE: navigation event box */ + + GtkWidget *statusbar; /* statusbar */ + + GimpCanvasItem *canvas_item; /* items drawn on the canvas */ + GimpCanvasItem *unrotated_item; /* unrotated items for e.g. cursor */ + GimpCanvasItem *passe_partout; /* item for the highlight */ + GimpCanvasItem *preview_items; /* item for previews */ + GimpCanvasItem *vectors; /* item proxy of vectors */ + GimpCanvasItem *grid; /* item proxy of the grid */ + GimpCanvasItem *guides; /* item proxies of guides */ + GimpCanvasItem *sample_points; /* item proxies of sample points */ + GimpCanvasItem *canvas_boundary; /* item for the cabvas boundary */ + GimpCanvasItem *layer_boundary; /* item for the layer boundary */ + GimpCanvasItem *tool_items; /* tools items, below the cursor */ + GimpCanvasItem *cursor; /* item for the software cursor */ + + guint title_idle_id; /* title update idle ID */ + gchar *title; /* current title */ + gchar *status; /* current default statusbar content */ + + gint icon_size; /* size of the icon pixbuf */ + gint icon_size_small; /* size of the icon's wilber pixbuf */ + guint icon_idle_id; /* ID of the idle-function */ + GdkPixbuf *icon; /* icon */ + + guint fill_idle_id; /* display_shell_fill() idle ID */ + + GimpHandedness cursor_handedness;/* Handedness for cursor display */ + GimpCursorType current_cursor; /* Currently installed main cursor */ + GimpToolCursorType tool_cursor; /* Current Tool cursor */ + GimpCursorModifier cursor_modifier; /* Cursor modifier (plus, minus, ...) */ + + GimpCursorType override_cursor; /* Overriding cursor */ + gboolean using_override_cursor; + gboolean draw_cursor; /* should we draw software cursor ? */ + + GtkWidget *close_dialog; /* close dialog */ + GtkWidget *scale_dialog; /* scale (zoom) dialog */ + GtkWidget *rotate_dialog; /* rotate dialog */ + GtkWidget *nav_popup; /* navigation popup */ + + GimpColorConfig *color_config; /* color management settings */ + gboolean color_config_set; /* settings changed from defaults */ + + GimpColorTransform *profile_transform; + GeglBuffer *profile_buffer; /* buffer for profile transform */ + guchar *profile_data; /* profile_buffer's pixels */ + gint profile_stride; /* profile_buffer's stride */ + + GimpColorDisplayStack *filter_stack; /* color display conversion stuff */ + guint filter_idle_id; + + GimpColorTransform *filter_transform; + const Babl *filter_format; /* filter_buffer's format */ + GeglBuffer *filter_buffer; /* buffer for display filters */ + guchar *filter_data; /* filter_buffer's pixels */ + gint filter_stride; /* filter_buffer's stride */ + + GimpDisplayXfer *xfer; /* manages image buffer transfers */ + cairo_surface_t *mask_surface; /* buffer for rendering the mask */ + cairo_pattern_t *checkerboard; /* checkerboard pattern */ + + gint paused_count; + + GimpTreeHandler *vectors_freeze_handler; + GimpTreeHandler *vectors_thaw_handler; + GimpTreeHandler *vectors_visible_handler; + + gboolean zoom_on_resize; + + gboolean size_allocate_from_configure_event; + gboolean size_allocate_center_image; + + /* the state of gimp_display_shell_tool_events() */ + gboolean pointer_grabbed; + guint32 pointer_grab_time; + + gboolean keyboard_grabbed; + guint32 keyboard_grab_time; + + gboolean inferior_ignore_mode; + + /* Two states are possible when the shell is grabbed: it can be + * grabbed with space (or space+button1 which is the same state), + * then if space is released but button1 was still pressed, we wait + * for button1 to be released as well. + */ + gboolean space_release_pending; + gboolean button1_release_pending; + const gchar *space_shaded_tool; + + gboolean scrolling; + gint scroll_start_x; + gint scroll_start_y; + gint scroll_last_x; + gint scroll_last_y; + gboolean rotating; + gdouble rotate_drag_angle; + gboolean scaling; + gpointer scroll_info; + gboolean layer_picking; + GimpLayer *picked_layer; + + GeglBuffer *mask; + gint mask_offset_x; + gint mask_offset_y; + GimpRGB mask_color; + gboolean mask_inverted; + + GimpMotionBuffer *motion_buffer; + + GQueue *zoom_focus_pointer_queue; + + gboolean blink; + guint blink_timeout_id; +}; + +struct _GimpDisplayShellClass +{ + GtkEventBoxClass parent_class; + + void (* scaled) (GimpDisplayShell *shell); + void (* scrolled) (GimpDisplayShell *shell); + void (* rotated) (GimpDisplayShell *shell); + void (* reconnect) (GimpDisplayShell *shell); +}; + + +GType gimp_display_shell_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_display_shell_new (GimpDisplay *display, + GimpUnit unit, + gdouble scale, + GimpUIManager *popup_manager, + GdkScreen *screen, + gint monitor); + +void gimp_display_shell_add_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble image_x, + gdouble image_y, + GimpHandleAnchor anchor, + gint spacing_x, + gint spacing_y); +void gimp_display_shell_move_overlay (GimpDisplayShell *shell, + GtkWidget *child, + gdouble image_x, + gdouble image_y, + GimpHandleAnchor anchor, + gint spacing_x, + gint spacing_y); + +GimpImageWindow * gimp_display_shell_get_window (GimpDisplayShell *shell); +GimpStatusbar * gimp_display_shell_get_statusbar (GimpDisplayShell *shell); + +GimpColorConfig * gimp_display_shell_get_color_config + (GimpDisplayShell *shell); + +void gimp_display_shell_present (GimpDisplayShell *shell); + +void gimp_display_shell_reconnect (GimpDisplayShell *shell); + +void gimp_display_shell_empty (GimpDisplayShell *shell); +void gimp_display_shell_fill (GimpDisplayShell *shell, + GimpImage *image, + GimpUnit unit, + gdouble scale); + +void gimp_display_shell_scaled (GimpDisplayShell *shell); +void gimp_display_shell_scrolled (GimpDisplayShell *shell); +void gimp_display_shell_rotated (GimpDisplayShell *shell); + +void gimp_display_shell_set_unit (GimpDisplayShell *shell, + GimpUnit unit); +GimpUnit gimp_display_shell_get_unit (GimpDisplayShell *shell); + +gboolean gimp_display_shell_snap_coords (GimpDisplayShell *shell, + GimpCoords *coords, + gint snap_offset_x, + gint snap_offset_y, + gint snap_width, + gint snap_height); + +gboolean gimp_display_shell_mask_bounds (GimpDisplayShell *shell, + gint *x, + gint *y, + gint *width, + gint *height); + +void gimp_display_shell_set_show_image + (GimpDisplayShell *shell, + gboolean show_image); + +void gimp_display_shell_set_show_all (GimpDisplayShell *shell, + gboolean show_all); + +GimpPickable * gimp_display_shell_get_pickable (GimpDisplayShell *shell); +GimpPickable * gimp_display_shell_get_canvas_pickable + (GimpDisplayShell *shell); +GeglRectangle gimp_display_shell_get_bounding_box + (GimpDisplayShell *shell); +gboolean gimp_display_shell_get_infinite_canvas + (GimpDisplayShell *shell); + +void gimp_display_shell_update_priority_rect + (GimpDisplayShell *shell); + +void gimp_display_shell_flush (GimpDisplayShell *shell, + gboolean now); + +void gimp_display_shell_pause (GimpDisplayShell *shell); +void gimp_display_shell_resume (GimpDisplayShell *shell); + +void gimp_display_shell_set_highlight (GimpDisplayShell *shell, + const GdkRectangle *highlight, + double opacity); +void gimp_display_shell_set_mask (GimpDisplayShell *shell, + GeglBuffer *mask, + gint offset_x, + gint offset_y, + const GimpRGB *color, + gboolean inverted); + + +#endif /* __GIMP_DISPLAY_SHELL_H__ */ diff --git a/app/display/gimpdisplayxfer.c b/app/display/gimpdisplayxfer.c new file mode 100644 index 0000000..1dcd13c --- /dev/null +++ b/app/display/gimpdisplayxfer.c @@ -0,0 +1,289 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "display-types.h" + +#include "gimpdisplayxfer.h" + + +#define NUM_PAGES 2 + +typedef struct _RTree RTree; +typedef struct _RTreeNode RTreeNode; + +struct _RTreeNode +{ + RTreeNode *children[2]; + RTreeNode *next; + gint x, y, w, h; +}; + +struct _RTree +{ + RTreeNode root; + RTreeNode *available; +}; + +struct _GimpDisplayXfer +{ + /* track subregions of render_surface for efficient uploads */ + RTree rtree; + cairo_surface_t *render_surface[NUM_PAGES]; + gint page; +}; + + +gint GIMP_DISPLAY_RENDER_BUF_WIDTH = 256; +gint GIMP_DISPLAY_RENDER_BUF_HEIGHT = 256; + + +static RTreeNode * +rtree_node_create (RTree *rtree, + RTreeNode **prev, + gint x, + gint y, + gint w, + gint h) +{ + RTreeNode *node; + + gimp_assert (x >= 0 && x+w <= rtree->root.w); + gimp_assert (y >= 0 && y+h <= rtree->root.h); + + if (w <= 0 || h <= 0) + return NULL; + + node = g_slice_alloc (sizeof (*node)); + + node->children[0] = NULL; + node->children[1] = NULL; + node->x = x; + node->y = y; + node->w = w; + node->h = h; + + node->next = *prev; + *prev = node; + + return node; +} + +static void +rtree_node_destroy (RTree *rtree, + RTreeNode *node) +{ + gint i; + + for (i = 0; i < 2; i++) + { + if (node->children[i]) + rtree_node_destroy (rtree, node->children[i]); + } + + g_slice_free (RTreeNode, node); +} + +static RTreeNode * +rtree_node_insert (RTree *rtree, + RTreeNode **prev, + RTreeNode *node, + gint w, + gint h) +{ + *prev = node->next; + + if (((node->w - w) | (node->h - h)) > 1) + { + gint ww = node->w - w; + gint hh = node->h - h; + + if (ww >= hh) + { + node->children[0] = rtree_node_create (rtree, prev, + node->x + w, node->y, + ww, node->h); + node->children[1] = rtree_node_create (rtree, prev, + node->x, node->y + h, + w, hh); + } + else + { + node->children[0] = rtree_node_create (rtree, prev, + node->x, node->y + h, + node->w, hh); + node->children[1] = rtree_node_create (rtree, prev, + node->x + w, node->y, + ww, h); + } + } + + return node; +} + +static RTreeNode * +rtree_insert (RTree *rtree, + gint w, + gint h) +{ + RTreeNode *node, **prev; + + for (prev = &rtree->available; (node = *prev); prev = &node->next) + if (node->w >= w && node->h >= h) + return rtree_node_insert (rtree, prev, node, w, h); + + return NULL; +} + +static void +rtree_init (RTree *rtree, + gint w, + gint h) +{ + rtree->root.x = 0; + rtree->root.y = 0; + rtree->root.w = w; + rtree->root.h = h; + rtree->root.children[0] = NULL; + rtree->root.children[1] = NULL; + rtree->root.next = NULL; + rtree->available = &rtree->root; +} + +static void +rtree_reset (RTree *rtree) +{ + gint i; + + for (i = 0; i < 2; i++) + { + if (rtree->root.children[i] == NULL) + continue; + + rtree_node_destroy (rtree, rtree->root.children[i]); + rtree->root.children[i] = NULL; + } + + rtree->root.next = NULL; + rtree->available = &rtree->root; +} + +static void +xfer_destroy (void *data) +{ + GimpDisplayXfer *xfer = data; + gint i; + + for (i = 0; i < NUM_PAGES; i++) + cairo_surface_destroy (xfer->render_surface[i]); + + rtree_reset (&xfer->rtree); + g_free (xfer); +} + +GimpDisplayXfer * +gimp_display_xfer_realize (GtkWidget *widget) +{ + GdkScreen *screen; + GimpDisplayXfer *xfer; + const gchar *env; + + env = g_getenv ("GIMP_DISPLAY_RENDER_BUF_SIZE"); + if (env) + { + gint width = atoi (env); + gint height = width; + + env = strchr (env, 'x'); + if (env) + height = atoi (env + 1); + + if (width > 0 && width <= 8192 && + height > 0 && height <= 8192) + { + GIMP_DISPLAY_RENDER_BUF_WIDTH = width; + GIMP_DISPLAY_RENDER_BUF_HEIGHT = height; + } + } + + screen = gtk_widget_get_screen (widget); + xfer = g_object_get_data (G_OBJECT (screen), "gimp-display-xfer"); + + if (xfer == NULL) + { + cairo_t *cr; + gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH; + gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT; + int n; + + xfer = g_new (GimpDisplayXfer, 1); + rtree_init (&xfer->rtree, w, h); + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + for (n = 0; n < NUM_PAGES; n++) + { + xfer->render_surface[n] = + cairo_surface_create_similar_image (cairo_get_target (cr), + CAIRO_FORMAT_ARGB32, w, h); + cairo_surface_mark_dirty (xfer->render_surface[n]); + } + cairo_destroy (cr); + xfer->page = 0; + + g_object_set_data_full (G_OBJECT (screen), + "gimp-display-xfer", + xfer, xfer_destroy); + } + + return xfer; +} + +cairo_surface_t * +gimp_display_xfer_get_surface (GimpDisplayXfer *xfer, + gint w, + gint h, + gint *src_x, + gint *src_y) +{ + RTreeNode *node; + + gimp_assert (w <= GIMP_DISPLAY_RENDER_BUF_WIDTH && + h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT); + + node = rtree_insert (&xfer->rtree, w, h); + if (node == NULL) + { + xfer->page = (xfer->page + 1) % NUM_PAGES; + cairo_surface_flush (xfer->render_surface[xfer->page]); + rtree_reset (&xfer->rtree); + cairo_surface_mark_dirty (xfer->render_surface[xfer->page]); /* XXX */ + node = rtree_insert (&xfer->rtree, w, h); + gimp_assert (node != NULL); + } + + *src_x = node->x; + *src_y = node->y; + + return xfer->render_surface[xfer->page]; +} diff --git a/app/display/gimpdisplayxfer.h b/app/display/gimpdisplayxfer.h new file mode 100644 index 0000000..20656b9 --- /dev/null +++ b/app/display/gimpdisplayxfer.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __GIMP_DISPLAY_XFER_H__ +#define __GIMP_DISPLAY_XFER_H__ + + +extern gint GIMP_DISPLAY_RENDER_BUF_WIDTH; +extern gint GIMP_DISPLAY_RENDER_BUF_HEIGHT; + +#define GIMP_DISPLAY_RENDER_MAX_SCALE 4.0 + + +GimpDisplayXfer * gimp_display_xfer_realize (GtkWidget *widget); + +cairo_surface_t * gimp_display_xfer_get_surface (GimpDisplayXfer *xfer, + gint w, + gint h, + gint *src_x, + gint *src_y); + + +#endif /* __GIMP_DISPLAY_XFER_H__ */ diff --git a/app/display/gimpimagewindow.c b/app/display/gimpimagewindow.c new file mode 100644 index 0000000..fd5c501 --- /dev/null +++ b/app/display/gimpimagewindow.c @@ -0,0 +1,2428 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#ifdef G_OS_WIN32 +#include +#include +#include +#endif + +#ifdef GDK_WINDOWING_QUARTZ +#import +#include +#endif /* !GDK_WINDOWING_QUARTZ */ + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpprogress.h" +#include "core/gimpcontainer.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockbook.h" +#include "widgets/gimpdockcolumns.h" +#include "widgets/gimpdockcontainer.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpsessioninfo-aux.h" +#include "widgets/gimpsessionmanaged.h" +#include "widgets/gimpsessioninfo-dock.h" +#include "widgets/gimptoolbox.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpview.h" +#include "widgets/gimpviewrenderer.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplay.h" +#include "gimpdisplay-foreach.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-close.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-tool-events.h" +#include "gimpdisplayshell-transform.h" +#include "gimpimagewindow.h" +#include "gimpstatusbar.h" + +#include "gimp-log.h" +#include "gimp-priorities.h" + +#include "gimp-intl.h" + + +#define GIMP_EMPTY_IMAGE_WINDOW_ENTRY_ID "gimp-empty-image-window" +#define GIMP_SINGLE_IMAGE_WINDOW_ENTRY_ID "gimp-single-image-window" + +/* The width of the left and right dock areas */ +#define GIMP_IMAGE_WINDOW_LEFT_DOCKS_WIDTH "left-docks-width" +#define GIMP_IMAGE_WINDOW_RIGHT_DOCKS_WIDTH "right-docks-width" + +/* deprecated property: GtkPaned position of the right docks area */ +#define GIMP_IMAGE_WINDOW_RIGHT_DOCKS_POS "right-docks-position" + +/* Whether the window's maximized or not */ +#define GIMP_IMAGE_WINDOW_MAXIMIZED "maximized" + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 +#define NSWindowCollectionBehaviorFullScreenAuxiliary (1 << 8) +#endif + + +enum +{ + PROP_0, + PROP_GIMP, + PROP_DIALOG_FACTORY, + PROP_INITIAL_SCREEN, + PROP_INITIAL_MONITOR +}; + + +typedef struct _GimpImageWindowPrivate GimpImageWindowPrivate; + +struct _GimpImageWindowPrivate +{ + Gimp *gimp; + GimpUIManager *menubar_manager; + GimpDialogFactory *dialog_factory; + + GList *shells; + GimpDisplayShell *active_shell; + + GtkWidget *main_vbox; + GtkWidget *menubar; + GtkWidget *hbox; + GtkWidget *left_hpane; + GtkWidget *left_docks; + GtkWidget *right_hpane; + GtkWidget *notebook; + GtkWidget *right_docks; + + GdkWindowState window_state; + + const gchar *entry_id; + + GdkScreen *initial_screen; + gint initial_monitor; + + gint suspend_keep_pos; + + gint update_ui_manager_idle_id; +}; + +typedef struct +{ + gint canvas_x; + gint canvas_y; + gint window_x; + gint window_y; +} PosCorrectionData; + + +#define GIMP_IMAGE_WINDOW_GET_PRIVATE(window) \ + ((GimpImageWindowPrivate *) gimp_image_window_get_instance_private ((GimpImageWindow *) (window))) + + +/* local function prototypes */ + +static void gimp_image_window_dock_container_iface_init + (GimpDockContainerInterface + *iface); +static void gimp_image_window_session_managed_iface_init + (GimpSessionManagedInterface + *iface); +static void gimp_image_window_constructed (GObject *object); +static void gimp_image_window_dispose (GObject *object); +static void gimp_image_window_finalize (GObject *object); +static void gimp_image_window_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_image_window_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_image_window_map (GtkWidget *widget); +static gboolean gimp_image_window_delete_event (GtkWidget *widget, + GdkEventAny *event); +static gboolean gimp_image_window_configure_event (GtkWidget *widget, + GdkEventConfigure *event); +static gboolean gimp_image_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event); +static void gimp_image_window_style_set (GtkWidget *widget, + GtkStyle *prev_style); + +static void gimp_image_window_monitor_changed (GimpWindow *window, + GdkScreen *screen, + gint monitor); + +static GList * gimp_image_window_get_docks (GimpDockContainer *dock_container); +static GimpDialogFactory * + gimp_image_window_dock_container_get_dialog_factory + (GimpDockContainer *dock_container); +static GimpUIManager * + gimp_image_window_dock_container_get_ui_manager + (GimpDockContainer *dock_container); +static void gimp_image_window_add_dock (GimpDockContainer *dock_container, + GimpDock *dock, + GimpSessionInfoDock *dock_info); +static GimpAlignmentType + gimp_image_window_get_dock_side (GimpDockContainer *dock_container, + GimpDock *dock); +static GList * gimp_image_window_get_aux_info (GimpSessionManaged *session_managed); +static void gimp_image_window_set_aux_info (GimpSessionManaged *session_managed, + GList *aux_info); + +static void gimp_image_window_config_notify (GimpImageWindow *window, + GParamSpec *pspec, + GimpGuiConfig *config); +static void gimp_image_window_session_clear (GimpImageWindow *window); +static void gimp_image_window_session_apply (GimpImageWindow *window, + const gchar *entry_id, + GdkScreen *screen, + gint monitor); +static void gimp_image_window_session_update (GimpImageWindow *window, + GimpDisplay *new_display, + const gchar *new_entry_id, + GdkScreen *screen, + gint monitor); +static const gchar * + gimp_image_window_config_to_entry_id (GimpGuiConfig *config); +static void gimp_image_window_show_tooltip (GimpUIManager *manager, + const gchar *tooltip, + GimpImageWindow *window); +static void gimp_image_window_hide_tooltip (GimpUIManager *manager, + GimpImageWindow *window); +static gboolean gimp_image_window_update_ui_manager_idle + (GimpImageWindow *window); +static void gimp_image_window_update_ui_manager (GimpImageWindow *window); + +static void gimp_image_window_shell_size_allocate (GimpDisplayShell *shell, + GtkAllocation *allocation, + PosCorrectionData *data); +static gboolean gimp_image_window_shell_events (GtkWidget *widget, + GdkEvent *event, + GimpImageWindow *window); + +static void gimp_image_window_switch_page (GtkNotebook *notebook, + gpointer page, + gint page_num, + GimpImageWindow *window); +static void gimp_image_window_page_removed (GtkNotebook *notebook, + GtkWidget *widget, + gint page_num, + GimpImageWindow *window); +static void gimp_image_window_page_reordered (GtkNotebook *notebook, + GtkWidget *widget, + gint page_num, + GimpImageWindow *window); +static void gimp_image_window_disconnect_from_active_shell + (GimpImageWindow *window); + +static void gimp_image_window_image_notify (GimpDisplay *display, + const GParamSpec *pspec, + GimpImageWindow *window); +static void gimp_image_window_shell_scaled (GimpDisplayShell *shell, + GimpImageWindow *window); +static void gimp_image_window_shell_rotated (GimpDisplayShell *shell, + GimpImageWindow *window); +static void gimp_image_window_shell_title_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpImageWindow *window); +static void gimp_image_window_shell_icon_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpImageWindow *window); +static GtkWidget * + gimp_image_window_create_tab_label (GimpImageWindow *window, + GimpDisplayShell *shell); +static void gimp_image_window_update_tab_labels (GimpImageWindow *window); + + +G_DEFINE_TYPE_WITH_CODE (GimpImageWindow, gimp_image_window, GIMP_TYPE_WINDOW, + G_ADD_PRIVATE (GimpImageWindow) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCK_CONTAINER, + gimp_image_window_dock_container_iface_init) + G_IMPLEMENT_INTERFACE (GIMP_TYPE_SESSION_MANAGED, + gimp_image_window_session_managed_iface_init)) + +#define parent_class gimp_image_window_parent_class + + +static const gchar image_window_rc_style[] = + "style \"fullscreen-menubar-style\"\n" + "{\n" + " GtkMenuBar::shadow-type = none\n" + " GtkMenuBar::internal-padding = 0\n" + "}\n" + "widget \"*.gimp-menubar-fullscreen\" style \"fullscreen-menubar-style\"\n"; + +static void +gimp_image_window_class_init (GimpImageWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GimpWindowClass *window_class = GIMP_WINDOW_CLASS (klass); + + object_class->constructed = gimp_image_window_constructed; + object_class->dispose = gimp_image_window_dispose; + object_class->finalize = gimp_image_window_finalize; + object_class->set_property = gimp_image_window_set_property; + object_class->get_property = gimp_image_window_get_property; + + widget_class->map = gimp_image_window_map; + widget_class->delete_event = gimp_image_window_delete_event; + widget_class->configure_event = gimp_image_window_configure_event; + widget_class->window_state_event = gimp_image_window_window_state_event; + widget_class->style_set = gimp_image_window_style_set; + + window_class->monitor_changed = gimp_image_window_monitor_changed; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", + NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_DIALOG_FACTORY, + g_param_spec_object ("dialog-factory", + NULL, NULL, + GIMP_TYPE_DIALOG_FACTORY, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_INITIAL_SCREEN, + g_param_spec_object ("initial-screen", + NULL, NULL, + GDK_TYPE_SCREEN, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_INITIAL_MONITOR, + g_param_spec_int ("initial-monitor", + NULL, NULL, + 0, 16, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + gtk_rc_parse_string (image_window_rc_style); +} + +static void +gimp_image_window_init (GimpImageWindow *window) +{ + static gint role_serial = 1; + gchar *role; + + role = g_strdup_printf ("gimp-image-window-%d", role_serial++); + gtk_window_set_role (GTK_WINDOW (window), role); + g_free (role); + + gtk_window_set_resizable (GTK_WINDOW (window), TRUE); +} + +static void +gimp_image_window_dock_container_iface_init (GimpDockContainerInterface *iface) +{ + iface->get_docks = gimp_image_window_get_docks; + iface->get_dialog_factory = gimp_image_window_dock_container_get_dialog_factory; + iface->get_ui_manager = gimp_image_window_dock_container_get_ui_manager; + iface->add_dock = gimp_image_window_add_dock; + iface->get_dock_side = gimp_image_window_get_dock_side; +} + +static void +gimp_image_window_session_managed_iface_init (GimpSessionManagedInterface *iface) +{ + iface->get_aux_info = gimp_image_window_get_aux_info; + iface->set_aux_info = gimp_image_window_set_aux_info; +} + +static void +gimp_image_window_constructed (GObject *object) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (object); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpMenuFactory *menu_factory; + GimpGuiConfig *config; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_GIMP (private->gimp)); + gimp_assert (GIMP_IS_DIALOG_FACTORY (private->dialog_factory)); + + menu_factory = gimp_dialog_factory_get_menu_factory (private->dialog_factory); + + private->menubar_manager = gimp_menu_factory_manager_new (menu_factory, + "", + window, + FALSE); + + g_signal_connect_object (private->dialog_factory, "dock-window-added", + G_CALLBACK (gimp_image_window_update_ui_manager), + window, G_CONNECT_SWAPPED); + g_signal_connect_object (private->dialog_factory, "dock-window-removed", + G_CALLBACK (gimp_image_window_update_ui_manager), + window, G_CONNECT_SWAPPED); + + gtk_window_add_accel_group (GTK_WINDOW (window), + gimp_ui_manager_get_accel_group (private->menubar_manager)); + + g_signal_connect (private->menubar_manager, "show-tooltip", + G_CALLBACK (gimp_image_window_show_tooltip), + window); + g_signal_connect (private->menubar_manager, "hide-tooltip", + G_CALLBACK (gimp_image_window_hide_tooltip), + window); + + config = GIMP_GUI_CONFIG (private->gimp->config); + + /* Create the window toplevel container */ + private->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (window), private->main_vbox); + gtk_widget_show (private->main_vbox); + + /* Create the menubar */ +#ifndef GDK_WINDOWING_QUARTZ + private->menubar = gimp_ui_manager_get_widget (private->menubar_manager, + "/image-menubar"); +#endif /* !GDK_WINDOWING_QUARTZ */ + if (private->menubar) + { + gtk_box_pack_start (GTK_BOX (private->main_vbox), + private->menubar, FALSE, FALSE, 0); + + /* make sure we can activate accels even if the menubar is invisible + * (see https://bugzilla.gnome.org/show_bug.cgi?id=137151) + */ + g_signal_connect (private->menubar, "can-activate-accel", + G_CALLBACK (gtk_true), + NULL); + + /* active display callback */ + g_signal_connect (private->menubar, "button-press-event", + G_CALLBACK (gimp_image_window_shell_events), + window); + g_signal_connect (private->menubar, "button-release-event", + G_CALLBACK (gimp_image_window_shell_events), + window); + g_signal_connect (private->menubar, "key-press-event", + G_CALLBACK (gimp_image_window_shell_events), + window); + } + + /* Create the hbox that contains docks and images */ + private->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (private->main_vbox), private->hbox, + TRUE, TRUE, 0); + gtk_widget_show (private->hbox); + + /* Create the left pane */ + private->left_hpane = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start (GTK_BOX (private->hbox), private->left_hpane, + TRUE, TRUE, 0); + gtk_widget_show (private->left_hpane); + + /* Create the left dock columns widget */ + private->left_docks = + gimp_dock_columns_new (gimp_get_user_context (private->gimp), + private->dialog_factory, + private->menubar_manager); + gtk_paned_pack1 (GTK_PANED (private->left_hpane), private->left_docks, + FALSE, FALSE); + gtk_widget_set_visible (private->left_docks, config->single_window_mode); + + /* Create the right pane */ + private->right_hpane = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL); + gtk_paned_pack2 (GTK_PANED (private->left_hpane), private->right_hpane, + TRUE, FALSE); + gtk_widget_show (private->right_hpane); + + /* Create notebook that contains images */ + private->notebook = gtk_notebook_new (); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (private->notebook), TRUE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (private->notebook), FALSE); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (private->notebook), FALSE); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (private->notebook), GTK_POS_TOP); + + gtk_paned_pack1 (GTK_PANED (private->right_hpane), private->notebook, + TRUE, TRUE); + + g_signal_connect (private->notebook, "switch-page", + G_CALLBACK (gimp_image_window_switch_page), + window); + g_signal_connect (private->notebook, "page-removed", + G_CALLBACK (gimp_image_window_page_removed), + window); + g_signal_connect (private->notebook, "page-reordered", + G_CALLBACK (gimp_image_window_page_reordered), + window); + gtk_widget_show (private->notebook); + + /* Create the right dock columns widget */ + private->right_docks = + gimp_dock_columns_new (gimp_get_user_context (private->gimp), + private->dialog_factory, + private->menubar_manager); + gtk_paned_pack2 (GTK_PANED (private->right_hpane), private->right_docks, + FALSE, FALSE); + gtk_widget_set_visible (private->right_docks, config->single_window_mode); + + g_signal_connect_object (config, "notify::single-window-mode", + G_CALLBACK (gimp_image_window_config_notify), + window, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::show-tabs", + G_CALLBACK (gimp_image_window_config_notify), + window, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::hide-docks", + G_CALLBACK (gimp_image_window_config_notify), + window, G_CONNECT_SWAPPED); + g_signal_connect_object (config, "notify::tabs-position", + G_CALLBACK (gimp_image_window_config_notify), + window, G_CONNECT_SWAPPED); + + gimp_image_window_session_update (window, + NULL /*new_display*/, + gimp_image_window_config_to_entry_id (config), + private->initial_screen, + private->initial_monitor); +} + +static void +gimp_image_window_dispose (GObject *object) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (object); + + if (private->dialog_factory) + { + g_signal_handlers_disconnect_by_func (private->dialog_factory, + gimp_image_window_update_ui_manager, + object); + private->dialog_factory = NULL; + } + + g_clear_object (&private->menubar_manager); + + if (private->update_ui_manager_idle_id) + { + g_source_remove (private->update_ui_manager_idle_id); + private->update_ui_manager_idle_id = 0; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_image_window_finalize (GObject *object) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (object); + + if (private->shells) + { + g_list_free (private->shells); + private->shells = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_image_window_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (object); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + switch (property_id) + { + case PROP_GIMP: + private->gimp = g_value_get_object (value); + break; + case PROP_DIALOG_FACTORY: + private->dialog_factory = g_value_get_object (value); + break; + case PROP_INITIAL_SCREEN: + private->initial_screen = g_value_get_object (value); + break; + case PROP_INITIAL_MONITOR: + private->initial_monitor = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_window_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (object); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, private->gimp); + break; + case PROP_DIALOG_FACTORY: + g_value_set_object (value, private->dialog_factory); + break; + case PROP_INITIAL_SCREEN: + g_value_set_object (value, private->initial_screen); + break; + case PROP_INITIAL_MONITOR: + g_value_set_int (value, private->initial_monitor); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_image_window_map (GtkWidget *widget) +{ +#ifdef GDK_WINDOWING_QUARTZ + GdkWindow *gdk_window; + NSWindow *ns_window; +#endif /* !GDK_WINDOWING_QUARTZ */ + + GTK_WIDGET_CLASS (parent_class)->map (widget); + +#ifdef GDK_WINDOWING_QUARTZ + gdk_window = gtk_widget_get_window (GTK_WIDGET (widget)); + ns_window = gdk_quartz_window_get_nswindow (gdk_window); + + /* Disable the new-style full screen mode. For now only the "old-style" + * full screen mode, via the "View" menu, is supported. In the future, and + * as soon as GTK+ has proper support for this, we will migrate to the + * new-style full screen mode. + */ + ns_window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; +#endif /* !GDK_WINDOWING_QUARTZ */ +} + +static gboolean +gimp_image_window_delete_event (GtkWidget *widget, + GdkEventAny *event) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (widget); + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpGuiConfig *config = GIMP_GUI_CONFIG (private->gimp->config); + + if (config->single_window_mode) + gimp_ui_manager_activate_action (gimp_image_window_get_ui_manager (window), + "file", "file-quit"); + else if (shell) + gimp_display_shell_close (shell, FALSE); + + return TRUE; +} + +static gboolean +gimp_image_window_configure_event (GtkWidget *widget, + GdkEventConfigure *event) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (widget); + GtkAllocation allocation; + gint current_width; + gint current_height; + + gtk_widget_get_allocation (widget, &allocation); + + /* Grab the size before we run the parent implementation */ + current_width = allocation.width; + current_height = allocation.height; + + /* Run the parent implementation */ + if (GTK_WIDGET_CLASS (parent_class)->configure_event) + GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); + + /* If the window size has changed, make sure additoinal logic is run + * in the display shell's size-allocate + */ + if (event->width != current_width || + event->height != current_height) + { + /* FIXME multiple shells */ + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + + if (shell && gimp_display_get_image (shell->display)) + shell->size_allocate_from_configure_event = TRUE; + } + + return TRUE; +} + +static gboolean +gimp_image_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (widget); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + + if (! shell) + return FALSE; + + private->window_state = event->new_window_state; + + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + { + gboolean fullscreen = gimp_image_window_get_fullscreen (window); + + GIMP_LOG (WM, "Image window '%s' [%p] set fullscreen %s", + gtk_window_get_title (GTK_WINDOW (widget)), + widget, + fullscreen ? "TRUE" : "FALSE"); + + if (private->menubar) + gtk_widget_set_name (private->menubar, + fullscreen ? "gimp-menubar-fullscreen" : NULL); + + gimp_image_window_suspend_keep_pos (window); + gimp_display_shell_appearance_update (shell); + gimp_image_window_resume_keep_pos (window); + } + + if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) + { + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + gboolean iconified = gimp_image_window_is_iconified (window); + + GIMP_LOG (WM, "Image window '%s' [%p] set %s", + gtk_window_get_title (GTK_WINDOW (widget)), + widget, + iconified ? "iconified" : "uniconified"); + + if (iconified) + { + if (gimp_displays_get_num_visible (private->gimp) == 0) + { + GIMP_LOG (WM, "No displays visible any longer"); + + gimp_dialog_factory_hide_with_display (private->dialog_factory); + } + } + else + { + gimp_dialog_factory_show_with_display (private->dialog_factory); + } + + if (gimp_progress_is_active (GIMP_PROGRESS (statusbar))) + { + if (iconified) + gimp_statusbar_override_window_title (statusbar); + else + gtk_window_set_title (GTK_WINDOW (window), shell->title); + } + } + + return FALSE; +} + +static void +gimp_image_window_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (widget); + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + GimpStatusbar *statusbar = NULL; + GtkRequisition requisition = { 0, }; + GdkGeometry geometry = { 0, }; + GdkWindowHints geometry_mask = 0; + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + if (! shell) + return; + + statusbar = gimp_display_shell_get_statusbar (shell); + + gtk_widget_size_request (GTK_WIDGET (statusbar), &requisition); + + geometry.min_height = 23; + + geometry.min_width = requisition.width; + geometry.min_height += requisition.height; + + if (private->menubar) + { + gtk_widget_size_request (private->menubar, &requisition); + + geometry.min_height += requisition.height; + } + + geometry_mask = GDK_HINT_MIN_SIZE; + + /* Only set user pos on the empty display because it gets a pos + * set by gimp. All other displays should be placed by the window + * manager. See https://bugzilla.gnome.org/show_bug.cgi?id=559580 + */ + if (! gimp_display_get_image (shell->display)) + geometry_mask |= GDK_HINT_USER_POS; + + gtk_window_set_geometry_hints (GTK_WINDOW (widget), NULL, + &geometry, geometry_mask); + + gimp_dialog_factory_set_has_min_size (GTK_WINDOW (widget), TRUE); +} + +static void +gimp_image_window_monitor_changed (GimpWindow *window, + GdkScreen *screen, + gint monitor) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GList *list; + + for (list = private->shells; list; list = g_list_next (list)) + { + /* hack, this should live here, and screen_changed call + * monitor_changed + */ + g_signal_emit_by_name (list->data, "screen-changed", + gtk_widget_get_screen (list->data)); + + /* make it fetch the new monitor's resolution */ + gimp_display_shell_scale_update (GIMP_DISPLAY_SHELL (list->data)); + + /* make it fetch the right monitor profile */ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (list->data)); + } +} + +static GList * +gimp_image_window_get_docks (GimpDockContainer *dock_container) +{ + GimpImageWindowPrivate *private; + GList *iter; + GList *all_docks = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (dock_container), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (dock_container); + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->left_docks)); + iter; + iter = g_list_next (iter)) + { + all_docks = g_list_append (all_docks, GIMP_DOCK (iter->data)); + } + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->right_docks)); + iter; + iter = g_list_next (iter)) + { + all_docks = g_list_append (all_docks, GIMP_DOCK (iter->data)); + } + + return all_docks; +} + +static GimpDialogFactory * +gimp_image_window_dock_container_get_dialog_factory (GimpDockContainer *dock_container) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (dock_container); + + return private->dialog_factory; +} + +static GimpUIManager * +gimp_image_window_dock_container_get_ui_manager (GimpDockContainer *dock_container) +{ + GimpImageWindow *window = GIMP_IMAGE_WINDOW (dock_container); + + return gimp_image_window_get_ui_manager (window); +} + +void +gimp_image_window_add_dock (GimpDockContainer *dock_container, + GimpDock *dock, + GimpSessionInfoDock *dock_info) +{ + GimpImageWindow *window; + GimpDisplayShell *active_shell; + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (dock_container)); + + window = GIMP_IMAGE_WINDOW (dock_container); + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (dock_info->side == GIMP_ALIGN_LEFT) + { + gimp_dock_columns_add_dock (GIMP_DOCK_COLUMNS (private->left_docks), + dock, + -1 /*index*/); + } + else + { + gimp_dock_columns_add_dock (GIMP_DOCK_COLUMNS (private->right_docks), + dock, + -1 /*index*/); + } + + active_shell = gimp_image_window_get_active_shell (window); + if (active_shell) + gimp_display_shell_appearance_update (active_shell); +} + +static GimpAlignmentType +gimp_image_window_get_dock_side (GimpDockContainer *dock_container, + GimpDock *dock) +{ + GimpAlignmentType side = -1; + GimpImageWindowPrivate *private; + GList *iter; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (dock_container), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (dock_container); + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->left_docks)); + iter && side == -1; + iter = g_list_next (iter)) + { + GimpDock *dock_iter = GIMP_DOCK (iter->data); + + if (dock_iter == dock) + side = GIMP_ALIGN_LEFT; + } + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->right_docks)); + iter && side == -1; + iter = g_list_next (iter)) + { + GimpDock *dock_iter = GIMP_DOCK (iter->data); + + if (dock_iter == dock) + side = GIMP_ALIGN_RIGHT; + } + + return side; +} + +static GList * +gimp_image_window_get_aux_info (GimpSessionManaged *session_managed) +{ + GList *aux_info = NULL; + GimpImageWindowPrivate *private; + GimpGuiConfig *config; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (session_managed), NULL); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (session_managed); + config = GIMP_GUI_CONFIG (private->gimp->config); + + if (config->single_window_mode) + { + GimpSessionInfoAux *aux; + GtkAllocation allocation; + gchar widthbuf[128]; + + g_snprintf (widthbuf, sizeof (widthbuf), "%d", + gtk_paned_get_position (GTK_PANED (private->left_hpane))); + aux = gimp_session_info_aux_new (GIMP_IMAGE_WINDOW_LEFT_DOCKS_WIDTH, + widthbuf); + aux_info = g_list_append (aux_info, aux); + + gtk_widget_get_allocation (private->right_hpane, &allocation); + + g_snprintf (widthbuf, sizeof (widthbuf), "%d", + allocation.width - + gtk_paned_get_position (GTK_PANED (private->right_hpane))); + aux = gimp_session_info_aux_new (GIMP_IMAGE_WINDOW_RIGHT_DOCKS_WIDTH, + widthbuf); + aux_info = g_list_append (aux_info, aux); + + aux = gimp_session_info_aux_new (GIMP_IMAGE_WINDOW_MAXIMIZED, + gimp_image_window_is_maximized (GIMP_IMAGE_WINDOW (session_managed)) ? + "yes" : "no"); + aux_info = g_list_append (aux_info, aux); + } + + return aux_info; +} + +static void +gimp_image_window_set_right_docks_width (GtkPaned *paned, + GtkAllocation *allocation, + void *data) +{ + gint width = GPOINTER_TO_INT (data); + + g_return_if_fail (GTK_IS_PANED (paned)); + + if (width > 0) + gtk_paned_set_position (paned, allocation->width - width); + else + gtk_paned_set_position (paned, - width); + + g_signal_handlers_disconnect_by_func (paned, + gimp_image_window_set_right_docks_width, + data); +} + +static void +gimp_image_window_set_aux_info (GimpSessionManaged *session_managed, + GList *aux_info) +{ + GimpImageWindowPrivate *private; + GList *iter; + gint left_docks_width = G_MININT; + gint right_docks_width = G_MININT; + gboolean wait_with_right_docks = FALSE; + gboolean maximized = FALSE; +#ifdef G_OS_WIN32 + STARTUPINFO StartupInfo; + + GetStartupInfo (&StartupInfo); +#endif + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (session_managed)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (session_managed); + + for (iter = aux_info; iter; iter = g_list_next (iter)) + { + GimpSessionInfoAux *aux = iter->data; + gint *width = NULL; + + if (! strcmp (aux->name, GIMP_IMAGE_WINDOW_LEFT_DOCKS_WIDTH)) + width = &left_docks_width; + else if (! strcmp (aux->name, GIMP_IMAGE_WINDOW_RIGHT_DOCKS_WIDTH)) + width = &right_docks_width; + else if (! strcmp (aux->name, GIMP_IMAGE_WINDOW_RIGHT_DOCKS_POS)) + width = &right_docks_width; + else if (! strcmp (aux->name, GIMP_IMAGE_WINDOW_MAXIMIZED)) + if (! g_ascii_strcasecmp (aux->value, "yes")) + maximized = TRUE; + + if (width) + sscanf (aux->value, "%d", width); + + /* compat handling for right docks */ + if (! strcmp (aux->name, GIMP_IMAGE_WINDOW_RIGHT_DOCKS_POS)) + { + /* negate the value because negative docks pos means docks width, + * also use the negativenes of a real docks pos as condition below. + */ + *width = - *width; + } + } + + if (left_docks_width != G_MININT && + gtk_paned_get_position (GTK_PANED (private->left_hpane)) != + left_docks_width) + { + gtk_paned_set_position (GTK_PANED (private->left_hpane), left_docks_width); + + /* We can't set the position of the right docks, because it will + * be undesirably adjusted when its get a new size + * allocation. We must wait until after the size allocation. + */ + wait_with_right_docks = TRUE; + } + + if (right_docks_width != G_MININT && + gtk_paned_get_position (GTK_PANED (private->right_hpane)) != + right_docks_width) + { + if (wait_with_right_docks || right_docks_width > 0) + { + /* We must wait for a size allocation before we can set the + * position + */ + g_signal_connect_data (private->right_hpane, "size-allocate", + G_CALLBACK (gimp_image_window_set_right_docks_width), + GINT_TO_POINTER (right_docks_width), NULL, + G_CONNECT_AFTER); + } + else + { + /* We can set the position directly, because we didn't + * change the left hpane position, and we got the old compat + * dock pos property. + */ + gtk_paned_set_position (GTK_PANED (private->right_hpane), + - right_docks_width); + } + } + +#ifdef G_OS_WIN32 + /* On Windows, user can provide startup hints to have a program + * maximized/minimized on startup. This can be done through command + * line: `start /max gimp-2.9.exe` or with the shortcut's "run" + * property. + * When such a hint is given, we should follow it and bypass the + * session's information. + */ + if (StartupInfo.wShowWindow == SW_SHOWMAXIMIZED) + gtk_window_maximize (GTK_WINDOW (session_managed)); + else if (StartupInfo.wShowWindow == SW_SHOWMINIMIZED || + StartupInfo.wShowWindow == SW_SHOWMINNOACTIVE || + StartupInfo.wShowWindow == SW_MINIMIZE) + /* XXX Iconification does not seem to work. I see the + * window being iconified and immediately re-raised. + * I leave this piece of code for later improvement. */ + gtk_window_iconify (GTK_WINDOW (session_managed)); + else + /* Another show property not relevant to min/max. + * Defaults is: SW_SHOWNORMAL + */ +#endif + if (maximized) + gtk_window_maximize (GTK_WINDOW (session_managed)); + else + gtk_window_unmaximize (GTK_WINDOW (session_managed)); +} + + +/* public functions */ + +GimpImageWindow * +gimp_image_window_new (Gimp *gimp, + GimpImage *image, + GimpDialogFactory *dialog_factory, + GdkScreen *screen, + gint monitor) +{ + GimpImageWindow *window; + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (dialog_factory), NULL); + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + window = g_object_new (GIMP_TYPE_IMAGE_WINDOW, + "gimp", gimp, + "dialog-factory", dialog_factory, + "initial-screen", screen, + "initial-monitor", monitor, + /* The window position will be overridden by the + * dialog factory, it is only really used on first + * startup. + */ + image ? NULL : "window-position", + GTK_WIN_POS_CENTER, + NULL); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + gimp->image_windows = g_list_append (gimp->image_windows, window); + + if (! GIMP_GUI_CONFIG (private->gimp->config)->single_window_mode) + { + GdkScreen *pointer_screen; + gint pointer_monitor; + + pointer_monitor = gimp_get_monitor_at_pointer (&pointer_screen); + + /* If we are supposed to go to a monitor other than where the + * pointer is, place the window on that monitor manually, + * otherwise simply let the window manager place the window on + * the poiner's monitor. + */ + if (pointer_screen != screen || + pointer_monitor != monitor) + { + GdkRectangle rect; + gchar geom[32]; + + gdk_screen_get_monitor_workarea (screen, monitor, &rect); + + /* FIXME: image window placement + * + * This is ugly beyond description but better than showing + * the window on the wrong monitor + */ + g_snprintf (geom, sizeof (geom), "%+d%+d", + rect.x + 300, rect.y + 30); + gtk_window_parse_geometry (GTK_WINDOW (window), geom); + } + } + + return window; +} + +void +gimp_image_window_destroy (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + private->gimp->image_windows = g_list_remove (private->gimp->image_windows, + window); + + gtk_widget_destroy (GTK_WIDGET (window)); +} + +GimpUIManager * +gimp_image_window_get_ui_manager (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return private->menubar_manager; +} + +GimpDockColumns * +gimp_image_window_get_left_docks (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return GIMP_DOCK_COLUMNS (private->left_docks); +} + +GimpDockColumns * +gimp_image_window_get_right_docks (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return GIMP_DOCK_COLUMNS (private->right_docks); +} + +void +gimp_image_window_add_shell (GimpImageWindow *window, + GimpDisplayShell *shell) +{ + GimpImageWindowPrivate *private; + GtkWidget *tab_label; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (g_list_find (private->shells, shell) == NULL); + + private->shells = g_list_append (private->shells, shell); + + tab_label = gimp_image_window_create_tab_label (window, shell); + + gtk_notebook_append_page (GTK_NOTEBOOK (private->notebook), + GTK_WIDGET (shell), tab_label); + gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (private->notebook), + GTK_WIDGET (shell), TRUE); + + gtk_widget_show (GTK_WIDGET (shell)); + + /* make it fetch the right monitor profile */ + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (shell)); +} + +GimpDisplayShell * +gimp_image_window_get_shell (GimpImageWindow *window, + gint index) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), NULL); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return g_list_nth_data (private->shells, index); +} + +void +gimp_image_window_remove_shell (GimpImageWindow *window, + GimpDisplayShell *shell) +{ + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (g_list_find (private->shells, shell) != NULL); + + private->shells = g_list_remove (private->shells, shell); + + gtk_container_remove (GTK_CONTAINER (private->notebook), + GTK_WIDGET (shell)); +} + +gint +gimp_image_window_get_n_shells (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), 0); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return g_list_length (private->shells); +} + +void +gimp_image_window_set_active_shell (GimpImageWindow *window, + GimpDisplayShell *shell) +{ + GimpImageWindowPrivate *private; + gint page_num; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (g_list_find (private->shells, shell)); + + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (private->notebook), + GTK_WIDGET (shell)); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (private->notebook), page_num); +} + +GimpDisplayShell * +gimp_image_window_get_active_shell (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), NULL); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return private->active_shell; +} + +void +gimp_image_window_set_fullscreen (GimpImageWindow *window, + gboolean fullscreen) +{ + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + if (fullscreen != gimp_image_window_get_fullscreen (window)) + { + if (fullscreen) + gtk_window_fullscreen (GTK_WINDOW (window)); + else + gtk_window_unfullscreen (GTK_WINDOW (window)); + } +} + +gboolean +gimp_image_window_get_fullscreen (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return (private->window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; +} + +void +gimp_image_window_set_show_menubar (GimpImageWindow *window, + gboolean show) +{ + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (private->menubar) + gtk_widget_set_visible (private->menubar, show); +} + +gboolean +gimp_image_window_get_show_menubar (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (private->menubar) + return gtk_widget_get_visible (private->menubar); + + return FALSE; +} + +gboolean +gimp_image_window_is_iconified (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return (private->window_state & GDK_WINDOW_STATE_ICONIFIED) != 0; +} + +gboolean +gimp_image_window_is_maximized (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + return (private->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0; +} + +/** + * gimp_image_window_has_toolbox: + * @window: + * + * Returns: %TRUE if the image window contains a GimpToolbox. + **/ +gboolean +gimp_image_window_has_toolbox (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + GList *iter = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE_WINDOW (window), FALSE); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->left_docks)); + iter; + iter = g_list_next (iter)) + { + if (GIMP_IS_TOOLBOX (iter->data)) + return TRUE; + } + + for (iter = gimp_dock_columns_get_docks (GIMP_DOCK_COLUMNS (private->right_docks)); + iter; + iter = g_list_next (iter)) + { + if (GIMP_IS_TOOLBOX (iter->data)) + return TRUE; + } + + return FALSE; +} + +void +gimp_image_window_shrink_wrap (GimpImageWindow *window, + gboolean grow_only) +{ + GimpDisplayShell *active_shell; + GtkWidget *widget; + GtkAllocation allocation; + GdkScreen *screen; + GdkRectangle rect; + gint monitor; + gint disp_width, disp_height; + gint width, height; + gint max_auto_width, max_auto_height; + gint border_width, border_height; + gboolean resize = FALSE; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + if (! gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + /* FIXME this so needs cleanup and shell/window separation */ + + active_shell = gimp_image_window_get_active_shell (window); + + if (!active_shell) + return; + + widget = GTK_WIDGET (window); + screen = gtk_widget_get_screen (widget); + + gtk_widget_get_allocation (widget, &allocation); + + monitor = gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (widget)); + gdk_screen_get_monitor_workarea (screen, monitor, &rect); + + if (! gimp_display_shell_get_infinite_canvas (active_shell)) + { + gimp_display_shell_scale_get_image_size (active_shell, + &width, &height); + } + else + { + gimp_display_shell_scale_get_image_bounding_box (active_shell, + NULL, NULL, + &width, &height); + } + + disp_width = active_shell->disp_width; + disp_height = active_shell->disp_height; + + + /* As long as the disp_width/disp_height is larger than 1 we + * can reliably depend on it to calculate the + * border_width/border_height because that means there is enough + * room in the top-level for the canvas as well as the rulers and + * scrollbars. If it is 1 or smaller it is likely that the rulers + * and scrollbars are overlapping each other and thus we cannot use + * the normal approach to border size, so special case that. + */ + if (disp_width > 1 || !active_shell->vsb) + { + border_width = allocation.width - disp_width; + } + else + { + GtkAllocation vsb_allocation; + + gtk_widget_get_allocation (active_shell->vsb, &vsb_allocation); + + border_width = allocation.width - disp_width + vsb_allocation.width; + } + + if (disp_height > 1 || !active_shell->hsb) + { + border_height = allocation.height - disp_height; + } + else + { + GtkAllocation hsb_allocation; + + gtk_widget_get_allocation (active_shell->hsb, &hsb_allocation); + + border_height = allocation.height - disp_height + hsb_allocation.height; + } + + + max_auto_width = (rect.width - border_width) * 0.75; + max_auto_height = (rect.height - border_height) * 0.75; + + /* If one of the display dimensions has changed and one of the + * dimensions fits inside the screen + */ + if (((width + border_width) < rect.width || + (height + border_height) < rect.height) && + (width != disp_width || + height != disp_height)) + { + width = ((width + border_width) < rect.width) ? width : max_auto_width; + height = ((height + border_height) < rect.height) ? height : max_auto_height; + + resize = TRUE; + } + + /* If the projected dimension is greater than current, but less than + * 3/4 of the screen size, expand automagically + */ + else if ((width > disp_width || + height > disp_height) && + (disp_width < max_auto_width || + disp_height < max_auto_height)) + { + width = MIN (max_auto_width, width); + height = MIN (max_auto_height, height); + + resize = TRUE; + } + + if (resize) + { + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (active_shell); + gint statusbar_width; + + gtk_widget_get_size_request (GTK_WIDGET (statusbar), + &statusbar_width, NULL); + + if (width < statusbar_width) + width = statusbar_width; + + width = width + border_width; + height = height + border_height; + + if (grow_only) + { + if (width < allocation.width) + width = allocation.width; + + if (height < allocation.height) + height = allocation.height; + } + + gtk_window_resize (GTK_WINDOW (window), width, height); + } + + /* A wrap always means that we should center the image too. If the + * window changes size another center will be done in + * GimpDisplayShell::configure_event(). + */ + /* FIXME multiple shells */ + gimp_display_shell_scroll_center_content (active_shell, TRUE, TRUE); +} + +static GtkWidget * +gimp_image_window_get_first_dockbook (GimpDockColumns *columns) +{ + GList *dock_iter; + + for (dock_iter = gimp_dock_columns_get_docks (columns); + dock_iter; + dock_iter = g_list_next (dock_iter)) + { + GimpDock *dock = GIMP_DOCK (dock_iter->data); + GList *dockbooks = gimp_dock_get_dockbooks (dock); + + if (dockbooks) + return GTK_WIDGET (dockbooks->data); + } + + return NULL; +} + +/** + * gimp_image_window_get_default_dockbook: + * @window: + * + * Gets the default dockbook, which is the dockbook in which new + * dockables should be put in single-window mode. + * + * Returns: The default dockbook for new dockables, or NULL if no + * dockbook were available. + **/ +GtkWidget * +gimp_image_window_get_default_dockbook (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + GimpDockColumns *dock_columns; + GtkWidget *dockbook = NULL; + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + /* First try the first dockbook in the right docks */ + dock_columns = GIMP_DOCK_COLUMNS (private->right_docks); + dockbook = gimp_image_window_get_first_dockbook (dock_columns); + + /* Then the left docks */ + if (! dockbook) + { + dock_columns = GIMP_DOCK_COLUMNS (private->left_docks); + dockbook = gimp_image_window_get_first_dockbook (dock_columns); + } + + return dockbook; +} + +/** + * gimp_image_window_keep_canvas_pos: + * @window: + * + * Stores the coordinates of the current image canvas origin relatively + * its GtkWindow; and on the first size-allocate sets the offsets in + * the shell so that the image origin remains the same (even on another + * GtkWindow). + * + * Example use case: The user hides docks attached to the side of image + * windows. You want the image to remain fixed on the screen though, + * so you use this function to keep the image fixed after the docks + * have been hidden. + **/ +void +gimp_image_window_keep_canvas_pos (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + GimpDisplayShell *shell; + gint canvas_x; + gint canvas_y; + gint window_x; + gint window_y; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (private->suspend_keep_pos > 0) + return; + + shell = gimp_image_window_get_active_shell (window); + + gimp_display_shell_transform_xy (shell, 0.0, 0.0, &canvas_x, &canvas_y); + + if (gtk_widget_translate_coordinates (GTK_WIDGET (shell->canvas), + GTK_WIDGET (window), + canvas_x, canvas_y, + &window_x, &window_y)) + { + PosCorrectionData *data = g_new0 (PosCorrectionData, 1); + + data->canvas_x = canvas_x; + data->canvas_y = canvas_y; + data->window_x = window_x; + data->window_y = window_y; + + g_signal_connect_data (shell, "size-allocate", + G_CALLBACK (gimp_image_window_shell_size_allocate), + data, (GClosureNotify) g_free, + G_CONNECT_AFTER); + } +} + +void +gimp_image_window_suspend_keep_pos (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + private->suspend_keep_pos++; +} + +void +gimp_image_window_resume_keep_pos (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (private->suspend_keep_pos > 0); + + private->suspend_keep_pos--; +} + +/** + * gimp_image_window_update_tabs: + * @window: the Image Window to update. + * + * Holds the logics of whether shell tabs are to be shown or not in the + * Image Window @window. This function should be called after every + * change to @window where one might expect tab visibility to change. + * + * No direct call to gtk_notebook_set_show_tabs() should ever be made. + * If we change the logics of tab hiding, we should only change this + * procedure instead. + **/ +void +gimp_image_window_update_tabs (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private; + GimpGuiConfig *config; + GtkPositionType position; + + g_return_if_fail (GIMP_IS_IMAGE_WINDOW (window)); + + private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + config = GIMP_GUI_CONFIG (private->gimp->config); + + /* Tab visibility. */ + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (private->notebook), + config->single_window_mode && + config->show_tabs && + ! config->hide_docks && + ((private->active_shell && + private->active_shell->display && + gimp_display_get_image (private->active_shell->display)) || + g_list_length (private->shells) > 1)); + + /* Tab position. */ + switch (config->tabs_position) + { + case GIMP_POSITION_TOP: + position = GTK_POS_TOP; + break; + case GIMP_POSITION_BOTTOM: + position = GTK_POS_BOTTOM; + break; + case GIMP_POSITION_LEFT: + position = GTK_POS_LEFT; + break; + case GIMP_POSITION_RIGHT: + position = GTK_POS_RIGHT; + break; + default: + /* If we have any strange value, just reset to default. */ + position = GTK_POS_TOP; + break; + } + + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (private->notebook), position); +} + + +/* private functions */ + +static void +gimp_image_window_show_tooltip (GimpUIManager *manager, + const gchar *tooltip, + GimpImageWindow *window) +{ + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + GimpStatusbar *statusbar = NULL; + + if (! shell) + return; + + statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_push (statusbar, "menu-tooltip", + NULL, "%s", tooltip); +} + +static void +gimp_image_window_config_notify (GimpImageWindow *window, + GParamSpec *pspec, + GimpGuiConfig *config) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + /* Dock column visibility */ + if (strcmp (pspec->name, "single-window-mode") == 0 || + strcmp (pspec->name, "hide-docks") == 0 || + strcmp (pspec->name, "show-tabs") == 0 || + strcmp (pspec->name, "tabs-position") == 0) + { + if (strcmp (pspec->name, "single-window-mode") == 0 || + strcmp (pspec->name, "hide-docks") == 0) + { + gboolean show_docks = (config->single_window_mode && + ! config->hide_docks); + + gimp_image_window_keep_canvas_pos (window); + gtk_widget_set_visible (private->left_docks, show_docks); + gtk_widget_set_visible (private->right_docks, show_docks); + + /* If docks are being shown, and we are in multi-window-mode, + * and this is the window of the active display, try to set + * the keyboard focus to this window because it might have + * been stolen by a dock. See bug #567333. + */ + if (strcmp (pspec->name, "hide-docks") == 0 && + ! config->single_window_mode && + ! config->hide_docks) + { + GimpDisplayShell *shell; + GimpContext *user_context; + + shell = gimp_image_window_get_active_shell (window); + user_context = gimp_get_user_context (private->gimp); + + if (gimp_context_get_display (user_context) == shell->display) + { + GdkWindow *w = gtk_widget_get_window (GTK_WIDGET (window)); + + if (w) + gdk_window_focus (w, gtk_get_current_event_time ()); + } + } + } + + gimp_image_window_update_tabs (window); + } + + /* Session management */ + if (strcmp (pspec->name, "single-window-mode") == 0) + { + gimp_image_window_session_update (window, + NULL /*new_display*/, + gimp_image_window_config_to_entry_id (config), + gtk_widget_get_screen (GTK_WIDGET (window)), + gimp_widget_get_monitor (GTK_WIDGET (window))); + } +} + +static void +gimp_image_window_hide_tooltip (GimpUIManager *manager, + GimpImageWindow *window) +{ + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + GimpStatusbar *statusbar = NULL; + + if (! shell) + return; + + statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_pop (statusbar, "menu-tooltip"); +} + +static gboolean +gimp_image_window_update_ui_manager_idle (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + gimp_assert (private->active_shell != NULL); + + gimp_ui_manager_update (private->menubar_manager, + private->active_shell->display); + + private->update_ui_manager_idle_id = 0; + + return G_SOURCE_REMOVE; +} + +static void +gimp_image_window_update_ui_manager (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (! private->update_ui_manager_idle_id) + { + private->update_ui_manager_idle_id = + g_idle_add_full (GIMP_PRIORITY_IMAGE_WINDOW_UPDATE_UI_MANAGER_IDLE, + (GSourceFunc) gimp_image_window_update_ui_manager_idle, + window, + NULL); + } +} + +static void +gimp_image_window_shell_size_allocate (GimpDisplayShell *shell, + GtkAllocation *allocation, + PosCorrectionData *data) +{ + GimpImageWindow *window = gimp_display_shell_get_window (shell); + gint new_window_x; + gint new_window_y; + + if (gtk_widget_translate_coordinates (GTK_WIDGET (shell->canvas), + GTK_WIDGET (window), + data->canvas_x, data->canvas_y, + &new_window_x, &new_window_y)) + { + gint off_x = new_window_x - data->window_x; + gint off_y = new_window_y - data->window_y; + + if (off_x || off_y) + gimp_display_shell_scroll (shell, off_x, off_y); + } + + g_signal_handlers_disconnect_by_func (shell, + gimp_image_window_shell_size_allocate, + data); +} + +static gboolean +gimp_image_window_shell_events (GtkWidget *widget, + GdkEvent *event, + GimpImageWindow *window) +{ + GimpDisplayShell *shell = gimp_image_window_get_active_shell (window); + + if (! shell) + return FALSE; + + return gimp_display_shell_events (widget, event, shell); +} + +static void +gimp_image_window_switch_page (GtkNotebook *notebook, + gpointer page, + gint page_num, + GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpDisplayShell *shell; + GimpDisplay *active_display; + + shell = GIMP_DISPLAY_SHELL (gtk_notebook_get_nth_page (notebook, page_num)); + + if (shell == private->active_shell) + return; + + gimp_image_window_disconnect_from_active_shell (window); + + GIMP_LOG (WM, "GimpImageWindow %p, private->active_shell = %p; \n", + window, shell); + private->active_shell = shell; + + gimp_window_set_primary_focus_widget (GIMP_WINDOW (window), + shell->canvas); + + active_display = private->active_shell->display; + + g_signal_connect (active_display, "notify::image", + G_CALLBACK (gimp_image_window_image_notify), + window); + + g_signal_connect (private->active_shell, "scaled", + G_CALLBACK (gimp_image_window_shell_scaled), + window); + g_signal_connect (private->active_shell, "rotated", + G_CALLBACK (gimp_image_window_shell_rotated), + window); + g_signal_connect (private->active_shell, "notify::title", + G_CALLBACK (gimp_image_window_shell_title_notify), + window); + g_signal_connect (private->active_shell, "notify::icon", + G_CALLBACK (gimp_image_window_shell_icon_notify), + window); + + gtk_window_set_title (GTK_WINDOW (window), shell->title); + gtk_window_set_icon (GTK_WINDOW (window), shell->icon); + + gimp_display_shell_appearance_update (private->active_shell); + + if (gtk_widget_get_window (GTK_WIDGET (window))) + { + /* we are fully initialized, use the window's current monitor + */ + gimp_image_window_session_update (window, + active_display, + NULL /*new_entry_id*/, + gtk_widget_get_screen (GTK_WIDGET (window)), + gimp_widget_get_monitor (GTK_WIDGET (window))); + } + else + { + /* we are in construction, use the initial monitor; calling + * gimp_widget_get_monitor() would get us the monitor where the + * pointer is + */ + gimp_image_window_session_update (window, + active_display, + NULL /*new_entry_id*/, + private->initial_screen, + private->initial_monitor); + } + + gimp_context_set_display (gimp_get_user_context (private->gimp), + active_display); + + gimp_image_window_update_ui_manager (window); + + gimp_image_window_update_tab_labels (window); +} + +static void +gimp_image_window_page_removed (GtkNotebook *notebook, + GtkWidget *widget, + gint page_num, + GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + if (GTK_WIDGET (private->active_shell) == widget) + { + GIMP_LOG (WM, "GimpImageWindow %p, private->active_shell = %p; \n", + window, NULL); + gimp_image_window_disconnect_from_active_shell (window); + private->active_shell = NULL; + } +} + +static void +gimp_image_window_page_reordered (GtkNotebook *notebook, + GtkWidget *widget, + gint page_num, + GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpContainer *displays = private->gimp->displays; + gint index = g_list_index (private->shells, widget); + + if (index != page_num) + { + private->shells = g_list_remove (private->shells, widget); + private->shells = g_list_insert (private->shells, widget, page_num); + } + + /* We need to reorder the displays as well in order to update the + * numbered accelerators (alt-1, alt-2, etc.). + */ + gimp_container_reorder (displays, + GIMP_OBJECT (GIMP_DISPLAY_SHELL (widget)->display), + page_num); + + gtk_notebook_reorder_child (notebook, widget, page_num); +} + +static void +gimp_image_window_disconnect_from_active_shell (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpDisplay *active_display = NULL; + + if (! private->active_shell) + return; + + active_display = private->active_shell->display; + + if (active_display) + g_signal_handlers_disconnect_by_func (active_display, + gimp_image_window_image_notify, + window); + + g_signal_handlers_disconnect_by_func (private->active_shell, + gimp_image_window_shell_scaled, + window); + g_signal_handlers_disconnect_by_func (private->active_shell, + gimp_image_window_shell_rotated, + window); + g_signal_handlers_disconnect_by_func (private->active_shell, + gimp_image_window_shell_title_notify, + window); + g_signal_handlers_disconnect_by_func (private->active_shell, + gimp_image_window_shell_icon_notify, + window); + + if (private->menubar_manager) + gimp_image_window_hide_tooltip (private->menubar_manager, window); + + if (private->update_ui_manager_idle_id) + { + g_source_remove (private->update_ui_manager_idle_id); + private->update_ui_manager_idle_id = 0; + } +} + +static void +gimp_image_window_image_notify (GimpDisplay *display, + const GParamSpec *pspec, + GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GtkWidget *tab_label; + GList *children; + GtkWidget *view; + + gimp_image_window_session_update (window, + display, + NULL /*new_entry_id*/, + gtk_widget_get_screen (GTK_WIDGET (window)), + gimp_widget_get_monitor (GTK_WIDGET (window))); + + tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (private->notebook), + GTK_WIDGET (gimp_display_get_shell (display))); + children = gtk_container_get_children (GTK_CONTAINER (tab_label)); + view = GTK_WIDGET (children->data); + g_list_free (children); + + gimp_view_set_viewable (GIMP_VIEW (view), + GIMP_VIEWABLE (gimp_display_get_image (display))); + + gimp_image_window_update_ui_manager (window); +} + +static void +gimp_image_window_session_clear (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GtkWidget *widget = GTK_WIDGET (window); + + if (gimp_dialog_factory_from_widget (widget, NULL)) + gimp_dialog_factory_remove_dialog (private->dialog_factory, + widget); +} + +static void +gimp_image_window_session_apply (GimpImageWindow *window, + const gchar *entry_id, + GdkScreen *screen, + gint monitor) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GimpSessionInfo *session_info = NULL; + gint width = -1; + gint height = -1; + + gtk_window_unfullscreen (GTK_WINDOW (window)); + + /* get the NIW size before adding the display to the dialog + * factory so the window's current size doesn't affect the + * stored session info entry. + */ + session_info = + gimp_dialog_factory_find_session_info (private->dialog_factory, entry_id); + + if (session_info) + { + width = gimp_session_info_get_width (session_info); + height = gimp_session_info_get_height (session_info); + } + else + { + GtkAllocation allocation; + + gtk_widget_get_allocation (GTK_WIDGET (window), &allocation); + + width = allocation.width; + height = allocation.height; + } + + gimp_dialog_factory_add_foreign (private->dialog_factory, + entry_id, + GTK_WIDGET (window), + screen, + monitor); + + gtk_window_unmaximize (GTK_WINDOW (window)); + gtk_window_resize (GTK_WINDOW (window), width, height); +} + +static void +gimp_image_window_session_update (GimpImageWindow *window, + GimpDisplay *new_display, + const gchar *new_entry_id, + GdkScreen *screen, + gint monitor) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + + /* Handle changes to the entry id */ + if (new_entry_id) + { + if (! private->entry_id) + { + /* We're initializing. If we're in single-window mode, this + * will be the only window, so start to session manage + * it. If we're in multi-window mode, we will find out if we + * should session manage ourselves when we get a display + */ + if (strcmp (new_entry_id, GIMP_SINGLE_IMAGE_WINDOW_ENTRY_ID) == 0) + { + gimp_image_window_session_apply (window, new_entry_id, + screen, monitor); + } + } + else if (strcmp (private->entry_id, new_entry_id) != 0) + { + /* The entry id changed, immediately and always stop session + * managing the old entry + */ + gimp_image_window_session_clear (window); + + if (strcmp (new_entry_id, GIMP_EMPTY_IMAGE_WINDOW_ENTRY_ID) == 0) + { + /* If there is only one imageless display, we shall + * become the empty image window + */ + if (private->active_shell && + private->active_shell->display && + ! gimp_display_get_image (private->active_shell->display) && + g_list_length (private->shells) <= 1) + { + gimp_image_window_session_apply (window, new_entry_id, + screen, monitor); + } + } + else if (strcmp (new_entry_id, GIMP_SINGLE_IMAGE_WINDOW_ENTRY_ID) == 0) + { + /* As soon as we become the single image window, we + * shall session manage ourself until single-window mode + * is exited + */ + gimp_image_window_session_apply (window, new_entry_id, + screen, monitor); + } + } + + private->entry_id = new_entry_id; + } + + /* Handle changes to the displays. When in single-window mode, we + * just keep session managing the single image window. We only need + * to care about the multi-window mode case here + */ + if (new_display && + strcmp (private->entry_id, GIMP_EMPTY_IMAGE_WINDOW_ENTRY_ID) == 0) + { + if (gimp_display_get_image (new_display)) + { + /* As soon as we have an image we should not affect the size of the + * empty image window + */ + gimp_image_window_session_clear (window); + } + else if (! gimp_display_get_image (new_display) && + g_list_length (private->shells) <= 1) + { + /* As soon as we have no image (and no other shells that may + * contain images) we should become the empty image window + */ + gimp_image_window_session_apply (window, private->entry_id, + screen, monitor); + } + } +} + +static const gchar * +gimp_image_window_config_to_entry_id (GimpGuiConfig *config) +{ + return (config->single_window_mode ? + GIMP_SINGLE_IMAGE_WINDOW_ENTRY_ID : + GIMP_EMPTY_IMAGE_WINDOW_ENTRY_ID); +} + +static void +gimp_image_window_shell_scaled (GimpDisplayShell *shell, + GimpImageWindow *window) +{ + /* update the /View/Zoom menu */ + gimp_image_window_update_ui_manager (window); +} + +static void +gimp_image_window_shell_rotated (GimpDisplayShell *shell, + GimpImageWindow *window) +{ + /* update the /View/Rotate menu */ + gimp_image_window_update_ui_manager (window); +} + +static void +gimp_image_window_shell_title_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpImageWindow *window) +{ + gtk_window_set_title (GTK_WINDOW (window), shell->title); +} + +static void +gimp_image_window_shell_icon_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpImageWindow *window) +{ + gtk_window_set_icon (GTK_WINDOW (window), shell->icon); +} + +static void +gimp_image_window_shell_close_button_callback (GimpDisplayShell *shell) +{ + if (shell) + gimp_display_shell_close (shell, FALSE); +} + +static GtkWidget * +gimp_image_window_create_tab_label (GimpImageWindow *window, + GimpDisplayShell *shell) +{ + GtkWidget *hbox; + GtkWidget *view; + GimpImage *image; + GtkWidget *button; + GtkWidget *gtk_image; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); + gtk_widget_show (hbox); + + view = gimp_view_new_by_types (gimp_get_user_context (shell->display->gimp), + GIMP_TYPE_VIEW, GIMP_TYPE_IMAGE, + GIMP_VIEW_SIZE_LARGE, 0, FALSE); + gtk_widget_set_size_request (view, GIMP_VIEW_SIZE_LARGE, -1); + gimp_view_renderer_set_color_config (GIMP_VIEW (view)->renderer, + gimp_display_shell_get_color_config (shell)); + gtk_box_pack_start (GTK_BOX (hbox), view, FALSE, FALSE, 0); + gtk_widget_show (view); + + image = gimp_display_get_image (shell->display); + if (image) + gimp_view_set_viewable (GIMP_VIEW (view), GIMP_VIEWABLE (image)); + + button = gtk_button_new (); + gtk_widget_set_can_focus (button, FALSE); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_CLOSE, + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (button), gtk_image); + gtk_widget_show (gtk_image); + + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gimp_image_window_shell_close_button_callback), + shell); + + g_object_set_data (G_OBJECT (hbox), "close-button", button); + + return hbox; +} + +static void +gimp_image_window_update_tab_labels (GimpImageWindow *window) +{ + GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window); + GList *children; + GList *list; + + children = gtk_container_get_children (GTK_CONTAINER (private->notebook)); + + for (list = children; list; list = g_list_next (list)) + { + GtkWidget *shell = list->data; + GtkWidget *tab_widget; + GtkWidget *close_button; + + tab_widget = gtk_notebook_get_tab_label (GTK_NOTEBOOK (private->notebook), + shell); + + close_button = g_object_get_data (G_OBJECT (tab_widget), "close-button"); + + if (gimp_context_get_display (gimp_get_user_context (private->gimp)) == + GIMP_DISPLAY_SHELL (shell)->display) + { + gtk_widget_show (close_button); + } + else + { + gtk_widget_hide (close_button); + } + } + + g_list_free (children); +} diff --git a/app/display/gimpimagewindow.h b/app/display/gimpimagewindow.h new file mode 100644 index 0000000..121dc71 --- /dev/null +++ b/app/display/gimpimagewindow.h @@ -0,0 +1,100 @@ +/* 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 . + */ + +#ifndef __GIMP_IMAGE_WINDOW_H__ +#define __GIMP_IMAGE_WINDOW_H__ + + +#include "widgets/gimpwindow.h" + + +#define GIMP_TYPE_IMAGE_WINDOW (gimp_image_window_get_type ()) +#define GIMP_IMAGE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGE_WINDOW, GimpImageWindow)) +#define GIMP_IMAGE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_IMAGE_WINDOW, GimpImageWindowClass)) +#define GIMP_IS_IMAGE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGE_WINDOW)) +#define GIMP_IS_IMAGE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_IMAGE_WINDOW)) +#define GIMP_IMAGE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_IMAGE_WINDOW, GimpImageWindowClass)) + + +typedef struct _GimpImageWindowClass GimpImageWindowClass; + +struct _GimpImageWindow +{ + GimpWindow parent_instance; +}; + +struct _GimpImageWindowClass +{ + GimpWindowClass parent_class; +}; + + +GType gimp_image_window_get_type (void) G_GNUC_CONST; + +GimpImageWindow * gimp_image_window_new (Gimp *gimp, + GimpImage *image, + GimpDialogFactory *dialog_factory, + GdkScreen *screen, + gint monitor); +void gimp_image_window_destroy (GimpImageWindow *window); + +GimpUIManager * gimp_image_window_get_ui_manager (GimpImageWindow *window); +GimpDockColumns * gimp_image_window_get_left_docks (GimpImageWindow *window); +GimpDockColumns * gimp_image_window_get_right_docks (GimpImageWindow *window); + +void gimp_image_window_add_shell (GimpImageWindow *window, + GimpDisplayShell *shell); +GimpDisplayShell * gimp_image_window_get_shell (GimpImageWindow *window, + gint index); +void gimp_image_window_remove_shell (GimpImageWindow *window, + GimpDisplayShell *shell); + +gint gimp_image_window_get_n_shells (GimpImageWindow *window); + +void gimp_image_window_set_active_shell (GimpImageWindow *window, + GimpDisplayShell *shell); +GimpDisplayShell * gimp_image_window_get_active_shell (GimpImageWindow *window); + +void gimp_image_window_set_fullscreen (GimpImageWindow *window, + gboolean fullscreen); +gboolean gimp_image_window_get_fullscreen (GimpImageWindow *window); + +void gimp_image_window_set_show_menubar (GimpImageWindow *window, + gboolean show); +gboolean gimp_image_window_get_show_menubar (GimpImageWindow *window); + +void gimp_image_window_set_show_statusbar (GimpImageWindow *window, + gboolean show); +gboolean gimp_image_window_get_show_statusbar (GimpImageWindow *window); + +gboolean gimp_image_window_is_iconified (GimpImageWindow *window); +gboolean gimp_image_window_is_maximized (GimpImageWindow *window); + +gboolean gimp_image_window_has_toolbox (GimpImageWindow *window); + +void gimp_image_window_shrink_wrap (GimpImageWindow *window, + gboolean grow_only); + +GtkWidget * gimp_image_window_get_default_dockbook (GimpImageWindow *window); + +void gimp_image_window_keep_canvas_pos (GimpImageWindow *window); +void gimp_image_window_suspend_keep_pos (GimpImageWindow *window); +void gimp_image_window_resume_keep_pos (GimpImageWindow *window); + +void gimp_image_window_update_tabs (GimpImageWindow *window); + +#endif /* __GIMP_IMAGE_WINDOW_H__ */ diff --git a/app/display/gimpmotionbuffer.c b/app/display/gimpmotionbuffer.c new file mode 100644 index 0000000..549f6f0 --- /dev/null +++ b/app/display/gimpmotionbuffer.c @@ -0,0 +1,594 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpcoords.h" +#include "core/gimpcoords-interpolate.h" +#include "core/gimpmarshal.h" + +#include "gimpmotionbuffer.h" + + +/* Velocity unit is screen pixels per millisecond we pass to tools as 1. */ +#define VELOCITY_UNIT 3.0 +#define EVENT_FILL_PRECISION 6.0 +#define DIRECTION_RADIUS (1.0 / MAX (scale_x, scale_y)) +#define SMOOTH_FACTOR 0.3 + + +enum +{ + PROP_0 +}; + +enum +{ + STROKE, + HOVER, + LAST_SIGNAL +}; + + +/* local function prototypes */ + +static void gimp_motion_buffer_dispose (GObject *object); +static void gimp_motion_buffer_finalize (GObject *object); +static void gimp_motion_buffer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_motion_buffer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_motion_buffer_push_event_history (GimpMotionBuffer *buffer, + const GimpCoords *coords); +static void gimp_motion_buffer_pop_event_queue (GimpMotionBuffer *buffer, + GimpCoords *coords); + +static void gimp_motion_buffer_interpolate_stroke (GimpMotionBuffer *buffer, + GimpCoords *coords); +static gboolean gimp_motion_buffer_event_queue_timeout (GimpMotionBuffer *buffer); + + +G_DEFINE_TYPE (GimpMotionBuffer, gimp_motion_buffer, GIMP_TYPE_OBJECT) + +#define parent_class gimp_motion_buffer_parent_class + +static guint motion_buffer_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_motion_buffer_class_init (GimpMotionBufferClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + motion_buffer_signals[STROKE] = + g_signal_new ("stroke", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpMotionBufferClass, stroke), + NULL, NULL, + gimp_marshal_VOID__POINTER_UINT_FLAGS, + G_TYPE_NONE, 3, + G_TYPE_POINTER, + G_TYPE_UINT, + GDK_TYPE_MODIFIER_TYPE); + + motion_buffer_signals[HOVER] = + g_signal_new ("hover", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpMotionBufferClass, hover), + NULL, NULL, + gimp_marshal_VOID__POINTER_FLAGS_BOOLEAN, + G_TYPE_NONE, 3, + G_TYPE_POINTER, + GDK_TYPE_MODIFIER_TYPE, + G_TYPE_BOOLEAN); + + object_class->dispose = gimp_motion_buffer_dispose; + object_class->finalize = gimp_motion_buffer_finalize; + object_class->set_property = gimp_motion_buffer_set_property; + object_class->get_property = gimp_motion_buffer_get_property; +} + +static void +gimp_motion_buffer_init (GimpMotionBuffer *buffer) +{ + buffer->event_history = g_array_new (FALSE, FALSE, sizeof (GimpCoords)); + buffer->event_queue = g_array_new (FALSE, FALSE, sizeof (GimpCoords)); +} + +static void +gimp_motion_buffer_dispose (GObject *object) +{ + GimpMotionBuffer *buffer = GIMP_MOTION_BUFFER (object); + + if (buffer->event_delay_timeout) + { + g_source_remove (buffer->event_delay_timeout); + buffer->event_delay_timeout = 0; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_motion_buffer_finalize (GObject *object) +{ + GimpMotionBuffer *buffer = GIMP_MOTION_BUFFER (object); + + if (buffer->event_history) + { + g_array_free (buffer->event_history, TRUE); + buffer->event_history = NULL; + } + + if (buffer->event_queue) + { + g_array_free (buffer->event_queue, TRUE); + buffer->event_queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_motion_buffer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_motion_buffer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +GimpMotionBuffer * +gimp_motion_buffer_new (void) +{ + return g_object_new (GIMP_TYPE_MOTION_BUFFER, + NULL); +} + +void +gimp_motion_buffer_begin_stroke (GimpMotionBuffer *buffer, + guint32 time, + GimpCoords *last_motion) +{ + g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer)); + g_return_if_fail (last_motion != NULL); + + buffer->last_read_motion_time = time; + + *last_motion = buffer->last_coords; +} + +void +gimp_motion_buffer_end_stroke (GimpMotionBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer)); + + if (buffer->event_delay_timeout) + { + g_source_remove (buffer->event_delay_timeout); + buffer->event_delay_timeout = 0; + } + + gimp_motion_buffer_event_queue_timeout (buffer); +} + +/** + * gimp_motion_buffer_motion_event: + * @buffer: + * @coords: + * @time: + * @event_fill: + * + * This function evaluates the event to decide if the change is big + * enough to need handling and returns FALSE, if change is less than + * set filter level taking a whole lot of load off any draw tools that + * have no use for these events anyway. If the event is seen fit at + * first look, it is evaluated for speed and smoothed. Due to lousy + * time resolution of events pretty strong smoothing is applied to + * timestamps for sensible speed result. This function is also ideal + * for other event adjustment like pressure curve or calculating other + * derived dynamics factors like angular velocity calculation from + * tilt values, to allow for even more dynamic brushes. Calculated + * distance to last event is stored in GimpCoords because its a + * sideproduct of velocity calculation and is currently calculated in + * each tool. If they were to use this distance, more resources on + * recalculating the same value would be saved. + * + * Return value: %TRUE if the motion was significant enough to be + * processed, %FALSE otherwise. + **/ +gboolean +gimp_motion_buffer_motion_event (GimpMotionBuffer *buffer, + GimpCoords *coords, + guint32 time, + gboolean event_fill) +{ + gdouble delta_time = 0.001; + gdouble delta_x = 0.0; + gdouble delta_y = 0.0; + gdouble distance = 1.0; + gdouble scale_x = coords->xscale; + gdouble scale_y = coords->yscale; + + g_return_val_if_fail (GIMP_IS_MOTION_BUFFER (buffer), FALSE); + g_return_val_if_fail (coords != NULL, FALSE); + + /* the last_read_motion_time most be set unconditionally, so set + * it early + */ + buffer->last_read_motion_time = time; + + delta_time = (buffer->last_motion_delta_time * (1 - SMOOTH_FACTOR) + + (time - buffer->last_motion_time) * SMOOTH_FACTOR); + + if (buffer->last_motion_time == 0) + { + /* First pair is invalid to do any velocity calculation, so we + * apply a constant value. + */ + coords->velocity = 1.0; + } + else + { + GimpCoords last_dir_event = buffer->last_coords; + gdouble filter; + gdouble dist; + gdouble delta_dir; + gdouble dir_delta_x = 0.0; + gdouble dir_delta_y = 0.0; + + delta_x = last_dir_event.x - coords->x; + delta_y = last_dir_event.y - coords->y; + + /* Events with distances less than the screen resolution are + * not worth handling. + */ + filter = MIN (1.0 / scale_x, 1.0 / scale_y) / 2.0; + + if (fabs (delta_x) < filter && + fabs (delta_y) < filter) + { + return FALSE; + } + + distance = dist = sqrt (SQR (delta_x) + SQR (delta_y)); + + /* If even smoothed time resolution does not allow to guess for + * speed, use last velocity. + */ + if (delta_time == 0) + { + coords->velocity = buffer->last_coords.velocity; + } + else + { + /* We need to calculate the velocity in screen coordinates + * for human interaction + */ + gdouble screen_distance = (distance * MIN (scale_x, scale_y)); + + /* Calculate raw valocity */ + coords->velocity = ((screen_distance / delta_time) / VELOCITY_UNIT); + + /* Adding velocity dependent smoothing, feels better in tools. */ + coords->velocity = (buffer->last_coords.velocity * + (1 - MIN (SMOOTH_FACTOR, coords->velocity)) + + coords->velocity * + MIN (SMOOTH_FACTOR, coords->velocity)); + + /* Speed needs upper limit */ + coords->velocity = MIN (coords->velocity, 1.0); + } + + if (((fabs (delta_x) > DIRECTION_RADIUS) && + (fabs (delta_y) > DIRECTION_RADIUS)) || + (buffer->event_history->len < 4)) + { + dir_delta_x = delta_x; + dir_delta_y = delta_y; + } + else + { + gint x = CLAMP ((buffer->event_history->len - 1), 3, 15); + + while (((fabs (dir_delta_x) < DIRECTION_RADIUS) || + (fabs (dir_delta_y) < DIRECTION_RADIUS)) && + (x >= 0)) + { + last_dir_event = g_array_index (buffer->event_history, + GimpCoords, x); + + dir_delta_x = last_dir_event.x - coords->x; + dir_delta_y = last_dir_event.y - coords->y; + + x--; + } + } + + if ((fabs (dir_delta_x) < DIRECTION_RADIUS) || + (fabs (dir_delta_y) < DIRECTION_RADIUS)) + { + coords->direction = buffer->last_coords.direction; + } + else + { + coords->direction = gimp_coords_direction (&last_dir_event, coords); + } + + coords->direction = coords->direction - floor (coords->direction); + + delta_dir = coords->direction - buffer->last_coords.direction; + + if (delta_dir < -0.5) + { + coords->direction = (0.5 * coords->direction + + 0.5 * (buffer->last_coords.direction - 1.0)); + } + else if (delta_dir > 0.5) + { + coords->direction = (0.5 * coords->direction + + 0.5 * (buffer->last_coords.direction + 1.0)); + } + else + { + coords->direction = (0.5 * coords->direction + + 0.5 * buffer->last_coords.direction); + } + + coords->direction = coords->direction - floor (coords->direction); + + /* do event fill for devices that do not provide enough events */ + if (distance >= EVENT_FILL_PRECISION && + event_fill && + buffer->event_history->len >= 2) + { + if (buffer->event_delay) + { + gimp_motion_buffer_interpolate_stroke (buffer, coords); + } + else + { + buffer->event_delay = TRUE; + gimp_motion_buffer_push_event_history (buffer, coords); + } + } + else + { + if (buffer->event_delay) + buffer->event_delay = FALSE; + + gimp_motion_buffer_push_event_history (buffer, coords); + } + +#ifdef EVENT_VERBOSE + g_printerr ("DIST: %f, DT:%f, Vel:%f, Press:%f,smooth_dd:%f, POS: (%f, %f)\n", + distance, + delta_time, + buffer->last_coords.velocity, + coords->pressure, + distance - dist, + coords->x, + coords->y); +#endif + } + + g_array_append_val (buffer->event_queue, *coords); + + buffer->last_coords = *coords; + buffer->last_motion_time = time; + buffer->last_motion_delta_time = delta_time; + buffer->last_motion_delta_x = delta_x; + buffer->last_motion_delta_y = delta_y; + buffer->last_motion_distance = distance; + + return TRUE; +} + +guint32 +gimp_motion_buffer_get_last_motion_time (GimpMotionBuffer *buffer) +{ + g_return_val_if_fail (GIMP_IS_MOTION_BUFFER (buffer), 0); + + return buffer->last_read_motion_time; +} + +void +gimp_motion_buffer_request_stroke (GimpMotionBuffer *buffer, + GdkModifierType state, + guint32 time) +{ + GdkModifierType event_state; + gint keep = 0; + + g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer)); + + if (buffer->event_delay) + { + /* If we are in delay we use LAST state, not current */ + event_state = buffer->last_active_state; + + keep = 1; /* Holding one event in buf */ + } + else + { + /* Save the state */ + event_state = state; + } + + if (buffer->event_delay_timeout) + { + g_source_remove (buffer->event_delay_timeout); + buffer->event_delay_timeout = 0; + } + + buffer->last_active_state = state; + + while (buffer->event_queue->len > keep) + { + GimpCoords buf_coords; + + gimp_motion_buffer_pop_event_queue (buffer, &buf_coords); + + g_signal_emit (buffer, motion_buffer_signals[STROKE], 0, + &buf_coords, time, event_state); + } + + if (buffer->event_delay) + { + buffer->event_delay_timeout = + g_timeout_add (50, + (GSourceFunc) gimp_motion_buffer_event_queue_timeout, + buffer); + } +} + +void +gimp_motion_buffer_request_hover (GimpMotionBuffer *buffer, + GdkModifierType state, + gboolean proximity) +{ + g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer)); + + if (buffer->event_queue->len > 0) + { + GimpCoords buf_coords = g_array_index (buffer->event_queue, + GimpCoords, + buffer->event_queue->len - 1); + + g_signal_emit (buffer, motion_buffer_signals[HOVER], 0, + &buf_coords, state, proximity); + + g_array_set_size (buffer->event_queue, 0); + } +} + + +/* private functions */ + +static void +gimp_motion_buffer_push_event_history (GimpMotionBuffer *buffer, + const GimpCoords *coords) +{ + if (buffer->event_history->len == 4) + g_array_remove_index (buffer->event_history, 0); + + g_array_append_val (buffer->event_history, *coords); +} + +static void +gimp_motion_buffer_pop_event_queue (GimpMotionBuffer *buffer, + GimpCoords *coords) +{ + *coords = g_array_index (buffer->event_queue, GimpCoords, 0); + + g_array_remove_index (buffer->event_queue, 0); +} + +static void +gimp_motion_buffer_interpolate_stroke (GimpMotionBuffer *buffer, + GimpCoords *coords) +{ + GimpCoords catmull[4]; + GArray *ret_coords; + gint i = buffer->event_history->len - 1; + + /* Note that there must be exactly one event in buffer or bad things + * can happen. This must never get called under other circumstances. + */ + ret_coords = g_array_new (FALSE, FALSE, sizeof (GimpCoords)); + + catmull[0] = g_array_index (buffer->event_history, GimpCoords, i - 1); + catmull[1] = g_array_index (buffer->event_history, GimpCoords, i); + catmull[2] = g_array_index (buffer->event_queue, GimpCoords, 0); + catmull[3] = *coords; + + gimp_coords_interpolate_catmull (catmull, EVENT_FILL_PRECISION / 2, + ret_coords, NULL); + + /* Push the last actual event in history */ + gimp_motion_buffer_push_event_history (buffer, + &g_array_index (buffer->event_queue, + GimpCoords, 0)); + + g_array_set_size (buffer->event_queue, 0); + + g_array_append_vals (buffer->event_queue, + &g_array_index (ret_coords, GimpCoords, 0), + ret_coords->len); + + g_array_free (ret_coords, TRUE); +} + +static gboolean +gimp_motion_buffer_event_queue_timeout (GimpMotionBuffer *buffer) +{ + buffer->event_delay = FALSE; + buffer->event_delay_timeout = 0; + + if (buffer->event_queue->len > 0) + { + GimpCoords last_coords = g_array_index (buffer->event_queue, + GimpCoords, + buffer->event_queue->len - 1); + + gimp_motion_buffer_push_event_history (buffer, &last_coords); + + gimp_motion_buffer_request_stroke (buffer, + buffer->last_active_state, + buffer->last_read_motion_time); + } + + return FALSE; +} diff --git a/app/display/gimpmotionbuffer.h b/app/display/gimpmotionbuffer.h new file mode 100644 index 0000000..c7aee64 --- /dev/null +++ b/app/display/gimpmotionbuffer.h @@ -0,0 +1,99 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmotionbuffer.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 . + */ + +#ifndef __GIMP_MOTION_BUFFER_H__ +#define __GIMP_MOTION_BUFFER_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_MOTION_BUFFER (gimp_motion_buffer_get_type ()) +#define GIMP_MOTION_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MOTION_BUFFER, GimpMotionBuffer)) +#define GIMP_MOTION_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MOTION_BUFFER, GimpMotionBufferClass)) +#define GIMP_IS_MOTION_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MOTION_BUFFER)) +#define GIMP_IS_MOTION_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MOTION_BUFFER)) +#define GIMP_MOTION_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MOTION_BUFFER, GimpMotionBufferClass)) + + +typedef struct _GimpMotionBufferClass GimpMotionBufferClass; + +struct _GimpMotionBuffer +{ + GimpObject parent_instance; + + guint32 last_read_motion_time; + + guint32 last_motion_time; /* previous time of a forwarded motion event */ + gdouble last_motion_delta_time; + gdouble last_motion_delta_x; + gdouble last_motion_delta_y; + gdouble last_motion_distance; + + GimpCoords last_coords; /* last motion event */ + + GArray *event_history; + GArray *event_queue; + gboolean event_delay; /* TRUE if there's an unsent event in + * the history buffer + */ + + gint event_delay_timeout; + GdkModifierType last_active_state; +}; + +struct _GimpMotionBufferClass +{ + GimpObjectClass parent_class; + + void (* stroke) (GimpMotionBuffer *buffer, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); + void (* hover) (GimpMotionBuffer *buffer, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +}; + + +GType gimp_motion_buffer_get_type (void) G_GNUC_CONST; + +GimpMotionBuffer * gimp_motion_buffer_new (void); + +void gimp_motion_buffer_begin_stroke (GimpMotionBuffer *buffer, + guint32 time, + GimpCoords *last_motion); +void gimp_motion_buffer_end_stroke (GimpMotionBuffer *buffer); + +gboolean gimp_motion_buffer_motion_event (GimpMotionBuffer *buffer, + GimpCoords *coords, + guint32 time, + gboolean event_fill); +guint32 gimp_motion_buffer_get_last_motion_time (GimpMotionBuffer *buffer); + +void gimp_motion_buffer_request_stroke (GimpMotionBuffer *buffer, + GdkModifierType state, + guint32 time); +void gimp_motion_buffer_request_hover (GimpMotionBuffer *buffer, + GdkModifierType state, + gboolean proximity); + + +#endif /* __GIMP_MOTION_BUFFER_H__ */ diff --git a/app/display/gimpmultiwindowstrategy.c b/app/display/gimpmultiwindowstrategy.c new file mode 100644 index 0000000..ca5cb22 --- /dev/null +++ b/app/display/gimpmultiwindowstrategy.c @@ -0,0 +1,89 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmultiwindowstrategy.c + * Copyright (C) 2011 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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpwindowstrategy.h" + +#include "gimpmultiwindowstrategy.h" + + +static void gimp_multi_window_strategy_window_strategy_iface_init (GimpWindowStrategyInterface *iface); +static GtkWidget * gimp_multi_window_strategy_show_dockable_dialog (GimpWindowStrategy *strategy, + Gimp *gimp, + GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + const gchar *identifiers); + + +G_DEFINE_TYPE_WITH_CODE (GimpMultiWindowStrategy, gimp_multi_window_strategy, GIMP_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_WINDOW_STRATEGY, + gimp_multi_window_strategy_window_strategy_iface_init)) + +#define parent_class gimp_multi_window_strategy_parent_class + + +static void +gimp_multi_window_strategy_class_init (GimpMultiWindowStrategyClass *klass) +{ +} + +static void +gimp_multi_window_strategy_init (GimpMultiWindowStrategy *strategy) +{ +} + +static void +gimp_multi_window_strategy_window_strategy_iface_init (GimpWindowStrategyInterface *iface) +{ + iface->show_dockable_dialog = gimp_multi_window_strategy_show_dockable_dialog; +} + +static GtkWidget * +gimp_multi_window_strategy_show_dockable_dialog (GimpWindowStrategy *strategy, + Gimp *gimp, + GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + const gchar *identifiers) +{ + return gimp_dialog_factory_dialog_raise (factory, screen, monitor, + identifiers, -1); +} + +GimpObject * +gimp_multi_window_strategy_get_singleton (void) +{ + static GimpObject *singleton = NULL; + + if (! singleton) + singleton = g_object_new (GIMP_TYPE_MULTI_WINDOW_STRATEGY, NULL); + + return singleton; +} diff --git a/app/display/gimpmultiwindowstrategy.h b/app/display/gimpmultiwindowstrategy.h new file mode 100644 index 0000000..4df8873 --- /dev/null +++ b/app/display/gimpmultiwindowstrategy.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpmultiwindowstrategy.h + * Copyright (C) 2011 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 . + */ + +#ifndef __GIMP_MULTI_WINDOW_STRATEGY_H__ +#define __GIMP_MULTI_WINDOW_STRATEGY_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_MULTI_WINDOW_STRATEGY (gimp_multi_window_strategy_get_type ()) +#define GIMP_MULTI_WINDOW_STRATEGY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MULTI_WINDOW_STRATEGY, GimpMultiWindowStrategy)) +#define GIMP_MULTI_WINDOW_STRATEGY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MULTI_WINDOW_STRATEGY, GimpMultiWindowStrategyClass)) +#define GIMP_IS_MULTI_WINDOW_STRATEGY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MULTI_WINDOW_STRATEGY)) +#define GIMP_IS_MULTI_WINDOW_STRATEGY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MULTI_WINDOW_STRATEGY)) +#define GIMP_MULTI_WINDOW_STRATEGY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MULTI_WINDOW_STRATEGY, GimpMultiWindowStrategyClass)) + + +typedef struct _GimpMultiWindowStrategyClass GimpMultiWindowStrategyClass; + +struct _GimpMultiWindowStrategy +{ + GimpObject parent_instance; +}; + +struct _GimpMultiWindowStrategyClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_multi_window_strategy_get_type (void) G_GNUC_CONST; + +GimpObject * gimp_multi_window_strategy_get_singleton (void); + + +#endif /* __GIMP_MULTI_WINDOW_STRATEGY_H__ */ diff --git a/app/display/gimpnavigationeditor.c b/app/display/gimpnavigationeditor.c new file mode 100644 index 0000000..a518882 --- /dev/null +++ b/app/display/gimpnavigationeditor.c @@ -0,0 +1,890 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpnavigationeditor.c + * Copyright (C) 2001 Michael Natterer + * + * partly based on app/nav_window + * Copyright (C) 1999 Andy Thomas + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimageproxy.h" + +#include "widgets/gimpdocked.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpnavigationview.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpviewrenderer.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-scale.h" +#include "gimpdisplayshell-scroll.h" +#include "gimpdisplayshell-transform.h" +#include "gimpnavigationeditor.h" + +#include "gimp-intl.h" + + +#define UPDATE_DELAY 300 /* From GtkRange in GTK+ 2.22 */ + + +static void gimp_navigation_editor_docked_iface_init (GimpDockedInterface *iface); + +static void gimp_navigation_editor_dispose (GObject *object); + +static void gimp_navigation_editor_set_context (GimpDocked *docked, + GimpContext *context); + +static GtkWidget * gimp_navigation_editor_new_private (GimpMenuFactory *menu_factory, + GimpDisplayShell *shell); + +static void gimp_navigation_editor_set_shell (GimpNavigationEditor *editor, + GimpDisplayShell *shell); +static gboolean gimp_navigation_editor_button_release (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell); +static void gimp_navigation_editor_marker_changed (GimpNavigationView *view, + gdouble center_x, + gdouble center_y, + gdouble width, + gdouble height, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_zoom (GimpNavigationView *view, + GimpZoomType direction, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_scroll (GimpNavigationView *view, + GdkScrollDirection direction, + GimpNavigationEditor *editor); + +static void gimp_navigation_editor_zoom_adj_changed (GtkAdjustment *adj, + GimpNavigationEditor *editor); + +static void gimp_navigation_editor_shell_infinite_canvas_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_shell_scaled (GimpDisplayShell *shell, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_shell_scrolled (GimpDisplayShell *shell, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_shell_rotated (GimpDisplayShell *shell, + GimpNavigationEditor *editor); +static void gimp_navigation_editor_shell_reconnect (GimpDisplayShell *shell, + GimpNavigationEditor *editor); + +static void gimp_navigation_editor_viewable_size_changed (GimpViewable *viewable, + GimpNavigationEditor *editor); + +static void gimp_navigation_editor_options_show_canvas_notify (GimpDisplayOptions *options, + const GParamSpec *pspec, + GimpNavigationEditor *editor); + +static void gimp_navigation_editor_update_marker (GimpNavigationEditor *editor); + + +G_DEFINE_TYPE_WITH_CODE (GimpNavigationEditor, gimp_navigation_editor, + GIMP_TYPE_EDITOR, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED, + gimp_navigation_editor_docked_iface_init)) + +#define parent_class gimp_navigation_editor_parent_class + + +static void +gimp_navigation_editor_class_init (GimpNavigationEditorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_navigation_editor_dispose; +} + +static void +gimp_navigation_editor_docked_iface_init (GimpDockedInterface *iface) +{ + iface->set_context = gimp_navigation_editor_set_context; +} + +static void +gimp_navigation_editor_init (GimpNavigationEditor *editor) +{ + GtkWidget *frame; + + editor->context = NULL; + editor->shell = NULL; + editor->scale_timeout = 0; + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (editor), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + editor->view = gimp_view_new_by_types (NULL, + GIMP_TYPE_NAVIGATION_VIEW, + GIMP_TYPE_IMAGE_PROXY, + GIMP_VIEW_SIZE_MEDIUM, 0, TRUE); + gtk_container_add (GTK_CONTAINER (frame), editor->view); + gtk_widget_show (editor->view); + + g_signal_connect (editor->view, "marker-changed", + G_CALLBACK (gimp_navigation_editor_marker_changed), + editor); + g_signal_connect (editor->view, "zoom", + G_CALLBACK (gimp_navigation_editor_zoom), + editor); + g_signal_connect (editor->view, "scroll", + G_CALLBACK (gimp_navigation_editor_scroll), + editor); + + gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE); +} + +static void +gimp_navigation_editor_dispose (GObject *object) +{ + GimpNavigationEditor *editor = GIMP_NAVIGATION_EDITOR (object); + + if (editor->shell) + gimp_navigation_editor_set_shell (editor, NULL); + + if (editor->scale_timeout) + { + g_source_remove (editor->scale_timeout); + editor->scale_timeout = 0; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_navigation_editor_display_changed (GimpContext *context, + GimpDisplay *display, + GimpNavigationEditor *editor) +{ + GimpDisplayShell *shell = NULL; + + if (display && gimp_display_get_image (display)) + shell = gimp_display_get_shell (display); + + gimp_navigation_editor_set_shell (editor, shell); +} + +static void +gimp_navigation_editor_image_chaged (GimpContext *context, + GimpImage *image, + GimpNavigationEditor *editor) +{ + GimpDisplay *display = gimp_context_get_display (context); + GimpDisplayShell *shell = NULL; + + if (display && image) + shell = gimp_display_get_shell (display); + + gimp_navigation_editor_set_shell (editor, shell); +} + +static void +gimp_navigation_editor_set_context (GimpDocked *docked, + GimpContext *context) +{ + GimpNavigationEditor *editor = GIMP_NAVIGATION_EDITOR (docked); + GimpDisplay *display = NULL; + + if (editor->context) + { + g_signal_handlers_disconnect_by_func (editor->context, + gimp_navigation_editor_display_changed, + editor); + g_signal_handlers_disconnect_by_func (editor->context, + gimp_navigation_editor_image_chaged, + editor); + } + + editor->context = context; + + if (editor->context) + { + g_signal_connect (context, "display-changed", + G_CALLBACK (gimp_navigation_editor_display_changed), + editor); + /* make sure to also call gimp_navigation_editor_set_shell() when the + * last image is closed, even though the display isn't changed, so that + * the editor is properly cleared. + */ + g_signal_connect (context, "image-changed", + G_CALLBACK (gimp_navigation_editor_image_chaged), + editor); + + display = gimp_context_get_display (context); + } + + gimp_view_renderer_set_context (GIMP_VIEW (editor->view)->renderer, + context); + + gimp_navigation_editor_display_changed (editor->context, + display, + editor); +} + + +/* public functions */ + +GtkWidget * +gimp_navigation_editor_new (GimpMenuFactory *menu_factory) +{ + return gimp_navigation_editor_new_private (menu_factory, NULL); +} + +void +gimp_navigation_editor_popup (GimpDisplayShell *shell, + GtkWidget *widget, + gint click_x, + gint click_y) +{ + GtkStyle *style = gtk_widget_get_style (widget); + GimpNavigationEditor *editor; + GimpNavigationView *view; + GdkScreen *screen; + gint x, y; + gint view_marker_center_x, view_marker_center_y; + gint view_marker_width, view_marker_height; + + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (! shell->nav_popup) + { + GtkWidget *frame; + + shell->nav_popup = gtk_window_new (GTK_WINDOW_POPUP); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_container_add (GTK_CONTAINER (shell->nav_popup), frame); + gtk_widget_show (frame); + + editor = + GIMP_NAVIGATION_EDITOR (gimp_navigation_editor_new_private (NULL, + shell)); + gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (editor)); + gtk_widget_show (GTK_WIDGET (editor)); + + g_signal_connect (editor->view, "button-release-event", + G_CALLBACK (gimp_navigation_editor_button_release), + shell); + } + else + { + GtkWidget *bin = gtk_bin_get_child (GTK_BIN (shell->nav_popup)); + + editor = GIMP_NAVIGATION_EDITOR (gtk_bin_get_child (GTK_BIN (bin))); + } + + view = GIMP_NAVIGATION_VIEW (editor->view); + + /* Set poup screen */ + screen = gtk_widget_get_screen (widget); + gtk_window_set_screen (GTK_WINDOW (shell->nav_popup), screen); + + gimp_navigation_view_get_local_marker (view, + &view_marker_center_x, + &view_marker_center_y, + &view_marker_width, + &view_marker_height); + /* Position the popup */ + { + gint x_origin, y_origin; + gint popup_width, popup_height; + gint border_width, border_height; + gint screen_click_x, screen_click_y; + + gdk_window_get_origin (gtk_widget_get_window (widget), + &x_origin, &y_origin); + + screen_click_x = x_origin + click_x; + screen_click_y = y_origin + click_y; + border_width = 2 * style->xthickness; + border_height = 2 * style->ythickness; + popup_width = GIMP_VIEW (view)->renderer->width - 2 * border_width; + popup_height = GIMP_VIEW (view)->renderer->height - 2 * border_height; + + x = screen_click_x - + border_width - + view_marker_center_x; + + y = screen_click_y - + border_height - + view_marker_center_y; + + /* When the image is zoomed out and overscrolled, the above + * calculation risks positioning the popup far far away from the + * click coordinate. We don't want that, so perform some clamping. + */ + x = CLAMP (x, screen_click_x - popup_width, screen_click_x); + y = CLAMP (y, screen_click_y - popup_height, screen_click_y); + + /* If the popup doesn't fit into the screen, we have a problem. + * We move the popup onscreen and risk that the pointer is not + * in the square representing the viewable area anymore. Moving + * the pointer will make the image scroll by a large amount, + * but then it works as usual. Probably better than a popup that + * is completely unusable in the lower right of the screen. + * + * Warping the pointer would be another solution ... + */ + x = CLAMP (x, 0, gdk_screen_get_width (screen) - popup_width); + y = CLAMP (y, 0, gdk_screen_get_height (screen) - popup_height); + + gtk_window_move (GTK_WINDOW (shell->nav_popup), x, y); + } + + gtk_widget_show (shell->nav_popup); + gdk_flush (); + + /* fill in then grab pointer */ + gimp_navigation_view_set_motion_offset (view, 0, 0); + gimp_navigation_view_grab_pointer (view); +} + + +/* private functions */ + +static GtkWidget * +gimp_navigation_editor_new_private (GimpMenuFactory *menu_factory, + GimpDisplayShell *shell) +{ + GimpNavigationEditor *editor; + + g_return_val_if_fail (menu_factory == NULL || + GIMP_IS_MENU_FACTORY (menu_factory), NULL); + g_return_val_if_fail (shell == NULL || GIMP_IS_DISPLAY_SHELL (shell), NULL); + g_return_val_if_fail (menu_factory || shell, NULL); + + if (shell) + { + Gimp *gimp = shell->display->gimp; + GimpDisplayConfig *config = shell->display->config; + GimpView *view; + + editor = g_object_new (GIMP_TYPE_NAVIGATION_EDITOR, NULL); + + view = GIMP_VIEW (editor->view); + + gimp_view_renderer_set_size (view->renderer, + config->nav_preview_size * 3, + view->renderer->border_width); + gimp_view_renderer_set_context (view->renderer, + gimp_get_user_context (gimp)); + gimp_view_renderer_set_color_config (view->renderer, + gimp_display_shell_get_color_config (shell)); + + gimp_navigation_editor_set_shell (editor, shell); + + } + else + { + GtkWidget *hscale; + GtkWidget *hbox; + + editor = g_object_new (GIMP_TYPE_NAVIGATION_EDITOR, + "menu-factory", menu_factory, + "menu-identifier", "", + NULL); + + gtk_widget_set_size_request (editor->view, + GIMP_VIEW_SIZE_HUGE, + GIMP_VIEW_SIZE_HUGE); + gimp_view_set_expand (GIMP_VIEW (editor->view), TRUE); + + /* the editor buttons */ + + editor->zoom_out_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-zoom-out", NULL); + + editor->zoom_in_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-zoom-in", NULL); + + editor->zoom_100_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-zoom-1-1", NULL); + + editor->zoom_fit_in_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-zoom-fit-in", NULL); + + editor->zoom_fill_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-zoom-fill", NULL); + + editor->shrink_wrap_button = + gimp_editor_add_action_button (GIMP_EDITOR (editor), "view", + "view-shrink-wrap", NULL); + + /* the zoom scale */ + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_end (GTK_BOX (editor), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + editor->zoom_adjustment = + GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -8.0, 8.0, 0.5, 1.0, 0.0)); + + g_signal_connect (editor->zoom_adjustment, "value-changed", + G_CALLBACK (gimp_navigation_editor_zoom_adj_changed), + editor); + + hscale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, + editor->zoom_adjustment); + gtk_scale_set_draw_value (GTK_SCALE (hscale), FALSE); + gtk_box_pack_start (GTK_BOX (hbox), hscale, TRUE, TRUE, 0); + gtk_widget_show (hscale); + + /* the zoom label */ + + editor->zoom_label = gtk_label_new ("100%"); + gtk_label_set_width_chars (GTK_LABEL (editor->zoom_label), 7); + gtk_box_pack_start (GTK_BOX (hbox), editor->zoom_label, FALSE, FALSE, 0); + gtk_widget_show (editor->zoom_label); + } + + gimp_view_renderer_set_background (GIMP_VIEW (editor->view)->renderer, + GIMP_ICON_TEXTURE); + + return GTK_WIDGET (editor); +} + +static void +gimp_navigation_editor_set_shell (GimpNavigationEditor *editor, + GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_NAVIGATION_EDITOR (editor)); + g_return_if_fail (! shell || GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell == editor->shell) + return; + + if (editor->shell) + { + g_signal_handlers_disconnect_by_func (editor->shell, + gimp_navigation_editor_shell_infinite_canvas_notify, + editor); + g_signal_handlers_disconnect_by_func (editor->shell, + gimp_navigation_editor_shell_scaled, + editor); + g_signal_handlers_disconnect_by_func (editor->shell, + gimp_navigation_editor_shell_scrolled, + editor); + g_signal_handlers_disconnect_by_func (editor->shell, + gimp_navigation_editor_shell_rotated, + editor); + g_signal_handlers_disconnect_by_func (editor->shell, + gimp_navigation_editor_shell_reconnect, + editor); + + g_signal_handlers_disconnect_by_func (editor->shell->options, + gimp_navigation_editor_options_show_canvas_notify, + editor); + g_signal_handlers_disconnect_by_func (editor->shell->fullscreen_options, + gimp_navigation_editor_options_show_canvas_notify, + editor); + } + else if (shell) + { + gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE); + } + + editor->shell = shell; + + if (editor->shell) + { + GimpImage *image = gimp_display_get_image (shell->display); + + g_clear_object (&editor->image_proxy); + + if (image) + { + editor->image_proxy = gimp_image_proxy_new (image); + + g_signal_connect ( + editor->image_proxy, "size-changed", + G_CALLBACK (gimp_navigation_editor_viewable_size_changed), + editor); + } + + gimp_view_set_viewable (GIMP_VIEW (editor->view), + GIMP_VIEWABLE (editor->image_proxy)); + + g_signal_connect (editor->shell, "notify::infinite-canvas", + G_CALLBACK (gimp_navigation_editor_shell_infinite_canvas_notify), + editor); + g_signal_connect (editor->shell, "scaled", + G_CALLBACK (gimp_navigation_editor_shell_scaled), + editor); + g_signal_connect (editor->shell, "scrolled", + G_CALLBACK (gimp_navigation_editor_shell_scrolled), + editor); + g_signal_connect (editor->shell, "rotated", + G_CALLBACK (gimp_navigation_editor_shell_rotated), + editor); + g_signal_connect (editor->shell, "reconnect", + G_CALLBACK (gimp_navigation_editor_shell_reconnect), + editor); + + g_signal_connect (editor->shell->options, "notify::show-canvas-boundary", + G_CALLBACK (gimp_navigation_editor_options_show_canvas_notify), + editor); + g_signal_connect (editor->shell->fullscreen_options, "notify::show-canvas-boundary", + G_CALLBACK (gimp_navigation_editor_options_show_canvas_notify), + editor); + + gimp_navigation_editor_shell_scaled (editor->shell, editor); + } + else + { + gimp_view_set_viewable (GIMP_VIEW (editor->view), NULL); + gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE); + + g_clear_object (&editor->image_proxy); + } + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static gboolean +gimp_navigation_editor_button_release (GtkWidget *widget, + GdkEventButton *bevent, + GimpDisplayShell *shell) +{ + if (bevent->button == 1) + { + gtk_widget_hide (shell->nav_popup); + } + + return FALSE; +} + +static void +gimp_navigation_editor_marker_changed (GimpNavigationView *view, + gdouble center_x, + gdouble center_y, + gdouble width, + gdouble height, + GimpNavigationEditor *editor) +{ + GimpViewRenderer *renderer = GIMP_VIEW (editor->view)->renderer; + + if (editor->shell) + { + if (gimp_display_get_image (editor->shell->display)) + { + GeglRectangle bounding_box; + + bounding_box = gimp_image_proxy_get_bounding_box ( + GIMP_IMAGE_PROXY (renderer->viewable)); + + center_x += bounding_box.x; + center_y += bounding_box.y; + + gimp_display_shell_scroll_center_image_xy (editor->shell, + center_x, center_y); + } + } +} + +static void +gimp_navigation_editor_zoom (GimpNavigationView *view, + GimpZoomType direction, + GimpNavigationEditor *editor) +{ + g_return_if_fail (direction != GIMP_ZOOM_TO); + + if (editor->shell) + { + if (gimp_display_get_image (editor->shell->display)) + gimp_display_shell_scale (editor->shell, + direction, + 0.0, + GIMP_ZOOM_FOCUS_BEST_GUESS); + } +} + +static void +gimp_navigation_editor_scroll (GimpNavigationView *view, + GdkScrollDirection direction, + GimpNavigationEditor *editor) +{ + if (editor->shell) + { + GtkAdjustment *adj = NULL; + gdouble value; + + switch (direction) + { + case GDK_SCROLL_LEFT: + case GDK_SCROLL_RIGHT: + adj = editor->shell->hsbdata; + break; + + case GDK_SCROLL_UP: + case GDK_SCROLL_DOWN: + adj = editor->shell->vsbdata; + break; + } + + gimp_assert (adj != NULL); + + value = gtk_adjustment_get_value (adj); + + switch (direction) + { + case GDK_SCROLL_LEFT: + case GDK_SCROLL_UP: + value -= gtk_adjustment_get_page_increment (adj) / 2; + break; + + case GDK_SCROLL_RIGHT: + case GDK_SCROLL_DOWN: + value += gtk_adjustment_get_page_increment (adj) / 2; + break; + } + + value = CLAMP (value, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + + gtk_adjustment_set_value (adj, value); + } +} + +static gboolean +gimp_navigation_editor_zoom_adj_changed_timeout (gpointer data) +{ + GimpNavigationEditor *editor = GIMP_NAVIGATION_EDITOR (data); + GtkAdjustment *adj = editor->zoom_adjustment; + + if (gimp_display_get_image (editor->shell->display)) + gimp_display_shell_scale (editor->shell, + GIMP_ZOOM_TO, + pow (2.0, gtk_adjustment_get_value (adj)), + GIMP_ZOOM_FOCUS_BEST_GUESS); + + editor->scale_timeout = 0; + + return FALSE; +} + +static void +gimp_navigation_editor_zoom_adj_changed (GtkAdjustment *adj, + GimpNavigationEditor *editor) +{ + if (editor->scale_timeout) + g_source_remove (editor->scale_timeout); + + editor->scale_timeout = + g_timeout_add (UPDATE_DELAY, + gimp_navigation_editor_zoom_adj_changed_timeout, + editor); +} + +static void +gimp_navigation_editor_shell_infinite_canvas_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpNavigationEditor *editor) +{ + gimp_navigation_editor_update_marker (editor); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_shell_scaled (GimpDisplayShell *shell, + GimpNavigationEditor *editor) +{ + if (editor->zoom_label) + { + gchar *str; + + g_object_get (shell->zoom, + "percentage", &str, + NULL); + gtk_label_set_text (GTK_LABEL (editor->zoom_label), str); + g_free (str); + } + + if (editor->zoom_adjustment) + { + gdouble val; + + val = log (gimp_zoom_model_get_factor (shell->zoom)) / G_LN2; + + g_signal_handlers_block_by_func (editor->zoom_adjustment, + gimp_navigation_editor_zoom_adj_changed, + editor); + + gtk_adjustment_set_value (editor->zoom_adjustment, val); + + g_signal_handlers_unblock_by_func (editor->zoom_adjustment, + gimp_navigation_editor_zoom_adj_changed, + editor); + } + + gimp_navigation_editor_update_marker (editor); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_shell_scrolled (GimpDisplayShell *shell, + GimpNavigationEditor *editor) +{ + gimp_navigation_editor_update_marker (editor); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_shell_rotated (GimpDisplayShell *shell, + GimpNavigationEditor *editor) +{ + gimp_navigation_editor_update_marker (editor); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_viewable_size_changed (GimpViewable *viewable, + GimpNavigationEditor *editor) +{ + gimp_navigation_editor_update_marker (editor); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_options_show_canvas_notify (GimpDisplayOptions *options, + const GParamSpec *pspec, + GimpNavigationEditor *editor) +{ + gimp_navigation_editor_update_marker (editor); +} + +static void +gimp_navigation_editor_shell_reconnect (GimpDisplayShell *shell, + GimpNavigationEditor *editor) +{ + GimpImage *image = gimp_display_get_image (shell->display); + + g_clear_object (&editor->image_proxy); + + if (image) + { + editor->image_proxy = gimp_image_proxy_new (image); + + g_signal_connect ( + editor->image_proxy, "size-changed", + G_CALLBACK (gimp_navigation_editor_viewable_size_changed), + editor); + } + + gimp_view_set_viewable (GIMP_VIEW (editor->view), + GIMP_VIEWABLE (editor->image_proxy)); + + if (gimp_editor_get_ui_manager (GIMP_EDITOR (editor))) + gimp_ui_manager_update (gimp_editor_get_ui_manager (GIMP_EDITOR (editor)), + gimp_editor_get_popup_data (GIMP_EDITOR (editor))); +} + +static void +gimp_navigation_editor_update_marker (GimpNavigationEditor *editor) +{ + GimpViewRenderer *renderer = GIMP_VIEW (editor->view)->renderer; + GimpDisplayShell *shell = editor->shell; + + if (renderer->dot_for_dot != shell->dot_for_dot) + gimp_view_renderer_set_dot_for_dot (renderer, shell->dot_for_dot); + + if (renderer->viewable) + { + GimpNavigationView *view = GIMP_NAVIGATION_VIEW (editor->view); + GimpImage *image; + GeglRectangle bounding_box; + gdouble x, y; + gdouble w, h; + + image = gimp_image_proxy_get_image ( + GIMP_IMAGE_PROXY (renderer->viewable)); + + gimp_image_proxy_set_show_all ( + GIMP_IMAGE_PROXY (renderer->viewable), + gimp_display_shell_get_infinite_canvas (shell)); + + bounding_box = gimp_image_proxy_get_bounding_box ( + GIMP_IMAGE_PROXY (renderer->viewable)); + + gimp_display_shell_scroll_get_viewport (shell, &x, &y, &w, &h); + gimp_display_shell_untransform_xy_f (shell, + shell->disp_width / 2, + shell->disp_height / 2, + &x, &y); + + x -= bounding_box.x; + y -= bounding_box.y; + + gimp_navigation_view_set_marker (view, + x, y, w, h, + shell->flip_horizontally, + shell->flip_vertically, + shell->rotate_angle); + + gimp_navigation_view_set_canvas ( + view, + gimp_display_shell_get_infinite_canvas (shell) && + gimp_display_shell_get_show_canvas (shell), + -bounding_box.x, -bounding_box.y, + gimp_image_get_width (image), gimp_image_get_height (image)); + } +} diff --git a/app/display/gimpnavigationeditor.h b/app/display/gimpnavigationeditor.h new file mode 100644 index 0000000..8e876c2 --- /dev/null +++ b/app/display/gimpnavigationeditor.h @@ -0,0 +1,79 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpnavigationeditor.h + * Copyright (C) 2002 Michael Natterer + * + * partly based on app/nav_window + * Copyright (C) 1999 Andy Thomas + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_NAVIGATION_EDITOR_H__ +#define __GIMP_NAVIGATION_EDITOR_H__ + + +#include "widgets/gimpeditor.h" + + +#define GIMP_TYPE_NAVIGATION_EDITOR (gimp_navigation_editor_get_type ()) +#define GIMP_NAVIGATION_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_NAVIGATION_EDITOR, GimpNavigationEditor)) +#define GIMP_NAVIGATION_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_NAVIGATION_EDITOR, GimpNavigationEditorClass)) +#define GIMP_IS_NAVIGATION_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_NAVIGATION_EDITOR)) +#define GIMP_IS_NAVIGATION_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_NAVIGATION_EDITOR)) +#define GIMP_NAVIGATION_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_NAVIGATION_EDITOR, GimpNavigationEditorClass)) + + +typedef struct _GimpNavigationEditorClass GimpNavigationEditorClass; + +struct _GimpNavigationEditor +{ + GimpEditor parent_instance; + + GimpContext *context; + GimpDisplayShell *shell; + + GimpImageProxy *image_proxy; + + GtkWidget *view; + GtkWidget *zoom_label; + GtkAdjustment *zoom_adjustment; + + GtkWidget *zoom_out_button; + GtkWidget *zoom_in_button; + GtkWidget *zoom_100_button; + GtkWidget *zoom_fit_in_button; + GtkWidget *zoom_fill_button; + GtkWidget *shrink_wrap_button; + + guint scale_timeout; +}; + +struct _GimpNavigationEditorClass +{ + GimpEditorClass parent_class; +}; + + +GType gimp_navigation_editor_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_navigation_editor_new (GimpMenuFactory *menu_factory); +void gimp_navigation_editor_popup (GimpDisplayShell *shell, + GtkWidget *widget, + gint click_x, + gint click_y); + + +#endif /* __GIMP_NAVIGATION_EDITOR_H__ */ diff --git a/app/display/gimpscalecombobox.c b/app/display/gimpscalecombobox.c new file mode 100644 index 0000000..3a5bba0 --- /dev/null +++ b/app/display/gimpscalecombobox.c @@ -0,0 +1,562 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpscalecombobox.c + * Copyright (C) 2004, 2008 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 . + */ + +#include "config.h" + +#include "stdlib.h" + +#include +#include +#include "gdk/gdkkeysyms.h" + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpmarshal.h" + +#include "gimpscalecombobox.h" + + +#define MAX_ITEMS 10 + +enum +{ + COLUMN_SCALE, + COLUMN_LABEL, + COLUMN_PERSISTENT, + N_COLUMNS +}; + +enum +{ + ENTRY_ACTIVATED, + LAST_SIGNAL +}; + + +static void gimp_scale_combo_box_constructed (GObject *object); +static void gimp_scale_combo_box_finalize (GObject *object); + +static void gimp_scale_combo_box_style_set (GtkWidget *widget, + GtkStyle *prev_style); + +static void gimp_scale_combo_box_changed (GimpScaleComboBox *combo_box); +static void gimp_scale_combo_box_entry_activate (GtkWidget *entry, + GimpScaleComboBox *combo_box); +static gboolean gimp_scale_combo_box_entry_key_press (GtkWidget *entry, + GdkEventKey *event, + GimpScaleComboBox *combo_box); + +static void gimp_scale_combo_box_scale_iter_set (GtkListStore *store, + GtkTreeIter *iter, + gdouble scale, + gboolean persistent); + + +G_DEFINE_TYPE (GimpScaleComboBox, gimp_scale_combo_box, + GTK_TYPE_COMBO_BOX) + +#define parent_class gimp_scale_combo_box_parent_class + +static guint scale_combo_box_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_scale_combo_box_class_init (GimpScaleComboBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + scale_combo_box_signals[ENTRY_ACTIVATED] = + g_signal_new ("entry-activated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpScaleComboBoxClass, entry_activated), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->constructed = gimp_scale_combo_box_constructed; + object_class->finalize = gimp_scale_combo_box_finalize; + + widget_class->style_set = gimp_scale_combo_box_style_set; + + klass->entry_activated = NULL; + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_double ("label-scale", + NULL, NULL, + 0.0, + G_MAXDOUBLE, + 1.0, + GIMP_PARAM_READABLE)); +} + +static void +gimp_scale_combo_box_init (GimpScaleComboBox *combo_box) +{ + combo_box->scale = 1.0; + combo_box->last_path = NULL; +} + +static void +gimp_scale_combo_box_constructed (GObject *object) +{ + GimpScaleComboBox *combo_box = GIMP_SCALE_COMBO_BOX (object); + GtkWidget *entry; + GtkListStore *store; + GtkCellLayout *layout; + GtkCellRenderer *cell; + GtkTreeIter iter; + GtkBorder border = { 0, 0, 0, 0 }; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + store = gtk_list_store_new (N_COLUMNS, + G_TYPE_DOUBLE, /* SCALE */ + G_TYPE_STRING, /* LABEL */ + G_TYPE_BOOLEAN); /* PERSISTENT */ + + gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store)); + g_object_unref (store); + + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo_box), + COLUMN_LABEL); + + entry = gtk_bin_get_child (GTK_BIN (combo_box)); + + g_object_set (entry, + "xalign", 1.0, + "width-chars", 5, + "truncate-multiline", TRUE, + "inner-border", &border, + NULL); + + layout = GTK_CELL_LAYOUT (combo_box); + + cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, + "xalign", 1.0, + NULL); + + gtk_cell_layout_clear (layout); + gtk_cell_layout_pack_start (layout, cell, TRUE); + gtk_cell_layout_set_attributes (layout, cell, + "text", COLUMN_LABEL, + NULL); + + for (i = 8; i > 0; i /= 2) + { + gtk_list_store_append (store, &iter); + gimp_scale_combo_box_scale_iter_set (store, &iter, i, TRUE); + } + + for (i = 2; i <= 8; i *= 2) + { + gtk_list_store_append (store, &iter); + gimp_scale_combo_box_scale_iter_set (store, &iter, 1.0 / i, TRUE); + } + + g_signal_connect (combo_box, "changed", + G_CALLBACK (gimp_scale_combo_box_changed), + NULL); + + g_signal_connect (entry, "activate", + G_CALLBACK (gimp_scale_combo_box_entry_activate), + combo_box); + g_signal_connect (entry, "key-press-event", + G_CALLBACK (gimp_scale_combo_box_entry_key_press), + combo_box); +} + +static void +gimp_scale_combo_box_finalize (GObject *object) +{ + GimpScaleComboBox *combo_box = GIMP_SCALE_COMBO_BOX (object); + + if (combo_box->last_path) + { + gtk_tree_path_free (combo_box->last_path); + combo_box->last_path = NULL; + } + + if (combo_box->mru) + { + g_list_free_full (combo_box->mru, + (GDestroyNotify) gtk_tree_row_reference_free); + combo_box->mru = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_scale_combo_box_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GtkWidget *entry; + GtkRcStyle *rc_style; + PangoContext *context; + PangoFontDescription *font_desc; + gint font_size; + gdouble label_scale; + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + gtk_widget_style_get (widget, "label-scale", &label_scale, NULL); + + entry = gtk_bin_get_child (GTK_BIN (widget)); + + rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (entry)); + + if (rc_style->font_desc) + pango_font_description_free (rc_style->font_desc); + + context = gtk_widget_get_pango_context (widget); + font_desc = pango_context_get_font_description (context); + rc_style->font_desc = pango_font_description_copy (font_desc); + + font_size = pango_font_description_get_size (rc_style->font_desc); + pango_font_description_set_size (rc_style->font_desc, label_scale * font_size); + + gtk_widget_modify_style (GTK_WIDGET (entry), rc_style); +} + +static void +gimp_scale_combo_box_changed (GimpScaleComboBox *combo_box) +{ + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) + { + GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + gdouble scale; + + gtk_tree_model_get (model, &iter, + COLUMN_SCALE, &scale, + -1); + if (scale > 0.0) + { + combo_box->scale = scale; + + if (combo_box->last_path) + gtk_tree_path_free (combo_box->last_path); + + combo_box->last_path = gtk_tree_model_get_path (model, &iter); + } + } +} + +static gboolean +gimp_scale_combo_box_parse_text (const gchar *text, + gdouble *scale) +{ + gchar *end; + gdouble left_number; + gdouble right_number; + + /* try to parse a number */ + left_number = strtod (text, &end); + + if (end == text) + return FALSE; + else + text = end; + + /* skip over whitespace */ + while (g_unichar_isspace (g_utf8_get_char (text))) + text = g_utf8_next_char (text); + + if (*text == '\0' || *text == '%') + { + *scale = left_number / 100.0; + return TRUE; + } + + /* check for a valid separator */ + if (*text != '/' && *text != ':') + { + *scale = left_number; + return TRUE; + } + + text = g_utf8_next_char (text); + + /* skip over whitespace */ + while (g_unichar_isspace (g_utf8_get_char (text))) + text = g_utf8_next_char (text); + + /* try to parse another number */ + right_number = strtod (text, &end); + + if (end == text) + return FALSE; + + if (right_number == 0.0) + return FALSE; + + *scale = left_number / right_number; + return TRUE; +} + +static void +gimp_scale_combo_box_entry_activate (GtkWidget *entry, + GimpScaleComboBox *combo_box) +{ + const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry)); + gdouble scale; + + if (gimp_scale_combo_box_parse_text (text, &scale) && + scale >= 1.0 / 256.0 && + scale <= 256.0) + { + gimp_scale_combo_box_set_scale (combo_box, scale); + } + else + { + gtk_widget_error_bell (entry); + + gimp_scale_combo_box_set_scale (combo_box, combo_box->scale); + } + + g_signal_emit (combo_box, scale_combo_box_signals[ENTRY_ACTIVATED], 0); +} + +static gboolean +gimp_scale_combo_box_entry_key_press (GtkWidget *entry, + GdkEventKey *event, + GimpScaleComboBox *combo_box) +{ + if (event->keyval == GDK_KEY_Escape) + { + gimp_scale_combo_box_set_scale (combo_box, combo_box->scale); + + g_signal_emit (combo_box, scale_combo_box_signals[ENTRY_ACTIVATED], 0); + + return TRUE; + } + + if (event->keyval == GDK_KEY_Tab || + event->keyval == GDK_KEY_KP_Tab || + event->keyval == GDK_KEY_ISO_Left_Tab) + { + gimp_scale_combo_box_entry_activate (entry, combo_box); + + return TRUE; + } + + return FALSE; +} + +static void +gimp_scale_combo_box_scale_iter_set (GtkListStore *store, + GtkTreeIter *iter, + gdouble scale, + gboolean persistent) +{ + gchar label[32]; + +#ifdef G_OS_WIN32 + + /* use a normal space until pango's windows backend uses harfbuzz, + * see bug #735505 + */ +#define PERCENT_SPACE " " + +#else + + /* use U+2009 THIN SPACE to separate the percent sign from the number */ +#define PERCENT_SPACE "\342\200\211" + +#endif + + if (scale > 1.0) + g_snprintf (label, sizeof (label), + "%d" PERCENT_SPACE "%%", (gint) ROUND (100.0 * scale)); + else + g_snprintf (label, sizeof (label), + "%.3g" PERCENT_SPACE "%%", 100.0 * scale); + + gtk_list_store_set (store, iter, + COLUMN_SCALE, scale, + COLUMN_LABEL, label, + COLUMN_PERSISTENT, persistent, + -1); +} + +static void +gimp_scale_combo_box_mru_add (GimpScaleComboBox *combo_box, + GtkTreeIter *iter) +{ + GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + GtkTreePath *path = gtk_tree_model_get_path (model, iter); + GList *list; + gboolean found; + + for (list = combo_box->mru, found = FALSE; list && !found; list = list->next) + { + GtkTreePath *this = gtk_tree_row_reference_get_path (list->data); + + if (gtk_tree_path_compare (this, path) == 0) + { + if (list->prev) + { + combo_box->mru = g_list_remove_link (combo_box->mru, list); + combo_box->mru = g_list_concat (list, combo_box->mru); + } + + found = TRUE; + } + + gtk_tree_path_free (this); + } + + if (! found) + combo_box->mru = g_list_prepend (combo_box->mru, + gtk_tree_row_reference_new (model, path)); + + gtk_tree_path_free (path); +} + +static void +gimp_scale_combo_box_mru_remove_last (GimpScaleComboBox *combo_box) +{ + GtkTreeModel *model; + GtkTreePath *path; + GList *last; + GtkTreeIter iter; + + if (! combo_box->mru) + return; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + + last = g_list_last (combo_box->mru); + path = gtk_tree_row_reference_get_path (last->data); + + if (gtk_tree_model_get_iter (model, &iter, path)) + { + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + gtk_tree_row_reference_free (last->data); + combo_box->mru = g_list_delete_link (combo_box->mru, last); + } + + gtk_tree_path_free (path); +} + + +/** + * gimp_scale_combo_box_new: + * + * Return value: a new #GimpScaleComboBox. + **/ +GtkWidget * +gimp_scale_combo_box_new (void) +{ + return g_object_new (GIMP_TYPE_SCALE_COMBO_BOX, + "has-entry", TRUE, + NULL); +} + +void +gimp_scale_combo_box_set_scale (GimpScaleComboBox *combo_box, + gdouble scale) +{ + GtkTreeModel *model; + GtkListStore *store; + GtkWidget *entry; + GtkTreeIter iter; + gboolean iter_valid; + gboolean persistent; + gint n_digits; + + g_return_if_fail (GIMP_IS_SCALE_COMBO_BOX (combo_box)); + g_return_if_fail (scale > 0.0); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); + store = GTK_LIST_STORE (model); + + for (iter_valid = gtk_tree_model_get_iter_first (model, &iter); + iter_valid; + iter_valid = gtk_tree_model_iter_next (model, &iter)) + { + gdouble this; + + gtk_tree_model_get (model, &iter, + COLUMN_SCALE, &this, + -1); + + if (fabs (this - scale) < 0.0001) + break; + } + + if (! iter_valid) + { + GtkTreeIter sibling; + + for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling); + iter_valid; + iter_valid = gtk_tree_model_iter_next (model, &sibling)) + { + gdouble this; + + gtk_tree_model_get (model, &sibling, + COLUMN_SCALE, &this, + -1); + + if (this < scale) + break; + } + + gtk_list_store_insert_before (store, &iter, iter_valid ? &sibling : NULL); + gimp_scale_combo_box_scale_iter_set (store, &iter, scale, FALSE); + } + + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter); + + gtk_tree_model_get (model, &iter, + COLUMN_PERSISTENT, &persistent, + -1); + if (! persistent) + { + gimp_scale_combo_box_mru_add (combo_box, &iter); + + if (gtk_tree_model_iter_n_children (model, NULL) > MAX_ITEMS) + gimp_scale_combo_box_mru_remove_last (combo_box); + } + + /* Update entry size appropriately. */ + entry = gtk_bin_get_child (GTK_BIN (combo_box)); + n_digits = (gint) floor (log10 (scale) + 1); + + g_object_set (entry, + "width-chars", MAX (5, n_digits + 4), + NULL); +} + +gdouble +gimp_scale_combo_box_get_scale (GimpScaleComboBox *combo_box) +{ + g_return_val_if_fail (GIMP_IS_SCALE_COMBO_BOX (combo_box), 1.0); + + return combo_box->scale; +} diff --git a/app/display/gimpscalecombobox.h b/app/display/gimpscalecombobox.h new file mode 100644 index 0000000..1c35e2f --- /dev/null +++ b/app/display/gimpscalecombobox.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpscalecombobox.h + * Copyright (C) 2004, 2008 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 . + */ + +#ifndef __GIMP_SCALE_COMBO_BOX_H__ +#define __GIMP_SCALE_COMBO_BOX_H__ + + +#define GIMP_TYPE_SCALE_COMBO_BOX (gimp_scale_combo_box_get_type ()) +#define GIMP_SCALE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SCALE_COMBO_BOX, GimpScaleComboBox)) +#define GIMP_SCALE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SCALE_COMBO_BOX, GimpScaleComboBoxClass)) +#define GIMP_IS_SCALE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SCALE_COMBO_BOX)) +#define GIMP_IS_SCALE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SCALE_COMBO_BOX)) +#define GIMP_SCALE_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SCALE_COMBO_BOX, GimpScaleComboBoxClass)) + + +typedef struct _GimpScaleComboBoxClass GimpScaleComboBoxClass; + +struct _GimpScaleComboBoxClass +{ + GtkComboBoxClass parent_instance; + + void (* entry_activated) (GimpScaleComboBox *combo_box); +}; + +struct _GimpScaleComboBox +{ + GtkComboBox parent_instance; + + gdouble scale; + GtkTreePath *last_path; + GList *mru; +}; + + +GType gimp_scale_combo_box_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_scale_combo_box_new (void); +void gimp_scale_combo_box_set_scale (GimpScaleComboBox *combo_box, + gdouble scale); +gdouble gimp_scale_combo_box_get_scale (GimpScaleComboBox *combo_box); + + +#endif /* __GIMP_SCALE_COMBO_BOX_H__ */ diff --git a/app/display/gimpsinglewindowstrategy.c b/app/display/gimpsinglewindowstrategy.c new file mode 100644 index 0000000..e840dfa --- /dev/null +++ b/app/display/gimpsinglewindowstrategy.c @@ -0,0 +1,157 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsinglewindowstrategy.c + * Copyright (C) 2011 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "display-types.h" + +#include "core/gimp.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockbook.h" +#include "widgets/gimpdockcolumns.h" +#include "widgets/gimpwindowstrategy.h" + +#include "gimpimagewindow.h" +#include "gimpsinglewindowstrategy.h" + + +static void gimp_single_window_strategy_window_strategy_iface_init (GimpWindowStrategyInterface *iface); +static GtkWidget * gimp_single_window_strategy_show_dockable_dialog (GimpWindowStrategy *strategy, + Gimp *gimp, + GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + const gchar *identifiers); + + +G_DEFINE_TYPE_WITH_CODE (GimpSingleWindowStrategy, gimp_single_window_strategy, GIMP_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_WINDOW_STRATEGY, + gimp_single_window_strategy_window_strategy_iface_init)) + +#define parent_class gimp_single_window_strategy_parent_class + + +static void +gimp_single_window_strategy_class_init (GimpSingleWindowStrategyClass *klass) +{ +} + +static void +gimp_single_window_strategy_init (GimpSingleWindowStrategy *strategy) +{ +} + +static void +gimp_single_window_strategy_window_strategy_iface_init (GimpWindowStrategyInterface *iface) +{ + iface->show_dockable_dialog = gimp_single_window_strategy_show_dockable_dialog; +} + +static GtkWidget * +gimp_single_window_strategy_show_dockable_dialog (GimpWindowStrategy *strategy, + Gimp *gimp, + GimpDialogFactory *factory, + GdkScreen *screen, + gint monitor, + const gchar *identifiers) +{ + GList *windows = gimp_get_image_windows (gimp); + GtkWidget *widget = NULL; + GimpImageWindow *window; + + g_return_val_if_fail (windows != NULL, NULL); + + /* In single-window mode, there should only be one window... */ + window = GIMP_IMAGE_WINDOW (windows->data); + + if (strcmp ("gimp-toolbox", identifiers) == 0) + { + /* Only allow one toolbox... */ + if (! gimp_image_window_has_toolbox (window)) + { + GimpDockColumns *columns; + GimpUIManager *ui_manager = gimp_image_window_get_ui_manager (window); + + widget = gimp_dialog_factory_dialog_new (factory, + screen, + monitor, + ui_manager, + "gimp-toolbox", + -1 /*view_size*/, + FALSE /*present*/); + gtk_widget_show (widget); + + columns = gimp_image_window_get_left_docks (window); + gimp_dock_columns_add_dock (columns, + GIMP_DOCK (widget), + -1 /*index*/); + } + } + else if (gimp_dialog_factory_find_widget (factory, identifiers)) + { + /* if the dialog is already open, simply raise it */ + return gimp_dialog_factory_dialog_raise (factory, screen, monitor, + identifiers, -1); + } + else + { + GtkWidget *dockbook; + + dockbook = gimp_image_window_get_default_dockbook (window); + + if (! dockbook) + { + GimpDockColumns *dock_columns; + + /* No dock, need to add one */ + dock_columns = gimp_image_window_get_right_docks (window); + gimp_dock_columns_prepare_dockbook (dock_columns, + -1 /*index*/, + &dockbook); + } + + widget = gimp_dockbook_add_from_dialog_factory (GIMP_DOCKBOOK (dockbook), + identifiers, + -1 /*index*/); + } + + + g_list_free (windows); + + return widget; +} + +GimpObject * +gimp_single_window_strategy_get_singleton (void) +{ + static GimpObject *singleton = NULL; + + if (! singleton) + singleton = g_object_new (GIMP_TYPE_SINGLE_WINDOW_STRATEGY, NULL); + + return singleton; +} diff --git a/app/display/gimpsinglewindowstrategy.h b/app/display/gimpsinglewindowstrategy.h new file mode 100644 index 0000000..5b145c7 --- /dev/null +++ b/app/display/gimpsinglewindowstrategy.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpsinglewindowstrategy.h + * Copyright (C) 2011 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 . + */ + +#ifndef __GIMP_SINGLE_WINDOW_STRATEGY_H__ +#define __GIMP_SINGLE_WINDOW_STRATEGY_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_SINGLE_WINDOW_STRATEGY (gimp_single_window_strategy_get_type ()) +#define GIMP_SINGLE_WINDOW_STRATEGY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SINGLE_WINDOW_STRATEGY, GimpSingleWindowStrategy)) +#define GIMP_SINGLE_WINDOW_STRATEGY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SINGLE_WINDOW_STRATEGY, GimpSingleWindowStrategyClass)) +#define GIMP_IS_SINGLE_WINDOW_STRATEGY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SINGLE_WINDOW_STRATEGY)) +#define GIMP_IS_SINGLE_WINDOW_STRATEGY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SINGLE_WINDOW_STRATEGY)) +#define GIMP_SINGLE_WINDOW_STRATEGY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SINGLE_WINDOW_STRATEGY, GimpSingleWindowStrategyClass)) + + +typedef struct _GimpSingleWindowStrategyClass GimpSingleWindowStrategyClass; + +struct _GimpSingleWindowStrategy +{ + GimpObject parent_instance; +}; + +struct _GimpSingleWindowStrategyClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_single_window_strategy_get_type (void) G_GNUC_CONST; + +GimpObject * gimp_single_window_strategy_get_singleton (void); + + +#endif /* __GIMP_SINGLE_WINDOW_STRATEGY_H__ */ diff --git a/app/display/gimpstatusbar.c b/app/display/gimpstatusbar.c new file mode 100644 index 0000000..2a2648f --- /dev/null +++ b/app/display/gimpstatusbar.c @@ -0,0 +1,1750 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "config/gimpdisplayconfig.h" + +#include "core/gimpimage.h" +#include "core/gimpprogress.h" + +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scale.h" +#include "gimpimagewindow.h" +#include "gimpscalecombobox.h" +#include "gimpstatusbar.h" + +#include "gimp-intl.h" + + +/* maximal width of the string holding the cursor-coordinates */ +#define CURSOR_LEN 256 + +/* the spacing of the hbox */ +#define HBOX_SPACING 1 + +/* spacing between the icon and the statusbar label */ +#define ICON_SPACING 2 + +/* width/height of the statusbar icon rect */ +#define ICON_SIZE 16 + +/* timeout (in milliseconds) for temporary statusbar messages */ +#define MESSAGE_TIMEOUT 8000 + +/* minimal interval (in microseconds) between progress updates */ +#define MIN_PROGRESS_UPDATE_INTERVAL 50000 + + +typedef struct _GimpStatusbarMsg GimpStatusbarMsg; + +struct _GimpStatusbarMsg +{ + guint context_id; + gchar *icon_name; + gchar *text; +}; + + +static void gimp_statusbar_progress_iface_init (GimpProgressInterface *iface); + +static void gimp_statusbar_dispose (GObject *object); +static void gimp_statusbar_finalize (GObject *object); + +static void gimp_statusbar_screen_changed (GtkWidget *widget, + GdkScreen *previous); +static void gimp_statusbar_style_set (GtkWidget *widget, + GtkStyle *prev_style); + +static void gimp_statusbar_hbox_size_request (GtkWidget *widget, + GtkRequisition *requisition, + GimpStatusbar *statusbar); + +static GimpProgress * + gimp_statusbar_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message); +static void gimp_statusbar_progress_end (GimpProgress *progress); +static gboolean gimp_statusbar_progress_is_active (GimpProgress *progress); +static void gimp_statusbar_progress_set_text (GimpProgress *progress, + const gchar *message); +static void gimp_statusbar_progress_set_value (GimpProgress *progress, + gdouble percentage); +static gdouble gimp_statusbar_progress_get_value (GimpProgress *progress); +static void gimp_statusbar_progress_pulse (GimpProgress *progress); +static gboolean gimp_statusbar_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); +static void gimp_statusbar_progress_canceled (GtkWidget *button, + GimpStatusbar *statusbar); + +static gboolean gimp_statusbar_label_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpStatusbar *statusbar); + +static void gimp_statusbar_update (GimpStatusbar *statusbar); +static void gimp_statusbar_unit_changed (GimpUnitComboBox *combo, + GimpStatusbar *statusbar); +static void gimp_statusbar_scale_changed (GimpScaleComboBox *combo, + GimpStatusbar *statusbar); +static void gimp_statusbar_scale_activated (GimpScaleComboBox *combo, + GimpStatusbar *statusbar); +static gboolean gimp_statusbar_rotate_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar); +static gboolean gimp_statusbar_horiz_flip_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar); +static gboolean gimp_statusbar_vert_flip_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar); +static void gimp_statusbar_shell_scaled (GimpDisplayShell *shell, + GimpStatusbar *statusbar); +static void gimp_statusbar_shell_rotated (GimpDisplayShell *shell, + GimpStatusbar *statusbar); +static void gimp_statusbar_shell_status_notify(GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpStatusbar *statusbar); +static guint gimp_statusbar_get_context_id (GimpStatusbar *statusbar, + const gchar *context); +static gboolean gimp_statusbar_temp_timeout (GimpStatusbar *statusbar); + +static void gimp_statusbar_add_message (GimpStatusbar *statusbar, + guint context_id, + const gchar *icon_name, + const gchar *format, + va_list args, + gboolean move_to_front) G_GNUC_PRINTF (4, 0); +static void gimp_statusbar_remove_message (GimpStatusbar *statusbar, + guint context_id); +static void gimp_statusbar_msg_free (GimpStatusbarMsg *msg); + +static gchar * gimp_statusbar_vprintf (const gchar *format, + va_list args) G_GNUC_PRINTF (1, 0); + +static GdkPixbuf * gimp_statusbar_load_icon (GimpStatusbar *statusbar, + const gchar *icon_name); + + +G_DEFINE_TYPE_WITH_CODE (GimpStatusbar, gimp_statusbar, GTK_TYPE_STATUSBAR, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS, + gimp_statusbar_progress_iface_init)) + +#define parent_class gimp_statusbar_parent_class + + +static void +gimp_statusbar_class_init (GimpStatusbarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gimp_statusbar_dispose; + object_class->finalize = gimp_statusbar_finalize; + + widget_class->screen_changed = gimp_statusbar_screen_changed; + widget_class->style_set = gimp_statusbar_style_set; +} + +static void +gimp_statusbar_progress_iface_init (GimpProgressInterface *iface) +{ + iface->start = gimp_statusbar_progress_start; + iface->end = gimp_statusbar_progress_end; + iface->is_active = gimp_statusbar_progress_is_active; + iface->set_text = gimp_statusbar_progress_set_text; + iface->set_value = gimp_statusbar_progress_set_value; + iface->get_value = gimp_statusbar_progress_get_value; + iface->pulse = gimp_statusbar_progress_pulse; + iface->message = gimp_statusbar_progress_message; +} + +static void +gimp_statusbar_init (GimpStatusbar *statusbar) +{ + GtkWidget *hbox; + GtkWidget *hbox2; + GtkWidget *image; + GtkWidget *label; + GimpUnitStore *store; + GList *children; + + statusbar->shell = NULL; + statusbar->messages = NULL; + statusbar->context_ids = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + statusbar->seq_context_id = 1; + + statusbar->temp_context_id = + gimp_statusbar_get_context_id (statusbar, "gimp-statusbar-temp"); + + statusbar->cursor_format_str[0] = '\0'; + statusbar->cursor_format_str_f[0] = '\0'; + statusbar->length_format_str[0] = '\0'; + + statusbar->progress_active = FALSE; + statusbar->progress_shown = FALSE; + + /* remove the message label from the message area */ + hbox = gtk_statusbar_get_message_area (GTK_STATUSBAR (statusbar)); + gtk_box_set_spacing (GTK_BOX (hbox), HBOX_SPACING); + + children = gtk_container_get_children (GTK_CONTAINER (hbox)); + statusbar->label = g_object_ref (children->data); + g_list_free (children); + + gtk_container_remove (GTK_CONTAINER (hbox), statusbar->label); + + g_signal_connect (hbox, "size-request", + G_CALLBACK (gimp_statusbar_hbox_size_request), + statusbar); + + statusbar->cursor_label = gtk_label_new ("8888, 8888"); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->cursor_label, FALSE, FALSE, 0); + gtk_widget_show (statusbar->cursor_label); + + store = gimp_unit_store_new (2); + statusbar->unit_combo = gimp_unit_combo_box_new_with_model (store); + g_object_unref (store); + + /* see issue #2642 */ + gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (statusbar->unit_combo), 1); + + gtk_widget_set_can_focus (statusbar->unit_combo, FALSE); + g_object_set (statusbar->unit_combo, "focus-on-click", FALSE, NULL); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->unit_combo, FALSE, FALSE, 0); + gtk_widget_show (statusbar->unit_combo); + + g_signal_connect (statusbar->unit_combo, "changed", + G_CALLBACK (gimp_statusbar_unit_changed), + statusbar); + + statusbar->scale_combo = gimp_scale_combo_box_new (); + gtk_widget_set_can_focus (statusbar->scale_combo, FALSE); + g_object_set (statusbar->scale_combo, "focus-on-click", FALSE, NULL); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->scale_combo, FALSE, FALSE, 0); + gtk_widget_show (statusbar->scale_combo); + + g_signal_connect (statusbar->scale_combo, "changed", + G_CALLBACK (gimp_statusbar_scale_changed), + statusbar); + + g_signal_connect (statusbar->scale_combo, "entry-activated", + G_CALLBACK (gimp_statusbar_scale_activated), + statusbar); + + /* Shell transform status */ + statusbar->rotate_widget = gtk_event_box_new (); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->rotate_widget, + FALSE, FALSE, 1); + gtk_widget_show (statusbar->rotate_widget); + + statusbar->rotate_label = gtk_label_new (NULL); + gtk_container_add (GTK_CONTAINER (statusbar->rotate_widget), + statusbar->rotate_label); + gtk_widget_show (statusbar->rotate_label); + + g_signal_connect (statusbar->rotate_widget, "button-press-event", + G_CALLBACK (gimp_statusbar_rotate_pressed), + statusbar); + + statusbar->horizontal_flip_icon = gtk_event_box_new (); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->horizontal_flip_icon, + FALSE, FALSE, 1); + gtk_widget_show (statusbar->horizontal_flip_icon); + + image = gtk_image_new_from_icon_name ("gimp-flip-horizontal", + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (statusbar->horizontal_flip_icon), image); + gtk_widget_show (image); + + g_signal_connect (statusbar->horizontal_flip_icon, "button-press-event", + G_CALLBACK (gimp_statusbar_horiz_flip_pressed), + statusbar); + + statusbar->vertical_flip_icon = gtk_event_box_new (); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->vertical_flip_icon, + FALSE, FALSE, 1); + gtk_widget_show (statusbar->vertical_flip_icon); + + image = gtk_image_new_from_icon_name ("gimp-flip-vertical", + GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (statusbar->vertical_flip_icon), image); + gtk_widget_show (image); + + g_signal_connect (statusbar->vertical_flip_icon, "button-press-event", + G_CALLBACK (gimp_statusbar_vert_flip_pressed), + statusbar); + + /* put the label back into the message area */ + gtk_box_pack_start (GTK_BOX (hbox), statusbar->label, TRUE, TRUE, 1); + + g_object_unref (statusbar->label); + + g_signal_connect_after (statusbar->label, "expose-event", + G_CALLBACK (gimp_statusbar_label_expose), + statusbar); + + statusbar->progressbar = g_object_new (GTK_TYPE_PROGRESS_BAR, + "text-xalign", 0.0, + "text-yalign", 0.5, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + gtk_box_pack_start (GTK_BOX (hbox), statusbar->progressbar, TRUE, TRUE, 0); + /* don't show the progress bar */ + + /* construct the cancel button's contents manually because we + * always want image and label regardless of settings, and we want + * a menu size image. + */ + statusbar->cancel_button = gtk_button_new (); + gtk_widget_set_can_focus (statusbar->cancel_button, FALSE); + gtk_button_set_relief (GTK_BUTTON (statusbar->cancel_button), + GTK_RELIEF_NONE); + gtk_widget_set_sensitive (statusbar->cancel_button, FALSE); + gtk_box_pack_end (GTK_BOX (hbox), + statusbar->cancel_button, FALSE, FALSE, 0); + /* don't show the cancel button */ + + hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add (GTK_CONTAINER (statusbar->cancel_button), hbox2); + gtk_widget_show (hbox2); + + image = gtk_image_new_from_icon_name ("gtk-cancel", GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (hbox2), image, FALSE, FALSE, 2); + gtk_widget_show (image); + + label = gtk_label_new ("Cancel"); + gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 2); + gtk_widget_show (label); + + g_signal_connect (statusbar->cancel_button, "clicked", + G_CALLBACK (gimp_statusbar_progress_canceled), + statusbar); +} + +static void +gimp_statusbar_dispose (GObject *object) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (object); + + if (statusbar->temp_timeout_id) + { + g_source_remove (statusbar->temp_timeout_id); + statusbar->temp_timeout_id = 0; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_statusbar_finalize (GObject *object) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (object); + + g_clear_object (&statusbar->icon); + g_clear_pointer (&statusbar->icon_hash, g_hash_table_unref); + + g_slist_free_full (statusbar->messages, + (GDestroyNotify) gimp_statusbar_msg_free); + statusbar->messages = NULL; + + g_clear_pointer (&statusbar->context_ids, g_hash_table_destroy); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_statusbar_screen_changed (GtkWidget *widget, + GdkScreen *previous) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (widget); + + if (GTK_WIDGET_CLASS (parent_class)->screen_changed) + GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous); + + g_clear_pointer (&statusbar->icon_hash, g_hash_table_unref); +} + +static void +gimp_statusbar_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (widget); + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + g_clear_pointer (&statusbar->icon_hash, g_hash_table_unref); +} + +static void +gimp_statusbar_hbox_size_request (GtkWidget *widget, + GtkRequisition *requisition, + GimpStatusbar *statusbar) +{ + GtkRequisition child_requisition; + gint width = 0; + + /* also consider the children which can be invisible */ + + gtk_widget_size_request (statusbar->cursor_label, &child_requisition); + width += child_requisition.width; + requisition->height = MAX (requisition->height, + child_requisition.height); + + gtk_widget_size_request (statusbar->unit_combo, &child_requisition); + width += child_requisition.width; + requisition->height = MAX (requisition->height, + child_requisition.height); + + gtk_widget_size_request (statusbar->scale_combo, &child_requisition); + width += child_requisition.width; + requisition->height = MAX (requisition->height, + child_requisition.height); + + gtk_widget_size_request (statusbar->progressbar, &child_requisition); + requisition->height = MAX (requisition->height, + child_requisition.height); + + gtk_widget_size_request (statusbar->label, &child_requisition); + requisition->height = MAX (requisition->height, + child_requisition.height); + + gtk_widget_size_request (statusbar->cancel_button, &child_requisition); + requisition->height = MAX (requisition->height, + child_requisition.height); + + requisition->width = MAX (requisition->width, width + 32); +} + +static GimpProgress * +gimp_statusbar_progress_start (GimpProgress *progress, + gboolean cancellable, + const gchar *message) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (! statusbar->progress_active) + { + GtkWidget *bar = statusbar->progressbar; + GtkAllocation allocation; + + statusbar->progress_active = TRUE; + statusbar->progress_value = 0.0; + statusbar->progress_last_update_time = g_get_monotonic_time (); + + gimp_statusbar_push (statusbar, "progress", NULL, "%s", message); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0); + gtk_widget_set_sensitive (statusbar->cancel_button, cancellable); + + if (cancellable) + { + if (message) + { + gchar *tooltip = g_strdup_printf (_("Cancel %s"), message); + + gimp_help_set_help_data_with_markup (statusbar->cancel_button, + tooltip, NULL); + g_free (tooltip); + } + + gtk_widget_show (statusbar->cancel_button); + } + + gtk_widget_get_allocation (statusbar->label, &allocation); + + gtk_widget_show (statusbar->progressbar); + gtk_widget_hide (statusbar->label); + + /* This shit is needed so that the progress bar is drawn in the + * correct place in the cases where we suck completely and run + * an operation that blocks the GUI and doesn't let the main + * loop run. + */ + gtk_container_resize_children (GTK_CONTAINER (statusbar)); + gtk_widget_size_allocate (statusbar->progressbar, &allocation); + + if (! gtk_widget_get_visible (GTK_WIDGET (statusbar))) + { + gtk_widget_show (GTK_WIDGET (statusbar)); + statusbar->progress_shown = TRUE; + } + + gimp_widget_flush_expose (bar); + + gimp_statusbar_override_window_title (statusbar); + + return progress; + } + + return NULL; +} + +static void +gimp_statusbar_progress_end (GimpProgress *progress) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (statusbar->progress_active) + { + GtkWidget *bar = statusbar->progressbar; + + if (statusbar->progress_shown) + { + gtk_widget_hide (GTK_WIDGET (statusbar)); + statusbar->progress_shown = FALSE; + } + + statusbar->progress_active = FALSE; + statusbar->progress_value = 0.0; + + gtk_widget_hide (bar); + gtk_widget_show (statusbar->label); + + gimp_statusbar_pop (statusbar, "progress"); + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0); + gtk_widget_set_sensitive (statusbar->cancel_button, FALSE); + gtk_widget_hide (statusbar->cancel_button); + + gimp_statusbar_restore_window_title (statusbar); + } +} + +static gboolean +gimp_statusbar_progress_is_active (GimpProgress *progress) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + return statusbar->progress_active; +} + +static void +gimp_statusbar_progress_set_text (GimpProgress *progress, + const gchar *message) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (statusbar->progress_active) + { + GtkWidget *bar = statusbar->progressbar; + + gimp_statusbar_replace (statusbar, "progress", NULL, "%s", message); + + gimp_widget_flush_expose (bar); + + gimp_statusbar_override_window_title (statusbar); + } +} + +static void +gimp_statusbar_progress_set_value (GimpProgress *progress, + gdouble percentage) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (statusbar->progress_active) + { + guint64 time = g_get_monotonic_time (); + + if (time - statusbar->progress_last_update_time >= + MIN_PROGRESS_UPDATE_INTERVAL) + { + GtkWidget *bar = statusbar->progressbar; + GtkAllocation allocation; + gdouble diff; + + gtk_widget_get_allocation (bar, &allocation); + + statusbar->progress_value = percentage; + + diff = fabs (percentage - + gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (bar))); + + /* only update the progress bar if this causes a visible change */ + if (allocation.width * diff >= 1.0) + { + statusbar->progress_last_update_time = time; + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), + percentage); + + gimp_widget_flush_expose (bar); + } + } + } +} + +static gdouble +gimp_statusbar_progress_get_value (GimpProgress *progress) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (statusbar->progress_active) + return statusbar->progress_value; + + return 0.0; +} + +static void +gimp_statusbar_progress_pulse (GimpProgress *progress) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + + if (statusbar->progress_active) + { + guint64 time = g_get_monotonic_time (); + + if (time - statusbar->progress_last_update_time >= + MIN_PROGRESS_UPDATE_INTERVAL) + { + GtkWidget *bar = statusbar->progressbar; + + statusbar->progress_last_update_time = time; + + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar)); + + gimp_widget_flush_expose (bar); + } + } +} + +static gboolean +gimp_statusbar_progress_message (GimpProgress *progress, + Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GimpStatusbar *statusbar = GIMP_STATUSBAR (progress); + PangoLayout *layout; + const gchar *icon_name; + gboolean handle_msg = FALSE; + + /* don't accept a message if we are already displaying a more severe one */ + if (statusbar->temp_timeout_id && statusbar->temp_severity > severity) + return FALSE; + + /* we can only handle short one-liners */ + layout = gtk_widget_create_pango_layout (statusbar->label, message); + + icon_name = gimp_get_message_icon_name (severity); + + if (pango_layout_get_line_count (layout) == 1) + { + GtkAllocation label_allocation; + gint width; + + gtk_widget_get_allocation (statusbar->label, &label_allocation); + + pango_layout_get_pixel_size (layout, &width, NULL); + + if (width < label_allocation.width) + { + if (icon_name) + { + GdkPixbuf *pixbuf; + + pixbuf = gimp_statusbar_load_icon (statusbar, icon_name); + + width += ICON_SPACING + gdk_pixbuf_get_width (pixbuf); + + g_object_unref (pixbuf); + + handle_msg = (width < label_allocation.width); + } + else + { + handle_msg = TRUE; + } + } + } + + g_object_unref (layout); + + if (handle_msg) + gimp_statusbar_push_temp (statusbar, severity, icon_name, "%s", message); + + return handle_msg; +} + +static void +gimp_statusbar_progress_canceled (GtkWidget *button, + GimpStatusbar *statusbar) +{ + if (statusbar->progress_active) + gimp_progress_cancel (GIMP_PROGRESS (statusbar)); +} + +static void +gimp_statusbar_set_text (GimpStatusbar *statusbar, + const gchar *icon_name, + const gchar *text) +{ + if (statusbar->progress_active) + { + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (statusbar->progressbar), + text); + } + else + { + g_clear_object (&statusbar->icon); + + if (icon_name) + statusbar->icon = gimp_statusbar_load_icon (statusbar, icon_name); + + if (statusbar->icon) + { + PangoAttrList *attrs; + PangoAttribute *attr; + PangoRectangle rect; + gchar *tmp; + + tmp = g_strconcat (" ", text, NULL); + gtk_label_set_text (GTK_LABEL (statusbar->label), tmp); + g_free (tmp); + + rect.x = 0; + rect.y = 0; + rect.width = PANGO_SCALE * (gdk_pixbuf_get_width (statusbar->icon) + + ICON_SPACING); + rect.height = 0; + + attrs = pango_attr_list_new (); + + attr = pango_attr_shape_new (&rect, &rect); + attr->start_index = 0; + attr->end_index = 1; + pango_attr_list_insert (attrs, attr); + + gtk_label_set_attributes (GTK_LABEL (statusbar->label), attrs); + pango_attr_list_unref (attrs); + } + else + { + gtk_label_set_text (GTK_LABEL (statusbar->label), text); + gtk_label_set_attributes (GTK_LABEL (statusbar->label), NULL); + } + } +} + +static void +gimp_statusbar_update (GimpStatusbar *statusbar) +{ + GimpStatusbarMsg *msg = NULL; + + if (statusbar->messages) + msg = statusbar->messages->data; + + if (msg && msg->text) + { + gimp_statusbar_set_text (statusbar, msg->icon_name, msg->text); + } + else + { + gimp_statusbar_set_text (statusbar, NULL, ""); + } +} + + +/* public functions */ + +GtkWidget * +gimp_statusbar_new (void) +{ + return g_object_new (GIMP_TYPE_STATUSBAR, NULL); +} + +void +gimp_statusbar_set_shell (GimpStatusbar *statusbar, + GimpDisplayShell *shell) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell == statusbar->shell) + return; + + if (statusbar->shell) + { + g_signal_handlers_disconnect_by_func (statusbar->shell, + gimp_statusbar_shell_scaled, + statusbar); + g_signal_handlers_disconnect_by_func (statusbar->shell, + gimp_statusbar_shell_rotated, + statusbar); + g_signal_handlers_disconnect_by_func (statusbar->shell, + gimp_statusbar_shell_status_notify, + statusbar); + } + + statusbar->shell = shell; + + g_signal_connect_object (statusbar->shell, "scaled", + G_CALLBACK (gimp_statusbar_shell_scaled), + statusbar, 0); + g_signal_connect_object (statusbar->shell, "rotated", + G_CALLBACK (gimp_statusbar_shell_rotated), + statusbar, 0); + g_signal_connect_object (statusbar->shell, "notify::status", + G_CALLBACK (gimp_statusbar_shell_status_notify), + statusbar, 0); + gimp_statusbar_shell_rotated (shell, statusbar); +} + +gboolean +gimp_statusbar_get_visible (GimpStatusbar *statusbar) +{ + g_return_val_if_fail (GIMP_IS_STATUSBAR (statusbar), FALSE); + + if (statusbar->progress_shown) + return FALSE; + + return gtk_widget_get_visible (GTK_WIDGET (statusbar)); +} + +void +gimp_statusbar_set_visible (GimpStatusbar *statusbar, + gboolean visible) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + if (statusbar->progress_shown) + { + if (visible) + { + statusbar->progress_shown = FALSE; + return; + } + } + + gtk_widget_set_visible (GTK_WIDGET (statusbar), visible); +} + +void +gimp_statusbar_empty (GimpStatusbar *statusbar) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + gtk_widget_hide (statusbar->cursor_label); + gtk_widget_hide (statusbar->unit_combo); + gtk_widget_hide (statusbar->scale_combo); + gtk_widget_hide (statusbar->rotate_widget); + gtk_widget_hide (statusbar->horizontal_flip_icon); + gtk_widget_hide (statusbar->vertical_flip_icon); +} + +void +gimp_statusbar_fill (GimpStatusbar *statusbar) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + gtk_widget_show (statusbar->cursor_label); + gtk_widget_show (statusbar->unit_combo); + gtk_widget_show (statusbar->scale_combo); + gtk_widget_show (statusbar->rotate_widget); + gimp_statusbar_shell_rotated (statusbar->shell, statusbar); +} + +void +gimp_statusbar_override_window_title (GimpStatusbar *statusbar) +{ + GtkWidget *toplevel; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (statusbar)); + + if (gimp_image_window_is_iconified (GIMP_IMAGE_WINDOW (toplevel))) + { + const gchar *message = gimp_statusbar_peek (statusbar, "progress"); + + if (message) + gtk_window_set_title (GTK_WINDOW (toplevel), message); + } +} + +void +gimp_statusbar_restore_window_title (GimpStatusbar *statusbar) +{ + GtkWidget *toplevel; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (statusbar)); + + if (gimp_image_window_is_iconified (GIMP_IMAGE_WINDOW (toplevel))) + { + g_object_notify (G_OBJECT (statusbar->shell), "title"); + } +} + +void +gimp_statusbar_push (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + ...) +{ + va_list args; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (context != NULL); + g_return_if_fail (format != NULL); + + va_start (args, format); + gimp_statusbar_push_valist (statusbar, context, icon_name, format, args); + va_end (args); +} + +void +gimp_statusbar_push_valist (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + va_list args) +{ + guint context_id; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (context != NULL); + g_return_if_fail (format != NULL); + + context_id = gimp_statusbar_get_context_id (statusbar, context); + + gimp_statusbar_add_message (statusbar, + context_id, + icon_name, format, args, + /* move_to_front = */ TRUE); +} + +void +gimp_statusbar_push_coords (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + GimpCursorPrecision precision, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help) +{ + GimpDisplayShell *shell; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (title != NULL); + g_return_if_fail (separator != NULL); + + if (help == NULL) + help = ""; + + shell = statusbar->shell; + + switch (precision) + { + case GIMP_CURSOR_PRECISION_PIXEL_CENTER: + x = (gint) x; + y = (gint) y; + break; + + case GIMP_CURSOR_PRECISION_PIXEL_BORDER: + x = RINT (x); + y = RINT (y); + break; + + case GIMP_CURSOR_PRECISION_SUBPIXEL: + break; + } + + if (shell->unit == GIMP_UNIT_PIXEL) + { + if (precision == GIMP_CURSOR_PRECISION_SUBPIXEL) + { + gimp_statusbar_push (statusbar, context, + icon_name, + statusbar->cursor_format_str_f, + title, + x, + separator, + y, + help); + } + else + { + gimp_statusbar_push (statusbar, context, + icon_name, + statusbar->cursor_format_str, + title, + (gint) RINT (x), + separator, + (gint) RINT (y), + help); + } + } + else /* show real world units */ + { + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (gimp_display_get_image (shell->display), + &xres, &yres); + + gimp_statusbar_push (statusbar, context, + icon_name, + statusbar->cursor_format_str, + title, + gimp_pixels_to_units (x, shell->unit, xres), + separator, + gimp_pixels_to_units (y, shell->unit, yres), + help); + } +} + +void +gimp_statusbar_push_length (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *title, + GimpOrientationType axis, + gdouble value, + const gchar *help) +{ + GimpDisplayShell *shell; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (title != NULL); + + if (help == NULL) + help = ""; + + shell = statusbar->shell; + + if (shell->unit == GIMP_UNIT_PIXEL) + { + gimp_statusbar_push (statusbar, context, + icon_name, + statusbar->length_format_str, + title, + (gint) RINT (value), + help); + } + else /* show real world units */ + { + gdouble xres; + gdouble yres; + gdouble resolution; + + gimp_image_get_resolution (gimp_display_get_image (shell->display), + &xres, &yres); + + switch (axis) + { + case GIMP_ORIENTATION_HORIZONTAL: + resolution = xres; + break; + + case GIMP_ORIENTATION_VERTICAL: + resolution = yres; + break; + + default: + g_return_if_reached (); + break; + } + + gimp_statusbar_push (statusbar, context, + icon_name, + statusbar->length_format_str, + title, + gimp_pixels_to_units (value, shell->unit, resolution), + help); + } +} + +void +gimp_statusbar_replace (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + ...) +{ + va_list args; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (context != NULL); + g_return_if_fail (format != NULL); + + va_start (args, format); + gimp_statusbar_replace_valist (statusbar, context, icon_name, format, args); + va_end (args); +} + +void +gimp_statusbar_replace_valist (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + va_list args) +{ + guint context_id; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (context != NULL); + g_return_if_fail (format != NULL); + + context_id = gimp_statusbar_get_context_id (statusbar, context); + + gimp_statusbar_add_message (statusbar, + context_id, + icon_name, format, args, + /* move_to_front = */ FALSE); +} + +const gchar * +gimp_statusbar_peek (GimpStatusbar *statusbar, + const gchar *context) +{ + GSList *list; + guint context_id; + + g_return_val_if_fail (GIMP_IS_STATUSBAR (statusbar), NULL); + g_return_val_if_fail (context != NULL, NULL); + + context_id = gimp_statusbar_get_context_id (statusbar, context); + + for (list = statusbar->messages; list; list = list->next) + { + GimpStatusbarMsg *msg = list->data; + + if (msg->context_id == context_id) + { + return msg->text; + } + } + + return NULL; +} + +void +gimp_statusbar_pop (GimpStatusbar *statusbar, + const gchar *context) +{ + guint context_id; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (context != NULL); + + context_id = gimp_statusbar_get_context_id (statusbar, context); + + gimp_statusbar_remove_message (statusbar, + context_id); +} + +void +gimp_statusbar_push_temp (GimpStatusbar *statusbar, + GimpMessageSeverity severity, + const gchar *icon_name, + const gchar *format, + ...) +{ + va_list args; + + va_start (args, format); + gimp_statusbar_push_temp_valist (statusbar, severity, icon_name, format, args); + va_end (args); +} + +void +gimp_statusbar_push_temp_valist (GimpStatusbar *statusbar, + GimpMessageSeverity severity, + const gchar *icon_name, + const gchar *format, + va_list args) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + g_return_if_fail (severity <= GIMP_MESSAGE_WARNING); + g_return_if_fail (format != NULL); + + /* don't accept a message if we are already displaying a more severe one */ + if (statusbar->temp_timeout_id && statusbar->temp_severity > severity) + return; + + if (statusbar->temp_timeout_id) + g_source_remove (statusbar->temp_timeout_id); + + statusbar->temp_timeout_id = + g_timeout_add (MESSAGE_TIMEOUT, + (GSourceFunc) gimp_statusbar_temp_timeout, statusbar); + + statusbar->temp_severity = severity; + + gimp_statusbar_add_message (statusbar, + statusbar->temp_context_id, + icon_name, format, args, + /* move_to_front = */ TRUE); + + if (severity >= GIMP_MESSAGE_WARNING) + gimp_widget_blink (statusbar->label); +} + +void +gimp_statusbar_pop_temp (GimpStatusbar *statusbar) +{ + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + if (statusbar->temp_timeout_id) + { + g_source_remove (statusbar->temp_timeout_id); + statusbar->temp_timeout_id = 0; + + gimp_statusbar_remove_message (statusbar, + statusbar->temp_context_id); + } +} + +void +gimp_statusbar_update_cursor (GimpStatusbar *statusbar, + GimpCursorPrecision precision, + gdouble x, + gdouble y) +{ + GimpDisplayShell *shell; + GimpImage *image; + gchar buffer[CURSOR_LEN]; + + g_return_if_fail (GIMP_IS_STATUSBAR (statusbar)); + + shell = statusbar->shell; + image = gimp_display_get_image (shell->display); + + if (! image || + x < 0 || + y < 0 || + x >= gimp_image_get_width (image) || + y >= gimp_image_get_height (image)) + { + gtk_widget_set_sensitive (statusbar->cursor_label, FALSE); + } + else + { + gtk_widget_set_sensitive (statusbar->cursor_label, TRUE); + } + + switch (precision) + { + case GIMP_CURSOR_PRECISION_PIXEL_CENTER: + x = (gint) x; + y = (gint) y; + break; + + case GIMP_CURSOR_PRECISION_PIXEL_BORDER: + x = RINT (x); + y = RINT (y); + break; + + case GIMP_CURSOR_PRECISION_SUBPIXEL: + break; + } + + if (shell->unit == GIMP_UNIT_PIXEL) + { + if (precision == GIMP_CURSOR_PRECISION_SUBPIXEL) + { + g_snprintf (buffer, sizeof (buffer), + statusbar->cursor_format_str_f, + "", x, ", ", y, ""); + } + else + { + g_snprintf (buffer, sizeof (buffer), + statusbar->cursor_format_str, + "", (gint) RINT (x), ", ", (gint) RINT (y), ""); + } + } + else /* show real world units */ + { + GtkTreeModel *model; + GimpUnitStore *store; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (statusbar->unit_combo)); + store = GIMP_UNIT_STORE (model); + + gimp_unit_store_set_pixel_values (store, x, y); + gimp_unit_store_get_values (store, shell->unit, &x, &y); + + g_snprintf (buffer, sizeof (buffer), + statusbar->cursor_format_str, + "", x, ", ", y, ""); + } + + gtk_label_set_text (GTK_LABEL (statusbar->cursor_label), buffer); +} + +void +gimp_statusbar_clear_cursor (GimpStatusbar *statusbar) +{ + gtk_label_set_text (GTK_LABEL (statusbar->cursor_label), ""); + gtk_widget_set_sensitive (statusbar->cursor_label, TRUE); +} + + +/* private functions */ + +static gboolean +gimp_statusbar_label_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpStatusbar *statusbar) +{ + if (statusbar->icon) + { + cairo_t *cr; + PangoRectangle rect; + gint x, y; + + cr = gdk_cairo_create (event->window); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + gtk_label_get_layout_offsets (GTK_LABEL (widget), &x, &y); + + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (widget)), 0, + &rect); + + /* the rectangle width is negative when rendering right-to-left */ + x += PANGO_PIXELS (rect.x) + (rect.width < 0 ? + PANGO_PIXELS (rect.width) : 0); + y += PANGO_PIXELS (rect.y / ICON_SIZE); + + gdk_cairo_set_source_pixbuf (cr, statusbar->icon, x, y); + cairo_paint (cr); + + cairo_destroy (cr); + } + + return FALSE; +} + +static void +gimp_statusbar_shell_scaled (GimpDisplayShell *shell, + GimpStatusbar *statusbar) +{ + static PangoLayout *layout = NULL; + + GimpImage *image = gimp_display_get_image (shell->display); + GtkTreeModel *model; + const gchar *text; + gint image_width; + gint image_height; + gdouble image_xres; + gdouble image_yres; + gint width; + + if (image) + { + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + gimp_image_get_resolution (image, &image_xres, &image_yres); + } + else + { + image_width = shell->disp_width; + image_height = shell->disp_height; + image_xres = shell->display->config->monitor_xres; + image_yres = shell->display->config->monitor_yres; + } + + g_signal_handlers_block_by_func (statusbar->scale_combo, + gimp_statusbar_scale_changed, statusbar); + gimp_scale_combo_box_set_scale (GIMP_SCALE_COMBO_BOX (statusbar->scale_combo), + gimp_zoom_model_get_factor (shell->zoom)); + g_signal_handlers_unblock_by_func (statusbar->scale_combo, + gimp_statusbar_scale_changed, statusbar); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (statusbar->unit_combo)); + gimp_unit_store_set_resolutions (GIMP_UNIT_STORE (model), + image_xres, image_yres); + + g_signal_handlers_block_by_func (statusbar->unit_combo, + gimp_statusbar_unit_changed, statusbar); + gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (statusbar->unit_combo), + shell->unit); + g_signal_handlers_unblock_by_func (statusbar->unit_combo, + gimp_statusbar_unit_changed, statusbar); + + if (shell->unit == GIMP_UNIT_PIXEL) + { + g_snprintf (statusbar->cursor_format_str, + sizeof (statusbar->cursor_format_str), + "%%s%%d%%s%%d%%s"); + g_snprintf (statusbar->cursor_format_str_f, + sizeof (statusbar->cursor_format_str_f), + "%%s%%.1f%%s%%.1f%%s"); + g_snprintf (statusbar->length_format_str, + sizeof (statusbar->length_format_str), + "%%s%%d%%s"); + } + else /* show real world units */ + { + gint w_digits; + gint h_digits; + + w_digits = gimp_unit_get_scaled_digits (shell->unit, image_xres); + h_digits = gimp_unit_get_scaled_digits (shell->unit, image_yres); + + g_snprintf (statusbar->cursor_format_str, + sizeof (statusbar->cursor_format_str), + "%%s%%.%df%%s%%.%df%%s", + w_digits, h_digits); + strcpy (statusbar->cursor_format_str_f, statusbar->cursor_format_str); + g_snprintf (statusbar->length_format_str, + sizeof (statusbar->length_format_str), + "%%s%%.%df%%s", MAX (w_digits, h_digits)); + } + + gimp_statusbar_update_cursor (statusbar, GIMP_CURSOR_PRECISION_SUBPIXEL, + -image_width, -image_height); + + text = gtk_label_get_text (GTK_LABEL (statusbar->cursor_label)); + + /* one static layout for all displays should be fine */ + if (! layout) + layout = gtk_widget_create_pango_layout (statusbar->cursor_label, NULL); + + pango_layout_set_text (layout, text, -1); + pango_layout_get_pixel_size (layout, &width, NULL); + + gtk_widget_set_size_request (statusbar->cursor_label, width, -1); + + gimp_statusbar_clear_cursor (statusbar); +} + +static void +gimp_statusbar_shell_rotated (GimpDisplayShell *shell, + GimpStatusbar *statusbar) +{ + if (shell->rotate_angle != 0.0) + { + /* Degree symbol U+00B0. There are no spaces between the value and the + * unit for angular rotation. + */ + gchar *text = g_strdup_printf (" %.2f\xC2\xB0", shell->rotate_angle); + + gtk_label_set_text (GTK_LABEL (statusbar->rotate_label), text); + g_free (text); + + gtk_widget_show (statusbar->rotate_widget); + } + else + { + gtk_widget_hide (statusbar->rotate_widget); + } + + if (shell->flip_horizontally) + gtk_widget_show (statusbar->horizontal_flip_icon); + else + gtk_widget_hide (statusbar->horizontal_flip_icon); + + if (shell->flip_vertically) + gtk_widget_show (statusbar->vertical_flip_icon); + else + gtk_widget_hide (statusbar->vertical_flip_icon); +} + +static void +gimp_statusbar_shell_status_notify (GimpDisplayShell *shell, + const GParamSpec *pspec, + GimpStatusbar *statusbar) +{ + gimp_statusbar_replace (statusbar, "title", + NULL, "%s", shell->status); +} + +static void +gimp_statusbar_unit_changed (GimpUnitComboBox *combo, + GimpStatusbar *statusbar) +{ + gimp_display_shell_set_unit (statusbar->shell, + gimp_unit_combo_box_get_active (combo)); +} + +static void +gimp_statusbar_scale_changed (GimpScaleComboBox *combo, + GimpStatusbar *statusbar) +{ + gimp_display_shell_scale (statusbar->shell, + GIMP_ZOOM_TO, + gimp_scale_combo_box_get_scale (combo), + GIMP_ZOOM_FOCUS_BEST_GUESS); +} + +static void +gimp_statusbar_scale_activated (GimpScaleComboBox *combo, + GimpStatusbar *statusbar) +{ + gtk_widget_grab_focus (statusbar->shell->canvas); +} + +static gboolean +gimp_statusbar_rotate_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar) +{ + GimpImageWindow *window = gimp_display_shell_get_window (statusbar->shell); + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_activate_action (manager, "view", "view-rotate-other"); + return FALSE; +} + +static gboolean +gimp_statusbar_horiz_flip_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar) +{ + GimpImageWindow *window = gimp_display_shell_get_window (statusbar->shell); + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_activate_action (manager, "view", "view-flip-horizontally"); + + return FALSE; +} + +static gboolean +gimp_statusbar_vert_flip_pressed (GtkWidget *event_box, + GdkEvent *event, + GimpStatusbar *statusbar) +{ + GimpImageWindow *window = gimp_display_shell_get_window (statusbar->shell); + GimpUIManager *manager = gimp_image_window_get_ui_manager (window); + + gimp_ui_manager_activate_action (manager, "view", "view-flip-vertically"); + + return FALSE; +} + +static guint +gimp_statusbar_get_context_id (GimpStatusbar *statusbar, + const gchar *context) +{ + guint id = GPOINTER_TO_UINT (g_hash_table_lookup (statusbar->context_ids, + context)); + + if (! id) + { + id = statusbar->seq_context_id++; + + g_hash_table_insert (statusbar->context_ids, + g_strdup (context), GUINT_TO_POINTER (id)); + } + + return id; +} + +static gboolean +gimp_statusbar_temp_timeout (GimpStatusbar *statusbar) +{ + gimp_statusbar_pop_temp (statusbar); + + return FALSE; +} + +static void +gimp_statusbar_add_message (GimpStatusbar *statusbar, + guint context_id, + const gchar *icon_name, + const gchar *format, + va_list args, + gboolean move_to_front) +{ + gchar *message; + GSList *list; + GimpStatusbarMsg *msg; + gint position; + + message = gimp_statusbar_vprintf (format, args); + + for (list = statusbar->messages; list; list = g_slist_next (list)) + { + msg = list->data; + + if (msg->context_id == context_id) + { + gboolean is_front_message = (list == statusbar->messages); + + if ((is_front_message || ! move_to_front) && + strcmp (msg->text, message) == 0 && + g_strcmp0 (msg->icon_name, icon_name) == 0) + { + g_free (message); + return; + } + + if (move_to_front) + { + statusbar->messages = g_slist_remove (statusbar->messages, msg); + gimp_statusbar_msg_free (msg); + + break; + } + else + { + g_free (msg->icon_name); + msg->icon_name = g_strdup (icon_name); + + g_free (msg->text); + msg->text = message; + + if (is_front_message) + gimp_statusbar_update (statusbar); + + return; + } + } + } + + msg = g_slice_new (GimpStatusbarMsg); + + msg->context_id = context_id; + msg->icon_name = g_strdup (icon_name); + msg->text = message; + + /* find the position at which to insert the new message */ + position = 0; + /* progress messages are always at the front of the list */ + if (! (statusbar->progress_active && + context_id == gimp_statusbar_get_context_id (statusbar, "progress"))) + { + if (statusbar->progress_active) + position++; + + /* temporary messages are in front of all other non-progress messages */ + if (statusbar->temp_timeout_id && + context_id != statusbar->temp_context_id) + position++; + } + + statusbar->messages = g_slist_insert (statusbar->messages, msg, position); + + if (position == 0) + gimp_statusbar_update (statusbar); +} + +static void +gimp_statusbar_remove_message (GimpStatusbar *statusbar, + guint context_id) +{ + GSList *list; + gboolean needs_update = FALSE; + + for (list = statusbar->messages; list; list = g_slist_next (list)) + { + GimpStatusbarMsg *msg = list->data; + + if (msg->context_id == context_id) + { + needs_update = (list == statusbar->messages); + + statusbar->messages = g_slist_remove (statusbar->messages, msg); + gimp_statusbar_msg_free (msg); + + break; + } + } + + if (needs_update) + gimp_statusbar_update (statusbar); +} + +static void +gimp_statusbar_msg_free (GimpStatusbarMsg *msg) +{ + g_free (msg->icon_name); + g_free (msg->text); + + g_slice_free (GimpStatusbarMsg, msg); +} + +static gchar * +gimp_statusbar_vprintf (const gchar *format, + va_list args) +{ + gchar *message; + gchar *newline; + + message = g_strdup_vprintf (format, args); + + /* guard us from multi-line strings */ + newline = strchr (message, '\r'); + if (newline) + *newline = '\0'; + + newline = strchr (message, '\n'); + if (newline) + *newline = '\0'; + + return message; +} + +static GdkPixbuf * +gimp_statusbar_load_icon (GimpStatusbar *statusbar, + const gchar *icon_name) +{ + GdkPixbuf *icon; + + if (G_UNLIKELY (! statusbar->icon_hash)) + { + statusbar->icon_hash = + g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + } + + icon = g_hash_table_lookup (statusbar->icon_hash, icon_name); + + if (icon) + return g_object_ref (icon); + + icon = gimp_widget_load_icon (statusbar->label, icon_name, ICON_SIZE); + + /* this is not optimal but so what */ + if (g_hash_table_size (statusbar->icon_hash) > 16) + g_hash_table_remove_all (statusbar->icon_hash); + + g_hash_table_insert (statusbar->icon_hash, + g_strdup (icon_name), g_object_ref (icon)); + + return icon; +} diff --git a/app/display/gimpstatusbar.h b/app/display/gimpstatusbar.h new file mode 100644 index 0000000..7d5f279 --- /dev/null +++ b/app/display/gimpstatusbar.h @@ -0,0 +1,158 @@ +/* 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 . + */ + +#ifndef __GIMP_STATUSBAR_H__ +#define __GIMP_STATUSBAR_H__ + +G_BEGIN_DECLS + + +/* maximal length of the format string for the cursor-coordinates */ +#define CURSOR_FORMAT_LENGTH 32 + + +#define GIMP_TYPE_STATUSBAR (gimp_statusbar_get_type ()) +#define GIMP_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_STATUSBAR, GimpStatusbar)) +#define GIMP_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_STATUSBAR, GimpStatusbarClass)) +#define GIMP_IS_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_STATUSBAR)) +#define GIMP_IS_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_STATUSBAR)) +#define GIMP_STATUSBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_STATUSBAR, GimpStatusbarClass)) + +typedef struct _GimpStatusbarClass GimpStatusbarClass; + +struct _GimpStatusbar +{ + GtkStatusbar parent_instance; + + GimpDisplayShell *shell; + + GSList *messages; + GHashTable *context_ids; + guint seq_context_id; + + GdkPixbuf *icon; + GHashTable *icon_hash; + + guint temp_context_id; + guint temp_timeout_id; + GimpMessageSeverity temp_severity; + + gchar cursor_format_str[CURSOR_FORMAT_LENGTH]; + gchar cursor_format_str_f[CURSOR_FORMAT_LENGTH]; + gchar length_format_str[CURSOR_FORMAT_LENGTH]; + + GtkWidget *cursor_label; + GtkWidget *unit_combo; + GtkWidget *scale_combo; + GtkWidget *rotate_widget; + GtkWidget *rotate_label; + GtkWidget *horizontal_flip_icon; + GtkWidget *vertical_flip_icon; + GtkWidget *label; /* same as GtkStatusbar->label */ + + GtkWidget *progressbar; + GtkWidget *cancel_button; + gboolean progress_active; + gboolean progress_shown; + gdouble progress_value; + guint64 progress_last_update_time; +}; + +struct _GimpStatusbarClass +{ + GtkStatusbarClass parent_class; +}; + + +GType gimp_statusbar_get_type (void) G_GNUC_CONST; +GtkWidget * gimp_statusbar_new (void); + +void gimp_statusbar_set_shell (GimpStatusbar *statusbar, + GimpDisplayShell *shell); + +gboolean gimp_statusbar_get_visible (GimpStatusbar *statusbar); +void gimp_statusbar_set_visible (GimpStatusbar *statusbar, + gboolean visible); +void gimp_statusbar_empty (GimpStatusbar *statusbar); +void gimp_statusbar_fill (GimpStatusbar *statusbar); + +void gimp_statusbar_override_window_title (GimpStatusbar *statusbar); +void gimp_statusbar_restore_window_title (GimpStatusbar *statusbar); + +void gimp_statusbar_push (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + ...) G_GNUC_PRINTF (4, 5); +void gimp_statusbar_push_valist (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + va_list args) G_GNUC_PRINTF (4, 0); +void gimp_statusbar_push_coords (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + GimpCursorPrecision precision, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help); +void gimp_statusbar_push_length (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *title, + GimpOrientationType axis, + gdouble value, + const gchar *help); +void gimp_statusbar_replace (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + ...) G_GNUC_PRINTF (4, 5); +void gimp_statusbar_replace_valist (GimpStatusbar *statusbar, + const gchar *context, + const gchar *icon_name, + const gchar *format, + va_list args) G_GNUC_PRINTF (4, 0); +const gchar * gimp_statusbar_peek (GimpStatusbar *statusbar, + const gchar *context); +void gimp_statusbar_pop (GimpStatusbar *statusbar, + const gchar *context); + +void gimp_statusbar_push_temp (GimpStatusbar *statusbar, + GimpMessageSeverity severity, + const gchar *icon_name, + const gchar *format, + ...) G_GNUC_PRINTF (4, 5); +void gimp_statusbar_push_temp_valist (GimpStatusbar *statusbar, + GimpMessageSeverity severity, + const gchar *icon_name, + const gchar *format, + va_list args) G_GNUC_PRINTF (4, 0); +void gimp_statusbar_pop_temp (GimpStatusbar *statusbar); + +void gimp_statusbar_update_cursor (GimpStatusbar *statusbar, + GimpCursorPrecision precision, + gdouble x, + gdouble y); +void gimp_statusbar_clear_cursor (GimpStatusbar *statusbar); + + +G_END_DECLS + +#endif /* __GIMP_STATUSBAR_H__ */ diff --git a/app/display/gimptoolcompass.c b/app/display/gimptoolcompass.c new file mode 100644 index 0000000..3d9045b --- /dev/null +++ b/app/display/gimptoolcompass.c @@ -0,0 +1,1218 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolcompass.c + * Copyright (C) 2017 Michael Natterer + * + * Measure tool + * Copyright (C) 1999-2003 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpimage.h" +#include "core/gimpmarshal.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvasline.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayshell-utils.h" +#include "gimptoolcompass.h" + +#include "gimp-intl.h" + + +#define ARC_RADIUS 30 +#define ARC_GAP (ARC_RADIUS / 2) +#define EPSILON 1e-6 + + +/* possible measure functions */ +typedef enum +{ + CREATING, + ADDING, + MOVING, + MOVING_ALL, + GUIDING, + FINISHED +} CompassFunction; + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_N_POINTS, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_X3, + PROP_Y3, + PROP_PIXEL_ANGLE, + PROP_UNIT_ANGLE, + PROP_EFFECTIVE_ORIENTATION +}; + +enum +{ + CREATE_GUIDES, + LAST_SIGNAL +}; + +struct _GimpToolCompassPrivate +{ + GimpCompassOrientation orientation; + gint n_points; + gint x[3]; + gint y[3]; + + GimpVector2 radius1; + GimpVector2 radius2; + gdouble display_angle; + gdouble pixel_angle; + gdouble unit_angle; + GimpCompassOrientation effective_orientation; + + CompassFunction function; + gdouble mouse_x; + gdouble mouse_y; + gint last_x; + gint last_y; + gint point; + + GimpCanvasItem *line1; + GimpCanvasItem *line2; + GimpCanvasItem *arc; + GimpCanvasItem *arc_line; + GimpCanvasItem *handles[3]; +}; + + +/* local function prototypes */ + +static void gimp_tool_compass_constructed (GObject *object); +static void gimp_tool_compass_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_compass_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_compass_changed (GimpToolWidget *widget); +static gint gimp_tool_compass_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_compass_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_compass_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_compass_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_leave_notify (GimpToolWidget *widget); +static void gimp_tool_compass_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_compass_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static gint gimp_tool_compass_get_point (GimpToolCompass *compass, + const GimpCoords *coords); +static void gimp_tool_compass_update_hilight (GimpToolCompass *compass); +static void gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolCompass, gimp_tool_compass, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_compass_parent_class + +static guint compass_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_tool_compass_class_init (GimpToolCompassClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_compass_constructed; + object_class->set_property = gimp_tool_compass_set_property; + object_class->get_property = gimp_tool_compass_get_property; + + widget_class->changed = gimp_tool_compass_changed; + widget_class->button_press = gimp_tool_compass_button_press; + widget_class->button_release = gimp_tool_compass_button_release; + widget_class->motion = gimp_tool_compass_motion; + widget_class->hit = gimp_tool_compass_hit; + widget_class->hover = gimp_tool_compass_hover; + widget_class->leave_notify = gimp_tool_compass_leave_notify; + widget_class->motion_modifier = gimp_tool_compass_motion_modifier; + widget_class->get_cursor = gimp_tool_compass_get_cursor; + widget_class->update_on_scale = TRUE; + widget_class->update_on_rotate = TRUE; + + compass_signals[CREATE_GUIDES] = + g_signal_new ("create-guides", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolCompassClass, create_guides), + NULL, NULL, + gimp_marshal_VOID__INT_INT_BOOLEAN_BOOLEAN, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", NULL, NULL, + GIMP_TYPE_COMPASS_ORIENTATION, + GIMP_COMPASS_ORIENTATION_AUTO, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_N_POINTS, + g_param_spec_int ("n-points", NULL, NULL, + 1, 3, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_int ("x1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_int ("y1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_int ("x2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_int ("y2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X3, + g_param_spec_int ("x3", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y3, + g_param_spec_int ("y3", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIXEL_ANGLE, + g_param_spec_double ("pixel-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_UNIT_ANGLE, + g_param_spec_double ("unit-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_EFFECTIVE_ORIENTATION, + g_param_spec_enum ("effective-orientation", NULL, NULL, + GIMP_TYPE_COMPASS_ORIENTATION, + GIMP_COMPASS_ORIENTATION_AUTO, + GIMP_PARAM_READABLE)); +} + +static void +gimp_tool_compass_init (GimpToolCompass *compass) +{ + compass->private = gimp_tool_compass_get_instance_private (compass); + + compass->private->point = -1; +} + +static void +gimp_tool_compass_constructed (GObject *object) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolCompassPrivate *private = compass->private; + GimpCanvasGroup *stroke_group; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->line1 = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[1], + private->y[1]); + + private->line2 = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + + private->arc = gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + private->x[0], + private->y[0], + ARC_RADIUS * 2 + 1, + ARC_RADIUS * 2 + 1, + GIMP_HANDLE_ANCHOR_CENTER); + + private->arc_line = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[0] + 10, + private->y[0]); + + gimp_tool_widget_pop_group (widget); + + for (i = 0; i < 3; i++) + { + private->handles[i] = + gimp_tool_widget_add_handle (widget, + i == 0 ? + GIMP_HANDLE_CIRCLE : GIMP_HANDLE_CROSS, + private->x[i], + private->y[i], + GIMP_CANVAS_HANDLE_SIZE_CROSS, + GIMP_CANVAS_HANDLE_SIZE_CROSS, + GIMP_HANDLE_ANCHOR_CENTER); + } + + gimp_tool_compass_changed (widget); +} + +static void +gimp_tool_compass_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolCompassPrivate *private = compass->private; + + switch (property_id) + { + case PROP_ORIENTATION: + private->orientation = g_value_get_enum (value); + break; + case PROP_N_POINTS: + private->n_points = g_value_get_int (value); + break; + case PROP_X1: + private->x[0] = g_value_get_int (value); + break; + case PROP_Y1: + private->y[0] = g_value_get_int (value); + break; + case PROP_X2: + private->x[1] = g_value_get_int (value); + break; + case PROP_Y2: + private->y[1] = g_value_get_int (value); + break; + case PROP_X3: + private->x[2] = g_value_get_int (value); + break; + case PROP_Y3: + private->y[2] = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_compass_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolCompassPrivate *private = compass->private; + + switch (property_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, private->orientation); + break; + case PROP_N_POINTS: + g_value_set_int (value, private->n_points); + break; + case PROP_X1: + g_value_set_int (value, private->x[0]); + break; + case PROP_Y1: + g_value_set_int (value, private->y[0]); + break; + case PROP_X2: + g_value_set_int (value, private->x[1]); + break; + case PROP_Y2: + g_value_set_int (value, private->y[1]); + break; + case PROP_X3: + g_value_set_int (value, private->x[2]); + break; + case PROP_Y3: + g_value_set_int (value, private->y[2]); + break; + case PROP_PIXEL_ANGLE: + g_value_set_double (value, private->pixel_angle); + break; + case PROP_UNIT_ANGLE: + g_value_set_double (value, private->unit_angle); + break; + case PROP_EFFECTIVE_ORIENTATION: + g_value_set_enum (value, private->effective_orientation); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_compass_changed (GimpToolWidget *widget) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + gdouble angle1; + gdouble angle2; + gint draw_arc = 0; + gboolean draw_arc_line = FALSE; + gdouble arc_line_display_length; + gdouble arc_line_length; + + gimp_tool_compass_update_angle (compass, private->orientation, FALSE); + + angle1 = -atan2 (private->radius1.y * shell->scale_y, + private->radius1.x * shell->scale_x); + angle2 = -private->display_angle; + + gimp_canvas_line_set (private->line1, + private->x[0], + private->y[0], + private->x[1], + private->y[1]); + gimp_canvas_item_set_visible (private->line1, private->n_points > 1); + if (private->n_points > 1 && + gimp_canvas_item_transform_distance (private->line1, + private->x[0], + private->y[0], + private->x[1], + private->y[1]) > ARC_RADIUS) + { + draw_arc++; + } + + + arc_line_display_length = ARC_RADIUS + + (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1) + + ARC_GAP; + arc_line_length = arc_line_display_length / + hypot (private->radius2.x * shell->scale_x, + private->radius2.y * shell->scale_y); + + if (private->n_points > 2) + { + gdouble length = gimp_canvas_item_transform_distance (private->line2, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + + if (length > ARC_RADIUS) + { + draw_arc++; + draw_arc_line = TRUE; + + if (length > arc_line_display_length) + { + gimp_canvas_line_set ( + private->line2, + private->x[0] + private->radius2.x * arc_line_length, + private->y[0] + private->radius2.y * arc_line_length, + private->x[2], + private->y[2]); + gimp_canvas_item_set_visible (private->line2, TRUE); + } + else + { + gimp_canvas_item_set_visible (private->line2, FALSE); + } + } + else + { + gimp_canvas_line_set (private->line2, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + gimp_canvas_item_set_visible (private->line2, TRUE); + } + } + else + { + gimp_canvas_item_set_visible (private->line2, FALSE); + } + + gimp_canvas_handle_set_position (private->arc, + private->x[0], private->y[0]); + gimp_canvas_handle_set_angles (private->arc, angle1, angle2); + gimp_canvas_item_set_visible (private->arc, + private->n_points > 1 && + draw_arc == private->n_points - 1 && + fabs (angle2) > EPSILON); + + arc_line_length = (ARC_RADIUS + (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1)) / + hypot (private->radius2.x * shell->scale_x, + private->radius2.y * shell->scale_y); + + gimp_canvas_line_set (private->arc_line, + private->x[0], + private->y[0], + private->x[0] + private->radius2.x * arc_line_length, + private->y[0] + private->radius2.y * arc_line_length); + gimp_canvas_item_set_visible (private->arc_line, + (private->n_points == 2 || draw_arc_line) && + fabs (angle2) > EPSILON); + + gimp_canvas_handle_set_position (private->handles[0], + private->x[0], private->y[0]); + gimp_canvas_item_set_visible (private->handles[0], + private->n_points > 0); + + gimp_canvas_handle_set_position (private->handles[1], + private->x[1], private->y[1]); + gimp_canvas_item_set_visible (private->handles[1], + private->n_points > 1); + + gimp_canvas_handle_set_position (private->handles[2], + private->x[2], private->y[2]); + gimp_canvas_item_set_visible (private->handles[2], + private->n_points > 2); + + gimp_tool_compass_update_hilight (compass); +} + +gint +gimp_tool_compass_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + private->function = CREATING; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + /* if the cursor is in one of the handles, the new function will be + * moving or adding a new point or guide + */ + if (private->point != -1) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + + if (state & (toggle_mask | GDK_MOD1_MASK)) + { + gboolean create_hguide = (state & toggle_mask); + gboolean create_vguide = (state & GDK_MOD1_MASK); + + g_signal_emit (compass, compass_signals[CREATE_GUIDES], 0, + private->x[private->point], + private->y[private->point], + create_hguide, + create_vguide); + + private->function = GUIDING; + } + else + { + if (private->n_points == 1 || (state & extend_mask)) + private->function = ADDING; + else + private->function = MOVING; + } + } + + /* adding to the middle point makes no sense */ + if (private->point == 0 && + private->function == ADDING && + private->n_points == 3) + { + private->function = MOVING; + } + + /* if the function is still CREATING, we are outside the handles */ + if (private->function == CREATING) + { + if (private->n_points > 1 && (state & GDK_MOD1_MASK)) + { + private->function = MOVING_ALL; + + private->last_x = coords->x; + private->last_y = coords->y; + } + } + + if (private->function == CREATING) + { + /* set the first point and go into ADDING mode */ + g_object_set (compass, + "n-points", 1, + "x1", (gint) (coords->x + 0.5), + "y1", (gint) (coords->y + 0.5), + "x2", 0, + "y2", 0, + "x3", 0, + "y3", 0, + NULL); + + private->point = 0; + private->function = ADDING; + } + + return 1; +} + +void +gimp_tool_compass_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + private->function = FINISHED; +} + +void +gimp_tool_compass_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + gint new_n_points; + gint new_x[3]; + gint new_y[3]; + gint dx, dy; + gint tmp; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + /* A few comments here, because this routine looks quite weird at first ... + * + * The goal is to keep point 0, called the start point, to be + * always the one in the middle or, if there are only two points, + * the one that is fixed. The angle is then always measured at + * this point. + */ + + new_n_points = private->n_points; + new_x[0] = private->x[0]; + new_y[0] = private->y[0]; + new_x[1] = private->x[1]; + new_y[1] = private->y[1]; + new_x[2] = private->x[2]; + new_y[2] = private->y[2]; + + switch (private->function) + { + case ADDING: + switch (private->point) + { + case 0: + /* we are adding to the start point */ + break; + + case 1: + /* we are adding to the end point, make it the new start point */ + new_x[0] = private->x[1]; + new_y[0] = private->y[1]; + + new_x[1] = private->x[0]; + new_y[1] = private->y[0]; + break; + + case 2: + /* we are adding to the third point, make it the new start point */ + new_x[1] = private->x[0]; + new_y[1] = private->y[0]; + new_x[0] = private->x[2]; + new_y[0] = private->y[2]; + break; + + default: + break; + } + + new_n_points = MIN (new_n_points + 1, 3); + + private->point = new_n_points - 1; + private->function = MOVING; + /* don't break here! */ + + case MOVING: + /* if we are moving the start point and only have two, make it + * the end point + */ + if (new_n_points == 2 && private->point == 0) + { + tmp = new_x[0]; + new_x[0] = new_x[1]; + new_x[1] = tmp; + + tmp = new_y[0]; + new_y[0] = new_y[1]; + new_y[1] = tmp; + + private->point = 1; + } + + new_x[private->point] = ROUND (coords->x); + new_y[private->point] = ROUND (coords->y); + + if (state & gimp_get_constrain_behavior_mask ()) + { + gdouble x = new_x[private->point]; + gdouble y = new_y[private->point]; + + gimp_display_shell_constrain_line (gimp_tool_widget_get_shell (widget), + new_x[0], new_y[0], + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + + new_x[private->point] = ROUND (x); + new_y[private->point] = ROUND (y); + } + + g_object_set (compass, + "n-points", new_n_points, + "x1", new_x[0], + "y1", new_y[0], + "x2", new_x[1], + "y2", new_y[1], + "x3", new_x[2], + "y3", new_y[2], + NULL); + break; + + case MOVING_ALL: + dx = ROUND (coords->x) - private->last_x; + dy = ROUND (coords->y) - private->last_y; + + g_object_set (compass, + "x1", new_x[0] + dx, + "y1", new_y[0] + dy, + "x2", new_x[1] + dx, + "y2", new_y[1] + dy, + "x3", new_x[2] + dx, + "y3", new_y[2] + dy, + NULL); + + private->last_x = ROUND (coords->x); + private->last_y = ROUND (coords->y); + break; + + default: + break; + } +} + +GimpHit +gimp_tool_compass_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + + if (gimp_tool_compass_get_point (compass, coords) >= 0) + return GIMP_HIT_DIRECT; + else + return GIMP_HIT_INDIRECT; +} + +void +gimp_tool_compass_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + gint point; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + point = gimp_tool_compass_get_point (compass, coords); + + if (point >= 0) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + gchar *status; + + if (state & toggle_mask) + { + if (state & GDK_MOD1_MASK) + { + status = gimp_suggest_modifiers (_("Click to place " + "vertical and " + "horizontal guides"), + 0, + NULL, NULL, NULL); + } + else + { + status = gimp_suggest_modifiers (_("Click to place a " + "horizontal guide"), + GDK_MOD1_MASK & ~state, + NULL, NULL, NULL); + } + } + else if (state & GDK_MOD1_MASK) + { + status = gimp_suggest_modifiers (_("Click to place a " + "vertical guide"), + toggle_mask & ~state, + NULL, NULL, NULL); + } + else if ((state & extend_mask) && + ! ((point == 0) && (private->n_points == 3))) + { + status = gimp_suggest_modifiers (_("Click-Drag to add a " + "new point"), + (toggle_mask | + GDK_MOD1_MASK) & ~state, + NULL, NULL, NULL); + } + else + { + if ((point == 0) && (private->n_points == 3)) + state |= extend_mask; + + status = gimp_suggest_modifiers (_("Click-Drag to move this " + "point"), + (extend_mask | + toggle_mask | + GDK_MOD1_MASK) & ~state, + NULL, NULL, NULL); + } + + gimp_tool_widget_set_status (widget, status); + + g_free (status); + } + else + { + if ((private->n_points > 1) && (state & GDK_MOD1_MASK)) + { + gimp_tool_widget_set_status (widget, + _("Click-Drag to move all points")); + } + else + { + gimp_tool_widget_set_status (widget, NULL); + } + } + + if (point != private->point) + { + private->point = point; + + gimp_tool_compass_update_hilight (compass); + } +} + +void +gimp_tool_compass_leave_notify (GimpToolWidget *widget) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (private->point != -1) + { + private->point = -1; + + gimp_tool_compass_update_hilight (compass); + } + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static void +gimp_tool_compass_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (key == gimp_get_constrain_behavior_mask () && + private->function == MOVING) + { + gint new_x[3]; + gint new_y[3]; + gdouble x = private->mouse_x; + gdouble y = private->mouse_y; + + new_x[0] = private->x[0]; + new_y[0] = private->y[0]; + new_x[1] = private->x[1]; + new_y[1] = private->y[1]; + new_x[2] = private->x[2]; + new_y[2] = private->y[2]; + + if (press) + { + gimp_display_shell_constrain_line (gimp_tool_widget_get_shell (widget), + private->x[0], private->y[0], + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + + new_x[private->point] = ROUND (x); + new_y[private->point] = ROUND (y); + + g_object_set (compass, + "x1", new_x[0], + "y1", new_y[0], + "x2", new_x[1], + "y2", new_y[1], + "x3", new_x[2], + "y3", new_y[2], + NULL); + } +} + +static gboolean +gimp_tool_compass_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (private->point != -1) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + + if (state & toggle_mask) + { + if (state & GDK_MOD1_MASK) + { + *cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT; + return TRUE; + } + else + { + *cursor = GIMP_CURSOR_SIDE_BOTTOM; + return TRUE; + } + } + else if (state & GDK_MOD1_MASK) + { + *cursor = GIMP_CURSOR_SIDE_RIGHT; + return TRUE; + } + else if ((state & extend_mask) && + ! ((private->point == 0) && + (private->n_points == 3))) + { + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + return TRUE; + } + else + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + return TRUE; + } + } + else + { + if ((private->n_points > 1) && (state & GDK_MOD1_MASK)) + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + return TRUE; + } + } + + return FALSE; +} + +static gint +gimp_tool_compass_get_point (GimpToolCompass *compass, + const GimpCoords *coords) +{ + GimpToolCompassPrivate *private = compass->private; + gint i; + + for (i = 0; i < private->n_points; i++) + { + if (gimp_canvas_item_hit (private->handles[i], + coords->x, coords->y)) + { + return i; + } + } + + return -1; +} + +static void +gimp_tool_compass_update_hilight (GimpToolCompass *compass) +{ + GimpToolCompassPrivate *private = compass->private; + gint i; + + for (i = 0; i < private->n_points; i++) + { + if (private->handles[i]) + { + gimp_canvas_item_set_highlight (private->handles[i], + private->point == i); + } + } +} + +static void +gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (compass); + GimpToolCompassPrivate *private = compass->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpImage *image = gimp_display_get_image (shell->display); + GimpVector2 radius1; + GimpVector2 radius2; + gdouble pixel_angle; + gdouble unit_angle; + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + private->radius1.x = private->x[1] - private->x[0]; + private->radius1.y = private->y[1] - private->y[0]; + + if (private->n_points == 3) + { + orientation = GIMP_COMPASS_ORIENTATION_AUTO; + + private->radius2.x = private->x[2] - private->x[0]; + private->radius2.y = private->y[2] - private->y[0]; + } + else + { + gdouble angle = -shell->rotate_angle * G_PI / 180.0; + + if (orientation == GIMP_COMPASS_ORIENTATION_VERTICAL) + angle -= G_PI / 2.0; + + if (flip) + angle += G_PI; + + if (shell->flip_horizontally) + angle = G_PI - angle; + if (shell->flip_vertically) + angle = -angle; + + private->radius2.x = cos (angle); + private->radius2.y = sin (angle); + + if (! shell->dot_for_dot) + { + private->radius2.x *= xres; + private->radius2.y *= yres; + + gimp_vector2_normalize (&private->radius2); + } + } + + radius1 = private->radius1; + radius2 = private->radius2; + + pixel_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + radius1.x /= xres; + radius1.y /= yres; + + radius2.x /= xres; + radius2.y /= yres; + + unit_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + if (shell->dot_for_dot) + private->display_angle = pixel_angle; + else + private->display_angle = unit_angle; + + if (private->n_points == 2) + { + if (! flip && fabs (private->display_angle) > G_PI / 2.0 + EPSILON) + { + gimp_tool_compass_update_angle (compass, orientation, TRUE); + + return; + } + else if (orientation == GIMP_COMPASS_ORIENTATION_AUTO) + { + if (fabs (private->display_angle) <= G_PI / 4.0 + EPSILON) + { + orientation = GIMP_COMPASS_ORIENTATION_HORIZONTAL; + } + else + { + gimp_tool_compass_update_angle (compass, + GIMP_COMPASS_ORIENTATION_VERTICAL, + FALSE); + + return; + } + } + } + + if (fabs (pixel_angle - private->pixel_angle) > EPSILON) + { + private->pixel_angle = pixel_angle; + + g_object_notify (G_OBJECT (compass), "pixel-angle"); + } + + if (fabs (unit_angle - private->unit_angle) > EPSILON) + { + private->unit_angle = unit_angle; + + g_object_notify (G_OBJECT (compass), "unit-angle"); + } + + if (orientation != private->effective_orientation) + { + private->effective_orientation = orientation; + + g_object_notify (G_OBJECT (compass), "effective-orientation"); + } +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_compass_new (GimpDisplayShell *shell, + GimpCompassOrientation orientation, + gint n_points, + gint x1, + gint y1, + gint x2, + gint y2, + gint x3, + gint y3) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_COMPASS, + "shell", shell, + "orientation", orientation, + "n-points", n_points, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} diff --git a/app/display/gimptoolcompass.h b/app/display/gimptoolcompass.h new file mode 100644 index 0000000..8e6fdfe --- /dev/null +++ b/app/display/gimptoolcompass.h @@ -0,0 +1,72 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolcompass.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_COMPASS_H__ +#define __GIMP_TOOL_COMPASS_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_COMPASS (gimp_tool_compass_get_type ()) +#define GIMP_TOOL_COMPASS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_COMPASS, GimpToolCompass)) +#define GIMP_TOOL_COMPASS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_COMPASS, GimpToolCompassClass)) +#define GIMP_IS_TOOL_COMPASS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_COMPASS)) +#define GIMP_IS_TOOL_COMPASS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_COMPASS)) +#define GIMP_TOOL_COMPASS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_COMPASS, GimpToolCompassClass)) + + +typedef struct _GimpToolCompass GimpToolCompass; +typedef struct _GimpToolCompassPrivate GimpToolCompassPrivate; +typedef struct _GimpToolCompassClass GimpToolCompassClass; + +struct _GimpToolCompass +{ + GimpToolWidget parent_instance; + + GimpToolCompassPrivate *private; +}; + +struct _GimpToolCompassClass +{ + GimpToolWidgetClass parent_class; + + void (* create_guides) (GimpToolCompass *compass, + gint x, + gint y, + gboolean horizontal, + gboolean vertical); +}; + + +GType gimp_tool_compass_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_compass_new (GimpDisplayShell *shell, + GimpCompassOrientation orinetation, + gint n_points, + gint x1, + gint y1, + gint x2, + gint y2, + gint y3, + gint x3); + + +#endif /* __GIMP_TOOL_COMPASS_H__ */ diff --git a/app/display/gimptooldialog.c b/app/display/gimptooldialog.c new file mode 100644 index 0000000..e0bc082 --- /dev/null +++ b/app/display/gimptooldialog.c @@ -0,0 +1,208 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptooldialog.c + * Copyright (C) 2003 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimpobject.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimpdialogfactory.h" + +#include "gimpdisplayshell.h" +#include "gimptooldialog.h" + + +typedef struct _GimpToolDialogPrivate GimpToolDialogPrivate; + +struct _GimpToolDialogPrivate +{ + GimpDisplayShell *shell; +}; + +#define GET_PRIVATE(dialog) ((GimpToolDialogPrivate *) gimp_tool_dialog_get_instance_private ((GimpToolDialog *) (dialog))) + + +static void gimp_tool_dialog_dispose (GObject *object); + +static void gimp_tool_dialog_shell_unmap (GimpDisplayShell *shell, + GimpToolDialog *dialog); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolDialog, gimp_tool_dialog, + GIMP_TYPE_VIEWABLE_DIALOG) + + +static void +gimp_tool_dialog_class_init (GimpToolDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_tool_dialog_dispose; +} + +static void +gimp_tool_dialog_init (GimpToolDialog *dialog) +{ +} + +static void +gimp_tool_dialog_dispose (GObject *object) +{ + GimpToolDialogPrivate *private = GET_PRIVATE (object); + + if (private->shell) + { + g_object_remove_weak_pointer (G_OBJECT (private->shell), + (gpointer) &private->shell); + private->shell = NULL; + } + + G_OBJECT_CLASS (gimp_tool_dialog_parent_class)->dispose (object); +} + + +/** + * gimp_tool_dialog_new: + * @tool_info: a #GimpToolInfo + * @desc: a string to use in the dialog header or %NULL to use the help + * field from #GimpToolInfo + * @...: a %NULL-terminated valist of button parameters as described in + * gtk_dialog_new_with_buttons(). + * + * This function conveniently creates a #GimpViewableDialog using the + * information stored in @tool_info. It also registers the tool with + * the "toplevel" dialog factory. + * + * Return value: a new #GimpViewableDialog + **/ +GtkWidget * +gimp_tool_dialog_new (GimpToolInfo *tool_info, + GdkScreen *screen, + gint monitor, + const gchar *title, + const gchar *description, + const gchar *icon_name, + const gchar *help_id, + ...) +{ + GtkWidget *dialog; + gchar *identifier; + va_list args; + + g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); + + if (! title) + title = tool_info->label; + + if (! description) + description = tool_info->tooltip; + + if (! help_id) + help_id = tool_info->help_id; + + if (! icon_name) + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + + dialog = g_object_new (GIMP_TYPE_TOOL_DIALOG, + "title", title, + "role", gimp_object_get_name (tool_info), + "description", description, + "icon-name", icon_name, + "help-func", gimp_standard_help_func, + "help-id", help_id, + NULL); + + va_start (args, help_id); + gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args); + va_end (args); + + identifier = g_strconcat (gimp_object_get_name (tool_info), "-dialog", NULL); + + gimp_dialog_factory_add_foreign (gimp_dialog_factory_get_singleton (), + identifier, + dialog, + screen, + monitor); + + g_free (identifier); + + return dialog; +} + +void +gimp_tool_dialog_set_shell (GimpToolDialog *tool_dialog, + GimpDisplayShell *shell) +{ + GimpToolDialogPrivate *private = GET_PRIVATE (tool_dialog); + + g_return_if_fail (GIMP_IS_TOOL_DIALOG (tool_dialog)); + g_return_if_fail (shell == NULL || GIMP_IS_DISPLAY_SHELL (shell)); + + if (shell == private->shell) + return; + + if (private->shell) + { + g_object_remove_weak_pointer (G_OBJECT (private->shell), + (gpointer) &private->shell); + g_signal_handlers_disconnect_by_func (private->shell, + gimp_tool_dialog_shell_unmap, + tool_dialog); + + gtk_window_set_transient_for (GTK_WINDOW (tool_dialog), NULL); + } + + private->shell = shell; + + if (private->shell) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + gtk_window_set_transient_for (GTK_WINDOW (tool_dialog), + GTK_WINDOW (toplevel)); + + g_signal_connect_object (private->shell, "unmap", + G_CALLBACK (gimp_tool_dialog_shell_unmap), + tool_dialog, 0); + g_object_add_weak_pointer (G_OBJECT (private->shell), + (gpointer) &private->shell); + } +} + + +/* private functions */ + +static void +gimp_tool_dialog_shell_unmap (GimpDisplayShell *shell, + GimpToolDialog *dialog) +{ + /* the dialog being mapped while the shell is being unmapped + * happens when switching images in GimpImageWindow + */ + if (gtk_widget_get_mapped (GTK_WIDGET (dialog))) + g_signal_emit_by_name (dialog, "close"); +} diff --git a/app/display/gimptooldialog.h b/app/display/gimptooldialog.h new file mode 100644 index 0000000..13f437d --- /dev/null +++ b/app/display/gimptooldialog.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptooldialog.h + * Copyright (C) 2003 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 . + */ + +#ifndef __GIMP_TOOL_DIALOG_H__ +#define __GIMP_TOOL_DIALOG_H__ + +#include "widgets/gimpviewabledialog.h" + + +#define GIMP_TYPE_TOOL_DIALOG (gimp_tool_dialog_get_type ()) +#define GIMP_TOOL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_DIALOG, GimpToolDialog)) +#define GIMP_TOOL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_DIALOG, GimpToolDialogClass)) +#define GIMP_IS_TOOL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_DIALOG)) +#define GIMP_IS_TOOL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_DIALOG)) +#define GIMP_TOOL_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_DIALOG, GimpToolDialogClass)) + + +typedef struct _GimpViewableDialogClass GimpToolDialogClass; + +struct _GimpToolDialog +{ + GimpViewableDialog parent_instance; +}; + + +GType gimp_tool_dialog_get_type (void) G_GNUC_CONST; + +GtkWidget * gimp_tool_dialog_new (GimpToolInfo *tool_info, + GdkScreen *screen, + gint monitor, + const gchar *title, + const gchar *description, + const gchar *icon_name, + const gchar *help_id, + ...) G_GNUC_NULL_TERMINATED; + +void gimp_tool_dialog_set_shell (GimpToolDialog *tool_dialog, + GimpDisplayShell *shell); + + +#endif /* __GIMP_TOOL_DIALOG_H__ */ diff --git a/app/display/gimptoolfocus.c b/app/display/gimptoolfocus.c new file mode 100644 index 0000000..a607f06 --- /dev/null +++ b/app/display/gimptoolfocus.c @@ -0,0 +1,1209 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolfocus.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvasgroup.h" +#include "gimpcanvashandle.h" +#include "gimpcanvaslimit.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayshell-utils.h" +#include "gimptoolfocus.h" + +#include "gimp-intl.h" + + +#define HANDLE_SIZE 12.0 +#define SNAP_DISTANCE 12.0 + +#define EPSILON 1e-6 + + +enum +{ + PROP_0, + PROP_TYPE, + PROP_X, + PROP_Y, + PROP_RADIUS, + PROP_ASPECT_RATIO, + PROP_ANGLE, + PROP_INNER_LIMIT, + PROP_MIDPOINT +}; + +enum +{ + LIMIT_OUTER, + LIMIT_INNER, + LIMIT_MIDDLE, + + N_LIMITS +}; + + +typedef enum +{ + HOVER_NONE, + HOVER_LIMIT, + HOVER_HANDLE, + HOVER_MOVE, + HOVER_ROTATE +} Hover; + + +typedef struct +{ + GimpCanvasItem *item; + + GtkOrientation orientation; + GimpVector2 dir; +} GimpToolFocusHandle; + +typedef struct +{ + GimpCanvasGroup *group; + GimpCanvasItem *item; + + gint n_handles; + GimpToolFocusHandle handles[4]; +} GimpToolFocusLimit; + +struct _GimpToolFocusPrivate +{ + GimpLimitType type; + + gdouble x; + gdouble y; + gdouble radius; + gdouble aspect_ratio; + gdouble angle; + + gdouble inner_limit; + gdouble midpoint; + + GimpToolFocusLimit limits[N_LIMITS]; + + Hover hover; + gint hover_limit; + gint hover_handle; + GimpCanvasItem *hover_item; + + GimpCanvasItem *last_hover_item; + + gdouble saved_x; + gdouble saved_y; + gdouble saved_radius; + gdouble saved_aspect_ratio; + gdouble saved_angle; + + gdouble saved_inner_limit; + gdouble saved_midpoint; + + gdouble orig_x; + gdouble orig_y; +}; + + +/* local function prototypes */ + +static void gimp_tool_focus_constructed (GObject *object); +static void gimp_tool_focus_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_focus_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_focus_changed (GimpToolWidget *widget); +static gint gimp_tool_focus_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_focus_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_focus_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_focus_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_focus_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_focus_leave_notify (GimpToolWidget *widget); +static void gimp_tool_focus_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static void gimp_tool_focus_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_focus_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_focus_update_hover (GimpToolFocus *focus, + const GimpCoords *coords, + gboolean proximity); + +static void gimp_tool_focus_update_highlight (GimpToolFocus *focus); +static void gimp_tool_focus_update_status (GimpToolFocus *focus, + GdkModifierType state); + +static void gimp_tool_focus_save (GimpToolFocus *focus); +static void gimp_tool_focus_restore (GimpToolFocus *focus); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolFocus, gimp_tool_focus, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_focus_parent_class + + +/* private functions */ + +static void +gimp_tool_focus_class_init (GimpToolFocusClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_focus_constructed; + object_class->set_property = gimp_tool_focus_set_property; + object_class->get_property = gimp_tool_focus_get_property; + + widget_class->changed = gimp_tool_focus_changed; + widget_class->button_press = gimp_tool_focus_button_press; + widget_class->button_release = gimp_tool_focus_button_release; + widget_class->motion = gimp_tool_focus_motion; + widget_class->hit = gimp_tool_focus_hit; + widget_class->hover = gimp_tool_focus_hover; + widget_class->leave_notify = gimp_tool_focus_leave_notify; + widget_class->motion_modifier = gimp_tool_focus_motion_modifier; + widget_class->hover_modifier = gimp_tool_focus_hover_modifier; + widget_class->get_cursor = gimp_tool_focus_get_cursor; + widget_class->update_on_scale = TRUE; + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_LIMIT_TYPE, + GIMP_LIMIT_CIRCLE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RADIUS, + g_param_spec_double ("radius", NULL, NULL, + 0.0, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ASPECT_RATIO, + g_param_spec_double ("aspect-ratio", NULL, NULL, + -1.0, + +1.0, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE, + g_param_spec_double ("angle", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_INNER_LIMIT, + g_param_spec_double ("inner-limit", NULL, NULL, + 0.0, + 1.0, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_MIDPOINT, + g_param_spec_double ("midpoint", NULL, NULL, + 0.0, + 1.0, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_focus_init (GimpToolFocus *focus) +{ + GimpToolFocusPrivate *priv; + + priv = gimp_tool_focus_get_instance_private (focus); + + focus->priv = priv; + + priv->hover = HOVER_NONE; +} + +static void +gimp_tool_focus_constructed (GObject *object) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolFocusPrivate *priv = focus->priv; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + for (i = N_LIMITS - 1; i >= 0; i--) + { + priv->limits[i].group = gimp_tool_widget_add_group (widget); + + gimp_tool_widget_push_group (widget, priv->limits[i].group); + + priv->limits[i].item = gimp_tool_widget_add_limit ( + widget, + GIMP_LIMIT_CIRCLE, + 0.0, 0.0, + 0.0, + 1.0, + 0.0, + /* dashed = */ i == LIMIT_MIDDLE); + + if (i == LIMIT_OUTER || i == LIMIT_INNER) + { + gint j; + + priv->limits[i].n_handles = 4; + + for (j = priv->limits[i].n_handles - 1; j >= 0; j--) + { + priv->limits[i].handles[j].item = gimp_tool_widget_add_handle ( + widget, + GIMP_HANDLE_FILLED_CIRCLE, + 0.0, 0.0, HANDLE_SIZE, HANDLE_SIZE, + GIMP_HANDLE_ANCHOR_CENTER); + + priv->limits[i].handles[j].orientation = + j % 2 == 0 ? GTK_ORIENTATION_HORIZONTAL : + GTK_ORIENTATION_VERTICAL; + + priv->limits[i].handles[j].dir.x = cos (j * G_PI / 2.0); + priv->limits[i].handles[j].dir.y = sin (j * G_PI / 2.0); + } + } + + gimp_tool_widget_pop_group (widget); + } + + gimp_tool_focus_changed (widget); +} + +static void +gimp_tool_focus_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (object); + GimpToolFocusPrivate *priv = focus->priv; + + switch (property_id) + { + case PROP_TYPE: + priv->type = g_value_get_enum (value); + break; + + case PROP_X: + priv->x = g_value_get_double (value); + break; + + case PROP_Y: + priv->y = g_value_get_double (value); + break; + + case PROP_RADIUS: + priv->radius = g_value_get_double (value); + break; + + case PROP_ASPECT_RATIO: + priv->aspect_ratio = g_value_get_double (value); + break; + + case PROP_ANGLE: + priv->angle = g_value_get_double (value); + break; + + case PROP_INNER_LIMIT: + priv->inner_limit = g_value_get_double (value); + break; + + case PROP_MIDPOINT: + priv->midpoint = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_focus_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (object); + GimpToolFocusPrivate *priv = focus->priv; + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, priv->type); + break; + + case PROP_X: + g_value_set_double (value, priv->x); + break; + + case PROP_Y: + g_value_set_double (value, priv->y); + break; + + case PROP_RADIUS: + g_value_set_double (value, priv->radius); + break; + + case PROP_ASPECT_RATIO: + g_value_set_double (value, priv->aspect_ratio); + break; + + case PROP_ANGLE: + g_value_set_double (value, priv->angle); + break; + + case PROP_INNER_LIMIT: + g_value_set_double (value, priv->inner_limit); + break; + + case PROP_MIDPOINT: + g_value_set_double (value, priv->midpoint); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_focus_changed (GimpToolWidget *widget) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + GimpToolFocusPrivate *priv = focus->priv; + gint i; + + for (i = 0; i < N_LIMITS; i++) + { + gimp_canvas_item_begin_change (priv->limits[i].item); + + g_object_set (priv->limits[i].item, + "type", priv->type, + "x", priv->x, + "y", priv->y, + "aspect-ratio", priv->aspect_ratio, + "angle", priv->angle, + NULL); + } + + g_object_set (priv->limits[LIMIT_OUTER].item, + "radius", priv->radius, + NULL); + + g_object_set (priv->limits[LIMIT_INNER].item, + "radius", priv->radius * priv->inner_limit, + NULL); + + g_object_set (priv->limits[LIMIT_MIDDLE].item, + "radius", priv->radius * (priv->inner_limit + + (1.0 - priv->inner_limit) * + priv->midpoint), + NULL); + + for (i = 0; i < N_LIMITS; i++) + { + gdouble rx, ry; + gdouble max_r = 0.0; + gint j; + + gimp_canvas_limit_get_radii (GIMP_CANVAS_LIMIT (priv->limits[i].item), + &rx, &ry); + + for (j = 0; j < priv->limits[i].n_handles; j++) + { + GimpVector2 p = priv->limits[i].handles[j].dir; + gdouble r; + + p.x *= rx; + p.y *= ry; + + gimp_vector2_rotate (&p, -priv->angle); + + p.x += priv->x; + p.y += priv->y; + + gimp_canvas_handle_set_position (priv->limits[i].handles[j].item, + p.x, p.y); + + r = gimp_canvas_item_transform_distance ( + priv->limits[i].handles[j].item, + priv->x, priv->y, + p.x, p.y); + + max_r = MAX (max_r, r); + } + + for (j = 0; j < priv->limits[i].n_handles; j++) + { + gimp_canvas_item_set_visible (priv->limits[i].handles[j].item, + priv->type != GIMP_LIMIT_HORIZONTAL && + priv->type != GIMP_LIMIT_VERTICAL && + max_r >= 1.5 * HANDLE_SIZE); + } + + gimp_canvas_item_end_change (priv->limits[i].item); + } +} + +static gint +gimp_tool_focus_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + GimpToolFocusPrivate *priv = focus->priv; + + if (press_type == GIMP_BUTTON_PRESS_NORMAL) + { + gimp_tool_focus_save (focus); + + priv->orig_x = coords->x; + priv->orig_y = coords->y; + + return TRUE; + } + + return FALSE; +} + +static void +gimp_tool_focus_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + + if (release_type == GIMP_BUTTON_RELEASE_CANCEL) + gimp_tool_focus_restore (focus); +} + +static void +gimp_tool_focus_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + GimpToolFocusPrivate *priv = focus->priv; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + gboolean extend; + gboolean constrain; + + extend = state & gimp_get_extend_selection_mask (); + constrain = state & gimp_get_constrain_behavior_mask (); + + switch (priv->hover) + { + case HOVER_NONE: + break; + + case HOVER_LIMIT: + { + GimpCanvasItem *limit = priv->limits[priv->hover_limit].item; + gdouble radius; + gdouble outer_radius; + gdouble inner_radius; + gdouble x, y; + gdouble cx, cy; + + x = coords->x; + y = coords->y; + + gimp_canvas_limit_center_point (GIMP_CANVAS_LIMIT (limit), + x, y, + &cx, &cy); + + if (gimp_canvas_item_transform_distance (limit, + x, y, + cx, cy) <= SNAP_DISTANCE) + { + x = cx; + y = cy; + } + + if (fabs (fabs (priv->aspect_ratio) - 1.0) <= EPSILON) + { + if (priv->radius <= EPSILON) + { + g_object_set (focus, + "aspect-ratio", 0.0, + NULL); + } + else + { + break; + } + } + + radius = gimp_canvas_limit_boundary_radius (GIMP_CANVAS_LIMIT (limit), + x, y); + + outer_radius = priv->radius; + inner_radius = priv->radius * priv->inner_limit; + + switch (priv->hover_limit) + { + case LIMIT_OUTER: + { + outer_radius = radius; + + if (extend) + inner_radius = priv->inner_limit * radius; + else + outer_radius = MAX (outer_radius, inner_radius); + } + break; + + case LIMIT_INNER: + { + inner_radius = radius; + + if (extend) + { + if (priv->inner_limit > EPSILON) + outer_radius = inner_radius / priv->inner_limit; + else + inner_radius = 0.0; + } + else + { + inner_radius = MIN (inner_radius, outer_radius); + } + } + break; + + case LIMIT_MIDDLE: + { + if (extend) + { + if (priv->inner_limit > EPSILON || priv->midpoint > EPSILON) + { + outer_radius = radius / (priv->inner_limit + + (1.0 - priv->inner_limit) * + priv->midpoint); + inner_radius = priv->inner_limit * outer_radius; + } + else + { + radius = 0.0; + } + } + else + { + radius = CLAMP (radius, inner_radius, outer_radius); + } + + if (fabs (outer_radius - inner_radius) > EPSILON) + { + g_object_set (focus, + "midpoint", MAX ((radius - inner_radius) / + (outer_radius - inner_radius), + 0.0), + NULL); + } + } + break; + } + + g_object_set (focus, + "radius", outer_radius, + NULL); + + if (outer_radius > EPSILON) + { + g_object_set (focus, + "inner-limit", inner_radius / outer_radius, + NULL); + } + } + break; + + case HOVER_HANDLE: + { + GimpToolFocusHandle *handle; + GimpVector2 e; + GimpVector2 s; + GimpVector2 p; + gdouble rx, ry; + gdouble r; + + handle = &priv->limits[priv->hover_limit].handles[priv->hover_handle]; + + e = handle->dir; + + gimp_vector2_rotate (&e, -priv->angle); + + s = e; + + gimp_canvas_limit_get_radii ( + GIMP_CANVAS_LIMIT (priv->limits[priv->hover_limit].item), + &rx, &ry); + + if (handle->orientation == GTK_ORIENTATION_HORIZONTAL) + gimp_vector2_mul (&s, ry); + else + gimp_vector2_mul (&s, rx); + + p.x = coords->x - priv->x; + p.y = coords->y - priv->y; + + r = gimp_vector2_inner_product (&p, &e); + r = MAX (r, 0.0); + + p = e; + + gimp_vector2_mul (&p, r); + + if (extend) + { + if (handle->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (rx <= EPSILON && ry > EPSILON) + break; + + ry = r * (1.0 - priv->aspect_ratio); + } + else + { + if (ry <= EPSILON && rx > EPSILON) + break; + + rx = r * (1.0 + priv->aspect_ratio); + } + } + else + { + if (gimp_canvas_item_transform_distance ( + priv->limits[priv->hover_limit].item, + s.x, s.y, + p.x, p.y) <= SNAP_DISTANCE * 0.75) + { + if (handle->orientation == GTK_ORIENTATION_HORIZONTAL) + r = ry; + else + r = rx; + } + } + + if (handle->orientation == GTK_ORIENTATION_HORIZONTAL) + rx = r; + else + ry = r; + + r = MAX (rx, ry); + + if (priv->hover_limit == LIMIT_INNER) + r /= priv->inner_limit; + + g_object_set (focus, + "radius", r, + NULL); + + if (! extend) + { + gdouble aspect_ratio; + + if (fabs (rx - ry) <= EPSILON) + aspect_ratio = 0.0; + else if (rx > ry) + aspect_ratio = 1.0 - ry / rx; + else + aspect_ratio = rx / ry - 1.0; + + g_object_set (focus, + "aspect-ratio", aspect_ratio, + NULL); + } + } + break; + + case HOVER_MOVE: + g_object_set (focus, + "x", priv->saved_x + (coords->x - priv->orig_x), + "y", priv->saved_y + (coords->y - priv->orig_y), + NULL); + break; + + case HOVER_ROTATE: + { + gdouble angle; + gdouble orig_angle; + + angle = atan2 (coords->y - priv->y, coords->x - priv->x); + orig_angle = atan2 (priv->orig_y - priv->y, priv->orig_x - priv->x); + + angle = priv->saved_angle + (angle - orig_angle); + + if (constrain) + angle = gimp_display_shell_constrain_angle (shell, angle, 12); + + g_object_set (focus, + "angle", angle, + NULL); + } + break; + } +} + +static GimpHit +gimp_tool_focus_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + GimpToolFocusPrivate *priv = focus->priv; + + gimp_tool_focus_update_hover (focus, coords, proximity); + + switch (priv->hover) + { + case HOVER_NONE: + return GIMP_HIT_NONE; + + case HOVER_LIMIT: + case HOVER_HANDLE: + return GIMP_HIT_DIRECT; + + case HOVER_MOVE: + case HOVER_ROTATE: + return GIMP_HIT_INDIRECT; + } + + g_return_val_if_reached (GIMP_HIT_NONE); +} + +static void +gimp_tool_focus_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + + gimp_tool_focus_update_hover (focus, coords, proximity); + + gimp_tool_focus_update_highlight (focus); + gimp_tool_focus_update_status (focus, state); +} + +static void +gimp_tool_focus_leave_notify (GimpToolWidget *widget) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + + gimp_tool_focus_update_hover (focus, NULL, FALSE); + + gimp_tool_focus_update_highlight (focus); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static void +gimp_tool_focus_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + + gimp_tool_focus_update_status (focus, state); +} + +static void +gimp_tool_focus_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + + gimp_tool_focus_update_status (focus, state); +} + +static gboolean +gimp_tool_focus_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolFocus *focus = GIMP_TOOL_FOCUS (widget); + GimpToolFocusPrivate *priv = focus->priv; + + switch (priv->hover) + { + case HOVER_NONE: + return FALSE; + + case HOVER_LIMIT: + case HOVER_HANDLE: + *modifier = GIMP_CURSOR_MODIFIER_RESIZE; + return TRUE; + + case HOVER_MOVE: + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + return TRUE; + + case HOVER_ROTATE: + *modifier = GIMP_CURSOR_MODIFIER_ROTATE; + return TRUE; + } + + g_return_val_if_reached (FALSE); +} + +static void +gimp_tool_focus_update_hover (GimpToolFocus *focus, + const GimpCoords *coords, + gboolean proximity) +{ + GimpToolFocusPrivate *priv = focus->priv; + gdouble min_handle_dist = HANDLE_SIZE; + gint i; + + priv->hover = HOVER_NONE; + priv->hover_item = NULL; + + if (! proximity) + return; + + for (i = 0; i < N_LIMITS; i++) + { + gint j; + + for (j = 0; j < priv->limits[i].n_handles; j++) + { + GimpCanvasItem *handle = priv->limits[i].handles[j].item; + + if (gimp_canvas_item_get_visible (handle)) + { + gdouble x, y; + gdouble dist; + + g_object_get (handle, + "x", &x, + "y", &y, + NULL); + + dist = gimp_canvas_item_transform_distance (handle, + x, y, + coords->x, coords->y); + + if (dist < min_handle_dist) + { + min_handle_dist = dist; + + priv->hover = HOVER_HANDLE; + priv->hover_limit = i; + priv->hover_handle = j; + priv->hover_item = handle; + } + } + } + } + + if (priv->hover != HOVER_NONE) + return; + + if ( gimp_canvas_limit_is_inside ( + GIMP_CANVAS_LIMIT (priv->limits[LIMIT_OUTER].item), + coords->x, coords->y) && + ! gimp_canvas_limit_is_inside ( + GIMP_CANVAS_LIMIT (priv->limits[LIMIT_INNER].item), + coords->x, coords->y)) + { + if (gimp_canvas_item_hit (priv->limits[LIMIT_MIDDLE].item, + coords->x, coords->y)) + { + priv->hover = HOVER_LIMIT; + priv->hover_limit = LIMIT_MIDDLE; + priv->hover_item = priv->limits[LIMIT_MIDDLE].item; + + return; + } + } + + if (! gimp_canvas_limit_is_inside ( + GIMP_CANVAS_LIMIT (priv->limits[LIMIT_INNER].item), + coords->x, coords->y)) + { + if (gimp_canvas_item_hit (priv->limits[LIMIT_OUTER].item, + coords->x, coords->y)) + { + priv->hover = HOVER_LIMIT; + priv->hover_limit = LIMIT_OUTER; + priv->hover_item = priv->limits[LIMIT_OUTER].item; + + return; + } + } + + if (gimp_canvas_item_hit (priv->limits[LIMIT_INNER].item, + coords->x, coords->y)) + { + priv->hover = HOVER_LIMIT; + priv->hover_limit = LIMIT_INNER; + priv->hover_item = priv->limits[LIMIT_INNER].item; + + return; + } + + if (gimp_canvas_limit_is_inside ( + GIMP_CANVAS_LIMIT (priv->limits[LIMIT_OUTER].item), + coords->x, coords->y)) + { + priv->hover = HOVER_MOVE; + } + else + { + priv->hover = HOVER_ROTATE; + } +} + +static void +gimp_tool_focus_update_highlight (GimpToolFocus *focus) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (focus); + GimpToolFocusPrivate *priv = focus->priv; + gint i; + + if (priv->hover_item == priv->last_hover_item) + return; + + if (priv->last_hover_item) + gimp_canvas_item_set_highlight (priv->last_hover_item, FALSE); + + #define RAISE_ITEM(item) \ + G_STMT_START \ + { \ + g_object_ref (item); \ + \ + gimp_tool_widget_remove_item (widget, item); \ + gimp_tool_widget_add_item (widget, item); \ + \ + g_object_unref (item); \ + } \ + G_STMT_END + + for (i = N_LIMITS - 1; i >= 0; i--) + RAISE_ITEM (GIMP_CANVAS_ITEM (priv->limits[i].group)); + + if (priv->hover_item) + { + gimp_canvas_item_set_highlight (priv->hover_item, TRUE); + + RAISE_ITEM (GIMP_CANVAS_ITEM (priv->limits[priv->hover_limit].group)); + } + + #undef RAISE_ITEM + + priv->last_hover_item = priv->hover_item; +} + +static void +gimp_tool_focus_update_status (GimpToolFocus *focus, + GdkModifierType state) +{ + GimpToolFocusPrivate *priv = focus->priv; + GdkModifierType state_mask = 0; + const gchar *message = NULL; + const gchar *extend_selection_format = NULL; + const gchar *toggle_behavior_format = NULL; + gchar *status; + + switch (priv->hover) + { + case HOVER_NONE: + break; + + case HOVER_LIMIT: + if (! (state & gimp_get_extend_selection_mask ())) + { + if (priv->hover_limit == LIMIT_MIDDLE) + message = _("Click-Drag to change the midpoint"); + else + message = _("Click-Drag to resize the limit"); + + extend_selection_format = _("%s to resize the focus"); + state_mask |= gimp_get_extend_selection_mask (); + } + else + { + message = _("Click-Drag to resize the focus"); + } + break; + + case HOVER_HANDLE: + if (! (state & gimp_get_extend_selection_mask ())) + { + message = _("Click-Drag to change the aspect ratio"); + extend_selection_format = _("%s to resize the focus"); + state_mask |= gimp_get_extend_selection_mask (); + } + else + { + message = _("Click-Drag to resize the focus"); + } + break; + + case HOVER_MOVE: + message = _("Click-Drag to move the focus"); + break; + + case HOVER_ROTATE: + message = _("Click-Drag to rotate the focus"); + toggle_behavior_format = _("%s for constrained angles"); + state_mask |= gimp_get_constrain_behavior_mask (); + break; + } + + status = gimp_suggest_modifiers (message, + ~state & state_mask, + extend_selection_format, + toggle_behavior_format, + NULL); + + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (focus), status); + + g_free (status); +} + +static void +gimp_tool_focus_save (GimpToolFocus *focus) +{ + GimpToolFocusPrivate *priv = focus->priv; + + priv->saved_x = priv->x; + priv->saved_y = priv->y; + priv->saved_radius = priv->radius; + priv->saved_aspect_ratio = priv->aspect_ratio; + priv->saved_angle = priv->angle; + + priv->saved_inner_limit = priv->inner_limit; + priv->saved_midpoint = priv->midpoint; +} + +static void +gimp_tool_focus_restore (GimpToolFocus *focus) +{ + GimpToolFocusPrivate *priv = focus->priv; + + g_object_set (focus, + "x", priv->saved_x, + "y", priv->saved_y, + "radius", priv->saved_radius, + "aspect-ratio", priv->saved_aspect_ratio, + "angle", priv->saved_angle, + + "inner_limit", priv->saved_inner_limit, + "midpoint", priv->saved_midpoint, + + NULL); +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_focus_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_FOCUS, + "shell", shell, + NULL); +} diff --git a/app/display/gimptoolfocus.h b/app/display/gimptoolfocus.h new file mode 100644 index 0000000..e2fc5bd --- /dev/null +++ b/app/display/gimptoolfocus.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolfocus.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_TOOL_FOCUS_H__ +#define __GIMP_TOOL_FOCUS_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_FOCUS (gimp_tool_focus_get_type ()) +#define GIMP_TOOL_FOCUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_FOCUS, GimpToolFocus)) +#define GIMP_TOOL_FOCUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_FOCUS, GimpToolFocusClass)) +#define GIMP_IS_TOOL_FOCUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_FOCUS)) +#define GIMP_IS_TOOL_FOCUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_FOCUS)) +#define GIMP_TOOL_FOCUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_FOCUS, GimpToolFocusClass)) + + +typedef struct _GimpToolFocus GimpToolFocus; +typedef struct _GimpToolFocusPrivate GimpToolFocusPrivate; +typedef struct _GimpToolFocusClass GimpToolFocusClass; + +struct _GimpToolFocus +{ + GimpToolWidget parent_instance; + + GimpToolFocusPrivate *priv; +}; + +struct _GimpToolFocusClass +{ + GimpToolWidgetClass parent_class; +}; + + +GType gimp_tool_focus_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_focus_new (GimpDisplayShell *shell); + + +#endif /* __GIMP_TOOL_FOCUS_H__ */ diff --git a/app/display/gimptoolgui.c b/app/display/gimptoolgui.c new file mode 100644 index 0000000..28c40bf --- /dev/null +++ b/app/display/gimptoolgui.c @@ -0,0 +1,1037 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgui.c + * Copyright (C) 2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "display-types.h" + +#include "core/gimpcontext.h" +#include "core/gimpmarshal.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpoverlaybox.h" +#include "widgets/gimpoverlaydialog.h" +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplayshell.h" +#include "gimptooldialog.h" +#include "gimptoolgui.h" + + +enum +{ + RESPONSE, + LAST_SIGNAL +}; + + +typedef struct _ResponseEntry ResponseEntry; + +struct _ResponseEntry +{ + gint response_id; + gchar *button_text; + gint alternative_position; + gboolean sensitive; +}; + +typedef struct _GimpToolGuiPrivate GimpToolGuiPrivate; + +struct _GimpToolGuiPrivate +{ + GimpToolInfo *tool_info; + gchar *title; + gchar *description; + gchar *icon_name; + gchar *help_id; + GList *response_entries; + gint default_response; + gboolean focus_on_map; + + gboolean overlay; + gboolean auto_overlay; + + GimpDisplayShell *shell; + GimpViewable *viewable; + + GtkWidget *dialog; + GtkWidget *vbox; +}; + +#define GET_PRIVATE(gui) ((GimpToolGuiPrivate *) gimp_tool_gui_get_instance_private ((GimpToolGui *) (gui))) + + +static void gimp_tool_gui_dispose (GObject *object); +static void gimp_tool_gui_finalize (GObject *object); + +static void gimp_tool_gui_create_dialog (GimpToolGui *gui, + GdkScreen *screen, + gint monitor); +static void gimp_tool_gui_add_dialog_button (GimpToolGui *gui, + ResponseEntry *entry); +static void gimp_tool_gui_update_buttons (GimpToolGui *gui); +static void gimp_tool_gui_update_shell (GimpToolGui *gui); +static void gimp_tool_gui_update_viewable (GimpToolGui *gui); + +static void gimp_tool_gui_dialog_response (GtkWidget *dialog, + gint response_id, + GimpToolGui *gui); +static void gimp_tool_gui_canvas_resized (GtkWidget *canvas, + GtkAllocation *allocation, + GimpToolGui *gui); + +static ResponseEntry * response_entry_new (gint response_id, + const gchar *button_text); +static void response_entry_free (ResponseEntry *entry); +static ResponseEntry * response_entry_find (GList *entries, + gint response_id); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolGui, gimp_tool_gui, GIMP_TYPE_OBJECT) + +static guint signals[LAST_SIGNAL] = { 0, }; + +#define parent_class gimp_tool_gui_parent_class + + +static void +gimp_tool_gui_class_init (GimpToolGuiClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_tool_gui_dispose; + object_class->finalize = gimp_tool_gui_finalize; + + signals[RESPONSE] = + g_signal_new ("response", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpToolGuiClass, response), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); +} + +static void +gimp_tool_gui_init (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + + private->default_response = -1; + private->focus_on_map = TRUE; + + private->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + g_object_ref_sink (private->vbox); +} + +static void +gimp_tool_gui_dispose (GObject *object) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (object); + + g_clear_object (&private->tool_info); + + if (private->shell) + gimp_tool_gui_set_shell (GIMP_TOOL_GUI (object), NULL); + + if (private->viewable) + gimp_tool_gui_set_viewable (GIMP_TOOL_GUI (object), NULL); + + g_clear_object (&private->vbox); + + if (private->dialog) + { + if (gtk_widget_get_visible (private->dialog)) + gimp_tool_gui_hide (GIMP_TOOL_GUI (object)); + + if (private->overlay) + g_object_unref (private->dialog); + else + gtk_widget_destroy (private->dialog); + + private->dialog = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_tool_gui_finalize (GObject *object) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (object); + + g_clear_pointer (&private->title, g_free); + g_clear_pointer (&private->description, g_free); + g_clear_pointer (&private->icon_name, g_free); + g_clear_pointer (&private->help_id, g_free); + + if (private->response_entries) + { + g_list_free_full (private->response_entries, + (GDestroyNotify) response_entry_free); + private->response_entries = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +/** + * gimp_tool_gui_new: + * @tool_info: a #GimpToolInfo + * @description: a string to use in the gui header or %NULL to use the help + * field from #GimpToolInfo + * @...: a %NULL-terminated valist of button parameters as described in + * gtk_gui_new_with_buttons(). + * + * This function creates a #GimpToolGui using the information stored + * in @tool_info. + * + * Return value: a new #GimpToolGui + **/ +GimpToolGui * +gimp_tool_gui_new (GimpToolInfo *tool_info, + const gchar *title, + const gchar *description, + const gchar *icon_name, + const gchar *help_id, + GdkScreen *screen, + gint monitor, + gboolean overlay, + ...) +{ + GimpToolGui *gui; + GimpToolGuiPrivate *private; + va_list args; + + g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); + + gui = g_object_new (GIMP_TYPE_TOOL_GUI, NULL); + + private = GET_PRIVATE (gui); + + if (! title) + title = tool_info->label; + + if (! description) + description = tool_info->label; + + if (! icon_name) + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)); + + if (! help_id) + help_id = tool_info->help_id; + + private->tool_info = g_object_ref (tool_info); + private->title = g_strdup (title); + private->description = g_strdup (description); + private->icon_name = g_strdup (icon_name); + private->help_id = g_strdup (help_id); + private->overlay = overlay; + + va_start (args, overlay); + + gimp_tool_gui_add_buttons_valist (gui, args); + + va_end (args); + + gimp_tool_gui_create_dialog (gui, screen, monitor); + + return gui; +} + +void +gimp_tool_gui_set_title (GimpToolGui *gui, + const gchar *title) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (title == private->title) + return; + + g_free (private->title); + private->title = g_strdup (title); + + if (! title) + title = private->tool_info->label; + + g_object_set (private->dialog, "title", title, NULL); +} + +void +gimp_tool_gui_set_description (GimpToolGui *gui, + const gchar *description) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (description == private->description) + return; + + g_free (private->description); + private->description = g_strdup (description); + + if (! description) + description = private->tool_info->tooltip; + + if (private->overlay) + { + /* TODO */ + } + else + { + g_object_set (private->dialog, "description", description, NULL); + } +} + +void +gimp_tool_gui_set_icon_name (GimpToolGui *gui, + const gchar *icon_name) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (icon_name == private->icon_name) + return; + + g_free (private->icon_name); + private->icon_name = g_strdup (icon_name); + + if (! icon_name) + icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (private->tool_info)); + + g_object_set (private->dialog, "icon-name", icon_name, NULL); +} + +void +gimp_tool_gui_set_help_id (GimpToolGui *gui, + const gchar *help_id) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (help_id == private->help_id) + return; + + g_free (private->help_id); + private->help_id = g_strdup (help_id); + + if (! help_id) + help_id = private->tool_info->help_id; + + if (private->overlay) + { + /* TODO */ + } + else + { + g_object_set (private->dialog, "help-id", help_id, NULL); + } +} + +void +gimp_tool_gui_set_shell (GimpToolGui *gui, + GimpDisplayShell *shell) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + g_return_if_fail (shell == NULL || GIMP_IS_DISPLAY_SHELL (shell)); + + private = GET_PRIVATE (gui); + + if (shell == private->shell) + return; + + if (private->shell) + { + g_object_remove_weak_pointer (G_OBJECT (private->shell), + (gpointer) &private->shell); + g_signal_handlers_disconnect_by_func (private->shell->canvas, + gimp_tool_gui_canvas_resized, + gui); + } + + private->shell = shell; + + if (private->shell) + { + g_signal_connect (private->shell->canvas, "size-allocate", + G_CALLBACK (gimp_tool_gui_canvas_resized), + gui); + g_object_add_weak_pointer (G_OBJECT (private->shell), + (gpointer) &private->shell); + } + + gimp_tool_gui_update_shell (gui); +} + +void +gimp_tool_gui_set_viewable (GimpToolGui *gui, + GimpViewable *viewable) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + g_return_if_fail (viewable == NULL || GIMP_IS_VIEWABLE (viewable)); + + private = GET_PRIVATE (gui); + + if (private->viewable == viewable) + return; + + if (private->viewable) + g_object_remove_weak_pointer (G_OBJECT (private->viewable), + (gpointer) &private->viewable); + + private->viewable = viewable; + + if (private->viewable) + g_object_add_weak_pointer (G_OBJECT (private->viewable), + (gpointer) &private->viewable); + + gimp_tool_gui_update_viewable (gui); +} + +GtkWidget * +gimp_tool_gui_get_dialog (GimpToolGui *gui) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), NULL); + + return GET_PRIVATE (gui)->dialog; +} + +GtkWidget * +gimp_tool_gui_get_vbox (GimpToolGui *gui) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), NULL); + + return GET_PRIVATE (gui)->vbox; +} + +gboolean +gimp_tool_gui_get_visible (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private; + + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), FALSE); + + private = GET_PRIVATE (gui); + + if (private->overlay) + return gtk_widget_get_parent (private->dialog) != NULL; + else + return gtk_widget_get_visible (private->dialog); +} + +void +gimp_tool_gui_show (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + g_return_if_fail (private->shell != NULL); + + if (private->overlay) + { + if (! gtk_widget_get_parent (private->dialog)) + { + gimp_overlay_box_add_child (GIMP_OVERLAY_BOX (private->shell->canvas), + private->dialog, 1.0, 0.0); + gtk_widget_show (private->dialog); + } + } + else + { + if (gtk_widget_get_visible (private->dialog)) + gdk_window_show (gtk_widget_get_window (private->dialog)); + else + gtk_widget_show (private->dialog); + } +} + +void +gimp_tool_gui_hide (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (private->overlay) + { + if (gtk_widget_get_parent (private->dialog)) + { + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (private->dialog)), + private->dialog); + gtk_widget_hide (private->dialog); + } + } + else + { + if (gimp_dialog_factory_from_widget (private->dialog, NULL)) + { + gimp_dialog_factory_hide_dialog (private->dialog); + } + else + { + gtk_widget_hide (private->dialog); + } + } +} + +void +gimp_tool_gui_set_overlay (GimpToolGui *gui, + GdkScreen *screen, + gint monitor, + gboolean overlay) +{ + GimpToolGuiPrivate *private; + gboolean visible; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (private->overlay == overlay) + return; + + if (! private->dialog) + { + private->overlay = overlay; + return; + } + + visible = gtk_widget_get_visible (private->dialog); + + if (visible) + gimp_tool_gui_hide (gui); + + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (private->vbox)), + private->vbox); + + if (private->overlay) + g_object_unref (private->dialog); + else + gtk_widget_destroy (private->dialog); + + private->overlay = overlay; + + gimp_tool_gui_create_dialog (gui, screen, monitor); + + if (visible) + gimp_tool_gui_show (gui); +} + +gboolean +gimp_tool_gui_get_overlay (GimpToolGui *gui) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), FALSE); + + return GET_PRIVATE (gui)->overlay; +} + +void +gimp_tool_gui_set_auto_overlay (GimpToolGui *gui, + gboolean auto_overlay) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (private->auto_overlay != auto_overlay) + { + private->auto_overlay = auto_overlay; + + if (private->shell) + gimp_tool_gui_canvas_resized (private->shell->canvas, NULL, gui); + } +} + +gboolean +gimp_tool_gui_get_auto_overlay (GimpToolGui *gui) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), FALSE); + + return GET_PRIVATE (gui)->auto_overlay; +} + +void +gimp_tool_gui_set_focus_on_map (GimpToolGui *gui, + gboolean focus_on_map) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + if (private->focus_on_map == focus_on_map) + return; + + private->focus_on_map = focus_on_map ? TRUE : FALSE; + + if (! private->overlay) + { + gtk_window_set_focus_on_map (GTK_WINDOW (private->dialog), + private->focus_on_map); + } +} + +gboolean +gimp_tool_gui_get_focus_on_map (GimpToolGui *gui) +{ + g_return_val_if_fail (GIMP_IS_TOOL_GUI (gui), FALSE); + + return GET_PRIVATE (gui)->focus_on_map; +} + +void +gimp_tool_gui_add_buttons_valist (GimpToolGui *gui, + va_list args) +{ + const gchar *button_text; + gint response_id; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + while ((button_text = va_arg (args, const gchar *))) + { + response_id = va_arg (args, gint); + + gimp_tool_gui_add_button (gui, button_text, response_id); + } +} + +void +gimp_tool_gui_add_button (GimpToolGui *gui, + const gchar *button_text, + gint response_id) +{ + GimpToolGuiPrivate *private; + ResponseEntry *entry; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + g_return_if_fail (button_text != NULL); + + private = GET_PRIVATE (gui); + + entry = response_entry_new (response_id, button_text); + + private->response_entries = g_list_append (private->response_entries, + entry); + + if (private->dialog) + gimp_tool_gui_add_dialog_button (gui, entry); +} + +void +gimp_tool_gui_set_default_response (GimpToolGui *gui, + gint response_id) +{ + GimpToolGuiPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + g_return_if_fail (response_entry_find (private->response_entries, + response_id) != NULL); + + private->default_response = response_id; + + if (private->overlay) + { + gimp_overlay_dialog_set_default_response (GIMP_OVERLAY_DIALOG (private->dialog), + response_id); + } + else + { + gtk_dialog_set_default_response (GTK_DIALOG (private->dialog), + response_id); + } +} + +void +gimp_tool_gui_set_response_sensitive (GimpToolGui *gui, + gint response_id, + gboolean sensitive) +{ + GimpToolGuiPrivate *private; + ResponseEntry *entry; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + entry = response_entry_find (private->response_entries, response_id); + + if (! entry) + return; + + entry->sensitive = sensitive; + + if (private->overlay) + { + gimp_overlay_dialog_set_response_sensitive (GIMP_OVERLAY_DIALOG (private->dialog), + response_id, sensitive); + } + else + { + gtk_dialog_set_response_sensitive (GTK_DIALOG (private->dialog), + response_id, sensitive); + } +} + +void +gimp_tool_gui_set_alternative_button_order (GimpToolGui *gui, + ...) +{ + GimpToolGuiPrivate *private; + va_list args; + gint response_id; + gint i; + + g_return_if_fail (GIMP_IS_TOOL_GUI (gui)); + + private = GET_PRIVATE (gui); + + va_start (args, gui); + + for (response_id = va_arg (args, gint), i = 0; + response_id != -1; + response_id = va_arg (args, gint), i++) + { + ResponseEntry *entry = response_entry_find (private->response_entries, + response_id); + + if (entry) + entry->alternative_position = i; + } + + va_end (args); + + gimp_tool_gui_update_buttons (gui); +} + + +/* private functions */ + +static void +gimp_tool_gui_create_dialog (GimpToolGui *gui, + GdkScreen *screen, + gint monitor) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + GList *list; + + if (private->overlay) + { + private->dialog = gimp_overlay_dialog_new (private->tool_info, + private->description, + NULL); + g_object_ref_sink (private->dialog); + + for (list = private->response_entries; list; list = g_list_next (list)) + { + ResponseEntry *entry = list->data; + + gimp_tool_gui_add_dialog_button (gui, entry); + } + + if (private->default_response != -1) + gimp_overlay_dialog_set_default_response (GIMP_OVERLAY_DIALOG (private->dialog), + private->default_response); + + gtk_container_set_border_width (GTK_CONTAINER (private->dialog), 6); + + gtk_container_set_border_width (GTK_CONTAINER (private->vbox), 0); + gtk_container_add (GTK_CONTAINER (private->dialog), private->vbox); + gtk_widget_show (private->vbox); + } + else + { + private->dialog = gimp_tool_dialog_new (private->tool_info, + screen, monitor, + private->title, + private->description, + private->icon_name, + private->help_id, + NULL); + + for (list = private->response_entries; list; list = g_list_next (list)) + { + ResponseEntry *entry = list->data; + + gimp_tool_gui_add_dialog_button (gui, entry); + } + + if (private->default_response != -1) + gtk_dialog_set_default_response (GTK_DIALOG (private->dialog), + private->default_response); + + gtk_window_set_focus_on_map (GTK_WINDOW (private->dialog), + private->focus_on_map); + + gtk_container_set_border_width (GTK_CONTAINER (private->vbox), 6); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (private->dialog))), + private->vbox, TRUE, TRUE, 0); + gtk_widget_show (private->vbox); + } + + gimp_tool_gui_update_buttons (gui); + + if (private->shell) + gimp_tool_gui_update_shell (gui); + + if (private->viewable) + gimp_tool_gui_update_viewable (gui); + + g_signal_connect_object (private->dialog, "response", + G_CALLBACK (gimp_tool_gui_dialog_response), + G_OBJECT (gui), 0); +} + +static void +gimp_tool_gui_add_dialog_button (GimpToolGui *gui, + ResponseEntry *entry) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + + if (private->overlay) + { + gimp_overlay_dialog_add_button (GIMP_OVERLAY_DIALOG (private->dialog), + entry->button_text, + entry->response_id); + + if (! entry->sensitive) + { + gimp_overlay_dialog_set_response_sensitive ( + GIMP_OVERLAY_DIALOG (private->dialog), + entry->response_id, FALSE); + } + } + else + { + gimp_dialog_add_button (GIMP_DIALOG (private->dialog), + entry->button_text, + entry->response_id); + + if (! entry->sensitive) + gtk_dialog_set_response_sensitive (GTK_DIALOG (private->dialog), + entry->response_id, + FALSE); + } +} + +static void +gimp_tool_gui_update_buttons (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + GList *list; + gint *ids; + gint n_ids; + gint n_alternatives = 0; + gint i; + + n_ids = g_list_length (private->response_entries); + ids = g_new0 (gint, n_ids); + + for (list = private->response_entries, i = 0; + list; + list = g_list_next (list), i++) + { + ResponseEntry *entry = list->data; + + if (entry->alternative_position >= 0 && + entry->alternative_position < n_ids) + { + ids[entry->alternative_position] = entry->response_id; + n_alternatives++; + } + } + + if (n_ids == n_alternatives) + { + if (private->overlay) + { + gimp_overlay_dialog_set_alternative_button_order (GIMP_OVERLAY_DIALOG (private->dialog), + n_ids, ids); + } + else + { + gtk_dialog_set_alternative_button_order_from_array (GTK_DIALOG (private->dialog), + n_ids, ids); + } + } + + g_free (ids); +} + +static void +gimp_tool_gui_update_shell (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + + if (private->overlay) + { + if (gtk_widget_get_parent (private->dialog)) + { + gimp_tool_gui_hide (gui); + + if (private->shell) + gimp_tool_gui_show (gui); + } + } + else + { + gimp_tool_dialog_set_shell (GIMP_TOOL_DIALOG (private->dialog), + private->shell); + } +} + +static void +gimp_tool_gui_update_viewable (GimpToolGui *gui) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + + if (! private->overlay) + { + GimpContext *context = NULL; + + if (private->tool_info) + context = GIMP_CONTEXT (private->tool_info->tool_options); + + gimp_viewable_dialog_set_viewable (GIMP_VIEWABLE_DIALOG (private->dialog), + private->viewable, context); + } +} + +static void +gimp_tool_gui_dialog_response (GtkWidget *dialog, + gint response_id, + GimpToolGui *gui) +{ + if (response_id == GIMP_RESPONSE_DETACH) + { + gimp_tool_gui_set_auto_overlay (gui, FALSE); + gimp_tool_gui_set_overlay (gui, + gtk_widget_get_screen (dialog), + gimp_widget_get_monitor (dialog), + FALSE); + } + else + { + g_signal_emit (gui, signals[RESPONSE], 0, + response_id); + } +} + +static void +gimp_tool_gui_canvas_resized (GtkWidget *canvas, + GtkAllocation *unused, + GimpToolGui *gui) +{ + GimpToolGuiPrivate *private = GET_PRIVATE (gui); + + if (private->auto_overlay) + { + GtkRequisition requisition; + GtkAllocation allocation; + gboolean overlay = FALSE; + + gtk_widget_size_request (private->vbox, &requisition); + gtk_widget_get_allocation (canvas, &allocation); + + if (allocation.width > 2 * requisition.width && + allocation.height > 3 * requisition.height) + { + overlay = TRUE; + } + + gimp_tool_gui_set_overlay (gui, + gtk_widget_get_screen (private->dialog), + gimp_widget_get_monitor (private->dialog), + overlay); + } +} + +static ResponseEntry * +response_entry_new (gint response_id, + const gchar *button_text) +{ + ResponseEntry *entry = g_slice_new0 (ResponseEntry); + + entry->response_id = response_id; + entry->button_text = g_strdup (button_text); + entry->alternative_position = -1; + entry->sensitive = TRUE; + + return entry; +} + +static void +response_entry_free (ResponseEntry *entry) +{ + g_free (entry->button_text); + + g_slice_free (ResponseEntry, entry); +} + +static ResponseEntry * +response_entry_find (GList *entries, + gint response_id) +{ + for (; entries; entries = g_list_next (entries)) + { + ResponseEntry *entry = entries->data; + + if (entry->response_id == response_id) + return entry; + } + + return NULL; +} diff --git a/app/display/gimptoolgui.h b/app/display/gimptoolgui.h new file mode 100644 index 0000000..7b099a5 --- /dev/null +++ b/app/display/gimptoolgui.h @@ -0,0 +1,115 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgui.h + * Copyright (C) 2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_GUI_H__ +#define __GIMP_TOOL_GUI_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_TOOL_GUI (gimp_tool_gui_get_type ()) +#define GIMP_TOOL_GUI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_GUI, GimpToolGui)) +#define GIMP_TOOL_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_GUI, GimpToolGuiClass)) +#define GIMP_IS_TOOL_GUI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_GUI)) +#define GIMP_IS_TOOL_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_GUI)) +#define GIMP_TOOL_GUI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_GUI, GimpToolGuiClass)) + + +typedef struct _GimpToolGuiClass GimpToolGuiClass; + +struct _GimpToolGui +{ + GimpObject parent_instance; +}; + +struct _GimpToolGuiClass +{ + GimpObjectClass parent_instance; + + void (* response) (GimpToolGui *gui, + gint response_id); + +}; + + +GType gimp_tool_gui_get_type (void) G_GNUC_CONST; + +GimpToolGui * gimp_tool_gui_new (GimpToolInfo *tool_info, + const gchar *title, + const gchar *description, + const gchar *icon_name, + const gchar *help_id, + GdkScreen *screen, + gint monitor, + gboolean overlay, + ...) G_GNUC_NULL_TERMINATED; + +void gimp_tool_gui_set_title (GimpToolGui *gui, + const gchar *title); +void gimp_tool_gui_set_description (GimpToolGui *gui, + const gchar *description); +void gimp_tool_gui_set_icon_name (GimpToolGui *gui, + const gchar *icon_name); +void gimp_tool_gui_set_help_id (GimpToolGui *gui, + const gchar *help_id); + +void gimp_tool_gui_set_shell (GimpToolGui *gui, + GimpDisplayShell *shell); +void gimp_tool_gui_set_viewable (GimpToolGui *gui, + GimpViewable *viewable); + +GtkWidget * gimp_tool_gui_get_dialog (GimpToolGui *gui); +GtkWidget * gimp_tool_gui_get_vbox (GimpToolGui *gui); + +gboolean gimp_tool_gui_get_visible (GimpToolGui *gui); +void gimp_tool_gui_show (GimpToolGui *gui); +void gimp_tool_gui_hide (GimpToolGui *gui); + +void gimp_tool_gui_set_overlay (GimpToolGui *gui, + GdkScreen *screen, + gint monitor, + gboolean overlay); +gboolean gimp_tool_gui_get_overlay (GimpToolGui *gui); + +void gimp_tool_gui_set_auto_overlay (GimpToolGui *gui, + gboolean auto_overlay); +gboolean gimp_tool_gui_get_auto_overlay (GimpToolGui *gui); + +void gimp_tool_gui_set_focus_on_map (GimpToolGui *gui, + gboolean focus_on_map); +gboolean gimp_tool_gui_get_focus_on_map (GimpToolGui *gui); + + +void gimp_tool_gui_add_buttons_valist (GimpToolGui *gui, + va_list args); +void gimp_tool_gui_add_button (GimpToolGui *gui, + const gchar *button_text, + gint response_id); +void gimp_tool_gui_set_default_response (GimpToolGui *gui, + gint response_id); +void gimp_tool_gui_set_response_sensitive (GimpToolGui *gui, + gint response_id, + gboolean sensitive); +void gimp_tool_gui_set_alternative_button_order (GimpToolGui *gui, + ...); + + +#endif /* __GIMP_TOOL_GUI_H__ */ diff --git a/app/display/gimptoolgyroscope.c b/app/display/gimptoolgyroscope.c new file mode 100644 index 0000000..4d6b175 --- /dev/null +++ b/app/display/gimptoolgyroscope.c @@ -0,0 +1,879 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgyroscope.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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" +#include "gimptoolgyroscope.h" + +#include "gimp-intl.h" + + +#define EPSILON 1e-6 +#define DEG_TO_RAD (G_PI / 180.0) + + +typedef enum +{ + MODE_NONE, + MODE_PAN, + MODE_ROTATE, + MODE_ZOOM +} Mode; + +typedef enum +{ + CONSTRAINT_NONE, + CONSTRAINT_UNKNOWN, + CONSTRAINT_HORIZONTAL, + CONSTRAINT_VERTICAL +} Constraint; + +enum +{ + PROP_0, + PROP_YAW, + PROP_PITCH, + PROP_ROLL, + PROP_ZOOM, + PROP_INVERT, + PROP_SPEED, + PROP_PIVOT_X, + PROP_PIVOT_Y +}; + +struct _GimpToolGyroscopePrivate +{ + gdouble yaw; + gdouble pitch; + gdouble roll; + gdouble zoom; + + gdouble orig_yaw; + gdouble orig_pitch; + gdouble orig_roll; + gdouble orig_zoom; + + gboolean invert; + + gdouble speed; + + gdouble pivot_x; + gdouble pivot_y; + + Mode mode; + Constraint constraint; + + gdouble last_x; + gdouble last_y; + + gdouble last_angle; + gdouble curr_angle; + + gdouble last_zoom; +}; + + +/* local function prototypes */ + +static void gimp_tool_gyroscope_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_gyroscope_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint gimp_tool_gyroscope_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_gyroscope_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_gyroscope_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_gyroscope_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_gyroscope_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static gboolean gimp_tool_gyroscope_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static void gimp_tool_gyroscope_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_gyroscope_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_gyroscope_update_status (GimpToolGyroscope *gyroscope, + GdkModifierType state); + +static void gimp_tool_gyroscope_save (GimpToolGyroscope *gyroscope); +static void gimp_tool_gyroscope_restore (GimpToolGyroscope *gyroscope); + +static void gimp_tool_gyroscope_rotate (GimpToolGyroscope *gyroscope, + const GimpVector3 *axis); +static void gimp_tool_gyroscope_rotate_vector (GimpVector3 *vector, + const GimpVector3 *axis); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolGyroscope, gimp_tool_gyroscope, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_gyroscope_parent_class + + +static void +gimp_tool_gyroscope_class_init (GimpToolGyroscopeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->set_property = gimp_tool_gyroscope_set_property; + object_class->get_property = gimp_tool_gyroscope_get_property; + + widget_class->button_press = gimp_tool_gyroscope_button_press; + widget_class->button_release = gimp_tool_gyroscope_button_release; + widget_class->motion = gimp_tool_gyroscope_motion; + widget_class->hit = gimp_tool_gyroscope_hit; + widget_class->hover = gimp_tool_gyroscope_hover; + widget_class->key_press = gimp_tool_gyroscope_key_press; + widget_class->motion_modifier = gimp_tool_gyroscope_motion_modifier; + widget_class->get_cursor = gimp_tool_gyroscope_get_cursor; + + g_object_class_install_property (object_class, PROP_YAW, + g_param_spec_double ("yaw", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PITCH, + g_param_spec_double ("pitch", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ROLL, + g_param_spec_double ("roll", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ZOOM, + g_param_spec_double ("zoom", NULL, NULL, + 0.0, + +G_MAXDOUBLE, + 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_INVERT, + g_param_spec_boolean ("invert", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SPEED, + g_param_spec_double ("speed", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_X, + g_param_spec_double ("pivot-x", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_Y, + g_param_spec_double ("pivot-y", NULL, NULL, + -G_MAXDOUBLE, + +G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_gyroscope_init (GimpToolGyroscope *gyroscope) +{ + gyroscope->private = gimp_tool_gyroscope_get_instance_private (gyroscope); +} + +static void +gimp_tool_gyroscope_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (object); + GimpToolGyroscopePrivate *private = gyroscope->private; + + switch (property_id) + { + case PROP_YAW: + private->yaw = g_value_get_double (value); + break; + case PROP_PITCH: + private->pitch = g_value_get_double (value); + break; + case PROP_ROLL: + private->roll = g_value_get_double (value); + break; + case PROP_ZOOM: + private->zoom = g_value_get_double (value); + break; + case PROP_INVERT: + private->invert = g_value_get_boolean (value); + break; + case PROP_SPEED: + private->speed = g_value_get_double (value); + break; + case PROP_PIVOT_X: + private->pivot_x = g_value_get_double (value); + break; + case PROP_PIVOT_Y: + private->pivot_y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_gyroscope_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (object); + GimpToolGyroscopePrivate *private = gyroscope->private; + + switch (property_id) + { + case PROP_YAW: + g_value_set_double (value, private->yaw); + break; + case PROP_PITCH: + g_value_set_double (value, private->pitch); + break; + case PROP_ROLL: + g_value_set_double (value, private->roll); + break; + case PROP_ZOOM: + g_value_set_double (value, private->zoom); + break; + case PROP_INVERT: + g_value_set_boolean (value, private->invert); + break; + case PROP_SPEED: + g_value_set_double (value, private->speed); + break; + case PROP_PIVOT_X: + g_value_set_double (value, private->pivot_x); + break; + case PROP_PIVOT_Y: + g_value_set_double (value, private->pivot_y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint +gimp_tool_gyroscope_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget); + GimpToolGyroscopePrivate *private = gyroscope->private; + + gimp_tool_gyroscope_save (gyroscope); + + if (state & GDK_MOD1_MASK) + { + private->mode = MODE_ZOOM; + + private->last_zoom = private->zoom; + } + else if (state & gimp_get_extend_selection_mask ()) + { + private->mode = MODE_ROTATE; + + private->last_angle = atan2 (coords->y - private->pivot_y, + coords->x - private->pivot_x); + private->curr_angle = private->last_angle; + } + else + { + private->mode = MODE_PAN; + + if (state & gimp_get_constrain_behavior_mask ()) + private->constraint = CONSTRAINT_UNKNOWN; + else + private->constraint = CONSTRAINT_NONE; + } + + private->last_x = coords->x; + private->last_y = coords->y; + + gimp_tool_gyroscope_update_status (gyroscope, state); + + return 1; +} + +static void +gimp_tool_gyroscope_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget); + GimpToolGyroscopePrivate *private = gyroscope->private; + + if (release_type == GIMP_BUTTON_RELEASE_CANCEL) + gimp_tool_gyroscope_restore (gyroscope); + + private->mode = MODE_NONE; + + gimp_tool_gyroscope_update_status (gyroscope, state); +} + +static void +gimp_tool_gyroscope_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget); + GimpToolGyroscopePrivate *private = gyroscope->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpVector3 axis = {}; + + switch (private->mode) + { + case MODE_PAN: + { + gdouble x1 = private->last_x; + gdouble y1 = private->last_y; + gdouble x2 = coords->x; + gdouble y2 = coords->y; + gdouble factor = 1.0 / private->zoom; + + if (private->constraint != CONSTRAINT_NONE) + { + gimp_display_shell_rotate_xy_f (shell, x1, y1, &x1, &y1); + gimp_display_shell_rotate_xy_f (shell, x2, y2, &x2, &y2); + + if (private->constraint == CONSTRAINT_UNKNOWN) + { + if (fabs (x2 - x1) > fabs (y2 - y1)) + private->constraint = CONSTRAINT_HORIZONTAL; + else if (fabs (y2 - y1) > fabs (x2 - x1)) + private->constraint = CONSTRAINT_VERTICAL; + } + + if (private->constraint == CONSTRAINT_HORIZONTAL) + y2 = y1; + else if (private->constraint == CONSTRAINT_VERTICAL) + x2 = x1; + + gimp_display_shell_unrotate_xy_f (shell, x1, y1, &x1, &y1); + gimp_display_shell_unrotate_xy_f (shell, x2, y2, &x2, &y2); + } + + if (private->invert) + factor = 1.0 / factor; + + gimp_vector3_set (&axis, y2 - y1, x2 - x1, 0.0); + gimp_vector3_mul (&axis, factor * private->speed); + } + break; + + case MODE_ROTATE: + { + gdouble angle; + + angle = atan2 (coords->y - private->pivot_y, + coords->x - private->pivot_x); + + private->curr_angle = angle; + + angle -= private->last_angle; + + if (state & gimp_get_constrain_behavior_mask ()) + angle = RINT (angle / (G_PI / 6.0)) * (G_PI / 6.0); + + gimp_vector3_set (&axis, 0.0, 0.0, angle); + + private->last_angle += angle; + } + break; + + case MODE_ZOOM: + { + gdouble x1, y1; + gdouble x2, y2; + gdouble zoom; + + gimp_display_shell_transform_xy_f (shell, + private->last_x, private->last_y, + &x1, &y1); + gimp_display_shell_transform_xy_f (shell, + coords->x, coords->y, + &x2, &y2); + + zoom = (y1 - y2) * shell->scale_y / 128.0; + + if (private->invert) + zoom = -zoom; + + private->last_zoom *= pow (2.0, zoom); + + zoom = log (private->last_zoom / private->zoom) / G_LN2; + + if (state & gimp_get_constrain_behavior_mask ()) + zoom = RINT (zoom * 2.0) / 2.0; + + g_object_set (gyroscope, + "zoom", private->zoom * pow (2.0, zoom), + NULL); + } + break; + + case MODE_NONE: + g_return_if_reached (); + } + + private->last_x = coords->x; + private->last_y = coords->y; + + gimp_tool_gyroscope_rotate (gyroscope, &axis); +} + +static GimpHit +gimp_tool_gyroscope_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + return GIMP_HIT_INDIRECT; +} + +static void +gimp_tool_gyroscope_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + gimp_tool_gyroscope_update_status (GIMP_TOOL_GYROSCOPE (widget), state); +} + +static gboolean +gimp_tool_gyroscope_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget); + GimpToolGyroscopePrivate *private = gyroscope->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpVector3 axis = {}; + gboolean fast; + gboolean result = FALSE; + + fast = (kevent->state & gimp_get_constrain_behavior_mask ()); + + if (kevent->state & GDK_MOD1_MASK) + { + /* zoom */ + gdouble zoom = 0.0; + + switch (kevent->keyval) + { + case GDK_KEY_Up: + zoom = fast ? +1.0 : +1.0 / 8.0; + result = TRUE; + break; + + case GDK_KEY_Down: + zoom = fast ? -1.0 : -1.0 / 8.0; + result = TRUE; + break; + } + + if (private->invert) + zoom = -zoom; + + if (zoom) + { + g_object_set (gyroscope, + "zoom", private->zoom * pow (2.0, zoom), + NULL); + } + } + else if (kevent->state & gimp_get_extend_selection_mask ()) + { + /* rotate */ + gdouble angle = 0.0; + + switch (kevent->keyval) + { + case GDK_KEY_Left: + angle = fast ? +15.0 : +1.0; + result = TRUE; + break; + + case GDK_KEY_Right: + angle = fast ? -15.0 : -1.0; + result = TRUE; + break; + } + + if (shell->flip_horizontally ^ shell->flip_vertically) + angle = -angle; + + gimp_vector3_set (&axis, 0.0, 0.0, angle * DEG_TO_RAD); + } + else + { + /* pan */ + gdouble x0 = 0.0; + gdouble y0 = 0.0; + gdouble x = 0.0; + gdouble y = 0.0; + gdouble factor = 1.0 / private->zoom; + + if (private->invert) + factor = 1.0 / factor; + + switch (kevent->keyval) + { + case GDK_KEY_Left: + x = fast ? +15.0 : +1.0; + result = TRUE; + break; + + case GDK_KEY_Right: + x = fast ? -15.0 : -1.0; + result = TRUE; + break; + + case GDK_KEY_Up: + y = fast ? +15.0 : +1.0; + result = TRUE; + break; + + case GDK_KEY_Down: + y = fast ? -15.0 : -1.0; + result = TRUE; + break; + } + + gimp_display_shell_unrotate_xy_f (shell, x0, y0, &x0, &y0); + gimp_display_shell_unrotate_xy_f (shell, x, y, &x, &y); + + gimp_vector3_set (&axis, + (y - y0) * DEG_TO_RAD, + (x - x0) * DEG_TO_RAD, + 0.0); + gimp_vector3_mul (&axis, factor); + } + + gimp_tool_gyroscope_rotate (gyroscope, &axis); + + return result; +} + +static void +gimp_tool_gyroscope_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget); + GimpToolGyroscopePrivate *private = gyroscope->private; + + gimp_tool_gyroscope_update_status (gyroscope, state); + + if (key == gimp_get_constrain_behavior_mask ()) + { + switch (private->mode) + { + case MODE_PAN: + if (state & gimp_get_constrain_behavior_mask ()) + private->constraint = CONSTRAINT_UNKNOWN; + else + private->constraint = CONSTRAINT_NONE; + break; + + case MODE_ROTATE: + if (! (state & gimp_get_constrain_behavior_mask ())) + private->last_angle = private->curr_angle; + break; + + case MODE_ZOOM: + if (! (state & gimp_get_constrain_behavior_mask ())) + private->last_zoom = private->zoom; + break; + + case MODE_NONE: + break; + } + } +} + +static gboolean +gimp_tool_gyroscope_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + if (state & GDK_MOD1_MASK) + *modifier = GIMP_CURSOR_MODIFIER_ZOOM; + else if (state & gimp_get_extend_selection_mask ()) + *modifier = GIMP_CURSOR_MODIFIER_ROTATE; + else + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + + return TRUE; +} + +static void +gimp_tool_gyroscope_update_status (GimpToolGyroscope *gyroscope, + GdkModifierType state) +{ + GimpToolGyroscopePrivate *private = gyroscope->private; + gchar *status; + + if (private->mode == MODE_ZOOM || + (private->mode == MODE_NONE && + state & GDK_MOD1_MASK)) + { + status = gimp_suggest_modifiers (_("Click-Drag to zoom"), + gimp_get_toggle_behavior_mask () & + ~state, + NULL, + _("%s for constrained steps"), + NULL); + } + else if (private->mode == MODE_ROTATE || + (private->mode == MODE_NONE && + state & gimp_get_extend_selection_mask ())) + { + status = gimp_suggest_modifiers (_("Click-Drag to rotate"), + gimp_get_toggle_behavior_mask () & + ~state, + NULL, + _("%s for constrained angles"), + NULL); + } + else + { + status = gimp_suggest_modifiers (_("Click-Drag to pan"), + (((gimp_get_extend_selection_mask () | + GDK_MOD1_MASK) * + (private->mode == MODE_NONE)) | + gimp_get_toggle_behavior_mask ()) & + ~state, + _("%s to rotate"), + _("%s for a constrained axis"), + _("%s to zoom")); + } + + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (gyroscope), status); + + g_free (status); +} + +static void +gimp_tool_gyroscope_save (GimpToolGyroscope *gyroscope) +{ + GimpToolGyroscopePrivate *private = gyroscope->private; + + private->orig_yaw = private->yaw; + private->orig_pitch = private->pitch; + private->orig_roll = private->roll; + private->orig_zoom = private->zoom; +} + +static void +gimp_tool_gyroscope_restore (GimpToolGyroscope *gyroscope) +{ + GimpToolGyroscopePrivate *private = gyroscope->private; + + g_object_set (gyroscope, + "yaw", private->orig_yaw, + "pitch", private->orig_pitch, + "roll", private->orig_roll, + "zoom", private->orig_zoom, + NULL); +} + +static void +gimp_tool_gyroscope_rotate (GimpToolGyroscope *gyroscope, + const GimpVector3 *axis) +{ + GimpToolGyroscopePrivate *private = gyroscope->private; + GimpVector3 real_axis; + GimpVector3 basis[2]; + gdouble yaw; + gdouble pitch; + gdouble roll; + gint i; + + if (gimp_vector3_length (axis) < EPSILON) + return; + + real_axis = *axis; + + if (private->invert) + gimp_vector3_neg (&real_axis); + + for (i = 0; i < 2; i++) + { + gimp_vector3_set (&basis[i], i == 0, i == 1, 0.0); + + if (private->invert) + gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis); + + gimp_tool_gyroscope_rotate_vector ( + &basis[i], &(GimpVector3) {0.0, private->yaw * DEG_TO_RAD, 0.0}); + gimp_tool_gyroscope_rotate_vector ( + &basis[i], &(GimpVector3) {private->pitch * DEG_TO_RAD, 0.0, 0.0}); + gimp_tool_gyroscope_rotate_vector ( + &basis[i], &(GimpVector3) {0.0, 0.0, private->roll * DEG_TO_RAD}); + + if (! private->invert) + gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis); + } + + roll = atan2 (basis[1].x, basis[1].y); + + for (i = 0; i < 2; i++) + { + gimp_tool_gyroscope_rotate_vector ( + &basis[i], &(GimpVector3) {0.0, 0.0, -roll}); + } + + pitch = atan2 (-basis[1].z, basis[1].y); + + for (i = 0; i < 1; i++) + { + gimp_tool_gyroscope_rotate_vector ( + &basis[i], &(GimpVector3) {-pitch, 0.0, 0.0}); + } + + yaw = atan2 (basis[0].z, basis[0].x); + + g_object_set (gyroscope, + "yaw", yaw / DEG_TO_RAD, + "pitch", pitch / DEG_TO_RAD, + "roll", roll / DEG_TO_RAD, + NULL); +} + +static void +gimp_tool_gyroscope_rotate_vector (GimpVector3 *vector, + const GimpVector3 *axis) +{ + GimpVector3 normalized_axis; + GimpVector3 projection; + GimpVector3 u; + GimpVector3 v; + gdouble angle; + + angle = gimp_vector3_length (axis); + + if (angle < EPSILON) + return; + + normalized_axis = gimp_vector3_mul_val (*axis, 1.0 / angle); + + projection = gimp_vector3_mul_val ( + normalized_axis, + gimp_vector3_inner_product (vector, &normalized_axis)); + + u = gimp_vector3_sub_val (*vector, projection); + v = gimp_vector3_cross_product (&u, &normalized_axis); + + gimp_vector3_mul (&u, cos (angle)); + gimp_vector3_mul (&v, sin (angle)); + + gimp_vector3_add (vector, &u, &v); + gimp_vector3_add (vector, vector, &projection); +} + + +/* public functions */ + + +GimpToolWidget * +gimp_tool_gyroscope_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_GYROSCOPE, + "shell", shell, + NULL); +} diff --git a/app/display/gimptoolgyroscope.h b/app/display/gimptoolgyroscope.h new file mode 100644 index 0000000..3d89648 --- /dev/null +++ b/app/display/gimptoolgyroscope.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolgyroscope.h + * 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 . + */ + +#ifndef __GIMP_TOOL_GYROSCOPE_H__ +#define __GIMP_TOOL_GYROSCOPE_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_GYROSCOPE (gimp_tool_gyroscope_get_type ()) +#define GIMP_TOOL_GYROSCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_GYROSCOPE, GimpToolGyroscope)) +#define GIMP_TOOL_GYROSCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_GYROSCOPE, GimpToolGyroscopeClass)) +#define GIMP_IS_TOOL_GYROSCOPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_GYROSCOPE)) +#define GIMP_IS_TOOL_GYROSCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_GYROSCOPE)) +#define GIMP_TOOL_GYROSCOPE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_GYROSCOPE, GimpToolGyroscopeClass)) + + +typedef struct _GimpToolGyroscope GimpToolGyroscope; +typedef struct _GimpToolGyroscopePrivate GimpToolGyroscopePrivate; +typedef struct _GimpToolGyroscopeClass GimpToolGyroscopeClass; + +struct _GimpToolGyroscope +{ + GimpToolWidget parent_instance; + + GimpToolGyroscopePrivate *private; +}; + +struct _GimpToolGyroscopeClass +{ + GimpToolWidgetClass parent_class; +}; + + +GType gimp_tool_gyroscope_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_gyroscope_new (GimpDisplayShell *shell); + + +#endif /* __GIMP_TOOL_GYROSCOPE_H__ */ diff --git a/app/display/gimptoolhandlegrid.c b/app/display/gimptoolhandlegrid.c new file mode 100644 index 0000000..06dc156 --- /dev/null +++ b/app/display/gimptoolhandlegrid.c @@ -0,0 +1,1217 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolhandlegrid.c + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpHandleTransformTool + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpdisplayshell.h" +#include "gimptoolhandlegrid.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_HANDLE_MODE, + PROP_N_HANDLES, + PROP_ORIG_X1, + PROP_ORIG_Y1, + PROP_ORIG_X2, + PROP_ORIG_Y2, + PROP_ORIG_X3, + PROP_ORIG_Y3, + PROP_ORIG_X4, + PROP_ORIG_Y4, + PROP_TRANS_X1, + PROP_TRANS_Y1, + PROP_TRANS_X2, + PROP_TRANS_Y2, + PROP_TRANS_X3, + PROP_TRANS_Y3, + PROP_TRANS_X4, + PROP_TRANS_Y4 +}; + + +struct _GimpToolHandleGridPrivate +{ + GimpTransformHandleMode handle_mode; /* enum to be renamed */ + + gint n_handles; + GimpVector2 orig[4]; + GimpVector2 trans[4]; + + gint handle; + gdouble last_x; + gdouble last_y; + + gboolean hover; + gdouble mouse_x; + gdouble mouse_y; + + GimpCanvasItem *handles[5]; +}; + + +/* local function prototypes */ + +static void gimp_tool_handle_grid_constructed (GObject *object); +static void gimp_tool_handle_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_handle_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_handle_grid_changed (GimpToolWidget *widget); +static gint gimp_tool_handle_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_handle_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_handle_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_handle_grid_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_handle_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_handle_grid_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_handle_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static gint gimp_tool_handle_grid_get_handle (GimpToolHandleGrid *grid, + const GimpCoords *coords); +static void gimp_tool_handle_grid_update_hilight (GimpToolHandleGrid *grid); +static void gimp_tool_handle_grid_update_matrix (GimpToolHandleGrid *grid); + +static gboolean is_handle_position_valid (GimpToolHandleGrid *grid, + gint handle); +static void handle_micro_move (GimpToolHandleGrid *grid, + gint handle); + +static inline gdouble calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by); +static inline gdouble calc_len (gdouble a, + gdouble b); +static inline gdouble calc_lineintersect_ratio (gdouble p1x, + gdouble p1y, + gdouble p2x, + gdouble p2y, + gdouble q1x, + gdouble q1y, + gdouble q2x, + gdouble q2y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolHandleGrid, gimp_tool_handle_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_handle_grid_parent_class + + +static void +gimp_tool_handle_grid_class_init (GimpToolHandleGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_handle_grid_constructed; + object_class->set_property = gimp_tool_handle_grid_set_property; + object_class->get_property = gimp_tool_handle_grid_get_property; + + widget_class->changed = gimp_tool_handle_grid_changed; + widget_class->button_press = gimp_tool_handle_grid_button_press; + widget_class->button_release = gimp_tool_handle_grid_button_release; + widget_class->motion = gimp_tool_handle_grid_motion; + widget_class->hit = gimp_tool_handle_grid_hit; + widget_class->hover = gimp_tool_handle_grid_hover; + widget_class->leave_notify = gimp_tool_handle_grid_leave_notify; + widget_class->get_cursor = gimp_tool_handle_grid_get_cursor; + + g_object_class_install_property (object_class, PROP_HANDLE_MODE, + g_param_spec_enum ("handle-mode", + NULL, NULL, + GIMP_TYPE_TRANSFORM_HANDLE_MODE, + GIMP_HANDLE_MODE_ADD_TRANSFORM, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_N_HANDLES, + g_param_spec_int ("n-handles", + NULL, NULL, + 0, 4, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X1, + g_param_spec_double ("orig-x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y1, + g_param_spec_double ("orig-y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X2, + g_param_spec_double ("orig-x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y2, + g_param_spec_double ("orig-y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X3, + g_param_spec_double ("orig-x3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y3, + g_param_spec_double ("orig-y3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X4, + g_param_spec_double ("orig-x4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y4, + g_param_spec_double ("orig-y4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X1, + g_param_spec_double ("trans-x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y1, + g_param_spec_double ("trans-y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X2, + g_param_spec_double ("trans-x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y2, + g_param_spec_double ("trans-y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X3, + g_param_spec_double ("trans-x3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y3, + g_param_spec_double ("trans-y3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X4, + g_param_spec_double ("trans-x4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y4, + g_param_spec_double ("trans-y4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_handle_grid_init (GimpToolHandleGrid *grid) +{ + grid->private = gimp_tool_handle_grid_get_instance_private (grid); +} + +static void +gimp_tool_handle_grid_constructed (GObject *object) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + for (i = 0; i < 4; i++) + { + private->handles[i + 1] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + 0, 0, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + } + + gimp_tool_handle_grid_changed (widget); +} + +static void +gimp_tool_handle_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolHandleGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_HANDLE_MODE: + private->handle_mode = g_value_get_enum (value); + break; + + case PROP_N_HANDLES: + private->n_handles = g_value_get_int (value); + break; + + case PROP_ORIG_X1: + private->orig[0].x = g_value_get_double (value); + break; + case PROP_ORIG_Y1: + private->orig[0].y = g_value_get_double (value); + break; + case PROP_ORIG_X2: + private->orig[1].x = g_value_get_double (value); + break; + case PROP_ORIG_Y2: + private->orig[1].y = g_value_get_double (value); + break; + case PROP_ORIG_X3: + private->orig[2].x = g_value_get_double (value); + break; + case PROP_ORIG_Y3: + private->orig[2].y = g_value_get_double (value); + break; + case PROP_ORIG_X4: + private->orig[3].x = g_value_get_double (value); + break; + case PROP_ORIG_Y4: + private->orig[3].y = g_value_get_double (value); + break; + + case PROP_TRANS_X1: + private->trans[0].x = g_value_get_double (value); + break; + case PROP_TRANS_Y1: + private->trans[0].y = g_value_get_double (value); + break; + case PROP_TRANS_X2: + private->trans[1].x = g_value_get_double (value); + break; + case PROP_TRANS_Y2: + private->trans[1].y = g_value_get_double (value); + break; + case PROP_TRANS_X3: + private->trans[2].x = g_value_get_double (value); + break; + case PROP_TRANS_Y3: + private->trans[2].y = g_value_get_double (value); + break; + case PROP_TRANS_X4: + private->trans[3].x = g_value_get_double (value); + break; + case PROP_TRANS_Y4: + private->trans[3].y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_handle_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolHandleGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_N_HANDLES: + g_value_set_int (value, private->n_handles); + break; + + case PROP_HANDLE_MODE: + g_value_set_enum (value, private->handle_mode); + break; + + case PROP_ORIG_X1: + g_value_set_double (value, private->orig[0].x); + break; + case PROP_ORIG_Y1: + g_value_set_double (value, private->orig[0].y); + break; + case PROP_ORIG_X2: + g_value_set_double (value, private->orig[1].x); + break; + case PROP_ORIG_Y2: + g_value_set_double (value, private->orig[1].y); + break; + case PROP_ORIG_X3: + g_value_set_double (value, private->orig[2].x); + break; + case PROP_ORIG_Y3: + g_value_set_double (value, private->orig[2].y); + break; + case PROP_ORIG_X4: + g_value_set_double (value, private->orig[3].x); + break; + case PROP_ORIG_Y4: + g_value_set_double (value, private->orig[3].y); + break; + + case PROP_TRANS_X1: + g_value_set_double (value, private->trans[0].x); + break; + case PROP_TRANS_Y1: + g_value_set_double (value, private->trans[0].y); + break; + case PROP_TRANS_X2: + g_value_set_double (value, private->trans[1].x); + break; + case PROP_TRANS_Y2: + g_value_set_double (value, private->trans[1].y); + break; + case PROP_TRANS_X3: + g_value_set_double (value, private->trans[2].x); + break; + case PROP_TRANS_Y3: + g_value_set_double (value, private->trans[2].y); + break; + case PROP_TRANS_X4: + g_value_set_double (value, private->trans[3].x); + break; + case PROP_TRANS_Y4: + g_value_set_double (value, private->trans[3].y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_handle_grid_changed (GimpToolWidget *widget) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->changed (widget); + + for (i = 0; i < 4; i++) + { + gimp_canvas_handle_set_position (private->handles[i + 1], + private->trans[i].x, + private->trans[i].y); + gimp_canvas_item_set_visible (private->handles[i + 1], + i < private->n_handles); + } + + gimp_tool_handle_grid_update_hilight (grid); +} + +static gint +gimp_tool_handle_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint n_handles = private->n_handles; + gint active_handle = private->handle - 1; + GimpCanvasItem *dragged_handle = NULL; + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (n_handles < 4 && active_handle == -1) + { + /* add handle */ + + GimpMatrix3 *matrix; + + active_handle = n_handles; + + private->trans[active_handle].x = coords->x; + private->trans[active_handle].y = coords->y; + private->n_handles++; + + if (! is_handle_position_valid (grid, active_handle)) + { + handle_micro_move (grid, active_handle); + } + + /* handle was added, calculate new original position */ + g_object_get (grid, + "transform", &matrix, + NULL); + + gimp_matrix3_invert (matrix); + gimp_matrix3_transform_point (matrix, + private->trans[active_handle].x, + private->trans[active_handle].y, + &private->orig[active_handle].x, + &private->orig[active_handle].y); + + g_free (matrix); + + private->handle = active_handle + 1; + + g_object_notify (G_OBJECT (grid), "n-handles"); + } + else if (active_handle >= 0 && + active_handle < 4) + { + /* existing handle is being dragged. don't set dragged_handle for + * newly-created handles, otherwise their snap offset will be wrong + */ + dragged_handle = private->handles[private->handle]; + } + break; + + case GIMP_HANDLE_MODE_MOVE: + if (active_handle >= 0 && + active_handle < 4) + { + /* existing handle is being dragged */ + dragged_handle = private->handles[private->handle]; + } + /* check for valid position and calculating of OX0...OY3 is + * done on button release + */ + break; + + case GIMP_HANDLE_MODE_REMOVE: + if (n_handles > 0 && + active_handle >= 0 && + active_handle < 4) + { + /* remove handle */ + + GimpVector2 temp = private->trans[active_handle]; + GimpVector2 tempo = private->orig[active_handle]; + gint i; + + n_handles--; + private->n_handles--; + + for (i = active_handle; i < n_handles; i++) + { + private->trans[i] = private->trans[i + 1]; + private->orig[i] = private->orig[i + 1]; + } + + private->trans[n_handles] = temp; + private->orig[n_handles] = tempo; + + g_object_notify (G_OBJECT (grid), "n-handles"); + } + break; + } + + /* ensure dragged handles snap to guides based on the handle center, not where + * the cursor grabbed them + */ + if (dragged_handle) + { + gdouble x, y; + + gimp_canvas_handle_get_position (dragged_handle, + &x, + &y); + gimp_tool_widget_set_snap_offsets (widget, + SIGNED_ROUND (x - coords->x), + SIGNED_ROUND (y - coords->y), + 0, 0); + } + + private->last_x = coords->x; + private->last_y = coords->y; + + return private->handle; +} + +static void +gimp_tool_handle_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint active_handle = private->handle - 1; + + if (private->handle_mode == GIMP_HANDLE_MODE_MOVE && + active_handle >= 0 && + active_handle < 4) + { + GimpMatrix3 *matrix; + + if (! is_handle_position_valid (grid, active_handle)) + { + handle_micro_move (grid, active_handle); + } + + /* handle was moved, calculate new original position */ + g_object_get (grid, + "transform", &matrix, + NULL); + + gimp_matrix3_invert (matrix); + gimp_matrix3_transform_point (matrix, + private->trans[active_handle].x, + private->trans[active_handle].y, + &private->orig[active_handle].x, + &private->orig[active_handle].y); + + g_free (matrix); + + gimp_tool_handle_grid_update_matrix (grid); + } +} + +void +gimp_tool_handle_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint n_handles = private->n_handles; + gint active_handle = private->handle - 1; + gdouble diff_x = coords->x - private->last_x; + gdouble diff_y = coords->y - private->last_y; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + if (active_handle >= 0 && active_handle < 4) + { + if (private->handle_mode == GIMP_HANDLE_MODE_MOVE) + { + private->trans[active_handle].x += diff_x; + private->trans[active_handle].y += diff_y; + + /* check for valid position and calculating of OX0...OY3 is + * done on button release hopefully this makes the code run + * faster Moving could be even faster if there was caching + * for the image preview + */ + gimp_canvas_handle_set_position (private->handles[active_handle + 1], + private->trans[active_handle].x, + private->trans[active_handle].y); + } + else if (private->handle_mode == GIMP_HANDLE_MODE_ADD_TRANSFORM) + { + gdouble angle, angle_sin, angle_cos, scale; + GimpVector2 fixed_handles[3]; + GimpVector2 oldpos[4]; + GimpVector2 newpos[4]; + gint i, j; + + for (i = 0, j = 0; i < 4; i++) + { + /* Find all visible handles that are not being moved */ + if (i < n_handles && i != active_handle) + { + fixed_handles[j] = private->trans[i]; + j++; + } + + newpos[i] = oldpos[i] = private->trans[i]; + } + + newpos[active_handle].x = oldpos[active_handle].x + diff_x; + newpos[active_handle].y = oldpos[active_handle].y + diff_y; + + switch (n_handles) + { + case 1: + /* move */ + for (i = 0; i < 4; i++) + { + newpos[i].x = oldpos[i].x + diff_x; + newpos[i].y = oldpos[i].y + diff_y; + } + break; + + case 2: + /* rotate and keep-aspect-scale */ + scale = + calc_len (newpos[active_handle].x - fixed_handles[0].x, + newpos[active_handle].y - fixed_handles[0].y) / + calc_len (oldpos[active_handle].x - fixed_handles[0].x, + oldpos[active_handle].y - fixed_handles[0].y); + + angle = calc_angle (oldpos[active_handle].x - fixed_handles[0].x, + oldpos[active_handle].y - fixed_handles[0].y, + newpos[active_handle].x - fixed_handles[0].x, + newpos[active_handle].y - fixed_handles[0].y); + + angle_sin = sin (angle); + angle_cos = cos (angle); + + for (i = 2; i < 4; i++) + { + newpos[i].x = + fixed_handles[0].x + + scale * (angle_cos * (oldpos[i].x - fixed_handles[0].x) + + angle_sin * (oldpos[i].y - fixed_handles[0].y)); + + newpos[i].y = + fixed_handles[0].y + + scale * (-angle_sin * (oldpos[i].x - fixed_handles[0].x) + + angle_cos * (oldpos[i].y - fixed_handles[0].y)); + } + break; + + case 3: + /* shear and non-aspect-scale */ + scale = calc_lineintersect_ratio (oldpos[3].x, + oldpos[3].y, + oldpos[active_handle].x, + oldpos[active_handle].y, + fixed_handles[0].x, + fixed_handles[0].y, + fixed_handles[1].x, + fixed_handles[1].y); + + newpos[3].x = oldpos[3].x + scale * diff_x; + newpos[3].y = oldpos[3].y + scale * diff_y; + break; + } + + for (i = 0; i < 4; i++) + { + private->trans[i] = newpos[i]; + } + + gimp_tool_handle_grid_update_matrix (grid); + } + } + + private->last_x = coords->x; + private->last_y = coords->y; +} + +static GimpHit +gimp_tool_handle_grid_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + + if (proximity) + { + gint handle = gimp_tool_handle_grid_get_handle (grid, coords); + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (handle > 0) + return GIMP_HIT_DIRECT; + else + return GIMP_HIT_INDIRECT; + break; + + case GIMP_HANDLE_MODE_MOVE: + case GIMP_HANDLE_MODE_REMOVE: + if (private->handle > 0) + return GIMP_HIT_DIRECT; + break; + } + } + + return GIMP_HIT_NONE; +} + +static void +gimp_tool_handle_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gchar *status = NULL; + + private->hover = TRUE; + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + private->handle = gimp_tool_handle_grid_get_handle (grid, coords); + + if (proximity) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (private->handle > 0) + { + const gchar *s = NULL; + + switch (private->n_handles) + { + case 1: + s = _("Click-Drag to move"); + break; + case 2: + s = _("Click-Drag to rotate and scale"); + break; + case 3: + s = _("Click-Drag to shear and scale"); + break; + case 4: + s = _("Click-Drag to change perspective"); + break; + } + + status = gimp_suggest_modifiers (s, + extend_mask | toggle_mask, + NULL, NULL, NULL); + } + else + { + if (private->n_handles < 4) + status = g_strdup (_("Click to add a handle")); + } + break; + + case GIMP_HANDLE_MODE_MOVE: + if (private->handle > 0) + status = g_strdup (_("Click-Drag to move this handle")); + break; + + case GIMP_HANDLE_MODE_REMOVE: + if (private->handle > 0) + status = g_strdup (_("Click-Drag to remove this handle")); + break; + } + } + + gimp_tool_widget_set_status (widget, status); + g_free (status); + + gimp_tool_handle_grid_update_hilight (grid); +} + +static void +gimp_tool_handle_grid_leave_notify (GimpToolWidget *widget) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + + private->hover = FALSE; + private->handle = 0; + + gimp_tool_handle_grid_update_hilight (grid); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static gboolean +gimp_tool_handle_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + + *cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + *tool_cursor = GIMP_TOOL_CURSOR_NONE; + *modifier = GIMP_CURSOR_MODIFIER_NONE; + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (private->handle > 0) + { + switch (private->n_handles) + { + case 1: + *tool_cursor = GIMP_TOOL_CURSOR_MOVE; + break; + case 2: + *tool_cursor = GIMP_TOOL_CURSOR_ROTATE; + break; + case 3: + *tool_cursor = GIMP_TOOL_CURSOR_SHEAR; + break; + case 4: + *tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE; + break; + } + } + else + { + if (private->n_handles < 4) + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + else + *modifier = GIMP_CURSOR_MODIFIER_BAD; + } + break; + + case GIMP_HANDLE_MODE_MOVE: + if (private->handle > 0) + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + else + *modifier = GIMP_CURSOR_MODIFIER_BAD; + break; + + case GIMP_HANDLE_MODE_REMOVE: + if (private->handle > 0) + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + else + *modifier = GIMP_CURSOR_MODIFIER_BAD; + break; + } + + return TRUE; +} + +static gint +gimp_tool_handle_grid_get_handle (GimpToolHandleGrid *grid, + const GimpCoords *coords) +{ + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + for (i = 0; i < 4; i++) + { + if (private->handles[i + 1] && + gimp_canvas_item_hit (private->handles[i + 1], + coords->x, coords->y)) + { + return i + 1; + } + } + + return 0; +} + +static void +gimp_tool_handle_grid_update_hilight (GimpToolHandleGrid *grid) +{ + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + for (i = 0; i < 4; i++) + { + GimpCanvasItem *item = private->handles[i + 1]; + + if (item) + { + gdouble diameter = GIMP_CANVAS_HANDLE_SIZE_CIRCLE; + + if (private->hover) + { + diameter = gimp_canvas_handle_calc_size ( + item, + private->mouse_x, + private->mouse_y, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + 2 * GIMP_CANVAS_HANDLE_SIZE_CIRCLE); + } + + gimp_canvas_handle_set_size (item, diameter, diameter); + gimp_canvas_item_set_highlight (item, (i + 1) == private->handle); + } + } +} + +static void +gimp_tool_handle_grid_update_matrix (GimpToolHandleGrid *grid) +{ + GimpToolHandleGridPrivate *private = grid->private; + GimpMatrix3 transform; + gboolean transform_valid; + + gimp_matrix3_identity (&transform); + transform_valid = gimp_transform_matrix_generic (&transform, + private->orig, + private->trans); + + g_object_set (grid, + "transform", &transform, + "show-guides", transform_valid, + NULL); +} + +/* check if a handle is not on the connection line of two other handles */ +static gboolean +is_handle_position_valid (GimpToolHandleGrid *grid, + gint handle) +{ + GimpToolHandleGridPrivate *private = grid->private; + gint i, j, k; + + for (i = 0; i < 2; i++) + { + for (j = i + 1; j < 3; j++) + { + for (k = j + 1; i < 4; i++) + { + if (handle == i || + handle == j || + handle == k) + { + if ((private->trans[i].x - + private->trans[j].x) * + (private->trans[j].y - + private->trans[k].y) == + + (private->trans[j].x - + private->trans[k].x) * + (private->trans[i].y - + private->trans[j].y)) + { + return FALSE; + } + } + } + } + } + + return TRUE; +} + +/* three handles on a line causes problems. + * Let's move the new handle around a bit to find a better position */ +static void +handle_micro_move (GimpToolHandleGrid *grid, + gint handle) +{ + GimpToolHandleGridPrivate *private = grid->private; + gdouble posx = private->trans[handle].x; + gdouble posy = private->trans[handle].y; + gdouble dx, dy; + + for (dx = -0.1; dx < 0.11; dx += 0.1) + { + private->trans[handle].x = posx + dx; + + for (dy = -0.1; dy < 0.11; dy += 0.1) + { + private->trans[handle].y = posy + dy; + + if (is_handle_position_valid (grid, handle)) + { + return; + } + } + } +} + +/* finds the clockwise angle between the vectors given, 0-2π */ +static inline gdouble +calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by) +{ + gdouble angle; + gdouble direction; + gdouble length = sqrt ((ax * ax + ay * ay) * (bx * bx + by * by)); + + angle = acos ((ax * bx + ay * by) / length); + direction = ax * by - ay * bx; + + return ((direction < 0) ? angle : 2 * G_PI - angle); +} + +static inline gdouble +calc_len (gdouble a, + gdouble b) +{ + return sqrt (a * a + b * b); +} + +/* imagine two lines, one through the points p1 and p2, the other one + * through the points q1 and q2. Find the intersection point r. + * Calculate (distance p1 to r)/(distance p2 to r) + */ +static inline gdouble +calc_lineintersect_ratio (gdouble p1x, gdouble p1y, + gdouble p2x, gdouble p2y, + gdouble q1x, gdouble q1y, + gdouble q2x, gdouble q2y) +{ + gdouble denom, u; + + denom = (q2y - q1y) * (p2x - p1x) - (q2x - q1x) * (p2y - p1y); + if (denom == 0.0) + { + /* u is infinite, so u/(u-1) is 1 */ + return 1.0; + } + + u = (q2y - q1y) * (q1x - p1x) - (q1y - p1y) * (q2x - q1x); + u /= denom; + + return u / (u - 1); +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_handle_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_HANDLE_GRID, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "clip-guides", TRUE, + NULL); +} diff --git a/app/display/gimptoolhandlegrid.h b/app/display/gimptoolhandlegrid.h new file mode 100644 index 0000000..77d31dc --- /dev/null +++ b/app/display/gimptoolhandlegrid.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolhandlegrid.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_HANDLE_GRID_H__ +#define __GIMP_TOOL_HANDLE_GRID_H__ + + +#include "gimptooltransformgrid.h" + + +#define GIMP_TYPE_TOOL_HANDLE_GRID (gimp_tool_handle_grid_get_type ()) +#define GIMP_TOOL_HANDLE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGrid)) +#define GIMP_TOOL_HANDLE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGridClass)) +#define GIMP_IS_TOOL_HANDLE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_HANDLE_GRID)) +#define GIMP_IS_TOOL_HANDLE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_HANDLE_GRID)) +#define GIMP_TOOL_HANDLE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGridClass)) + + +typedef struct _GimpToolHandleGrid GimpToolHandleGrid; +typedef struct _GimpToolHandleGridPrivate GimpToolHandleGridPrivate; +typedef struct _GimpToolHandleGridClass GimpToolHandleGridClass; + +struct _GimpToolHandleGrid +{ + GimpToolTransformGrid parent_instance; + + GimpToolHandleGridPrivate *private; +}; + +struct _GimpToolHandleGridClass +{ + GimpToolTransformGridClass parent_class; +}; + + +GType gimp_tool_handle_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_handle_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + + +#endif /* __GIMP_TOOL_HANDLE_GRID_H__ */ diff --git a/app/display/gimptoolline.c b/app/display/gimptoolline.c new file mode 100644 index 0000000..271f926 --- /dev/null +++ b/app/display/gimptoolline.c @@ -0,0 +1,1796 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolline.c + * Copyright (C) 2017 Michael Natterer + * + * Major improvements for interactivity + * Copyright (C) 2014 Michael Henning + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpmarshal.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvasgroup.h" +#include "gimpcanvashandle.h" +#include "gimpcanvasline.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-cursor.h" +#include "gimpdisplayshell-utils.h" +#include "gimptoolline.h" + +#include "gimp-intl.h" + + +#define SHOW_LINE TRUE +#define GRAB_LINE_MASK GDK_MOD1_MASK +#define ENDPOINT_HANDLE_TYPE GIMP_HANDLE_CROSS +#define ENDPOINT_HANDLE_SIZE GIMP_CANVAS_HANDLE_SIZE_CROSS +#define SLIDER_HANDLE_TYPE GIMP_HANDLE_FILLED_DIAMOND +#define SLIDER_HANDLE_SIZE (ENDPOINT_HANDLE_SIZE * 2 / 3) +#define HANDLE_CIRCLE_SCALE 1.8 +#define LINE_VICINITY ((gint) (SLIDER_HANDLE_SIZE * HANDLE_CIRCLE_SCALE) / 2) +#define SLIDER_TEAR_DISTANCE (5 * LINE_VICINITY) + + +/* hover-only "handles" */ +#define HOVER_NEW_SLIDER (GIMP_TOOL_LINE_HANDLE_NONE - 1) + + +typedef enum +{ + GRAB_NONE, + GRAB_SELECTION, + GRAB_LINE +} GimpToolLineGrab; + +enum +{ + PROP_0, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_SLIDERS, + PROP_SELECTION, + PROP_STATUS_TITLE, +}; + +enum +{ + CAN_ADD_SLIDER, + ADD_SLIDER, + PREPARE_TO_REMOVE_SLIDER, + REMOVE_SLIDER, + SELECTION_CHANGED, + HANDLE_CLICKED, + LAST_SIGNAL +}; + +struct _GimpToolLinePrivate +{ + gdouble x1; + gdouble y1; + gdouble x2; + gdouble y2; + GArray *sliders; + gint selection; + gchar *status_title; + + gdouble saved_x1; + gdouble saved_y1; + gdouble saved_x2; + gdouble saved_y2; + gdouble saved_slider_value; + + gdouble mouse_x; + gdouble mouse_y; + gint hover; + gdouble new_slider_value; + gboolean remove_slider; + GimpToolLineGrab grab; + + GimpCanvasItem *line; + GimpCanvasItem *start_handle; + GimpCanvasItem *end_handle; + GimpCanvasItem *slider_group; + GArray *slider_handles; + GimpCanvasItem *handle_circle; +}; + + +/* local function prototypes */ + +static void gimp_tool_line_constructed (GObject *object); +static void gimp_tool_line_finalize (GObject *object); +static void gimp_tool_line_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_line_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_line_changed (GimpToolWidget *widget); +static void gimp_tool_line_focus_changed (GimpToolWidget *widget); +static gint gimp_tool_line_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_line_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_line_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_line_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_line_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_line_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_line_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static void gimp_tool_line_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_line_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static gint gimp_tool_line_get_hover (GimpToolLine *line, + const GimpCoords *coords, + GdkModifierType state); +static GimpControllerSlider * + gimp_tool_line_get_slider (GimpToolLine *line, + gint slider); +static GimpCanvasItem * + gimp_tool_line_get_handle (GimpToolLine *line, + gint handle); +static gdouble gimp_tool_line_project_point (GimpToolLine *line, + gdouble x, + gdouble y, + gboolean constrain, + gdouble *dist); + +static gboolean + gimp_tool_line_selection_motion (GimpToolLine *line, + gboolean constrain); + +static void gimp_tool_line_update_handles (GimpToolLine *line); +static void gimp_tool_line_update_circle (GimpToolLine *line); +static void gimp_tool_line_update_hilight (GimpToolLine *line); +static void gimp_tool_line_update_status (GimpToolLine *line, + GdkModifierType state, + gboolean proximity); + +static gboolean gimp_tool_line_handle_hit (GimpCanvasItem *handle, + gdouble x, + gdouble y, + gdouble *min_dist); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolLine, gimp_tool_line, GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_line_parent_class + +static guint line_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_tool_line_class_init (GimpToolLineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_line_constructed; + object_class->finalize = gimp_tool_line_finalize; + object_class->set_property = gimp_tool_line_set_property; + object_class->get_property = gimp_tool_line_get_property; + + widget_class->changed = gimp_tool_line_changed; + widget_class->focus_changed = gimp_tool_line_focus_changed; + widget_class->button_press = gimp_tool_line_button_press; + widget_class->button_release = gimp_tool_line_button_release; + widget_class->motion = gimp_tool_line_motion; + widget_class->hit = gimp_tool_line_hit; + widget_class->hover = gimp_tool_line_hover; + widget_class->leave_notify = gimp_tool_line_leave_notify; + widget_class->key_press = gimp_tool_line_key_press; + widget_class->motion_modifier = gimp_tool_line_motion_modifier; + widget_class->get_cursor = gimp_tool_line_get_cursor; + + line_signals[CAN_ADD_SLIDER] = + g_signal_new ("can-add-slider", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpToolLineClass, can_add_slider), + NULL, NULL, + gimp_marshal_BOOLEAN__DOUBLE, + G_TYPE_BOOLEAN, 1, + G_TYPE_DOUBLE); + + line_signals[ADD_SLIDER] = + g_signal_new ("add-slider", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpToolLineClass, add_slider), + NULL, NULL, + gimp_marshal_INT__DOUBLE, + G_TYPE_INT, 1, + G_TYPE_DOUBLE); + + line_signals[PREPARE_TO_REMOVE_SLIDER] = + g_signal_new ("prepare-to-remove-slider", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolLineClass, prepare_to_remove_slider), + NULL, NULL, + gimp_marshal_VOID__INT_BOOLEAN, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_BOOLEAN); + + line_signals[REMOVE_SLIDER] = + g_signal_new ("remove-slider", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolLineClass, remove_slider), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + line_signals[SELECTION_CHANGED] = + g_signal_new ("selection-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolLineClass, selection_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + line_signals[HANDLE_CLICKED] = + g_signal_new ("handle-clicked", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpToolLineClass, handle_clicked), + NULL, NULL, + gimp_marshal_BOOLEAN__INT_UINT_ENUM, + G_TYPE_BOOLEAN, 3, + G_TYPE_INT, + G_TYPE_UINT, + GIMP_TYPE_BUTTON_PRESS_TYPE); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SLIDERS, + g_param_spec_boxed ("sliders", NULL, NULL, + G_TYPE_ARRAY, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_SELECTION, + g_param_spec_int ("selection", NULL, NULL, + GIMP_TOOL_LINE_HANDLE_NONE, + G_MAXINT, + GIMP_TOOL_LINE_HANDLE_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_STATUS_TITLE, + g_param_spec_string ("status-title", + NULL, NULL, + _("Line: "), + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_line_init (GimpToolLine *line) +{ + GimpToolLinePrivate *private; + + private = line->private = gimp_tool_line_get_instance_private (line); + + private->sliders = g_array_new (FALSE, FALSE, sizeof (GimpControllerSlider)); + + private->selection = GIMP_TOOL_LINE_HANDLE_NONE; + private->hover = GIMP_TOOL_LINE_HANDLE_NONE; + private->grab = GRAB_NONE; + + private->slider_handles = g_array_new (FALSE, TRUE, + sizeof (GimpCanvasItem *)); +} + +static void +gimp_tool_line_constructed (GObject *object) +{ + GimpToolLine *line = GIMP_TOOL_LINE (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolLinePrivate *private = line->private; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + private->line = gimp_tool_widget_add_line (widget, + private->x1, + private->y1, + private->x2, + private->y2); + + gimp_canvas_item_set_visible (private->line, SHOW_LINE); + + private->start_handle = + gimp_tool_widget_add_handle (widget, + ENDPOINT_HANDLE_TYPE, + private->x1, + private->y1, + ENDPOINT_HANDLE_SIZE, + ENDPOINT_HANDLE_SIZE, + GIMP_HANDLE_ANCHOR_CENTER); + + private->end_handle = + gimp_tool_widget_add_handle (widget, + ENDPOINT_HANDLE_TYPE, + private->x2, + private->y2, + ENDPOINT_HANDLE_SIZE, + ENDPOINT_HANDLE_SIZE, + GIMP_HANDLE_ANCHOR_CENTER); + + private->slider_group = + gimp_canvas_group_new (gimp_tool_widget_get_shell (widget)); + gimp_tool_widget_add_item (widget, private->slider_group); + g_object_unref (private->slider_group); + + private->handle_circle = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + private->x1, + private->y1, + ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE, + ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE, + GIMP_HANDLE_ANCHOR_CENTER); + + gimp_tool_line_changed (widget); +} + +static void +gimp_tool_line_finalize (GObject *object) +{ + GimpToolLine *line = GIMP_TOOL_LINE (object); + GimpToolLinePrivate *private = line->private; + + g_clear_pointer (&private->sliders, g_array_unref); + g_clear_pointer (&private->status_title, g_free); + g_clear_pointer (&private->slider_handles, g_array_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_line_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolLine *line = GIMP_TOOL_LINE (object); + GimpToolLinePrivate *private = line->private; + + switch (property_id) + { + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + case PROP_SLIDERS: + { + GArray *sliders = g_value_dup_boxed (value); + gboolean deselect; + + g_return_if_fail (sliders != NULL); + + deselect = + GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) && + (sliders->len != private->sliders->len || + ! gimp_tool_line_get_slider (line, private->selection)->selectable); + + g_array_unref (private->sliders); + private->sliders = sliders; + + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover)) + private->hover = GIMP_TOOL_LINE_HANDLE_NONE; + + if (deselect) + gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE); + } + break; + + case PROP_SELECTION: + { + gint selection = g_value_get_int (value); + + g_return_if_fail (selection < (gint) private->sliders->len); + g_return_if_fail (selection < 0 || + gimp_tool_line_get_slider (line, + selection)->selectable); + + if (selection != private->selection) + { + private->selection = selection; + + if (private->grab == GRAB_SELECTION) + private->grab = GRAB_NONE; + + g_signal_emit (line, line_signals[SELECTION_CHANGED], 0); + } + } + break; + + case PROP_STATUS_TITLE: + g_free (private->status_title); + private->status_title = g_value_dup_string (value); + if (! private->status_title) + private->status_title = g_strdup (_("Line: ")); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_line_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolLine *line = GIMP_TOOL_LINE (object); + GimpToolLinePrivate *private = line->private; + + switch (property_id) + { + case PROP_X1: + g_value_set_double (value, private->x1); + break; + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + case PROP_X2: + g_value_set_double (value, private->x2); + break; + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_SLIDERS: + g_value_set_boxed (value, private->sliders); + break; + + case PROP_SELECTION: + g_value_set_int (value, private->selection); + break; + + case PROP_STATUS_TITLE: + g_value_set_string (value, private->status_title); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_line_changed (GimpToolWidget *widget) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + gint i; + + gimp_canvas_line_set (private->line, + private->x1, + private->y1, + private->x2, + private->y2); + + gimp_canvas_handle_set_position (private->start_handle, + private->x1, + private->y1); + + gimp_canvas_handle_set_position (private->end_handle, + private->x2, + private->y2); + + /* remove excessive slider handles */ + for (i = private->sliders->len; i < private->slider_handles->len; i++) + { + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (private->slider_group), + gimp_tool_line_get_handle (line, i)); + } + + g_array_set_size (private->slider_handles, private->sliders->len); + + for (i = 0; i < private->sliders->len; i++) + { + gdouble value; + gdouble x; + gdouble y; + GimpCanvasItem **handle; + + value = gimp_tool_line_get_slider (line, i)->value; + + x = private->x1 + (private->x2 - private->x1) * value; + y = private->y1 + (private->y2 - private->y1) * value; + + handle = &g_array_index (private->slider_handles, GimpCanvasItem *, i); + + if (*handle) + { + gimp_canvas_handle_set_position (*handle, x, y); + } + else + { + *handle = gimp_canvas_handle_new (gimp_tool_widget_get_shell (widget), + SLIDER_HANDLE_TYPE, + GIMP_HANDLE_ANCHOR_CENTER, + x, + y, + SLIDER_HANDLE_SIZE, + SLIDER_HANDLE_SIZE); + + gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (private->slider_group), + *handle); + g_object_unref (*handle); + } + } + + gimp_tool_line_update_handles (line); + gimp_tool_line_update_circle (line); + gimp_tool_line_update_hilight (line); +} + +static void +gimp_tool_line_focus_changed (GimpToolWidget *widget) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + + gimp_tool_line_update_hilight (line); +} + +gboolean +gimp_tool_line_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + gboolean result = FALSE; + + private->grab = GRAB_NONE; + private->remove_slider = FALSE; + + private->saved_x1 = private->x1; + private->saved_y1 = private->y1; + private->saved_x2 = private->x2; + private->saved_y2 = private->y2; + + if (press_type != GIMP_BUTTON_PRESS_NORMAL && + private->hover > GIMP_TOOL_LINE_HANDLE_NONE && + private->selection > GIMP_TOOL_LINE_HANDLE_NONE) + { + g_signal_emit (line, line_signals[HANDLE_CLICKED], 0, + private->selection, state, press_type, &result); + + if (! result) + gimp_tool_widget_hover (widget, coords, state, TRUE); + } + + if (press_type == GIMP_BUTTON_PRESS_NORMAL || ! result) + { + private->saved_x1 = private->x1; + private->saved_y1 = private->y1; + private->saved_x2 = private->x2; + private->saved_y2 = private->y2; + + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover)) + { + private->saved_slider_value = + gimp_tool_line_get_slider (line, private->hover)->value; + } + + if (private->hover > GIMP_TOOL_LINE_HANDLE_NONE) + { + gimp_tool_line_set_selection (line, private->hover); + + private->grab = GRAB_SELECTION; + } + else if (private->hover == HOVER_NEW_SLIDER) + { + gint slider; + + g_signal_emit (line, line_signals[ADD_SLIDER], 0, + private->new_slider_value, &slider); + + g_return_val_if_fail (slider < (gint) private->sliders->len, FALSE); + + if (slider >= 0) + { + gimp_tool_line_set_selection (line, slider); + + private->saved_slider_value = + gimp_tool_line_get_slider (line, private->selection)->value; + + private->grab = GRAB_SELECTION; + } + } + else if (state & GRAB_LINE_MASK) + { + private->grab = GRAB_LINE; + } + + result = (private->grab != GRAB_NONE); + } + + if (! result) + { + private->hover = GIMP_TOOL_LINE_HANDLE_NONE; + + gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE); + } + + gimp_tool_line_update_handles (line); + gimp_tool_line_update_circle (line); + gimp_tool_line_update_status (line, state, TRUE); + + return result; +} + +void +gimp_tool_line_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + GimpToolLineGrab grab = private->grab; + + private->grab = GRAB_NONE; + + if (release_type == GIMP_BUTTON_RELEASE_CANCEL) + { + if (grab != GRAB_NONE) + { + if (grab == GRAB_SELECTION && + GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection)) + { + gimp_tool_line_get_slider (line, private->selection)->value = + private->saved_slider_value; + + if (private->remove_slider) + { + private->remove_slider = FALSE; + + g_signal_emit (line, line_signals[PREPARE_TO_REMOVE_SLIDER], 0, + private->selection, FALSE); + } + } + + g_object_set (line, + "x1", private->saved_x1, + "y1", private->saved_y1, + "x2", private->saved_x2, + "y2", private->saved_y2, + NULL); + } + } + else if (grab == GRAB_SELECTION) + { + if (private->remove_slider) + { + private->remove_slider = FALSE; + + g_signal_emit (line, line_signals[REMOVE_SLIDER], 0, + private->selection); + } + else if (release_type == GIMP_BUTTON_RELEASE_CLICK) + { + gboolean result; + + g_signal_emit (line, line_signals[HANDLE_CLICKED], 0, + private->selection, state, GIMP_BUTTON_PRESS_NORMAL, + &result); + } + } +} + +void +gimp_tool_line_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + gdouble diff_x = coords->x - private->mouse_x; + gdouble diff_y = coords->y - private->mouse_y; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + if (private->grab == GRAB_LINE) + { + g_object_set (line, + "x1", private->x1 + diff_x, + "y1", private->y1 + diff_y, + "x2", private->x2 + diff_x, + "y2", private->y2 + diff_y, + NULL); + } + else + { + gboolean constrain = (state & gimp_get_constrain_behavior_mask ()) != 0; + + gimp_tool_line_selection_motion (line, constrain); + } + + gimp_tool_line_update_status (line, state, TRUE); +} + +GimpHit +gimp_tool_line_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + + if (! (state & GRAB_LINE_MASK)) + { + gint hover = gimp_tool_line_get_hover (line, coords, state); + + if (hover != GIMP_TOOL_LINE_HANDLE_NONE) + return GIMP_HIT_DIRECT; + } + else + { + return GIMP_HIT_INDIRECT; + } + + return GIMP_HIT_NONE; +} + +void +gimp_tool_line_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + if (! (state & GRAB_LINE_MASK)) + private->hover = gimp_tool_line_get_hover (line, coords, state); + else + private->hover = GIMP_TOOL_LINE_HANDLE_NONE; + + gimp_tool_line_update_handles (line); + gimp_tool_line_update_circle (line); + gimp_tool_line_update_status (line, state, proximity); +} + +static void +gimp_tool_line_leave_notify (GimpToolWidget *widget) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + + private->hover = GIMP_TOOL_LINE_HANDLE_NONE; + + gimp_tool_line_update_handles (line); + gimp_tool_line_update_circle (line); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static gboolean +gimp_tool_line_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + GimpDisplayShell *shell; + gdouble pixels = 1.0; + gboolean move_line; + + move_line = kevent->state & GRAB_LINE_MASK; + + if (private->selection == GIMP_TOOL_LINE_HANDLE_NONE && ! move_line) + return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); + + shell = gimp_tool_widget_get_shell (widget); + + if (kevent->state & gimp_get_extend_selection_mask ()) + pixels = 10.0; + + if (kevent->state & gimp_get_toggle_behavior_mask ()) + pixels = 50.0; + + switch (kevent->keyval) + { + case GDK_KEY_Left: + case GDK_KEY_Right: + case GDK_KEY_Up: + case GDK_KEY_Down: + /* move an endpoint (or both endpoints) */ + if (private->selection < 0 || move_line) + { + gdouble xdist, ydist; + gdouble dx, dy; + + xdist = FUNSCALEX (shell, pixels); + ydist = FUNSCALEY (shell, pixels); + + dx = 0.0; + dy = 0.0; + + switch (kevent->keyval) + { + case GDK_KEY_Left: dx = -xdist; break; + case GDK_KEY_Right: dx = +xdist; break; + case GDK_KEY_Up: dy = -ydist; break; + case GDK_KEY_Down: dy = +ydist; break; + } + + if (private->selection == GIMP_TOOL_LINE_HANDLE_START || move_line) + { + g_object_set (line, + "x1", private->x1 + dx, + "y1", private->y1 + dy, + NULL); + } + + if (private->selection == GIMP_TOOL_LINE_HANDLE_END || move_line) + { + g_object_set (line, + "x2", private->x2 + dx, + "y2", private->y2 + dy, + NULL); + } + } + /* move a slider */ + else + { + GimpControllerSlider *slider; + gdouble dist; + gdouble dvalue; + + slider = gimp_tool_line_get_slider (line, private->selection); + + if (! slider->movable) + break; + + dist = gimp_canvas_item_transform_distance (private->line, + private->x1, private->y1, + private->x2, private->y2); + + if (dist > 0.0) + dist = pixels / dist; + + dvalue = 0.0; + + switch (kevent->keyval) + { + case GDK_KEY_Left: + if (private->x1 < private->x2) dvalue = -dist; + else if (private->x1 > private->x2) dvalue = +dist; + break; + + case GDK_KEY_Right: + if (private->x1 < private->x2) dvalue = +dist; + else if (private->x1 > private->x2) dvalue = -dist; + break; + + case GDK_KEY_Up: + if (private->y1 < private->y2) dvalue = -dist; + else if (private->y1 > private->y2) dvalue = +dist; + break; + + case GDK_KEY_Down: + if (private->y1 < private->y2) dvalue = +dist; + else if (private->y1 > private->y2) dvalue = -dist; + break; + } + + if (dvalue != 0.0) + { + slider->value += dvalue; + slider->value = CLAMP (slider->value, slider->min, slider->max); + slider->value = CLAMP (slider->value, 0.0, 1.0); + + g_object_set (line, + "sliders", private->sliders, + NULL); + } + } + return TRUE; + + case GDK_KEY_BackSpace: + case GDK_KEY_Delete: + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection)) + { + if (gimp_tool_line_get_slider (line, private->selection)->removable) + { + g_signal_emit (line, line_signals[REMOVE_SLIDER], 0, + private->selection); + } + } + return TRUE; + } + + return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); +} + +static void +gimp_tool_line_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + + if (key == gimp_get_constrain_behavior_mask ()) + { + gimp_tool_line_selection_motion (line, press); + + gimp_tool_line_update_status (line, state, TRUE); + } +} + +static gboolean +gimp_tool_line_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolLine *line = GIMP_TOOL_LINE (widget); + GimpToolLinePrivate *private = line->private; + + if (private->grab ==GRAB_LINE || (state & GRAB_LINE_MASK)) + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + + return TRUE; + } + else if (private->grab == GRAB_SELECTION || + private->hover > GIMP_TOOL_LINE_HANDLE_NONE) + { + const GimpControllerSlider *slider = NULL; + + if (private->grab == GRAB_SELECTION) + { + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection)) + slider = gimp_tool_line_get_slider (line, private->selection); + } + else if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover)) + { + slider = gimp_tool_line_get_slider (line, private->hover); + } + + if (private->grab == GRAB_SELECTION && slider && private->remove_slider) + { + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + + return TRUE; + } + else if (! slider || slider->movable) + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + + return TRUE; + } + } + else if (private->hover == HOVER_NEW_SLIDER) + { + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + + return TRUE; + } + + return FALSE; +} + +static gint +gimp_tool_line_get_hover (GimpToolLine *line, + const GimpCoords *coords, + GdkModifierType state) +{ + GimpToolLinePrivate *private = line->private; + gint hover = GIMP_TOOL_LINE_HANDLE_NONE; + gdouble min_dist; + gint first_handle; + gint i; + + /* find the closest handle to the cursor */ + min_dist = G_MAXDOUBLE; + first_handle = private->sliders->len - 1; + + /* skip the sliders if the two endpoints are the same, in particular so + * that if the line is created during a button-press event (as in the + * blend tool), the end endpoint is dragged, instead of a slider. + */ + if (private->x1 == private->x2 && private->y1 == private->y2) + first_handle = -1; + + for (i = first_handle; i > GIMP_TOOL_LINE_HANDLE_NONE; i--) + { + GimpCanvasItem *handle; + + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (i)) + { + const GimpControllerSlider *slider; + + slider = gimp_tool_line_get_slider (line, i); + + if (! slider->visible || ! slider->selectable) + continue; + } + + handle = gimp_tool_line_get_handle (line, i); + + if (gimp_tool_line_handle_hit (handle, + private->mouse_x, + private->mouse_y, + &min_dist)) + { + hover = i; + } + } + + if (hover == GIMP_TOOL_LINE_HANDLE_NONE) + { + gboolean constrain; + gdouble value; + gdouble dist; + + constrain = (state & gimp_get_constrain_behavior_mask ()) != 0; + + value = gimp_tool_line_project_point (line, + private->mouse_x, + private->mouse_y, + constrain, + &dist); + + if (value >= 0.0 && value <= 1.0 && dist <= LINE_VICINITY) + { + gboolean can_add; + + g_signal_emit (line, line_signals[CAN_ADD_SLIDER], 0, + value, &can_add); + + if (can_add) + { + hover = HOVER_NEW_SLIDER; + private->new_slider_value = value; + } + } + } + + return hover; +} + +static GimpControllerSlider * +gimp_tool_line_get_slider (GimpToolLine *line, + gint slider) +{ + GimpToolLinePrivate *private = line->private; + + gimp_assert (slider >= 0 && slider < private->sliders->len); + + return &g_array_index (private->sliders, GimpControllerSlider, slider); +} + +static GimpCanvasItem * +gimp_tool_line_get_handle (GimpToolLine *line, + gint handle) +{ + GimpToolLinePrivate *private = line->private; + + switch (handle) + { + case GIMP_TOOL_LINE_HANDLE_NONE: + return NULL; + + case GIMP_TOOL_LINE_HANDLE_START: + return private->start_handle; + + case GIMP_TOOL_LINE_HANDLE_END: + return private->end_handle; + + default: + gimp_assert (handle >= 0 && + handle < (gint) private->slider_handles->len); + + return g_array_index (private->slider_handles, + GimpCanvasItem *, handle); + } +} + +static gdouble +gimp_tool_line_project_point (GimpToolLine *line, + gdouble x, + gdouble y, + gboolean constrain, + gdouble *dist) +{ + GimpToolLinePrivate *private = line->private; + gdouble length_sqr; + gdouble value = 0.0; + + length_sqr = SQR (private->x2 - private->x1) + + SQR (private->y2 - private->y1); + + /* don't calculate the projection for 0-length lines, since we'll just get + * NaN. + */ + if (length_sqr > 0.0) + { + value = (private->x2 - private->x1) * (x - private->x1) + + (private->y2 - private->y1) * (y - private->y1); + value /= length_sqr; + + if (dist) + { + gdouble px; + gdouble py; + + px = private->x1 + (private->x2 - private->x1) * value; + py = private->y1 + (private->y2 - private->y1) * value; + + *dist = gimp_canvas_item_transform_distance (private->line, + x, y, + px, py); + } + + if (constrain) + value = RINT (12.0 * value) / 12.0; + } + else + { + if (dist) + { + *dist = gimp_canvas_item_transform_distance (private->line, + x, y, + private->x1, private->y1); + } + } + + return value; +} + +static gboolean +gimp_tool_line_selection_motion (GimpToolLine *line, + gboolean constrain) +{ + GimpToolLinePrivate *private = line->private; + gdouble x = private->mouse_x; + gdouble y = private->mouse_y; + + if (private->grab != GRAB_SELECTION) + return FALSE; + + switch (private->selection) + { + case GIMP_TOOL_LINE_HANDLE_NONE: + gimp_assert_not_reached (); + + case GIMP_TOOL_LINE_HANDLE_START: + if (constrain) + { + gimp_display_shell_constrain_line ( + gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (line)), + private->x2, private->y2, + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + + g_object_set (line, + "x1", x, + "y1", y, + NULL); + return TRUE; + + case GIMP_TOOL_LINE_HANDLE_END: + if (constrain) + { + gimp_display_shell_constrain_line ( + gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (line)), + private->x1, private->y1, + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + + g_object_set (line, + "x2", x, + "y2", y, + NULL); + return TRUE; + + default: + { + GimpDisplayShell *shell; + GimpControllerSlider *slider; + gdouble value; + gdouble dist; + gboolean remove_slider; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (line)); + + slider = gimp_tool_line_get_slider (line, private->selection); + + /* project the cursor position onto the line */ + value = gimp_tool_line_project_point (line, x, y, constrain, &dist); + + /* slider dragging */ + if (slider->movable) + { + value = CLAMP (value, slider->min, slider->max); + value = CLAMP (value, 0.0, 1.0); + + value = fabs (value); /* avoid negative zero */ + + slider->value = value; + + g_object_set (line, + "sliders", private->sliders, + NULL); + } + + /* slider tearing */ + remove_slider = slider->removable && dist > SLIDER_TEAR_DISTANCE; + + if (remove_slider != private->remove_slider) + { + private->remove_slider = remove_slider; + + g_signal_emit (line, line_signals[PREPARE_TO_REMOVE_SLIDER], 0, + private->selection, remove_slider); + + /* set the cursor modifier to a minus by talking to the shell + * directly -- eek! + */ + { + GimpCursorType cursor; + GimpToolCursorType tool_cursor; + GimpCursorModifier modifier; + + cursor = shell->current_cursor; + tool_cursor = shell->tool_cursor; + modifier = GIMP_CURSOR_MODIFIER_NONE; + + gimp_tool_line_get_cursor (GIMP_TOOL_WIDGET (line), NULL, 0, + &cursor, &tool_cursor, &modifier); + + gimp_display_shell_set_cursor (shell, cursor, tool_cursor, modifier); + } + + gimp_tool_line_update_handles (line); + gimp_tool_line_update_circle (line); + gimp_tool_line_update_status (line, + constrain ? + gimp_get_constrain_behavior_mask () : + 0, + TRUE); + } + + return TRUE; + } + } +} + +static void +gimp_tool_line_update_handles (GimpToolLine *line) +{ + GimpToolLinePrivate *private = line->private; + gdouble value; + gdouble dist; + gint i; + + value = gimp_tool_line_project_point (line, + private->mouse_x, + private->mouse_y, + FALSE, + &dist); + + for (i = 0; i < private->sliders->len; i++) + { + const GimpControllerSlider *slider; + GimpCanvasItem *handle; + gint size; + gint hit_radius; + gboolean show_autohidden; + gboolean visible; + + slider = gimp_tool_line_get_slider (line, i); + handle = gimp_tool_line_get_handle (line, i); + + size = slider->size * SLIDER_HANDLE_SIZE; + size = MAX (size, 1); + + hit_radius = (MAX (size, SLIDER_HANDLE_SIZE) * HANDLE_CIRCLE_SCALE) / 2; + + /* show a autohidden slider if it's selected, or if no other handle is + * grabbed or hovered-over, and the cursor is close enough to the line, + * between the slider's min and max values. + */ + show_autohidden = private->selection == i || + (private->grab == GRAB_NONE && + (private->hover <= GIMP_TOOL_LINE_HANDLE_NONE || + private->hover == i) && + dist <= hit_radius && + value >= slider->min && + value <= slider->max); + + visible = slider->visible && + (! slider->autohide || show_autohidden) && + ! (private->selection == i && private->remove_slider); + + handle = gimp_tool_line_get_handle (line, i); + + if (visible) + { + g_object_set (handle, + "type", slider->type, + "width", size, + "height", size, + NULL); + } + + gimp_canvas_item_set_visible (handle, visible); + } +} + +static void +gimp_tool_line_update_circle (GimpToolLine *line) +{ + GimpToolLinePrivate *private = line->private; + gboolean visible; + + visible = (private->grab == GRAB_NONE && + private->hover != GIMP_TOOL_LINE_HANDLE_NONE) || + (private->grab == GRAB_SELECTION && + private->remove_slider); + + if (visible) + { + gdouble x; + gdouble y; + gint width; + gint height; + gboolean dashed; + + if (private->grab == GRAB_NONE && private->hover == HOVER_NEW_SLIDER) + { + /* new slider */ + x = private->x1 + + (private->x2 - private->x1) * private->new_slider_value; + y = private->y1 + + (private->y2 - private->y1) * private->new_slider_value; + + width = height = SLIDER_HANDLE_SIZE; + + dashed = TRUE; + } + else + { + GimpCanvasItem *handle; + + if (private->grab == GRAB_SELECTION) + { + /* tear slider */ + handle = gimp_tool_line_get_handle (line, private->selection); + dashed = TRUE; + } + else + { + /* hover over handle */ + handle = gimp_tool_line_get_handle (line, private->hover); + dashed = FALSE; + } + + gimp_canvas_handle_get_position (handle, &x, &y); + gimp_canvas_handle_get_size (handle, &width, &height); + } + + width = MAX (width, SLIDER_HANDLE_SIZE); + height = MAX (height, SLIDER_HANDLE_SIZE); + + width *= HANDLE_CIRCLE_SCALE; + height *= HANDLE_CIRCLE_SCALE; + + gimp_canvas_handle_set_position (private->handle_circle, x, y); + gimp_canvas_handle_set_size (private->handle_circle, width, height); + + g_object_set (private->handle_circle, + "type", dashed ? GIMP_HANDLE_DASHED_CIRCLE : + GIMP_HANDLE_CIRCLE, + NULL); + } + + gimp_canvas_item_set_visible (private->handle_circle, visible); +} + +static void +gimp_tool_line_update_hilight (GimpToolLine *line) +{ + GimpToolLinePrivate *private = line->private; + gboolean focus; + gint i; + + focus = gimp_tool_widget_get_focus (GIMP_TOOL_WIDGET (line)); + + for (i = GIMP_TOOL_LINE_HANDLE_NONE + 1; + i < (gint) private->sliders->len; + i++) + { + GimpCanvasItem *handle; + + handle = gimp_tool_line_get_handle (line, i); + + gimp_canvas_item_set_highlight (handle, focus && i == private->selection); + } +} + +static void +gimp_tool_line_update_status (GimpToolLine *line, + GdkModifierType state, + gboolean proximity) +{ + GimpToolLinePrivate *private = line->private; + + if (proximity) + { + GimpDisplayShell *shell; + const gchar *toggle_behavior_format = NULL; + const gchar *message = NULL; + gchar *line_status = NULL; + gchar *status; + gint handle; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (line)); + + if (private->grab == GRAB_SELECTION) + handle = private->selection; + else + handle = private->hover; + + if (handle == GIMP_TOOL_LINE_HANDLE_START || + handle == GIMP_TOOL_LINE_HANDLE_END) + { + line_status = gimp_display_shell_get_line_status (shell, + _("Click-Drag to move the endpoint"), + ". ", + private->x1, + private->y1, + private->x2, + private->y2); + toggle_behavior_format = _("%s for constrained angles"); + } + else if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (handle) || + handle == HOVER_NEW_SLIDER) + { + if (private->grab == GRAB_SELECTION && private->remove_slider) + { + message = _("Release to remove the slider"); + } + else + { + toggle_behavior_format = _("%s for constrained values"); + + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (handle)) + { + if (gimp_tool_line_get_slider (line, handle)->movable) + { + if (gimp_tool_line_get_slider (line, handle)->removable) + { + if (private->grab == GRAB_SELECTION) + { + message = _("Click-Drag to move the slider; " + "drag away to remove the slider"); + } + else + { + message = _("Click-Drag to move or remove the slider"); + } + } + else + { + message = _("Click-Drag to move the slider"); + } + } + else + { + toggle_behavior_format = NULL; + + if (gimp_tool_line_get_slider (line, handle)->removable) + { + if (private->grab == GRAB_SELECTION) + { + message = _("Click-Drag away to remove the slider"); + } + else + { + message = _("Click-Drag to remove the slider"); + } + } + else + { + message = NULL; + } + } + } + else + { + message = _("Click or Click-Drag to add a new slider"); + } + } + } + else if (state & GDK_MOD1_MASK) + { + message = _("Click-Drag to move the line"); + } + + status = + gimp_suggest_modifiers (message ? message : (line_status ? line_status : ""), + ((toggle_behavior_format ? + gimp_get_constrain_behavior_mask () : 0) | + (private->grab == GRAB_NONE ? + GDK_MOD1_MASK : 0)) & + ~state, + NULL, + toggle_behavior_format, + _("%s to move the whole line")); + + if (message || line_status) + { + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (line), status); + } + else + { + line_status = gimp_display_shell_get_line_status (shell, + private->status_title, + ". ", + private->x1, + private->y1, + private->x2, + private->y2); + gimp_tool_widget_set_status_coords (GIMP_TOOL_WIDGET (line), + line_status, + private->x2 - private->x1, + ", ", + private->y2 - private->y1, + status); + } + + g_free (status); + if (line_status) + g_free (line_status); + } + else + { + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (line), NULL); + } +} + +static gboolean +gimp_tool_line_handle_hit (GimpCanvasItem *handle, + gdouble x, + gdouble y, + gdouble *min_dist) +{ + gdouble handle_x; + gdouble handle_y; + gint handle_width; + gint handle_height; + gint radius; + gdouble dist; + + gimp_canvas_handle_get_position (handle, &handle_x, &handle_y); + gimp_canvas_handle_get_size (handle, &handle_width, &handle_height); + + handle_width = MAX (handle_width, SLIDER_HANDLE_SIZE); + handle_height = MAX (handle_height, SLIDER_HANDLE_SIZE); + + radius = ((gint) (handle_width * HANDLE_CIRCLE_SCALE)) / 2; + radius = MAX (radius, LINE_VICINITY); + + dist = gimp_canvas_item_transform_distance (handle, + x, y, handle_x, handle_y); + + if (dist <= radius && dist < *min_dist) + { + *min_dist = dist; + + return TRUE; + } + else + { + return FALSE; + } +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_line_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_LINE, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} + +void +gimp_tool_line_set_sliders (GimpToolLine *line, + const GimpControllerSlider *sliders, + gint n_sliders) +{ + GimpToolLinePrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_LINE (line)); + g_return_if_fail (n_sliders == 0 || (n_sliders > 0 && sliders != NULL)); + + private = line->private; + + if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) && + private->sliders->len != n_sliders) + { + gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE); + } + + g_array_set_size (private->sliders, n_sliders); + + memcpy (private->sliders->data, sliders, + n_sliders * sizeof (GimpControllerSlider)); + + g_object_set (line, + "sliders", private->sliders, + NULL); +} + +const GimpControllerSlider * +gimp_tool_line_get_sliders (GimpToolLine *line, + gint *n_sliders) +{ + GimpToolLinePrivate *private; + + g_return_val_if_fail (GIMP_IS_TOOL_LINE (line), NULL); + + private = line->private; + + if (n_sliders) *n_sliders = private->sliders->len; + + return (const GimpControllerSlider *) private->sliders->data; +} + +void +gimp_tool_line_set_selection (GimpToolLine *line, + gint handle) +{ + GimpToolLinePrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_LINE (line)); + + private = line->private; + + g_return_if_fail (handle >= GIMP_TOOL_LINE_HANDLE_NONE && + handle < (gint) private->sliders->len); + + g_object_set (line, + "selection", handle, + NULL); +} + +gint +gimp_tool_line_get_selection (GimpToolLine *line) +{ + GimpToolLinePrivate *private; + + g_return_val_if_fail (GIMP_IS_TOOL_LINE (line), GIMP_TOOL_LINE_HANDLE_NONE); + + private = line->private; + + return private->selection; +} diff --git a/app/display/gimptoolline.h b/app/display/gimptoolline.h new file mode 100644 index 0000000..80d046d --- /dev/null +++ b/app/display/gimptoolline.h @@ -0,0 +1,99 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolline.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_LINE_H__ +#define __GIMP_TOOL_LINE_H__ + + +#include "gimptoolwidget.h" + + +/* in the context of GimpToolLine, "handle" is a collective term for either an + * endpoint or a slider. a handle value may be either a (nonnegative) slider + * index, or one of the values below: + */ +#define GIMP_TOOL_LINE_HANDLE_NONE (-3) +#define GIMP_TOOL_LINE_HANDLE_START (-2) +#define GIMP_TOOL_LINE_HANDLE_END (-1) + +#define GIMP_TOOL_LINE_HANDLE_IS_SLIDER(handle) ((handle) >= 0) + + +#define GIMP_TYPE_TOOL_LINE (gimp_tool_line_get_type ()) +#define GIMP_TOOL_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_LINE, GimpToolLine)) +#define GIMP_TOOL_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_LINE, GimpToolLineClass)) +#define GIMP_IS_TOOL_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_LINE)) +#define GIMP_IS_TOOL_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_LINE)) +#define GIMP_TOOL_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_LINE, GimpToolLineClass)) + + +typedef struct _GimpToolLine GimpToolLine; +typedef struct _GimpToolLinePrivate GimpToolLinePrivate; +typedef struct _GimpToolLineClass GimpToolLineClass; + +struct _GimpToolLine +{ + GimpToolWidget parent_instance; + + GimpToolLinePrivate *private; +}; + +struct _GimpToolLineClass +{ + GimpToolWidgetClass parent_class; + + /* signals */ + gboolean (* can_add_slider) (GimpToolLine *line, + gdouble value); + gint (* add_slider) (GimpToolLine *line, + gdouble value); + void (* prepare_to_remove_slider) (GimpToolLine *line, + gint slider, + gboolean remove); + void (* remove_slider) (GimpToolLine *line, + gint slider); + void (* selection_changed) (GimpToolLine *line); + gboolean (* handle_clicked) (GimpToolLine *line, + gint handle, + GdkModifierType state, + GimpButtonPressType press_type); +}; + + +GType gimp_tool_line_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_line_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + +void gimp_tool_line_set_sliders (GimpToolLine *line, + const GimpControllerSlider *sliders, + gint n_sliders); +const GimpControllerSlider * gimp_tool_line_get_sliders (GimpToolLine *line, + gint *n_sliders); + +void gimp_tool_line_set_selection (GimpToolLine *line, + gint handle); +gint gimp_tool_line_get_selection (GimpToolLine *line); + + +#endif /* __GIMP_TOOL_LINE_H__ */ diff --git a/app/display/gimptoolpath.c b/app/display/gimptoolpath.c new file mode 100644 index 0000000..9e2cf54 --- /dev/null +++ b/app/display/gimptoolpath.c @@ -0,0 +1,1904 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolpath.c + * Copyright (C) 2017 Michael Natterer + * + * Vector tool + * Copyright (C) 2003 Simon Budig + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "display-types.h" + +#include "vectors/gimpanchor.h" +#include "vectors/gimpbezierstroke.h" +#include "vectors/gimpvectors.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "tools/gimptools-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvasitem-utils.h" +#include "gimpcanvasline.h" +#include "gimpcanvaspath.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimptoolpath.h" + +#include "gimp-intl.h" + + +#define TOGGLE_MASK gimp_get_extend_selection_mask () +#define MOVE_MASK GDK_MOD1_MASK +#define INSDEL_MASK gimp_get_toggle_behavior_mask () + + +/* possible vector functions */ +typedef enum +{ + VECTORS_SELECT_VECTOR, + VECTORS_CREATE_VECTOR, + VECTORS_CREATE_STROKE, + VECTORS_ADD_ANCHOR, + VECTORS_MOVE_ANCHOR, + VECTORS_MOVE_ANCHORSET, + VECTORS_MOVE_HANDLE, + VECTORS_MOVE_CURVE, + VECTORS_MOVE_STROKE, + VECTORS_MOVE_VECTORS, + VECTORS_INSERT_ANCHOR, + VECTORS_DELETE_ANCHOR, + VECTORS_CONNECT_STROKES, + VECTORS_DELETE_SEGMENT, + VECTORS_CONVERT_EDGE, + VECTORS_FINISHED +} GimpVectorFunction; + +enum +{ + PROP_0, + PROP_VECTORS, + PROP_EDIT_MODE, + PROP_POLYGONAL +}; + +enum +{ + BEGIN_CHANGE, + END_CHANGE, + ACTIVATE, + LAST_SIGNAL +}; + +struct _GimpToolPathPrivate +{ + GimpVectors *vectors; /* the current Vector data */ + GimpVectorMode edit_mode; + gboolean polygonal; + + GimpVectorFunction function; /* function we're performing */ + GimpAnchorFeatureType restriction; /* movement restriction */ + gboolean modifier_lock; /* can we toggle the Shift key? */ + GdkModifierType saved_state; /* modifier state at button_press */ + gdouble last_x; /* last x coordinate */ + gdouble last_y; /* last y coordinate */ + gboolean undo_motion; /* we need a motion to have an undo */ + gboolean have_undo; /* did we push an undo at */ + /* ..._button_press? */ + + GimpAnchor *cur_anchor; /* the current Anchor */ + GimpAnchor *cur_anchor2; /* secondary Anchor (end on_curve) */ + GimpStroke *cur_stroke; /* the current Stroke */ + gdouble cur_position; /* the current Position on a segment */ + + gint sel_count; /* number of selected anchors */ + GimpAnchor *sel_anchor; /* currently selected anchor, NULL */ + /* if multiple anchors are selected */ + GimpStroke *sel_stroke; /* selected stroke */ + + GimpVectorMode saved_mode; /* used by modifier_key() */ + + GimpCanvasItem *path; + GList *items; +}; + + +/* local function prototypes */ + +static void gimp_tool_path_constructed (GObject *object); +static void gimp_tool_path_dispose (GObject *object); +static void gimp_tool_path_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_path_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_path_changed (GimpToolWidget *widget); +static gint gimp_tool_path_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_path_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_path_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_path_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_path_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static gboolean gimp_tool_path_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static gboolean gimp_tool_path_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static GimpVectorFunction + gimp_tool_path_get_function (GimpToolPath *path, + const GimpCoords *coords, + GdkModifierType state); + +static void gimp_tool_path_update_status (GimpToolPath *path, + GdkModifierType state, + gboolean proximity); + +static void gimp_tool_path_begin_change (GimpToolPath *path, + const gchar *desc); +static void gimp_tool_path_end_change (GimpToolPath *path, + gboolean success); + +static void gimp_tool_path_vectors_visible (GimpVectors *vectors, + GimpToolPath *path); +static void gimp_tool_path_vectors_freeze (GimpVectors *vectors, + GimpToolPath *path); +static void gimp_tool_path_vectors_thaw (GimpVectors *vectors, + GimpToolPath *path); +static void gimp_tool_path_verify_state (GimpToolPath *path); + +static void gimp_tool_path_move_selected_anchors + (GimpToolPath *path, + gdouble x, + gdouble y); +static void gimp_tool_path_delete_selected_anchors + (GimpToolPath *path); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolPath, gimp_tool_path, GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_path_parent_class + +static guint path_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_tool_path_class_init (GimpToolPathClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_path_constructed; + object_class->dispose = gimp_tool_path_dispose; + object_class->set_property = gimp_tool_path_set_property; + object_class->get_property = gimp_tool_path_get_property; + + widget_class->changed = gimp_tool_path_changed; + widget_class->focus_changed = gimp_tool_path_changed; + widget_class->button_press = gimp_tool_path_button_press; + widget_class->button_release = gimp_tool_path_button_release; + widget_class->motion = gimp_tool_path_motion; + widget_class->hit = gimp_tool_path_hit; + widget_class->hover = gimp_tool_path_hover; + widget_class->key_press = gimp_tool_path_key_press; + widget_class->get_cursor = gimp_tool_path_get_cursor; + + path_signals[BEGIN_CHANGE] = + g_signal_new ("begin-change", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolPathClass, begin_change), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + path_signals[END_CHANGE] = + g_signal_new ("end-change", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolPathClass, end_change), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + path_signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolPathClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__FLAGS, + G_TYPE_NONE, 1, + GDK_TYPE_MODIFIER_TYPE); + + g_object_class_install_property (object_class, PROP_VECTORS, + g_param_spec_object ("vectors", NULL, NULL, + GIMP_TYPE_VECTORS, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_EDIT_MODE, + g_param_spec_enum ("edit-mode", + _("Edit Mode"), + NULL, + GIMP_TYPE_VECTOR_MODE, + GIMP_VECTOR_MODE_DESIGN, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_POLYGONAL, + g_param_spec_boolean ("polygonal", + _("Polygonal"), + _("Restrict editing to polygons"), + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_path_init (GimpToolPath *path) +{ + path->private = gimp_tool_path_get_instance_private (path); +} + +static void +gimp_tool_path_constructed (GObject *object) +{ + GimpToolPath *path = GIMP_TOOL_PATH (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolPathPrivate *private = path->private; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + private->path = gimp_tool_widget_add_path (widget, NULL); + + gimp_tool_path_changed (widget); +} + +static void +gimp_tool_path_dispose (GObject *object) +{ + GimpToolPath *path = GIMP_TOOL_PATH (object); + + gimp_tool_path_set_vectors (path, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_tool_path_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolPath *path = GIMP_TOOL_PATH (object); + GimpToolPathPrivate *private = path->private; + + switch (property_id) + { + case PROP_VECTORS: + gimp_tool_path_set_vectors (path, g_value_get_object (value)); + break; + case PROP_EDIT_MODE: + private->edit_mode = g_value_get_enum (value); + break; + case PROP_POLYGONAL: + private->polygonal = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_path_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolPath *path = GIMP_TOOL_PATH (object); + GimpToolPathPrivate *private = path->private; + + switch (property_id) + { + case PROP_VECTORS: + g_value_set_object (value, private->vectors); + break; + case PROP_EDIT_MODE: + g_value_set_enum (value, private->edit_mode); + break; + case PROP_POLYGONAL: + g_value_set_boolean (value, private->polygonal); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +item_remove_func (GimpCanvasItem *item, + GimpToolWidget *widget) +{ + gimp_tool_widget_remove_item (widget, item); +} + +static void +gimp_tool_path_changed (GimpToolWidget *widget) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + GimpVectors *vectors = private->vectors; + + if (private->items) + { + g_list_foreach (private->items, (GFunc) item_remove_func, widget); + g_list_free (private->items); + private->items = NULL; + } + + if (vectors && gimp_vectors_get_bezier (vectors)) + { + GimpStroke *cur_stroke; + + gimp_canvas_path_set (private->path, + gimp_vectors_get_bezier (vectors)); + gimp_canvas_item_set_visible (private->path, + ! gimp_item_get_visible (GIMP_ITEM (vectors))); + + for (cur_stroke = gimp_vectors_stroke_get_next (vectors, NULL); + cur_stroke; + cur_stroke = gimp_vectors_stroke_get_next (vectors, cur_stroke)) + { + GimpCanvasItem *item; + GArray *coords; + GList *draw_anchors; + GList *list; + + /* anchor handles */ + draw_anchors = gimp_stroke_get_draw_anchors (cur_stroke); + + for (list = draw_anchors; list; list = g_list_next (list)) + { + GimpAnchor *cur_anchor = GIMP_ANCHOR (list->data); + + if (cur_anchor->type == GIMP_ANCHOR_ANCHOR) + { + item = + gimp_tool_widget_add_handle (widget, + cur_anchor->selected ? + GIMP_HANDLE_CIRCLE : + GIMP_HANDLE_FILLED_CIRCLE, + cur_anchor->position.x, + cur_anchor->position.y, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + + private->items = g_list_prepend (private->items, item); + } + } + + g_list_free (draw_anchors); + + if (private->sel_count <= 2) + { + /* the lines to the control handles */ + coords = gimp_stroke_get_draw_lines (cur_stroke); + + if (coords) + { + if (coords->len % 2 == 0) + { + gint i; + + for (i = 0; i < coords->len; i += 2) + { + item = gimp_tool_widget_add_line + (widget, + g_array_index (coords, GimpCoords, i).x, + g_array_index (coords, GimpCoords, i).y, + g_array_index (coords, GimpCoords, i + 1).x, + g_array_index (coords, GimpCoords, i + 1).y); + + if (gimp_tool_widget_get_focus (widget)) + gimp_canvas_item_set_highlight (item, TRUE); + + private->items = g_list_prepend (private->items, item); + } + } + + g_array_free (coords, TRUE); + } + + /* control handles */ + draw_anchors = gimp_stroke_get_draw_controls (cur_stroke); + + for (list = draw_anchors; list; list = g_list_next (list)) + { + GimpAnchor *cur_anchor = GIMP_ANCHOR (list->data); + + item = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_SQUARE, + cur_anchor->position.x, + cur_anchor->position.y, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE - 3, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE - 3, + GIMP_HANDLE_ANCHOR_CENTER); + + private->items = g_list_prepend (private->items, item); + } + + g_list_free (draw_anchors); + } + } + } + else + { + gimp_canvas_path_set (private->path, NULL); + } +} + +static gboolean +gimp_tool_path_check_writable (GimpToolPath *path) +{ + GimpToolPathPrivate *private = path->private; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (path); + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + + if (gimp_item_is_content_locked (GIMP_ITEM (private->vectors)) || + gimp_item_is_position_locked (GIMP_ITEM (private->vectors))) + { + gimp_tool_widget_message_literal (GIMP_TOOL_WIDGET (path), + _("The active path is locked.")); + + /* FIXME: this should really be done by the tool */ + gimp_tools_blink_lock_box (shell->display->gimp, + GIMP_ITEM (private->vectors)); + + private->function = VECTORS_FINISHED; + + return FALSE; + } + + return TRUE; +} + +gboolean +gimp_tool_path_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + + /* do nothing if we are in a FINISHED state */ + if (private->function == VECTORS_FINISHED) + return 0; + + g_return_val_if_fail (private->vectors != NULL || + private->function == VECTORS_SELECT_VECTOR || + private->function == VECTORS_CREATE_VECTOR, 0); + + private->undo_motion = FALSE; + + /* save the current modifier state */ + + private->saved_state = state; + + + /* select a vectors object */ + + if (private->function == VECTORS_SELECT_VECTOR) + { + GimpVectors *vectors; + + if (gimp_canvas_item_on_vectors (private->path, + coords, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + NULL, NULL, NULL, NULL, NULL, &vectors)) + { + gimp_tool_path_set_vectors (path, vectors); + } + + private->function = VECTORS_FINISHED; + } + + + /* create a new vector from scratch */ + + if (private->function == VECTORS_CREATE_VECTOR) + { + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpImage *image = gimp_display_get_image (shell->display); + GimpVectors *vectors; + + vectors = gimp_vectors_new (image, _("Unnamed")); + g_object_ref_sink (vectors); + + /* Undo step gets added implicitly */ + private->have_undo = TRUE; + + private->undo_motion = TRUE; + + gimp_tool_path_set_vectors (path, vectors); + g_object_unref (vectors); + + private->function = VECTORS_CREATE_STROKE; + } + + + gimp_vectors_freeze (private->vectors); + + /* create a new stroke */ + + if (private->function == VECTORS_CREATE_STROKE && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Add Stroke")); + private->undo_motion = TRUE; + + private->cur_stroke = gimp_bezier_stroke_new (); + gimp_vectors_stroke_add (private->vectors, private->cur_stroke); + g_object_unref (private->cur_stroke); + + private->sel_stroke = private->cur_stroke; + private->cur_anchor = NULL; + private->sel_anchor = NULL; + private->function = VECTORS_ADD_ANCHOR; + } + + + /* add an anchor to an existing stroke */ + + if (private->function == VECTORS_ADD_ANCHOR && + gimp_tool_path_check_writable (path)) + { + GimpCoords position = GIMP_COORDS_DEFAULT_VALUES; + + position.x = coords->x; + position.y = coords->y; + + gimp_tool_path_begin_change (path, _("Add Anchor")); + private->undo_motion = TRUE; + + private->cur_anchor = gimp_bezier_stroke_extend (private->sel_stroke, + &position, + private->sel_anchor, + EXTEND_EDITABLE); + + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + + if (! private->polygonal) + private->function = VECTORS_MOVE_HANDLE; + else + private->function = VECTORS_MOVE_ANCHOR; + + private->cur_stroke = private->sel_stroke; + } + + + /* insertion of an anchor in a curve segment */ + + if (private->function == VECTORS_INSERT_ANCHOR && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Insert Anchor")); + private->undo_motion = TRUE; + + private->cur_anchor = gimp_stroke_anchor_insert (private->cur_stroke, + private->cur_anchor, + private->cur_position); + if (private->cur_anchor) + { + if (private->polygonal) + { + gimp_stroke_anchor_convert (private->cur_stroke, + private->cur_anchor, + GIMP_ANCHOR_FEATURE_EDGE); + } + + private->function = VECTORS_MOVE_ANCHOR; + } + else + { + private->function = VECTORS_FINISHED; + } + } + + + /* move a handle */ + + if (private->function == VECTORS_MOVE_HANDLE && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Drag Handle")); + + if (private->cur_anchor->type == GIMP_ANCHOR_ANCHOR) + { + if (! private->cur_anchor->selected) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor, + TRUE, TRUE); + private->undo_motion = TRUE; + } + + gimp_canvas_item_on_vectors_handle (private->path, + private->vectors, coords, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_ANCHOR_CONTROL, TRUE, + &private->cur_anchor, + &private->cur_stroke); + if (! private->cur_anchor) + private->function = VECTORS_FINISHED; + } + } + + + /* move an anchor */ + + if (private->function == VECTORS_MOVE_ANCHOR && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Drag Anchor")); + + if (! private->cur_anchor->selected) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor, + TRUE, TRUE); + private->undo_motion = TRUE; + } + } + + + /* move multiple anchors */ + + if (private->function == VECTORS_MOVE_ANCHORSET && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Drag Anchors")); + + if (state & TOGGLE_MASK) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor, + !private->cur_anchor->selected, + FALSE); + private->undo_motion = TRUE; + + if (private->cur_anchor->selected == FALSE) + private->function = VECTORS_FINISHED; + } + } + + + /* move a curve segment directly */ + + if (private->function == VECTORS_MOVE_CURVE && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Drag Curve")); + + /* the magic numbers are taken from the "feel good" parameter + * from gimp_bezier_stroke_point_move_relative in gimpbezierstroke.c. */ + if (private->cur_position < 5.0 / 6.0) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor, TRUE, TRUE); + private->undo_motion = TRUE; + } + + if (private->cur_position > 1.0 / 6.0) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor2, TRUE, + (private->cur_position >= 5.0 / 6.0)); + private->undo_motion = TRUE; + } + + } + + + /* connect two strokes */ + + if (private->function == VECTORS_CONNECT_STROKES && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Connect Strokes")); + private->undo_motion = TRUE; + + gimp_stroke_connect_stroke (private->sel_stroke, + private->sel_anchor, + private->cur_stroke, + private->cur_anchor); + + if (private->cur_stroke != private->sel_stroke && + gimp_stroke_is_empty (private->cur_stroke)) + { + gimp_vectors_stroke_remove (private->vectors, + private->cur_stroke); + } + + private->sel_anchor = private->cur_anchor; + private->cur_stroke = private->sel_stroke; + + gimp_vectors_anchor_select (private->vectors, + private->sel_stroke, + private->sel_anchor, TRUE, TRUE); + + private->function = VECTORS_FINISHED; + } + + + /* move a stroke or all strokes of a vectors object */ + + if ((private->function == VECTORS_MOVE_STROKE || + private->function == VECTORS_MOVE_VECTORS) && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Drag Path")); + + /* Work is being done in gimp_tool_path_motion()... */ + } + + + /* convert an anchor to something that looks like an edge */ + + if (private->function == VECTORS_CONVERT_EDGE && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Convert Edge")); + private->undo_motion = TRUE; + + gimp_stroke_anchor_convert (private->cur_stroke, + private->cur_anchor, + GIMP_ANCHOR_FEATURE_EDGE); + + if (private->cur_anchor->type == GIMP_ANCHOR_ANCHOR) + { + gimp_vectors_anchor_select (private->vectors, + private->cur_stroke, + private->cur_anchor, TRUE, TRUE); + + private->function = VECTORS_MOVE_ANCHOR; + } + else + { + private->cur_stroke = NULL; + private->cur_anchor = NULL; + + /* avoid doing anything stupid */ + private->function = VECTORS_FINISHED; + } + } + + + /* removal of a node in a stroke */ + + if (private->function == VECTORS_DELETE_ANCHOR && + gimp_tool_path_check_writable (path)) + { + gimp_tool_path_begin_change (path, _("Delete Anchor")); + private->undo_motion = TRUE; + + gimp_stroke_anchor_delete (private->cur_stroke, + private->cur_anchor); + + if (gimp_stroke_is_empty (private->cur_stroke)) + gimp_vectors_stroke_remove (private->vectors, + private->cur_stroke); + + private->cur_stroke = NULL; + private->cur_anchor = NULL; + private->function = VECTORS_FINISHED; + } + + + /* deleting a segment (opening up a stroke) */ + + if (private->function == VECTORS_DELETE_SEGMENT && + gimp_tool_path_check_writable (path)) + { + GimpStroke *new_stroke; + + gimp_tool_path_begin_change (path, _("Delete Segment")); + private->undo_motion = TRUE; + + new_stroke = gimp_stroke_open (private->cur_stroke, + private->cur_anchor); + if (new_stroke) + { + gimp_vectors_stroke_add (private->vectors, new_stroke); + g_object_unref (new_stroke); + } + + private->cur_stroke = NULL; + private->cur_anchor = NULL; + private->function = VECTORS_FINISHED; + } + + private->last_x = coords->x; + private->last_y = coords->y; + + gimp_vectors_thaw (private->vectors); + + return 1; +} + +void +gimp_tool_path_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + + private->function = VECTORS_FINISHED; + + if (private->have_undo) + { + if (! private->undo_motion || + release_type == GIMP_BUTTON_RELEASE_CANCEL) + { + gimp_tool_path_end_change (path, FALSE); + } + else + { + gimp_tool_path_end_change (path, TRUE); + } + } +} + +void +gimp_tool_path_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + GimpCoords position = GIMP_COORDS_DEFAULT_VALUES; + GimpAnchor *anchor; + + if (private->function == VECTORS_FINISHED) + return; + + position.x = coords->x; + position.y = coords->y; + + gimp_vectors_freeze (private->vectors); + + if ((private->saved_state & TOGGLE_MASK) != (state & TOGGLE_MASK)) + private->modifier_lock = FALSE; + + if (! private->modifier_lock) + { + if (state & TOGGLE_MASK) + { + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + } + else + { + private->restriction = GIMP_ANCHOR_FEATURE_NONE; + } + } + + switch (private->function) + { + case VECTORS_MOVE_ANCHOR: + case VECTORS_MOVE_HANDLE: + anchor = private->cur_anchor; + + if (anchor) + { + gimp_stroke_anchor_move_absolute (private->cur_stroke, + private->cur_anchor, + &position, + private->restriction); + private->undo_motion = TRUE; + } + break; + + case VECTORS_MOVE_CURVE: + if (private->polygonal) + { + gimp_tool_path_move_selected_anchors (path, + coords->x - private->last_x, + coords->y - private->last_y); + private->undo_motion = TRUE; + } + else + { + gimp_stroke_point_move_absolute (private->cur_stroke, + private->cur_anchor, + private->cur_position, + &position, + private->restriction); + private->undo_motion = TRUE; + } + break; + + case VECTORS_MOVE_ANCHORSET: + gimp_tool_path_move_selected_anchors (path, + coords->x - private->last_x, + coords->y - private->last_y); + private->undo_motion = TRUE; + break; + + case VECTORS_MOVE_STROKE: + if (private->cur_stroke) + { + gimp_stroke_translate (private->cur_stroke, + coords->x - private->last_x, + coords->y - private->last_y); + private->undo_motion = TRUE; + } + else if (private->sel_stroke) + { + gimp_stroke_translate (private->sel_stroke, + coords->x - private->last_x, + coords->y - private->last_y); + private->undo_motion = TRUE; + } + break; + + case VECTORS_MOVE_VECTORS: + gimp_item_translate (GIMP_ITEM (private->vectors), + coords->x - private->last_x, + coords->y - private->last_y, FALSE); + private->undo_motion = TRUE; + break; + + default: + break; + } + + gimp_vectors_thaw (private->vectors); + + private->last_x = coords->x; + private->last_y = coords->y; +} + +GimpHit +gimp_tool_path_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + + switch (gimp_tool_path_get_function (path, coords, state)) + { + case VECTORS_SELECT_VECTOR: + case VECTORS_MOVE_ANCHOR: + case VECTORS_MOVE_ANCHORSET: + case VECTORS_MOVE_HANDLE: + case VECTORS_MOVE_CURVE: + case VECTORS_MOVE_STROKE: + case VECTORS_DELETE_ANCHOR: + case VECTORS_DELETE_SEGMENT: + case VECTORS_INSERT_ANCHOR: + case VECTORS_CONNECT_STROKES: + case VECTORS_CONVERT_EDGE: + return GIMP_HIT_DIRECT; + + case VECTORS_CREATE_VECTOR: + case VECTORS_CREATE_STROKE: + case VECTORS_ADD_ANCHOR: + case VECTORS_MOVE_VECTORS: + return GIMP_HIT_INDIRECT; + + case VECTORS_FINISHED: + return GIMP_HIT_NONE; + } + + return GIMP_HIT_NONE; +} + +void +gimp_tool_path_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + + private->function = gimp_tool_path_get_function (path, coords, state); + + gimp_tool_path_update_status (path, state, proximity); +} + +static gboolean +gimp_tool_path_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + GimpDisplayShell *shell; + gdouble xdist, ydist; + gdouble pixels = 1.0; + + if (! private->vectors) + return FALSE; + + shell = gimp_tool_widget_get_shell (widget); + + if (kevent->state & gimp_get_extend_selection_mask ()) + pixels = 10.0; + + if (kevent->state & gimp_get_toggle_behavior_mask ()) + pixels = 50.0; + + switch (kevent->keyval) + { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + g_signal_emit (path, path_signals[ACTIVATE], 0, + kevent->state); + break; + + case GDK_KEY_BackSpace: + case GDK_KEY_Delete: + gimp_tool_path_delete_selected_anchors (path); + break; + + case GDK_KEY_Left: + case GDK_KEY_Right: + case GDK_KEY_Up: + case GDK_KEY_Down: + xdist = FUNSCALEX (shell, pixels); + ydist = FUNSCALEY (shell, pixels); + + gimp_tool_path_begin_change (path, _("Move Anchors")); + gimp_vectors_freeze (private->vectors); + + switch (kevent->keyval) + { + case GDK_KEY_Left: + gimp_tool_path_move_selected_anchors (path, -xdist, 0); + break; + + case GDK_KEY_Right: + gimp_tool_path_move_selected_anchors (path, xdist, 0); + break; + + case GDK_KEY_Up: + gimp_tool_path_move_selected_anchors (path, 0, -ydist); + break; + + case GDK_KEY_Down: + gimp_tool_path_move_selected_anchors (path, 0, ydist); + break; + + default: + break; + } + + gimp_vectors_thaw (private->vectors); + gimp_tool_path_end_change (path, TRUE); + break; + + case GDK_KEY_Escape: + if (private->edit_mode != GIMP_VECTOR_MODE_DESIGN) + g_object_set (private, + "vectors-edit-mode", GIMP_VECTOR_MODE_DESIGN, + NULL); + break; + + default: + return FALSE; + } + + return TRUE; +} + +static gboolean +gimp_tool_path_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolPath *path = GIMP_TOOL_PATH (widget); + GimpToolPathPrivate *private = path->private; + + *tool_cursor = GIMP_TOOL_CURSOR_PATHS; + *modifier = GIMP_CURSOR_MODIFIER_NONE; + + switch (private->function) + { + case VECTORS_SELECT_VECTOR: + *tool_cursor = GIMP_TOOL_CURSOR_HAND; + break; + + case VECTORS_CREATE_VECTOR: + case VECTORS_CREATE_STROKE: + *modifier = GIMP_CURSOR_MODIFIER_CONTROL; + break; + + case VECTORS_ADD_ANCHOR: + case VECTORS_INSERT_ANCHOR: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR; + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + break; + + case VECTORS_DELETE_ANCHOR: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR; + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + break; + + case VECTORS_DELETE_SEGMENT: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT; + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + break; + + case VECTORS_MOVE_HANDLE: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_CONTROL; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case VECTORS_CONVERT_EDGE: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_CONTROL; + *modifier = GIMP_CURSOR_MODIFIER_MINUS; + break; + + case VECTORS_MOVE_ANCHOR: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case VECTORS_MOVE_CURVE: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case VECTORS_MOVE_STROKE: + case VECTORS_MOVE_VECTORS: + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case VECTORS_MOVE_ANCHORSET: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case VECTORS_CONNECT_STROKES: + *tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT; + *modifier = GIMP_CURSOR_MODIFIER_JOIN; + break; + + default: + *modifier = GIMP_CURSOR_MODIFIER_BAD; + break; + } + + return TRUE; +} + +static GimpVectorFunction +gimp_tool_path_get_function (GimpToolPath *path, + const GimpCoords *coords, + GdkModifierType state) +{ + GimpToolPathPrivate *private = path->private; + GimpAnchor *anchor = NULL; + GimpAnchor *anchor2 = NULL; + GimpStroke *stroke = NULL; + gdouble position = -1; + gboolean on_handle = FALSE; + gboolean on_curve = FALSE; + gboolean on_vectors = FALSE; + GimpVectorFunction function = VECTORS_FINISHED; + + private->modifier_lock = FALSE; + + /* are we hovering the current vectors on the current display? */ + if (private->vectors) + { + on_handle = gimp_canvas_item_on_vectors_handle (private->path, + private->vectors, + coords, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_ANCHOR_ANCHOR, + private->sel_count > 2, + &anchor, &stroke); + + if (! on_handle) + on_curve = gimp_canvas_item_on_vectors_curve (private->path, + private->vectors, + coords, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + NULL, + &position, &anchor, + &anchor2, &stroke); + } + + if (! on_handle && ! on_curve) + { + on_vectors = gimp_canvas_item_on_vectors (private->path, + coords, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + NULL, NULL, NULL, NULL, NULL, + NULL); + } + + private->cur_position = position; + private->cur_anchor = anchor; + private->cur_anchor2 = anchor2; + private->cur_stroke = stroke; + + switch (private->edit_mode) + { + case GIMP_VECTOR_MODE_DESIGN: + if (! private->vectors) + { + if (on_vectors) + { + function = VECTORS_SELECT_VECTOR; + } + else + { + function = VECTORS_CREATE_VECTOR; + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + private->modifier_lock = TRUE; + } + } + else if (on_handle) + { + if (anchor->type == GIMP_ANCHOR_ANCHOR) + { + if (state & TOGGLE_MASK) + { + function = VECTORS_MOVE_ANCHORSET; + } + else + { + if (private->sel_count >= 2 && anchor->selected) + function = VECTORS_MOVE_ANCHORSET; + else + function = VECTORS_MOVE_ANCHOR; + } + } + else + { + function = VECTORS_MOVE_HANDLE; + + if (state & TOGGLE_MASK) + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + else + private->restriction = GIMP_ANCHOR_FEATURE_NONE; + } + } + else if (on_curve) + { + if (gimp_stroke_point_is_movable (stroke, anchor, position)) + { + function = VECTORS_MOVE_CURVE; + + if (state & TOGGLE_MASK) + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + else + private->restriction = GIMP_ANCHOR_FEATURE_NONE; + } + else + { + function = VECTORS_FINISHED; + } + } + else + { + if (private->sel_stroke && + private->sel_anchor && + gimp_stroke_is_extendable (private->sel_stroke, + private->sel_anchor) && + ! (state & TOGGLE_MASK)) + function = VECTORS_ADD_ANCHOR; + else + function = VECTORS_CREATE_STROKE; + + private->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC; + private->modifier_lock = TRUE; + } + + break; + + case GIMP_VECTOR_MODE_EDIT: + if (! private->vectors) + { + if (on_vectors) + { + function = VECTORS_SELECT_VECTOR; + } + else + { + function = VECTORS_FINISHED; + } + } + else if (on_handle) + { + if (anchor->type == GIMP_ANCHOR_ANCHOR) + { + if (! (state & TOGGLE_MASK) && + private->sel_anchor && + private->sel_anchor != anchor && + gimp_stroke_is_extendable (private->sel_stroke, + private->sel_anchor) && + gimp_stroke_is_extendable (stroke, anchor)) + { + function = VECTORS_CONNECT_STROKES; + } + else + { + if (state & TOGGLE_MASK) + { + function = VECTORS_DELETE_ANCHOR; + } + else + { + if (private->polygonal) + function = VECTORS_MOVE_ANCHOR; + else + function = VECTORS_MOVE_HANDLE; + } + } + } + else + { + if (state & TOGGLE_MASK) + function = VECTORS_CONVERT_EDGE; + else + function = VECTORS_MOVE_HANDLE; + } + } + else if (on_curve) + { + if (state & TOGGLE_MASK) + { + function = VECTORS_DELETE_SEGMENT; + } + else if (gimp_stroke_anchor_is_insertable (stroke, anchor, position)) + { + function = VECTORS_INSERT_ANCHOR; + } + else + { + function = VECTORS_FINISHED; + } + } + else + { + function = VECTORS_FINISHED; + } + + break; + + case GIMP_VECTOR_MODE_MOVE: + if (! private->vectors) + { + if (on_vectors) + { + function = VECTORS_SELECT_VECTOR; + } + else + { + function = VECTORS_FINISHED; + } + } + else if (on_handle || on_curve) + { + if (state & TOGGLE_MASK) + { + function = VECTORS_MOVE_VECTORS; + } + else + { + function = VECTORS_MOVE_STROKE; + } + } + else + { + if (on_vectors) + { + function = VECTORS_SELECT_VECTOR; + } + else + { + function = VECTORS_MOVE_VECTORS; + } + } + break; + } + + return function; +} + +static void +gimp_tool_path_update_status (GimpToolPath *path, + GdkModifierType state, + gboolean proximity) +{ + GimpToolPathPrivate *private = path->private; + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + const gchar *status = NULL; + gboolean free_status = FALSE; + + if (! proximity) + { + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (path), NULL); + return; + } + + switch (private->function) + { + case VECTORS_SELECT_VECTOR: + status = _("Click to pick path to edit"); + break; + + case VECTORS_CREATE_VECTOR: + status = _("Click to create a new path"); + break; + + case VECTORS_CREATE_STROKE: + status = _("Click to create a new component of the path"); + break; + + case VECTORS_ADD_ANCHOR: + status = gimp_suggest_modifiers (_("Click or Click-Drag to create " + "a new anchor"), + extend_mask & ~state, + NULL, NULL, NULL); + free_status = TRUE; + break; + + case VECTORS_MOVE_ANCHOR: + if (private->edit_mode != GIMP_VECTOR_MODE_EDIT) + { + status = gimp_suggest_modifiers (_("Click-Drag to move the " + "anchor around"), + toggle_mask & ~state, + NULL, NULL, NULL); + free_status = TRUE; + } + else + status = _("Click-Drag to move the anchor around"); + break; + + case VECTORS_MOVE_ANCHORSET: + status = _("Click-Drag to move the anchors around"); + break; + + case VECTORS_MOVE_HANDLE: + if (private->restriction != GIMP_ANCHOR_FEATURE_SYMMETRIC) + { + status = gimp_suggest_modifiers (_("Click-Drag to move the " + "handle around"), + extend_mask & ~state, + NULL, NULL, NULL); + } + else + { + status = gimp_suggest_modifiers (_("Click-Drag to move the " + "handles around symmetrically"), + extend_mask & ~state, + NULL, NULL, NULL); + } + free_status = TRUE; + break; + + case VECTORS_MOVE_CURVE: + if (private->polygonal) + status = gimp_suggest_modifiers (_("Click-Drag to move the " + "anchors around"), + extend_mask & ~state, + NULL, NULL, NULL); + else + status = gimp_suggest_modifiers (_("Click-Drag to change the " + "shape of the curve"), + extend_mask & ~state, + _("%s: symmetrical"), NULL, NULL); + free_status = TRUE; + break; + + case VECTORS_MOVE_STROKE: + status = gimp_suggest_modifiers (_("Click-Drag to move the " + "component around"), + extend_mask & ~state, + NULL, NULL, NULL); + free_status = TRUE; + break; + + case VECTORS_MOVE_VECTORS: + status = _("Click-Drag to move the path around"); + break; + + case VECTORS_INSERT_ANCHOR: + status = gimp_suggest_modifiers (_("Click-Drag to insert an anchor " + "on the path"), + extend_mask & ~state, + NULL, NULL, NULL); + free_status = TRUE; + break; + + case VECTORS_DELETE_ANCHOR: + status = _("Click to delete this anchor"); + break; + + case VECTORS_CONNECT_STROKES: + status = _("Click to connect this anchor " + "with the selected endpoint"); + break; + + case VECTORS_DELETE_SEGMENT: + status = _("Click to open up the path"); + break; + + case VECTORS_CONVERT_EDGE: + status = _("Click to make this node angular"); + break; + + case VECTORS_FINISHED: + status = _("Clicking here does nothing, try clicking on path elements."); + break; + } + + gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (path), status); + + if (free_status) + g_free ((gchar *) status); +} + +static void +gimp_tool_path_begin_change (GimpToolPath *path, + const gchar *desc) +{ + GimpToolPathPrivate *private = path->private; + + g_return_if_fail (private->vectors != NULL); + + /* don't push two undos */ + if (private->have_undo) + return; + + g_signal_emit (path, path_signals[BEGIN_CHANGE], 0, + desc); + + private->have_undo = TRUE; +} + +static void +gimp_tool_path_end_change (GimpToolPath *path, + gboolean success) +{ + GimpToolPathPrivate *private = path->private; + + private->have_undo = FALSE; + private->undo_motion = FALSE; + + g_signal_emit (path, path_signals[END_CHANGE], 0, + success); +} + +static void +gimp_tool_path_vectors_visible (GimpVectors *vectors, + GimpToolPath *path) +{ + GimpToolPathPrivate *private = path->private; + + gimp_canvas_item_set_visible (private->path, + ! gimp_item_get_visible (GIMP_ITEM (vectors))); +} + +static void +gimp_tool_path_vectors_freeze (GimpVectors *vectors, + GimpToolPath *path) +{ +} + +static void +gimp_tool_path_vectors_thaw (GimpVectors *vectors, + GimpToolPath *path) +{ + /* Ok, the vector might have changed externally (e.g. Undo) we need + * to validate our internal state. + */ + gimp_tool_path_verify_state (path); + gimp_tool_path_changed (GIMP_TOOL_WIDGET (path)); +} + +static void +gimp_tool_path_verify_state (GimpToolPath *path) +{ + GimpToolPathPrivate *private = path->private; + GimpStroke *cur_stroke = NULL; + gboolean cur_anchor_valid = FALSE; + gboolean cur_stroke_valid = FALSE; + + private->sel_count = 0; + private->sel_anchor = NULL; + private->sel_stroke = NULL; + + if (! private->vectors) + { + private->cur_position = -1; + private->cur_anchor = NULL; + private->cur_stroke = NULL; + return; + } + + while ((cur_stroke = gimp_vectors_stroke_get_next (private->vectors, + cur_stroke))) + { + GList *anchors; + GList *list; + + /* anchor handles */ + anchors = gimp_stroke_get_draw_anchors (cur_stroke); + + if (cur_stroke == private->cur_stroke) + cur_stroke_valid = TRUE; + + for (list = anchors; list; list = g_list_next (list)) + { + GimpAnchor *cur_anchor = list->data; + + if (cur_anchor == private->cur_anchor) + cur_anchor_valid = TRUE; + + if (cur_anchor->type == GIMP_ANCHOR_ANCHOR && + cur_anchor->selected) + { + private->sel_count++; + if (private->sel_count == 1) + { + private->sel_anchor = cur_anchor; + private->sel_stroke = cur_stroke; + } + else + { + private->sel_anchor = NULL; + private->sel_stroke = NULL; + } + } + } + + g_list_free (anchors); + + anchors = gimp_stroke_get_draw_controls (cur_stroke); + + for (list = anchors; list; list = g_list_next (list)) + { + GimpAnchor *cur_anchor = list->data; + + if (cur_anchor == private->cur_anchor) + cur_anchor_valid = TRUE; + } + + g_list_free (anchors); + } + + if (! cur_stroke_valid) + private->cur_stroke = NULL; + + if (! cur_anchor_valid) + private->cur_anchor = NULL; +} + +static void +gimp_tool_path_move_selected_anchors (GimpToolPath *path, + gdouble x, + gdouble y) +{ + GimpToolPathPrivate *private = path->private; + GimpAnchor *cur_anchor; + GimpStroke *cur_stroke = NULL; + GList *anchors; + GList *list; + GimpCoords offset = { 0.0, }; + + offset.x = x; + offset.y = y; + + while ((cur_stroke = gimp_vectors_stroke_get_next (private->vectors, + cur_stroke))) + { + /* anchors */ + anchors = gimp_stroke_get_draw_anchors (cur_stroke); + + for (list = anchors; list; list = g_list_next (list)) + { + cur_anchor = GIMP_ANCHOR (list->data); + + if (cur_anchor->selected) + gimp_stroke_anchor_move_relative (cur_stroke, + cur_anchor, + &offset, + GIMP_ANCHOR_FEATURE_NONE); + } + + g_list_free (anchors); + } +} + +static void +gimp_tool_path_delete_selected_anchors (GimpToolPath *path) +{ + GimpToolPathPrivate *private = path->private; + GimpAnchor *cur_anchor; + GimpStroke *cur_stroke = NULL; + GList *anchors; + GList *list; + gboolean have_undo = FALSE; + + gimp_vectors_freeze (private->vectors); + + while ((cur_stroke = gimp_vectors_stroke_get_next (private->vectors, + cur_stroke))) + { + /* anchors */ + anchors = gimp_stroke_get_draw_anchors (cur_stroke); + + for (list = anchors; list; list = g_list_next (list)) + { + cur_anchor = GIMP_ANCHOR (list->data); + + if (cur_anchor->selected) + { + if (! have_undo) + { + gimp_tool_path_begin_change (path, _("Delete Anchors")); + have_undo = TRUE; + } + + gimp_stroke_anchor_delete (cur_stroke, cur_anchor); + + if (gimp_stroke_is_empty (cur_stroke)) + { + gimp_vectors_stroke_remove (private->vectors, cur_stroke); + cur_stroke = NULL; + } + } + } + + g_list_free (anchors); + } + + if (have_undo) + gimp_tool_path_end_change (path, TRUE); + + gimp_vectors_thaw (private->vectors); +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_path_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_PATH, + "shell", shell, + NULL); +} + +void +gimp_tool_path_set_vectors (GimpToolPath *path, + GimpVectors *vectors) +{ + GimpToolPathPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_PATH (path)); + g_return_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors)); + + private = path->private; + + if (vectors == private->vectors) + return; + + if (private->vectors) + { + g_signal_handlers_disconnect_by_func (private->vectors, + gimp_tool_path_vectors_visible, + path); + g_signal_handlers_disconnect_by_func (private->vectors, + gimp_tool_path_vectors_freeze, + path); + g_signal_handlers_disconnect_by_func (private->vectors, + gimp_tool_path_vectors_thaw, + path); + + g_object_unref (private->vectors); + } + + private->vectors = vectors; + private->function = VECTORS_FINISHED; + gimp_tool_path_verify_state (path); + + if (private->vectors) + { + g_object_ref (private->vectors); + + g_signal_connect_object (private->vectors, "visibility-changed", + G_CALLBACK (gimp_tool_path_vectors_visible), + path, 0); + g_signal_connect_object (private->vectors, "freeze", + G_CALLBACK (gimp_tool_path_vectors_freeze), + path, 0); + g_signal_connect_object (private->vectors, "thaw", + G_CALLBACK (gimp_tool_path_vectors_thaw), + path, 0); + } + + g_object_notify (G_OBJECT (path), "vectors"); +} diff --git a/app/display/gimptoolpath.h b/app/display/gimptoolpath.h new file mode 100644 index 0000000..861d5e8 --- /dev/null +++ b/app/display/gimptoolpath.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolpath.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_PATH_H__ +#define __GIMP_TOOL_PATH_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_PATH (gimp_tool_path_get_type ()) +#define GIMP_TOOL_PATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_PATH, GimpToolPath)) +#define GIMP_TOOL_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_PATH, GimpToolPathClass)) +#define GIMP_IS_TOOL_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_PATH)) +#define GIMP_IS_TOOL_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_PATH)) +#define GIMP_TOOL_PATH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_PATH, GimpToolPathClass)) + + +typedef struct _GimpToolPath GimpToolPath; +typedef struct _GimpToolPathPrivate GimpToolPathPrivate; +typedef struct _GimpToolPathClass GimpToolPathClass; + +struct _GimpToolPath +{ + GimpToolWidget parent_instance; + + GimpToolPathPrivate *private; +}; + +struct _GimpToolPathClass +{ + GimpToolWidgetClass parent_class; + + void (* begin_change) (GimpToolPath *path, + const gchar *desc); + void (* end_change) (GimpToolPath *path, + gboolean success); + void (* activate) (GimpToolPath *path, + GdkModifierType state); +}; + + +GType gimp_tool_path_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_path_new (GimpDisplayShell *shell); + +void gimp_tool_path_set_vectors (GimpToolPath *path, + GimpVectors *vectors); + + +#endif /* __GIMP_TOOL_PATH_H__ */ diff --git a/app/display/gimptoolpolygon.c b/app/display/gimptoolpolygon.c new file mode 100644 index 0000000..4f2c20b --- /dev/null +++ b/app/display/gimptoolpolygon.c @@ -0,0 +1,1495 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolpolygon.c + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpFreeSelectTool + * + * Major improvement to support polygonal segments + * Copyright (C) 2008 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpmarshal.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvasline.h" +#include "gimpcanvaspolygon.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-utils.h" +#include "gimptoolpolygon.h" + +#include "gimp-intl.h" + + +#define POINT_GRAB_THRESHOLD_SQ SQR (GIMP_CANVAS_HANDLE_SIZE_CIRCLE / 2) +#define POINT_SHOW_THRESHOLD_SQ SQR (GIMP_CANVAS_HANDLE_SIZE_CIRCLE * 7) +#define N_ITEMS_PER_ALLOC 1024 +#define INVALID_INDEX (-1) +#define NO_CLICK_TIME_AVAILABLE 0 + + +enum +{ + CHANGE_COMPLETE, + LAST_SIGNAL +}; + + +struct _GimpToolPolygonPrivate +{ + /* Index of grabbed segment index. */ + gint grabbed_segment_index; + + /* We need to keep track of a number of points when we move a + * segment vertex + */ + GimpVector2 *saved_points_lower_segment; + GimpVector2 *saved_points_higher_segment; + gint max_n_saved_points_lower_segment; + gint max_n_saved_points_higher_segment; + + /* Keeps track whether or not a modification of the polygon has been + * made between _button_press and _button_release + */ + gboolean polygon_modified; + + /* Point which is used to draw the polygon but which is not part of + * it yet + */ + GimpVector2 pending_point; + gboolean show_pending_point; + + /* The points of the polygon */ + GimpVector2 *points; + gint max_n_points; + + /* The number of points actually in use */ + gint n_points; + + + /* Any int array containing the indices for the points in the + * polygon that connects different segments together + */ + gint *segment_indices; + gint max_n_segment_indices; + + /* The number of segment indices actually in use */ + gint n_segment_indices; + + /* Is the polygon closed? */ + gboolean polygon_closed; + + /* Whether or not to constrain the angle for newly created polygonal + * segments. + */ + gboolean constrain_angle; + + /* Whether or not to suppress handles (so that new segments can be + * created immediately after an existing segment vertex. + */ + gboolean suppress_handles; + + /* Last _oper_update or _motion coords */ + gboolean hover; + GimpVector2 last_coords; + + /* A double-click commits the selection, keep track of last + * click-time and click-position. + */ + guint32 last_click_time; + GimpCoords last_click_coord; + + /* Are we in a click-drag-release? */ + gboolean button_down; + + GimpCanvasItem *polygon; + GimpCanvasItem *pending_line; + GimpCanvasItem *closing_line; + GPtrArray *handles; +}; + + +/* local function prototypes */ + +static void gimp_tool_polygon_constructed (GObject *object); +static void gimp_tool_polygon_finalize (GObject *object); +static void gimp_tool_polygon_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_polygon_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_polygon_changed (GimpToolWidget *widget); +static gint gimp_tool_polygon_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_polygon_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_polygon_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_polygon_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_polygon_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_polygon_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_polygon_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static void gimp_tool_polygon_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static void gimp_tool_polygon_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_polygon_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_polygon_change_complete (GimpToolPolygon *polygon); + +static gint gimp_tool_polygon_get_segment_index (GimpToolPolygon *polygon, + const GimpCoords *coords); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolPolygon, gimp_tool_polygon, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_polygon_parent_class + +static guint polygon_signals[LAST_SIGNAL] = { 0, }; + +static const GimpVector2 vector2_zero = { 0.0, 0.0 }; + + +static void +gimp_tool_polygon_class_init (GimpToolPolygonClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_polygon_constructed; + object_class->finalize = gimp_tool_polygon_finalize; + object_class->set_property = gimp_tool_polygon_set_property; + object_class->get_property = gimp_tool_polygon_get_property; + + widget_class->changed = gimp_tool_polygon_changed; + widget_class->button_press = gimp_tool_polygon_button_press; + widget_class->button_release = gimp_tool_polygon_button_release; + widget_class->motion = gimp_tool_polygon_motion; + widget_class->hit = gimp_tool_polygon_hit; + widget_class->hover = gimp_tool_polygon_hover; + widget_class->leave_notify = gimp_tool_polygon_leave_notify; + widget_class->key_press = gimp_tool_polygon_key_press; + widget_class->motion_modifier = gimp_tool_polygon_motion_modifier; + widget_class->hover_modifier = gimp_tool_polygon_hover_modifier; + widget_class->get_cursor = gimp_tool_polygon_get_cursor; + + polygon_signals[CHANGE_COMPLETE] = + g_signal_new ("change-complete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolPolygonClass, change_complete), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gimp_tool_polygon_init (GimpToolPolygon *polygon) +{ + polygon->private = gimp_tool_polygon_get_instance_private (polygon); + + polygon->private->grabbed_segment_index = INVALID_INDEX; + polygon->private->last_click_time = NO_CLICK_TIME_AVAILABLE; +} + +static void +gimp_tool_polygon_constructed (GObject *object) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolPolygonPrivate *private = polygon->private; + GimpCanvasGroup *stroke_group; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->polygon = gimp_tool_widget_add_polygon (widget, NULL, + NULL, 0, FALSE); + + private->pending_line = gimp_tool_widget_add_line (widget, 0, 0, 0, 0); + private->closing_line = gimp_tool_widget_add_line (widget, 0, 0, 0, 0); + + gimp_tool_widget_pop_group (widget); + + private->handles = g_ptr_array_new (); + + gimp_tool_polygon_changed (widget); +} + +static void +gimp_tool_polygon_finalize (GObject *object) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (object); + GimpToolPolygonPrivate *private = polygon->private; + + g_free (private->points); + g_free (private->segment_indices); + g_free (private->saved_points_lower_segment); + g_free (private->saved_points_higher_segment); + + g_clear_pointer (&private->handles, g_ptr_array_unref); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_polygon_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_polygon_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_polygon_get_segment (GimpToolPolygon *polygon, + GimpVector2 **points, + gint *n_points, + gint segment_start, + gint segment_end) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + *points = &priv->points[priv->segment_indices[segment_start]]; + *n_points = priv->segment_indices[segment_end] - + priv->segment_indices[segment_start] + + 1; +} + +static void +gimp_tool_polygon_get_segment_point (GimpToolPolygon *polygon, + gdouble *start_point_x, + gdouble *start_point_y, + gint segment_index) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + *start_point_x = priv->points[priv->segment_indices[segment_index]].x; + *start_point_y = priv->points[priv->segment_indices[segment_index]].y; +} + +static gboolean +gimp_tool_polygon_should_close (GimpToolPolygon *polygon, + guint32 time, + const GimpCoords *coords) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (polygon); + GimpToolPolygonPrivate *priv = polygon->private; + gboolean double_click = FALSE; + gdouble dist; + + if (priv->polygon_modified || + priv->n_segment_indices < 1 || + priv->n_points < 3 || + priv->polygon_closed) + return FALSE; + + dist = gimp_canvas_item_transform_distance_square (priv->polygon, + coords->x, + coords->y, + priv->points[0].x, + priv->points[0].y); + + /* Handle double-click. It must be within GTK+ global double-click + * time since last click, and it must be within GTK+ global + * double-click distance away from the last point + */ + if (time != NO_CLICK_TIME_AVAILABLE) + { + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (shell)); + gint double_click_time; + gint double_click_distance; + gint click_time_passed; + gdouble dist_from_last_point; + + click_time_passed = time - priv->last_click_time; + + dist_from_last_point = + gimp_canvas_item_transform_distance_square (priv->polygon, + coords->x, + coords->y, + priv->last_click_coord.x, + priv->last_click_coord.y); + + g_object_get (settings, + "gtk-double-click-time", &double_click_time, + "gtk-double-click-distance", &double_click_distance, + NULL); + + double_click = click_time_passed < double_click_time && + dist_from_last_point < double_click_distance; + } + + return ((! priv->suppress_handles && dist < POINT_GRAB_THRESHOLD_SQ) || + double_click); +} + +static void +gimp_tool_polygon_revert_to_last_segment (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + priv->n_points = priv->segment_indices[priv->n_segment_indices - 1] + 1; +} + +static void +gimp_tool_polygon_remove_last_segment (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + if (priv->polygon_closed) + { + priv->polygon_closed = FALSE; + + gimp_tool_polygon_changed (GIMP_TOOL_WIDGET (polygon)); + + return; + } + + if (priv->n_segment_indices > 0) + { + GimpCanvasItem *handle; + + priv->n_segment_indices--; + + handle = g_ptr_array_index (priv->handles, + priv->n_segment_indices); + + gimp_tool_widget_remove_item (GIMP_TOOL_WIDGET (polygon), handle); + g_ptr_array_remove (priv->handles, handle); + } + + if (priv->n_segment_indices <= 0) + { + priv->grabbed_segment_index = INVALID_INDEX; + priv->show_pending_point = FALSE; + priv->n_points = 0; + priv->n_segment_indices = 0; + + gimp_tool_widget_response (GIMP_TOOL_WIDGET (polygon), + GIMP_TOOL_WIDGET_RESPONSE_CANCEL); + } + else + { + gimp_tool_polygon_revert_to_last_segment (polygon); + + gimp_tool_polygon_changed (GIMP_TOOL_WIDGET (polygon)); + } +} + +static void +gimp_tool_polygon_add_point (GimpToolPolygon *polygon, + gdouble x, + gdouble y) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + if (priv->n_points >= priv->max_n_points) + { + priv->max_n_points += N_ITEMS_PER_ALLOC; + + priv->points = g_realloc (priv->points, + sizeof (GimpVector2) * priv->max_n_points); + } + + priv->points[priv->n_points].x = x; + priv->points[priv->n_points].y = y; + + priv->n_points++; +} + +static void +gimp_tool_polygon_add_segment_index (GimpToolPolygon *polygon, + gint index) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + if (priv->n_segment_indices >= priv->max_n_segment_indices) + { + priv->max_n_segment_indices += N_ITEMS_PER_ALLOC; + + priv->segment_indices = g_realloc (priv->segment_indices, + sizeof (GimpVector2) * + priv->max_n_segment_indices); + } + + priv->segment_indices[priv->n_segment_indices] = index; + + g_ptr_array_add (priv->handles, + gimp_tool_widget_add_handle (GIMP_TOOL_WIDGET (polygon), + GIMP_HANDLE_CROSS, + 0, 0, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER)); + + priv->n_segment_indices++; +} + +static gboolean +gimp_tool_polygon_is_point_grabbed (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + return priv->grabbed_segment_index != INVALID_INDEX; +} + +static void +gimp_tool_polygon_fit_segment (GimpToolPolygon *polygon, + GimpVector2 *dest_points, + GimpVector2 dest_start_target, + GimpVector2 dest_end_target, + const GimpVector2 *source_points, + gint n_points) +{ + GimpVector2 origo_translation_offset; + GimpVector2 untranslation_offset; + gdouble rotation; + gdouble scale; + + /* Handle some quick special cases */ + if (n_points <= 0) + { + return; + } + else if (n_points == 1) + { + dest_points[0] = dest_end_target; + return; + } + else if (n_points == 2) + { + dest_points[0] = dest_start_target; + dest_points[1] = dest_end_target; + return; + } + + /* Copy from source to dest; we work on the dest data */ + memcpy (dest_points, source_points, sizeof (GimpVector2) * n_points); + + /* Transform the destination end point */ + { + GimpVector2 *dest_end; + GimpVector2 origo_translated_end_target; + gdouble target_rotation; + gdouble current_rotation; + gdouble target_length; + gdouble current_length; + + dest_end = &dest_points[n_points - 1]; + + /* Transate to origin */ + gimp_vector2_sub (&origo_translation_offset, + &vector2_zero, + &dest_points[0]); + gimp_vector2_add (dest_end, + dest_end, + &origo_translation_offset); + + /* Calculate origo_translated_end_target */ + gimp_vector2_sub (&origo_translated_end_target, + &dest_end_target, + &dest_start_target); + + /* Rotate */ + target_rotation = atan2 (vector2_zero.y - origo_translated_end_target.y, + vector2_zero.x - origo_translated_end_target.x); + current_rotation = atan2 (vector2_zero.y - dest_end->y, + vector2_zero.x - dest_end->x); + rotation = current_rotation - target_rotation; + + gimp_vector2_rotate (dest_end, rotation); + + + /* Scale */ + target_length = gimp_vector2_length (&origo_translated_end_target); + current_length = gimp_vector2_length (dest_end); + scale = target_length / current_length; + + gimp_vector2_mul (dest_end, scale); + + + /* Untranslate */ + gimp_vector2_sub (&untranslation_offset, + &dest_end_target, + dest_end); + gimp_vector2_add (dest_end, + dest_end, + &untranslation_offset); + } + + /* Do the same transformation for the rest of the points */ + { + gint i; + + for (i = 0; i < n_points - 1; i++) + { + /* Translate */ + gimp_vector2_add (&dest_points[i], + &origo_translation_offset, + &dest_points[i]); + + /* Rotate */ + gimp_vector2_rotate (&dest_points[i], + rotation); + + /* Scale */ + gimp_vector2_mul (&dest_points[i], + scale); + + /* Untranslate */ + gimp_vector2_add (&dest_points[i], + &dest_points[i], + &untranslation_offset); + } + } +} + +static void +gimp_tool_polygon_move_segment_vertex_to (GimpToolPolygon *polygon, + gint segment_index, + gdouble new_x, + gdouble new_y) +{ + GimpToolPolygonPrivate *priv = polygon->private; + GimpVector2 cursor_point = { new_x, new_y }; + GimpVector2 *dest; + GimpVector2 *dest_start_target; + GimpVector2 *dest_end_target; + gint n_points; + + /* Handle the segment before the grabbed point */ + if (segment_index > 0) + { + gimp_tool_polygon_get_segment (polygon, + &dest, + &n_points, + priv->grabbed_segment_index - 1, + priv->grabbed_segment_index); + + dest_start_target = &dest[0]; + dest_end_target = &cursor_point; + + gimp_tool_polygon_fit_segment (polygon, + dest, + *dest_start_target, + *dest_end_target, + priv->saved_points_lower_segment, + n_points); + } + + /* Handle the segment after the grabbed point */ + if (segment_index < priv->n_segment_indices - 1) + { + gimp_tool_polygon_get_segment (polygon, + &dest, + &n_points, + priv->grabbed_segment_index, + priv->grabbed_segment_index + 1); + + dest_start_target = &cursor_point; + dest_end_target = &dest[n_points - 1]; + + gimp_tool_polygon_fit_segment (polygon, + dest, + *dest_start_target, + *dest_end_target, + priv->saved_points_higher_segment, + n_points); + } + + /* Handle when there only is one point */ + if (segment_index == 0 && + priv->n_segment_indices == 1) + { + priv->points[0].x = new_x; + priv->points[0].y = new_y; + } +} + +static void +gimp_tool_polygon_revert_to_saved_state (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *priv = polygon->private; + GimpVector2 *dest; + gint n_points; + + /* Without a point grab we have no sensible information to fall back + * on, bail out + */ + if (! gimp_tool_polygon_is_point_grabbed (polygon)) + { + return; + } + + if (priv->grabbed_segment_index > 0) + { + gimp_tool_polygon_get_segment (polygon, + &dest, + &n_points, + priv->grabbed_segment_index - 1, + priv->grabbed_segment_index); + + memcpy (dest, + priv->saved_points_lower_segment, + sizeof (GimpVector2) * n_points); + } + + if (priv->grabbed_segment_index < priv->n_segment_indices - 1) + { + gimp_tool_polygon_get_segment (polygon, + &dest, + &n_points, + priv->grabbed_segment_index, + priv->grabbed_segment_index + 1); + + memcpy (dest, + priv->saved_points_higher_segment, + sizeof (GimpVector2) * n_points); + } + + if (priv->grabbed_segment_index == 0 && + priv->n_segment_indices == 1) + { + priv->points[0] = *priv->saved_points_lower_segment; + } +} + +static void +gimp_tool_polygon_prepare_for_move (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *priv = polygon->private; + GimpVector2 *source; + gint n_points; + + if (priv->grabbed_segment_index > 0) + { + gimp_tool_polygon_get_segment (polygon, + &source, + &n_points, + priv->grabbed_segment_index - 1, + priv->grabbed_segment_index); + + if (n_points > priv->max_n_saved_points_lower_segment) + { + priv->max_n_saved_points_lower_segment = n_points; + + priv->saved_points_lower_segment = + g_realloc (priv->saved_points_lower_segment, + sizeof (GimpVector2) * n_points); + } + + memcpy (priv->saved_points_lower_segment, + source, + sizeof (GimpVector2) * n_points); + } + + if (priv->grabbed_segment_index < priv->n_segment_indices - 1) + { + gimp_tool_polygon_get_segment (polygon, + &source, + &n_points, + priv->grabbed_segment_index, + priv->grabbed_segment_index + 1); + + if (n_points > priv->max_n_saved_points_higher_segment) + { + priv->max_n_saved_points_higher_segment = n_points; + + priv->saved_points_higher_segment = + g_realloc (priv->saved_points_higher_segment, + sizeof (GimpVector2) * n_points); + } + + memcpy (priv->saved_points_higher_segment, + source, + sizeof (GimpVector2) * n_points); + } + + /* A special-case when there only is one point */ + if (priv->grabbed_segment_index == 0 && + priv->n_segment_indices == 1) + { + if (priv->max_n_saved_points_lower_segment == 0) + { + priv->max_n_saved_points_lower_segment = 1; + + priv->saved_points_lower_segment = g_new0 (GimpVector2, 1); + } + + *priv->saved_points_lower_segment = priv->points[0]; + } +} + +static void +gimp_tool_polygon_update_motion (GimpToolPolygon *polygon, + gdouble new_x, + gdouble new_y) +{ + GimpToolPolygonPrivate *priv = polygon->private; + + if (gimp_tool_polygon_is_point_grabbed (polygon)) + { + priv->polygon_modified = TRUE; + + if (priv->constrain_angle && + priv->n_segment_indices > 1) + { + gdouble start_point_x; + gdouble start_point_y; + gint segment_index; + + /* Base constraints on the last segment vertex if we move + * the first one, otherwise base on the previous segment + * vertex + */ + if (priv->grabbed_segment_index == 0) + { + segment_index = priv->n_segment_indices - 1; + } + else + { + segment_index = priv->grabbed_segment_index - 1; + } + + gimp_tool_polygon_get_segment_point (polygon, + &start_point_x, + &start_point_y, + segment_index); + + gimp_display_shell_constrain_line ( + gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (polygon)), + start_point_x, + start_point_y, + &new_x, + &new_y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + + gimp_tool_polygon_move_segment_vertex_to (polygon, + priv->grabbed_segment_index, + new_x, + new_y); + + /* We also must update the pending point if we are moving the + * first point + */ + if (priv->grabbed_segment_index == 0) + { + priv->pending_point.x = new_x; + priv->pending_point.y = new_y; + } + } + else + { + /* Don't show the pending point while we are adding points */ + priv->show_pending_point = FALSE; + + gimp_tool_polygon_add_point (polygon, new_x, new_y); + } +} + +static void +gimp_tool_polygon_status_update (GimpToolPolygon *polygon, + const GimpCoords *coords, + gboolean proximity) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (polygon); + GimpToolPolygonPrivate *priv = polygon->private; + + if (proximity) + { + const gchar *status_text = NULL; + + if (gimp_tool_polygon_is_point_grabbed (polygon)) + { + if (gimp_tool_polygon_should_close (polygon, + NO_CLICK_TIME_AVAILABLE, + coords)) + { + status_text = _("Click to close shape"); + } + else + { + status_text = _("Click-Drag to move segment vertex"); + } + } + else if (priv->polygon_closed) + { + status_text = _("Return commits, Escape cancels, Backspace re-opens shape"); + } + else if (priv->n_segment_indices >= 3) + { + status_text = _("Return commits, Escape cancels, Backspace removes last segment"); + } + else + { + status_text = _("Click-Drag adds a free segment, Click adds a polygonal segment"); + } + + if (status_text) + { + gimp_tool_widget_set_status (widget, status_text); + } + } + else + { + gimp_tool_widget_set_status (widget, NULL); + } +} + +static void +gimp_tool_polygon_changed (GimpToolWidget *widget) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *private = polygon->private; + gboolean hovering_first_point = FALSE; + gboolean handles_wants_to_show = FALSE; + GimpCoords coords = { private->last_coords.x, + private->last_coords.y, + /* pad with 0 */ }; + gint i; + + gimp_canvas_polygon_set_points (private->polygon, + private->points, private->n_points); + + if (private->show_pending_point) + { + GimpVector2 last = private->points[private->n_points - 1]; + + gimp_canvas_line_set (private->pending_line, + last.x, + last.y, + private->pending_point.x, + private->pending_point.y); + } + + gimp_canvas_item_set_visible (private->pending_line, + private->show_pending_point); + + if (private->polygon_closed) + { + GimpVector2 first = private->points[0]; + GimpVector2 last = private->points[private->n_points - 1]; + + gimp_canvas_line_set (private->closing_line, + first.x, first.y, + last.x, last.y); + } + + gimp_canvas_item_set_visible (private->closing_line, + private->polygon_closed); + + hovering_first_point = + gimp_tool_polygon_should_close (polygon, + NO_CLICK_TIME_AVAILABLE, + &coords); + + /* We always show the handle for the first point, even with button1 + * down, since releasing the button on the first point will close + * the polygon, so it's a significant state which we must give + * feedback for + */ + handles_wants_to_show = (hovering_first_point || ! private->button_down); + + for (i = 0; i < private->n_segment_indices; i++) + { + GimpCanvasItem *handle; + GimpVector2 *point; + + handle = g_ptr_array_index (private->handles, i); + point = &private->points[private->segment_indices[i]]; + + if (private->hover && + handles_wants_to_show && + ! private->suppress_handles && + + /* If the first point is hovered while button1 is held down, + * only draw the first handle, the other handles are not + * relevant (see comment a few lines up) + */ + (i == 0 || + ! (private->button_down || hovering_first_point))) + { + gdouble dist; + GimpHandleType handle_type = -1; + + dist = + gimp_canvas_item_transform_distance_square (handle, + private->last_coords.x, + private->last_coords.y, + point->x, + point->y); + + /* If the cursor is over the point, fill, if it's just + * close, draw an outline + */ + if (dist < POINT_GRAB_THRESHOLD_SQ) + handle_type = GIMP_HANDLE_FILLED_CIRCLE; + else if (dist < POINT_SHOW_THRESHOLD_SQ) + handle_type = GIMP_HANDLE_CIRCLE; + + if (handle_type != -1) + { + gint size; + + gimp_canvas_item_begin_change (handle); + + gimp_canvas_handle_set_position (handle, point->x, point->y); + + g_object_set (handle, + "type", handle_type, + NULL); + + size = gimp_canvas_handle_calc_size (handle, + private->last_coords.x, + private->last_coords.y, + GIMP_CANVAS_HANDLE_SIZE_CIRCLE, + 2 * GIMP_CANVAS_HANDLE_SIZE_CIRCLE); + if (FALSE) + gimp_canvas_handle_set_size (handle, size, size); + + if (dist < POINT_GRAB_THRESHOLD_SQ) + gimp_canvas_item_set_highlight (handle, TRUE); + else + gimp_canvas_item_set_highlight (handle, FALSE); + + gimp_canvas_item_set_visible (handle, TRUE); + + gimp_canvas_item_end_change (handle); + } + else + { + gimp_canvas_item_set_visible (handle, FALSE); + } + } + else + { + gimp_canvas_item_set_visible (handle, FALSE); + } + } +} + +static gboolean +gimp_tool_polygon_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + if (gimp_tool_polygon_is_point_grabbed (polygon)) + { + gimp_tool_polygon_prepare_for_move (polygon); + } + else if (priv->polygon_closed) + { + if (press_type == GIMP_BUTTON_PRESS_DOUBLE && + gimp_canvas_item_hit (priv->polygon, coords->x, coords->y)) + { + gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_CONFIRM); + } + + return 0; + } + else + { + GimpVector2 point_to_add; + + /* Note that we add the pending point (unless it is the first + * point we add) because the pending point is setup correctly + * with regards to angle constraints. + */ + if (priv->n_points > 0) + { + point_to_add = priv->pending_point; + } + else + { + point_to_add.x = coords->x; + point_to_add.y = coords->y; + } + + /* No point was grabbed, add a new point and mark this as a + * segment divider. For a line segment, this will be the only + * new point. For a free segment, this will be the first point + * of the free segment. + */ + gimp_tool_polygon_add_point (polygon, + point_to_add.x, + point_to_add.y); + gimp_tool_polygon_add_segment_index (polygon, priv->n_points - 1); + } + + priv->button_down = TRUE; + + gimp_tool_polygon_changed (widget); + + return 1; +} + +static void +gimp_tool_polygon_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + g_object_ref (widget); + + priv->button_down = FALSE; + + switch (release_type) + { + case GIMP_BUTTON_RELEASE_CLICK: + case GIMP_BUTTON_RELEASE_NO_MOTION: + /* If a click was made, we don't consider the polygon modified */ + priv->polygon_modified = FALSE; + + /* First finish of the line segment if no point was grabbed */ + if (! gimp_tool_polygon_is_point_grabbed (polygon)) + { + /* Revert any free segment points that might have been added */ + gimp_tool_polygon_revert_to_last_segment (polygon); + } + + /* After the segments are up to date and we have handled + * double-click, see if it's committing time + */ + if (gimp_tool_polygon_should_close (polygon, time, coords)) + { + /* We can get a click notification even though the end point + * has been moved a few pixels. Since a move will change the + * free selection, revert it before doing the commit. + */ + gimp_tool_polygon_revert_to_saved_state (polygon); + + priv->polygon_closed = TRUE; + + gimp_tool_polygon_change_complete (polygon); + } + + priv->last_click_time = time; + priv->last_click_coord = *coords; + break; + + case GIMP_BUTTON_RELEASE_NORMAL: + /* First finish of the free segment if no point was grabbed */ + if (! gimp_tool_polygon_is_point_grabbed (polygon)) + { + /* The points are all setup, just make a segment */ + gimp_tool_polygon_add_segment_index (polygon, priv->n_points - 1); + } + + /* After the segments are up to date, see if it's committing time */ + if (gimp_tool_polygon_should_close (polygon, + NO_CLICK_TIME_AVAILABLE, + coords)) + { + priv->polygon_closed = TRUE; + } + + gimp_tool_polygon_change_complete (polygon); + break; + + case GIMP_BUTTON_RELEASE_CANCEL: + if (gimp_tool_polygon_is_point_grabbed (polygon)) + gimp_tool_polygon_revert_to_saved_state (polygon); + else + gimp_tool_polygon_remove_last_segment (polygon); + break; + + default: + break; + } + + /* Reset */ + priv->polygon_modified = FALSE; + + gimp_tool_polygon_changed (widget); + + g_object_unref (widget); +} + +static void +gimp_tool_polygon_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + priv->last_coords.x = coords->x; + priv->last_coords.y = coords->y; + + gimp_tool_polygon_update_motion (polygon, + coords->x, + coords->y); + + gimp_tool_polygon_changed (widget); +} + +static GimpHit +gimp_tool_polygon_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + if ((priv->n_points > 0 && ! priv->polygon_closed) || + gimp_tool_polygon_get_segment_index (polygon, coords) != INVALID_INDEX) + { + return GIMP_HIT_DIRECT; + } + else if (priv->polygon_closed && + gimp_canvas_item_hit (priv->polygon, coords->x, coords->y)) + { + return GIMP_HIT_INDIRECT; + } + + return GIMP_HIT_NONE; +} + +static void +gimp_tool_polygon_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + gboolean hovering_first_point; + + priv->grabbed_segment_index = gimp_tool_polygon_get_segment_index (polygon, + coords); + priv->hover = TRUE; + + hovering_first_point = + gimp_tool_polygon_should_close (polygon, + NO_CLICK_TIME_AVAILABLE, + coords); + + priv->last_coords.x = coords->x; + priv->last_coords.y = coords->y; + + if (priv->n_points == 0 || + priv->polygon_closed || + (gimp_tool_polygon_is_point_grabbed (polygon) && + ! hovering_first_point) || + ! proximity) + { + priv->show_pending_point = FALSE; + } + else + { + priv->show_pending_point = TRUE; + + if (hovering_first_point) + { + priv->pending_point = priv->points[0]; + } + else + { + priv->pending_point.x = coords->x; + priv->pending_point.y = coords->y; + + if (priv->constrain_angle && priv->n_points > 0) + { + gdouble start_point_x; + gdouble start_point_y; + + /* the last point is the line's start point */ + gimp_tool_polygon_get_segment_point (polygon, + &start_point_x, + &start_point_y, + priv->n_segment_indices - 1); + + gimp_display_shell_constrain_line ( + gimp_tool_widget_get_shell (widget), + start_point_x, start_point_y, + &priv->pending_point.x, + &priv->pending_point.y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + } + } + + gimp_tool_polygon_status_update (polygon, coords, proximity); + + gimp_tool_polygon_changed (widget); +} + +static void +gimp_tool_polygon_leave_notify (GimpToolWidget *widget) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + priv->grabbed_segment_index = INVALID_INDEX; + priv->hover = FALSE; + priv->show_pending_point = FALSE; + + gimp_tool_polygon_changed (widget); +} + +static gboolean +gimp_tool_polygon_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + switch (kevent->keyval) + { + case GDK_KEY_BackSpace: + gimp_tool_polygon_remove_last_segment (polygon); + + if (priv->n_segment_indices > 0) + gimp_tool_polygon_change_complete (polygon); + return TRUE; + + default: + break; + } + + return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); +} + +static void +gimp_tool_polygon_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + priv->constrain_angle = ((state & gimp_get_constrain_behavior_mask ()) ? + TRUE : FALSE); + + /* If we didn't came here due to a mouse release, immediately update + * the position of the thing we move. + */ + if (state & GDK_BUTTON1_MASK) + { + gimp_tool_polygon_update_motion (polygon, + priv->last_coords.x, + priv->last_coords.y); + } + + gimp_tool_polygon_changed (widget); +} + +static void +gimp_tool_polygon_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + GimpToolPolygonPrivate *priv = polygon->private; + + priv->constrain_angle = ((state & gimp_get_constrain_behavior_mask ()) ? + TRUE : FALSE); + + priv->suppress_handles = ((state & gimp_get_extend_selection_mask ()) ? + TRUE : FALSE); + + gimp_tool_polygon_changed (widget); +} + +static gboolean +gimp_tool_polygon_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + + if (gimp_tool_polygon_is_point_grabbed (polygon) && + ! gimp_tool_polygon_should_close (polygon, + NO_CLICK_TIME_AVAILABLE, + coords)) + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + + return TRUE; + } + + return FALSE; +} + +static void +gimp_tool_polygon_change_complete (GimpToolPolygon *polygon) +{ + g_signal_emit (polygon, polygon_signals[CHANGE_COMPLETE], 0); +} + +static gint +gimp_tool_polygon_get_segment_index (GimpToolPolygon *polygon, + const GimpCoords *coords) +{ + GimpToolPolygonPrivate *priv = polygon->private; + gint segment_index = INVALID_INDEX; + + if (! priv->suppress_handles) + { + gdouble shortest_dist = POINT_GRAB_THRESHOLD_SQ; + gint i; + + for (i = 0; i < priv->n_segment_indices; i++) + { + gdouble dist; + GimpVector2 *point; + + point = &priv->points[priv->segment_indices[i]]; + + dist = gimp_canvas_item_transform_distance_square (priv->polygon, + coords->x, + coords->y, + point->x, + point->y); + + if (dist < shortest_dist) + { + shortest_dist = dist; + + segment_index = i; + } + } + } + + return segment_index; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_polygon_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_POLYGON, + "shell", shell, + NULL); +} + +gboolean +gimp_tool_polygon_is_closed (GimpToolPolygon *polygon) +{ + GimpToolPolygonPrivate *private; + + g_return_val_if_fail (GIMP_IS_TOOL_POLYGON (polygon), FALSE); + + private = polygon->private; + + return private->polygon_closed; +} + +void +gimp_tool_polygon_get_points (GimpToolPolygon *polygon, + const GimpVector2 **points, + gint *n_points) +{ + GimpToolPolygonPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_POLYGON (polygon)); + + private = polygon->private; + + if (points) *points = private->points; + if (n_points) *n_points = private->n_points; +} diff --git a/app/display/gimptoolpolygon.h b/app/display/gimptoolpolygon.h new file mode 100644 index 0000000..fa3560a --- /dev/null +++ b/app/display/gimptoolpolygon.h @@ -0,0 +1,66 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolpolygon.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_POLYGON_H__ +#define __GIMP_TOOL_POLYGON_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_POLYGON (gimp_tool_polygon_get_type ()) +#define GIMP_TOOL_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_POLYGON, GimpToolPolygon)) +#define GIMP_TOOL_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_POLYGON, GimpToolPolygonClass)) +#define GIMP_IS_TOOL_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_POLYGON)) +#define GIMP_IS_TOOL_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_POLYGON)) +#define GIMP_TOOL_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_POLYGON, GimpToolPolygonClass)) + + +typedef struct _GimpToolPolygon GimpToolPolygon; +typedef struct _GimpToolPolygonPrivate GimpToolPolygonPrivate; +typedef struct _GimpToolPolygonClass GimpToolPolygonClass; + +struct _GimpToolPolygon +{ + GimpToolWidget parent_instance; + + GimpToolPolygonPrivate *private; +}; + +struct _GimpToolPolygonClass +{ + GimpToolWidgetClass parent_class; + + /* signals */ + void (* change_complete) (GimpToolPolygon *polygon); +}; + + +GType gimp_tool_polygon_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_polygon_new (GimpDisplayShell *shell); + +gboolean gimp_tool_polygon_is_closed (GimpToolPolygon *polygon); +void gimp_tool_polygon_get_points (GimpToolPolygon *polygon, + const GimpVector2 **points, + gint *n_points); + + +#endif /* __GIMP_TOOL_POLYGON_H__ */ diff --git a/app/display/gimptoolrectangle.c b/app/display/gimptoolrectangle.c new file mode 100644 index 0000000..09dc3e5 --- /dev/null +++ b/app/display/gimptoolrectangle.c @@ -0,0 +1,4292 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolrectangle.c + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpRectangleTool + * Copyright (C) 2007 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpitem.h" +#include "core/gimpmarshal.h" +#include "core/gimppickable.h" +#include "core/gimppickable-auto-shrink.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvasarc.h" +#include "gimpcanvascorner.h" +#include "gimpcanvashandle.h" +#include "gimpcanvasitem-utils.h" +#include "gimpcanvasrectangle.h" +#include "gimpcanvasrectangleguides.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-scroll.h" +#include "gimptoolrectangle.h" + +#include "gimp-intl.h" + + +/* speed of key movement */ +#define ARROW_VELOCITY 25 + +#define MAX_HANDLE_SIZE 50 +#define MIN_HANDLE_SIZE 15 +#define NARROW_MODE_HANDLE_SIZE 15 +#define NARROW_MODE_THRESHOLD 45 + + +enum +{ + PROP_0, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_CONSTRAINT, + PROP_PRECISION, + PROP_NARROW_MODE, + PROP_FORCE_NARROW_MODE, + PROP_DRAW_ELLIPSE, + PROP_ROUND_CORNERS, + PROP_CORNER_RADIUS, + PROP_STATUS_TITLE, + + PROP_HIGHLIGHT, + PROP_HIGHLIGHT_OPACITY, + PROP_GUIDE, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_FIXED_RULE_ACTIVE, + PROP_FIXED_RULE, + PROP_DESIRED_FIXED_WIDTH, + PROP_DESIRED_FIXED_HEIGHT, + PROP_DESIRED_FIXED_SIZE_WIDTH, + PROP_DESIRED_FIXED_SIZE_HEIGHT, + PROP_ASPECT_NUMERATOR, + PROP_ASPECT_DENOMINATOR, + PROP_FIXED_CENTER +}; + +enum +{ + CHANGE_COMPLETE, + LAST_SIGNAL +}; + +typedef enum +{ + CLAMPED_NONE = 0, + CLAMPED_LEFT = 1 << 0, + CLAMPED_RIGHT = 1 << 1, + CLAMPED_TOP = 1 << 2, + CLAMPED_BOTTOM = 1 << 3 +} ClampedSide; + +typedef enum +{ + SIDE_TO_RESIZE_NONE, + SIDE_TO_RESIZE_LEFT, + SIDE_TO_RESIZE_RIGHT, + SIDE_TO_RESIZE_TOP, + SIDE_TO_RESIZE_BOTTOM, + SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY, + SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY, +} SideToResize; + + +#define FEQUAL(a,b) (fabs ((a) - (b)) < 0.0001) +#define PIXEL_FEQUAL(a,b) (fabs ((a) - (b)) < 0.5) + + +struct _GimpToolRectanglePrivate +{ + /* The following members are "constants", that is, variables that are setup + * during gimp_tool_rectangle_button_press and then only read. + */ + + /* Whether or not the rectangle currently being rubber-banded is the + * first one created with this instance, this determines if we can + * undo it on button_release. + */ + gboolean is_first; + + /* Whether or not the rectangle currently being rubber-banded was + * created from scratch. + */ + gboolean is_new; + + /* Holds the coordinate that should be used as the "other side" when + * fixed-center is turned off. + */ + gdouble other_side_x; + gdouble other_side_y; + + /* Holds the coordinate to be used as center when fixed-center is used. */ + gdouble center_x_on_fixed_center; + gdouble center_y_on_fixed_center; + + /* True when the rectangle is being adjusted (moved or + * rubber-banded). + */ + gboolean rect_adjusting; + + + /* The rest of the members are internal state variables, that is, variables + * that might change during the manipulation session of the rectangle. Make + * sure these variables are in consistent states. + */ + + /* Coordinates of upper left and lower right rectangle corners. */ + gdouble x1, y1; + gdouble x2, y2; + + /* Integer coordinats of upper left corner and size. We must + * calculate this separately from the gdouble ones because sometimes + * we don't want to affect the integer size (e.g. when moving the + * rectangle), but that will be the case if we always calculate the + * integer coordinates based on rounded values of the gdouble + * coordinates even if the gdouble width remains constant. + * + * TODO: Change the internal double-representation of the rectangle + * to x,y width,height instead of x1,y1 x2,y2. That way we don't + * need to keep a separate representation of the integer version of + * the rectangle; rounding width an height will yield consistent + * results and not depend on position of the rectangle. + */ + gint x1_int, y1_int; + gint width_int, height_int; + + /* How to constrain the rectangle. */ + GimpRectangleConstraint constraint; + + /* What precision the rectangle will appear to have externally (it + * will always be double internally) + */ + GimpRectanglePrecision precision; + + /* Previous coordinate applied to the rectangle. */ + gdouble lastx; + gdouble lasty; + + /* Width and height of corner handles. */ + gint corner_handle_w; + gint corner_handle_h; + + /* Width and height of side handles. */ + gint top_and_bottom_handle_w; + gint left_and_right_handle_h; + + /* Whether or not the rectangle is in a 'narrow situation' i.e. it is + * too small for reasonable sized handle to be inside. In this case + * we put handles on the outside. + */ + gboolean narrow_mode; + + /* This boolean allows to always set narrow mode */ + gboolean force_narrow_mode; + + /* Whether or not to draw an ellipse inside the rectangle */ + gboolean draw_ellipse; + + /* Whether to draw round corners */ + gboolean round_corners; + gdouble corner_radius; + + /* The title for the statusbar coords */ + gchar *status_title; + + /* For saving in case of cancellation. */ + gdouble saved_x1; + gdouble saved_y1; + gdouble saved_x2; + gdouble saved_y2; + + gint suppress_updates; + + GimpRectangleFunction function; + + /* The following values are externally synced with GimpRectangleOptions */ + + gboolean highlight; + gdouble highlight_opacity; + GimpGuidesType guide; + + gdouble x; + gdouble y; + gdouble width; + gdouble height; + + gboolean fixed_rule_active; + GimpRectangleFixedRule fixed_rule; + gdouble desired_fixed_width; + gdouble desired_fixed_height; + gdouble desired_fixed_size_width; + gdouble desired_fixed_size_height; + gdouble aspect_numerator; + gdouble aspect_denominator; + gboolean fixed_center; + + /* Canvas items for drawing the GUI */ + + GimpCanvasItem *guides; + GimpCanvasItem *rectangle; + GimpCanvasItem *ellipse; + GimpCanvasItem *corners[4]; + GimpCanvasItem *center; + GimpCanvasItem *creating_corners[4]; + GimpCanvasItem *handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS]; + GimpCanvasItem *highlight_handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS]; + + /* Flags to prevent temporary changes to the Expand from center and Fixed + options for the rectangle select tool, ellipse select tool, and the crop + tool due to use of the modifier keys becoming latched. See issue #7954 and + MR !779. */ + gboolean fixed_center_copy; + gboolean fixed_rule_active_copy; + gboolean modifier_toggle_allowed; +}; + + +/* local function prototypes */ + +static void gimp_tool_rectangle_constructed (GObject *object); +static void gimp_tool_rectangle_finalize (GObject *object); +static void gimp_tool_rectangle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_rectangle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_tool_rectangle_notify (GObject *object, + GParamSpec *pspec); + +static void gimp_tool_rectangle_changed (GimpToolWidget *widget); +static gint gimp_tool_rectangle_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_rectangle_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_rectangle_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_rectangle_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_rectangle_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_rectangle_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_rectangle_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static void gimp_tool_rectangle_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_rectangle_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_rectangle_change_complete (GimpToolRectangle *rectangle); + +static void gimp_tool_rectangle_update_options (GimpToolRectangle *rectangle); +static void gimp_tool_rectangle_update_handle_sizes + (GimpToolRectangle *rectangle); +static void gimp_tool_rectangle_update_status (GimpToolRectangle *rectangle); + +static void gimp_tool_rectangle_synthesize_motion + (GimpToolRectangle *rectangle, + gint function, + gdouble new_x, + gdouble new_y); + +static GimpRectangleFunction + gimp_tool_rectangle_calc_function (GimpToolRectangle *rectangle, + const GimpCoords *coords, + gboolean proximity); +static void gimp_tool_rectangle_check_function (GimpToolRectangle *rectangle); + +static gboolean gimp_tool_rectangle_coord_outside (GimpToolRectangle *rectangle, + const GimpCoords *coords); + +static gboolean gimp_tool_rectangle_coord_on_handle (GimpToolRectangle *rectangle, + const GimpCoords *coords, + GimpHandleAnchor anchor); + +static GimpHandleAnchor gimp_tool_rectangle_get_anchor + (GimpRectangleFunction function); +static gboolean gimp_tool_rectangle_rect_rubber_banding_func + (GimpToolRectangle *rectangle); +static gboolean gimp_tool_rectangle_rect_adjusting_func + (GimpToolRectangle *rectangle); + +static void gimp_tool_rectangle_get_other_side (GimpToolRectangle *rectangle, + gdouble **other_x, + gdouble **other_y); +static void gimp_tool_rectangle_get_other_side_coord + (GimpToolRectangle *rectangle, + gdouble *other_side_x, + gdouble *other_side_y); +static void gimp_tool_rectangle_set_other_side_coord + (GimpToolRectangle *rectangle, + gdouble other_side_x, + gdouble other_side_y); + +static void gimp_tool_rectangle_apply_coord (GimpToolRectangle *rectangle, + gdouble coord_x, + gdouble coord_y); +static void gimp_tool_rectangle_setup_snap_offsets + (GimpToolRectangle *rectangle, + const GimpCoords *coords); + +static void gimp_tool_rectangle_clamp (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically); +static void gimp_tool_rectangle_clamp_width (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically); +static void gimp_tool_rectangle_clamp_height (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically); + +static void gimp_tool_rectangle_keep_inside (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint); +static void gimp_tool_rectangle_keep_inside_horizontally + (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint); +static void gimp_tool_rectangle_keep_inside_vertically + (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint); + +static void gimp_tool_rectangle_apply_fixed_width + (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint, + gdouble width); +static void gimp_tool_rectangle_apply_fixed_height + (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint, + gdouble height); + +static void gimp_tool_rectangle_apply_aspect (GimpToolRectangle *rectangle, + gdouble aspect, + gint clamped_sides); + +static void gimp_tool_rectangle_update_with_coord + (GimpToolRectangle *rectangle, + gdouble new_x, + gdouble new_y); +static void gimp_tool_rectangle_apply_fixed_rule(GimpToolRectangle *rectangle); + +static void gimp_tool_rectangle_get_constraints (GimpToolRectangle *rectangle, + gint *min_x, + gint *min_y, + gint *max_x, + gint *max_y, + GimpRectangleConstraint constraint); + +static void gimp_tool_rectangle_handle_general_clamping + (GimpToolRectangle *rectangle); +static void gimp_tool_rectangle_update_int_rect (GimpToolRectangle *rectangle); +static void gimp_tool_rectangle_adjust_coord (GimpToolRectangle *rectangle, + gdouble coord_x_input, + gdouble coord_y_input, + gdouble *coord_x_output, + gdouble *coord_y_output); +static void gimp_tool_rectangle_recalculate_center_xy + (GimpToolRectangle *rectangle); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolRectangle, gimp_tool_rectangle, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_rectangle_parent_class + +static guint rectangle_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_tool_rectangle_class_init (GimpToolRectangleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_rectangle_constructed; + object_class->finalize = gimp_tool_rectangle_finalize; + object_class->set_property = gimp_tool_rectangle_set_property; + object_class->get_property = gimp_tool_rectangle_get_property; + object_class->notify = gimp_tool_rectangle_notify; + + widget_class->changed = gimp_tool_rectangle_changed; + widget_class->button_press = gimp_tool_rectangle_button_press; + widget_class->button_release = gimp_tool_rectangle_button_release; + widget_class->motion = gimp_tool_rectangle_motion; + widget_class->hit = gimp_tool_rectangle_hit; + widget_class->hover = gimp_tool_rectangle_hover; + widget_class->leave_notify = gimp_tool_rectangle_leave_notify; + widget_class->key_press = gimp_tool_rectangle_key_press; + widget_class->motion_modifier = gimp_tool_rectangle_motion_modifier; + widget_class->get_cursor = gimp_tool_rectangle_get_cursor; + widget_class->update_on_scale = TRUE; + + rectangle_signals[CHANGE_COMPLETE] = + g_signal_new ("change-complete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolRectangleClass, change_complete), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAINT, + g_param_spec_enum ("constraint", + NULL, NULL, + GIMP_TYPE_RECTANGLE_CONSTRAINT, + GIMP_RECTANGLE_CONSTRAIN_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PRECISION, + g_param_spec_enum ("precision", + NULL, NULL, + GIMP_TYPE_RECTANGLE_PRECISION, + GIMP_RECTANGLE_PRECISION_INT, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_NARROW_MODE, + g_param_spec_boolean ("narrow-mode", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FORCE_NARROW_MODE, + g_param_spec_boolean ("force-narrow-mode", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DRAW_ELLIPSE, + g_param_spec_boolean ("draw-ellipse", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ROUND_CORNERS, + g_param_spec_boolean ("round-corners", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CORNER_RADIUS, + g_param_spec_double ("corner-radius", + NULL, NULL, + 0.0, 10000.0, 10.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_STATUS_TITLE, + g_param_spec_string ("status-title", + NULL, NULL, + _("Rectangle: "), + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_HIGHLIGHT, + g_param_spec_boolean ("highlight", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_HIGHLIGHT_OPACITY, + g_param_spec_double ("highlight-opacity", + NULL, NULL, + 0.0, 1.0, 0.5, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GUIDE, + g_param_spec_enum ("guide", + NULL, NULL, + GIMP_TYPE_GUIDES_TYPE, + GIMP_GUIDES_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_double ("width", + NULL, NULL, + 0.0, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_double ("height", + NULL, NULL, + 0.0, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FIXED_RULE_ACTIVE, + g_param_spec_boolean ("fixed-rule-active", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FIXED_RULE, + g_param_spec_enum ("fixed-rule", + NULL, NULL, + GIMP_TYPE_RECTANGLE_FIXED_RULE, + GIMP_RECTANGLE_FIXED_ASPECT, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DESIRED_FIXED_WIDTH, + g_param_spec_double ("desired-fixed-width", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 100.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DESIRED_FIXED_HEIGHT, + g_param_spec_double ("desired-fixed-height", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 100.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_WIDTH, + g_param_spec_double ("desired-fixed-size-width", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 100.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_HEIGHT, + g_param_spec_double ("desired-fixed-size-height", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 100.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ASPECT_NUMERATOR, + g_param_spec_double ("aspect-numerator", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ASPECT_DENOMINATOR, + g_param_spec_double ("aspect-denominator", + NULL, NULL, + 0.0, GIMP_MAX_IMAGE_SIZE, + 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FIXED_CENTER, + g_param_spec_boolean ("fixed-center", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_rectangle_init (GimpToolRectangle *rectangle) +{ + rectangle->private = gimp_tool_rectangle_get_instance_private (rectangle); + + rectangle->private->function = GIMP_TOOL_RECTANGLE_CREATING; + rectangle->private->is_first = TRUE; + rectangle->private->modifier_toggle_allowed = FALSE; +} + +static void +gimp_tool_rectangle_constructed (GObject *object) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolRectanglePrivate *private = rectangle->private; + GimpCanvasGroup *stroke_group; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->guides = gimp_tool_widget_add_rectangle_guides (widget, + 0, 0, 10, 10, + GIMP_GUIDES_NONE); + + private->rectangle = gimp_tool_widget_add_rectangle (widget, + 0, 0, 10, 10, + FALSE); + + private->ellipse = gimp_tool_widget_add_arc (widget, + 0, 0, 10, 10, + 0.0, 2 * G_PI, + FALSE); + + for (i = 0; i < 4; i++) + private->corners[i] = gimp_tool_widget_add_arc (widget, + 0, 0, 10, 10, + 0.0, 2 * G_PI, + FALSE); + + gimp_tool_widget_pop_group (widget); + + private->center = gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CROSS, + 0, 0, + GIMP_CANVAS_HANDLE_SIZE_SMALL, + GIMP_CANVAS_HANDLE_SIZE_SMALL, + GIMP_HANDLE_ANCHOR_CENTER); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->creating_corners[0] = + gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_NORTH_WEST, + 10, 10, + FALSE); + + private->creating_corners[1] = + gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_NORTH_EAST, + 10, 10, + FALSE); + + private->creating_corners[2] = + gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_SOUTH_WEST, + 10, 10, + FALSE); + + private->creating_corners[3] = + gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_SOUTH_EAST, + 10, 10, + FALSE); + + gimp_tool_widget_pop_group (widget); + + for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT; + i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM; + i++) + { + GimpHandleAnchor anchor; + + anchor = gimp_tool_rectangle_get_anchor (i); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->handles[i] = gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + anchor, + 10, 10, + FALSE); + + gimp_tool_widget_pop_group (widget); + + private->highlight_handles[i] = gimp_tool_widget_add_corner (widget, + 0, 0, 10, 10, + anchor, + 10, 10, + FALSE); + gimp_canvas_item_set_highlight (private->highlight_handles[i], TRUE); + } + + gimp_tool_rectangle_changed (widget); +} + +static void +gimp_tool_rectangle_finalize (GObject *object) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); + GimpToolRectanglePrivate *private = rectangle->private; + + g_clear_pointer (&private->status_title, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_rectangle_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); + GimpToolRectanglePrivate *private = rectangle->private; + + switch (property_id) + { + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + case PROP_CONSTRAINT: + private->constraint = g_value_get_enum (value); + break; + case PROP_PRECISION: + private->precision = g_value_get_enum (value); + break; + + case PROP_NARROW_MODE: + private->narrow_mode = g_value_get_boolean (value); + break; + case PROP_FORCE_NARROW_MODE: + private->force_narrow_mode = g_value_get_boolean (value); + break; + case PROP_DRAW_ELLIPSE: + private->draw_ellipse = g_value_get_boolean (value); + break; + case PROP_ROUND_CORNERS: + private->round_corners = g_value_get_boolean (value); + break; + case PROP_CORNER_RADIUS: + private->corner_radius = g_value_get_double (value); + break; + + case PROP_STATUS_TITLE: + g_free (private->status_title); + private->status_title = g_value_dup_string (value); + if (! private->status_title) + private->status_title = g_strdup (_("Rectangle: ")); + break; + + case PROP_HIGHLIGHT: + private->highlight = g_value_get_boolean (value); + break; + case PROP_HIGHLIGHT_OPACITY: + private->highlight_opacity = g_value_get_double (value); + break; + case PROP_GUIDE: + private->guide = g_value_get_enum (value); + break; + + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_WIDTH: + private->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + private->height = g_value_get_double (value); + break; + + case PROP_FIXED_RULE_ACTIVE: + private->fixed_rule_active = g_value_get_boolean (value); + break; + case PROP_FIXED_RULE: + private->fixed_rule = g_value_get_enum (value); + break; + case PROP_DESIRED_FIXED_WIDTH: + private->desired_fixed_width = g_value_get_double (value); + break; + case PROP_DESIRED_FIXED_HEIGHT: + private->desired_fixed_height = g_value_get_double (value); + break; + case PROP_DESIRED_FIXED_SIZE_WIDTH: + private->desired_fixed_size_width = g_value_get_double (value); + break; + case PROP_DESIRED_FIXED_SIZE_HEIGHT: + private->desired_fixed_size_height = g_value_get_double (value); + break; + case PROP_ASPECT_NUMERATOR: + private->aspect_numerator = g_value_get_double (value); + break; + case PROP_ASPECT_DENOMINATOR: + private->aspect_denominator = g_value_get_double (value); + break; + + case PROP_FIXED_CENTER: + private->fixed_center = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_rectangle_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); + GimpToolRectanglePrivate *private = rectangle->private; + + switch (property_id) + { + case PROP_X1: + g_value_set_double (value, private->x1); + break; + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + case PROP_X2: + g_value_set_double (value, private->x2); + break; + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_CONSTRAINT: + g_value_set_enum (value, private->constraint); + break; + case PROP_PRECISION: + g_value_set_enum (value, private->precision); + break; + + case PROP_NARROW_MODE: + g_value_set_boolean (value, private->narrow_mode); + break; + case PROP_FORCE_NARROW_MODE: + g_value_set_boolean (value, private->force_narrow_mode); + break; + case PROP_DRAW_ELLIPSE: + g_value_set_boolean (value, private->draw_ellipse); + break; + case PROP_ROUND_CORNERS: + g_value_set_boolean (value, private->round_corners); + break; + case PROP_CORNER_RADIUS: + g_value_set_double (value, private->corner_radius); + break; + + case PROP_STATUS_TITLE: + g_value_set_string (value, private->status_title); + break; + + case PROP_HIGHLIGHT: + g_value_set_boolean (value, private->highlight); + break; + case PROP_HIGHLIGHT_OPACITY: + g_value_set_double (value, private->highlight_opacity); + break; + case PROP_GUIDE: + g_value_set_enum (value, private->guide); + break; + + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_WIDTH: + g_value_set_double (value, private->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, private->height); + break; + + case PROP_FIXED_RULE_ACTIVE: + g_value_set_boolean (value, private->fixed_rule_active); + break; + case PROP_FIXED_RULE: + g_value_set_enum (value, private->fixed_rule); + break; + case PROP_DESIRED_FIXED_WIDTH: + g_value_set_double (value, private->desired_fixed_width); + break; + case PROP_DESIRED_FIXED_HEIGHT: + g_value_set_double (value, private->desired_fixed_height); + break; + case PROP_DESIRED_FIXED_SIZE_WIDTH: + g_value_set_double (value, private->desired_fixed_size_width); + break; + case PROP_DESIRED_FIXED_SIZE_HEIGHT: + g_value_set_double (value, private->desired_fixed_size_height); + break; + case PROP_ASPECT_NUMERATOR: + g_value_set_double (value, private->aspect_numerator); + break; + case PROP_ASPECT_DENOMINATOR: + g_value_set_double (value, private->aspect_denominator); + break; + + case PROP_FIXED_CENTER: + g_value_set_boolean (value, private->fixed_center); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_rectangle_notify (GObject *object, + GParamSpec *pspec) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); + GimpToolRectanglePrivate *private = rectangle->private; + + if (G_OBJECT_CLASS (parent_class)->notify) + G_OBJECT_CLASS (parent_class)->notify (object, pspec); + + if (! strcmp (pspec->name, "x1") || + ! strcmp (pspec->name, "y1") || + ! strcmp (pspec->name, "x2") || + ! strcmp (pspec->name, "y2")) + { + gimp_tool_rectangle_update_int_rect (rectangle); + + gimp_tool_rectangle_recalculate_center_xy (rectangle); + + gimp_tool_rectangle_update_options (rectangle); + } + else if (! strcmp (pspec->name, "x") && + ! PIXEL_FEQUAL (private->x1, private->x)) + { + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_MOVING, + private->x, + private->y1); + } + else if (! strcmp (pspec->name, "y") && + ! PIXEL_FEQUAL (private->y1, private->y)) + { + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_MOVING, + private->x1, + private->y); + } + else if (! strcmp (pspec->name, "width") && + ! PIXEL_FEQUAL (private->x2 - private->x1, private->width)) + { + /* Calculate x2, y2 that will create a rectangle of given width, + * for the current options. + */ + gdouble x2; + + if (private->fixed_center) + { + x2 = private->center_x_on_fixed_center + + private->width / 2; + } + else + { + x2 = private->x1 + private->width; + } + + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_RESIZING_RIGHT, + x2, + private->y2); + } + else if (! strcmp (pspec->name, "height") && + ! PIXEL_FEQUAL (private->y2 - private->y1, private->height)) + { + /* Calculate x2, y2 that will create a rectangle of given + * height, for the current options. + */ + gdouble y2; + + if (private->fixed_center) + { + y2 = private->center_y_on_fixed_center + + private->height / 2; + } + else + { + y2 = private->y1 + private->height; + } + + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM, + private->x2, + y2); + } + else if (! strcmp (pspec->name, "desired-fixed-size-width")) + { + /* We are only interested in when width and height swaps, so + * it's enough to only check e.g. for width. + */ + + gdouble width = private->x2 - private->x1; + gdouble height = private->y2 - private->y1; + + /* Depending on a bunch of conditions, we might want to + * immedieately switch width and height of the pending + * rectangle. + */ + if (private->fixed_rule_active && +#if 0 + tool->button_press_state == 0 && + tool->active_modifier_state == 0 && +#endif + FEQUAL (private->desired_fixed_size_width, height) && + FEQUAL (private->desired_fixed_size_height, width)) + { + gdouble x = private->x1; + gdouble y = private->y1; + + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT, + private->x2, + private->y2); + + /* For some reason these needs to be set separately... */ + g_object_set (rectangle, + "x", x, + NULL); + g_object_set (rectangle, + "y", y, + NULL); + } + } + else if (! strcmp (pspec->name, "aspect-numerator")) + { + /* We are only interested in when numerator and denominator + * swaps, so it's enough to only check e.g. for numerator. + */ + + double width = private->x2 - private->x1; + double height = private->y2 - private->y1; + gdouble new_inverse_ratio = private->aspect_denominator / + private->aspect_numerator; + gdouble lower_ratio; + gdouble higher_ratio; + + /* The ratio of the Fixed: Aspect ratio rule and the pending + * rectangle is very rarely exactly the same so use an + * interval. For small rectangles the below code will + * automatically yield a more generous accepted ratio interval + * which is exactly what we want. + */ + if (width > height && height > 1.0) + { + lower_ratio = width / (height + 1.0); + higher_ratio = width / (height - 1.0); + } + else + { + lower_ratio = (width - 1.0) / height; + higher_ratio = (width + 1.0) / height; + } + + /* Depending on a bunch of conditions, we might want to + * immedieately switch width and height of the pending + * rectangle. + */ + if (private->fixed_rule_active && +#if 0 + tool->button_press_state == 0 && + tool->active_modifier_state == 0 && +#endif + lower_ratio < new_inverse_ratio && + higher_ratio > new_inverse_ratio) + { + gdouble new_x2 = private->x1 + private->y2 - private->y1; + gdouble new_y2 = private->y1 + private->x2 - private->x1; + + gimp_tool_rectangle_synthesize_motion (rectangle, + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT, + new_x2, + new_y2); + } + } +} + +static void +gimp_tool_rectangle_changed (GimpToolWidget *widget) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + gdouble x1, y1, x2, y2; + gint handle_width; + gint handle_height; + gint i; + + gimp_tool_rectangle_update_handle_sizes (rectangle); + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + + gimp_canvas_rectangle_guides_set (private->guides, + x1, y1, + x2 - x1, + y2 - y1, + private->guide, 4); + + gimp_canvas_rectangle_set (private->rectangle, + x1, y1, + x2 - x1, + y2 - y1); + + if (private->draw_ellipse) + { + gimp_canvas_arc_set (private->ellipse, + (x1 + x2) / 2.0, + (y1 + y2) / 2.0, + (x2 - x1) / 2.0, + (y2 - y1) / 2.0, + 0.0, 2 * G_PI); + gimp_canvas_item_set_visible (private->ellipse, TRUE); + } + else + { + gimp_canvas_item_set_visible (private->ellipse, FALSE); + } + + if (private->round_corners && private->corner_radius > 0.0) + { + gdouble radius; + + radius = MIN (private->corner_radius, + MIN ((x2 - x1) / 2.0, (y2 - y1) / 2.0)); + + gimp_canvas_arc_set (private->corners[0], + x1 + radius, + y1 + radius, + radius, radius, + G_PI / 2.0, G_PI / 2.0); + + gimp_canvas_arc_set (private->corners[1], + x2 - radius, + y1 + radius, + radius, radius, + 0.0, G_PI / 2.0); + + gimp_canvas_arc_set (private->corners[2], + x1 + radius, + y2 - radius, + radius, radius, + G_PI, G_PI / 2.0); + + gimp_canvas_arc_set (private->corners[3], + x2 - radius, + y2 - radius, + radius, radius, + G_PI * 1.5, G_PI / 2.0); + + for (i = 0; i < 4; i++) + gimp_canvas_item_set_visible (private->corners[i], TRUE); + } + else + { + for (i = 0; i < 4; i++) + gimp_canvas_item_set_visible (private->corners[i], FALSE); + } + + gimp_canvas_item_set_visible (private->center, FALSE); + + for (i = 0; i < 4; i++) + { + gimp_canvas_item_set_visible (private->creating_corners[i], FALSE); + gimp_canvas_item_set_highlight (private->creating_corners[i], FALSE); + } + + for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT; + i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM; + i++) + { + gimp_canvas_item_set_visible (private->handles[i], FALSE); + gimp_canvas_item_set_visible (private->highlight_handles[i], FALSE); + } + + handle_width = private->corner_handle_w; + handle_height = private->corner_handle_h; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_MOVING: + if (private->rect_adjusting) + { + /* Mark the center because we snap to it */ + gimp_canvas_handle_set_position (private->center, + (x1 + x2) / 2.0, + (y1 + y2) / 2.0); + gimp_canvas_item_set_visible (private->center, TRUE); + break; + } + + /* else fallthrough */ + + case GIMP_TOOL_RECTANGLE_DEAD: + case GIMP_TOOL_RECTANGLE_CREATING: + case GIMP_TOOL_RECTANGLE_AUTO_SHRINK: + for (i = 0; i < 4; i++) + { + gimp_canvas_corner_set (private->creating_corners[i], + x1, y1, x2 - x1, y2 - y1, + handle_width, handle_height, + private->narrow_mode); + gimp_canvas_item_set_visible (private->creating_corners[i], TRUE); + } + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + handle_width = private->top_and_bottom_handle_w; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + handle_height = private->left_and_right_handle_h; + break; + + default: + break; + } + + if (handle_width >= 3 && + handle_height >= 3 && + private->function >= GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT && + private->function <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM) + { + GimpCanvasItem *corner; + + if (private->rect_adjusting) + corner = private->handles[private->function]; + else + corner = private->highlight_handles[private->function]; + + gimp_canvas_corner_set (corner, + x1, y1, x2 - x1, y2 - y1, + handle_width, handle_height, + private->narrow_mode); + gimp_canvas_item_set_visible (corner, TRUE); + } + + if (private->highlight && ! private->rect_adjusting) + { + GdkRectangle rect; + + rect.x = x1; + rect.y = y1; + rect.width = x2 - x1; + rect.height = y2 - y1; + + gimp_display_shell_set_highlight (shell, &rect, private->highlight_opacity); + } + else + { + gimp_display_shell_set_highlight (shell, NULL, 0.0); + } +} + +gint +gimp_tool_rectangle_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + gdouble snapped_x, snapped_y; + gint snap_x, snap_y; + + /* Prevent the latching of toggled modifiers (Ctrl and Shift) when the + selection is cancelled whilst one or both of these modifiers are + being pressed (see issue #7954 and MR !799) */ + private->fixed_center_copy = private->fixed_center; + private->fixed_rule_active_copy = private->fixed_rule_active; + private->modifier_toggle_allowed = TRUE; + + /* save existing shape in case of cancellation */ + private->saved_x1 = private->x1; + private->saved_y1 = private->y1; + private->saved_x2 = private->x2; + private->saved_y2 = private->y2; + + gimp_tool_rectangle_setup_snap_offsets (rectangle, coords); + gimp_tool_widget_get_snap_offsets (widget, &snap_x, &snap_y, NULL, NULL); + + snapped_x = coords->x + snap_x; + snapped_y = coords->y + snap_y; + + private->lastx = snapped_x; + private->lasty = snapped_y; + + if (private->function == GIMP_TOOL_RECTANGLE_CREATING) + { + /* Remember that this rectangle was created from scratch. */ + private->is_new = TRUE; + + private->x1 = private->x2 = snapped_x; + private->y1 = private->y2 = snapped_y; + + /* Unless forced, created rectangles should not be started in + * narrow-mode + */ + if (private->force_narrow_mode) + private->narrow_mode = TRUE; + else + private->narrow_mode = FALSE; + + /* If the rectangle is being modified we want the center on + * fixed_center to be at the center of the currently existing + * rectangle, otherwise we want the point where the user clicked + * to be the center on fixed_center. + */ + private->center_x_on_fixed_center = snapped_x; + private->center_y_on_fixed_center = snapped_y; + + /* When the user toggles modifier keys, we want to keep track of + * what coordinates the "other side" should have. If we are + * creating a rectangle, use the current mouse coordinates as + * the coordinate of the "other side", otherwise use the + * immediate "other side" for that. + */ + private->other_side_x = snapped_x; + private->other_side_y = snapped_y; + } + else + { + /* This rectangle was not created from scratch. */ + private->is_new = FALSE; + + private->center_x_on_fixed_center = (private->x1 + private->x2) / 2; + private->center_y_on_fixed_center = (private->y1 + private->y2) / 2; + + gimp_tool_rectangle_get_other_side_coord (rectangle, + &private->other_side_x, + &private->other_side_y); + } + + gimp_tool_rectangle_update_int_rect (rectangle); + + /* Is the rectangle being rubber-banded? */ + private->rect_adjusting = gimp_tool_rectangle_rect_adjusting_func (rectangle); + + gimp_tool_rectangle_changed (widget); + + gimp_tool_rectangle_update_status (rectangle); + + return 1; +} + +void +gimp_tool_rectangle_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + gint response = 0; + + gimp_tool_widget_set_status (widget, NULL); + + /* On button release, we are not rubber-banding the rectangle any longer. */ + private->rect_adjusting = FALSE; + + gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0); + + /* Prevent the latching of toggled modifiers (Ctrl and Shift) when the + selection is cancelled whilst one or both of these modifiers are + being pressed (see issue #7954 and MR !799) */ + private->modifier_toggle_allowed = FALSE; + g_object_set (rectangle, + "fixed-center", private->fixed_center_copy, + "fixed-rule-active", private->fixed_rule_active_copy, + NULL); + + switch (release_type) + { + case GIMP_BUTTON_RELEASE_NO_MOTION: + /* If the first created rectangle was not expanded, halt the + * tool... + */ + if (gimp_tool_rectangle_rectangle_is_first (rectangle)) + { + response = GIMP_TOOL_WIDGET_RESPONSE_CANCEL; + break; + } + + /* ...else fallthrough and treat a long click without movement + * like a normal change + */ + + case GIMP_BUTTON_RELEASE_NORMAL: + /* If a normal click-drag-release actually created a rectangle + * with content... + */ + if (private->x1 != private->x2 && + private->y1 != private->y2) + { + gimp_tool_rectangle_change_complete (rectangle); + break; + } + + /* ...else fallthrough and undo the operation, we can't have + * zero-extent rectangles + */ + + case GIMP_BUTTON_RELEASE_CANCEL: + private->x1 = private->saved_x1; + private->y1 = private->saved_y1; + private->x2 = private->saved_x2; + private->y2 = private->saved_y2; + + gimp_tool_rectangle_update_int_rect (rectangle); + + /* If the first created rectangle was canceled, halt the tool */ + if (gimp_tool_rectangle_rectangle_is_first (rectangle)) + response = GIMP_TOOL_WIDGET_RESPONSE_CANCEL; + break; + + case GIMP_BUTTON_RELEASE_CLICK: + /* When a dead area is clicked, don't execute. */ + if (private->function != GIMP_TOOL_RECTANGLE_DEAD) + response = GIMP_TOOL_WIDGET_RESPONSE_CONFIRM; + break; + } + + /* We must update this. */ + gimp_tool_rectangle_recalculate_center_xy (rectangle); + + gimp_tool_rectangle_update_options (rectangle); + + gimp_tool_rectangle_changed (widget); + + private->is_first = FALSE; + + /* emit response at the end, so everything is up to date even if + * a signal handler decides hot to shut down the rectangle + */ + if (response != 0) + gimp_tool_widget_response (widget, response); +} + +void +gimp_tool_rectangle_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + gdouble snapped_x; + gdouble snapped_y; + gint snap_x, snap_y; + + /* Motion events should be ignored when we're just waiting for the + * button release event to execute or if the user has grabbed a dead + * area of the rectangle. + */ + if (private->function == GIMP_TOOL_RECTANGLE_EXECUTING || + private->function == GIMP_TOOL_RECTANGLE_DEAD) + return; + + /* Handle snapping. */ + gimp_tool_widget_get_snap_offsets (widget, &snap_x, &snap_y, NULL, NULL); + + snapped_x = coords->x + snap_x; + snapped_y = coords->y + snap_y; + + /* This is the core rectangle shape updating function: */ + gimp_tool_rectangle_update_with_coord (rectangle, snapped_x, snapped_y); + + gimp_tool_rectangle_update_status (rectangle); + + if (private->function == GIMP_TOOL_RECTANGLE_CREATING) + { + GimpRectangleFunction function = GIMP_TOOL_RECTANGLE_CREATING; + gdouble dx = snapped_x - private->lastx; + gdouble dy = snapped_y - private->lasty; + + /* When the user starts to move the cursor, set the current + * function to one of the corner-grabbed functions, depending on + * in what direction the user starts dragging the rectangle. + */ + if (dx < 0) + { + function = (dy < 0 ? + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT : + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT); + } + else if (dx > 0) + { + function = (dy < 0 ? + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT : + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT); + } + else if (dy < 0) + { + function = (dx < 0 ? + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT : + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT); + } + else if (dy > 0) + { + function = (dx < 0 ? + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT : + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT); + } + + gimp_tool_rectangle_set_function (rectangle, function); + + if (private->fixed_rule_active && + private->fixed_rule == GIMP_RECTANGLE_FIXED_SIZE) + { + /* For fixed size, set the function to moving immediately since the + * rectangle can not be resized anyway. + */ + + /* We fake a coord update to get the right size. */ + gimp_tool_rectangle_update_with_coord (rectangle, + snapped_x, + snapped_y); + + gimp_tool_widget_set_snap_offsets (widget, + -(private->x2 - private->x1) / 2, + -(private->y2 - private->y1) / 2, + private->x2 - private->x1, + private->y2 - private->y1); + + gimp_tool_rectangle_set_function (rectangle, + GIMP_TOOL_RECTANGLE_MOVING); + } + } + + gimp_tool_rectangle_update_options (rectangle); + + private->lastx = snapped_x; + private->lasty = snapped_y; +} + +GimpHit +gimp_tool_rectangle_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleFunction function; + + if (private->suppress_updates) + { + function = gimp_tool_rectangle_get_function (rectangle); + } + else + { + function = gimp_tool_rectangle_calc_function (rectangle, + coords, proximity); + } + + switch (function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + return GIMP_HIT_DIRECT; + + case GIMP_TOOL_RECTANGLE_CREATING: + case GIMP_TOOL_RECTANGLE_MOVING: + return GIMP_HIT_INDIRECT; + + case GIMP_TOOL_RECTANGLE_DEAD: + case GIMP_TOOL_RECTANGLE_AUTO_SHRINK: + case GIMP_TOOL_RECTANGLE_EXECUTING: + default: + return GIMP_HIT_NONE; + } +} + +void +gimp_tool_rectangle_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleFunction function; + + if (private->suppress_updates) + { + private->suppress_updates--; + return; + } + + function = gimp_tool_rectangle_calc_function (rectangle, coords, proximity); + + gimp_tool_rectangle_set_function (rectangle, function); +} + +static void +gimp_tool_rectangle_leave_notify (GimpToolWidget *widget) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + + gimp_tool_rectangle_set_function (rectangle, GIMP_TOOL_RECTANGLE_DEAD); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static gboolean +gimp_tool_rectangle_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + gint dx = 0; + gint dy = 0; + gdouble new_x = 0; + gdouble new_y = 0; + + switch (kevent->keyval) + { + case GDK_KEY_Up: + dy = -1; + break; + case GDK_KEY_Left: + dx = -1; + break; + case GDK_KEY_Right: + dx = 1; + break; + case GDK_KEY_Down: + dy = 1; + break; + + default: + return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); + } + + /* If the shift key is down, move by an accelerated increment */ + if (kevent->state & gimp_get_extend_selection_mask ()) + { + dx *= ARROW_VELOCITY; + dy *= ARROW_VELOCITY; + } + + gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0); + + /* Resize the rectangle if the mouse is over a handle, otherwise move it */ + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_MOVING: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + new_x = private->x1 + dx; + new_y = private->y1 + dy; + private->lastx = new_x; + private->lasty = new_y; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + new_x = private->x2 + dx; + new_y = private->y1 + dy; + private->lastx = new_x; + private->lasty = new_y; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + new_x = private->x1 + dx; + new_y = private->y2 + dy; + private->lastx = new_x; + private->lasty = new_y; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + new_x = private->x2 + dx; + new_y = private->y2 + dy; + private->lastx = new_x; + private->lasty = new_y; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + new_x = private->x1 + dx; + private->lastx = new_x; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + new_x = private->x2 + dx; + private->lastx = new_x; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + new_y = private->y1 + dy; + private->lasty = new_y; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + new_y = private->y2 + dy; + private->lasty = new_y; + break; + + default: + return TRUE; + } + + gimp_tool_rectangle_update_with_coord (rectangle, new_x, new_y); + + gimp_tool_rectangle_recalculate_center_xy (rectangle); + + gimp_tool_rectangle_update_options (rectangle); + + gimp_tool_rectangle_change_complete (rectangle); + + /* Evil hack to suppress oper updates. We do this because we don't + * want the rectangle tool to change function while the rectangle + * is being resized or moved using the keyboard. + */ + private->suppress_updates = 2; + + return TRUE; +} + +static void +gimp_tool_rectangle_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + gboolean button1_down; + + button1_down = (state & GDK_BUTTON1_MASK); + + if (key == gimp_get_extend_selection_mask ()) + { +#if 0 + /* Here we want to handle manually when to update the rectangle, so we + * don't want gimp_tool_rectangle_options_notify to do anything. + */ + g_signal_handlers_block_by_func (options, + gimp_tool_rectangle_options_notify, + rectangle); +#endif + if (private->modifier_toggle_allowed) + g_object_set (rectangle, + "fixed-rule-active", ! private->fixed_rule_active, + NULL); + +#if 0 + g_signal_handlers_unblock_by_func (options, + gimp_tool_rectangle_options_notify, + rectangle); +#endif + + /* Only change the shape if the mouse is still down (i.e. the user is + * still editing the rectangle. + */ + if (button1_down) + { + if (! private->fixed_rule_active) + { + /* Reset anchor point */ + gimp_tool_rectangle_set_other_side_coord (rectangle, + private->other_side_x, + private->other_side_y); + } + + gimp_tool_rectangle_update_with_coord (rectangle, + private->lastx, + private->lasty); + } + } + + if (key == gimp_get_toggle_behavior_mask ()) + { + if (private->modifier_toggle_allowed) + g_object_set (rectangle, + "fixed-center", ! private->fixed_center, + NULL); + + if (private->fixed_center) + { + gimp_tool_rectangle_update_with_coord (rectangle, + private->lastx, + private->lasty); + + /* Only emit the rectangle-changed signal if the button is + * not down. If it is down, the signal will and shall be + * emitted on _button_release instead. + */ + if (! button1_down) + { + gimp_tool_rectangle_change_complete (rectangle); + } + } + else if (button1_down) + { + /* If we are leaving fixed_center mode we want to set the + * "other side" where it should be. Don't do anything if we + * came here by a mouse-click though, since then the user + * has confirmed the shape and we don't want to modify it + * afterwards. + */ + gimp_tool_rectangle_set_other_side_coord (rectangle, + private->other_side_x, + private->other_side_y); + } + } + + gimp_tool_rectangle_update_options (rectangle); +} + +static gboolean +gimp_tool_rectangle_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget); + GimpToolRectanglePrivate *private = rectangle->private; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_CREATING: + *cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + break; + case GIMP_TOOL_RECTANGLE_MOVING: + *cursor = GIMP_CURSOR_MOVE; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + *cursor = GIMP_CURSOR_CORNER_TOP_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + *cursor = GIMP_CURSOR_CORNER_TOP_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + *cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + *cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + *cursor = GIMP_CURSOR_SIDE_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + *cursor = GIMP_CURSOR_SIDE_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + *cursor = GIMP_CURSOR_SIDE_TOP; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + *cursor = GIMP_CURSOR_SIDE_BOTTOM; + break; + + default: + return FALSE; + } + + return TRUE; +} + +static void +gimp_tool_rectangle_change_complete (GimpToolRectangle *rectangle) +{ + g_signal_emit (rectangle, rectangle_signals[CHANGE_COMPLETE], 0); +} + +static void +gimp_tool_rectangle_update_options (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gdouble x1, y1; + gdouble x2, y2; + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + +#if 0 + g_signal_handlers_block_by_func (options, + gimp_tool_rectangle_options_notify, + rect_tool); +#endif + + g_object_freeze_notify (G_OBJECT (rectangle)); + + if (! FEQUAL (private->x, x1)) + g_object_set (rectangle, "x", x1, NULL); + + if (! FEQUAL (private->y, y1)) + g_object_set (rectangle, "y", y1, NULL); + + if (! FEQUAL (private->width, x2 - x1)) + g_object_set (rectangle, "width", x2 - x1, NULL); + + if (! FEQUAL (private->height, y2 - y1)) + g_object_set (rectangle, "height", y2 - y1, NULL); + + g_object_thaw_notify (G_OBJECT (rectangle)); + +#if 0 + g_signal_handlers_unblock_by_func (options, + gimp_tool_rectangle_options_notify, + rect_tool); +#endif +} + +static void +gimp_tool_rectangle_update_handle_sizes (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpDisplayShell *shell; + gint visible_rectangle_width; + gint visible_rectangle_height; + gint rectangle_width; + gint rectangle_height; + gdouble pub_x1, pub_y1; + gdouble pub_x2, pub_y2; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + + gimp_tool_rectangle_get_public_rect (rectangle, + &pub_x1, &pub_y1, &pub_x2, &pub_y2); + { + /* Calculate rectangles of the selection rectangle and the display + * shell, with origin at (0, 0) of image, and in screen coordinate + * scale. + */ + gint x1 = pub_x1 * shell->scale_x; + gint y1 = pub_y1 * shell->scale_y; + gint w1 = (pub_x2 - pub_x1) * shell->scale_x; + gint h1 = (pub_y2 - pub_y1) * shell->scale_y; + + gint x2, y2, w2, h2; + + gimp_display_shell_scroll_get_scaled_viewport (shell, &x2, &y2, &w2, &h2); + + rectangle_width = w1; + rectangle_height = h1; + + /* Handle size calculations shall be based on the visible part of + * the rectangle, so calculate the size for the visible rectangle + * by intersecting with the viewport rectangle. + */ + gimp_rectangle_intersect (x1, y1, + w1, h1, + x2, y2, + w2, h2, + NULL, NULL, + &visible_rectangle_width, + &visible_rectangle_height); + + /* Determine if we are in narrow-mode or not. */ + if (private->force_narrow_mode) + private->narrow_mode = TRUE; + else + private->narrow_mode = (visible_rectangle_width < NARROW_MODE_THRESHOLD || + visible_rectangle_height < NARROW_MODE_THRESHOLD); + } + + if (private->narrow_mode) + { + /* Corner handles always have the same (on-screen) size in + * narrow-mode. + */ + private->corner_handle_w = NARROW_MODE_HANDLE_SIZE; + private->corner_handle_h = NARROW_MODE_HANDLE_SIZE; + + private->top_and_bottom_handle_w = CLAMP (rectangle_width, + MIN (rectangle_width - 2, + NARROW_MODE_HANDLE_SIZE), + G_MAXINT); + private->left_and_right_handle_h = CLAMP (rectangle_height, + MIN (rectangle_height - 2, + NARROW_MODE_HANDLE_SIZE), + G_MAXINT); + } + else + { + /* Calculate and clamp corner handle size. */ + + private->corner_handle_w = visible_rectangle_width / 4; + private->corner_handle_h = visible_rectangle_height / 4; + + private->corner_handle_w = CLAMP (private->corner_handle_w, + MIN_HANDLE_SIZE, + MAX_HANDLE_SIZE); + private->corner_handle_h = CLAMP (private->corner_handle_h, + MIN_HANDLE_SIZE, + MAX_HANDLE_SIZE); + + /* Calculate and clamp side handle size. */ + + private->top_and_bottom_handle_w = rectangle_width - 3 * private->corner_handle_w; + private->left_and_right_handle_h = rectangle_height - 3 * private->corner_handle_h; + + private->top_and_bottom_handle_w = CLAMP (private->top_and_bottom_handle_w, + MIN_HANDLE_SIZE, + G_MAXINT); + private->left_and_right_handle_h = CLAMP (private->left_and_right_handle_h, + MIN_HANDLE_SIZE, + G_MAXINT); + } +} + +static void +gimp_tool_rectangle_update_status (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gdouble x1, y1, x2, y2; + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + + if (private->function == GIMP_TOOL_RECTANGLE_MOVING) + { + gimp_tool_widget_set_status_coords (GIMP_TOOL_WIDGET (rectangle), + _("Position: "), + x1, ", ", y1, + NULL); + } + else + { + gchar *aspect_text = NULL; + gint width = x2 - x1; + gint height = y2 - y1; + + if (width > 0.0 && height > 0.0) + { + aspect_text = g_strdup_printf (" (%.2f:1)", + (gdouble) width / (gdouble) height); + } + + gimp_tool_widget_set_status_coords (GIMP_TOOL_WIDGET (rectangle), + private->status_title, + width, " × ", height, + aspect_text); + g_free (aspect_text); + } +} + +static void +gimp_tool_rectangle_synthesize_motion (GimpToolRectangle *rectangle, + gint function, + gdouble new_x, + gdouble new_y) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleFunction old_function; + + /* We don't want to synthesize motions if the tool control is active + * since that means the mouse button is down and the rectangle will + * get updated in _motion anyway. The reason we want to prevent this + * function from executing is that is emits the + * rectangle-changed-complete signal which we don't want in the + * middle of a rectangle change. + * + * In addition to that, we don't want to synthesize a motion if + * there is no pending rectangle because that doesn't make any + * sense. + */ + if (private->rect_adjusting) + return; + + old_function = private->function; + + gimp_tool_rectangle_set_function (rectangle, function); + + gimp_tool_rectangle_update_with_coord (rectangle, new_x, new_y); + + /* We must update this. */ + gimp_tool_rectangle_recalculate_center_xy (rectangle); + + gimp_tool_rectangle_update_options (rectangle); + + gimp_tool_rectangle_set_function (rectangle, old_function); + + gimp_tool_rectangle_change_complete (rectangle); +} + +static void +swap_doubles (gdouble *i, + gdouble *j) +{ + gdouble tmp; + + tmp = *i; + *i = *j; + *j = tmp; +} + +static GimpRectangleFunction +gimp_tool_rectangle_calc_function (GimpToolRectangle *rectangle, + const GimpCoords *coords, + gboolean proximity) +{ + if (! proximity) + { + return GIMP_TOOL_RECTANGLE_DEAD; + } + else if (gimp_tool_rectangle_coord_outside (rectangle, coords)) + { + /* The cursor is outside of the rectangle, clicking should + * create a new rectangle. + */ + return GIMP_TOOL_RECTANGLE_CREATING; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_NORTH_WEST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_SOUTH_EAST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_NORTH_EAST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_SOUTH_WEST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_WEST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_LEFT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_EAST)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_RIGHT; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_NORTH)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_TOP; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_SOUTH)) + { + return GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM; + } + else if (gimp_tool_rectangle_coord_on_handle (rectangle, + coords, + GIMP_HANDLE_ANCHOR_CENTER)) + { + return GIMP_TOOL_RECTANGLE_MOVING; + } + else + { + return GIMP_TOOL_RECTANGLE_DEAD; + } +} + +/* gimp_tool_rectangle_check_function() is needed to deal with + * situations where the user drags a corner or edge across one of the + * existing edges, thereby changing its function. Ugh. + */ +static void +gimp_tool_rectangle_check_function (GimpToolRectangle *rectangle) + +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleFunction function = private->function; + + if (private->x2 < private->x1) + { + swap_doubles (&private->x1, &private->x2); + + switch (function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + function = GIMP_TOOL_RECTANGLE_RESIZING_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + function = GIMP_TOOL_RECTANGLE_RESIZING_LEFT; + break; + /* avoid annoying warnings about unhandled enums */ + default: + break; + } + } + + if (private->y2 < private->y1) + { + swap_doubles (&private->y1, &private->y2); + + switch (function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + function = GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM; + break; + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + function = GIMP_TOOL_RECTANGLE_RESIZING_TOP; + break; + default: + break; + } + } + + gimp_tool_rectangle_set_function (rectangle, function); +} + +/** + * gimp_tool_rectangle_coord_outside: + * + * Returns: %TRUE if the coord is outside the rectangle bounds + * including any outside handles. + */ +static gboolean +gimp_tool_rectangle_coord_outside (GimpToolRectangle *rectangle, + const GimpCoords *coord) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpDisplayShell *shell; + gboolean narrow_mode = private->narrow_mode; + gdouble x1, y1, x2, y2; + gdouble x1_b, y1_b, x2_b, y2_b; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + + x1_b = x1 - (narrow_mode ? private->corner_handle_w / shell->scale_x : 0); + x2_b = x2 + (narrow_mode ? private->corner_handle_w / shell->scale_x : 0); + y1_b = y1 - (narrow_mode ? private->corner_handle_h / shell->scale_y : 0); + y2_b = y2 + (narrow_mode ? private->corner_handle_h / shell->scale_y : 0); + + return (coord->x < x1_b || + coord->x > x2_b || + coord->y < y1_b || + coord->y > y2_b); +} + +/** + * gimp_tool_rectangle_coord_on_handle: + * + * Returns: %TRUE if the coord is on the handle that corresponds to + * @anchor. + */ +static gboolean +gimp_tool_rectangle_coord_on_handle (GimpToolRectangle *rectangle, + const GimpCoords *coords, + GimpHandleAnchor anchor) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpDisplayShell *shell; + gdouble x1, y1, x2, y2; + gdouble rect_w, rect_h; + gdouble handle_x = 0; + gdouble handle_y = 0; + gdouble handle_width = 0; + gdouble handle_height = 0; + gint narrow_mode_x_dir = 0; + gint narrow_mode_y_dir = 0; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + + rect_w = x2 - x1; + rect_h = y2 - y1; + + switch (anchor) + { + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + handle_x = x1; + handle_y = y1; + handle_width = private->corner_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = -1; + narrow_mode_y_dir = -1; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + handle_x = x2; + handle_y = y2; + handle_width = private->corner_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = 1; + narrow_mode_y_dir = 1; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + handle_x = x2; + handle_y = y1; + handle_width = private->corner_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = 1; + narrow_mode_y_dir = -1; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + handle_x = x1; + handle_y = y2; + handle_width = private->corner_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = -1; + narrow_mode_y_dir = 1; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + handle_x = x1; + handle_y = y1 + rect_h / 2; + handle_width = private->corner_handle_w; + handle_height = private->left_and_right_handle_h; + + narrow_mode_x_dir = -1; + narrow_mode_y_dir = 0; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + handle_x = x2; + handle_y = y1 + rect_h / 2; + handle_width = private->corner_handle_w; + handle_height = private->left_and_right_handle_h; + + narrow_mode_x_dir = 1; + narrow_mode_y_dir = 0; + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + handle_x = x1 + rect_w / 2; + handle_y = y1; + handle_width = private->top_and_bottom_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = 0; + narrow_mode_y_dir = -1; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + handle_x = x1 + rect_w / 2; + handle_y = y2; + handle_width = private->top_and_bottom_handle_w; + handle_height = private->corner_handle_h; + + narrow_mode_x_dir = 0; + narrow_mode_y_dir = 1; + break; + + case GIMP_HANDLE_ANCHOR_CENTER: + handle_x = x1 + rect_w / 2; + handle_y = y1 + rect_h / 2; + + if (private->narrow_mode) + { + handle_width = rect_w * shell->scale_x; + handle_height = rect_h * shell->scale_y; + } + else + { + handle_width = rect_w * shell->scale_x - private->corner_handle_w * 2; + handle_height = rect_h * shell->scale_y - private->corner_handle_h * 2; + } + + narrow_mode_x_dir = 0; + narrow_mode_y_dir = 0; + break; + } + + if (private->narrow_mode) + { + handle_x += narrow_mode_x_dir * handle_width / shell->scale_x; + handle_y += narrow_mode_y_dir * handle_height / shell->scale_y; + } + + return gimp_canvas_item_on_handle (private->rectangle, + coords->x, coords->y, + GIMP_HANDLE_SQUARE, + handle_x, handle_y, + handle_width, handle_height, + anchor); +} + +static GimpHandleAnchor +gimp_tool_rectangle_get_anchor (GimpRectangleFunction function) +{ + switch (function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + return GIMP_HANDLE_ANCHOR_NORTH_WEST; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + return GIMP_HANDLE_ANCHOR_NORTH_EAST; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + return GIMP_HANDLE_ANCHOR_SOUTH_WEST; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + return GIMP_HANDLE_ANCHOR_SOUTH_EAST; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + return GIMP_HANDLE_ANCHOR_WEST; + + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + return GIMP_HANDLE_ANCHOR_EAST; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + return GIMP_HANDLE_ANCHOR_NORTH; + + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + return GIMP_HANDLE_ANCHOR_SOUTH; + + default: + return GIMP_HANDLE_ANCHOR_CENTER; + } +} + +static gboolean +gimp_tool_rectangle_rect_rubber_banding_func (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_CREATING: + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_AUTO_SHRINK: + return TRUE; + + case GIMP_TOOL_RECTANGLE_MOVING: + case GIMP_TOOL_RECTANGLE_DEAD: + default: + break; + } + + return FALSE; +} + +/** + * gimp_tool_rectangle_rect_adjusting_func: + * @rectangle: + * + * Returns: %TRUE if the current function is a rectangle adjusting + * function. + */ +static gboolean +gimp_tool_rectangle_rect_adjusting_func (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + return (gimp_tool_rectangle_rect_rubber_banding_func (rectangle) || + private->function == GIMP_TOOL_RECTANGLE_MOVING); +} + +/** + * gimp_tool_rectangle_get_other_side: + * @rectangle: A #GimpToolRectangle. + * @other_x: Pointer to double of the other-x double. + * @other_y: Pointer to double of the other-y double. + * + * Calculates pointers to member variables that hold the coordinates + * of the opposite side (either the opposite corner or literally the + * opposite side), based on the current function. The opposite of a + * corner needs two coordinates, the opposite of a side only needs + * one. + */ +static void +gimp_tool_rectangle_get_other_side (GimpToolRectangle *rectangle, + gdouble **other_x, + gdouble **other_y) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + *other_x = &private->x1; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + *other_x = &private->x2; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + default: + *other_x = NULL; + break; + } + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + *other_y = &private->y1; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + *other_y = &private->y2; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + default: + *other_y = NULL; + break; + } +} + +static void +gimp_tool_rectangle_get_other_side_coord (GimpToolRectangle *rectangle, + gdouble *other_side_x, + gdouble *other_side_y) +{ + gdouble *other_x = NULL; + gdouble *other_y = NULL; + + gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y); + + if (other_x) + *other_side_x = *other_x; + if (other_y) + *other_side_y = *other_y; +} + +static void +gimp_tool_rectangle_set_other_side_coord (GimpToolRectangle *rectangle, + gdouble other_side_x, + gdouble other_side_y) +{ + gdouble *other_x = NULL; + gdouble *other_y = NULL; + + gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y); + + if (other_x) + *other_x = other_side_x; + if (other_y) + *other_y = other_side_y; + + gimp_tool_rectangle_check_function (rectangle); + + gimp_tool_rectangle_update_int_rect (rectangle); +} + +/** + * gimp_tool_rectangle_apply_coord: + * @param: A #GimpToolRectangle. + * @coord_x: X of coord. + * @coord_y: Y of coord. + * + * Adjust the rectangle to the new position specified by passed + * coordinate, taking fixed_center into account, which means it + * expands the rectangle around the center point. + */ +static void +gimp_tool_rectangle_apply_coord (GimpToolRectangle *rectangle, + gdouble coord_x, + gdouble coord_y) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + if (private->function == GIMP_TOOL_RECTANGLE_MOVING) + { + /* Preserve width and height while moving the grab-point to where the + * cursor is. + */ + gdouble w = private->x2 - private->x1; + gdouble h = private->y2 - private->y1; + + private->x1 = coord_x; + private->y1 = coord_y; + + private->x2 = private->x1 + w; + private->y2 = private->y1 + h; + + /* We are done already. */ + return; + } + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + private->x1 = coord_x; + + if (private->fixed_center) + private->x2 = 2 * private->center_x_on_fixed_center - private->x1; + + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + private->x2 = coord_x; + + if (private->fixed_center) + private->x1 = 2 * private->center_x_on_fixed_center - private->x2; + + break; + + default: + break; + } + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + private->y1 = coord_y; + + if (private->fixed_center) + private->y2 = 2 * private->center_y_on_fixed_center - private->y1; + + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + private->y2 = coord_y; + + if (private->fixed_center) + private->y1 = 2 * private->center_y_on_fixed_center - private->y2; + + break; + + default: + break; + } +} + +static void +gimp_tool_rectangle_setup_snap_offsets (GimpToolRectangle *rectangle, + const GimpCoords *coords) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (rectangle); + GimpToolRectanglePrivate *private = rectangle->private; + gdouble x1, y1, x2, y2; + gdouble coord_x, coord_y; + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + gimp_tool_rectangle_adjust_coord (rectangle, + coords->x, coords->y, + &coord_x, &coord_y); + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_CREATING: + gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + gimp_tool_widget_set_snap_offsets (widget, + x1 - coord_x, + y1 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + gimp_tool_widget_set_snap_offsets (widget, + x2 - coord_x, + y1 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + gimp_tool_widget_set_snap_offsets (widget, + x1 - coord_x, + y2 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + gimp_tool_widget_set_snap_offsets (widget, + x2 - coord_x, + y2 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + gimp_tool_widget_set_snap_offsets (widget, + x1 - coord_x, 0, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + gimp_tool_widget_set_snap_offsets (widget, + x2 - coord_x, 0, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + gimp_tool_widget_set_snap_offsets (widget, + 0, y1 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + gimp_tool_widget_set_snap_offsets (widget, + 0, y2 - coord_y, + 0, 0); + break; + + case GIMP_TOOL_RECTANGLE_MOVING: + gimp_tool_widget_set_snap_offsets (widget, + x1 - coord_x, + y1 - coord_y, + x2 - x1, + y2 - y1); + break; + + default: + break; + } +} + +/** + * gimp_tool_rectangle_clamp: + * @rectangle: A #GimpToolRectangle. + * @clamped_sides: Where to put contrainment information. + * @constraint: Constraint to use. + * @symmetrically: Whether or not to clamp symmetrically. + * + * Clamps rectangle inside specified bounds, providing information of + * where clamping was done. Can also clamp symmetrically. + */ +static void +gimp_tool_rectangle_clamp (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically) +{ + gimp_tool_rectangle_clamp_width (rectangle, + clamped_sides, + constraint, + symmetrically); + + gimp_tool_rectangle_clamp_height (rectangle, + clamped_sides, + constraint, + symmetrically); +} + +/** + * gimp_tool_rectangle_clamp_width: + * @rectangle: A #GimpToolRectangle. + * @clamped_sides: Where to put contrainment information. + * @constraint: Constraint to use. + * @symmetrically: Whether or not to clamp symmetrically. + * + * Clamps height of rectangle. Set symmetrically to true when using + * for fixed_center:ed rectangles, since that will clamp symmetrically + * which is just what is needed. + * + * When this function constrains, it puts what it constrains in + * @constraint. This information is essential when an aspect ratio is + * to be applied. + */ +static void +gimp_tool_rectangle_clamp_width (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gint min_x; + gint max_x; + + if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE) + return; + + gimp_tool_rectangle_get_constraints (rectangle, + &min_x, NULL, + &max_x, NULL, + constraint); + if (private->x1 < min_x) + { + gdouble dx = min_x - private->x1; + + private->x1 += dx; + + if (symmetrically) + private->x2 -= dx; + + if (private->x2 < min_x) + private->x2 = min_x; + + if (clamped_sides) + *clamped_sides |= CLAMPED_LEFT; + } + + if (private->x2 > max_x) + { + gdouble dx = max_x - private->x2; + + private->x2 += dx; + + if (symmetrically) + private->x1 -= dx; + + if (private->x1 > max_x) + private->x1 = max_x; + + if (clamped_sides) + *clamped_sides |= CLAMPED_RIGHT; + } +} + +/** + * gimp_tool_rectangle_clamp_height: + * @rectangle: A #GimpToolRectangle. + * @clamped_sides: Where to put contrainment information. + * @constraint: Constraint to use. + * @symmetrically: Whether or not to clamp symmetrically. + * + * Clamps height of rectangle. Set symmetrically to true when using for + * fixed_center:ed rectangles, since that will clamp symmetrically which is just + * what is needed. + * + * When this function constrains, it puts what it constrains in + * @constraint. This information is essential when an aspect ratio is to be + * applied. + */ +static void +gimp_tool_rectangle_clamp_height (GimpToolRectangle *rectangle, + ClampedSide *clamped_sides, + GimpRectangleConstraint constraint, + gboolean symmetrically) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gint min_y; + gint max_y; + + if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE) + return; + + gimp_tool_rectangle_get_constraints (rectangle, + NULL, &min_y, + NULL, &max_y, + constraint); + if (private->y1 < min_y) + { + gdouble dy = min_y - private->y1; + + private->y1 += dy; + + if (symmetrically) + private->y2 -= dy; + + if (private->y2 < min_y) + private->y2 = min_y; + + if (clamped_sides) + *clamped_sides |= CLAMPED_TOP; + } + + if (private->y2 > max_y) + { + gdouble dy = max_y - private->y2; + + private->y2 += dy; + + if (symmetrically) + private->y1 -= dy; + + if (private->y1 > max_y) + private->y1 = max_y; + + if (clamped_sides) + *clamped_sides |= CLAMPED_BOTTOM; + } +} + +/** + * gimp_tool_rectangle_keep_inside: + * @rectangle: A #GimpToolRectangle. + * + * If the rectangle is outside of the canvas, move it into it. If the rectangle is + * larger than the canvas in any direction, make it fill the canvas in that direction. + */ +static void +gimp_tool_rectangle_keep_inside (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint) +{ + gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint); + gimp_tool_rectangle_keep_inside_vertically (rectangle, constraint); +} + +/** + * gimp_tool_rectangle_keep_inside_horizontally: + * @rectangle: A #GimpToolRectangle. + * @constraint: Constraint to use. + * + * If the rectangle is outside of the given constraint horizontally, move it + * inside. If it is too big to fit inside, make it just as big as the width + * limit. + */ +static void +gimp_tool_rectangle_keep_inside_horizontally (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gint min_x; + gint max_x; + + if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE) + return; + + gimp_tool_rectangle_get_constraints (rectangle, + &min_x, NULL, + &max_x, NULL, + constraint); + + if (max_x - min_x < private->x2 - private->x1) + { + private->x1 = min_x; + private->x2 = max_x; + } + else + { + if (private->x1 < min_x) + { + gdouble dx = min_x - private->x1; + + private->x1 += dx; + private->x2 += dx; + } + if (private->x2 > max_x) + { + gdouble dx = max_x - private->x2; + + private->x1 += dx; + private->x2 += dx; + } + } +} + +/** + * gimp_tool_rectangle_keep_inside_vertically: + * @rectangle: A #GimpToolRectangle. + * @constraint: Constraint to use. + * + * If the rectangle is outside of the given constraint vertically, + * move it inside. If it is too big to fit inside, make it just as big + * as the width limit. + */ +static void +gimp_tool_rectangle_keep_inside_vertically (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gint min_y; + gint max_y; + + if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE) + return; + + gimp_tool_rectangle_get_constraints (rectangle, + NULL, &min_y, + NULL, &max_y, + constraint); + + if (max_y - min_y < private->y2 - private->y1) + { + private->y1 = min_y; + private->y2 = max_y; + } + else + { + if (private->y1 < min_y) + { + gdouble dy = min_y - private->y1; + + private->y1 += dy; + private->y2 += dy; + } + if (private->y2 > max_y) + { + gdouble dy = max_y - private->y2; + + private->y1 += dy; + private->y2 += dy; + } + } +} + +/** + * gimp_tool_rectangle_apply_fixed_width: + * @rectangle: A #GimpToolRectangle. + * @constraint: Constraint to use. + * @width: + * + * Makes the rectangle have a fixed_width, following the constrainment + * rules of fixed widths as well. Please refer to the rectangle tools + * spec. + */ +static void +gimp_tool_rectangle_apply_fixed_width (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint, + gdouble width) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + /* We always want to center around fixed_center here, since we want the + * anchor point to be directly on the opposite side. + */ + private->x1 = private->center_x_on_fixed_center - + width / 2; + private->x2 = private->x1 + width; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + /* We always want to center around fixed_center here, since we want the + * anchor point to be directly on the opposite side. + */ + private->x1 = private->center_x_on_fixed_center - + width / 2; + private->x2 = private->x1 + width; + break; + + default: + break; + } + + /* Width shall be kept even after constraints, so we move the + * rectangle sideways rather than adjusting a side. + */ + gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint); +} + +/** + * gimp_tool_rectangle_apply_fixed_height: + * @rectangle: A #GimpToolRectangle. + * @constraint: Constraint to use. + * @height: + * + * Makes the rectangle have a fixed_height, following the + * constrainment rules of fixed heights as well. Please refer to the + * rectangle tools spec. + */ +static void +gimp_tool_rectangle_apply_fixed_height (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint, + gdouble height) + +{ + GimpToolRectanglePrivate *private = rectangle->private; + + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + /* We always want to center around fixed_center here, since we + * want the anchor point to be directly on the opposite side. + */ + private->y1 = private->center_y_on_fixed_center - + height / 2; + private->y2 = private->y1 + height; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + /* We always want to center around fixed_center here, since we + * want the anchor point to be directly on the opposite side. + */ + private->y1 = private->center_y_on_fixed_center - + height / 2; + private->y2 = private->y1 + height; + break; + + default: + break; + } + + /* Width shall be kept even after constraints, so we move the + * rectangle sideways rather than adjusting a side. + */ + gimp_tool_rectangle_keep_inside_vertically (rectangle, constraint); +} + +/** + * gimp_tool_rectangle_apply_aspect: + * @rectangle: A #GimpToolRectangle. + * @aspect: The desired aspect. + * @clamped_sides: Bitfield of sides that have been clamped. + * + * Adjust the rectangle to the desired aspect. + * + * Sometimes, a side must not be moved outwards, for example if a the + * RIGHT side has been clamped previously, we must not move the RIGHT + * side to the right, since that would violate the constraint + * again. The clamped_sides bitfield keeps track of sides that have + * previously been clamped. + * + * If fixed_center is used, the function adjusts the aspect by + * symmetrically adjusting the left and right, or top and bottom side. + */ +static void +gimp_tool_rectangle_apply_aspect (GimpToolRectangle *rectangle, + gdouble aspect, + gint clamped_sides) +{ + GimpToolRectanglePrivate *private = rectangle->private; + gdouble current_w; + gdouble current_h; + gdouble current_aspect; + SideToResize side_to_resize = SIDE_TO_RESIZE_NONE; + + current_w = private->x2 - private->x1; + current_h = private->y2 - private->y1; + + current_aspect = (gdouble) current_w / (gdouble) current_h; + + /* Do we have to do anything? */ + if (current_aspect == aspect) + return; + + if (private->fixed_center) + { + /* We may only adjust the sides symmetrically to get desired aspect. */ + if (current_aspect > aspect) + { + /* We prefer to use top and bottom (since that will make the + * cursor remain on the rectangle edge), unless that is what + * the user has grabbed. + */ + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + if (! (clamped_sides & CLAMPED_TOP) && + ! (clamped_sides & CLAMPED_BOTTOM)) + { + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + } + else + { + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + } + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + default: + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + break; + } + } + else /* (current_aspect < aspect) */ + { + /* We prefer to use left and right (since that will make the + * cursor remain on the rectangle edge), unless that is what + * the user has grabbed. + */ + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + if (! (clamped_sides & CLAMPED_LEFT) && + ! (clamped_sides & CLAMPED_RIGHT)) + { + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + } + else + { + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + } + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + default: + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + break; + } + } + } + else if (current_aspect > aspect) + { + /* We can safely pick LEFT or RIGHT, since using those sides + * will make the rectangle smaller, so we don't need to check + * for clamped_sides. We may only use TOP and BOTTOM if not + * those sides have been clamped, since using them will make the + * rectangle bigger. + */ + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + if (! (clamped_sides & CLAMPED_TOP)) + side_to_resize = SIDE_TO_RESIZE_TOP; + else + side_to_resize = SIDE_TO_RESIZE_LEFT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + if (! (clamped_sides & CLAMPED_TOP)) + side_to_resize = SIDE_TO_RESIZE_TOP; + else + side_to_resize = SIDE_TO_RESIZE_RIGHT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + if (! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + else + side_to_resize = SIDE_TO_RESIZE_LEFT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + if (! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + else + side_to_resize = SIDE_TO_RESIZE_RIGHT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + if (! (clamped_sides & CLAMPED_TOP) && + ! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + else + side_to_resize = SIDE_TO_RESIZE_LEFT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + if (! (clamped_sides & CLAMPED_TOP) && + ! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + else + side_to_resize = SIDE_TO_RESIZE_RIGHT; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + break; + + case GIMP_TOOL_RECTANGLE_MOVING: + default: + if (! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + else if (! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_RIGHT; + else if (! (clamped_sides & CLAMPED_TOP)) + side_to_resize = SIDE_TO_RESIZE_TOP; + else if (! (clamped_sides & CLAMPED_LEFT)) + side_to_resize = SIDE_TO_RESIZE_LEFT; + break; + } + } + else /* (current_aspect < aspect) */ + { + /* We can safely pick TOP or BOTTOM, since using those sides + * will make the rectangle smaller, so we don't need to check + * for clamped_sides. We may only use LEFT and RIGHT if not + * those sides have been clamped, since using them will make the + * rectangle bigger. + */ + switch (private->function) + { + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT: + if (! (clamped_sides & CLAMPED_LEFT)) + side_to_resize = SIDE_TO_RESIZE_LEFT; + else + side_to_resize = SIDE_TO_RESIZE_TOP; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT: + if (! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_RIGHT; + else + side_to_resize = SIDE_TO_RESIZE_TOP; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT: + if (! (clamped_sides & CLAMPED_LEFT)) + side_to_resize = SIDE_TO_RESIZE_LEFT; + else + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT: + if (! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_RIGHT; + else + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_TOP: + if (! (clamped_sides & CLAMPED_LEFT) && + ! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + else + side_to_resize = SIDE_TO_RESIZE_TOP; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM: + if (! (clamped_sides & CLAMPED_LEFT) && + ! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY; + else + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + break; + + case GIMP_TOOL_RECTANGLE_RESIZING_LEFT: + case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT: + side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY; + break; + + case GIMP_TOOL_RECTANGLE_MOVING: + default: + if (! (clamped_sides & CLAMPED_BOTTOM)) + side_to_resize = SIDE_TO_RESIZE_BOTTOM; + else if (! (clamped_sides & CLAMPED_RIGHT)) + side_to_resize = SIDE_TO_RESIZE_RIGHT; + else if (! (clamped_sides & CLAMPED_TOP)) + side_to_resize = SIDE_TO_RESIZE_TOP; + else if (! (clamped_sides & CLAMPED_LEFT)) + side_to_resize = SIDE_TO_RESIZE_LEFT; + break; + } + } + + /* We now know what side(s) we should resize, so now we just solve + * the aspect equation for that side(s). + */ + switch (side_to_resize) + { + case SIDE_TO_RESIZE_NONE: + return; + + case SIDE_TO_RESIZE_LEFT: + private->x1 = private->x2 - aspect * current_h; + break; + + case SIDE_TO_RESIZE_RIGHT: + private->x2 = private->x1 + aspect * current_h; + break; + + case SIDE_TO_RESIZE_TOP: + private->y1 = private->y2 - current_w / aspect; + break; + + case SIDE_TO_RESIZE_BOTTOM: + private->y2 = private->y1 + current_w / aspect; + break; + + case SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY: + { + gdouble correct_h = current_w / aspect; + + private->y1 = private->center_y_on_fixed_center - correct_h / 2; + private->y2 = private->y1 + correct_h; + } + break; + + case SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY: + { + gdouble correct_w = current_h * aspect; + + private->x1 = private->center_x_on_fixed_center - correct_w / 2; + private->x2 = private->x1 + correct_w; + } + break; + } +} + +/** + * gimp_tool_rectangle_update_with_coord: + * @rectangle: A #GimpToolRectangle. + * @new_x: New X-coordinate in the context of the current function. + * @new_y: New Y-coordinate in the context of the current function. + * + * The core rectangle adjustment function. It updates the rectangle + * for the passed cursor coordinate, taking current function and tool + * options into account. It also updates the current + * private->function if necessary. + */ +static void +gimp_tool_rectangle_update_with_coord (GimpToolRectangle *rectangle, + gdouble new_x, + gdouble new_y) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + /* Move the corner or edge the user currently has grabbed. */ + gimp_tool_rectangle_apply_coord (rectangle, new_x, new_y); + + /* Update private->function. The function changes if the user + * "flips" the rectangle. + */ + gimp_tool_rectangle_check_function (rectangle); + + /* Clamp the rectangle if necessary */ + gimp_tool_rectangle_handle_general_clamping (rectangle); + + /* If the rectangle is being moved, do not run through any further + * rectangle adjusting functions since it's shape should not change + * then. + */ + if (private->function != GIMP_TOOL_RECTANGLE_MOVING) + { + gimp_tool_rectangle_apply_fixed_rule (rectangle); + } + + gimp_tool_rectangle_update_int_rect (rectangle); +} + +static void +gimp_tool_rectangle_apply_fixed_rule (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleConstraint constraint_to_use; + GimpDisplayShell *shell; + GimpImage *image; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + image = gimp_display_get_image (shell->display); + + /* Calculate what constraint to use when needed. */ + constraint_to_use = gimp_tool_rectangle_get_constraint (rectangle); + + if (private->fixed_rule_active && + private->fixed_rule == GIMP_RECTANGLE_FIXED_ASPECT) + { + gdouble aspect; + + aspect = CLAMP (private->aspect_numerator / + private->aspect_denominator, + 1.0 / gimp_image_get_height (image), + gimp_image_get_width (image)); + + if (constraint_to_use == GIMP_RECTANGLE_CONSTRAIN_NONE) + { + gimp_tool_rectangle_apply_aspect (rectangle, + aspect, + CLAMPED_NONE); + } + else + { + if (private->function != GIMP_TOOL_RECTANGLE_MOVING) + { + ClampedSide clamped_sides = CLAMPED_NONE; + + gimp_tool_rectangle_apply_aspect (rectangle, + aspect, + clamped_sides); + + /* After we have applied aspect, we might have taken the + * rectangle outside of constraint, so clamp and apply + * aspect again. We will get the right result this time, + * since 'clamped_sides' will be setup correctly now. + */ + gimp_tool_rectangle_clamp (rectangle, + &clamped_sides, + constraint_to_use, + private->fixed_center); + + gimp_tool_rectangle_apply_aspect (rectangle, + aspect, + clamped_sides); + } + else + { + gimp_tool_rectangle_apply_aspect (rectangle, + aspect, + CLAMPED_NONE); + + gimp_tool_rectangle_keep_inside (rectangle, + constraint_to_use); + } + } + } + else if (private->fixed_rule_active && + private->fixed_rule == GIMP_RECTANGLE_FIXED_SIZE) + { + gimp_tool_rectangle_apply_fixed_width (rectangle, + constraint_to_use, + private->desired_fixed_size_width); + gimp_tool_rectangle_apply_fixed_height (rectangle, + constraint_to_use, + private->desired_fixed_size_height); + } + else if (private->fixed_rule_active && + private->fixed_rule == GIMP_RECTANGLE_FIXED_WIDTH) + { + gimp_tool_rectangle_apply_fixed_width (rectangle, + constraint_to_use, + private->desired_fixed_width); + } + else if (private->fixed_rule_active && + private->fixed_rule == GIMP_RECTANGLE_FIXED_HEIGHT) + { + gimp_tool_rectangle_apply_fixed_height (rectangle, + constraint_to_use, + private->desired_fixed_height); + } +} + +/** + * gimp_tool_rectangle_get_constraints: + * @rectangle: A #GimpToolRectangle. + * @min_x: + * @min_y: + * @max_x: + * @max_y: Pointers of where to put constraints. NULL allowed. + * @constraint: Whether to return image or layer constraints. + * + * Calculates constraint coordinates for image or layer. + */ +static void +gimp_tool_rectangle_get_constraints (GimpToolRectangle *rectangle, + gint *min_x, + gint *min_y, + gint *max_x, + gint *max_y, + GimpRectangleConstraint constraint) +{ + GimpDisplayShell *shell; + GimpImage *image; + gint min_x_dummy; + gint min_y_dummy; + gint max_x_dummy; + gint max_y_dummy; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + image = gimp_display_get_image (shell->display); + + if (! min_x) min_x = &min_x_dummy; + if (! min_y) min_y = &min_y_dummy; + if (! max_x) max_x = &max_x_dummy; + if (! max_y) max_y = &max_y_dummy; + + *min_x = 0; + *min_y = 0; + *max_x = 0; + *max_y = 0; + + switch (constraint) + { + case GIMP_RECTANGLE_CONSTRAIN_IMAGE: + if (image) + { + *min_x = 0; + *min_y = 0; + *max_x = gimp_image_get_width (image); + *max_y = gimp_image_get_height (image); + } + break; + + case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE: + if (image) + { + GimpItem *item = GIMP_ITEM (gimp_image_get_active_drawable (image)); + + if (item) + { + gimp_item_get_offset (item, min_x, min_y); + *max_x = *min_x + gimp_item_get_width (item); + *max_y = *min_y + gimp_item_get_height (item); + } + } + break; + + default: + g_warning ("Invalid rectangle constraint.\n"); + return; + } +} + +/** + * gimp_tool_rectangle_handle_general_clamping: + * @rectangle: A #GimpToolRectangle. + * + * Make sure that constraints are applied to the rectangle, either by + * manually doing it, or by looking at the rectangle tool options and + * concluding it will be done later. + */ +static void +gimp_tool_rectangle_handle_general_clamping (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + GimpRectangleConstraint constraint; + + constraint = gimp_tool_rectangle_get_constraint (rectangle); + + /* fixed_aspect takes care of clamping by it self, so just return in + * case that is in use. Also return if no constraints should be + * enforced. + */ + if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE) + return; + + if (private->function != GIMP_TOOL_RECTANGLE_MOVING) + { + gimp_tool_rectangle_clamp (rectangle, + NULL, + constraint, + private->fixed_center); + } + else + { + gimp_tool_rectangle_keep_inside (rectangle, constraint); + } +} + +/** + * gimp_tool_rectangle_update_int_rect: + * @rectangle: + * + * Update integer representation of rectangle. + **/ +static void +gimp_tool_rectangle_update_int_rect (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + private->x1_int = SIGNED_ROUND (private->x1); + private->y1_int = SIGNED_ROUND (private->y1); + + if (gimp_tool_rectangle_rect_rubber_banding_func (rectangle)) + { + private->width_int = (gint) SIGNED_ROUND (private->x2) - private->x1_int; + private->height_int = (gint) SIGNED_ROUND (private->y2) - private->y1_int; + } +} + +/** + * gimp_tool_rectangle_adjust_coord: + * @rectangle: + * @ccoord_x_input: + * @ccoord_x_input: + * @ccoord_x_output: + * @ccoord_x_output: + * + * Transforms a coordinate to better fit the public behaviour of the + * rectangle. + */ +static void +gimp_tool_rectangle_adjust_coord (GimpToolRectangle *rectangle, + gdouble coord_x_input, + gdouble coord_y_input, + gdouble *coord_x_output, + gdouble *coord_y_output) +{ + GimpToolRectanglePrivate *priv = rectangle->private; + + switch (priv->precision) + { + case GIMP_RECTANGLE_PRECISION_INT: + *coord_x_output = RINT (coord_x_input); + *coord_y_output = RINT (coord_y_input); + break; + + case GIMP_RECTANGLE_PRECISION_DOUBLE: + default: + *coord_x_output = coord_x_input; + *coord_y_output = coord_y_input; + break; + } +} + +static void +gimp_tool_rectangle_recalculate_center_xy (GimpToolRectangle *rectangle) +{ + GimpToolRectanglePrivate *private = rectangle->private; + + private->center_x_on_fixed_center = (private->x1 + private->x2) / 2; + private->center_y_on_fixed_center = (private->y1 + private->y2) / 2; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_rectangle_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_RECTANGLE, + "shell", shell, + NULL); +} + +GimpRectangleFunction +gimp_tool_rectangle_get_function (GimpToolRectangle *rectangle) +{ + g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), + GIMP_TOOL_RECTANGLE_DEAD); + + return rectangle->private->function; +} + +void +gimp_tool_rectangle_set_function (GimpToolRectangle *rectangle, + GimpRectangleFunction function) +{ + GimpToolRectanglePrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + + private = rectangle->private; + + if (private->function != function) + { + private->function = function; + + gimp_tool_rectangle_changed (GIMP_TOOL_WIDGET (rectangle)); + } +} + +void +gimp_tool_rectangle_set_constraint (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint) +{ + GimpToolRectanglePrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + + private = rectangle->private; + + if (constraint != private->constraint) + { + g_object_freeze_notify (G_OBJECT (rectangle)); + + private->constraint = constraint; + g_object_notify (G_OBJECT (rectangle), "constraint"); + + gimp_tool_rectangle_clamp (rectangle, NULL, constraint, FALSE); + + g_object_thaw_notify (G_OBJECT (rectangle)); + + gimp_tool_rectangle_change_complete (rectangle); + } +} + +GimpRectangleConstraint +gimp_tool_rectangle_get_constraint (GimpToolRectangle *rectangle) +{ + g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), 0); + + return rectangle->private->constraint; +} + +/** + * gimp_tool_rectangle_get_public_rect: + * @rectangle: + * @x1: + * @y1: + * @x2: + * @y2: + * + * This function returns the rectangle as it appears to be publicly + * (based on integer or double precision-mode). + **/ +void +gimp_tool_rectangle_get_public_rect (GimpToolRectangle *rectangle, + gdouble *x1, + gdouble *y1, + gdouble *x2, + gdouble *y2) +{ + GimpToolRectanglePrivate *priv; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + g_return_if_fail (x1 != NULL); + g_return_if_fail (y1 != NULL); + g_return_if_fail (x2 != NULL); + g_return_if_fail (y2 != NULL); + + priv = rectangle->private; + + switch (priv->precision) + { + case GIMP_RECTANGLE_PRECISION_INT: + *x1 = priv->x1_int; + *y1 = priv->y1_int; + *x2 = priv->x1_int + priv->width_int; + *y2 = priv->y1_int + priv->height_int; + break; + + case GIMP_RECTANGLE_PRECISION_DOUBLE: + default: + *x1 = priv->x1; + *y1 = priv->y1; + *x2 = priv->x2; + *y2 = priv->y2; + break; + } +} + +/** + * gimp_tool_rectangle_pending_size_set: + * @width_property: Option property to set to pending rectangle width. + * @height_property: Option property to set to pending rectangle height. + * + * Sets specified rectangle tool options properties to the width and + * height of the current pending rectangle. + */ +void +gimp_tool_rectangle_pending_size_set (GimpToolRectangle *rectangle, + GObject *object, + const gchar *width_property, + const gchar *height_property) +{ + GimpToolRectanglePrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + g_return_if_fail (width_property != NULL); + g_return_if_fail (height_property != NULL); + + private = rectangle->private; + + g_object_set (object, + width_property, MAX (private->x2 - private->x1, 1.0), + height_property, MAX (private->y2 - private->y1, 1.0), + NULL); +} + +/** + * gimp_tool_rectangle_constraint_size_set: + * @width_property: Option property to set to current constraint width. + * @height_property: Option property to set to current constraint height. + * + * Sets specified rectangle tool options properties to the width and + * height of the current constraint size. + */ +void +gimp_tool_rectangle_constraint_size_set (GimpToolRectangle *rectangle, + GObject *object, + const gchar *width_property, + const gchar *height_property) +{ + GimpDisplayShell *shell; + GimpContext *context; + GimpImage *image; + gdouble width; + gdouble height; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + context = gimp_get_user_context (shell->display->gimp); + image = gimp_context_get_image (context); + + if (! image) + { + width = 1.0; + height = 1.0; + } + else + { + GimpRectangleConstraint constraint; + + constraint = gimp_tool_rectangle_get_constraint (rectangle); + + switch (constraint) + { + case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE: + { + GimpItem *item = GIMP_ITEM (gimp_image_get_active_layer (image)); + + if (! item) + { + width = 1.0; + height = 1.0; + } + else + { + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + } + } + break; + + case GIMP_RECTANGLE_CONSTRAIN_IMAGE: + default: + { + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + } + break; + } + } + + g_object_set (object, + width_property, width, + height_property, height, + NULL); +} + +/** + * gimp_tool_rectangle_rectangle_is_first: + * @rectangle: + * + * Returns: %TRUE if the user is creating the first rectangle with + * this instance from scratch, %FALSE if modifying an existing + * rectangle, or creating a new rectangle, discarding the existing + * one. This function is only meaningful in _motion and + * _button_release. + */ +gboolean +gimp_tool_rectangle_rectangle_is_first (GimpToolRectangle *rectangle) +{ + g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE); + + return rectangle->private->is_first; +} + +/** + * gimp_tool_rectangle_rectangle_is_new: + * @rectangle: + * + * Returns: %TRUE if the user is creating a new rectangle from + * scratch, %FALSE if modifying n previously existing rectangle. This + * function is only meaningful in _motion and _button_release. + */ +gboolean +gimp_tool_rectangle_rectangle_is_new (GimpToolRectangle *rectangle) +{ + g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE); + + return rectangle->private->is_new; +} + +/** + * gimp_tool_rectangle_point_in_rectangle: + * @rectangle: + * @x: X-coord of point to test (in image coordinates) + * @y: Y-coord of point to test (in image coordinates) + * + * Returns: %TRUE if the passed point was within the rectangle + **/ +gboolean +gimp_tool_rectangle_point_in_rectangle (GimpToolRectangle *rectangle, + gdouble x, + gdouble y) +{ + gdouble x1, y1, x2, y2; + + g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE); + + gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2); + + return (x >= x1 && x <= x2 && + y >= y1 && y <= y2); +} + +/** + * gimp_tool_rectangle_frame_item: + * @rectangle: a #GimpToolRectangle interface + * @item: a #GimpItem attached to the image on which a + * rectangle is being shown. + * + * Convenience function to set the corners of the rectangle to + * match the bounds of the specified item. The rectangle interface + * must be active (i.e., showing a rectangle), and the item must be + * attached to the image on which the rectangle is active. + **/ +void +gimp_tool_rectangle_frame_item (GimpToolRectangle *rectangle, + GimpItem *item) +{ + GimpDisplayShell *shell; + gint offset_x; + gint offset_y; + gint width; + gint height; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + g_return_if_fail (GIMP_IS_ITEM (item)); + g_return_if_fail (gimp_item_is_attached (item)); + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + + g_return_if_fail (gimp_display_get_image (shell->display) == + gimp_item_get_image (item)); + + width = gimp_item_get_width (item); + height = gimp_item_get_height (item); + + gimp_item_get_offset (item, &offset_x, &offset_y); + + gimp_tool_rectangle_set_function (rectangle, GIMP_TOOL_RECTANGLE_CREATING); + + g_object_set (rectangle, + "x1", (gdouble) offset_x, + "y1", (gdouble) offset_y, + "x2", (gdouble) (offset_x + width), + "y2", (gdouble) (offset_y + height), + NULL); + + /* kludge to force handle sizes to update. This call may be harmful + * if this function is ever moved out of the text tool code. + */ + gimp_tool_rectangle_set_constraint (rectangle, GIMP_RECTANGLE_CONSTRAIN_NONE); +} + +void +gimp_tool_rectangle_auto_shrink (GimpToolRectangle *rectangle, + gboolean shrink_merged) +{ + GimpToolRectanglePrivate *private; + GimpDisplayShell *shell; + GimpImage *image; + GimpPickable *pickable; + gint offset_x = 0; + gint offset_y = 0; + gint x1, y1; + gint x2, y2; + gint shrunk_x; + gint shrunk_y; + gint shrunk_width; + gint shrunk_height; + + g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle)); + + private = rectangle->private; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle)); + image = gimp_display_get_image (shell->display); + + if (shrink_merged) + { + pickable = GIMP_PICKABLE (image); + + x1 = private->x1; + y1 = private->y1; + x2 = private->x2; + y2 = private->y2; + } + else + { + pickable = GIMP_PICKABLE (gimp_image_get_active_drawable (image)); + + if (! pickable) + return; + + gimp_item_get_offset (GIMP_ITEM (pickable), &offset_x, &offset_y); + + x1 = private->x1 - offset_x; + y1 = private->y1 - offset_y; + x2 = private->x2 - offset_x; + y2 = private->y2 - offset_y; + } + + switch (gimp_pickable_auto_shrink (pickable, + x1, y1, x2 - x1, y2 - y1, + &shrunk_x, + &shrunk_y, + &shrunk_width, + &shrunk_height)) + { + case GIMP_AUTO_SHRINK_SHRINK: + { + GimpRectangleFunction original_function = private->function; + + private->function = GIMP_TOOL_RECTANGLE_AUTO_SHRINK; + + private->x1 = offset_x + shrunk_x; + private->y1 = offset_y + shrunk_y; + private->x2 = offset_x + shrunk_x + shrunk_width; + private->y2 = offset_y + shrunk_y + shrunk_height; + + gimp_tool_rectangle_update_int_rect (rectangle); + + gimp_tool_rectangle_change_complete (rectangle); + + private->function = original_function; + + gimp_tool_rectangle_update_options (rectangle); + } + break; + + default: + break; + } +} diff --git a/app/display/gimptoolrectangle.h b/app/display/gimptoolrectangle.h new file mode 100644 index 0000000..361839a --- /dev/null +++ b/app/display/gimptoolrectangle.h @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolrectangle.h + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpRectangleTool + * Copyright (C) 2007 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 . + */ + +#ifndef __GIMP_TOOL_RECTANGLE_H__ +#define __GIMP_TOOL_RECTANGLE_H__ + + +#include "gimptoolwidget.h" + + +typedef enum +{ + GIMP_TOOL_RECTANGLE_DEAD, + GIMP_TOOL_RECTANGLE_CREATING, + GIMP_TOOL_RECTANGLE_MOVING, + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT, + GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT, + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT, + GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT, + GIMP_TOOL_RECTANGLE_RESIZING_LEFT, + GIMP_TOOL_RECTANGLE_RESIZING_RIGHT, + GIMP_TOOL_RECTANGLE_RESIZING_TOP, + GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM, + GIMP_TOOL_RECTANGLE_AUTO_SHRINK, + GIMP_TOOL_RECTANGLE_EXECUTING, + GIMP_N_TOOL_RECTANGLE_FUNCTIONS +} GimpRectangleFunction; + + +#define GIMP_TYPE_TOOL_RECTANGLE (gimp_tool_rectangle_get_type ()) +#define GIMP_TOOL_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_RECTANGLE, GimpToolRectangle)) +#define GIMP_TOOL_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_RECTANGLE, GimpToolRectangleClass)) +#define GIMP_IS_TOOL_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_RECTANGLE)) +#define GIMP_IS_TOOL_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_RECTANGLE)) +#define GIMP_TOOL_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_RECTANGLE, GimpToolRectangleClass)) + + +typedef struct _GimpToolRectangle GimpToolRectangle; +typedef struct _GimpToolRectanglePrivate GimpToolRectanglePrivate; +typedef struct _GimpToolRectangleClass GimpToolRectangleClass; + +struct _GimpToolRectangle +{ + GimpToolWidget parent_instance; + + GimpToolRectanglePrivate *private; +}; + +struct _GimpToolRectangleClass +{ + GimpToolWidgetClass parent_class; + + /* signals */ + + gboolean (* change_complete) (GimpToolRectangle *rectangle); +}; + + +GType gimp_tool_rectangle_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_rectangle_new (GimpDisplayShell *shell); + +GimpRectangleFunction + gimp_tool_rectangle_get_function (GimpToolRectangle *rectangle); +void gimp_tool_rectangle_set_function (GimpToolRectangle *rectangle, + GimpRectangleFunction function); + +void gimp_tool_rectangle_set_constraint (GimpToolRectangle *rectangle, + GimpRectangleConstraint constraint); +GimpRectangleConstraint + gimp_tool_rectangle_get_constraint (GimpToolRectangle *rectangle); + +void gimp_tool_rectangle_get_public_rect (GimpToolRectangle *rectangle, + gdouble *pub_x1, + gdouble *pub_y1, + gdouble *pub_x2, + gdouble *pub_y2); + +void gimp_tool_rectangle_pending_size_set (GimpToolRectangle *rectangle, + GObject *object, + const gchar *width_property, + const gchar *height_property); + +void gimp_tool_rectangle_constraint_size_set + (GimpToolRectangle *rectangle, + GObject *object, + const gchar *width_property, + const gchar *height_property); + +gboolean gimp_tool_rectangle_rectangle_is_first + (GimpToolRectangle *rectangle); +gboolean gimp_tool_rectangle_rectangle_is_new (GimpToolRectangle *rectangle); +gboolean gimp_tool_rectangle_point_in_rectangle + (GimpToolRectangle *rectangle, + gdouble x, + gdouble y); + +void gimp_tool_rectangle_frame_item (GimpToolRectangle *rectangle, + GimpItem *item); +void gimp_tool_rectangle_auto_shrink (GimpToolRectangle *rectrectangle, + gboolean shrink_merged); + + +#endif /* __GIMP_TOOL_RECTANGLE_H__ */ diff --git a/app/display/gimptoolrotategrid.c b/app/display/gimptoolrotategrid.c new file mode 100644 index 0000000..c583490 --- /dev/null +++ b/app/display/gimptoolrotategrid.c @@ -0,0 +1,330 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolrotategrid.c + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "gimpdisplayshell.h" +#include "gimptoolrotategrid.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_ANGLE +}; + + +struct _GimpToolRotateGridPrivate +{ + gdouble angle; + + gboolean rotate_grab; + gdouble real_angle; + gdouble last_x; + gdouble last_y; +}; + + +/* local function prototypes */ + +static void gimp_tool_rotate_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_rotate_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint gimp_tool_rotate_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_rotate_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolRotateGrid, gimp_tool_rotate_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_rotate_grid_parent_class + + +static void +gimp_tool_rotate_grid_class_init (GimpToolRotateGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->set_property = gimp_tool_rotate_grid_set_property; + object_class->get_property = gimp_tool_rotate_grid_get_property; + + widget_class->button_press = gimp_tool_rotate_grid_button_press; + widget_class->motion = gimp_tool_rotate_grid_motion; + + g_object_class_install_property (object_class, PROP_ANGLE, + g_param_spec_double ("angle", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_rotate_grid_init (GimpToolRotateGrid *grid) +{ + grid->private = gimp_tool_rotate_grid_get_instance_private (grid); +} + +static void +gimp_tool_rotate_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (object); + GimpToolRotateGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_ANGLE: + private->angle = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_rotate_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (object); + GimpToolRotateGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_ANGLE: + g_value_set_double (value, private->angle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint +gimp_tool_rotate_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (widget); + GimpToolRotateGridPrivate *private = grid->private; + GimpTransformHandle handle; + + handle = GIMP_TOOL_WIDGET_CLASS (parent_class)->button_press (widget, + coords, time, + state, + press_type); + + if (handle == GIMP_TRANSFORM_HANDLE_ROTATION) + { + private->rotate_grab = TRUE; + private->real_angle = private->angle; + private->last_x = coords->x; + private->last_y = coords->y; + } + else + { + private->rotate_grab = FALSE; + } + + return handle; +} + +void +gimp_tool_rotate_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (widget); + GimpToolRotateGridPrivate *private = grid->private; + gdouble angle1, angle2, angle; + gdouble pivot_x, pivot_y; + gdouble x1, y1, x2, y2; + gboolean constrain; + GimpMatrix3 transform; + + if (! private->rotate_grab) + { + gdouble old_pivot_x; + gdouble old_pivot_y; + + g_object_get (widget, + "pivot-x", &old_pivot_x, + "pivot-y", &old_pivot_y, + NULL); + + g_object_freeze_notify (G_OBJECT (widget)); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->motion (widget, + coords, time, state); + + g_object_get (widget, + "pivot-x", &pivot_x, + "pivot-y", &pivot_y, + NULL); + + if (old_pivot_x != pivot_x || + old_pivot_y != pivot_y) + { + gimp_matrix3_identity (&transform); + gimp_transform_matrix_rotate_center (&transform, + pivot_x, pivot_y, + private->angle); + + g_object_set (widget, + "transform", &transform, + NULL); + } + + g_object_thaw_notify (G_OBJECT (widget)); + + return; + } + + g_object_get (widget, + "pivot-x", &pivot_x, + "pivot-y", &pivot_y, + "constrain-rotate", &constrain, + NULL); + + x1 = coords->x - pivot_x; + x2 = private->last_x - pivot_x; + y1 = pivot_y - coords->y; + y2 = pivot_y - private->last_y; + + /* find the first angle */ + angle1 = atan2 (y1, x1); + + /* find the angle */ + angle2 = atan2 (y2, x2); + + angle = angle2 - angle1; + + if (angle > G_PI || angle < -G_PI) + angle = angle2 - ((angle1 < 0) ? 2.0 * G_PI + angle1 : angle1 - 2.0 * G_PI); + + /* increment the transform tool's angle */ + private->real_angle += angle; + + /* limit the angle to between -180 and 180 degrees */ + if (private->real_angle < - G_PI) + { + private->real_angle += 2.0 * G_PI; + } + else if (private->real_angle > G_PI) + { + private->real_angle -= 2.0 * G_PI; + } + + /* constrain the angle to 15-degree multiples if ctrl is held down */ +#define FIFTEEN_DEG (G_PI / 12.0) + + if (constrain) + { + angle = FIFTEEN_DEG * (gint) ((private->real_angle + + FIFTEEN_DEG / 2.0) / FIFTEEN_DEG); + } + else + { + angle = private->real_angle; + } + + gimp_matrix3_identity (&transform); + gimp_transform_matrix_rotate_center (&transform, pivot_x, pivot_y, angle); + + g_object_set (widget, + "transform", &transform, + "angle", angle, + NULL); + + private->last_x = coords->x; + private->last_y = coords->y; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_rotate_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble pivot_x, + gdouble pivot_y, + gdouble angle) +{ + GimpMatrix3 transform; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + gimp_matrix3_identity (&transform); + gimp_transform_matrix_rotate_center (&transform, pivot_x, pivot_y, angle); + + return g_object_new (GIMP_TYPE_TOOL_ROTATE_GRID, + "shell", shell, + "transform", &transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "pivot-x", pivot_x, + "pivot-y", pivot_y, + "angle", angle, + NULL); +} diff --git a/app/display/gimptoolrotategrid.h b/app/display/gimptoolrotategrid.h new file mode 100644 index 0000000..2254c8d --- /dev/null +++ b/app/display/gimptoolrotategrid.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolrotategrid.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_ROTATE_GRID_H__ +#define __GIMP_TOOL_ROTATE_GRID_H__ + + +#include "gimptooltransformgrid.h" + + +#define GIMP_TYPE_TOOL_ROTATE_GRID (gimp_tool_rotate_grid_get_type ()) +#define GIMP_TOOL_ROTATE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_ROTATE_GRID, GimpToolRotateGrid)) +#define GIMP_TOOL_ROTATE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_ROTATE_GRID, GimpToolRotateGridClass)) +#define GIMP_IS_TOOL_ROTATE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_ROTATE_GRID)) +#define GIMP_IS_TOOL_ROTATE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_ROTATE_GRID)) +#define GIMP_TOOL_ROTATE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_ROTATE_GRID, GimpToolRotateGridClass)) + + +typedef struct _GimpToolRotateGrid GimpToolRotateGrid; +typedef struct _GimpToolRotateGridPrivate GimpToolRotateGridPrivate; +typedef struct _GimpToolRotateGridClass GimpToolRotateGridClass; + +struct _GimpToolRotateGrid +{ + GimpToolTransformGrid parent_instance; + + GimpToolRotateGridPrivate *private; +}; + +struct _GimpToolRotateGridClass +{ + GimpToolTransformGridClass parent_class; +}; + + +GType gimp_tool_rotate_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_rotate_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble pivot_x, + gdouble pivot_y, + gdouble angle); + + +#endif /* __GIMP_TOOL_ROTATE_GRID_H__ */ diff --git a/app/display/gimptoolsheargrid.c b/app/display/gimptoolsheargrid.c new file mode 100644 index 0000000..878c549 --- /dev/null +++ b/app/display/gimptoolsheargrid.c @@ -0,0 +1,360 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolsheargrid.c + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "gimpdisplayshell.h" +#include "gimptoolsheargrid.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_SHEAR_X, + PROP_SHEAR_Y +}; + + +struct _GimpToolShearGridPrivate +{ + GimpOrientationType orientation; + gdouble shear_x; + gdouble shear_y; + + gdouble last_x; + gdouble last_y; +}; + + +/* local function prototypes */ + +static void gimp_tool_shear_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_shear_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint gimp_tool_shear_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_shear_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolShearGrid, gimp_tool_shear_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_shear_grid_parent_class + + +static void +gimp_tool_shear_grid_class_init (GimpToolShearGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->set_property = gimp_tool_shear_grid_set_property; + object_class->get_property = gimp_tool_shear_grid_get_property; + + widget_class->button_press = gimp_tool_shear_grid_button_press; + widget_class->motion = gimp_tool_shear_grid_motion; + + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", + NULL, NULL, + GIMP_TYPE_ORIENTATION_TYPE, + GIMP_ORIENTATION_UNKNOWN, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SHEAR_X, + g_param_spec_double ("shear-x", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SHEAR_Y, + g_param_spec_double ("shear-y", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_shear_grid_init (GimpToolShearGrid *grid) +{ + grid->private = gimp_tool_shear_grid_get_instance_private (grid); + + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_SHEAR, + "outside-function", GIMP_TRANSFORM_FUNCTION_SHEAR, + "use-corner-handles", FALSE, + "use-perspective-handles", FALSE, + "use-side-handles", FALSE, + "use-shear-handles", FALSE, + "use-center-handle", FALSE, + "use-pivot-handle", FALSE, + NULL); +} + +static void +gimp_tool_shear_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (object); + GimpToolShearGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_ORIENTATION: + private->orientation = g_value_get_enum (value); + break; + case PROP_SHEAR_X: + private->shear_x = g_value_get_double (value); + break; + case PROP_SHEAR_Y: + private->shear_y = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_shear_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (object); + GimpToolShearGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, private->orientation); + break; + case PROP_SHEAR_X: + g_value_set_double (value, private->shear_x); + break; + case PROP_SHEAR_Y: + g_value_set_double (value, private->shear_y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint +gimp_tool_shear_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (widget); + GimpToolShearGridPrivate *private = grid->private; + + private->last_x = coords->x; + private->last_y = coords->y; + + return 1; +} + +void +gimp_tool_shear_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (widget); + GimpToolShearGridPrivate *private = grid->private; + gdouble diffx = coords->x - private->last_x; + gdouble diffy = coords->y - private->last_y; + gdouble amount = 0.0; + GimpMatrix3 transform; + GimpMatrix3 *t; + gdouble x1, y1; + gdouble x2, y2; + gdouble tx1, ty1; + gdouble tx2, ty2; + gdouble tx3, ty3; + gdouble tx4, ty4; + gdouble current_x; + gdouble current_y; + + g_object_get (widget, + "transform", &t, + "x1", &x1, + "y1", &y1, + "x2", &x2, + "y2", &y2, + NULL); + + gimp_matrix3_transform_point (t, x1, y1, &tx1, &ty1); + gimp_matrix3_transform_point (t, x2, y1, &tx2, &ty2); + gimp_matrix3_transform_point (t, x1, y2, &tx3, &ty3); + gimp_matrix3_transform_point (t, x2, y2, &tx4, &ty4); + + g_free (t); + + current_x = coords->x; + current_y = coords->y; + + diffx = current_x - private->last_x; + diffy = current_y - private->last_y; + + /* If we haven't yet decided on which way to control shearing + * decide using the maximum differential + */ + if (private->orientation == GIMP_ORIENTATION_UNKNOWN) + { +#define MIN_MOVE 5 + + if (ABS (diffx) > MIN_MOVE || ABS (diffy) > MIN_MOVE) + { + if (ABS (diffx) > ABS (diffy)) + { + private->orientation = GIMP_ORIENTATION_HORIZONTAL; + private->shear_x = 0.0; + } + else + { + private->orientation = GIMP_ORIENTATION_VERTICAL; + private->shear_y = 0.0; + } + } + /* set the current coords to the last ones */ + else + { + current_x = private->last_x; + current_y = private->last_y; + } + } + + /* if the direction is known, keep track of the magnitude */ + if (private->orientation == GIMP_ORIENTATION_HORIZONTAL) + { + if (current_y > (ty1 + ty3) / 2) + private->shear_x += diffx; + else + private->shear_x -= diffx; + + amount = private->shear_x; + } + else if (private->orientation == GIMP_ORIENTATION_VERTICAL) + { + if (current_x > (tx1 + tx2) / 2) + private->shear_y += diffy; + else + private->shear_y -= diffy; + + amount = private->shear_y; + } + + gimp_matrix3_identity (&transform); + gimp_transform_matrix_shear (&transform, + x1, y1, x2 - x1, y2 - y1, + private->orientation, amount); + + g_object_set (widget, + "transform", &transform, + "orientation", private->orientation, + "shear-x", private->shear_x, + "shear_y", private->shear_y, + NULL); + + private->last_x = current_x; + private->last_y = current_y; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_shear_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpOrientationType orientation, + gdouble shear_x, + gdouble shear_y) +{ + GimpMatrix3 transform; + gdouble amount; + + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + if (orientation == GIMP_ORIENTATION_HORIZONTAL) + amount = shear_x; + else + amount = shear_y; + + gimp_matrix3_identity (&transform); + gimp_transform_matrix_shear (&transform, + x1, y1, x2 - x1, y2 - y1, + orientation, amount); + + return g_object_new (GIMP_TYPE_TOOL_SHEAR_GRID, + "shell", shell, + "transform", &transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "orientation", orientation, + "shear-x", shear_x, + "shear-y", shear_y, + NULL); +} diff --git a/app/display/gimptoolsheargrid.h b/app/display/gimptoolsheargrid.h new file mode 100644 index 0000000..dab96d5 --- /dev/null +++ b/app/display/gimptoolsheargrid.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolsheargrid.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_SHEAR_GRID_H__ +#define __GIMP_TOOL_SHEAR_GRID_H__ + + +#include "gimptooltransformgrid.h" + + +#define GIMP_TYPE_TOOL_SHEAR_GRID (gimp_tool_shear_grid_get_type ()) +#define GIMP_TOOL_SHEAR_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_SHEAR_GRID, GimpToolShearGrid)) +#define GIMP_TOOL_SHEAR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_SHEAR_GRID, GimpToolShearGridClass)) +#define GIMP_IS_TOOL_SHEAR_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_SHEAR_GRID)) +#define GIMP_IS_TOOL_SHEAR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_SHEAR_GRID)) +#define GIMP_TOOL_SHEAR_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_SHEAR_GRID, GimpToolShearGridClass)) + + +typedef struct _GimpToolShearGrid GimpToolShearGrid; +typedef struct _GimpToolShearGridPrivate GimpToolShearGridPrivate; +typedef struct _GimpToolShearGridClass GimpToolShearGridClass; + +struct _GimpToolShearGrid +{ + GimpToolTransformGrid parent_instance; + + GimpToolShearGridPrivate *private; +}; + +struct _GimpToolShearGridClass +{ + GimpToolTransformGridClass parent_class; +}; + + +GType gimp_tool_shear_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_shear_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpOrientationType orientation, + gdouble shear_x, + gdouble shear_y); + + +#endif /* __GIMP_TOOL_SHEAR_GRID_H__ */ diff --git a/app/display/gimptooltransform3dgrid.c b/app/display/gimptooltransform3dgrid.c new file mode 100644 index 0000000..06cd955 --- /dev/null +++ b/app/display/gimptooltransform3dgrid.c @@ -0,0 +1,1162 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptool3dtransformgrid.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "core/gimp-transform-3d-utils.h" +#include "core/gimp-utils.h" + +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" +#include "gimptooltransform3dgrid.h" + +#include "gimp-intl.h" + + +#define CONSTRAINT_MIN_DIST 8.0 +#define PIXELS_PER_REVOLUTION 1000 + + +enum +{ + PROP_0, + PROP_MODE, + PROP_UNIFIED, + PROP_CONSTRAIN_AXIS, + PROP_Z_AXIS, + PROP_LOCAL_FRAME, + PROP_CAMERA_X, + PROP_CAMERA_Y, + PROP_CAMERA_Z, + PROP_OFFSET_X, + PROP_OFFSET_Y, + PROP_OFFSET_Z, + PROP_ROTATION_ORDER, + PROP_ANGLE_X, + PROP_ANGLE_Y, + PROP_ANGLE_Z, + PROP_PIVOT_3D_X, + PROP_PIVOT_3D_Y, + PROP_PIVOT_3D_Z +}; + +typedef enum +{ + AXIS_NONE, + AXIS_X, + AXIS_Y +} Axis; + +struct _GimpToolTransform3DGridPrivate +{ + GimpTransform3DMode mode; + gboolean unified; + + gboolean constrain_axis; + gboolean z_axis; + gboolean local_frame; + + gdouble camera_x; + gdouble camera_y; + gdouble camera_z; + + gdouble offset_x; + gdouble offset_y; + gdouble offset_z; + + gint rotation_order; + gdouble angle_x; + gdouble angle_y; + gdouble angle_z; + + gdouble pivot_x; + gdouble pivot_y; + gdouble pivot_z; + + GimpTransformHandle handle; + + gdouble orig_x; + gdouble orig_y; + gdouble orig_offset_x; + gdouble orig_offset_y; + gdouble orig_offset_z; + GimpMatrix3 orig_transform; + + gdouble last_x; + gdouble last_y; + + Axis constrained_axis; +}; + + +/* local function prototypes */ + +static void gimp_tool_transform_3d_grid_constructed (GObject *object); +static void gimp_tool_transform_3d_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_transform_3d_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint gimp_tool_transform_3d_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_transform_3d_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static void gimp_tool_transform_3d_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_transform_3d_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_transform_3d_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_transform_3d_grid_update_mode (GimpToolTransform3DGrid *grid); +static void gimp_tool_transform_3d_grid_reset_motion (GimpToolTransform3DGrid *grid); +static gboolean gimp_tool_transform_3d_grid_constrain (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y, + gdouble ox, + gdouble oy, + gdouble *tx, + gdouble *ty); + +static gboolean gimp_tool_transform_3d_grid_motion_vanishing_point (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); +static gboolean gimp_tool_transform_3d_grid_motion_move (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); +static gboolean gimp_tool_transform_3d_grid_motion_rotate (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolTransform3DGrid, gimp_tool_transform_3d_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_transform_3d_grid_parent_class + + +static void +gimp_tool_transform_3d_grid_class_init (GimpToolTransform3DGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_transform_3d_grid_constructed; + object_class->set_property = gimp_tool_transform_3d_grid_set_property; + object_class->get_property = gimp_tool_transform_3d_grid_get_property; + + widget_class->button_press = gimp_tool_transform_3d_grid_button_press; + widget_class->motion = gimp_tool_transform_3d_grid_motion; + widget_class->hover = gimp_tool_transform_3d_grid_hover; + widget_class->hover_modifier = gimp_tool_transform_3d_grid_hover_modifier; + widget_class->get_cursor = gimp_tool_transform_3d_grid_get_cursor; + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_enum ("mode", + NULL, NULL, + GIMP_TYPE_TRANSFORM_3D_MODE, + GIMP_TRANSFORM_3D_MODE_CAMERA, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_UNIFIED, + g_param_spec_boolean ("unified", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_AXIS, + g_param_spec_boolean ("constrain-axis", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Z_AXIS, + g_param_spec_boolean ("z-axis", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_LOCAL_FRAME, + g_param_spec_boolean ("local-frame", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_X, + g_param_spec_double ("camera-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_Y, + g_param_spec_double ("camera-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CAMERA_Z, + g_param_spec_double ("camera-z", + NULL, NULL, + -(1.0 / 0.0), + 1.0 / 0.0, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_X, + g_param_spec_double ("offset-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_Y, + g_param_spec_double ("offset-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET_Z, + g_param_spec_double ("offset-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ROTATION_ORDER, + g_param_spec_int ("rotation-order", + NULL, NULL, + 0, 6, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_X, + g_param_spec_double ("angle-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_Y, + g_param_spec_double ("angle-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ANGLE_Z, + g_param_spec_double ("angle-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_X, + g_param_spec_double ("pivot-3d-x", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_Y, + g_param_spec_double ("pivot-3d-y", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_3D_Z, + g_param_spec_double ("pivot-3d-z", + NULL, NULL, + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_transform_3d_grid_init (GimpToolTransform3DGrid *grid) +{ + grid->priv = gimp_tool_transform_3d_grid_get_instance_private (grid); +} + +static void +gimp_tool_transform_3d_grid_constructed (GObject *object) +{ + G_OBJECT_CLASS (parent_class)->constructed (object); + + g_object_set (object, + "clip-guides", TRUE, + "dynamic-handle-size", FALSE, + NULL); +} + +static void +gimp_tool_transform_3d_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (object); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + switch (property_id) + { + case PROP_MODE: + priv->mode = g_value_get_enum (value); + gimp_tool_transform_3d_grid_update_mode (grid); + break; + + case PROP_UNIFIED: + priv->unified = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_update_mode (grid); + break; + + case PROP_CONSTRAIN_AXIS: + priv->constrain_axis = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + case PROP_Z_AXIS: + priv->z_axis = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + case PROP_LOCAL_FRAME: + priv->local_frame = g_value_get_boolean (value); + gimp_tool_transform_3d_grid_reset_motion (grid); + break; + + case PROP_CAMERA_X: + priv->camera_x = g_value_get_double (value); + g_object_set (grid, + "pivot-x", priv->camera_x, + NULL); + break; + case PROP_CAMERA_Y: + priv->camera_y = g_value_get_double (value); + g_object_set (grid, + "pivot-y", priv->camera_y, + NULL); + break; + case PROP_CAMERA_Z: + priv->camera_z = g_value_get_double (value); + break; + + case PROP_OFFSET_X: + priv->offset_x = g_value_get_double (value); + break; + case PROP_OFFSET_Y: + priv->offset_y = g_value_get_double (value); + break; + case PROP_OFFSET_Z: + priv->offset_z = g_value_get_double (value); + break; + + case PROP_ROTATION_ORDER: + priv->rotation_order = g_value_get_int (value); + break; + case PROP_ANGLE_X: + priv->angle_x = g_value_get_double (value); + break; + case PROP_ANGLE_Y: + priv->angle_y = g_value_get_double (value); + break; + case PROP_ANGLE_Z: + priv->angle_z = g_value_get_double (value); + break; + + case PROP_PIVOT_3D_X: + priv->pivot_x = g_value_get_double (value); + break; + case PROP_PIVOT_3D_Y: + priv->pivot_y = g_value_get_double (value); + break; + case PROP_PIVOT_3D_Z: + priv->pivot_z = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_transform_3d_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (object); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + switch (property_id) + { + case PROP_MODE: + g_value_set_enum (value, priv->mode); + break; + case PROP_UNIFIED: + g_value_set_boolean (value, priv->unified); + break; + + case PROP_CONSTRAIN_AXIS: + g_value_set_boolean (value, priv->constrain_axis); + break; + case PROP_Z_AXIS: + g_value_set_boolean (value, priv->z_axis); + break; + case PROP_LOCAL_FRAME: + g_value_set_boolean (value, priv->local_frame); + break; + + case PROP_CAMERA_X: + g_value_set_double (value, priv->camera_x); + break; + case PROP_CAMERA_Y: + g_value_set_double (value, priv->camera_y); + break; + case PROP_CAMERA_Z: + g_value_set_double (value, priv->camera_z); + break; + + case PROP_OFFSET_X: + g_value_set_double (value, priv->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_double (value, priv->offset_y); + break; + case PROP_OFFSET_Z: + g_value_set_double (value, priv->offset_z); + break; + + case PROP_ROTATION_ORDER: + g_value_set_int (value, priv->rotation_order); + break; + case PROP_ANGLE_X: + g_value_set_double (value, priv->angle_x); + break; + case PROP_ANGLE_Y: + g_value_set_double (value, priv->angle_y); + break; + case PROP_ANGLE_Z: + g_value_set_double (value, priv->angle_z); + break; + + case PROP_PIVOT_3D_X: + g_value_set_double (value, priv->pivot_x); + break; + case PROP_PIVOT_3D_Y: + g_value_set_double (value, priv->pivot_y); + break; + case PROP_PIVOT_3D_Z: + g_value_set_double (value, priv->pivot_z); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint +gimp_tool_transform_3d_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + priv->handle = GIMP_TOOL_WIDGET_CLASS (parent_class)->button_press ( + widget, coords, time, state, press_type); + + priv->orig_x = coords->x; + priv->orig_y = coords->y; + priv->orig_offset_x = priv->offset_x; + priv->orig_offset_y = priv->offset_y; + priv->orig_offset_z = priv->offset_z; + priv->last_x = coords->x; + priv->last_y = coords->y; + + gimp_tool_transform_3d_grid_reset_motion (grid); + + return priv->handle; +} + +void +gimp_tool_transform_3d_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix3 transform; + gboolean update = TRUE; + + switch (priv->handle) + { + case GIMP_TRANSFORM_HANDLE_PIVOT: + update = gimp_tool_transform_3d_grid_motion_vanishing_point ( + grid, coords->x, coords->y); + break; + + case GIMP_TRANSFORM_HANDLE_CENTER: + update = gimp_tool_transform_3d_grid_motion_move ( + grid, coords->x, coords->y); + break; + + case GIMP_TRANSFORM_HANDLE_ROTATION: + update = gimp_tool_transform_3d_grid_motion_rotate ( + grid, coords->x, coords->y); + break; + + default: + g_return_if_reached (); + } + + if (update) + { + gimp_transform_3d_matrix (&transform, + + priv->camera_x, + priv->camera_y, + priv->camera_z, + + priv->offset_x, + priv->offset_y, + priv->offset_z, + + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + + priv->pivot_x, + priv->pivot_y, + priv->pivot_z); + + g_object_set (widget, + "transform", &transform, + NULL); + } +} + +static void +gimp_tool_transform_3d_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GIMP_TOOL_WIDGET_CLASS (parent_class)->hover (widget, + coords, state, proximity); + + if (proximity && + gimp_tool_transform_grid_get_handle (GIMP_TOOL_TRANSFORM_GRID (widget)) == + GIMP_TRANSFORM_HANDLE_PIVOT) + { + gimp_tool_widget_set_status (widget, + _("Click-Drag to move the vanishing point")); + } +} + +static void +gimp_tool_transform_3d_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolTransform3DGrid *grid = GIMP_TOOL_TRANSFORM_3D_GRID (widget); + GimpToolTransform3DGridPrivate *priv = grid->priv; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->hover_modifier (widget, + key, press, state); + + priv->local_frame = (state & gimp_get_extend_selection_mask ()) != 0; +} + +static gboolean +gimp_tool_transform_3d_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + if (! GIMP_TOOL_WIDGET_CLASS (parent_class)->get_cursor (widget, + coords, + state, + cursor, + tool_cursor, + modifier)) + { + return FALSE; + } + + if (gimp_tool_transform_grid_get_handle (GIMP_TOOL_TRANSFORM_GRID (widget)) == + GIMP_TRANSFORM_HANDLE_PIVOT) + { + *tool_cursor = GIMP_TOOL_CURSOR_TRANSFORM_3D_CAMERA; + } + + return TRUE; +} + +static void +gimp_tool_transform_3d_grid_update_mode (GimpToolTransform3DGrid *grid) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + + if (priv->unified) + { + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "outside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "use-pivot-handle", TRUE, + NULL); + } + else + { + switch (priv->mode) + { + case GIMP_TRANSFORM_3D_MODE_CAMERA: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_NONE, + "outside-function", GIMP_TRANSFORM_FUNCTION_NONE, + "use-pivot-handle", TRUE, + NULL); + break; + + case GIMP_TRANSFORM_3D_MODE_MOVE: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "outside-function", GIMP_TRANSFORM_FUNCTION_MOVE, + "use-pivot-handle", FALSE, + NULL); + break; + + case GIMP_TRANSFORM_3D_MODE_ROTATE: + g_object_set (grid, + "inside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "outside-function", GIMP_TRANSFORM_FUNCTION_ROTATE, + "use-pivot-handle", FALSE, + NULL); + break; + } + } +} + +static void +gimp_tool_transform_3d_grid_reset_motion (GimpToolTransform3DGrid *grid) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix3 *transform; + + priv->constrained_axis = AXIS_NONE; + + g_object_get (grid, + "transform", &transform, + NULL); + + priv->orig_transform = *transform; + + g_free (transform); +} + +static gboolean +gimp_tool_transform_3d_grid_constrain (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y, + gdouble ox, + gdouble oy, + gdouble *tx, + gdouble *ty) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + + if (! priv->constrain_axis) + return TRUE; + + if (priv->constrained_axis == AXIS_NONE) + { + GimpDisplayShell *shell; + gdouble x1, y1; + gdouble x2, y2; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (grid)); + + gimp_display_shell_transform_xy_f (shell, + priv->last_x, priv->last_y, + &x1, &y1); + gimp_display_shell_transform_xy_f (shell, + x, y, + &x2, &y2); + + if (hypot (x2 - x1, y2 - y1) < CONSTRAINT_MIN_DIST) + return FALSE; + + if (fabs (*tx - ox) >= fabs (*ty - oy)) + priv->constrained_axis = AXIS_X; + else + priv->constrained_axis = AXIS_Y; + } + + if (priv->constrained_axis == AXIS_X) + *ty = oy; + else + *tx = ox; + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_vanishing_point (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpCoords c = {}; + gdouble pivot_x; + gdouble pivot_y; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + priv->last_x, priv->last_y, + &x, &y)) + { + return FALSE; + } + + c.x = x; + c.y = y; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->motion (GIMP_TOOL_WIDGET (grid), + &c, 0, 0); + + g_object_get (grid, + "pivot-x", &pivot_x, + "pivot-y", &pivot_y, + NULL); + + g_object_set (grid, + "camera-x", pivot_x, + "camera-y", pivot_y, + NULL); + + priv->last_x = c.x; + priv->last_y = c.y; + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_move (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpMatrix4 matrix; + + if (! priv->z_axis) + { + gdouble x1, y1, z1, w1; + gdouble x2, y2, z2, w2; + + if (! priv->local_frame) + { + gimp_matrix4_identity (&matrix); + } + else + { + GimpMatrix3 transform_inv = priv->orig_transform;; + + gimp_matrix3_invert (&transform_inv); + + gimp_transform_3d_matrix3_to_matrix4 (&transform_inv, &matrix, 2); + } + + w1 = gimp_matrix4_transform_point (&matrix, + priv->last_x, priv->last_y, 0.0, + &x1, &y1, &z1); + w2 = gimp_matrix4_transform_point (&matrix, + x, y, 0.0, + &x2, &y2, &z2); + + if (w1 <= 0.0) + return FALSE; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + x1, y1, + &x2, &y2)) + { + return FALSE; + } + + if (priv->local_frame) + { + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + gimp_matrix4_transform_point (&matrix, + x1, y1, z1, + &x1, &y1, &z1); + gimp_matrix4_transform_point (&matrix, + x2, y2, z2, + &x2, &y2, &z2); + } + + if (w2 > 0.0) + { + g_object_set (grid, + "offset-x", priv->offset_x + (x2 - x1), + "offset-y", priv->offset_y + (y2 - y1), + "offset-z", priv->offset_z + (z2 - z1), + NULL); + + priv->last_x = x; + priv->last_y = y; + } + else + { + g_object_set (grid, + "offset-x", priv->orig_offset_x, + "offset-y", priv->orig_offset_y, + "offset-z", priv->orig_offset_z, + NULL); + + priv->last_x = priv->orig_x; + priv->last_y = priv->orig_y; + } + } + else + { + GimpVector3 axis; + gdouble amount; + + if (! priv->local_frame) + { + axis.x = 0.0; + axis.y = 0.0; + axis.z = 1.0; + } + else + { + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + axis.x = matrix.coeff[0][2]; + axis.y = matrix.coeff[1][2]; + axis.z = matrix.coeff[2][2]; + + if (axis.x < 0.0) + gimp_vector3_neg (&axis); + } + + amount = x - priv->last_x; + + g_object_set (grid, + "offset-x", priv->offset_x + axis.x * amount, + "offset-y", priv->offset_y + axis.y * amount, + "offset-z", priv->offset_z + axis.z * amount, + NULL); + + priv->last_x = x; + priv->last_y = y; + } + + return TRUE; +} + +static gboolean +gimp_tool_transform_3d_grid_motion_rotate (GimpToolTransform3DGrid *grid, + gdouble x, + gdouble y) +{ + GimpToolTransform3DGridPrivate *priv = grid->priv; + GimpDisplayShell *shell; + GimpMatrix4 matrix; + GimpMatrix2 basis_inv; + GimpVector3 omega; + gdouble z_sign; + gboolean local_frame; + + shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (grid)); + + local_frame = priv->local_frame && (priv->constrain_axis || priv->z_axis); + + if (! local_frame) + { + gimp_matrix2_identity (&basis_inv); + z_sign = 1.0; + } + else + { + { + GimpVector3 o, n, c; + + gimp_matrix4_identity (&matrix); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + priv->pivot_x, + priv->pivot_y, + priv->pivot_z); + + gimp_transform_3d_matrix4_translate (&matrix, + priv->offset_x, + priv->offset_y, + priv->offset_z); + + gimp_matrix4_transform_point (&matrix, + 0.0, 0.0, 0.0, + &o.x, &o.y, &o.z); + gimp_matrix4_transform_point (&matrix, + 0.0, 0.0, 1.0, + &n.x, &n.y, &n.z); + + c.x = priv->camera_x; + c.y = priv->camera_y; + c.z = priv->camera_z; + + gimp_vector3_sub (&n, &n, &o); + gimp_vector3_sub (&c, &c, &o); + + z_sign = gimp_vector3_inner_product (&c, &n) <= 0.0 ? +1.0 : -1.0; + } + + { + GimpVector2 o, u, v; + + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y, + &o.x, &o.y); + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x + 1.0, priv->pivot_y, + &u.x, &u.y); + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y + 1.0, + &v.x, &v.y); + + gimp_vector2_sub (&u, &u, &o); + gimp_vector2_sub (&v, &v, &o); + + gimp_vector2_normalize (&u); + gimp_vector2_normalize (&v); + + basis_inv.coeff[0][0] = u.x; + basis_inv.coeff[1][0] = u.y; + basis_inv.coeff[0][1] = v.x; + basis_inv.coeff[1][1] = v.y; + + gimp_matrix2_invert (&basis_inv); + } + } + + if (! priv->z_axis) + { + GimpVector2 scale; + gdouble norm; + + gimp_matrix2_transform_point (&basis_inv, + -(y - priv->last_y), + x - priv->last_x, + &omega.x, &omega.y); + + omega.z = 0.0; + + if (! gimp_tool_transform_3d_grid_constrain (grid, + x, y, + 0.0, 0.0, + &omega.x, &omega.y)) + { + return FALSE; + } + + norm = gimp_vector3_length (&omega); + + if (norm > 0.0) + { + scale.x = shell->scale_x * omega.y / norm; + scale.y = shell->scale_y * omega.x / norm; + + gimp_vector3_mul (&omega, gimp_vector2_length (&scale)); + gimp_vector3_mul (&omega, 2.0 * G_PI / PIXELS_PER_REVOLUTION); + } + } + else + { + GimpVector2 o; + GimpVector2 v1 = {priv->last_x, priv->last_y}; + GimpVector2 v2 = {x, y}; + + g_warn_if_fail (priv->pivot_z == 0.0); + + gimp_matrix3_transform_point (&priv->orig_transform, + priv->pivot_x, priv->pivot_y, + &o.x, &o.y); + + gimp_vector2_sub (&v1, &v1, &o); + gimp_vector2_sub (&v2, &v2, &o); + + gimp_vector2_normalize (&v1); + gimp_vector2_normalize (&v2); + + omega.x = 0.0; + omega.y = 0.0; + omega.z = atan2 (gimp_vector2_cross_product (&v1, &v2).y, + gimp_vector2_inner_product (&v1, &v2)); + + omega.z *= z_sign; + } + + gimp_matrix4_identity (&matrix); + + if (local_frame) + gimp_transform_3d_matrix4_rotate (&matrix, &omega); + + gimp_transform_3d_matrix4_rotate_euler (&matrix, + priv->rotation_order, + priv->angle_x, + priv->angle_y, + priv->angle_z, + 0.0, 0.0, 0.0); + + if (! local_frame) + gimp_transform_3d_matrix4_rotate (&matrix, &omega); + + gimp_transform_3d_matrix4_rotate_euler_decompose (&matrix, + priv->rotation_order, + &priv->angle_x, + &priv->angle_y, + &priv->angle_z); + + priv->last_x = x; + priv->last_y = y; + + g_object_set (grid, + "angle-x", priv->angle_x, + "angle-y", priv->angle_y, + "angle-z", priv->angle_z, + NULL); + + return TRUE; +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_transform_3d_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_TRANSFORM_3D_GRID, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "camera-x", camera_x, + "camera-y", camera_y, + "camera-z", camera_z, + "pivot-3d-x", (x1 + x2) / 2.0, + "pivot-3d-y", (y1 + y2) / 2.0, + "pivot-3d-z", 0.0, + NULL); +} diff --git a/app/display/gimptooltransform3dgrid.h b/app/display/gimptooltransform3dgrid.h new file mode 100644 index 0000000..42ac3ea --- /dev/null +++ b/app/display/gimptooltransform3dgrid.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptool3dtransformgrid.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_TOOL_TRANSFORM_3D_GRID_H__ +#define __GIMP_TOOL_TRANSFORM_3D_GRID_H__ + + +#include "gimptooltransformgrid.h" + + +#define GIMP_TYPE_TOOL_TRANSFORM_3D_GRID (gimp_tool_transform_3d_grid_get_type ()) +#define GIMP_TOOL_TRANSFORM_3D_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_TRANSFORM_3D_GRID, GimpToolTransform3DGrid)) +#define GIMP_TOOL_TRANSFORM_3D_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_TRANSFORM_3D_GRID, GimpToolTransform3DGridClass)) +#define GIMP_IS_TOOL_TRANSFORM_3D_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_TRANSFORM_3D_GRID)) +#define GIMP_IS_TOOL_TRANSFORM_3D_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_TRANSFORM_3D_GRID)) +#define GIMP_TOOL_TRANSFORM_3D_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_TRANSFORM_3D_GRID, GimpToolTransform3DGridClass)) + + +typedef struct _GimpToolTransform3DGrid GimpToolTransform3DGrid; +typedef struct _GimpToolTransform3DGridPrivate GimpToolTransform3DGridPrivate; +typedef struct _GimpToolTransform3DGridClass GimpToolTransform3DGridClass; + +struct _GimpToolTransform3DGrid +{ + GimpToolTransformGrid parent_instance; + + GimpToolTransform3DGridPrivate *priv; +}; + +struct _GimpToolTransform3DGridClass +{ + GimpToolTransformGridClass parent_class; +}; + + +GType gimp_tool_transform_3d_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_transform_3d_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble camera_x, + gdouble camera_y, + gdouble camera_z); + + +#endif /* __GIMP_TOOL_TRANSFORM_3D_GRID_H__ */ diff --git a/app/display/gimptooltransformgrid.c b/app/display/gimptooltransformgrid.c new file mode 100644 index 0000000..b186b91 --- /dev/null +++ b/app/display/gimptooltransformgrid.c @@ -0,0 +1,2494 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptooltransformgrid.c + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpUnifiedTransformTool + * Copyright (C) 2011 Mikael Magnusson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvastransformguides.h" +#include "gimpdisplayshell.h" +#include "gimptooltransformgrid.h" + +#include "gimp-intl.h" + + +#define MIN_HANDLE_SIZE 6 + + +enum +{ + PROP_0, + PROP_TRANSFORM, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_PIVOT_X, + PROP_PIVOT_Y, + PROP_GUIDE_TYPE, + PROP_N_GUIDES, + PROP_CLIP_GUIDES, + PROP_SHOW_GUIDES, + PROP_INSIDE_FUNCTION, + PROP_OUTSIDE_FUNCTION, + PROP_USE_CORNER_HANDLES, + PROP_USE_PERSPECTIVE_HANDLES, + PROP_USE_SIDE_HANDLES, + PROP_USE_SHEAR_HANDLES, + PROP_USE_CENTER_HANDLE, + PROP_USE_PIVOT_HANDLE, + PROP_DYNAMIC_HANDLE_SIZE, + PROP_CONSTRAIN_MOVE, + PROP_CONSTRAIN_SCALE, + PROP_CONSTRAIN_ROTATE, + PROP_CONSTRAIN_SHEAR, + PROP_CONSTRAIN_PERSPECTIVE, + PROP_FROMPIVOT_SCALE, + PROP_FROMPIVOT_SHEAR, + PROP_FROMPIVOT_PERSPECTIVE, + PROP_CORNERSNAP, + PROP_FIXEDPIVOT +}; + + +struct _GimpToolTransformGridPrivate +{ + GimpMatrix3 transform; + gdouble x1, y1; + gdouble x2, y2; + gdouble pivot_x; + gdouble pivot_y; + GimpGuidesType guide_type; + gint n_guides; + gboolean clip_guides; + gboolean show_guides; + GimpTransformFunction inside_function; + GimpTransformFunction outside_function; + gboolean use_corner_handles; + gboolean use_perspective_handles; + gboolean use_side_handles; + gboolean use_shear_handles; + gboolean use_center_handle; + gboolean use_pivot_handle; + gboolean dynamic_handle_size; + gboolean constrain_move; + gboolean constrain_scale; + gboolean constrain_rotate; + gboolean constrain_shear; + gboolean constrain_perspective; + gboolean frompivot_scale; + gboolean frompivot_shear; + gboolean frompivot_perspective; + gboolean cornersnap; + gboolean fixedpivot; + + gdouble curx; /* current x coord */ + gdouble cury; /* current y coord */ + + gdouble button_down; /* is the mouse button pressed */ + gdouble mousex; /* x coord where mouse was clicked */ + gdouble mousey; /* y coord where mouse was clicked */ + + gdouble cx, cy; /* center point (for moving) */ + + /* transformed handle coords */ + gdouble tx1, ty1; + gdouble tx2, ty2; + gdouble tx3, ty3; + gdouble tx4, ty4; + gdouble tcx, tcy; + gdouble tpx, tpy; + + /* previous transformed handle coords */ + gdouble prev_tx1, prev_ty1; + gdouble prev_tx2, prev_ty2; + gdouble prev_tx3, prev_ty3; + gdouble prev_tx4, prev_ty4; + gdouble prev_tcx, prev_tcy; + gdouble prev_tpx, prev_tpy; + + GimpTransformHandle handle; /* current tool activity */ + + GimpCanvasItem *guides; + GimpCanvasItem *handles[GIMP_N_TRANSFORM_HANDLES]; + GimpCanvasItem *center_items[2]; + GimpCanvasItem *pivot_items[2]; +}; + + +/* local function prototypes */ + +static void gimp_tool_transform_grid_constructed (GObject *object); +static void gimp_tool_transform_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_transform_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_transform_grid_changed (GimpToolWidget *widget); +static gint gimp_tool_transform_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_transform_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_transform_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_transform_grid_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_transform_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_transform_grid_leave_notify (GimpToolWidget *widget); +static void gimp_tool_transform_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_transform_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static GimpTransformHandle + gimp_tool_transform_grid_get_handle_for_coords + (GimpToolTransformGrid *grid, + const GimpCoords *coords); +static void gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid); +static void gimp_tool_transform_grid_update_box (GimpToolTransformGrid *grid); +static void gimp_tool_transform_grid_update_matrix (GimpToolTransformGrid *grid); +static void gimp_tool_transform_grid_calc_handles (GimpToolTransformGrid *grid, + gint *handle_w, + gint *handle_h); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolTransformGrid, gimp_tool_transform_grid, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_transform_grid_parent_class + + +static void +gimp_tool_transform_grid_class_init (GimpToolTransformGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_transform_grid_constructed; + object_class->set_property = gimp_tool_transform_grid_set_property; + object_class->get_property = gimp_tool_transform_grid_get_property; + + widget_class->changed = gimp_tool_transform_grid_changed; + widget_class->button_press = gimp_tool_transform_grid_button_press; + widget_class->button_release = gimp_tool_transform_grid_button_release; + widget_class->motion = gimp_tool_transform_grid_motion; + widget_class->hit = gimp_tool_transform_grid_hit; + widget_class->hover = gimp_tool_transform_grid_hover; + widget_class->leave_notify = gimp_tool_transform_grid_leave_notify; + widget_class->hover_modifier = gimp_tool_transform_grid_hover_modifier; + widget_class->get_cursor = gimp_tool_transform_grid_get_cursor; + widget_class->update_on_scale = TRUE; + + g_object_class_install_property (object_class, PROP_TRANSFORM, + gimp_param_spec_matrix3 ("transform", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_X, + g_param_spec_double ("pivot-x", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIVOT_Y, + g_param_spec_double ("pivot-y", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GUIDE_TYPE, + g_param_spec_enum ("guide-type", NULL, NULL, + GIMP_TYPE_GUIDES_TYPE, + GIMP_GUIDES_NONE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_N_GUIDES, + g_param_spec_int ("n-guides", NULL, NULL, + 1, 128, 4, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CLIP_GUIDES, + g_param_spec_boolean ("clip-guides", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SHOW_GUIDES, + g_param_spec_boolean ("show-guides", NULL, NULL, + TRUE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_INSIDE_FUNCTION, + g_param_spec_enum ("inside-function", + NULL, NULL, + GIMP_TYPE_TRANSFORM_FUNCTION, + GIMP_TRANSFORM_FUNCTION_MOVE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OUTSIDE_FUNCTION, + g_param_spec_enum ("outside-function", + NULL, NULL, + GIMP_TYPE_TRANSFORM_FUNCTION, + GIMP_TRANSFORM_FUNCTION_ROTATE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_CORNER_HANDLES, + g_param_spec_boolean ("use-corner-handles", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_PERSPECTIVE_HANDLES, + g_param_spec_boolean ("use-perspective-handles", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_SIDE_HANDLES, + g_param_spec_boolean ("use-side-handles", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_SHEAR_HANDLES, + g_param_spec_boolean ("use-shear-handles", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_CENTER_HANDLE, + g_param_spec_boolean ("use-center-handle", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_USE_PIVOT_HANDLE, + g_param_spec_boolean ("use-pivot-handle", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DYNAMIC_HANDLE_SIZE, + g_param_spec_boolean ("dynamic-handle-size", + NULL, NULL, + TRUE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_MOVE, + g_param_spec_boolean ("constrain-move", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_SCALE, + g_param_spec_boolean ("constrain-scale", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_ROTATE, + g_param_spec_boolean ("constrain-rotate", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_SHEAR, + g_param_spec_boolean ("constrain-shear", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CONSTRAIN_PERSPECTIVE, + g_param_spec_boolean ("constrain-perspective", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FROMPIVOT_SCALE, + g_param_spec_boolean ("frompivot-scale", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FROMPIVOT_SHEAR, + g_param_spec_boolean ("frompivot-shear", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FROMPIVOT_PERSPECTIVE, + g_param_spec_boolean ("frompivot-perspective", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_CORNERSNAP, + g_param_spec_boolean ("cornersnap", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FIXEDPIVOT, + g_param_spec_boolean ("fixedpivot", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tool_transform_grid_init (GimpToolTransformGrid *grid) +{ + grid->private = gimp_tool_transform_grid_get_instance_private (grid); +} + +static void +gimp_tool_transform_grid_constructed (GObject *object) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolTransformGridPrivate *private = grid->private; + GimpCanvasGroup *stroke_group; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + private->guides = gimp_tool_widget_add_transform_guides (widget, + &private->transform, + private->x1, + private->y1, + private->x2, + private->y2, + private->guide_type, + private->n_guides, + private->clip_guides); + + for (i = 0; i < 4; i++) + { + /* draw the scale handles */ + private->handles[GIMP_TRANSFORM_HANDLE_NW + i] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_SQUARE, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + + /* draw the perspective handles */ + private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_DIAMOND, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + + /* draw the side handles */ + private->handles[GIMP_TRANSFORM_HANDLE_N + i] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_SQUARE, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + + /* draw the shear handles */ + private->handles[GIMP_TRANSFORM_HANDLE_N_S + i] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_FILLED_DIAMOND, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + } + + /* draw the rotation center axis handle */ + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + private->handles[GIMP_TRANSFORM_HANDLE_PIVOT] = + GIMP_CANVAS_ITEM (stroke_group); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->pivot_items[0] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + private->pivot_items[1] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CROSS, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + + gimp_tool_widget_pop_group (widget); + + /* draw the center handle */ + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + private->handles[GIMP_TRANSFORM_HANDLE_CENTER] = + GIMP_CANVAS_ITEM (stroke_group); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->center_items[0] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_SQUARE, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + private->center_items[1] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CROSS, + 0, 0, 10, 10, + GIMP_HANDLE_ANCHOR_CENTER); + + gimp_tool_widget_pop_group (widget); + + gimp_tool_transform_grid_changed (widget); +} + +static void +gimp_tool_transform_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object); + GimpToolTransformGridPrivate *private = grid->private; + gboolean box = FALSE; + + switch (property_id) + { + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_boxed (value); + + if (transform) + private->transform = *transform; + else + gimp_matrix3_identity (&private->transform); + } + break; + + case PROP_X1: + private->x1 = g_value_get_double (value); + box = TRUE; + break; + case PROP_Y1: + private->y1 = g_value_get_double (value); + box = TRUE; + break; + case PROP_X2: + private->x2 = g_value_get_double (value); + box = TRUE; + break; + case PROP_Y2: + private->y2 = g_value_get_double (value); + box = TRUE; + break; + + case PROP_PIVOT_X: + private->pivot_x = g_value_get_double (value); + break; + case PROP_PIVOT_Y: + private->pivot_y = g_value_get_double (value); + break; + + case PROP_GUIDE_TYPE: + private->guide_type = g_value_get_enum (value); + break; + case PROP_N_GUIDES: + private->n_guides = g_value_get_int (value); + break; + case PROP_CLIP_GUIDES: + private->clip_guides = g_value_get_boolean (value); + break; + case PROP_SHOW_GUIDES: + private->show_guides = g_value_get_boolean (value); + break; + + case PROP_INSIDE_FUNCTION: + private->inside_function = g_value_get_enum (value); + break; + case PROP_OUTSIDE_FUNCTION: + private->outside_function = g_value_get_enum (value); + break; + + case PROP_USE_CORNER_HANDLES: + private->use_corner_handles = g_value_get_boolean (value); + break; + case PROP_USE_PERSPECTIVE_HANDLES: + private->use_perspective_handles = g_value_get_boolean (value); + break; + case PROP_USE_SIDE_HANDLES: + private->use_side_handles = g_value_get_boolean (value); + break; + case PROP_USE_SHEAR_HANDLES: + private->use_shear_handles = g_value_get_boolean (value); + break; + case PROP_USE_CENTER_HANDLE: + private->use_center_handle = g_value_get_boolean (value); + break; + case PROP_USE_PIVOT_HANDLE: + private->use_pivot_handle = g_value_get_boolean (value); + break; + + case PROP_DYNAMIC_HANDLE_SIZE: + private->dynamic_handle_size = g_value_get_boolean (value); + break; + + case PROP_CONSTRAIN_MOVE: + private->constrain_move = g_value_get_boolean (value); + break; + case PROP_CONSTRAIN_SCALE: + private->constrain_scale = g_value_get_boolean (value); + break; + case PROP_CONSTRAIN_ROTATE: + private->constrain_rotate = g_value_get_boolean (value); + break; + case PROP_CONSTRAIN_SHEAR: + private->constrain_shear = g_value_get_boolean (value); + break; + case PROP_CONSTRAIN_PERSPECTIVE: + private->constrain_perspective = g_value_get_boolean (value); + break; + + case PROP_FROMPIVOT_SCALE: + private->frompivot_scale = g_value_get_boolean (value); + break; + case PROP_FROMPIVOT_SHEAR: + private->frompivot_shear = g_value_get_boolean (value); + break; + case PROP_FROMPIVOT_PERSPECTIVE: + private->frompivot_perspective = g_value_get_boolean (value); + break; + + case PROP_CORNERSNAP: + private->cornersnap = g_value_get_boolean (value); + break; + case PROP_FIXEDPIVOT: + private->fixedpivot = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } + + if (box) + { + private->cx = (private->x1 + private->x2) / 2.0; + private->cy = (private->y1 + private->y2) / 2.0; + } +} + +static void +gimp_tool_transform_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object); + GimpToolTransformGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_TRANSFORM: + g_value_set_boxed (value, &private->transform); + break; + + case PROP_X1: + g_value_set_double (value, private->x1); + break; + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + case PROP_X2: + g_value_set_double (value, private->x2); + break; + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_PIVOT_X: + g_value_set_double (value, private->pivot_x); + break; + case PROP_PIVOT_Y: + g_value_set_double (value, private->pivot_y); + break; + + case PROP_GUIDE_TYPE: + g_value_set_enum (value, private->guide_type); + break; + case PROP_N_GUIDES: + g_value_set_int (value, private->n_guides); + break; + case PROP_CLIP_GUIDES: + g_value_set_boolean (value, private->clip_guides); + break; + case PROP_SHOW_GUIDES: + g_value_set_boolean (value, private->show_guides); + break; + + case PROP_INSIDE_FUNCTION: + g_value_set_enum (value, private->inside_function); + break; + case PROP_OUTSIDE_FUNCTION: + g_value_set_enum (value, private->outside_function); + break; + + case PROP_USE_CORNER_HANDLES: + g_value_set_boolean (value, private->use_corner_handles); + break; + case PROP_USE_PERSPECTIVE_HANDLES: + g_value_set_boolean (value, private->use_perspective_handles); + break; + case PROP_USE_SIDE_HANDLES: + g_value_set_boolean (value, private->use_side_handles); + break; + case PROP_USE_SHEAR_HANDLES: + g_value_set_boolean (value, private->use_shear_handles); + break; + case PROP_USE_CENTER_HANDLE: + g_value_set_boolean (value, private->use_center_handle); + break; + case PROP_USE_PIVOT_HANDLE: + g_value_set_boolean (value, private->use_pivot_handle); + break; + + case PROP_DYNAMIC_HANDLE_SIZE: + g_value_set_boolean (value, private->dynamic_handle_size); + break; + + case PROP_CONSTRAIN_MOVE: + g_value_set_boolean (value, private->constrain_move); + break; + case PROP_CONSTRAIN_SCALE: + g_value_set_boolean (value, private->constrain_scale); + break; + case PROP_CONSTRAIN_ROTATE: + g_value_set_boolean (value, private->constrain_rotate); + break; + case PROP_CONSTRAIN_SHEAR: + g_value_set_boolean (value, private->constrain_shear); + break; + case PROP_CONSTRAIN_PERSPECTIVE: + g_value_set_boolean (value, private->constrain_perspective); + break; + + case PROP_FROMPIVOT_SCALE: + g_value_set_boolean (value, private->frompivot_scale); + break; + case PROP_FROMPIVOT_SHEAR: + g_value_set_boolean (value, private->frompivot_shear); + break; + case PROP_FROMPIVOT_PERSPECTIVE: + g_value_set_boolean (value, private->frompivot_perspective); + break; + + case PROP_CORNERSNAP: + g_value_set_boolean (value, private->cornersnap); + break; + case PROP_FIXEDPIVOT: + g_value_set_boolean (value, private->fixedpivot); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +transform_is_convex (GimpVector2 *pos) +{ + return gimp_transform_polygon_is_convex (pos[0].x, pos[0].y, + pos[1].x, pos[1].y, + pos[2].x, pos[2].y, + pos[3].x, pos[3].y); +} + +static gboolean +transform_grid_is_convex (GimpToolTransformGrid *grid) +{ + GimpToolTransformGridPrivate *private = grid->private; + + return gimp_transform_polygon_is_convex (private->tx1, private->ty1, + private->tx2, private->ty2, + private->tx3, private->ty3, + private->tx4, private->ty4); +} + +static inline gboolean +vectorisnull (GimpVector2 v) +{ + return ((v.x == 0.0) && (v.y == 0.0)); +} + +static inline gdouble +dotprod (GimpVector2 a, + GimpVector2 b) +{ + return a.x * b.x + a.y * b.y; +} + +static inline gdouble +norm (GimpVector2 a) +{ + return sqrt (dotprod (a, a)); +} + +static inline GimpVector2 +vectorsubtract (GimpVector2 a, + GimpVector2 b) +{ + GimpVector2 c; + + c.x = a.x - b.x; + c.y = a.y - b.y; + + return c; +} + +static inline GimpVector2 +vectoradd (GimpVector2 a, + GimpVector2 b) +{ + GimpVector2 c; + + c.x = a.x + b.x; + c.y = a.y + b.y; + + return c; +} + +static inline GimpVector2 +scalemult (GimpVector2 a, + gdouble b) +{ + GimpVector2 c; + + c.x = a.x * b; + c.y = a.y * b; + + return c; +} + +static inline GimpVector2 +vectorproject (GimpVector2 a, + GimpVector2 b) +{ + return scalemult (b, dotprod (a, b) / dotprod (b, b)); +} + +/* finds the clockwise angle between the vectors given, 0-2π */ +static inline gdouble +calcangle (GimpVector2 a, + GimpVector2 b) +{ + gdouble angle, angle2; + gdouble length; + + if (vectorisnull (a) || vectorisnull (b)) + return 0.0; + + length = norm (a) * norm (b); + + angle = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0)); + angle2 = b.y; + b.y = -b.x; + b.x = angle2; + angle2 = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0)); + + return ((angle2 > G_PI / 2.0) ? angle : 2.0 * G_PI - angle); +} + +static inline GimpVector2 +rotate2d (GimpVector2 p, + gdouble angle) +{ + GimpVector2 ret; + + ret.x = cos (angle) * p.x-sin (angle) * p.y; + ret.y = sin (angle) * p.x+cos (angle) * p.y; + + return ret; +} + +static inline GimpVector2 +lineintersect (GimpVector2 p1, GimpVector2 p2, + GimpVector2 q1, GimpVector2 q2) +{ + gdouble denom, u; + GimpVector2 p; + + denom = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y); + if (denom == 0.0) + { + p.x = (p1.x + p2.x + q1.x + q2.x) / 4; + p.y = (p1.y + p2.y + q1.y + q2.y) / 4; + } + else + { + u = (q2.x - q1.x) * (p1.y - q1.y) - (q2.y - q1.y) * (p1.x - q1.x); + u /= denom; + + p.x = p1.x + u * (p2.x - p1.x); + p.y = p1.y + u * (p2.y - p1.y); + } + + return p; +} + +static inline GimpVector2 +get_pivot_delta (GimpToolTransformGrid *grid, + GimpVector2 *oldpos, + GimpVector2 *newpos, + GimpVector2 pivot) +{ + GimpToolTransformGridPrivate *private = grid->private; + GimpMatrix3 transform_before; + GimpMatrix3 transform_after; + GimpVector2 delta; + + gimp_matrix3_identity (&transform_before); + gimp_matrix3_identity (&transform_after); + + gimp_transform_matrix_perspective (&transform_before, + private->x1, + private->y1, + private->x2 - private->x1, + private->y2 - private->y1, + oldpos[0].x, oldpos[0].y, + oldpos[1].x, oldpos[1].y, + oldpos[2].x, oldpos[2].y, + oldpos[3].x, oldpos[3].y); + gimp_transform_matrix_perspective (&transform_after, + private->x1, + private->y1, + private->x2 - private->x1, + private->y2 - private->y1, + newpos[0].x, newpos[0].y, + newpos[1].x, newpos[1].y, + newpos[2].x, newpos[2].y, + newpos[3].x, newpos[3].y); + gimp_matrix3_invert (&transform_before); + gimp_matrix3_mult (&transform_after, &transform_before); + gimp_matrix3_transform_point (&transform_before, + pivot.x, pivot.y, &delta.x, &delta.y); + + delta = vectorsubtract (delta, pivot); + + return delta; +} + +static gboolean +point_is_inside_polygon (gint n, + gdouble *x, + gdouble *y, + gdouble px, + gdouble py) +{ + gint i, j; + gboolean odd = FALSE; + + for (i = 0, j = n - 1; i < n; j = i++) + { + if ((y[i] < py && y[j] >= py) || + (y[j] < py && y[i] >= py)) + { + if (x[i] + (py - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < px) + odd = !odd; + } + } + + return odd; +} + +static gboolean +point_is_inside_polygon_pos (GimpVector2 *pos, + GimpVector2 point) +{ + return point_is_inside_polygon (4, + (gdouble[4]){ pos[0].x, pos[1].x, + pos[3].x, pos[2].x }, + (gdouble[4]){ pos[0].y, pos[1].y, + pos[3].y, pos[2].y }, + point.x, point.y); +} + +static void +get_handle_geometry (GimpToolTransformGrid *grid, + GimpVector2 *position, + gdouble *angle) +{ + GimpToolTransformGridPrivate *private = grid->private; + + GimpVector2 o[] = { { .x = private->tx1, .y = private->ty1 }, + { .x = private->tx2, .y = private->ty2 }, + { .x = private->tx3, .y = private->ty3 }, + { .x = private->tx4, .y = private->ty4 } }; + GimpVector2 right = { .x = 1.0, .y = 0.0 }; + GimpVector2 up = { .x = 0.0, .y = 1.0 }; + + if (position) + { + position[0] = o[0]; + position[1] = o[1]; + position[2] = o[2]; + position[3] = o[3]; + } + + angle[0] = calcangle (vectorsubtract (o[1], o[0]), right); + angle[1] = calcangle (vectorsubtract (o[3], o[2]), right); + angle[2] = calcangle (vectorsubtract (o[3], o[1]), up); + angle[3] = calcangle (vectorsubtract (o[2], o[0]), up); + + angle[4] = (angle[0] + angle[3]) / 2.0; + angle[5] = (angle[0] + angle[2]) / 2.0; + angle[6] = (angle[1] + angle[3]) / 2.0; + angle[7] = (angle[1] + angle[2]) / 2.0; + + angle[8] = (angle[0] + angle[1] + angle[2] + angle[3]) / 4.0; +} + +static void +gimp_tool_transform_grid_changed (GimpToolWidget *widget) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + gdouble angle[9]; + GimpVector2 o[4], t[4]; + gint handle_w; + gint handle_h; + gint d, i; + + gimp_tool_transform_grid_update_box (grid); + + gimp_canvas_transform_guides_set (private->guides, + &private->transform, + private->x1, + private->y1, + private->x2, + private->y2, + private->guide_type, + private->n_guides, + private->clip_guides); + gimp_canvas_item_set_visible (private->guides, private->show_guides); + + get_handle_geometry (grid, o, angle); + gimp_tool_transform_grid_calc_handles (grid, &handle_w, &handle_h); + + for (i = 0; i < 4; i++) + { + GimpCanvasItem *h; + gdouble factor; + + /* the scale handles */ + factor = 1.0; + if (private->use_perspective_handles) + factor = 1.5; + + h = private->handles[GIMP_TRANSFORM_HANDLE_NW + i]; + gimp_canvas_item_set_visible (h, private->use_corner_handles); + + if (private->use_corner_handles) + { + gimp_canvas_handle_set_position (h, o[i].x, o[i].y); + gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor); + gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0); + } + + /* the perspective handles */ + factor = 1.0; + if (private->use_corner_handles) + factor = 0.8; + + h = private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i]; + gimp_canvas_item_set_visible (h, private->use_perspective_handles); + + if (private->use_perspective_handles) + { + gimp_canvas_handle_set_position (h, o[i].x, o[i].y); + gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor); + gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0); + } + } + + /* draw the side handles */ + t[0] = scalemult (vectoradd (o[0], o[1]), 0.5); + t[1] = scalemult (vectoradd (o[2], o[3]), 0.5); + t[2] = scalemult (vectoradd (o[1], o[3]), 0.5); + t[3] = scalemult (vectoradd (o[2], o[0]), 0.5); + + for (i = 0; i < 4; i++) + { + GimpCanvasItem *h; + + h = private->handles[GIMP_TRANSFORM_HANDLE_N + i]; + gimp_canvas_item_set_visible (h, private->use_side_handles); + + if (private->use_side_handles) + { + gimp_canvas_handle_set_position (h, t[i].x, t[i].y); + gimp_canvas_handle_set_size (h, handle_w, handle_h); + gimp_canvas_handle_set_angles (h, angle[i], 0.0); + } + } + + /* draw the shear handles */ + t[0] = scalemult (vectoradd ( o[0] , scalemult (o[1], 3.0)), + 0.25); + t[1] = scalemult (vectoradd (scalemult (o[2], 3.0), o[3] ), + 0.25); + t[2] = scalemult (vectoradd ( o[1] , scalemult (o[3], 3.0)), + 0.25); + t[3] = scalemult (vectoradd (scalemult (o[0], 3.0), o[2] ), + 0.25); + + for (i = 0; i < 4; i++) + { + GimpCanvasItem *h; + + h = private->handles[GIMP_TRANSFORM_HANDLE_N_S + i]; + gimp_canvas_item_set_visible (h, private->use_shear_handles); + + if (private->use_shear_handles) + { + gimp_canvas_handle_set_position (h, t[i].x, t[i].y); + gimp_canvas_handle_set_size (h, handle_w, handle_h); + gimp_canvas_handle_set_angles (h, angle[i], 0.0); + } + } + + d = MIN (handle_w, handle_h); + if (private->use_center_handle) + d *= 2; /* so you can grab it from under the center handle */ + + gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_PIVOT], + private->use_pivot_handle); + + if (private->use_pivot_handle) + { + gimp_canvas_handle_set_position (private->pivot_items[0], + private->tpx, private->tpy); + gimp_canvas_handle_set_size (private->pivot_items[0], d, d); + + gimp_canvas_handle_set_position (private->pivot_items[1], + private->tpx, private->tpy); + gimp_canvas_handle_set_size (private->pivot_items[1], d, d); + } + + d = MIN (handle_w, handle_h); + + gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_CENTER], + private->use_center_handle); + + if (private->use_center_handle) + { + gimp_canvas_handle_set_position (private->center_items[0], + private->tcx, private->tcy); + gimp_canvas_handle_set_size (private->center_items[0], d, d); + gimp_canvas_handle_set_angles (private->center_items[0], angle[8], 0.0); + + gimp_canvas_handle_set_position (private->center_items[1], + private->tcx, private->tcy); + gimp_canvas_handle_set_size (private->center_items[1], d, d); + gimp_canvas_handle_set_angles (private->center_items[1], angle[8], 0.0); + } + + gimp_tool_transform_grid_update_hilight (grid); +} + +gint +gimp_tool_transform_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + + private->button_down = TRUE; + private->mousex = coords->x; + private->mousey = coords->y; + + if (private->handle != GIMP_TRANSFORM_HANDLE_NONE) + { + if (private->handles[private->handle]) + { + GimpCanvasItem *handle; + gdouble x, y; + + switch (private->handle) + { + case GIMP_TRANSFORM_HANDLE_CENTER: + handle = private->center_items[0]; + break; + + case GIMP_TRANSFORM_HANDLE_PIVOT: + handle = private->pivot_items[0]; + break; + + default: + handle = private->handles[private->handle]; + break; + } + + gimp_canvas_handle_get_position (handle, &x, &y); + + gimp_tool_widget_set_snap_offsets (widget, + SIGNED_ROUND (x - coords->x), + SIGNED_ROUND (y - coords->y), + 0, 0); + } + else + { + gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0); + } + + private->prev_tx1 = private->tx1; + private->prev_ty1 = private->ty1; + private->prev_tx2 = private->tx2; + private->prev_ty2 = private->ty2; + private->prev_tx3 = private->tx3; + private->prev_ty3 = private->ty3; + private->prev_tx4 = private->tx4; + private->prev_ty4 = private->ty4; + private->prev_tpx = private->tpx; + private->prev_tpy = private->tpy; + private->prev_tcx = private->tcx; + private->prev_tcy = private->tcy; + + return private->handle; + } + + gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0); + + return 0; +} + +void +gimp_tool_transform_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + + private->button_down = FALSE; +} + +void +gimp_tool_transform_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + gdouble *x[4], *y[4]; + gdouble *newpivot_x, *newpivot_y; + + GimpVector2 oldpos[5], newpos[4]; + GimpVector2 cur = { .x = coords->x, + .y = coords->y }; + GimpVector2 mouse = { .x = private->mousex, + .y = private->mousey }; + GimpVector2 d; + GimpVector2 pivot; + + gboolean fixedpivot = private->fixedpivot; + GimpTransformHandle handle = private->handle; + gint i; + + private->curx = coords->x; + private->cury = coords->y; + + x[0] = &private->tx1; + y[0] = &private->ty1; + x[1] = &private->tx2; + y[1] = &private->ty2; + x[2] = &private->tx3; + y[2] = &private->ty3; + x[3] = &private->tx4; + y[3] = &private->ty4; + + newpos[0].x = oldpos[0].x = private->prev_tx1; + newpos[0].y = oldpos[0].y = private->prev_ty1; + newpos[1].x = oldpos[1].x = private->prev_tx2; + newpos[1].y = oldpos[1].y = private->prev_ty2; + newpos[2].x = oldpos[2].x = private->prev_tx3; + newpos[2].y = oldpos[2].y = private->prev_ty3; + newpos[3].x = oldpos[3].x = private->prev_tx4; + newpos[3].y = oldpos[3].y = private->prev_ty4; + + /* put center point in this array too */ + oldpos[4].x = private->prev_tcx; + oldpos[4].y = private->prev_tcy; + + d = vectorsubtract (cur, mouse); + + newpivot_x = &private->tpx; + newpivot_y = &private->tpy; + + if (private->use_pivot_handle) + { + pivot.x = private->prev_tpx; + pivot.y = private->prev_tpy; + } + else + { + /* when the transform grid doesn't use a pivot handle, use the center + * point as the pivot instead. + */ + pivot.x = private->prev_tcx; + pivot.y = private->prev_tcy; + + fixedpivot = TRUE; + } + + /* move */ + if (handle == GIMP_TRANSFORM_HANDLE_CENTER) + { + if (private->constrain_move) + { + /* snap to 45 degree vectors from starting point */ + gdouble angle = 16.0 * calcangle ((GimpVector2) { 1.0, 0.0 }, + d) / (2.0 * G_PI); + gdouble dist = norm (d) / sqrt (2); + + if (angle < 1.0 || angle >= 15.0) + d.y = 0; + else if (angle < 3.0) + d.y = -(d.x = dist); + else if (angle < 5.0) + d.x = 0; + else if (angle < 7.0) + d.x = d.y = -dist; + else if (angle < 9.0) + d.y = 0; + else if (angle < 11.0) + d.x = -(d.y = dist); + else if (angle < 13.0) + d.x = 0; + else if (angle < 15.0) + d.x = d.y = dist; + } + + for (i = 0; i < 4; i++) + newpos[i] = vectoradd (oldpos[i], d); + } + + /* rotate */ + if (handle == GIMP_TRANSFORM_HANDLE_ROTATION) + { + gdouble angle = calcangle (vectorsubtract (cur, pivot), + vectorsubtract (mouse, pivot)); + + if (private->constrain_rotate) + { + /* round to 15 degree multiple */ + angle /= 2 * G_PI / 24.0; + angle = round (angle); + angle *= 2 * G_PI / 24.0; + } + + for (i = 0; i < 4; i++) + newpos[i] = vectoradd (pivot, + rotate2d (vectorsubtract (oldpos[i], pivot), + angle)); + + fixedpivot = TRUE; + } + + /* move rotation axis */ + if (handle == GIMP_TRANSFORM_HANDLE_PIVOT) + { + pivot = vectoradd (pivot, d); + + if (private->cornersnap) + { + /* snap to corner points and center */ + gint closest = 0; + gdouble closest_dist = G_MAXDOUBLE, dist; + + for (i = 0; i < 5; i++) + { + dist = norm (vectorsubtract (pivot, oldpos[i])); + if (dist < closest_dist) + { + closest_dist = dist; + closest = i; + } + } + + if (closest_dist * + gimp_tool_widget_get_shell (widget)->scale_x < 50) + { + pivot = oldpos[closest]; + } + } + + fixedpivot = TRUE; + } + + /* scaling via corner */ + if (handle == GIMP_TRANSFORM_HANDLE_NW || + handle == GIMP_TRANSFORM_HANDLE_NE || + handle == GIMP_TRANSFORM_HANDLE_SE || + handle == GIMP_TRANSFORM_HANDLE_SW) + { + /* Scaling through scale handles means translating one corner point, + * with all sides at constant angles. + */ + + gint this, left, right, opposite; + + /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */ + if (handle == GIMP_TRANSFORM_HANDLE_NW) + { + this = 0; left = 1; right = 2; opposite = 3; + } + else if (handle == GIMP_TRANSFORM_HANDLE_NE) + { + this = 1; left = 3; right = 0; opposite = 2; + } + else if (handle == GIMP_TRANSFORM_HANDLE_SW) + { + this = 2; left = 0; right = 3; opposite = 1; + } + else if (handle == GIMP_TRANSFORM_HANDLE_SE) + { + this = 3; left = 2; right = 1; opposite = 0; + } + else + gimp_assert_not_reached (); + + /* when the keep aspect transformation constraint is enabled, + * the translation shall only be along the diagonal that runs + * trough this corner point. + */ + if (private->constrain_scale) + { + /* restrict to movement along the diagonal */ + GimpVector2 diag = vectorsubtract (oldpos[this], oldpos[opposite]); + + d = vectorproject (d, diag); + } + + /* Move the corner being interacted with */ + /* rp---------tp + * / /\ <- d, the interaction vector + * / / tp + * op----------/ + * + */ + newpos[this] = vectoradd (oldpos[this], d); + + /* Where the corner to the right and left would go, need these to form + * lines to intersect with the sides */ + /* rp----------/ + * /\ /\ + * / nr / nt + * op----------lp + * \ + * nl + */ + + newpos[right] = vectoradd (oldpos[right], d); + newpos[left] = vectoradd (oldpos[left], d); + + /* Now we just need to find the intersection of op-rp and nr-nt. + * rp----------/ + * / / + * / nr==========nt + * op----------/ + * + */ + newpos[right] = lineintersect (newpos[right], newpos[this], + oldpos[opposite], oldpos[right]); + newpos[left] = lineintersect (newpos[left], newpos[this], + oldpos[opposite], oldpos[left]); + /* /-----------/ + * / / + * rp============nt + * op----------/ + * + */ + + /* + * + * /--------------/ + * /--------------/ + * + */ + + if (private->frompivot_scale && + transform_is_convex (newpos) && + transform_is_convex (oldpos)) + { + /* transform the pivot point before the interaction and + * after, and move everything by this difference + */ + //TODO the handle doesn't actually end up where the mouse cursor is + GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot); + for (i = 0; i < 4; i++) + newpos[i] = vectorsubtract (newpos[i], delta); + + fixedpivot = TRUE; + } + } + + /* scaling via sides */ + if (handle == GIMP_TRANSFORM_HANDLE_N || + handle == GIMP_TRANSFORM_HANDLE_E || + handle == GIMP_TRANSFORM_HANDLE_S || + handle == GIMP_TRANSFORM_HANDLE_W) + { + gint this_l, this_r, opp_l, opp_r; + GimpVector2 side_l, side_r, midline; + + /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */ + if (handle == GIMP_TRANSFORM_HANDLE_N) + { + this_l = 1; this_r = 0; + } + else if (handle == GIMP_TRANSFORM_HANDLE_E) + { + this_l = 3; this_r = 1; + } + else if (handle == GIMP_TRANSFORM_HANDLE_S) + { + this_l = 2; this_r = 3; + } + else if (handle == GIMP_TRANSFORM_HANDLE_W) + { + this_l = 0; this_r = 2; + } + else + gimp_assert_not_reached (); + + opp_l = 3 - this_r; opp_r = 3 - this_l; + + side_l = vectorsubtract (oldpos[opp_l], oldpos[this_l]); + side_r = vectorsubtract (oldpos[opp_r], oldpos[this_r]); + midline = vectoradd (side_l, side_r); + + /* restrict to movement along the midline */ + d = vectorproject (d, midline); + + if (private->constrain_scale) + { + GimpVector2 before, after, effective_pivot = pivot; + gdouble distance; + + if (! private->frompivot_scale) + { + /* center of the opposite side is pivot */ + effective_pivot = scalemult (vectoradd (oldpos[opp_l], + oldpos[opp_r]), 0.5); + } + + /* get the difference between the distance from the pivot to + * where interaction started and the distance from the pivot + * to where cursor is now, and scale all corners distance + * from the pivot with this factor + */ + before = vectorsubtract (effective_pivot, mouse); + after = vectorsubtract (effective_pivot, cur); + after = vectorproject (after, before); + + distance = 0.5 * (after.x / before.x + after.y / before.y); + + for (i = 0; i < 4; i++) + newpos[i] = vectoradd (effective_pivot, + scalemult (vectorsubtract (oldpos[i], + effective_pivot), + distance)); + } + else + { + /* just move the side */ + newpos[this_l] = vectoradd (oldpos[this_l], d); + newpos[this_r] = vectoradd (oldpos[this_r], d); + } + + if (! private->constrain_scale && + private->frompivot_scale && + transform_is_convex (newpos) && + transform_is_convex (oldpos)) + { + GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot); + for (i = 0; i < 4; i++) + newpos[i] = vectorsubtract (newpos[i], delta); + + fixedpivot = TRUE; + } + } + + /* shear */ + if (handle == GIMP_TRANSFORM_HANDLE_N_S || + handle == GIMP_TRANSFORM_HANDLE_E_S || + handle == GIMP_TRANSFORM_HANDLE_S_S || + handle == GIMP_TRANSFORM_HANDLE_W_S) + { + gint this_l, this_r; + + /* set up indices for this edge and the opposite edge */ + if (handle == GIMP_TRANSFORM_HANDLE_N_S) + { + this_l = 1; this_r = 0; + } + else if (handle == GIMP_TRANSFORM_HANDLE_W_S) + { + this_l = 0; this_r = 2; + } + else if (handle == GIMP_TRANSFORM_HANDLE_S_S) + { + this_l = 2; this_r = 3; + } + else if (handle == GIMP_TRANSFORM_HANDLE_E_S) + { + this_l = 3; this_r = 1; + } + else + gimp_assert_not_reached (); + + if (private->constrain_shear) + { + /* restrict to movement along the side */ + GimpVector2 side = vectorsubtract (oldpos[this_r], oldpos[this_l]); + + d = vectorproject (d, side); + } + + newpos[this_l] = vectoradd (oldpos[this_l], d); + newpos[this_r] = vectoradd (oldpos[this_r], d); + + if (private->frompivot_shear && + transform_is_convex (newpos) && + transform_is_convex (oldpos)) + { + GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot); + for (i = 0; i < 4; i++) + newpos[i] = vectorsubtract (newpos[i], delta); + + fixedpivot = TRUE; + } + } + + /* perspective transform */ + if (handle == GIMP_TRANSFORM_HANDLE_NW_P || + handle == GIMP_TRANSFORM_HANDLE_NE_P || + handle == GIMP_TRANSFORM_HANDLE_SE_P || + handle == GIMP_TRANSFORM_HANDLE_SW_P) + { + gint this, left, right, opposite; + + /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */ + if (handle == GIMP_TRANSFORM_HANDLE_NW_P) + { + this = 0; left = 1; right = 2; opposite = 3; + } + else if (handle == GIMP_TRANSFORM_HANDLE_NE_P) + { + this = 1; left = 3; right = 0; opposite = 2; + } + else if (handle == GIMP_TRANSFORM_HANDLE_SW_P) + { + this = 2; left = 0; right = 3; opposite = 1; + } + else if (handle == GIMP_TRANSFORM_HANDLE_SE_P) + { + this = 3; left = 2; right = 1; opposite = 0; + } + else + gimp_assert_not_reached (); + + if (private->constrain_perspective) + { + /* when the constrain transformation constraint is enabled, + * the translation shall only be either along the side + * angles of the two sides that run to this corner point, or + * along the diagonal that runs trough this corner point. + */ + GimpVector2 proj[4]; + gdouble rej[4]; + + for (i = 0; i < 4; i++) + { + if (i == this) + continue; + + /* get the vectors along the sides and the diagonal */ + proj[i] = vectorsubtract (oldpos[this], oldpos[i]); + + /* project d on each candidate vector and see which has + * the shortest rejection + */ + proj[i] = vectorproject (d, proj[i]); + rej[i] = norm (vectorsubtract (d, proj[i])); + } + + if (rej[left] < rej[right] && rej[left] < rej[opposite]) + d = proj[left]; + else if (rej[right] < rej[opposite]) + d = proj[right]; + else + d = proj[opposite]; + } + + newpos[this] = vectoradd (oldpos[this], d); + + if (private->frompivot_perspective && + transform_is_convex (newpos) && + transform_is_convex (oldpos)) + { + GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot); + + for (i = 0; i < 4; i++) + newpos[i] = vectorsubtract (newpos[i], delta); + + fixedpivot = TRUE; + } + } + + /* this will have been set to TRUE if an operation used the pivot in + * addition to being a user option + */ + if (! fixedpivot && + transform_is_convex (newpos) && + transform_is_convex (oldpos) && + point_is_inside_polygon_pos (oldpos, pivot)) + { + GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot); + pivot = vectoradd (pivot, delta); + } + + /* make sure the new coordinates are valid */ + for (i = 0; i < 4; i++) + { + if (! isfinite (newpos[i].x) || ! isfinite (newpos[i].y)) + return; + } + + if (! isfinite (pivot.x) || ! isfinite (pivot.y)) + return; + + for (i = 0; i < 4; i++) + { + *x[i] = newpos[i].x; + *y[i] = newpos[i].y; + } + + /* set unconditionally: if options get toggled during operation, we + * have to move pivot back + */ + *newpivot_x = pivot.x; + *newpivot_y = pivot.y; + + gimp_tool_transform_grid_update_matrix (grid); +} + +static const gchar * +get_friendly_operation_name (GimpTransformHandle handle) +{ + switch (handle) + { + case GIMP_TRANSFORM_HANDLE_NONE: + return ""; + case GIMP_TRANSFORM_HANDLE_NW_P: + case GIMP_TRANSFORM_HANDLE_NE_P: + case GIMP_TRANSFORM_HANDLE_SW_P: + case GIMP_TRANSFORM_HANDLE_SE_P: + return _("Click-Drag to change perspective"); + case GIMP_TRANSFORM_HANDLE_NW: + case GIMP_TRANSFORM_HANDLE_NE: + case GIMP_TRANSFORM_HANDLE_SW: + case GIMP_TRANSFORM_HANDLE_SE: + return _("Click-Drag to scale"); + case GIMP_TRANSFORM_HANDLE_N: + case GIMP_TRANSFORM_HANDLE_S: + case GIMP_TRANSFORM_HANDLE_E: + case GIMP_TRANSFORM_HANDLE_W: + return _("Click-Drag to scale"); + case GIMP_TRANSFORM_HANDLE_CENTER: + return _("Click-Drag to move"); + case GIMP_TRANSFORM_HANDLE_PIVOT: + return _("Click-Drag to move the pivot point"); + case GIMP_TRANSFORM_HANDLE_N_S: + case GIMP_TRANSFORM_HANDLE_S_S: + case GIMP_TRANSFORM_HANDLE_E_S: + case GIMP_TRANSFORM_HANDLE_W_S: + return _("Click-Drag to shear"); + case GIMP_TRANSFORM_HANDLE_ROTATION: + return _("Click-Drag to rotate"); + default: + gimp_assert_not_reached (); + } +} + +static GimpTransformHandle +gimp_tool_transform_get_area_handle (GimpToolTransformGrid *grid, + const GimpCoords *coords, + GimpTransformFunction function) +{ + GimpToolTransformGridPrivate *private = grid->private; + GimpTransformHandle handle = GIMP_TRANSFORM_HANDLE_NONE; + + switch (function) + { + case GIMP_TRANSFORM_FUNCTION_NONE: + break; + + case GIMP_TRANSFORM_FUNCTION_MOVE: + handle = GIMP_TRANSFORM_HANDLE_CENTER; + break; + + case GIMP_TRANSFORM_FUNCTION_ROTATE: + handle = GIMP_TRANSFORM_HANDLE_ROTATION; + break; + + case GIMP_TRANSFORM_FUNCTION_SCALE: + case GIMP_TRANSFORM_FUNCTION_PERSPECTIVE: + { + gdouble closest_dist; + gdouble dist; + + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + private->tx1, + private->ty1); + closest_dist = dist; + if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE) + handle = GIMP_TRANSFORM_HANDLE_NW_P; + else + handle = GIMP_TRANSFORM_HANDLE_NW; + + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + private->tx2, + private->ty2); + if (dist < closest_dist) + { + closest_dist = dist; + if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE) + handle = GIMP_TRANSFORM_HANDLE_NE_P; + else + handle = GIMP_TRANSFORM_HANDLE_NE; + } + + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + private->tx3, + private->ty3); + if (dist < closest_dist) + { + closest_dist = dist; + if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE) + handle = GIMP_TRANSFORM_HANDLE_SW_P; + else + handle = GIMP_TRANSFORM_HANDLE_SW; + } + + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + private->tx4, + private->ty4); + if (dist < closest_dist) + { + closest_dist = dist; + if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE) + handle = GIMP_TRANSFORM_HANDLE_SE_P; + else + handle = GIMP_TRANSFORM_HANDLE_SE; + } + } + break; + + case GIMP_TRANSFORM_FUNCTION_SHEAR: + { + gdouble handle_x; + gdouble handle_y; + gdouble closest_dist; + gdouble dist; + + gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_N], + &handle_x, &handle_y); + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + handle_x, handle_y); + closest_dist = dist; + handle = GIMP_TRANSFORM_HANDLE_N_S; + + gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_W], + &handle_x, &handle_y); + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + handle_x, handle_y); + if (dist < closest_dist) + { + closest_dist = dist; + handle = GIMP_TRANSFORM_HANDLE_W_S; + } + + gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_E], + &handle_x, &handle_y); + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + handle_x, handle_y); + if (dist < closest_dist) + { + closest_dist = dist; + handle = GIMP_TRANSFORM_HANDLE_E_S; + } + + gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_S], + &handle_x, &handle_y); + dist = gimp_canvas_item_transform_distance_square (private->guides, + coords->x, coords->y, + handle_x, handle_y); + if (dist < closest_dist) + { + closest_dist = dist; + handle = GIMP_TRANSFORM_HANDLE_S_S; + } + } + break; + } + + return handle; +} + +GimpHit +gimp_tool_transform_grid_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpTransformHandle handle; + + handle = gimp_tool_transform_grid_get_handle_for_coords (grid, coords); + + if (handle != GIMP_TRANSFORM_HANDLE_NONE) + return GIMP_HIT_DIRECT; + + return GIMP_HIT_INDIRECT; +} + +void +gimp_tool_transform_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + GimpTransformHandle handle; + + handle = gimp_tool_transform_grid_get_handle_for_coords (grid, coords); + + if (handle == GIMP_TRANSFORM_HANDLE_NONE) + { + /* points passed in clockwise order */ + if (point_is_inside_polygon (4, + (gdouble[4]){ private->tx1, private->tx2, + private->tx4, private->tx3 }, + (gdouble[4]){ private->ty1, private->ty2, + private->ty4, private->ty3 }, + coords->x, coords->y)) + { + handle = gimp_tool_transform_get_area_handle (grid, coords, + private->inside_function); + } + else + { + handle = gimp_tool_transform_get_area_handle (grid, coords, + private->outside_function); + } + } + + if (handle != GIMP_TRANSFORM_HANDLE_NONE && proximity) + { + gimp_tool_widget_set_status (widget, + get_friendly_operation_name (handle)); + } + else + { + gimp_tool_widget_set_status (widget, NULL); + } + + private->handle = handle; + + gimp_tool_transform_grid_update_hilight (grid); +} + +void +gimp_tool_transform_grid_leave_notify (GimpToolWidget *widget) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + + private->handle = GIMP_TRANSFORM_HANDLE_NONE; + + gimp_tool_transform_grid_update_hilight (grid); + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static void +gimp_tool_transform_grid_modifier (GimpToolWidget *widget, + GdkModifierType key) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + + if (key == gimp_get_constrain_behavior_mask ()) + { + g_object_set (widget, + "frompivot-scale", ! private->frompivot_scale, + "frompivot-shear", ! private->frompivot_shear, + "frompivot-perspective", ! private->frompivot_perspective, + NULL); + } + else if (key == gimp_get_extend_selection_mask ()) + { + g_object_set (widget, + "cornersnap", ! private->cornersnap, + "constrain-move", ! private->constrain_move, + "constrain-scale", ! private->constrain_scale, + "constrain-rotate", ! private->constrain_rotate, + "constrain-shear", ! private->constrain_shear, + "constrain-perspective", ! private->constrain_perspective, + NULL); + } +} + +static void +gimp_tool_transform_grid_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + GimpCoords coords = { 0.0, }; + + gimp_tool_transform_grid_modifier (widget, key); + + if (private->button_down) + { + /* send a non-motion to update the grid with the new constraints */ + coords.x = private->curx; + coords.y = private->cury; + gimp_tool_transform_grid_motion (widget, &coords, 0, state); + } +} + +static gboolean +gimp_tool_transform_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget); + GimpToolTransformGridPrivate *private = grid->private; + gdouble angle[9]; + gint i; + GimpCursorType map[8]; + GimpVector2 pos[4], this, that; + gboolean flip = FALSE; + gboolean side = FALSE; + gboolean set_cursor = TRUE; + + map[0] = GIMP_CURSOR_CORNER_TOP_LEFT; + map[1] = GIMP_CURSOR_CORNER_TOP; + map[2] = GIMP_CURSOR_CORNER_TOP_RIGHT; + map[3] = GIMP_CURSOR_CORNER_RIGHT; + map[4] = GIMP_CURSOR_CORNER_BOTTOM_RIGHT; + map[5] = GIMP_CURSOR_CORNER_BOTTOM; + map[6] = GIMP_CURSOR_CORNER_BOTTOM_LEFT; + map[7] = GIMP_CURSOR_CORNER_LEFT; + + get_handle_geometry (grid, pos, angle); + + for (i = 0; i < 8; i++) + angle[i] = round (angle[i] * 180.0 / G_PI / 45.0); + + switch (private->handle) + { + case GIMP_TRANSFORM_HANDLE_NW_P: + case GIMP_TRANSFORM_HANDLE_NW: + i = (gint) angle[4] + 0; + this = pos[0]; + that = pos[3]; + break; + + case GIMP_TRANSFORM_HANDLE_NE_P: + case GIMP_TRANSFORM_HANDLE_NE: + i = (gint) angle[5] + 2; + this = pos[1]; + that = pos[2]; + break; + + case GIMP_TRANSFORM_HANDLE_SW_P: + case GIMP_TRANSFORM_HANDLE_SW: + i = (gint) angle[6] + 6; + this = pos[2]; + that = pos[1]; + break; + + case GIMP_TRANSFORM_HANDLE_SE_P: + case GIMP_TRANSFORM_HANDLE_SE: + i = (gint) angle[7] + 4; + this = pos[3]; + that = pos[0]; + break; + + case GIMP_TRANSFORM_HANDLE_N: + case GIMP_TRANSFORM_HANDLE_N_S: + i = (gint) angle[0] + 1; + this = vectoradd (pos[0], pos[1]); + that = vectoradd (pos[2], pos[3]); + side = TRUE; + break; + + case GIMP_TRANSFORM_HANDLE_S: + case GIMP_TRANSFORM_HANDLE_S_S: + i = (gint) angle[1] + 5; + this = vectoradd (pos[2], pos[3]); + that = vectoradd (pos[0], pos[1]); + side = TRUE; + break; + + case GIMP_TRANSFORM_HANDLE_E: + case GIMP_TRANSFORM_HANDLE_E_S: + i = (gint) angle[2] + 3; + this = vectoradd (pos[1], pos[3]); + that = vectoradd (pos[0], pos[2]); + side = TRUE; + break; + + case GIMP_TRANSFORM_HANDLE_W: + case GIMP_TRANSFORM_HANDLE_W_S: + i = (gint) angle[3] + 7; + this = vectoradd (pos[0], pos[2]); + that = vectoradd (pos[1], pos[3]); + side = TRUE; + break; + + default: + set_cursor = FALSE; + break; + } + + if (set_cursor) + { + i %= 8; + + switch (map[i]) + { + case GIMP_CURSOR_CORNER_TOP_LEFT: + if (this.x + this.y > that.x + that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_TOP: + if (this.y > that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_TOP_RIGHT: + if (this.x - this.y < that.x - that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_RIGHT: + if (this.x < that.x) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_BOTTOM_RIGHT: + if (this.x + this.y < that.x + that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_BOTTOM: + if (this.y < that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_BOTTOM_LEFT: + if (this.x - this.y > that.x - that.y) + flip = TRUE; + break; + case GIMP_CURSOR_CORNER_LEFT: + if (this.x > that.x) + flip = TRUE; + break; + default: + gimp_assert_not_reached (); + } + + if (flip) + *cursor = map[(i + 4) % 8]; + else + *cursor = map[i]; + + if (side) + *cursor += 8; + } + + /* parent class handles *cursor and *modifier for most handles */ + switch (private->handle) + { + case GIMP_TRANSFORM_HANDLE_NONE: + *tool_cursor = GIMP_TOOL_CURSOR_NONE; + break; + + case GIMP_TRANSFORM_HANDLE_NW_P: + case GIMP_TRANSFORM_HANDLE_NE_P: + case GIMP_TRANSFORM_HANDLE_SW_P: + case GIMP_TRANSFORM_HANDLE_SE_P: + *tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE; + break; + + case GIMP_TRANSFORM_HANDLE_NW: + case GIMP_TRANSFORM_HANDLE_NE: + case GIMP_TRANSFORM_HANDLE_SW: + case GIMP_TRANSFORM_HANDLE_SE: + case GIMP_TRANSFORM_HANDLE_N: + case GIMP_TRANSFORM_HANDLE_S: + case GIMP_TRANSFORM_HANDLE_E: + case GIMP_TRANSFORM_HANDLE_W: + *tool_cursor = GIMP_TOOL_CURSOR_RESIZE; + break; + + case GIMP_TRANSFORM_HANDLE_CENTER: + *tool_cursor = GIMP_TOOL_CURSOR_MOVE; + break; + + case GIMP_TRANSFORM_HANDLE_PIVOT: + *tool_cursor = GIMP_TOOL_CURSOR_ROTATE; + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + break; + + case GIMP_TRANSFORM_HANDLE_N_S: + case GIMP_TRANSFORM_HANDLE_S_S: + case GIMP_TRANSFORM_HANDLE_E_S: + case GIMP_TRANSFORM_HANDLE_W_S: + *tool_cursor = GIMP_TOOL_CURSOR_SHEAR; + break; + + case GIMP_TRANSFORM_HANDLE_ROTATION: + *tool_cursor = GIMP_TOOL_CURSOR_ROTATE; + break; + + default: + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +static GimpTransformHandle +gimp_tool_transform_grid_get_handle_for_coords (GimpToolTransformGrid *grid, + const GimpCoords *coords) +{ + GimpToolTransformGridPrivate *private = grid->private; + GimpTransformHandle i; + + for (i = GIMP_TRANSFORM_HANDLE_NONE + 1; i < GIMP_N_TRANSFORM_HANDLES; i++) + { + if (private->handles[i] && + gimp_canvas_item_hit (private->handles[i], coords->x, coords->y)) + { + return i; + } + } + + return GIMP_TRANSFORM_HANDLE_NONE; +} + +static void +gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid) +{ + GimpToolTransformGridPrivate *private = grid->private; + GimpTransformHandle handle; + + for (handle = GIMP_TRANSFORM_HANDLE_NONE; + handle < GIMP_N_TRANSFORM_HANDLES; + handle++) + { + if (private->handles[handle]) + { + gimp_canvas_item_set_highlight (private->handles[handle], + handle == private->handle); + } + } +} + +static void +gimp_tool_transform_grid_update_box (GimpToolTransformGrid *grid) +{ + GimpToolTransformGridPrivate *private = grid->private; + + gimp_matrix3_transform_point (&private->transform, + private->x1, private->y1, + &private->tx1, &private->ty1); + gimp_matrix3_transform_point (&private->transform, + private->x2, private->y1, + &private->tx2, &private->ty2); + gimp_matrix3_transform_point (&private->transform, + private->x1, private->y2, + &private->tx3, &private->ty3); + gimp_matrix3_transform_point (&private->transform, + private->x2, private->y2, + &private->tx4, &private->ty4); + + /* don't transform pivot */ + private->tpx = private->pivot_x; + private->tpy = private->pivot_y; + + if (transform_grid_is_convex (grid)) + { + gimp_matrix3_transform_point (&private->transform, + (private->x1 + private->x2) / 2.0, + (private->y1 + private->y2) / 2.0, + &private->tcx, &private->tcy); + } + else + { + private->tcx = (private->tx1 + + private->tx2 + + private->tx3 + + private->tx4) / 4.0; + private->tcy = (private->ty1 + + private->ty2 + + private->ty3 + + private->ty4) / 4.0; + } +} + +static void +gimp_tool_transform_grid_update_matrix (GimpToolTransformGrid *grid) +{ + GimpToolTransformGridPrivate *private = grid->private; + + gimp_matrix3_identity (&private->transform); + gimp_transform_matrix_perspective (&private->transform, + private->x1, + private->y1, + private->x2 - private->x1, + private->y2 - private->y1, + private->tx1, + private->ty1, + private->tx2, + private->ty2, + private->tx3, + private->ty3, + private->tx4, + private->ty4); + + private->pivot_x = private->tpx; + private->pivot_y = private->tpy; + + g_object_freeze_notify (G_OBJECT (grid)); + g_object_notify (G_OBJECT (grid), "transform"); + g_object_notify (G_OBJECT (grid), "pivot-x"); + g_object_notify (G_OBJECT (grid), "pivot-x"); + g_object_thaw_notify (G_OBJECT (grid)); +} + +static void +gimp_tool_transform_grid_calc_handles (GimpToolTransformGrid *grid, + gint *handle_w, + gint *handle_h) +{ + GimpToolTransformGridPrivate *private = grid->private; + gint dx1, dy1; + gint dx2, dy2; + gint dx3, dy3; + gint dx4, dy4; + gint x1, y1; + gint x2, y2; + + if (! private->dynamic_handle_size) + { + *handle_w = GIMP_CANVAS_HANDLE_SIZE_LARGE; + *handle_h = GIMP_CANVAS_HANDLE_SIZE_LARGE; + + return; + } + + gimp_canvas_item_transform_xy (private->guides, + private->tx1, private->ty1, + &dx1, &dy1); + gimp_canvas_item_transform_xy (private->guides, + private->tx2, private->ty2, + &dx2, &dy2); + gimp_canvas_item_transform_xy (private->guides, + private->tx3, private->ty3, + &dx3, &dy3); + gimp_canvas_item_transform_xy (private->guides, + private->tx4, private->ty4, + &dx4, &dy4); + + x1 = MIN4 (dx1, dx2, dx3, dx4); + y1 = MIN4 (dy1, dy2, dy3, dy4); + x2 = MAX4 (dx1, dx2, dx3, dx4); + y2 = MAX4 (dy1, dy2, dy3, dy4); + + *handle_w = CLAMP ((x2 - x1) / 3, + MIN_HANDLE_SIZE, GIMP_CANVAS_HANDLE_SIZE_LARGE); + *handle_h = CLAMP ((y2 - y1) / 3, + MIN_HANDLE_SIZE, GIMP_CANVAS_HANDLE_SIZE_LARGE); +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_transform_grid_new (GimpDisplayShell *shell, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_TRANSFORM_GRID, + "shell", shell, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} + + +/* protected functions */ + +GimpTransformHandle +gimp_tool_transform_grid_get_handle (GimpToolTransformGrid *grid) +{ + g_return_val_if_fail (GIMP_IS_TOOL_TRANSFORM_GRID (grid), + GIMP_TRANSFORM_HANDLE_NONE); + + return grid->private->handle; +} diff --git a/app/display/gimptooltransformgrid.h b/app/display/gimptooltransformgrid.h new file mode 100644 index 0000000..949beb2 --- /dev/null +++ b/app/display/gimptooltransformgrid.h @@ -0,0 +1,99 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptooltransformgrid.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_TRANSFORM_GRID_H__ +#define __GIMP_TOOL_TRANSFORM_GRID_H__ + + +#include "gimpcanvashandle.h" +#include "gimptoolwidget.h" + + +#define GIMP_TOOL_TRANSFORM_GRID_MAX_HANDLE_SIZE \ + (1.5 * GIMP_CANVAS_HANDLE_SIZE_LARGE) + + +typedef enum +{ + GIMP_TRANSFORM_HANDLE_NONE, + GIMP_TRANSFORM_HANDLE_NW_P, /* north west perspective */ + GIMP_TRANSFORM_HANDLE_NE_P, /* north east perspective */ + GIMP_TRANSFORM_HANDLE_SW_P, /* south west perspective */ + GIMP_TRANSFORM_HANDLE_SE_P, /* south east perspective */ + GIMP_TRANSFORM_HANDLE_NW, /* north west */ + GIMP_TRANSFORM_HANDLE_NE, /* north east */ + GIMP_TRANSFORM_HANDLE_SW, /* south west */ + GIMP_TRANSFORM_HANDLE_SE, /* south east */ + GIMP_TRANSFORM_HANDLE_N, /* north */ + GIMP_TRANSFORM_HANDLE_S, /* south */ + GIMP_TRANSFORM_HANDLE_E, /* east */ + GIMP_TRANSFORM_HANDLE_W, /* west */ + GIMP_TRANSFORM_HANDLE_CENTER, /* center for moving */ + GIMP_TRANSFORM_HANDLE_PIVOT, /* pivot for rotation and scaling */ + GIMP_TRANSFORM_HANDLE_N_S, /* north shearing */ + GIMP_TRANSFORM_HANDLE_S_S, /* south shearing */ + GIMP_TRANSFORM_HANDLE_E_S, /* east shearing */ + GIMP_TRANSFORM_HANDLE_W_S, /* west shearing */ + GIMP_TRANSFORM_HANDLE_ROTATION, /* rotation */ + + GIMP_N_TRANSFORM_HANDLES /* keep this last so *handles[] is the right size */ +} GimpTransformHandle; + + +#define GIMP_TYPE_TOOL_TRANSFORM_GRID (gimp_tool_transform_grid_get_type ()) +#define GIMP_TOOL_TRANSFORM_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGrid)) +#define GIMP_TOOL_TRANSFORM_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGridClass)) +#define GIMP_IS_TOOL_TRANSFORM_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_TRANSFORM_GRID)) +#define GIMP_IS_TOOL_TRANSFORM_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_TRANSFORM_GRID)) +#define GIMP_TOOL_TRANSFORM_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGridClass)) + + +typedef struct _GimpToolTransformGrid GimpToolTransformGrid; +typedef struct _GimpToolTransformGridPrivate GimpToolTransformGridPrivate; +typedef struct _GimpToolTransformGridClass GimpToolTransformGridClass; + +struct _GimpToolTransformGrid +{ + GimpToolWidget parent_instance; + + GimpToolTransformGridPrivate *private; +}; + +struct _GimpToolTransformGridClass +{ + GimpToolWidgetClass parent_class; +}; + + +GType gimp_tool_transform_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_transform_grid_new (GimpDisplayShell *shell, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + +/* protected functions */ + +GimpTransformHandle gimp_tool_transform_grid_get_handle (GimpToolTransformGrid *grid); + + +#endif /* __GIMP_TOOL_TRANSFORM_GRID_H__ */ diff --git a/app/display/gimptoolwidget.c b/app/display/gimptoolwidget.c new file mode 100644 index 0000000..516f366 --- /dev/null +++ b/app/display/gimptoolwidget.c @@ -0,0 +1,1117 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolwidget.c + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "display-types.h" + +#include "core/gimpmarshal.h" + +#include "gimpcanvasarc.h" +#include "gimpcanvascorner.h" +#include "gimpcanvasgroup.h" +#include "gimpcanvashandle.h" +#include "gimpcanvaslimit.h" +#include "gimpcanvasline.h" +#include "gimpcanvaspath.h" +#include "gimpcanvaspolygon.h" +#include "gimpcanvasrectangle.h" +#include "gimpcanvasrectangleguides.h" +#include "gimpcanvastransformguides.h" +#include "gimpdisplayshell.h" +#include "gimptoolwidget.h" + + +enum +{ + PROP_0, + PROP_SHELL, + PROP_ITEM +}; + +enum +{ + CHANGED, + RESPONSE, + SNAP_OFFSETS, + STATUS, + STATUS_COORDS, + MESSAGE, + FOCUS_CHANGED, + LAST_SIGNAL +}; + +struct _GimpToolWidgetPrivate +{ + GimpDisplayShell *shell; + GimpCanvasItem *item; + GList *group_stack; + + gint snap_offset_x; + gint snap_offset_y; + gint snap_width; + gint snap_height; + + gboolean visible; + gboolean focus; +}; + + +/* local function prototypes */ + +static void gimp_tool_widget_finalize (GObject *object); +static void gimp_tool_widget_constructed (GObject *object); +static void gimp_tool_widget_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_widget_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_tool_widget_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs); + +static void gimp_tool_widget_real_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_widget_real_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolWidget, gimp_tool_widget, GIMP_TYPE_OBJECT) + +#define parent_class gimp_tool_widget_parent_class + +static guint widget_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_tool_widget_class_init (GimpToolWidgetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_tool_widget_finalize; + object_class->constructed = gimp_tool_widget_constructed; + object_class->set_property = gimp_tool_widget_set_property; + object_class->get_property = gimp_tool_widget_get_property; + object_class->dispatch_properties_changed = gimp_tool_widget_properties_changed; + + klass->leave_notify = gimp_tool_widget_real_leave_notify; + klass->key_press = gimp_tool_widget_real_key_press; + + widget_signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + widget_signals[RESPONSE] = + g_signal_new ("response", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, response), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + widget_signals[SNAP_OFFSETS] = + g_signal_new ("snap-offsets", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, snap_offsets), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + + widget_signals[STATUS] = + g_signal_new ("status", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, status), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + widget_signals[STATUS_COORDS] = + g_signal_new ("status-coords", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, status_coords), + NULL, NULL, + gimp_marshal_VOID__STRING_DOUBLE_STRING_DOUBLE_STRING, + G_TYPE_NONE, 5, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_STRING); + + widget_signals[MESSAGE] = + g_signal_new ("message", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, message), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + widget_signals[FOCUS_CHANGED] = + g_signal_new ("focus-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolWidgetClass, focus_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, PROP_SHELL, + g_param_spec_object ("shell", + NULL, NULL, + GIMP_TYPE_DISPLAY_SHELL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_ITEM, + g_param_spec_object ("item", + NULL, NULL, + GIMP_TYPE_CANVAS_ITEM, + GIMP_PARAM_READABLE)); +} + +static void +gimp_tool_widget_init (GimpToolWidget *widget) +{ + widget->private = gimp_tool_widget_get_instance_private (widget); + + widget->private->visible = TRUE; +} + +static void +gimp_tool_widget_constructed (GObject *object) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolWidgetPrivate *private = widget->private; + GimpToolWidgetClass *klass = GIMP_TOOL_WIDGET_GET_CLASS (widget); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_DISPLAY_SHELL (private->shell)); + + private->item = gimp_canvas_group_new (private->shell); + + gimp_canvas_item_set_visible (private->item, private->visible); + + if (klass->changed) + { + if (klass->update_on_scale) + { + g_signal_connect_object (private->shell, "scaled", + G_CALLBACK (klass->changed), + widget, + G_CONNECT_SWAPPED); + } + + if (klass->update_on_scroll) + { + g_signal_connect_object (private->shell, "scrolled", + G_CALLBACK (klass->changed), + widget, + G_CONNECT_SWAPPED); + } + + if (klass->update_on_rotate) + { + g_signal_connect_object (private->shell, "rotated", + G_CALLBACK (klass->changed), + widget, + G_CONNECT_SWAPPED); + } + } +} + +static void +gimp_tool_widget_finalize (GObject *object) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolWidgetPrivate *private = widget->private; + + g_clear_object (&private->item); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tool_widget_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolWidgetPrivate *private = widget->private; + + switch (property_id) + { + case PROP_SHELL: + private->shell = g_value_get_object (value); /* don't ref */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_widget_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolWidgetPrivate *private = widget->private; + + switch (property_id) + { + case PROP_SHELL: + g_value_set_object (value, private->shell); + break; + + case PROP_ITEM: + g_value_set_object (value, private->item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_widget_properties_changed (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + + G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, + n_pspecs, + pspecs); + + gimp_tool_widget_changed (widget); +} + +static void +gimp_tool_widget_real_leave_notify (GimpToolWidget *widget) +{ + gimp_tool_widget_set_status (widget, NULL); +} + +static gboolean +gimp_tool_widget_real_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + switch (kevent->keyval) + { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_CONFIRM); + return TRUE; + + case GDK_KEY_Escape: + gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_CANCEL); + return TRUE; + + case GDK_KEY_BackSpace: + gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_RESET); + return TRUE; + + default: + break; + } + + return FALSE; +} + + +/* public functions */ + +GimpDisplayShell * +gimp_tool_widget_get_shell (GimpToolWidget *widget) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + return widget->private->shell; +} + +GimpCanvasItem * +gimp_tool_widget_get_item (GimpToolWidget *widget) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + return widget->private->item; +} + +void +gimp_tool_widget_set_visible (GimpToolWidget *widget, + gboolean visible) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + if (visible != widget->private->visible) + { + widget->private->visible = visible; + + if (widget->private->item) + gimp_canvas_item_set_visible (widget->private->item, visible); + + if (! visible) + gimp_tool_widget_set_status (widget, NULL); + } +} + +gboolean +gimp_tool_widget_get_visible (GimpToolWidget *widget) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); + + return widget->private->visible; +} + +void +gimp_tool_widget_set_focus (GimpToolWidget *widget, + gboolean focus) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + if (focus != widget->private->focus) + { + widget->private->focus = focus; + + g_signal_emit (widget, widget_signals[FOCUS_CHANGED], 0); + } +} + +gboolean +gimp_tool_widget_get_focus (GimpToolWidget *widget) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); + + return widget->private->focus; +} + +void +gimp_tool_widget_changed (GimpToolWidget *widget) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + g_signal_emit (widget, widget_signals[CHANGED], 0); +} + +void +gimp_tool_widget_response (GimpToolWidget *widget, + gint response_id) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + g_signal_emit (widget, widget_signals[RESPONSE], 0, + response_id); +} + +void +gimp_tool_widget_set_snap_offsets (GimpToolWidget *widget, + gint offset_x, + gint offset_y, + gint width, + gint height) +{ + GimpToolWidgetPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + private = widget->private; + + if (offset_x != private->snap_offset_x || + offset_y != private->snap_offset_y || + width != private->snap_width || + height != private->snap_height) + { + private->snap_offset_x = offset_x; + private->snap_offset_y = offset_y; + private->snap_width = width; + private->snap_height = height; + + g_signal_emit (widget, widget_signals[SNAP_OFFSETS], 0, + offset_x, offset_y, width, height); + } +} + +void +gimp_tool_widget_get_snap_offsets (GimpToolWidget *widget, + gint *offset_x, + gint *offset_y, + gint *width, + gint *height) +{ + GimpToolWidgetPrivate *private; + + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + private = widget->private; + + if (offset_x) *offset_x = private->snap_offset_x; + if (offset_y) *offset_y = private->snap_offset_y; + if (width) *width = private->snap_width; + if (height) *height = private->snap_height; +} + +void +gimp_tool_widget_set_status (GimpToolWidget *widget, + const gchar *status) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + g_signal_emit (widget, widget_signals[STATUS], 0, + status); +} + +void +gimp_tool_widget_set_status_coords (GimpToolWidget *widget, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + g_signal_emit (widget, widget_signals[STATUS_COORDS], 0, + title, x, separator, y, help); +} + +void +gimp_tool_widget_message (GimpToolWidget *widget, + const gchar *format, + ...) +{ + va_list args; + gchar *message; + + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (format != NULL); + + va_start (args, format); + + message = g_strdup_vprintf (format, args); + + va_end (args); + + gimp_tool_widget_message_literal (widget, message); + + g_free (message); +} + +void +gimp_tool_widget_message_literal (GimpToolWidget *widget, + const gchar *message) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (message != NULL); + + g_signal_emit (widget, widget_signals[MESSAGE], 0, + message); +} + +void +gimp_tool_widget_add_item (GimpToolWidget *widget, + GimpCanvasItem *item) +{ + GimpCanvasGroup *group; + + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + group = GIMP_CANVAS_GROUP (widget->private->item); + + if (widget->private->group_stack) + group = widget->private->group_stack->data; + + gimp_canvas_group_add_item (group, item); +} + +void +gimp_tool_widget_remove_item (GimpToolWidget *widget, + GimpCanvasItem *item) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); + + gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (widget->private->item), + item); +} + +GimpCanvasGroup * +gimp_tool_widget_add_group (GimpToolWidget *widget) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_group_new (widget->private->shell); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return GIMP_CANVAS_GROUP (item); +} + +GimpCanvasGroup * +gimp_tool_widget_add_stroke_group (GimpToolWidget *widget) +{ + GimpCanvasGroup *group; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + group = gimp_tool_widget_add_group (widget); + gimp_canvas_group_set_group_stroking (group, TRUE); + + return group; +} + +GimpCanvasGroup * +gimp_tool_widget_add_fill_group (GimpToolWidget *widget) +{ + GimpCanvasGroup *group; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + group = gimp_tool_widget_add_group (widget); + gimp_canvas_group_set_group_filling (group, TRUE); + + return group; +} + +void +gimp_tool_widget_push_group (GimpToolWidget *widget, + GimpCanvasGroup *group) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); + + widget->private->group_stack = g_list_prepend (widget->private->group_stack, + group); +} + +void +gimp_tool_widget_pop_group (GimpToolWidget *widget) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (widget->private->group_stack != NULL); + + widget->private->group_stack = g_list_remove (widget->private->group_stack, + widget->private->group_stack->data); +} + +/** + * gimp_tool_widget_add_line: + * @widget: the #GimpToolWidget + * @x1: start point X in image coordinates + * @y1: start point Y in image coordinates + * @x2: end point X in image coordinates + * @y2: end point Y in image coordinates + * + * This function adds a #GimpCanvasLine to @widget. + **/ +GimpCanvasItem * +gimp_tool_widget_add_line (GimpToolWidget *widget, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_line_new (widget->private->shell, + x1, y1, x2, y2); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_rectangle (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean filled) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_rectangle_new (widget->private->shell, + x, y, width, height, filled); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_arc (GimpToolWidget *widget, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle, + gboolean filled) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_arc_new (widget->private->shell, + center_x, center_y, + radius_x, radius_y, + start_angle, slice_angle, + filled); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_limit (GimpToolWidget *widget, + GimpLimitType type, + gdouble x, + gdouble y, + gdouble radius, + gdouble aspect_ratio, + gdouble angle, + gboolean dashed) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_limit_new (widget->private->shell, + type, + x, y, + radius, + aspect_ratio, + angle, + dashed); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_polygon (GimpToolWidget *widget, + GimpMatrix3 *transform, + const GimpVector2 *points, + gint n_points, + gboolean filled) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + g_return_val_if_fail (points == NULL || n_points > 0, NULL); + + item = gimp_canvas_polygon_new (widget->private->shell, + points, n_points, + transform, filled); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_polygon_from_coords (GimpToolWidget *widget, + GimpMatrix3 *transform, + const GimpCoords *points, + gint n_points, + gboolean filled) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + g_return_val_if_fail (points == NULL || n_points > 0, NULL); + + item = gimp_canvas_polygon_new_from_coords (widget->private->shell, + points, n_points, + transform, filled); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_path (GimpToolWidget *widget, + const GimpBezierDesc *desc) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_path_new (widget->private->shell, + desc, 0, 0, FALSE, GIMP_PATH_STYLE_DEFAULT); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_handle (GimpToolWidget *widget, + GimpHandleType type, + gdouble x, + gdouble y, + gint width, + gint height, + GimpHandleAnchor anchor) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_handle_new (widget->private->shell, + type, anchor, x, y, width, height); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_corner (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpHandleAnchor anchor, + gint corner_width, + gint corner_height, + gboolean outside) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_corner_new (widget->private->shell, + x, y, width, height, + anchor, corner_width, corner_height, + outside); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_rectangle_guides (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_rectangle_guides_new (widget->private->shell, + x, y, width, height, type, 4); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +GimpCanvasItem * +gimp_tool_widget_add_transform_guides (GimpToolWidget *widget, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), NULL); + + item = gimp_canvas_transform_guides_new (widget->private->shell, + transform, x1, y1, x2, y2, + type, n_guides, clip); + + gimp_tool_widget_add_item (widget, item); + g_object_unref (item); + + return item; +} + +gint +gimp_tool_widget_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), 0); + g_return_val_if_fail (coords != NULL, 0); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_press) + { + return GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_press (widget, + coords, time, + state, + press_type); + } + + return 0; +} + +void +gimp_tool_widget_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (coords != NULL); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_release) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_release (widget, + coords, time, state, + release_type); + } +} + +void +gimp_tool_widget_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (coords != NULL); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion (widget, + coords, time, state); + } +} + +GimpHit +gimp_tool_widget_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), GIMP_HIT_NONE); + g_return_val_if_fail (coords != NULL, GIMP_HIT_NONE); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hit) + { + return GIMP_TOOL_WIDGET_GET_CLASS (widget)->hit (widget, + coords, state, + proximity); + } + + return GIMP_HIT_NONE; +} + +void +gimp_tool_widget_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + g_return_if_fail (coords != NULL); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover (widget, + coords, state, proximity); + } +} + +void +gimp_tool_widget_leave_notify (GimpToolWidget *widget) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify (widget); + } +} + +gboolean +gimp_tool_widget_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); + g_return_val_if_fail (kevent != NULL, FALSE); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_press) + { + return GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_press (widget, kevent); + } + + return FALSE; +} + +gboolean +gimp_tool_widget_key_release (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); + g_return_val_if_fail (kevent != NULL, FALSE); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_release) + { + return GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_release (widget, kevent); + } + + return FALSE; +} + +void +gimp_tool_widget_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion_modifier) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion_modifier (widget, + key, press, state); + } +} + +void +gimp_tool_widget_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover_modifier) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover_modifier (widget, + key, press, state); + } +} + +gboolean +gimp_tool_widget_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) + +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); + g_return_val_if_fail (coords != NULL, FALSE); + + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->get_cursor) + { + GimpCursorType my_cursor; + GimpToolCursorType my_tool_cursor; + GimpCursorModifier my_modifier; + + if (cursor) my_cursor = *cursor; + if (tool_cursor) my_tool_cursor = *tool_cursor; + if (modifier) my_modifier = *modifier; + + if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->get_cursor (widget, coords, + state, + &my_cursor, + &my_tool_cursor, + &my_modifier)) + { + if (cursor) *cursor = my_cursor; + if (tool_cursor) *tool_cursor = my_tool_cursor; + if (modifier) *modifier = my_modifier; + + return TRUE; + } + } + + return FALSE; +} diff --git a/app/display/gimptoolwidget.h b/app/display/gimptoolwidget.h new file mode 100644 index 0000000..742aa2a --- /dev/null +++ b/app/display/gimptoolwidget.h @@ -0,0 +1,318 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolwidget.h + * Copyright (C) 2017 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_WIDGET_H__ +#define __GIMP_TOOL_WIDGET_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TOOL_WIDGET_RESPONSE_CONFIRM -1 +#define GIMP_TOOL_WIDGET_RESPONSE_CANCEL -2 +#define GIMP_TOOL_WIDGET_RESPONSE_RESET -3 + + +#define GIMP_TYPE_TOOL_WIDGET (gimp_tool_widget_get_type ()) +#define GIMP_TOOL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_WIDGET, GimpToolWidget)) +#define GIMP_TOOL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_WIDGET, GimpToolWidgetClass)) +#define GIMP_IS_TOOL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_WIDGET)) +#define GIMP_IS_TOOL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_WIDGET)) +#define GIMP_TOOL_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_WIDGET, GimpToolWidgetClass)) + + +typedef struct _GimpToolWidgetPrivate GimpToolWidgetPrivate; +typedef struct _GimpToolWidgetClass GimpToolWidgetClass; + +struct _GimpToolWidget +{ + GimpObject parent_instance; + + GimpToolWidgetPrivate *private; +}; + +struct _GimpToolWidgetClass +{ + GimpObjectClass parent_class; + + /* signals */ + void (* changed) (GimpToolWidget *widget); + void (* response) (GimpToolWidget *widget, + gint response_id); + void (* snap_offsets) (GimpToolWidget *widget, + gint offset_x, + gint offset_y, + gint width, + gint height); + void (* status) (GimpToolWidget *widget, + const gchar *status); + void (* status_coords) (GimpToolWidget *widget, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help); + void (* message) (GimpToolWidget *widget, + const gchar *message); + void (* focus_changed) (GimpToolWidget *widget); + + /* virtual functions */ + gint (* button_press) (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); + void (* button_release) (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); + void (* motion) (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); + + GimpHit (* hit) (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); + void (* hover) (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); + void (* leave_notify) (GimpToolWidget *widget); + + gboolean (* key_press) (GimpToolWidget *widget, + GdkEventKey *kevent); + gboolean (* key_release) (GimpToolWidget *widget, + GdkEventKey *kevent); + + void (* motion_modifier) (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); + void (* hover_modifier) (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); + + gboolean (* get_cursor) (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + + gboolean update_on_scale; + gboolean update_on_scroll; + gboolean update_on_rotate; +}; + + +GType gimp_tool_widget_get_type (void) G_GNUC_CONST; + +GimpDisplayShell * gimp_tool_widget_get_shell (GimpToolWidget *widget); +GimpCanvasItem * gimp_tool_widget_get_item (GimpToolWidget *widget); + +void gimp_tool_widget_set_visible (GimpToolWidget *widget, + gboolean visible); +gboolean gimp_tool_widget_get_visible (GimpToolWidget *widget); + +void gimp_tool_widget_set_focus (GimpToolWidget *widget, + gboolean focus); +gboolean gimp_tool_widget_get_focus (GimpToolWidget *widget); + +/* for subclasses, to notify the handling tool + */ +void gimp_tool_widget_changed (GimpToolWidget *widget); + +void gimp_tool_widget_response (GimpToolWidget *widget, + gint response_id); + +void gimp_tool_widget_set_snap_offsets (GimpToolWidget *widget, + gint offset_x, + gint offset_y, + gint width, + gint height); +void gimp_tool_widget_get_snap_offsets (GimpToolWidget *widget, + gint *offset_x, + gint *offset_y, + gint *width, + gint *height); + +void gimp_tool_widget_set_status (GimpToolWidget *widget, + const gchar *status); +void gimp_tool_widget_set_status_coords (GimpToolWidget *widget, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help); + +void gimp_tool_widget_message (GimpToolWidget *widget, + const gchar *format, + ...) G_GNUC_PRINTF (2, 3); +void gimp_tool_widget_message_literal (GimpToolWidget *widget, + const gchar *message); + +/* for subclasses, to add and manage their items + */ +void gimp_tool_widget_add_item (GimpToolWidget *widget, + GimpCanvasItem *item); +void gimp_tool_widget_remove_item (GimpToolWidget *widget, + GimpCanvasItem *item); + +GimpCanvasGroup * gimp_tool_widget_add_group (GimpToolWidget *widget); +GimpCanvasGroup * gimp_tool_widget_add_stroke_group (GimpToolWidget *widget); +GimpCanvasGroup * gimp_tool_widget_add_fill_group (GimpToolWidget *widget); + +void gimp_tool_widget_push_group (GimpToolWidget *widget, + GimpCanvasGroup *group); +void gimp_tool_widget_pop_group (GimpToolWidget *widget); + +/* for subclasses, convenience functions to add specific items + */ +GimpCanvasItem * gimp_tool_widget_add_line (GimpToolWidget *widget, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); +GimpCanvasItem * gimp_tool_widget_add_rectangle (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gboolean filled); +GimpCanvasItem * gimp_tool_widget_add_arc (GimpToolWidget *widget, + gdouble center_x, + gdouble center_y, + gdouble radius_x, + gdouble radius_y, + gdouble start_angle, + gdouble slice_angle, + gboolean filled); +GimpCanvasItem * gimp_tool_widget_add_limit (GimpToolWidget *widget, + GimpLimitType type, + gdouble x, + gdouble y, + gdouble radius, + gdouble aspect_ratio, + gdouble angle, + gboolean dashed); +GimpCanvasItem * gimp_tool_widget_add_polygon (GimpToolWidget *widget, + GimpMatrix3 *transform, + const GimpVector2 *points, + gint n_points, + gboolean filled); +GimpCanvasItem * gimp_tool_widget_add_polygon_from_coords + (GimpToolWidget *widget, + GimpMatrix3 *transform, + const GimpCoords *points, + gint n_points, + gboolean filled); +GimpCanvasItem * gimp_tool_widget_add_path (GimpToolWidget *widget, + const GimpBezierDesc *desc); + +GimpCanvasItem * gimp_tool_widget_add_handle (GimpToolWidget *widget, + GimpHandleType type, + gdouble x, + gdouble y, + gint width, + gint height, + GimpHandleAnchor anchor); +GimpCanvasItem * gimp_tool_widget_add_corner (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpHandleAnchor anchor, + gint corner_width, + gint corner_height, + gboolean outside); + +GimpCanvasItem * gimp_tool_widget_add_rectangle_guides + (GimpToolWidget *widget, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GimpGuidesType type); +GimpCanvasItem * gimp_tool_widget_add_transform_guides + (GimpToolWidget *widget, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip); + +/* for tools, to be called from the respective GimpTool method + * implementations + */ +gint gimp_tool_widget_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +void gimp_tool_widget_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +void gimp_tool_widget_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); + +GimpHit gimp_tool_widget_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +void gimp_tool_widget_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +void gimp_tool_widget_leave_notify (GimpToolWidget *widget); + +gboolean gimp_tool_widget_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +gboolean gimp_tool_widget_key_release (GimpToolWidget *widget, + GdkEventKey *kevent); + +void gimp_tool_widget_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +void gimp_tool_widget_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); + +gboolean gimp_tool_widget_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + + +#endif /* __GIMP_TOOL_WIDGET_H__ */ diff --git a/app/display/gimptoolwidgetgroup.c b/app/display/gimptoolwidgetgroup.c new file mode 100644 index 0000000..d62f4c9 --- /dev/null +++ b/app/display/gimptoolwidgetgroup.c @@ -0,0 +1,731 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolwidgetgroup.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 . + */ + +#include "config.h" + +#include +#include + +#include "display-types.h" + +#include "core/gimpcontainer.h" +#include "core/gimplist.h" + +#include "gimpcanvasgroup.h" +#include "gimpdisplayshell.h" +#include "gimptoolwidgetgroup.h" + + +struct _GimpToolWidgetGroupPrivate +{ + GimpContainer *children; + + GimpToolWidget *focus_widget; + GimpToolWidget *hover_widget; + + gboolean auto_raise; +}; + + +/* local function prototypes */ + +static void gimp_tool_widget_group_finalize (GObject *object); + +static void gimp_tool_widget_group_focus_changed (GimpToolWidget *widget); +static gint gimp_tool_widget_group_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_widget_group_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_widget_group_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_widget_group_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_widget_group_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_widget_group_leave_notify (GimpToolWidget *widget); +static gboolean gimp_tool_widget_group_key_press (GimpToolWidget *widget, + GdkEventKey *kevent); +static gboolean gimp_tool_widget_group_key_release (GimpToolWidget *widget, + GdkEventKey *kevent); +static void gimp_tool_widget_group_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static void gimp_tool_widget_group_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_widget_group_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static void gimp_tool_widget_group_children_add (GimpContainer *container, + GimpToolWidget *child, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_children_remove (GimpContainer *container, + GimpToolWidget *child, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_children_reorder (GimpContainer *container, + GimpToolWidget *child, + gint new_index, + GimpToolWidgetGroup *group); + +static void gimp_tool_widget_group_child_changed (GimpToolWidget *child, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_response (GimpToolWidget *child, + gint response_id, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_snap_offsets (GimpToolWidget *child, + gint offset_x, + gint offset_y, + gint width, + gint height, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_status (GimpToolWidget *child, + const gchar *status, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_status_coords (GimpToolWidget *child, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_message (GimpToolWidget *child, + const gchar *message, + GimpToolWidgetGroup *group); +static void gimp_tool_widget_group_child_focus_changed (GimpToolWidget *child, + GimpToolWidgetGroup *group); + +static GimpToolWidget * gimp_tool_widget_group_get_hover_widget (GimpToolWidgetGroup *group, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpHit *hit); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolWidgetGroup, gimp_tool_widget_group, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_widget_group_parent_class + + +/* priv functions */ + + +static void +gimp_tool_widget_group_class_init (GimpToolWidgetGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->finalize = gimp_tool_widget_group_finalize; + + widget_class->focus_changed = gimp_tool_widget_group_focus_changed; + widget_class->button_press = gimp_tool_widget_group_button_press; + widget_class->button_release = gimp_tool_widget_group_button_release; + widget_class->motion = gimp_tool_widget_group_motion; + widget_class->hit = gimp_tool_widget_group_hit; + widget_class->hover = gimp_tool_widget_group_hover; + widget_class->leave_notify = gimp_tool_widget_group_leave_notify; + widget_class->key_press = gimp_tool_widget_group_key_press; + widget_class->key_release = gimp_tool_widget_group_key_release; + widget_class->motion_modifier = gimp_tool_widget_group_motion_modifier; + widget_class->hover_modifier = gimp_tool_widget_group_hover_modifier; + widget_class->get_cursor = gimp_tool_widget_group_get_cursor; +} + +static void +gimp_tool_widget_group_init (GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv; + + priv = group->priv = gimp_tool_widget_group_get_instance_private (group); + + priv->children = g_object_new (GIMP_TYPE_LIST, + "children-type", GIMP_TYPE_TOOL_WIDGET, + "append", TRUE, + NULL); + + g_signal_connect (priv->children, "add", + G_CALLBACK (gimp_tool_widget_group_children_add), + group); + g_signal_connect (priv->children, "remove", + G_CALLBACK (gimp_tool_widget_group_children_remove), + group); + g_signal_connect (priv->children, "reorder", + G_CALLBACK (gimp_tool_widget_group_children_reorder), + group); + + gimp_container_add_handler (priv->children, "changed", + G_CALLBACK (gimp_tool_widget_group_child_changed), + group); + gimp_container_add_handler (priv->children, "response", + G_CALLBACK (gimp_tool_widget_group_child_response), + group); + gimp_container_add_handler (priv->children, "snap-offsets", + G_CALLBACK (gimp_tool_widget_group_child_snap_offsets), + group); + gimp_container_add_handler (priv->children, "status", + G_CALLBACK (gimp_tool_widget_group_child_status), + group); + gimp_container_add_handler (priv->children, "status-coords", + G_CALLBACK (gimp_tool_widget_group_child_status_coords), + group); + gimp_container_add_handler (priv->children, "message", + G_CALLBACK (gimp_tool_widget_group_child_message), + group); + gimp_container_add_handler (priv->children, "focus-changed", + G_CALLBACK (gimp_tool_widget_group_child_focus_changed), + group); +} + +static void +gimp_tool_widget_group_finalize (GObject *object) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (object); + GimpToolWidgetGroupPrivate *priv = group->priv; + + g_clear_object (&priv->children); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint +gimp_tool_widget_group_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + gimp_tool_widget_group_hover (widget, coords, state, TRUE); + + if (priv->focus_widget != priv->hover_widget) + { + if (priv->hover_widget) + gimp_tool_widget_set_focus (priv->hover_widget, TRUE); + else if (priv->focus_widget) + gimp_tool_widget_set_focus (priv->focus_widget, FALSE); + } + + if (priv->hover_widget) + { + if (priv->auto_raise) + { + gimp_container_reorder (priv->children, + GIMP_OBJECT (priv->hover_widget), -1); + } + + return gimp_tool_widget_button_press (priv->hover_widget, + coords, time, state, press_type); + } + + return FALSE; +} + +static void +gimp_tool_widget_group_focus_changed (GimpToolWidget *widget) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->focus_widget) + { + GimpToolWidget *focus_widget = priv->focus_widget; + + gimp_tool_widget_set_focus (priv->focus_widget, + gimp_tool_widget_get_focus (widget)); + + priv->focus_widget = focus_widget; + } +} + +static void +gimp_tool_widget_group_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->hover_widget) + { + gimp_tool_widget_button_release (priv->hover_widget, + coords, time, state, release_type); + } +} + +static void +gimp_tool_widget_group_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->hover_widget) + gimp_tool_widget_motion (priv->hover_widget, coords, time, state); +} + +static GimpHit +gimp_tool_widget_group_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpHit hit; + + gimp_tool_widget_group_get_hover_widget (group, + coords, state, proximity, + &hit); + + return hit; +} + +static void +gimp_tool_widget_group_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *hover_widget; + + hover_widget = + gimp_tool_widget_group_get_hover_widget (group, + coords, state, proximity, + NULL); + + if (priv->hover_widget && priv->hover_widget != hover_widget) + gimp_tool_widget_leave_notify (priv->hover_widget); + + priv->hover_widget = hover_widget; + + if (priv->hover_widget) + gimp_tool_widget_hover (priv->hover_widget, coords, state, proximity); +} + +static void +gimp_tool_widget_group_leave_notify (GimpToolWidget *widget) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->hover_widget) + { + gimp_tool_widget_leave_notify (priv->hover_widget); + + priv->hover_widget = NULL; + } + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static gboolean +gimp_tool_widget_group_key_press (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->focus_widget) + return gimp_tool_widget_key_press (priv->focus_widget, kevent); + + return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent); +} + +static gboolean +gimp_tool_widget_group_key_release (GimpToolWidget *widget, + GdkEventKey *kevent) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->focus_widget) + return gimp_tool_widget_key_release (priv->focus_widget, kevent); + + return FALSE; +} + +static void +gimp_tool_widget_group_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->hover_widget) + gimp_tool_widget_motion_modifier (priv->hover_widget, key, press, state); +} + +static void +gimp_tool_widget_group_hover_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + GList *iter; + + for (iter = g_queue_peek_head_link (GIMP_LIST (priv->children)->queue); + iter; + iter = g_list_next (iter)) + { + gimp_tool_widget_hover_modifier (iter->data, key, press, state); + } +} + +static gboolean +gimp_tool_widget_group_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolWidgetGroup *group = GIMP_TOOL_WIDGET_GROUP (widget); + GimpToolWidgetGroupPrivate *priv = group->priv; + + if (priv->hover_widget) + { + return gimp_tool_widget_get_cursor (priv->hover_widget, + coords, state, + cursor, tool_cursor, modifier); + } + + return FALSE; +} + +static void +gimp_tool_widget_group_children_add (GimpContainer *container, + GimpToolWidget *child, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + GimpCanvasGroup *canvas_group; + + canvas_group = GIMP_CANVAS_GROUP (gimp_tool_widget_get_item (widget)); + + gimp_canvas_group_add_item (canvas_group, + gimp_tool_widget_get_item (child)); + + if (gimp_tool_widget_get_focus (child) && priv->focus_widget) + { + gimp_tool_widget_set_focus (priv->focus_widget, FALSE); + + priv->focus_widget = NULL; + } + + if (! priv->focus_widget) + { + priv->focus_widget = child; + + gimp_tool_widget_set_focus (child, gimp_tool_widget_get_focus (widget)); + } + + gimp_tool_widget_changed (widget); +} + +static void +gimp_tool_widget_group_children_remove (GimpContainer *container, + GimpToolWidget *child, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + GimpCanvasGroup *canvas_group; + + canvas_group = GIMP_CANVAS_GROUP (gimp_tool_widget_get_item (widget)); + + if (priv->focus_widget == child) + { + gimp_tool_widget_set_focus (child, FALSE); + + priv->focus_widget = NULL; + } + + if (priv->hover_widget == child) + { + gimp_tool_widget_leave_notify (child); + + priv->hover_widget = NULL; + } + + if (! priv->focus_widget) + { + priv->focus_widget = + GIMP_TOOL_WIDGET (gimp_container_get_last_child (container)); + + if (priv->focus_widget) + gimp_tool_widget_set_focus (priv->focus_widget, TRUE); + } + + gimp_canvas_group_remove_item (canvas_group, + gimp_tool_widget_get_item (child)); + + gimp_tool_widget_changed (widget); +} + +static void +gimp_tool_widget_group_children_reorder (GimpContainer *container, + GimpToolWidget *child, + gint new_index, + GimpToolWidgetGroup *group) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + GimpCanvasGroup *canvas_group; + GList *iter; + + canvas_group = GIMP_CANVAS_GROUP (gimp_tool_widget_get_item (widget)); + + for (iter = g_queue_peek_head_link (GIMP_LIST (container)->queue); + iter; + iter = g_list_next (iter)) + { + GimpCanvasItem *item = gimp_tool_widget_get_item (iter->data); + + gimp_canvas_group_remove_item (canvas_group, item); + gimp_canvas_group_add_item (canvas_group, item); + } + + gimp_tool_widget_changed (widget); +} + +static void +gimp_tool_widget_group_child_changed (GimpToolWidget *child, + GimpToolWidgetGroup *group) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + gimp_tool_widget_changed (widget); +} + +static void +gimp_tool_widget_group_child_response (GimpToolWidget *child, + gint response_id, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (priv->focus_widget == child) + gimp_tool_widget_response (widget, response_id); +} + +static void +gimp_tool_widget_group_child_snap_offsets (GimpToolWidget *child, + gint offset_x, + gint offset_y, + gint width, + gint height, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (priv->hover_widget == child) + { + gimp_tool_widget_set_snap_offsets (widget, + offset_x, offset_y, width, height); + } +} + +static void +gimp_tool_widget_group_child_status (GimpToolWidget *child, + const gchar *status, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (priv->hover_widget == child) + gimp_tool_widget_set_status (widget, status); +} + +static void +gimp_tool_widget_group_child_status_coords (GimpToolWidget *child, + const gchar *title, + gdouble x, + const gchar *separator, + gdouble y, + const gchar *help, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (priv->hover_widget == child) + gimp_tool_widget_set_status_coords (widget, title, x, separator, y, help); +} + +static void +gimp_tool_widget_group_child_message (GimpToolWidget *child, + const gchar *message, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (priv->focus_widget == child) + gimp_tool_widget_message_literal (widget, message); +} + +static void +gimp_tool_widget_group_child_focus_changed (GimpToolWidget *child, + GimpToolWidgetGroup *group) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *widget = GIMP_TOOL_WIDGET (group); + + if (gimp_tool_widget_get_focus (child)) + { + if (priv->focus_widget && priv->focus_widget != child) + gimp_tool_widget_set_focus (priv->focus_widget, FALSE); + + priv->focus_widget = child; + + gimp_tool_widget_set_focus (widget, TRUE); + } + else + { + if (priv->focus_widget == child) + priv->focus_widget = NULL; + } +} + +static GimpToolWidget * +gimp_tool_widget_group_get_hover_widget (GimpToolWidgetGroup *group, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpHit *hit) +{ + GimpToolWidgetGroupPrivate *priv = group->priv; + GimpToolWidget *indirect_child = NULL; + gboolean indirect = FALSE; + GList *iter; + + for (iter = g_queue_peek_tail_link (GIMP_LIST (priv->children)->queue); + iter; + iter = g_list_previous (iter)) + { + GimpToolWidget *child = iter->data; + + switch (gimp_tool_widget_hit (child, coords, state, proximity)) + { + case GIMP_HIT_DIRECT: + if (hit) *hit = GIMP_HIT_DIRECT; + + return child; + + case GIMP_HIT_INDIRECT: + if (! indirect || child == priv->focus_widget) + indirect_child = child; + else if (indirect_child != priv->focus_widget) + indirect_child = NULL; + + indirect = TRUE; + + break; + + case GIMP_HIT_NONE: + break; + } + } + + if (hit) *hit = indirect_child ? GIMP_HIT_INDIRECT : GIMP_HIT_NONE; + + return indirect_child; +} + + +/* public functions */ + + +GimpToolWidget * +gimp_tool_widget_group_new (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_WIDGET_GROUP, + "shell", shell, + NULL); +} + +GimpContainer * +gimp_tool_widget_group_get_children (GimpToolWidgetGroup *group) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET_GROUP (group), NULL); + + return group->priv->children; +} + +GimpToolWidget * +gimp_tool_widget_group_get_focus_widget (GimpToolWidgetGroup *group) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET_GROUP (group), NULL); + + return group->priv->focus_widget; +} + +void +gimp_tool_widget_group_set_auto_raise (GimpToolWidgetGroup *group, + gboolean auto_raise) +{ + g_return_if_fail (GIMP_IS_TOOL_WIDGET_GROUP (group)); + + group->priv->auto_raise = auto_raise; +} + +gboolean +gimp_tool_widget_group_get_auto_raise (GimpToolWidgetGroup *group) +{ + g_return_val_if_fail (GIMP_IS_TOOL_WIDGET_GROUP (group), FALSE); + + return group->priv->auto_raise; +} + diff --git a/app/display/gimptoolwidgetgroup.h b/app/display/gimptoolwidgetgroup.h new file mode 100644 index 0000000..0a8331f --- /dev/null +++ b/app/display/gimptoolwidgetgroup.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolwidgetgroup.h + * 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 . + */ + +#ifndef __GIMP_TOOL_WIDGET_GROUP_H__ +#define __GIMP_TOOL_WIDGET_GROUP_H__ + + +#include "gimptoolwidget.h" + + +#define GIMP_TYPE_TOOL_WIDGET_GROUP (gimp_tool_widget_group_get_type ()) +#define GIMP_TOOL_WIDGET_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_WIDGET_GROUP, GimpToolWidgetGroup)) +#define GIMP_TOOL_WIDGET_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_WIDGET_GROUP, GimpToolWidgetGroupClass)) +#define GIMP_IS_TOOL_WIDGET_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_WIDGET_GROUP)) +#define GIMP_IS_TOOL_WIDGET_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_WIDGET_GROUP)) +#define GIMP_TOOL_WIDGET_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_WIDGET_GROUP, GimpToolWidgetGroupClass)) + + +typedef struct _GimpToolWidgetGroupPrivate GimpToolWidgetGroupPrivate; +typedef struct _GimpToolWidgetGroupClass GimpToolWidgetGroupClass; + +struct _GimpToolWidgetGroup +{ + GimpToolWidget parent_instance; + + GimpToolWidgetGroupPrivate *priv; +}; + +struct _GimpToolWidgetGroupClass +{ + GimpToolWidgetClass parent_class; +}; + + +GType gimp_tool_widget_group_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_widget_group_new (GimpDisplayShell *shell); + +GimpContainer * gimp_tool_widget_group_get_children (GimpToolWidgetGroup *group); + +GimpToolWidget * gimp_tool_widget_group_get_focus_widget (GimpToolWidgetGroup *group); + +void gimp_tool_widget_group_set_auto_raise (GimpToolWidgetGroup *group, + gboolean auto_raise); +gboolean gimp_tool_widget_group_get_auto_raise (GimpToolWidgetGroup *group); + + +#endif /* __GIMP_TOOL_WIDGET_GROUP_H__ */ diff --git a/app/errors.c b/app/errors.c new file mode 100644 index 0000000..5baea9f --- /dev/null +++ b/app/errors.c @@ -0,0 +1,475 @@ +/* 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 . + */ + +#include "config.h" + +#define _GNU_SOURCE /* need the POSIX signal API */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimpitem.h" +#include "core/gimpparamspecs.h" + +#include "config/gimpcoreconfig.h" + +#include "pdb/gimppdb.h" + +#include "errors.h" +#include "gimp-log.h" + +#ifdef G_OS_WIN32 +#include +#endif + +/* private variables */ + +static Gimp *the_errors_gimp = NULL; +static gboolean use_debug_handler = FALSE; +static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_QUERY; +static gchar *full_prog_name = NULL; +static gchar *backtrace_file = NULL; +static gchar *backup_path = NULL; +static GimpLogHandler log_domain_handler = 0; +static guint global_handler_id = 0; + + +/* local function prototypes */ + +static void gimp_message_log_func (const gchar *log_domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data); +static void gimp_error_log_func (const gchar *domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data) G_GNUC_NORETURN; + +static G_GNUC_NORETURN void gimp_eek (const gchar *reason, + const gchar *message, + gboolean use_handler); + + +/* public functions */ + +void +errors_init (Gimp *gimp, + const gchar *_full_prog_name, + gboolean _use_debug_handler, + GimpStackTraceMode _stack_trace_mode, + const gchar *_backtrace_file) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (_full_prog_name != NULL); + g_return_if_fail (full_prog_name == NULL); + +#ifdef GIMP_UNSTABLE + g_printerr ("This is a development version of GIMP. " + "Debug messages may appear here.\n\n"); +#endif /* GIMP_UNSTABLE */ + + the_errors_gimp = gimp; + use_debug_handler = _use_debug_handler ? TRUE : FALSE; + stack_trace_mode = _stack_trace_mode; + full_prog_name = g_strdup (_full_prog_name); + + /* Create parent directories for both the crash and backup files. */ + backtrace_file = g_path_get_dirname (_backtrace_file); + backup_path = g_build_filename (gimp_directory (), "backups", NULL); + + g_mkdir_with_parents (backtrace_file, S_IRUSR | S_IWUSR | S_IXUSR); + g_free (backtrace_file); + backtrace_file = g_strdup (_backtrace_file); + + g_mkdir_with_parents (backup_path, S_IRUSR | S_IWUSR | S_IXUSR); + g_free (backup_path); + backup_path = g_build_filename (gimp_directory (), "backups", + "backup-XXX.xcf", NULL); + + log_domain_handler = gimp_log_set_handler (FALSE, + G_LOG_LEVEL_WARNING | + G_LOG_LEVEL_MESSAGE | + G_LOG_LEVEL_CRITICAL, + gimp_message_log_func, gimp); + + global_handler_id = g_log_set_handler (NULL, + G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL, + gimp_error_log_func, gimp); +} + +void +errors_exit (void) +{ + if (log_domain_handler) + { + gimp_log_remove_handler (log_domain_handler); + + log_domain_handler = 0; + } + + if (global_handler_id) + { + g_log_remove_handler (NULL, global_handler_id); + + global_handler_id = 0; + } + + the_errors_gimp = NULL; + + if (backtrace_file) + g_free (backtrace_file); + if (full_prog_name) + g_free (full_prog_name); + if (backup_path) + g_free (backup_path); +} + +GList * +errors_recovered (void) +{ + GList *recovered = NULL; + gchar *backup_path = g_build_filename (gimp_directory (), "backups", NULL); + GDir *backup_dir = NULL; + + if ((backup_dir = g_dir_open (backup_path, 0, NULL))) + { + const gchar *file; + + while ((file = g_dir_read_name (backup_dir))) + { + if (g_str_has_suffix (file, ".xcf")) + { + gchar *path = g_build_filename (backup_path, file, NULL); + + if (g_file_test (path, G_FILE_TEST_IS_REGULAR) && + ! g_file_test (path, G_FILE_TEST_IS_SYMLINK)) + { + /* A quick basic security check. It is not foolproof, + * but better than nothing to make sure we are not + * trying to read, then delete a folder or a symlink + * to a file outside the backup directory. + */ + recovered = g_list_append (recovered, path); + } + else + { + g_free (path); + } + } + } + + g_dir_close (backup_dir); + } + g_free (backup_path); + + return recovered; +} + +void +gimp_fatal_error (const gchar *message) +{ + gimp_eek ("fatal error", message, TRUE); +} + +void +gimp_terminate (const gchar *message) +{ + gimp_eek ("terminated", message, use_debug_handler); +} + + +/* private functions */ + +static void +gimp_message_log_func (const gchar *log_domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data) +{ + Gimp *gimp = data; + GimpCoreConfig *config = gimp->config; + const gchar *msg_domain = NULL; + GimpMessageSeverity severity = GIMP_MESSAGE_WARNING; + gboolean gui_message = TRUE; + GimpDebugPolicy debug_policy; + + /* All GIMP messages are processed under the same domain, but + * we need to keep the log domain information for third party + * messages. + */ + if (! log_domain || + (! g_str_has_prefix (log_domain, "Gimp") && + ! g_str_has_prefix (log_domain, "LibGimp"))) + msg_domain = log_domain; + + /* If debug policy requires it, WARNING and CRITICAL errors must be + * routed for appropriate debugging. + */ + g_object_get (G_OBJECT (config), + "debug-policy", &debug_policy, + NULL); + + switch (flags & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_WARNING: + severity = GIMP_MESSAGE_BUG_WARNING; + if (debug_policy > GIMP_DEBUG_POLICY_WARNING) + gui_message = FALSE; + break; + case G_LOG_LEVEL_CRITICAL: + severity = GIMP_MESSAGE_BUG_CRITICAL; + if (debug_policy > GIMP_DEBUG_POLICY_CRITICAL) + gui_message = FALSE; + break; + } + + if (gimp && gui_message) + { + gimp_show_message (gimp, NULL, severity, msg_domain, message); + } + else + { + const gchar *reason = "Message"; + + gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity, + NULL, NULL, &reason, NULL); + + g_printerr ("%s: %s-%s: %s\n", + gimp_filename_to_utf8 (full_prog_name), + log_domain, reason, message); + } +} + +static void +gimp_error_log_func (const gchar *domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data) +{ + gimp_fatal_error (message); +} + +static void +gimp_eek (const gchar *reason, + const gchar *message, + gboolean use_handler) +{ + GimpCoreConfig *config = the_errors_gimp->config; + gboolean eek_handled = FALSE; + GimpDebugPolicy debug_policy; + GList *iter; + gint num_idx; + gint i = 0; + + /* GIMP has 2 ways to handle termination signals and fatal errors: one + * is the stack trace mode which is set at start as command line + * option --stack-trace-mode, this won't change for the length of the + * session and outputs a trace in terminal; the other is set in + * preferences, outputs a trace in a GUI and can change anytime during + * the session. + * The GUI backtrace has priority if it is set. + */ + g_object_get (G_OBJECT (config), + "debug-policy", &debug_policy, + NULL); + + /* Let's just always output on stdout at least so that there is a + * trace if the rest fails. */ + g_printerr ("%s: %s: %s\n", full_prog_name, reason, message); + +#if ! defined (G_OS_WIN32) || defined (HAVE_EXCHNDL) + + if (use_handler) + { +#ifndef GIMP_CONSOLE_COMPILATION + if (debug_policy != GIMP_DEBUG_POLICY_NEVER && + ! the_errors_gimp->no_interface && + backtrace_file) + { + FILE *fd; + gboolean has_backtrace = TRUE; + + /* If GUI backtrace enabled (it is disabled by default), it + * takes precedence over the command line argument. + */ +#ifdef G_OS_WIN32 + const gchar *gimpdebug = "gimp-debug-tool-" GIMP_TOOL_VERSION ".exe"; +#elif defined (PLATFORM_OSX) + const gchar *gimpdebug = "gimp-debug-tool-" GIMP_TOOL_VERSION; +#else + const gchar *gimpdebug = LIBEXECDIR "/gimp-debug-tool-" GIMP_TOOL_VERSION; +#endif + gchar *args[9] = { (gchar *) gimpdebug, full_prog_name, NULL, + (gchar *) reason, (gchar *) message, + backtrace_file, the_errors_gimp->config->last_known_release, + NULL, NULL }; + gchar pid[16]; + gchar timestamp[16]; + + g_snprintf (pid, 16, "%u", (guint) getpid ()); + args[2] = pid; + + g_snprintf (timestamp, 16, "%lu", the_errors_gimp->config->last_release_timestamp); + args[7] = timestamp; + +#ifndef G_OS_WIN32 + /* On Win32, the trace has already been processed by ExcHnl + * and is waiting for us in a text file. + */ + fd = g_fopen (backtrace_file, "w"); + has_backtrace = gimp_stack_trace_print ((const gchar *) full_prog_name, + fd, NULL); + fclose (fd); +#endif + + /* We don't care about any return value. If it fails, too + * bad, we just won't have any stack trace. + * We still need to use the sync() variant because we have + * to keep GIMP up long enough for the debugger to get its + * trace. + */ + if (has_backtrace && + g_file_test (backtrace_file, G_FILE_TEST_IS_REGULAR) && + g_spawn_async (NULL, args, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, NULL)) + eek_handled = TRUE; + } +#endif /* !GIMP_CONSOLE_COMPILATION */ + +#ifndef G_OS_WIN32 + if (! eek_handled) + { + switch (stack_trace_mode) + { + case GIMP_STACK_TRACE_NEVER: + break; + + case GIMP_STACK_TRACE_QUERY: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + + if (the_errors_gimp) + gimp_gui_ungrab (the_errors_gimp); + + gimp_stack_trace_query ((const gchar *) full_prog_name); + } + break; + + case GIMP_STACK_TRACE_ALWAYS: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + + gimp_stack_trace_print ((const gchar *) full_prog_name, + stdout, NULL); + } + break; + + default: + break; + } + } +#endif /* ! G_OS_WIN32 */ + } +#endif /* ! G_OS_WIN32 || HAVE_EXCHNDL */ + +#if defined (G_OS_WIN32) && ! defined (GIMP_CONSOLE_COMPILATION) + /* g_on_error_* don't do anything reasonable on Win32. */ + if (! eek_handled && ! the_errors_gimp->no_interface) + MessageBox (NULL, g_strdup_printf ("%s: %s", reason, message), + full_prog_name, MB_OK|MB_ICONERROR); +#endif + + /* Let's try to back-up all unsaved images! + * It is not 100%: when I tested with various bugs created on purpose, + * I had cases where saving failed. I am not sure if this is because + * of some memory management along the way to XCF saving or some other + * messed up state of GIMP, but this is normal not to expect too much + * during a crash. + * Nevertheless in various test cases, I had successful backups XCF of + * the work in progress. Yeah! + */ + if (backup_path) + { + /* increase the busy counter, so XCF saving calling + * gimp_set_busy() and gimp_unset_busy() won't call the GUI + * layer and do whatever windowing system calls to set cursors. + */ + the_errors_gimp->busy++; + + /* The index of 'XXX' in backup_path string. */ + num_idx = strlen (backup_path) - 7; + + iter = gimp_get_image_iter (the_errors_gimp); + for (; iter && i < 1000; iter = iter->next) + { + GimpImage *image = iter->data; + GimpItem *item; + + if (! gimp_image_is_dirty (image)) + continue; + + item = GIMP_ITEM (gimp_image_get_active_drawable (image)); + + /* This is a trick because we want to avoid any memory + * allocation when the process is abnormally terminated. + * We just assume that you'll never have more than 1000 images + * open (which is already far fetched). + */ + backup_path[num_idx + 2] = '0' + (i % 10); + backup_path[num_idx + 1] = '0' + ((i/10) % 10); + backup_path[num_idx] = '0' + ((i/100) % 10); + + /* Saving. */ + gimp_pdb_execute_procedure_by_name (the_errors_gimp->pdb, + gimp_get_user_context (the_errors_gimp), + NULL, NULL, + "gimp-xcf-save", + GIMP_TYPE_INT32, 0, + GIMP_TYPE_IMAGE_ID, gimp_image_get_ID (image), + GIMP_TYPE_DRAWABLE_ID, gimp_item_get_ID (item), + G_TYPE_STRING, backup_path, + G_TYPE_STRING, backup_path, + G_TYPE_NONE); + i++; + } + } + + exit (EXIT_FAILURE); +} diff --git a/app/errors.h b/app/errors.h new file mode 100644 index 0000000..0c306da --- /dev/null +++ b/app/errors.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 . + */ + +#ifndef __ERRORS_H__ +#define __ERRORS_H__ + +#ifndef GIMP_APP_GLUE_COMPILATION +#error You must not #include "errors.h" from an app/ subdir +#endif + + +void errors_init (Gimp *gimp, + const gchar *full_prog_name, + gboolean use_debug_handler, + GimpStackTraceMode stack_trace_mode, + const gchar *backtrace_file); +void errors_exit (void); + +GList * errors_recovered (void); + +void gimp_fatal_error (const gchar *message) G_GNUC_NORETURN; +void gimp_terminate (const gchar *message) G_GNUC_NORETURN; + + +#endif /* __ERRORS_H__ */ diff --git a/app/file-data/Makefile.am b/app/file-data/Makefile.am new file mode 100644 index 0000000..ba0fd57 --- /dev/null +++ b/app/file-data/Makefile.am @@ -0,0 +1,24 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-File-Data\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappfile-data.a + +libappfile_data_a_SOURCES = \ + file-data.c \ + file-data.h \ + file-data-gbr.c \ + file-data-gbr.h \ + file-data-gih.c \ + file-data-gih.h \ + file-data-pat.c \ + file-data-pat.h diff --git a/app/file-data/Makefile.in b/app/file-data/Makefile.in new file mode 100644 index 0000000..5f3e362 --- /dev/null +++ b/app/file-data/Makefile.in @@ -0,0 +1,934 @@ +# 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 = app/file-data +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 = +libappfile_data_a_AR = $(AR) $(ARFLAGS) +libappfile_data_a_LIBADD = +am_libappfile_data_a_OBJECTS = file-data.$(OBJEXT) \ + file-data-gbr.$(OBJEXT) file-data-gih.$(OBJEXT) \ + file-data-pat.$(OBJEXT) +libappfile_data_a_OBJECTS = $(am_libappfile_data_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)/file-data-gbr.Po \ + ./$(DEPDIR)/file-data-gih.Po ./$(DEPDIR)/file-data-pat.Po \ + ./$(DEPDIR)/file-data.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 = $(libappfile_data_a_SOURCES) +DIST_SOURCES = $(libappfile_data_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-File-Data\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappfile-data.a +libappfile_data_a_SOURCES = \ + file-data.c \ + file-data.h \ + file-data-gbr.c \ + file-data-gbr.h \ + file-data-gih.c \ + file-data-gih.h \ + file-data-pat.c \ + file-data-pat.h + +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 app/file-data/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/file-data/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) + +libappfile-data.a: $(libappfile_data_a_OBJECTS) $(libappfile_data_a_DEPENDENCIES) $(EXTRA_libappfile_data_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappfile-data.a + $(AM_V_AR)$(libappfile_data_a_AR) libappfile-data.a $(libappfile_data_a_OBJECTS) $(libappfile_data_a_LIBADD) + $(AM_V_at)$(RANLIB) libappfile-data.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-data-gbr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-data-gih.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-data-pat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-data.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)/file-data-gbr.Po + -rm -f ./$(DEPDIR)/file-data-gih.Po + -rm -f ./$(DEPDIR)/file-data-pat.Po + -rm -f ./$(DEPDIR)/file-data.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)/file-data-gbr.Po + -rm -f ./$(DEPDIR)/file-data-gih.Po + -rm -f ./$(DEPDIR)/file-data-pat.Po + -rm -f ./$(DEPDIR)/file-data.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/app/file-data/file-data-gbr.c b/app/file-data/file-data-gbr.c new file mode 100644 index 0000000..63d4c0e --- /dev/null +++ b/app/file-data/file-data-gbr.c @@ -0,0 +1,417 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpbrush.h" +#include "core/gimpbrush-load.h" +#include "core/gimpbrush-private.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimplayer-new.h" +#include "core/gimpimage-resize.h" +#include "core/gimpparamspecs.h" +#include "core/gimptempbuf.h" + +#include "pdb/gimpprocedure.h" + +#include "file-data-gbr.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static GimpImage * file_gbr_brush_to_image (Gimp *gimp, + GimpBrush *brush); +static GimpBrush * file_gbr_image_to_brush (GimpImage *image, + GimpDrawable *drawable, + const gchar *name, + gdouble spacing); + + +/* public functions */ + +GimpValueArray * +file_gbr_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image = NULL; + const gchar *uri; + GFile *file; + GInputStream *input; + GError *my_error = NULL; + + gimp_set_busy (gimp); + + uri = g_value_get_string (gimp_value_array_index (args, 1)); + file = g_file_new_for_uri (uri); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + + if (input) + { + GimpBrush *brush = gimp_brush_load_brush (context, file, input, error); + + if (brush) + { + image = file_gbr_brush_to_image (gimp, brush); + g_object_unref (brush); + } + + g_object_unref (input); + } + else + { + g_propagate_prefixed_error (error, my_error, + _("Could not open '%s' for reading: "), + gimp_file_get_utf8_name (file)); + } + + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, image != NULL, + error ? *error : NULL); + + if (image) + gimp_value_set_image (gimp_value_array_index (return_vals, 1), image); + + gimp_unset_busy (gimp); + + return return_vals; +} + +GimpValueArray * +file_gbr_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image; + GimpDrawable *drawable; + GimpBrush *brush; + const gchar *uri; + const gchar *name; + GFile *file; + gint spacing; + gboolean success; + + gimp_set_busy (gimp); + + image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); + drawable = gimp_value_get_drawable (gimp_value_array_index (args, 2), gimp); + uri = g_value_get_string (gimp_value_array_index (args, 3)); + spacing = g_value_get_int (gimp_value_array_index (args, 5)); + name = g_value_get_string (gimp_value_array_index (args, 6)); + + file = g_file_new_for_uri (uri); + + brush = file_gbr_image_to_brush (image, drawable, name, spacing); + + gimp_data_set_file (GIMP_DATA (brush), file, TRUE, TRUE); + + success = gimp_data_save (GIMP_DATA (brush), error); + + g_object_unref (brush); + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, success, + error ? *error : NULL); + + gimp_unset_busy (gimp); + + return return_vals; +} + +GimpLayer * +file_gbr_brush_to_layer (GimpImage *image, + GimpBrush *brush) +{ + GimpLayer *layer; + const Babl *format; + gboolean alpha; + gint width; + gint height; + gint image_width; + gint image_height; + GimpTempBuf *mask; + GimpTempBuf *pixmap; + GeglBuffer *buffer; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_BRUSH (brush), NULL); + + mask = gimp_brush_get_mask (brush); + pixmap = gimp_brush_get_pixmap (brush); + + if (pixmap) + alpha = TRUE; + else + alpha = FALSE; + + width = gimp_temp_buf_get_width (mask); + height = gimp_temp_buf_get_height (mask); + + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + + if (width > image_width || height > image_height) + { + gint new_width = MAX (image_width, width); + gint new_height = MAX (image_height, height); + + gimp_image_resize (image, gimp_get_user_context (image->gimp), + new_width, new_height, + (new_width - image_width) / 2, + (new_height - image_height) / 2, + NULL); + + image_width = new_width; + image_height = new_height; + } + + format = gimp_image_get_layer_format (image, alpha); + + layer = gimp_layer_new (image, width, height, format, + gimp_object_get_name (brush), + 1.0, GIMP_LAYER_MODE_NORMAL); + + gimp_item_set_offset (GIMP_ITEM (layer), + (image_width - width) / 2, + (image_height - height) / 2); + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + if (pixmap) + { + guchar *pixmap_data; + guchar *mask_data; + guchar *p; + guchar *m; + gint i; + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, + babl_format ("R'G'B' u8"), + gimp_temp_buf_get_data (pixmap), GEGL_AUTO_ROWSTRIDE); + + pixmap_data = gegl_buffer_linear_open (buffer, NULL, NULL, NULL); + mask_data = gimp_temp_buf_get_data (mask); + + for (i = 0, p = pixmap_data, m = mask_data; + i < width * height; + i++, p += 4, m += 1) + { + p[3] = *m; + } + + gegl_buffer_linear_close (buffer, pixmap_data); + } + else + { + guchar *mask_data = gimp_temp_buf_get_data (mask); + gint i; + + for (i = 0; i < width * height; i++) + mask_data[i] = 255 - mask_data[i]; + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, + babl_format ("Y' u8"), + mask_data, GEGL_AUTO_ROWSTRIDE); + } + + return layer; +} + +GimpBrush * +file_gbr_drawable_to_brush (GimpDrawable *drawable, + const GeglRectangle *rect, + const gchar *name, + gdouble spacing) +{ + GimpBrush *brush; + GeglBuffer *buffer; + GimpTempBuf *mask; + GimpTempBuf *pixmap = NULL; + gint width; + gint height; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (rect != NULL, NULL); + + buffer = gimp_drawable_get_buffer (drawable); + width = rect->width; + height = rect->height; + + brush = g_object_new (GIMP_TYPE_BRUSH, + "name", name, + "mime-type", "image/x-gimp-gbr", + "spacing", spacing, + NULL); + + mask = gimp_temp_buf_new (width, height, babl_format ("Y u8")); + + if (gimp_drawable_is_gray (drawable)) + { + guchar *m = gimp_temp_buf_get_data (mask); + gint i; + + if (gimp_drawable_has_alpha (drawable)) + { + GeglBufferIterator *iter; + GimpRGB white; + + gimp_rgba_set_uchar (&white, 255, 255, 255, 255); + + iter = gegl_buffer_iterator_new (buffer, rect, 0, + babl_format ("Y'A u8"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, + 1); + + while (gegl_buffer_iterator_next (iter)) + { + guint8 *data = (guint8 *) iter->items[0].data; + gint j; + + for (j = 0; j < iter->length; j++) + { + GimpRGB gray; + gint x, y; + gint dest; + + gimp_rgba_set_uchar (&gray, + data[0], data[0], data[0], + data[1]); + + gimp_rgb_composite (&gray, &white, + GIMP_RGB_COMPOSITE_BEHIND); + + x = iter->items[0].roi.x + j % iter->items[0].roi.width; + y = iter->items[0].roi.y + j / iter->items[0].roi.width; + + dest = y * width + x; + + gimp_rgba_get_uchar (&gray, &m[dest], NULL, NULL, NULL); + + data += 2; + } + } + } + else + { + gegl_buffer_get (buffer, rect, 1.0, + babl_format ("Y' u8"), m, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + /* invert */ + for (i = 0; i < width * height; i++) + m[i] = 255 - m[i]; + } + else + { + pixmap = gimp_temp_buf_new (width, height, babl_format ("R'G'B' u8")); + + gegl_buffer_get (buffer, rect, 1.0, + babl_format ("R'G'B' u8"), + gimp_temp_buf_get_data (pixmap), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gegl_buffer_get (buffer, rect, 1.0, + babl_format ("A u8"), + gimp_temp_buf_get_data (mask), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + + brush->priv->mask = mask; + brush->priv->pixmap = pixmap; + + return brush; +} + + +/* private functions */ + +static GimpImage * +file_gbr_brush_to_image (Gimp *gimp, + GimpBrush *brush) +{ + GimpImage *image; + GimpLayer *layer; + const gchar *name; + GimpImageBaseType base_type; + gint width; + gint height; + GimpTempBuf *mask = gimp_brush_get_mask (brush); + GimpTempBuf *pixmap = gimp_brush_get_pixmap (brush); + GimpParasite *parasite; + + if (pixmap) + base_type = GIMP_RGB; + else + base_type = GIMP_GRAY; + + name = gimp_object_get_name (brush); + width = gimp_temp_buf_get_width (mask); + height = gimp_temp_buf_get_height (mask); + + image = gimp_image_new (gimp, width, height, base_type, + GIMP_PRECISION_U8_GAMMA); + + parasite = gimp_parasite_new ("gimp-brush-name", + GIMP_PARASITE_PERSISTENT, + strlen (name) + 1, name); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + + layer = file_gbr_brush_to_layer (image, brush); + gimp_image_add_layer (image, layer, NULL, 0, FALSE); + + return image; +} + +static GimpBrush * +file_gbr_image_to_brush (GimpImage *image, + GimpDrawable *drawable, + const gchar *name, + gdouble spacing) +{ + gint width = gimp_item_get_width (GIMP_ITEM (drawable)); + gint height = gimp_item_get_height (GIMP_ITEM (drawable)); + + return file_gbr_drawable_to_brush (drawable, + GEGL_RECTANGLE (0, 0, width, height), + name, spacing); +} diff --git a/app/file-data/file-data-gbr.h b/app/file-data/file-data-gbr.h new file mode 100644 index 0000000..fb3bf47 --- /dev/null +++ b/app/file-data/file-data-gbr.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 . + */ + +#ifndef __FILE_DATA_GBR_H__ +#define __FILE_DATA_GBR_H__ + + +GimpValueArray * file_gbr_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + +GimpValueArray * file_gbr_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + +GimpLayer * file_gbr_brush_to_layer (GimpImage *image, + GimpBrush *brush); +GimpBrush * file_gbr_drawable_to_brush (GimpDrawable *drawable, + const GeglRectangle *rect, + const gchar *name, + gdouble spacing); + + +#endif /* __FILE_DATA_GBR_H__ */ diff --git a/app/file-data/file-data-gih.c b/app/file-data/file-data-gih.c new file mode 100644 index 0000000..d28e521 --- /dev/null +++ b/app/file-data/file-data-gih.c @@ -0,0 +1,364 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpbase/gimpparasiteio.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpbrushpipe.h" +#include "core/gimpbrushpipe-load.h" +#include "core/gimpbrush-private.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimplayer-new.h" +#include "core/gimpparamspecs.h" +#include "core/gimptempbuf.h" + +#include "pdb/gimpprocedure.h" + +#include "file-data-gbr.h" +#include "file-data-gih.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static GimpImage * file_gih_pipe_to_image (Gimp *gimp, + GimpBrushPipe *pipe); +static GimpBrushPipe * file_gih_image_to_pipe (GimpImage *image, + const gchar *name, + gdouble spacing, + const gchar *paramstring); + + +/* public functions */ + +GimpValueArray * +file_gih_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image = NULL; + const gchar *uri; + GFile *file; + GInputStream *input; + GError *my_error = NULL; + + gimp_set_busy (gimp); + + uri = g_value_get_string (gimp_value_array_index (args, 1)); + file = g_file_new_for_uri (uri); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + + if (input) + { + GList *list = gimp_brush_pipe_load (context, file, input, error); + + if (list) + { + GimpBrushPipe *pipe = list->data; + + g_list_free (list); + + image = file_gih_pipe_to_image (gimp, pipe); + g_object_unref (pipe); + } + + g_object_unref (input); + } + else + { + g_propagate_prefixed_error (error, my_error, + _("Could not open '%s' for reading: "), + gimp_file_get_utf8_name (file)); + } + + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, image != NULL, + error ? *error : NULL); + + if (image) + gimp_value_set_image (gimp_value_array_index (return_vals, 1), image); + + gimp_unset_busy (gimp); + + return return_vals; +} + +GimpValueArray * +file_gih_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image; + GimpBrushPipe *pipe; + const gchar *uri; + const gchar *name; + const gchar *params; + GFile *file; + gint spacing; + gboolean success; + + gimp_set_busy (gimp); + + image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); + uri = g_value_get_string (gimp_value_array_index (args, 3)); + spacing = g_value_get_int (gimp_value_array_index (args, 5)); + name = g_value_get_string (gimp_value_array_index (args, 6)); + params = g_value_get_string (gimp_value_array_index (args, 7)); + + file = g_file_new_for_uri (uri); + + pipe = file_gih_image_to_pipe (image, name, spacing, params); + + gimp_data_set_file (GIMP_DATA (pipe), file, TRUE, TRUE); + + success = gimp_data_save (GIMP_DATA (pipe), error); + + g_object_unref (pipe); + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, success, + error ? *error : NULL); + + gimp_unset_busy (gimp); + + return return_vals; +} + + +/* private functions */ + +static GimpImage * +file_gih_pipe_to_image (Gimp *gimp, + GimpBrushPipe *pipe) +{ + GimpImage *image; + const gchar *name; + GimpImageBaseType base_type; + GimpParasite *parasite; + gchar spacing[8]; + gint i; + + if (gimp_brush_get_pixmap (pipe->current)) + base_type = GIMP_RGB; + else + base_type = GIMP_GRAY; + + name = gimp_object_get_name (pipe); + + image = gimp_image_new (gimp, 1, 1, base_type, + GIMP_PRECISION_U8_GAMMA); + + parasite = gimp_parasite_new ("gimp-brush-pipe-name", + GIMP_PARASITE_PERSISTENT, + strlen (name) + 1, name); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + + g_snprintf (spacing, sizeof (spacing), "%d", + gimp_brush_get_spacing (GIMP_BRUSH (pipe))); + + parasite = gimp_parasite_new ("gimp-brush-pipe-spacing", + GIMP_PARASITE_PERSISTENT, + strlen (spacing) + 1, spacing); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + + for (i = 0; i < pipe->n_brushes; i++) + { + GimpLayer *layer; + + layer = file_gbr_brush_to_layer (image, pipe->brushes[i]); + gimp_image_add_layer (image, layer, NULL, i, FALSE); + } + + if (pipe->params) + { + GimpPixPipeParams params; + gchar *paramstring; + + /* Since we do not (yet) load the pipe as described in the + * header, but use one layer per brush, we have to alter the + * paramstring before attaching it as a parasite. + * + * (this comment copied over from file-gih, whatever "as + * described in the header" means) -- mitch + */ + + gimp_pixpipe_params_init (¶ms); + gimp_pixpipe_params_parse (pipe->params, ¶ms); + + params.cellwidth = gimp_image_get_width (image); + params.cellheight = gimp_image_get_height (image); + params.cols = 1; + params.rows = 1; + + paramstring = gimp_pixpipe_params_build (¶ms); + if (paramstring) + { + parasite = gimp_parasite_new ("gimp-brush-pipe-parameters", + GIMP_PARASITE_PERSISTENT, + strlen (paramstring) + 1, + paramstring); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + g_free (paramstring); + } + } + + return image; +} + +static GimpBrushPipe * +file_gih_image_to_pipe (GimpImage *image, + const gchar *name, + gdouble spacing, + const gchar *paramstring) +{ + GimpBrushPipe *pipe; + GimpPixPipeParams params; + GList *layers; + GList *list; + GList *brushes = NULL; + gint image_width; + gint image_height; + gint i; + + pipe = g_object_new (GIMP_TYPE_BRUSH_PIPE, + "name", name, + "mime-type", "image/x-gimp-gih", + "spacing", spacing, + NULL); + + gimp_pixpipe_params_init (¶ms); + gimp_pixpipe_params_parse (paramstring, ¶ms); + + image_width = gimp_image_get_width (image); + image_height = gimp_image_get_height (image); + + layers = gimp_image_get_layer_iter (image); + + for (list = layers; list; list = g_list_next (list)) + { + GimpLayer *layer = list->data; + gint width; + gint height; + gint offset_x; + gint offset_y; + gint row; + + width = gimp_item_get_width (GIMP_ITEM (layer)); + height = gimp_item_get_height (GIMP_ITEM (layer)); + + gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); + + /* Since we assume positive layer offsets we need to make sure this + * is always the case or we will crash for grayscale layers. + * See issue #6436. */ + if (offset_x < 0) + { + g_warning (_("Negative x offset: %d for layer %s corrected."), + offset_x, gimp_object_get_name (layer)); + width += offset_x; + offset_x = 0; + } + if (offset_y < 0) + { + g_warning (_("Negative y offset: %d for layer %s corrected."), + offset_y, gimp_object_get_name (layer)); + height += offset_y; + offset_y = 0; + } + + for (row = 0; row < params.rows; row++) + { + gint y, ynext; + gint thisy, thish; + gint col; + + y = (row * image_height) / params.rows; + ynext = ((row + 1) * image_height / params.rows); + + /* Assume layer is offset to positive direction in x and y. + * That's reasonable, as otherwise all of the layer + * won't be visible. + * thisy and thisx are in the drawable's coordinate space. + */ + thisy = MAX (0, y - offset_y); + thish = (ynext - offset_y) - thisy; + thish = MIN (thish, height - thisy); + + for (col = 0; col < params.cols; col++) + { + GimpBrush *brush; + gint x, xnext; + gint thisx, thisw; + + x = (col * image_width / params.cols); + xnext = ((col + 1) * image_width / params.cols); + thisx = MAX (0, x - offset_x); + thisw = (xnext - offset_x) - thisx; + thisw = MIN (thisw, width - thisx); + + brush = file_gbr_drawable_to_brush (GIMP_DRAWABLE (layer), + GEGL_RECTANGLE (thisx, thisy, + thisw, thish), + gimp_object_get_name (layer), + spacing); + + brushes = g_list_prepend (brushes, brush); + } + } + } + + brushes = g_list_reverse (brushes); + + pipe->n_brushes = g_list_length (brushes); + pipe->brushes = g_new0 (GimpBrush *, pipe->n_brushes); + + for (list = brushes, i = 0; list; list = g_list_next (list), i++) + pipe->brushes[i] = list->data; + + g_list_free (brushes); + + gimp_pixpipe_params_free (¶ms); + + gimp_brush_pipe_set_params (pipe, paramstring); + + return pipe; +} diff --git a/app/file-data/file-data-gih.h b/app/file-data/file-data-gih.h new file mode 100644 index 0000000..877e4a5 --- /dev/null +++ b/app/file-data/file-data-gih.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __FILE_DATA_GIH_H__ +#define __FILE_DATA_GIH_H__ + + +GimpValueArray * file_gih_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + +GimpValueArray * file_gih_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + + +#endif /* __FILE_DATA_GIH_H__ */ diff --git a/app/file-data/file-data-pat.c b/app/file-data/file-data-pat.c new file mode 100644 index 0000000..21506fe --- /dev/null +++ b/app/file-data/file-data-pat.c @@ -0,0 +1,263 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimplayer-new.h" +#include "core/gimpparamspecs.h" +#include "core/gimppattern.h" +#include "core/gimppattern-load.h" +#include "core/gimptempbuf.h" + +#include "pdb/gimpprocedure.h" + +#include "file-data-pat.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static GimpImage * file_pat_pattern_to_image (Gimp *gimp, + GimpPattern *pattern); +static GimpPattern * file_pat_image_to_pattern (GimpImage *image, + GimpDrawable *drawable, + const gchar *name); + + +/* public functions */ + +GimpValueArray * +file_pat_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image = NULL; + const gchar *uri; + GFile *file; + GInputStream *input; + GError *my_error = NULL; + + gimp_set_busy (gimp); + + uri = g_value_get_string (gimp_value_array_index (args, 1)); + file = g_file_new_for_uri (uri); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + + if (input) + { + GList *list = gimp_pattern_load (context, file, input, error); + + if (list) + { + GimpPattern *pattern = list->data; + + g_list_free (list); + + image = file_pat_pattern_to_image (gimp, pattern); + g_object_unref (pattern); + } + + g_object_unref (input); + } + else + { + g_propagate_prefixed_error (error, my_error, + _("Could not open '%s' for reading: "), + gimp_file_get_utf8_name (file)); + } + + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, image != NULL, + error ? *error : NULL); + + if (image) + gimp_value_set_image (gimp_value_array_index (return_vals, 1), image); + + gimp_unset_busy (gimp); + + return return_vals; +} + +GimpValueArray * +file_pat_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image; + GimpDrawable *drawable; + GimpPattern *pattern; + const gchar *uri; + const gchar *name; + GFile *file; + gboolean success; + + gimp_set_busy (gimp); + + image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); + drawable = gimp_value_get_drawable (gimp_value_array_index (args, 2), gimp); + uri = g_value_get_string (gimp_value_array_index (args, 3)); + name = g_value_get_string (gimp_value_array_index (args, 5)); + + file = g_file_new_for_uri (uri); + + pattern = file_pat_image_to_pattern (image, drawable, name); + + gimp_data_set_file (GIMP_DATA (pattern), file, TRUE, TRUE); + + success = gimp_data_save (GIMP_DATA (pattern), error); + + g_object_unref (pattern); + g_object_unref (file); + + return_vals = gimp_procedure_get_return_values (procedure, success, + error ? *error : NULL); + + gimp_unset_busy (gimp); + + return return_vals; +} + + +/* private functions */ + +static GimpImage * +file_pat_pattern_to_image (Gimp *gimp, + GimpPattern *pattern) +{ + GimpImage *image; + GimpLayer *layer; + const Babl *format; + const gchar *name; + GimpImageBaseType base_type; + gboolean alpha; + gint width; + gint height; + GimpTempBuf *mask = gimp_pattern_get_mask (pattern); + GeglBuffer *buffer; + GimpParasite *parasite; + + format = gimp_temp_buf_get_format (mask); + + switch (babl_format_get_bytes_per_pixel (format)) + { + case 1: + base_type = GIMP_GRAY; + alpha = FALSE; + break; + + case 2: + base_type = GIMP_GRAY; + alpha = TRUE; + break; + + case 3: + base_type = GIMP_RGB; + alpha = FALSE; + break; + + case 4: + base_type = GIMP_RGB; + alpha = TRUE; + break; + + default: + g_return_val_if_reached (NULL); + } + + name = gimp_object_get_name (pattern); + width = gimp_temp_buf_get_width (mask); + height = gimp_temp_buf_get_height (mask); + + image = gimp_image_new (gimp, width, height, base_type, + GIMP_PRECISION_U8_GAMMA); + + parasite = gimp_parasite_new ("gimp-pattern-name", + GIMP_PARASITE_PERSISTENT, + strlen (name) + 1, name); + gimp_image_parasite_attach (image, parasite, FALSE); + gimp_parasite_free (parasite); + + format = gimp_image_get_layer_format (image, alpha); + + layer = gimp_layer_new (image, width, height, format, name, + 1.0, GIMP_LAYER_MODE_NORMAL); + gimp_image_add_layer (image, layer, NULL, 0, FALSE); + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, + NULL, + gimp_temp_buf_get_data (mask), GEGL_AUTO_ROWSTRIDE); + + return image; +} + +static GimpPattern * +file_pat_image_to_pattern (GimpImage *image, + GimpDrawable *drawable, + const gchar *name) +{ + GimpPattern *pattern; + const Babl *format; + gint width; + gint height; + + format = gimp_babl_format (gimp_drawable_is_gray (drawable) ? + GIMP_GRAY : GIMP_RGB, + GIMP_PRECISION_U8_GAMMA, + gimp_drawable_has_alpha (drawable)); + + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + + pattern = g_object_new (GIMP_TYPE_PATTERN, + "name", name, + "mime-type", "image/x-gimp-pat", + NULL); + + pattern->mask = gimp_temp_buf_new (width, height, format); + + gegl_buffer_get (gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (0, 0, width, height), 1.0, + format, gimp_temp_buf_get_data (pattern->mask), + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + return pattern; +} diff --git a/app/file-data/file-data-pat.h b/app/file-data/file-data-pat.h new file mode 100644 index 0000000..1417139 --- /dev/null +++ b/app/file-data/file-data-pat.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __FILE_DATA_PAT_H__ +#define __FILE_DATA_PAT_H__ + + +GimpValueArray * file_pat_load_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + +GimpValueArray * file_pat_save_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error); + + +#endif /* __FILE_DATA_PAT_H__ */ diff --git a/app/file-data/file-data.c b/app/file-data/file-data.c new file mode 100644 index 0000000..411d2ca --- /dev/null +++ b/app/file-data/file-data.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpparamspecs.h" + +#include "plug-in/gimppluginmanager.h" +#include "plug-in/gimppluginprocedure.h" + +#include "file-data.h" +#include "file-data-gbr.h" +#include "file-data-gih.h" +#include "file-data-pat.h" + +#include "gimp-intl.h" + + +void +file_data_init (Gimp *gimp) +{ + GimpPlugInProcedure *proc; + GFile *file; + GimpProcedure *procedure; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + /* file-gbr-load */ + file = g_file_new_for_path ("file-gbr-load"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_gbr_load_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP brush")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-brush", + strlen ("gimp-brush") + 1); + gimp_plug_in_procedure_set_image_types (proc, NULL); + gimp_plug_in_procedure_set_file_proc (proc, "gbr, gbp", "", + "20, string, GIMP"); + gimp_plug_in_procedure_set_mime_types (proc, "image/gimp-x-gbr"); + gimp_plug_in_procedure_set_handles_uri (proc); + + gimp_object_set_static_name (GIMP_OBJECT (procedure), "file-gbr-load"); + gimp_procedure_set_static_strings (procedure, + "file-gbr-load", + "Loads GIMP brushes", + "Loads GIMP brushes (1 or 4 bpp " + "and old .gpb format)", + "Tim Newsome, Jens Lautenbacher, " + "Sven Neumann, Michael Natterer", + "Tim Newsome, Jens Lautenbacher, " + "Sven Neumann, Michael Natterer", + "1995-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + + gimp_procedure_add_return_value (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Output image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); + + /* file-gbr-save-internal */ + file = g_file_new_for_path ("file-gbr-save-internal"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_gbr_save_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP brush")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-brush", + strlen ("gimp-brush") + 1); + +#if 0 + /* do not register as file procedure */ + gimp_plug_in_procedure_set_image_types (proc, "RGB*, GRAY*, INDEXED*"); + gimp_plug_in_procedure_set_file_proc (proc, "gbr", "", NULL); + gimp_plug_in_procedure_set_mime_types (proc, "image/x-gimp-gbr"); + gimp_plug_in_procedure_set_handles_uri (proc); +#endif + + gimp_object_set_static_name (GIMP_OBJECT (procedure), + "file-gbr-save-internal"); + gimp_procedure_set_static_strings (procedure, + "file-gbr-save-internal", + "Exports Gimp brush file (.GBR)", + "Exports Gimp brush file (.GBR)", + "Tim Newsome, Michael Natterer", + "Tim Newsome, Michael Natterer", + "1995-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_drawable_id ("drawable", + "Drawable", + "Active drawable " + "of input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("spacing", + "spacing", + "Spacing of the brush", + 1, 1000, 10, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("name", + "name", + "The name of the " + "brush", + FALSE, FALSE, TRUE, + "GIMP Brush", + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); + + /* file-gih-load */ + file = g_file_new_for_path ("file-gih-load"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_gih_load_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP brush (animated)")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-brush", + strlen ("gimp-brush") + 1); + gimp_plug_in_procedure_set_image_types (proc, NULL); + gimp_plug_in_procedure_set_file_proc (proc, "gih", "", ""); + gimp_plug_in_procedure_set_mime_types (proc, "image/gimp-x-gih"); + gimp_plug_in_procedure_set_handles_uri (proc); + + gimp_object_set_static_name (GIMP_OBJECT (procedure), "file-gih-load"); + gimp_procedure_set_static_strings (procedure, + "file-gih-load", + "Loads GIMP animated brushes", + "This procedure loads a GIMP brush " + "pipe as an image.", + "Tor Lillqvist, Michael Natterer", + "Tor Lillqvist, Michael Natterer", + "1999-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + + gimp_procedure_add_return_value (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Output image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); + + /* file-gih-save-internal */ + file = g_file_new_for_path ("file-gih-save-internal"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_gih_save_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP brush (animated)")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-brush", + strlen ("gimp-brush") + 1); + +#if 0 + /* do not register as file procedure */ + gimp_plug_in_procedure_set_image_types (proc, "RGB*, GRAY*, INDEXED*"); + gimp_plug_in_procedure_set_file_proc (proc, "gih", "", NULL); + gimp_plug_in_procedure_set_mime_types (proc, "image/x-gimp-gih"); + gimp_plug_in_procedure_set_handles_uri (proc); +#endif + + gimp_object_set_static_name (GIMP_OBJECT (procedure), + "file-gih-save-internal"); + gimp_procedure_set_static_strings (procedure, + "file-gih-save-internal", + "Exports Gimp animated brush file (.gih)", + "Exports Gimp animated brush file (.gih)", + "Tor Lillqvist, Michael Natterer", + "Tor Lillqvist, Michael Natterer", + "1999-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_drawable_id ("drawable", + "Drawable", + "Active drawable " + "of input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("spacing", + "spacing", + "Spacing of the brush", + 1, 1000, 10, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("name", + "name", + "The name of the " + "brush", + FALSE, FALSE, TRUE, + "GIMP Brush", + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("params", + "params", + "The pipe's parameters", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); + + /* file-pat-load */ + file = g_file_new_for_path ("file-pat-load"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_pat_load_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP pattern")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-pattern", + strlen ("gimp-pattern") + 1); + gimp_plug_in_procedure_set_image_types (proc, NULL); + gimp_plug_in_procedure_set_file_proc (proc, "pat", "", + "20,string,GPAT"); + gimp_plug_in_procedure_set_mime_types (proc, "image/gimp-x-pat"); + gimp_plug_in_procedure_set_handles_uri (proc); + + gimp_object_set_static_name (GIMP_OBJECT (procedure), "file-pat-load"); + gimp_procedure_set_static_strings (procedure, + "file-pat-load", + "Loads GIMP patterns", + "Loads GIMP patterns", + "Tim Newsome, Michael Natterer", + "Tim Newsome, Michael Natterer", + "1997-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to load", + TRUE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_return_value (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Output image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); + + /* file-pat-save-internal */ + file = g_file_new_for_path ("file-pat-save-internal"); + procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file); + g_object_unref (file); + + procedure->proc_type = GIMP_INTERNAL; + procedure->marshal_func = file_pat_save_invoker; + + proc = GIMP_PLUG_IN_PROCEDURE (procedure); + proc->menu_label = g_strdup (N_("GIMP pattern")); + gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME, + (const guint8 *) "gimp-pattern", + strlen ("gimp-pattern") + 1); + +#if 0 + /* do not register as file procedure */ + gimp_plug_in_procedure_set_image_types (proc, "RGB*, GRAY*, INDEXED*"); + gimp_plug_in_procedure_set_file_proc (proc, "pat", "", NULL); + gimp_plug_in_procedure_set_mime_types (proc, "image/x-gimp-pat"); + gimp_plug_in_procedure_set_handles_uri (proc); +#endif + + gimp_object_set_static_name (GIMP_OBJECT (procedure), + "file-pat-save-internal"); + gimp_procedure_set_static_strings (procedure, + "file-pat-save-internal", + "Exports Gimp pattern file (.PAT)", + "Exports Gimp pattern file (.PAT)", + "Tim Newsome, Michael Natterer", + "Tim Newsome, Michael Natterer", + "1995-2019", + NULL); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_int32 ("dummy-param", + "Dummy Param", + "Dummy parameter", + G_MININT32, G_MAXINT32, 0, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_image_id ("image", + "Image", + "Input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_drawable_id ("drawable", + "Drawable", + "Active drawable " + "of input image", + gimp, FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("uri", + "URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("raw-uri", + "Raw URI", + "The URI of the file " + "to export", + FALSE, FALSE, TRUE, + NULL, + GIMP_PARAM_READWRITE)); + + gimp_procedure_add_argument (procedure, + gimp_param_spec_string ("name", + "name", + "The name of the " + "pattern", + FALSE, FALSE, TRUE, + "GIMP Pattern", + GIMP_PARAM_READWRITE)); + + gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc); + g_object_unref (procedure); +} + +void +file_data_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); +} diff --git a/app/file-data/file-data.h b/app/file-data/file-data.h new file mode 100644 index 0000000..a36381d --- /dev/null +++ b/app/file-data/file-data.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 . + */ + +#ifndef __FILE_DATA_H__ +#define __FILE_DATA_H__ + + +void file_data_init (Gimp *gimp); +void file_data_exit (Gimp *gimp); + + +#endif /* __FILE_DATA_H__ */ diff --git a/app/file/Makefile.am b/app/file/Makefile.am new file mode 100644 index 0000000..90b107f --- /dev/null +++ b/app/file/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-File\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappfile.a + +libappfile_a_SOURCES = \ + file-import.c \ + file-import.h \ + file-open.c \ + file-open.h \ + file-remote.c \ + file-remote.h \ + file-save.c \ + file-save.h \ + file-utils.c \ + file-utils.h \ + gimp-file.h diff --git a/app/file/Makefile.in b/app/file/Makefile.in new file mode 100644 index 0000000..74a398e --- /dev/null +++ b/app/file/Makefile.in @@ -0,0 +1,938 @@ +# 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 = app/file +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 = +libappfile_a_AR = $(AR) $(ARFLAGS) +libappfile_a_LIBADD = +am_libappfile_a_OBJECTS = file-import.$(OBJEXT) file-open.$(OBJEXT) \ + file-remote.$(OBJEXT) file-save.$(OBJEXT) file-utils.$(OBJEXT) +libappfile_a_OBJECTS = $(am_libappfile_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)/file-import.Po \ + ./$(DEPDIR)/file-open.Po ./$(DEPDIR)/file-remote.Po \ + ./$(DEPDIR)/file-save.Po ./$(DEPDIR)/file-utils.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 = $(libappfile_a_SOURCES) +DIST_SOURCES = $(libappfile_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-File\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappfile.a +libappfile_a_SOURCES = \ + file-import.c \ + file-import.h \ + file-open.c \ + file-open.h \ + file-remote.c \ + file-remote.h \ + file-save.c \ + file-save.h \ + file-utils.c \ + file-utils.h \ + gimp-file.h + +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 app/file/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/file/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) + +libappfile.a: $(libappfile_a_OBJECTS) $(libappfile_a_DEPENDENCIES) $(EXTRA_libappfile_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappfile.a + $(AM_V_AR)$(libappfile_a_AR) libappfile.a $(libappfile_a_OBJECTS) $(libappfile_a_LIBADD) + $(AM_V_at)$(RANLIB) libappfile.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-import.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-open.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-remote.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-save.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-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 + +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)/file-import.Po + -rm -f ./$(DEPDIR)/file-open.Po + -rm -f ./$(DEPDIR)/file-remote.Po + -rm -f ./$(DEPDIR)/file-save.Po + -rm -f ./$(DEPDIR)/file-utils.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)/file-import.Po + -rm -f ./$(DEPDIR)/file-open.Po + -rm -f ./$(DEPDIR)/file-remote.Po + -rm -f ./$(DEPDIR)/file-save.Po + -rm -f ./$(DEPDIR)/file-utils.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/app/file/file-import.c b/app/file/file-import.c new file mode 100644 index 0000000..f5e9baf --- /dev/null +++ b/app/file/file-import.c @@ -0,0 +1,109 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * file-import.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 . + */ + +#include "config.h" + +#include +#include + +#include "core/core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimpimage-color-profile.h" +#include "core/gimpimage-convert-precision.h" +#include "core/gimplayer.h" +#include "core/gimpprogress.h" + +#include "text/gimptextlayer.h" + +#include "file-import.h" + + +/* public functions */ + +void +file_import_image (GimpImage *image, + GimpContext *context, + GFile *file, + gboolean interactive, + GimpProgress *progress) +{ + GimpCoreConfig *config; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + config = image->gimp->config; + + if (interactive && gimp_image_get_base_type (image) != GIMP_INDEXED) + { + if (config->import_promote_float) + { + GimpPrecision old_precision = gimp_image_get_precision (image); + + if (old_precision != GIMP_PRECISION_FLOAT_LINEAR) + { + gimp_image_convert_precision (image, + GIMP_PRECISION_FLOAT_LINEAR, + GEGL_DITHER_NONE, + GEGL_DITHER_NONE, + GEGL_DITHER_NONE, + progress); + + if (config->import_promote_dither && + old_precision == GIMP_PRECISION_U8_GAMMA) + { + gimp_image_convert_dither_u8 (image, progress); + } + } + } + + if (config->import_add_alpha) + { + GList *layers = gimp_image_get_layer_list (image); + GList *list; + + for (list = layers; list; list = g_list_next (list)) + { + if (! gimp_viewable_get_children (list->data) && + ! gimp_item_is_text_layer (list->data) && + ! gimp_drawable_has_alpha (list->data)) + { + gimp_layer_add_alpha (list->data); + } + } + + g_list_free (layers); + } + } + + gimp_image_import_color_profile (image, context, progress, interactive); + + /* Remember the import source */ + gimp_image_set_imported_file (image, file); + + /* We shall treat this file as an Untitled file */ + gimp_image_set_file (image, NULL); +} diff --git a/app/file/file-import.h b/app/file/file-import.h new file mode 100644 index 0000000..d23eff6 --- /dev/null +++ b/app/file/file-import.h @@ -0,0 +1,31 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * file-import.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 . + */ + +#ifndef __FILE_IMPORT_H__ +#define __FILE_IMPORT_H__ + + +void file_import_image (GimpImage *image, + GimpContext *context, + GFile *file, + gboolean interactive, + GimpProgress *progress); + + +#endif /* __FILE_IMPORT_H__ */ diff --git a/app/file/file-open.c b/app/file/file-open.c new file mode 100644 index 0000000..b3ae129 --- /dev/null +++ b/app/file/file-open.c @@ -0,0 +1,833 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * file-open.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "gegl/gimp-babl.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpdocumentlist.h" +#include "core/gimpimage.h" +#include "core/gimpimage-merge.h" +#include "core/gimpimage-undo.h" +#include "core/gimpimagefile.h" +#include "core/gimplayer.h" +#include "core/gimpparamspecs.h" +#include "core/gimpprogress.h" + +#include "pdb/gimppdb.h" + +#include "plug-in/gimppluginmanager-file.h" +#include "plug-in/gimppluginprocedure.h" + +#include "file-import.h" +#include "file-open.h" +#include "file-remote.h" +#include "gimp-file.h" + +#include "gimp-intl.h" + + +static void file_open_sanitize_image (GimpImage *image, + gboolean as_new); +static void file_open_convert_items (GimpImage *dest_image, + const gchar *basename, + GList *items); +static GList * file_open_get_layers (GimpImage *image, + gboolean merge_visible, + gint *n_visible); +static gboolean file_open_file_proc_is_import (GimpPlugInProcedure *file_proc); + + +/* public functions */ + +GimpImage * +file_open_image (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + GFile *entered_file, + gboolean as_new, + GimpPlugInProcedure *file_proc, + GimpRunMode run_mode, + GimpPDBStatusType *status, + const gchar **mime_type, + GError **error) +{ + GimpValueArray *return_vals; + GimpImage *image = NULL; + GFile *local_file = NULL; + gchar *path = NULL; + gchar *entered_uri = NULL; + gboolean mounted = TRUE; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_FILE (entered_file), NULL); + g_return_val_if_fail (status != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + *status = GIMP_PDB_EXECUTION_ERROR; + + if (! g_file_is_native (file) && + ! file_remote_mount_file (gimp, file, progress, &my_error)) + { + if (my_error) + { + g_printerr ("%s: mounting remote volume failed, trying to download" + "the file: %s\n", + G_STRFUNC, my_error->message); + g_clear_error (&my_error); + + mounted = FALSE; + } + else + { + *status = GIMP_PDB_CANCEL; + + return NULL; + } + } + + /* FIXME enable these tests for remote files again, needs testing */ + if (g_file_is_native (file) && + g_file_query_exists (file, NULL)) + { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_READ, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (! info) + return NULL; + + if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Not a regular file")); + g_object_unref (info); + return NULL; + } + + if (! g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Permission denied")); + g_object_unref (info); + return NULL; + } + + g_object_unref (info); + } + + if (! file_proc) + file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_OPEN, + file, error); + + if (! file_proc || ! file_proc->handles_uri || ! mounted) + { + gchar *my_path = g_file_get_path (file); + + if (! my_path) + { + g_clear_error (error); + + local_file = file_remote_download_image (gimp, file, progress, + &my_error); + + if (! local_file) + { + if (my_error) + g_propagate_error (error, my_error); + else + *status = GIMP_PDB_CANCEL; + + return NULL; + } + + /* if we don't have a file proc yet, try again on the local + * file + */ + if (! file_proc) + file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_OPEN, + local_file, error); + } + + g_free (my_path); + } + + if (! file_proc) + { + if (local_file) + { + g_file_delete (local_file, NULL, NULL); + g_object_unref (local_file); + } + + return NULL; + } + + if (file_proc->handles_uri) + path = g_file_get_uri (local_file ? local_file : file); + else + path = g_file_get_path (local_file ? local_file : file); + + entered_uri = g_file_get_uri (entered_file); + + if (! entered_uri) + entered_uri = g_strdup (path); + + if (progress) + g_object_add_weak_pointer (G_OBJECT (progress), (gpointer) &progress); + + return_vals = + gimp_pdb_execute_procedure_by_name (gimp->pdb, + context, progress, error, + gimp_object_get_name (file_proc), + GIMP_TYPE_INT32, run_mode, + G_TYPE_STRING, path, + G_TYPE_STRING, entered_uri, + G_TYPE_NONE); + + if (progress) + g_object_remove_weak_pointer (G_OBJECT (progress), (gpointer) &progress); + + g_free (path); + g_free (entered_uri); + + *status = g_value_get_enum (gimp_value_array_index (return_vals, 0)); + + if (*status == GIMP_PDB_SUCCESS) + image = gimp_value_get_image (gimp_value_array_index (return_vals, 1), + gimp); + + if (local_file) + { + if (image) + gimp_image_set_file (image, file); + + g_file_delete (local_file, NULL, NULL); + g_object_unref (local_file); + } + + if (*status == GIMP_PDB_SUCCESS) + { + if (image) + { + /* Only set the load procedure if it hasn't already been set. */ + if (! gimp_image_get_load_proc (image)) + gimp_image_set_load_proc (image, file_proc); + + file_proc = gimp_image_get_load_proc (image); + + if (mime_type) + *mime_type = g_slist_nth_data (file_proc->mime_types_list, 0); + } + else + { + if (error && ! *error) + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("%s plug-in returned SUCCESS but did not " + "return an image"), + gimp_procedure_get_label (GIMP_PROCEDURE (file_proc))); + + *status = GIMP_PDB_EXECUTION_ERROR; + } + } + else if (*status != GIMP_PDB_CANCEL) + { + if (error && ! *error) + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("%s plug-in could not open image"), + gimp_procedure_get_label (GIMP_PROCEDURE (file_proc))); + } + + gimp_value_array_unref (return_vals); + + if (image) + { + gimp_image_undo_disable (image); + + if (file_open_file_proc_is_import (file_proc)) + { + file_import_image (image, context, file, + run_mode == GIMP_RUN_INTERACTIVE, + progress); + } + + /* Enables undo again */ + file_open_sanitize_image (image, as_new); + } + + return image; +} + +/** + * file_open_thumbnail: + * @gimp: + * @context: + * @progress: + * @file: an image file + * @size: requested size of the thumbnail + * @mime_type: return location for image MIME type + * @image_width: return location for image width + * @image_height: return location for image height + * @format: return location for image format (set to NULL if unknown) + * @num_layers: return location for number of layers + * (set to -1 if the number of layers is not known) + * @error: + * + * Attempts to load a thumbnail by using a registered thumbnail loader. + * + * Return value: the thumbnail image + */ +GimpImage * +file_open_thumbnail (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + gint size, + const gchar **mime_type, + gint *image_width, + gint *image_height, + const Babl **format, + gint *num_layers, + GError **error) +{ + GimpPlugInProcedure *file_proc; + GimpProcedure *procedure; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (mime_type != NULL, NULL); + g_return_val_if_fail (image_width != NULL, NULL); + g_return_val_if_fail (image_height != NULL, NULL); + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (num_layers != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + *image_width = 0; + *image_height = 0; + *format = NULL; + *num_layers = -1; + + file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_OPEN, + file, NULL); + + if (! file_proc || ! file_proc->thumb_loader) + return NULL; + + procedure = gimp_pdb_lookup_procedure (gimp->pdb, file_proc->thumb_loader); + + if (procedure && procedure->num_args >= 2 && procedure->num_values >= 1) + { + GimpPDBStatusType status; + GimpValueArray *return_vals; + GimpImage *image = NULL; + gchar *path = NULL; + + if (! file_proc->handles_uri) + path = g_file_get_path (file); + + if (! path) + path = g_file_get_uri (file); + + return_vals = + gimp_pdb_execute_procedure_by_name (gimp->pdb, + context, progress, error, + gimp_object_get_name (procedure), + G_TYPE_STRING, path, + GIMP_TYPE_INT32, size, + G_TYPE_NONE); + + g_free (path); + + status = g_value_get_enum (gimp_value_array_index (return_vals, 0)); + + if (status == GIMP_PDB_SUCCESS && + GIMP_VALUE_HOLDS_IMAGE_ID (gimp_value_array_index (return_vals, 1))) + { + image = gimp_value_get_image (gimp_value_array_index (return_vals, 1), + gimp); + + if (gimp_value_array_length (return_vals) >= 3 && + G_VALUE_HOLDS_INT (gimp_value_array_index (return_vals, 2)) && + G_VALUE_HOLDS_INT (gimp_value_array_index (return_vals, 3))) + { + *image_width = + MAX (0, g_value_get_int (gimp_value_array_index (return_vals, 2))); + + *image_height = + MAX (0, g_value_get_int (gimp_value_array_index (return_vals, 3))); + + if (gimp_value_array_length (return_vals) >= 5 && + G_VALUE_HOLDS_INT (gimp_value_array_index (return_vals, 4))) + { + gint value = g_value_get_int (gimp_value_array_index (return_vals, 4)); + + switch (value) + { + case GIMP_RGB_IMAGE: + *format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_U8_GAMMA, + FALSE); + break; + + case GIMP_RGBA_IMAGE: + *format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_U8_GAMMA, + TRUE); + break; + + case GIMP_GRAY_IMAGE: + *format = gimp_babl_format (GIMP_GRAY, + GIMP_PRECISION_U8_GAMMA, + FALSE); + break; + + case GIMP_GRAYA_IMAGE: + *format = gimp_babl_format (GIMP_GRAY, + GIMP_PRECISION_U8_GAMMA, + TRUE); + break; + + case GIMP_INDEXED_IMAGE: + case GIMP_INDEXEDA_IMAGE: + { + const Babl *rgb; + const Babl *rgba; + + babl_new_palette ("-gimp-indexed-format-dummy", + &rgb, &rgba); + + if (value == GIMP_INDEXED_IMAGE) + *format = rgb; + else + *format = rgba; + } + break; + + default: + break; + } + } + + if (gimp_value_array_length (return_vals) >= 6 && + G_VALUE_HOLDS_INT (gimp_value_array_index (return_vals, 5))) + { + *num_layers = + MAX (0, g_value_get_int (gimp_value_array_index (return_vals, 5))); + } + } + + if (image) + { + file_open_sanitize_image (image, FALSE); + + *mime_type = g_slist_nth_data (file_proc->mime_types_list, 0); + +#ifdef GIMP_UNSTABLE + g_printerr ("opened thumbnail at %d x %d\n", + gimp_image_get_width (image), + gimp_image_get_height (image)); +#endif + } + } + + gimp_value_array_unref (return_vals); + + return image; + } + + return NULL; +} + +GimpImage * +file_open_with_display (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + gboolean as_new, + GObject *screen, + gint monitor, + GimpPDBStatusType *status, + GError **error) +{ + return file_open_with_proc_and_display (gimp, context, progress, + file, file, as_new, NULL, + screen, monitor, + status, error); +} + +GimpImage * +file_open_with_proc_and_display (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + GFile *entered_file, + gboolean as_new, + GimpPlugInProcedure *file_proc, + GObject *screen, + gint monitor, + GimpPDBStatusType *status, + GError **error) +{ + GimpImage *image; + const gchar *mime_type = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (G_IS_FILE (entered_file), NULL); + g_return_val_if_fail (screen == NULL || G_IS_OBJECT (screen), NULL); + g_return_val_if_fail (status != NULL, NULL); + + image = file_open_image (gimp, context, progress, + file, + entered_file, + as_new, + file_proc, + GIMP_RUN_INTERACTIVE, + status, + &mime_type, + error); + + if (image) + { + /* If the file was imported we want to set the layer name to the + * file name. For now, assume that multi-layered imported images + * have named the layers already, so only rename the layer of + * single-layered imported files. Note that this will also + * rename already named layers from e.g. single-layered PSD + * files. To solve this properly, we would need new file plug-in + * API. + */ + if (! file_proc) + file_proc = gimp_image_get_load_proc (image); + + if (file_open_file_proc_is_import (file_proc) && + gimp_image_get_n_layers (image) == 1) + { + GimpObject *layer = gimp_image_get_layer_iter (image)->data; + gchar *basename; + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + + gimp_item_rename (GIMP_ITEM (layer), basename, NULL); + gimp_image_undo_free (image); + gimp_image_clean_all (image); + + g_free (basename); + } + + if (gimp_create_display (image->gimp, image, GIMP_UNIT_PIXEL, 1.0, + screen, monitor)) + { + /* the display owns the image now */ + g_object_unref (image); + } + + if (! as_new) + { + GimpDocumentList *documents = GIMP_DOCUMENT_LIST (gimp->documents); + GimpImagefile *imagefile; + GFile *any_file; + + imagefile = gimp_document_list_add_file (documents, file, mime_type); + + /* can only create a thumbnail if the passed file and the + * resulting image's file match. Use any_file() here so we + * create thumbnails for both XCF and imported images. + */ + any_file = gimp_image_get_any_file (image); + + if (any_file && g_file_equal (file, any_file)) + { + /* no need to save a thumbnail if there's a good one already */ + if (! gimp_imagefile_check_thumbnail (imagefile)) + { + gimp_imagefile_save_thumbnail (imagefile, mime_type, image, + NULL); + } + } + } + + /* announce that we opened this image */ + gimp_image_opened (image->gimp, file); + } + + return image; +} + +GList * +file_open_layers (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpImage *dest_image, + gboolean merge_visible, + GFile *file, + GimpRunMode run_mode, + GimpPlugInProcedure *file_proc, + GimpPDBStatusType *status, + GError **error) +{ + GimpImage *new_image; + GList *layers = NULL; + const gchar *mime_type = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (status != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + new_image = file_open_image (gimp, context, progress, + file, file, FALSE, + file_proc, + run_mode, + status, &mime_type, error); + + if (new_image) + { + gint n_visible = 0; + + gimp_image_undo_disable (new_image); + + layers = file_open_get_layers (new_image, merge_visible, &n_visible); + + if (merge_visible && n_visible > 1) + { + GimpLayer *layer; + + g_list_free (layers); + + layer = gimp_image_merge_visible_layers (new_image, context, + GIMP_CLIP_TO_IMAGE, + FALSE, FALSE, + NULL); + + layers = g_list_prepend (NULL, layer); + } + + if (layers) + { + gchar *basename; + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + file_open_convert_items (dest_image, basename, layers); + g_free (basename); + + gimp_document_list_add_file (GIMP_DOCUMENT_LIST (gimp->documents), + file, mime_type); + } + else + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Image doesn't contain any layers")); + *status = GIMP_PDB_EXECUTION_ERROR; + } + + g_object_unref (new_image); + } + + return g_list_reverse (layers); +} + + +/* This function is called for filenames passed on the command-line + * or from the D-Bus service. + */ +gboolean +file_open_from_command_line (Gimp *gimp, + GFile *file, + gboolean as_new, + GObject *screen, + gint monitor) + +{ + GimpImage *image; + GimpObject *display; + GimpPDBStatusType status; + gboolean success = FALSE; + GError *error = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (screen == NULL || G_IS_OBJECT (screen), FALSE); + + display = gimp_get_empty_display (gimp); + + /* show the progress in the last opened display, see bug #704896 */ + if (! display) + display = gimp_context_get_display (gimp_get_user_context (gimp)); + + if (display) + g_object_add_weak_pointer (G_OBJECT (display), (gpointer) &display); + + image = file_open_with_display (gimp, + gimp_get_user_context (gimp), + GIMP_PROGRESS (display), + file, as_new, + screen, monitor, + &status, &error); + + if (image) + { + success = TRUE; + + g_object_set_data_full (G_OBJECT (gimp), GIMP_FILE_OPEN_LAST_FILE_KEY, + g_object_ref (file), + (GDestroyNotify) g_object_unref); + } + else if (status != GIMP_PDB_CANCEL && display) + { + gimp_message (gimp, G_OBJECT (display), GIMP_MESSAGE_ERROR, + _("Opening '%s' failed: %s"), + gimp_file_get_utf8_name (file), error->message); + g_clear_error (&error); + } + + if (display) + g_object_remove_weak_pointer (G_OBJECT (display), (gpointer) &display); + + return success; +} + + +/* private functions */ + +static void +file_open_sanitize_image (GimpImage *image, + gboolean as_new) +{ + if (as_new) + gimp_image_set_file (image, NULL); + + /* clear all undo steps */ + gimp_image_undo_free (image); + + /* make sure that undo is enabled */ + while (! gimp_image_undo_is_enabled (image)) + gimp_image_undo_thaw (image); + + /* Set the image to clean. Note that export dirtiness is not set to + * clean here; we can only consider export clean after the first + * export + */ + gimp_image_clean_all (image); + + /* Make sure the projection is completely constructed from valid + * layers, this is needed in case something triggers projection or + * image preview creation before all layers are loaded, see bug #767663. + */ + gimp_image_invalidate_all (image); + + /* Make sure all image states are up-to-date */ + gimp_image_flush (image); +} + +/* Converts items from one image to another */ +static void +file_open_convert_items (GimpImage *dest_image, + const gchar *basename, + GList *items) +{ + GList *list; + + for (list = items; list; list = g_list_next (list)) + { + GimpItem *src = list->data; + GimpItem *item; + + item = gimp_item_convert (src, dest_image, G_TYPE_FROM_INSTANCE (src)); + + if (g_list_length (items) == 1) + { + gimp_object_set_name (GIMP_OBJECT (item), basename); + } + else + { + gimp_object_set_name (GIMP_OBJECT (item), + gimp_object_get_name (src)); + } + + list->data = item; + } +} + +static GList * +file_open_get_layers (GimpImage *image, + gboolean merge_visible, + gint *n_visible) +{ + GList *iter = NULL; + GList *layers = NULL; + + for (iter = gimp_image_get_layer_iter (image); + iter; + iter = g_list_next (iter)) + { + GimpItem *item = iter->data; + + if (! merge_visible) + layers = g_list_prepend (layers, item); + + if (gimp_item_get_visible (item)) + { + if (n_visible) + (*n_visible)++; + + if (! layers) + layers = g_list_prepend (layers, item); + } + } + + return layers; +} + +static gboolean +file_open_file_proc_is_import (GimpPlugInProcedure *file_proc) +{ + return !(file_proc && + file_proc->mime_types && + strcmp (file_proc->mime_types, "image/x-xcf") == 0); +} diff --git a/app/file/file-open.h b/app/file/file-open.h new file mode 100644 index 0000000..361dd1f --- /dev/null +++ b/app/file/file-open.h @@ -0,0 +1,87 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * file-open.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 . + */ + +#ifndef __FILE_OPEN_H__ +#define __FILE_OPEN_H__ + + +GimpImage * file_open_image (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + GFile *entered_file, + gboolean as_new, + GimpPlugInProcedure *file_proc, + GimpRunMode run_mode, + GimpPDBStatusType *status, + const gchar **mime_type, + GError **error); + +GimpImage * file_open_thumbnail (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + gint size, + const gchar **mime_type, + gint *image_width, + gint *image_height, + const Babl **format, + gint *num_layers, + GError **error); +GimpImage * file_open_with_display (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + gboolean as_new, + GObject *screen, + gint monitor, + GimpPDBStatusType *status, + GError **error); + +GimpImage * file_open_with_proc_and_display (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GFile *file, + GFile *entered_file, + gboolean as_new, + GimpPlugInProcedure *file_proc, + GObject *screen, + gint monitor, + GimpPDBStatusType *status, + GError **error); + +GList * file_open_layers (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpImage *dest_image, + gboolean merge_visible, + GFile *file, + GimpRunMode run_mode, + GimpPlugInProcedure *file_proc, + GimpPDBStatusType *status, + GError **error); + +gboolean file_open_from_command_line (Gimp *gimp, + GFile *file, + gboolean as_new, + GObject *screen, + gint monitor); + + +#endif /* __FILE_OPEN_H__ */ diff --git a/app/file/file-remote.c b/app/file/file-remote.c new file mode 100644 index 0000000..498115a --- /dev/null +++ b/app/file/file-remote.c @@ -0,0 +1,401 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * file-remote.c + * Copyright (C) 2014 Michael Natterer + * + * Based on: URI plug-in, GIO/GVfs backend + * Copyright (C) 2008 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpprogress.h" + +#include "file-remote.h" + +#include "gimp-intl.h" + + +typedef enum +{ + DOWNLOAD, + UPLOAD +} RemoteCopyMode; + +typedef struct +{ + GimpProgress *progress; + GCancellable *cancellable; + gboolean cancel; + GMainLoop *main_loop; + GError *error; +} RemoteMount; + +typedef struct +{ + RemoteCopyMode mode; + GimpProgress *progress; + GCancellable *cancellable; + gboolean cancel; + gint64 last_time; +} RemoteProgress; + + +static void file_remote_mount_volume_ready (GFile *file, + GAsyncResult *result, + RemoteMount *mount); +static void file_remote_mount_file_cancel (GimpProgress *progress, + RemoteMount *mount); + +static GFile * file_remote_get_temp_file (Gimp *gimp, + GFile *file); +static gboolean file_remote_copy_file (Gimp *gimp, + GFile *src_file, + GFile *dest_file, + RemoteCopyMode mode, + GimpProgress *progress, + GError **error); +static void file_remote_copy_file_cancel (GimpProgress *progress, + RemoteProgress *remote_progress); + +static void file_remote_progress_callback (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data); + + +/* public functions */ + +gboolean +file_remote_mount_file (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error) +{ + GMountOperation *operation; + RemoteMount mount = { 0, }; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + mount.progress = progress; + mount.main_loop = g_main_loop_new (NULL, FALSE); + + operation = gimp_get_mount_operation (gimp, progress); + + if (progress) + { + gimp_progress_start (progress, TRUE, _("Mounting remote volume")); + + mount.cancellable = g_cancellable_new (); + + g_signal_connect (progress, "cancel", + G_CALLBACK (file_remote_mount_file_cancel), + &mount); + } + + g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, + operation, mount.cancellable, + (GAsyncReadyCallback) file_remote_mount_volume_ready, + &mount); + + g_main_loop_run (mount.main_loop); + g_main_loop_unref (mount.main_loop); + + if (progress) + { + g_signal_handlers_disconnect_by_func (progress, + file_remote_mount_file_cancel, + &mount); + + g_object_unref (mount.cancellable); + + gimp_progress_end (progress); + } + + g_object_unref (operation); + + if (mount.error) + { + if (mount.error->domain != G_IO_ERROR || + mount.error->code != G_IO_ERROR_ALREADY_MOUNTED) + { + g_propagate_error (error, mount.error); + return FALSE; + } + else + { + g_clear_error (&mount.error); + } + } + + return TRUE; +} + +GFile * +file_remote_download_image (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error) +{ + GFile *local_file; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + local_file = file_remote_get_temp_file (gimp, file); + + if (! file_remote_copy_file (gimp, file, local_file, DOWNLOAD, + progress, error)) + { + g_object_unref (local_file); + return NULL; + } + + return local_file; +} + +GFile * +file_remote_upload_image_prepare (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error) +{ + GFile *local_file; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + local_file = file_remote_get_temp_file (gimp, file); + + return local_file; +} + +gboolean +file_remote_upload_image_finish (Gimp *gimp, + GFile *file, + GFile *local_file, + GimpProgress *progress, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + g_return_val_if_fail (G_IS_FILE (local_file), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (! file_remote_copy_file (gimp, local_file, file, UPLOAD, + progress, error)) + { + return FALSE; + } + + return TRUE; +} + + +/* private functions */ + +static void +file_remote_mount_volume_ready (GFile *file, + GAsyncResult *result, + RemoteMount *mount) +{ + g_file_mount_enclosing_volume_finish (file, result, &mount->error); + + g_main_loop_quit (mount->main_loop); +} + +static void +file_remote_mount_file_cancel (GimpProgress *progress, + RemoteMount *mount) +{ + mount->cancel = TRUE; + + g_cancellable_cancel (mount->cancellable); +} + +static GFile * +file_remote_get_temp_file (Gimp *gimp, + GFile *file) +{ + gchar *basename; + GFile *temp_file = NULL; + + basename = g_path_get_basename (gimp_file_get_utf8_name (file)); + + if (basename) + { + const gchar *ext = strchr (basename, '.'); + + if (ext && strlen (ext)) + temp_file = gimp_get_temp_file (gimp, ext + 1); + + g_free (basename); + } + + if (! temp_file) + temp_file = gimp_get_temp_file (gimp, "xxx"); + + return temp_file; +} + +static gboolean +file_remote_copy_file (Gimp *gimp, + GFile *src_file, + GFile *dest_file, + RemoteCopyMode mode, + GimpProgress *progress, + GError **error) +{ + RemoteProgress remote_progress = { 0, }; + gboolean success; + GError *my_error = NULL; + + remote_progress.mode = mode; + remote_progress.progress = progress; + + if (progress) + { + gimp_progress_start (progress, TRUE, _("Opening remote file")); + + remote_progress.cancellable = g_cancellable_new (); + + g_signal_connect (progress, "cancel", + G_CALLBACK (file_remote_copy_file_cancel), + &remote_progress); + + success = g_file_copy (src_file, dest_file, G_FILE_COPY_OVERWRITE, + remote_progress.cancellable, + file_remote_progress_callback, &remote_progress, + &my_error); + + g_signal_handlers_disconnect_by_func (progress, + file_remote_copy_file_cancel, + &remote_progress); + + g_object_unref (remote_progress.cancellable); + + gimp_progress_set_value (progress, 1.0); + gimp_progress_end (progress); + } + else + { + success = g_file_copy (src_file, dest_file, G_FILE_COPY_OVERWRITE, + NULL, NULL, NULL, + &my_error); + } + + return success; +} + +static void +file_remote_copy_file_cancel (GimpProgress *progress, + RemoteProgress *remote_progress) +{ + remote_progress->cancel = TRUE; + + g_cancellable_cancel (remote_progress->cancellable); +} + +static void +file_remote_progress_callback (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + RemoteProgress *progress = user_data; + gint64 now; + + /* update the progress only up to 10 times a second */ + now = g_get_monotonic_time (); + + if ((now - progress->last_time) / 1000 < 100) + return; + + progress->last_time = now; + + if (total_num_bytes > 0) + { + const gchar *format; + gchar *done = g_format_size (current_num_bytes); + gchar *total = g_format_size (total_num_bytes); + + switch (progress->mode) + { + case DOWNLOAD: + format = _("Downloading image (%s of %s)"); + break; + + case UPLOAD: + format = _("Uploading image (%s of %s)"); + break; + + default: + gimp_assert_not_reached (); + } + + gimp_progress_set_text (progress->progress, format, done, total); + g_free (total); + g_free (done); + + gimp_progress_set_value (progress->progress, + (gdouble) current_num_bytes / + (gdouble) total_num_bytes); + } + else + { + const gchar *format; + gchar *done = g_format_size (current_num_bytes); + + switch (progress->mode) + { + case DOWNLOAD: + format = _("Downloaded %s of image data"); + break; + + case UPLOAD: + format = _("Uploaded %s of image data"); + break; + + default: + gimp_assert_not_reached (); + } + + gimp_progress_set_text (progress->progress, format, done); + g_free (done); + + gimp_progress_pulse (progress->progress); + } + + while (! progress->cancel && g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); +} diff --git a/app/file/file-remote.h b/app/file/file-remote.h new file mode 100644 index 0000000..e5f38cc --- /dev/null +++ b/app/file/file-remote.h @@ -0,0 +1,48 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * file-remote.h + * Copyright (C) 2014 Michael Natterer + * + * Based on: URI plug-in, GIO/GVfs backend + * Copyright (C) 2008 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 . + */ + +#ifndef __FILE_REMOTE_H__ +#define __FILE_REMOTE_H__ + + +gboolean file_remote_mount_file (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error); + +GFile * file_remote_download_image (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error); + +GFile * file_remote_upload_image_prepare (Gimp *gimp, + GFile *file, + GimpProgress *progress, + GError **error); +gboolean file_remote_upload_image_finish (Gimp *gimp, + GFile *file, + GFile *local_file, + GimpProgress *progress, + GError **error); + +#endif /* __FILE_REMOTE_H__ */ diff --git a/app/file/file-save.c b/app/file/file-save.c new file mode 100644 index 0000000..7fc2dfd --- /dev/null +++ b/app/file/file-save.c @@ -0,0 +1,325 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * file-save.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpdocumentlist.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" +#include "core/gimpparamspecs.h" +#include "core/gimpprogress.h" + +#include "pdb/gimppdb.h" + +#include "plug-in/gimppluginprocedure.h" + +#include "file-remote.h" +#include "file-save.h" +#include "gimp-file.h" + +#include "gimp-intl.h" + + +/* public functions */ + +GimpPDBStatusType +file_save (Gimp *gimp, + GimpImage *image, + GimpProgress *progress, + GFile *file, + GimpPlugInProcedure *file_proc, + GimpRunMode run_mode, + gboolean change_saved_state, + gboolean export_backward, + gboolean export_forward, + GError **error) +{ + GimpDrawable *drawable; + GimpValueArray *return_vals; + GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR; + GFile *local_file = NULL; + gchar *path = NULL; + gchar *uri = NULL; + gboolean mounted = TRUE; + gint32 image_ID; + gint32 drawable_ID; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), + GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail (G_IS_FILE (file), GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (file_proc), + GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail ((export_backward && export_forward) == FALSE, + GIMP_PDB_CALLING_ERROR); + g_return_val_if_fail (error == NULL || *error == NULL, + GIMP_PDB_CALLING_ERROR); + + /* ref image and file, so they can't get deleted during save */ + g_object_ref (image); + g_object_ref (file); + + gimp_image_saving (image); + + drawable = gimp_image_get_active_drawable (image); + + if (! drawable) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("There is no active layer to save")); + goto out; + } + + /* FIXME enable these tests for remote files again, needs testing */ + if (g_file_is_native (file) && + g_file_query_exists (file, NULL)) + { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (! info) + { + /* extra paranoia */ + if (error && ! *error) + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Failed to get file information")); + goto out; + } + + if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Not a regular file")); + g_object_unref (info); + goto out; + } + + if (! g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Permission denied")); + g_object_unref (info); + goto out; + } + + g_object_unref (info); + } + + if (! g_file_is_native (file) && + ! file_remote_mount_file (gimp, file, progress, &my_error)) + { + if (my_error) + { + g_printerr ("%s: mounting remote volume failed, trying to upload" + "the file: %s\n", + G_STRFUNC, my_error->message); + g_clear_error (&my_error); + + mounted = FALSE; + } + else + { + status = GIMP_PDB_CANCEL; + + goto out; + } + } + + if (! file_proc->handles_uri || ! mounted) + { + gchar *my_path = g_file_get_path (file); + + if (! my_path) + { + local_file = file_remote_upload_image_prepare (gimp, file, progress, + &my_error); + + if (! local_file) + { + if (my_error) + g_propagate_error (error, my_error); + else + status = GIMP_PDB_CANCEL; + + goto out; + } + + if (file_proc->handles_uri) + path = g_file_get_uri (local_file); + else + path = g_file_get_path (local_file); + } + + g_free (my_path); + } + + if (! path) + { + if (file_proc->handles_uri) + path = g_file_get_uri (file); + else + path = g_file_get_path (file); + } + + uri = g_file_get_uri (file); + + image_ID = gimp_image_get_ID (image); + drawable_ID = gimp_item_get_ID (GIMP_ITEM (drawable)); + + return_vals = + gimp_pdb_execute_procedure_by_name (image->gimp->pdb, + gimp_get_user_context (gimp), + progress, error, + gimp_object_get_name (file_proc), + GIMP_TYPE_INT32, run_mode, + GIMP_TYPE_IMAGE_ID, image_ID, + GIMP_TYPE_DRAWABLE_ID, drawable_ID, + G_TYPE_STRING, path, + G_TYPE_STRING, uri, + G_TYPE_NONE); + + status = g_value_get_enum (gimp_value_array_index (return_vals, 0)); + + gimp_value_array_unref (return_vals); + + if (local_file) + { + if (status == GIMP_PDB_SUCCESS) + { + GError *my_error = NULL; + + if (! file_remote_upload_image_finish (gimp, file, local_file, + progress, &my_error)) + { + status = GIMP_PDB_EXECUTION_ERROR; + + if (my_error) + g_propagate_error (error, my_error); + else + status = GIMP_PDB_CANCEL; + } + } + + g_file_delete (local_file, NULL, NULL); + g_object_unref (local_file); + } + + if (status == GIMP_PDB_SUCCESS) + { + GimpDocumentList *documents; + GimpImagefile *imagefile; + + if (change_saved_state) + { + gimp_image_set_file (image, file); + gimp_image_set_save_proc (image, file_proc); + + /* Forget the import source when we save. We interpret a + * save as that the user is not interested in being able + * to quickly export back to the original any longer + */ + gimp_image_set_imported_file (image, NULL); + + gimp_image_clean_all (image); + } + else if (export_backward) + { + /* We exported the image back to its imported source, + * change nothing about export/import flags, only set + * the export state to clean + */ + gimp_image_export_clean_all (image); + } + else if (export_forward) + { + /* Remember the last entered Export URI for the image. We + * only need to do this explicitly when exporting. It + * happens implicitly when saving since the GimpObject name + * of a GimpImage is the last-save URI + */ + gimp_image_set_exported_file (image, file); + gimp_image_set_export_proc (image, file_proc); + + /* An image can not be considered both exported and imported + * at the same time, so stop consider it as imported now + * that we consider it exported. + */ + gimp_image_set_imported_file (image, NULL); + + gimp_image_export_clean_all (image); + } + + if (export_backward || export_forward) + gimp_image_exported (image, file); + else + gimp_image_saved (image, file); + + documents = GIMP_DOCUMENT_LIST (image->gimp->documents); + + imagefile = gimp_document_list_add_file (documents, file, + g_slist_nth_data (file_proc->mime_types_list, 0)); + + /* only save a thumbnail if we are saving as XCF, see bug #25272 */ + if (GIMP_PROCEDURE (file_proc)->proc_type == GIMP_INTERNAL) + gimp_imagefile_save_thumbnail (imagefile, + g_slist_nth_data (file_proc->mime_types_list, 0), + image, + NULL); + } + else if (status != GIMP_PDB_CANCEL) + { + if (error && *error == NULL) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("%s plug-in could not save image"), + gimp_procedure_get_label (GIMP_PROCEDURE (file_proc))); + } + } + + gimp_image_flush (image); + + out: + g_object_unref (file); + g_object_unref (image); + + g_free (path); + g_free (uri); + + return status; +} diff --git a/app/file/file-save.h b/app/file/file-save.h new file mode 100644 index 0000000..fcfac0c --- /dev/null +++ b/app/file/file-save.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * file-save.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 . + */ + +#ifndef __FILE_SAVE_H__ +#define __FILE_SAVE_H__ + + +GimpPDBStatusType file_save (Gimp *gimp, + GimpImage *image, + GimpProgress *progress, + GFile *file, + GimpPlugInProcedure *file_proc, + GimpRunMode run_mode, + gboolean change_saved_state, + gboolean export_backward, + gboolean export_forward, + GError **error); + + +#endif /* __FILE_SAVE_H__ */ diff --git a/app/file/file-utils.c b/app/file/file-utils.c new file mode 100644 index 0000000..230c1ef --- /dev/null +++ b/app/file/file-utils.c @@ -0,0 +1,249 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * file-utils.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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpthumb/gimpthumb.h" + +#include "core/core-types.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" + +#include "plug-in/gimppluginmanager-file.h" + +#include "file-utils.h" + +#include "gimp-intl.h" + + +static gboolean +file_utils_filename_is_uri (const gchar *filename, + GError **error) +{ + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strstr (filename, "://")) + { + gchar *scheme; + gchar *canon; + + scheme = g_strndup (filename, (strstr (filename, "://") - filename)); + canon = g_strdup (scheme); + + g_strcanon (canon, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.", '-'); + + if (strcmp (scheme, canon) || ! g_ascii_isgraph (canon[0])) + { + g_set_error (error, G_FILE_ERROR, 0, + _("'%s:' is not a valid URI scheme"), scheme); + + g_free (scheme); + g_free (canon); + + return FALSE; + } + + g_free (scheme); + g_free (canon); + + if (! g_utf8_validate (filename, -1, NULL)) + { + g_set_error_literal (error, + G_CONVERT_ERROR, + G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + _("Invalid character sequence in URI")); + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + +GFile * +file_utils_filename_to_file (Gimp *gimp, + const gchar *filename, + GError **error) +{ + GFile *file; + gchar *absolute; + GError *temp_error = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + file = g_file_new_for_uri (filename); + + if (! file) + { + /* Despite the docs says it never fails, it actually can on Windows. + * See issue #3093 (and glib#1819). + */ + g_set_error_literal (error, + G_CONVERT_ERROR, + G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + _("Invalid character sequence in URI")); + return NULL; + } + + /* check for prefixes like http or ftp */ + if (gimp_plug_in_manager_file_procedure_find_by_prefix (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_OPEN, + file)) + { + if (g_utf8_validate (filename, -1, NULL)) + { + return file; + } + else + { + g_set_error_literal (error, + G_CONVERT_ERROR, + G_CONVERT_ERROR_ILLEGAL_SEQUENCE, + _("Invalid character sequence in URI")); + return NULL; + } + } + else if (file_utils_filename_is_uri (filename, &temp_error)) + { + return file; + } + else if (temp_error) + { + g_propagate_error (error, temp_error); + g_object_unref (file); + + return NULL; + } + + g_object_unref (file); + + if (! g_path_is_absolute (filename)) + { + gchar *current; + + current = g_get_current_dir (); + absolute = g_build_filename (current, filename, NULL); + g_free (current); + } + else + { + absolute = g_strdup (filename); + } + + file = g_file_new_for_path (absolute); + + g_free (absolute); + + return file; +} + +GdkPixbuf * +file_utils_load_thumbnail (const gchar *filename) +{ + GimpThumbnail *thumbnail = NULL; + GdkPixbuf *pixbuf = NULL; + gchar *uri; + + g_return_val_if_fail (filename != NULL, NULL); + + uri = g_filename_to_uri (filename, NULL, NULL); + + if (uri) + { + thumbnail = gimp_thumbnail_new (); + gimp_thumbnail_set_uri (thumbnail, uri); + + pixbuf = gimp_thumbnail_load_thumb (thumbnail, + (GimpThumbSize) GIMP_THUMBNAIL_SIZE_NORMAL, + NULL); + } + + g_free (uri); + + if (pixbuf) + { + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); + + if (gdk_pixbuf_get_n_channels (pixbuf) != 3) + { + GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + width, height); + + gdk_pixbuf_composite_color (pixbuf, tmp, + 0, 0, width, height, 0, 0, 1.0, 1.0, + GDK_INTERP_NEAREST, 255, + 0, 0, GIMP_CHECK_SIZE_SM, + 0x66666666, 0x99999999); + + g_object_unref (pixbuf); + pixbuf = tmp; + } + } + + return pixbuf; +} + +gboolean +file_utils_save_thumbnail (GimpImage *image, + const gchar *filename) +{ + GFile *file; + gboolean success = FALSE; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + file = gimp_image_get_file (image); + + if (file) + { + gchar *image_uri = g_file_get_uri (file); + gchar *uri = g_filename_to_uri (filename, NULL, NULL); + + if (uri && image_uri && ! strcmp (uri, image_uri)) + { + GimpImagefile *imagefile; + + imagefile = gimp_imagefile_new (image->gimp, file); + success = gimp_imagefile_save_thumbnail (imagefile, NULL, image, + NULL); + g_object_unref (imagefile); + } + + g_free (image_uri); + g_free (uri); + } + + return success; +} diff --git a/app/file/file-utils.h b/app/file/file-utils.h new file mode 100644 index 0000000..02a30cc --- /dev/null +++ b/app/file/file-utils.h @@ -0,0 +1,33 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * file-utils.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 . + */ + +#ifndef __FILE_UTILS_H__ +#define __FILE_UTILS_H__ + + +GFile * file_utils_filename_to_file (Gimp *gimp, + const gchar *filename, + GError **error); + +GdkPixbuf * file_utils_load_thumbnail (const gchar *filename); +gboolean file_utils_save_thumbnail (GimpImage *image, + const gchar *filename); + + +#endif /* __FILE_UTILS_H__ */ diff --git a/app/file/gimp-file.h b/app/file/gimp-file.h new file mode 100644 index 0000000..a18442f --- /dev/null +++ b/app/file/gimp-file.h @@ -0,0 +1,30 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-file.h + * Copyright (C) 2009 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 . + */ + +#ifndef __GIMP_FILE_H__ +#define __GIMP_FILE_H__ + +/* Data keys for Gimp */ +#define GIMP_FILE_OPEN_LAST_FILE_KEY "gimp-file-open-last-file" +#define GIMP_FILE_SAVE_LAST_FILE_KEY "gimp-file-save-last-file" +#define GIMP_FILE_EXPORT_LAST_FILE_KEY "gimp-file-export-last-file" + + +#endif /* __GIMP_FILE_H__ */ diff --git a/app/gegl/Makefile.am b/app/gegl/Makefile.am new file mode 100644 index 0000000..e402056 --- /dev/null +++ b/app/gegl/Makefile.am @@ -0,0 +1,99 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-GEGL\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libappgegl-generic.a \ + libappgegl-sse2.a \ + libappgegl.a + +libappgegl_generic_a_sources = \ + gimp-gegl-enums.h \ + gimp-gegl-types.h \ + gimp-babl.c \ + gimp-babl.h \ + gimp-babl-compat.c \ + gimp-babl-compat.h \ + gimp-gegl.c \ + gimp-gegl.h \ + gimp-gegl-apply-operation.c \ + gimp-gegl-apply-operation.h \ + gimp-gegl-loops.cc \ + gimp-gegl-loops.h \ + gimp-gegl-mask.c \ + gimp-gegl-mask.h \ + gimp-gegl-mask-combine.cc \ + gimp-gegl-mask-combine.h \ + gimp-gegl-nodes.c \ + gimp-gegl-nodes.h \ + gimp-gegl-tile-compat.c \ + gimp-gegl-tile-compat.h \ + gimp-gegl-utils.c \ + gimp-gegl-utils.h \ + gimpapplicator.c \ + gimpapplicator.h \ + gimptilehandlervalidate.c \ + gimptilehandlervalidate.h + +libappgegl_generic_a_built_sources = gimp-gegl-enums.c + +libappgegl_sse2_a_sources = \ + gimp-gegl-loops-sse2.c \ + gimp-gegl-loops-sse2.h + +libappgegl_generic_a_SOURCES = $(libappgegl_generic_a_built_sources) $(libappgegl_generic_a_sources) + +libappgegl_sse2_a_SOURCES = $(libappgegl_sse2_a_sources) + +libappgegl_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS) + +libappgegl_a_SOURCES = + + +libappgegl.a: libappgegl-generic.a \ + libappgegl-sse2.a + $(AR) $(ARFLAGS) libappgegl.a \ + $(libappgegl_generic_a_OBJECTS) \ + $(libappgegl_sse2_a_OBJECTS) + $(RANLIB) libappgegl.a + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-ggec +CLEANFILES = $(gen_sources) + +xgen-ggec: $(srcdir)/gimp-gegl-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"core/core-enums.h\"\n#include \"gimp-gegl-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/gimp-gegl-enums.c: xgen-ggec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/gegl/Makefile.in b/app/gegl/Makefile.in new file mode 100644 index 0000000..1e9d24b --- /dev/null +++ b/app/gegl/Makefile.in @@ -0,0 +1,1116 @@ +# 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 = app/gegl +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 = +libappgegl_generic_a_AR = $(AR) $(ARFLAGS) +libappgegl_generic_a_LIBADD = +am__objects_1 = gimp-gegl-enums.$(OBJEXT) +am__objects_2 = gimp-babl.$(OBJEXT) gimp-babl-compat.$(OBJEXT) \ + gimp-gegl.$(OBJEXT) gimp-gegl-apply-operation.$(OBJEXT) \ + gimp-gegl-loops.$(OBJEXT) gimp-gegl-mask.$(OBJEXT) \ + gimp-gegl-mask-combine.$(OBJEXT) gimp-gegl-nodes.$(OBJEXT) \ + gimp-gegl-tile-compat.$(OBJEXT) gimp-gegl-utils.$(OBJEXT) \ + gimpapplicator.$(OBJEXT) gimptilehandlervalidate.$(OBJEXT) +am_libappgegl_generic_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappgegl_generic_a_OBJECTS = $(am_libappgegl_generic_a_OBJECTS) +libappgegl_sse2_a_AR = $(AR) $(ARFLAGS) +libappgegl_sse2_a_LIBADD = +am__objects_3 = libappgegl_sse2_a-gimp-gegl-loops-sse2.$(OBJEXT) +am_libappgegl_sse2_a_OBJECTS = $(am__objects_3) +libappgegl_sse2_a_OBJECTS = $(am_libappgegl_sse2_a_OBJECTS) +libappgegl_a_AR = $(AR) $(ARFLAGS) +libappgegl_a_LIBADD = +am_libappgegl_a_OBJECTS = +libappgegl_a_OBJECTS = $(am_libappgegl_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)/gimp-babl-compat.Po \ + ./$(DEPDIR)/gimp-babl.Po \ + ./$(DEPDIR)/gimp-gegl-apply-operation.Po \ + ./$(DEPDIR)/gimp-gegl-enums.Po ./$(DEPDIR)/gimp-gegl-loops.Po \ + ./$(DEPDIR)/gimp-gegl-mask-combine.Po \ + ./$(DEPDIR)/gimp-gegl-mask.Po ./$(DEPDIR)/gimp-gegl-nodes.Po \ + ./$(DEPDIR)/gimp-gegl-tile-compat.Po \ + ./$(DEPDIR)/gimp-gegl-utils.Po ./$(DEPDIR)/gimp-gegl.Po \ + ./$(DEPDIR)/gimpapplicator.Po \ + ./$(DEPDIR)/gimptilehandlervalidate.Po \ + ./$(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Po +am__mv = mv -f +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 = +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 = $(libappgegl_generic_a_SOURCES) $(libappgegl_sse2_a_SOURCES) \ + $(libappgegl_a_SOURCES) +DIST_SOURCES = $(libappgegl_generic_a_SOURCES) \ + $(libappgegl_sse2_a_SOURCES) $(libappgegl_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-GEGL\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libappgegl-generic.a \ + libappgegl-sse2.a \ + libappgegl.a + +libappgegl_generic_a_sources = \ + gimp-gegl-enums.h \ + gimp-gegl-types.h \ + gimp-babl.c \ + gimp-babl.h \ + gimp-babl-compat.c \ + gimp-babl-compat.h \ + gimp-gegl.c \ + gimp-gegl.h \ + gimp-gegl-apply-operation.c \ + gimp-gegl-apply-operation.h \ + gimp-gegl-loops.cc \ + gimp-gegl-loops.h \ + gimp-gegl-mask.c \ + gimp-gegl-mask.h \ + gimp-gegl-mask-combine.cc \ + gimp-gegl-mask-combine.h \ + gimp-gegl-nodes.c \ + gimp-gegl-nodes.h \ + gimp-gegl-tile-compat.c \ + gimp-gegl-tile-compat.h \ + gimp-gegl-utils.c \ + gimp-gegl-utils.h \ + gimpapplicator.c \ + gimpapplicator.h \ + gimptilehandlervalidate.c \ + gimptilehandlervalidate.h + +libappgegl_generic_a_built_sources = gimp-gegl-enums.c +libappgegl_sse2_a_sources = \ + gimp-gegl-loops-sse2.c \ + gimp-gegl-loops-sse2.h + +libappgegl_generic_a_SOURCES = $(libappgegl_generic_a_built_sources) $(libappgegl_generic_a_sources) +libappgegl_sse2_a_SOURCES = $(libappgegl_sse2_a_sources) +libappgegl_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS) +libappgegl_a_SOURCES = + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-ggec +CLEANFILES = $(gen_sources) +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 app/gegl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/gegl/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) + +libappgegl-generic.a: $(libappgegl_generic_a_OBJECTS) $(libappgegl_generic_a_DEPENDENCIES) $(EXTRA_libappgegl_generic_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappgegl-generic.a + $(AM_V_AR)$(libappgegl_generic_a_AR) libappgegl-generic.a $(libappgegl_generic_a_OBJECTS) $(libappgegl_generic_a_LIBADD) + $(AM_V_at)$(RANLIB) libappgegl-generic.a + +libappgegl-sse2.a: $(libappgegl_sse2_a_OBJECTS) $(libappgegl_sse2_a_DEPENDENCIES) $(EXTRA_libappgegl_sse2_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappgegl-sse2.a + $(AM_V_AR)$(libappgegl_sse2_a_AR) libappgegl-sse2.a $(libappgegl_sse2_a_OBJECTS) $(libappgegl_sse2_a_LIBADD) + $(AM_V_at)$(RANLIB) libappgegl-sse2.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-babl-compat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-babl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-apply-operation.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-enums.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-loops.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-mask-combine.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-mask.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-nodes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-tile-compat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-gegl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpapplicator.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptilehandlervalidate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.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 $@ $< + +libappgegl_sse2_a-gimp-gegl-loops-sse2.o: gimp-gegl-loops-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libappgegl_sse2_a_CFLAGS) $(CFLAGS) -MT libappgegl_sse2_a-gimp-gegl-loops-sse2.o -MD -MP -MF $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Tpo -c -o libappgegl_sse2_a-gimp-gegl-loops-sse2.o `test -f 'gimp-gegl-loops-sse2.c' || echo '$(srcdir)/'`gimp-gegl-loops-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Tpo $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-gegl-loops-sse2.c' object='libappgegl_sse2_a-gimp-gegl-loops-sse2.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) $(libappgegl_sse2_a_CFLAGS) $(CFLAGS) -c -o libappgegl_sse2_a-gimp-gegl-loops-sse2.o `test -f 'gimp-gegl-loops-sse2.c' || echo '$(srcdir)/'`gimp-gegl-loops-sse2.c + +libappgegl_sse2_a-gimp-gegl-loops-sse2.obj: gimp-gegl-loops-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libappgegl_sse2_a_CFLAGS) $(CFLAGS) -MT libappgegl_sse2_a-gimp-gegl-loops-sse2.obj -MD -MP -MF $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Tpo -c -o libappgegl_sse2_a-gimp-gegl-loops-sse2.obj `if test -f 'gimp-gegl-loops-sse2.c'; then $(CYGPATH_W) 'gimp-gegl-loops-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimp-gegl-loops-sse2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Tpo $(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimp-gegl-loops-sse2.c' object='libappgegl_sse2_a-gimp-gegl-loops-sse2.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) $(libappgegl_sse2_a_CFLAGS) $(CFLAGS) -c -o libappgegl_sse2_a-gimp-gegl-loops-sse2.obj `if test -f 'gimp-gegl-loops-sse2.c'; then $(CYGPATH_W) 'gimp-gegl-loops-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimp-gegl-loops-sse2.c'; fi` + +.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 $(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: + -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 clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gimp-babl-compat.Po + -rm -f ./$(DEPDIR)/gimp-babl.Po + -rm -f ./$(DEPDIR)/gimp-gegl-apply-operation.Po + -rm -f ./$(DEPDIR)/gimp-gegl-enums.Po + -rm -f ./$(DEPDIR)/gimp-gegl-loops.Po + -rm -f ./$(DEPDIR)/gimp-gegl-mask-combine.Po + -rm -f ./$(DEPDIR)/gimp-gegl-mask.Po + -rm -f ./$(DEPDIR)/gimp-gegl-nodes.Po + -rm -f ./$(DEPDIR)/gimp-gegl-tile-compat.Po + -rm -f ./$(DEPDIR)/gimp-gegl-utils.Po + -rm -f ./$(DEPDIR)/gimp-gegl.Po + -rm -f ./$(DEPDIR)/gimpapplicator.Po + -rm -f ./$(DEPDIR)/gimptilehandlervalidate.Po + -rm -f ./$(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.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)/gimp-babl-compat.Po + -rm -f ./$(DEPDIR)/gimp-babl.Po + -rm -f ./$(DEPDIR)/gimp-gegl-apply-operation.Po + -rm -f ./$(DEPDIR)/gimp-gegl-enums.Po + -rm -f ./$(DEPDIR)/gimp-gegl-loops.Po + -rm -f ./$(DEPDIR)/gimp-gegl-mask-combine.Po + -rm -f ./$(DEPDIR)/gimp-gegl-mask.Po + -rm -f ./$(DEPDIR)/gimp-gegl-nodes.Po + -rm -f ./$(DEPDIR)/gimp-gegl-tile-compat.Po + -rm -f ./$(DEPDIR)/gimp-gegl-utils.Po + -rm -f ./$(DEPDIR)/gimp-gegl.Po + -rm -f ./$(DEPDIR)/gimpapplicator.Po + -rm -f ./$(DEPDIR)/gimptilehandlervalidate.Po + -rm -f ./$(DEPDIR)/libappgegl_sse2_a-gimp-gegl-loops-sse2.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 + + +libappgegl.a: libappgegl-generic.a \ + libappgegl-sse2.a + $(AR) $(ARFLAGS) libappgegl.a \ + $(libappgegl_generic_a_OBJECTS) \ + $(libappgegl_sse2_a_OBJECTS) + $(RANLIB) libappgegl.a + +xgen-ggec: $(srcdir)/gimp-gegl-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"core/core-enums.h\"\n#include \"gimp-gegl-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/gimp-gegl-enums.c: xgen-ggec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/gegl/gimp-babl-compat.c b/app/gegl/gimp-babl-compat.c new file mode 100644 index 0000000..b077f86 --- /dev/null +++ b/app/gegl/gimp-babl-compat.c @@ -0,0 +1,93 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-babl-compat.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "gimp-gegl-types.h" + +#include "gimp-babl.h" +#include "gimp-babl-compat.h" + + +GimpImageType +gimp_babl_format_get_image_type (const Babl *format) +{ + const Babl *model; + + g_return_val_if_fail (format != NULL, -1); + + model = babl_format_get_model (format); + + if (model == babl_model ("Y") || + model == babl_model ("Y'")) + { + return GIMP_GRAY_IMAGE; + } + else if (model == babl_model ("YA") || + model == babl_model ("Y'A")) + { + return GIMP_GRAYA_IMAGE; + } + else if (model == babl_model ("RGB") || + model == babl_model ("R'G'B'")) + { + return GIMP_RGB_IMAGE; + } + else if (model == babl_model ("RGBA") || + model == babl_model ("R'G'B'A")) + { + return GIMP_RGBA_IMAGE; + } + else if (babl_format_is_palette (format)) + { + if (babl_format_has_alpha (format)) + return GIMP_INDEXEDA_IMAGE; + else + return GIMP_INDEXED_IMAGE; + } + + g_return_val_if_reached (-1); +} + +const Babl * +gimp_babl_compat_u8_format (const Babl *format) +{ + g_return_val_if_fail (format != NULL, NULL); + + /* indexed images only exist in u8, return the same format */ + if (babl_format_is_palette (format)) + return format; + + return gimp_babl_format (gimp_babl_format_get_base_type (format), + GIMP_PRECISION_U8_GAMMA, + babl_format_has_alpha (format)); +} + +const Babl * +gimp_babl_compat_u8_mask_format (const Babl *format) +{ + g_return_val_if_fail (format != NULL, NULL); + + return gimp_babl_format (gimp_babl_format_get_base_type (format), + GIMP_PRECISION_U8_LINEAR, + FALSE); +} diff --git a/app/gegl/gimp-babl-compat.h b/app/gegl/gimp-babl-compat.h new file mode 100644 index 0000000..778bf3e --- /dev/null +++ b/app/gegl/gimp-babl-compat.h @@ -0,0 +1,31 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-babl-compat.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BABL_COMPAT_H__ +#define __GIMP_BABL_COMPAT_H__ + + +GimpImageType gimp_babl_format_get_image_type (const Babl *format); + +const Babl * gimp_babl_compat_u8_format (const Babl *format); +const Babl * gimp_babl_compat_u8_mask_format (const Babl *format); + + +#endif /* __GIMP_BABL_COMPAT_H__ */ diff --git a/app/gegl/gimp-babl.c b/app/gegl/gimp-babl.c new file mode 100644 index 0000000..b2dc20a --- /dev/null +++ b/app/gegl/gimp-babl.c @@ -0,0 +1,1415 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-babl.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "gimp-gegl-types.h" + +#include "gimp-babl.h" + +#include "gimp-intl.h" + + +void +gimp_babl_init (void) +{ + babl_format_new ("name", "R u8", + babl_model ("RGBA"), + babl_type ("u8"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' u8", + babl_model ("R'G'B'A"), + babl_type ("u8"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G u8", + babl_model ("RGBA"), + babl_type ("u8"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' u8", + babl_model ("R'G'B'A"), + babl_type ("u8"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B u8", + babl_model ("RGBA"), + babl_type ("u8"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' u8", + babl_model ("R'G'B'A"), + babl_type ("u8"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A u8", + babl_model ("RGBA"), + babl_type ("u8"), + babl_component ("A"), + NULL); + + babl_format_new ("name", "R u16", + babl_model ("RGBA"), + babl_type ("u16"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' u16", + babl_model ("R'G'B'A"), + babl_type ("u16"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G u16", + babl_model ("RGBA"), + babl_type ("u16"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' u16", + babl_model ("R'G'B'A"), + babl_type ("u16"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B u16", + babl_model ("RGBA"), + babl_type ("u16"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' u16", + babl_model ("R'G'B'A"), + babl_type ("u16"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A u16", + babl_model ("RGBA"), + babl_type ("u16"), + babl_component ("A"), + NULL); + + babl_format_new ("name", "R u32", + babl_model ("RGBA"), + babl_type ("u32"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' u32", + babl_model ("R'G'B'A"), + babl_type ("u32"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G u32", + babl_model ("RGBA"), + babl_type ("u32"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' u32", + babl_model ("R'G'B'A"), + babl_type ("u32"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B u32", + babl_model ("RGBA"), + babl_type ("u32"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' u32", + babl_model ("R'G'B'A"), + babl_type ("u32"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A u32", + babl_model ("RGBA"), + babl_type ("u32"), + babl_component ("A"), + NULL); + + babl_format_new ("name", "R half", + babl_model ("RGBA"), + babl_type ("half"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' half", + babl_model ("R'G'B'A"), + babl_type ("half"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G half", + babl_model ("RGBA"), + babl_type ("half"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' half", + babl_model ("R'G'B'A"), + babl_type ("half"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B half", + babl_model ("RGBA"), + babl_type ("half"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' half", + babl_model ("R'G'B'A"), + babl_type ("half"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A half", + babl_model ("RGBA"), + babl_type ("half"), + babl_component ("A"), + NULL); + + babl_format_new ("name", "R float", + babl_model ("RGBA"), + babl_type ("float"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' float", + babl_model ("R'G'B'A"), + babl_type ("float"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G float", + babl_model ("RGBA"), + babl_type ("float"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' float", + babl_model ("R'G'B'A"), + babl_type ("float"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B float", + babl_model ("RGBA"), + babl_type ("float"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' float", + babl_model ("R'G'B'A"), + babl_type ("float"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A float", + babl_model ("RGBA"), + babl_type ("float"), + babl_component ("A"), + NULL); + + babl_format_new ("name", "R double", + babl_model ("RGBA"), + babl_type ("double"), + babl_component ("R"), + NULL); + babl_format_new ("name", "R' double", + babl_model ("R'G'B'A"), + babl_type ("double"), + babl_component ("R'"), + NULL); + babl_format_new ("name", "G double", + babl_model ("RGBA"), + babl_type ("double"), + babl_component ("G"), + NULL); + babl_format_new ("name", "G' double", + babl_model ("R'G'B'A"), + babl_type ("double"), + babl_component ("G'"), + NULL); + babl_format_new ("name", "B double", + babl_model ("RGBA"), + babl_type ("double"), + babl_component ("B"), + NULL); + babl_format_new ("name", "B' double", + babl_model ("R'G'B'A"), + babl_type ("double"), + babl_component ("B'"), + NULL); + babl_format_new ("name", "A double", + babl_model ("RGBA"), + babl_type ("double"), + babl_component ("A"), + NULL); +} + +void +gimp_babl_init_fishes (GimpInitStatusFunc status_callback) +{ + /* create a bunch of fishes - to decrease the initial lazy + * initialization cost for some interactions + */ + static const struct + { + const gchar *from_format; + const gchar *to_format; + } + fishes[] = + { + { "Y' u8", "RaGaBaA float" }, + { "Y u8", "RaGaBaA float" }, + { "R'G'B'A u8", "RaGaBaA float" }, + { "R'G'B'A float", "R'G'B'A u8" }, + { "R'G'B'A float", "R'G'B' u8" }, + { "R'G'B'A u8", "RGBA float" }, + { "RGBA float", "R'G'B'A u8" }, + { "RGBA float", "R'G'B'A u8" }, + { "RGBA float", "R'G'B'A float" }, + { "Y' u8", "R'G'B' u8" }, + { "Y u8", "Y float" }, + { "R'G'B' u8", "cairo-RGB24" }, + { "R'G'B' u8", "R'G'B'A float" }, + { "R'G'B' u8", "R'G'B'A u8" }, + { "R'G'B'A u8", "R'G'B'A float" }, + { "R'G'B'A u8", "cairo-ARGB32" }, + { "R'G'B'A double", "RGBA float" }, + { "R'G'B'A float", "RGBA double" }, + { "R'G'B' u8", "RGB float" }, + { "RGB float", "R'G'B'A float" }, + { "R'G'B' u8", "RGBA float" }, + { "RaGaBaA float", "R'G'B'A float" }, + { "RaGaBaA float", "RGBA float" }, + { "RGBA float", "RaGaBaA float" }, + { "R'G'B' u8", "RaGaBaA float" }, + { "cairo-ARGB32", "R'G'B'A u8" } + }; + + gint i; + + for (i = 0; i < G_N_ELEMENTS (fishes); i++) + { + status_callback (NULL, NULL, + (gdouble) (i + 1) / + (gdouble) G_N_ELEMENTS (fishes) * 0.8); + + babl_fish (babl_format (fishes[i].from_format), + babl_format (fishes[i].to_format)); + } +} + +static const struct +{ + const gchar *name; + const gchar *description; +} +babl_descriptions[] = +{ + { "RGB u8", N_("RGB") }, + { "R'G'B' u8", N_("RGB") }, + { "RGB u16", N_("RGB") }, + { "R'G'B' u16", N_("RGB") }, + { "RGB u32", N_("RGB") }, + { "R'G'B' u32", N_("RGB") }, + { "RGB half", N_("RGB") }, + { "R'G'B' half", N_("RGB") }, + { "RGB float", N_("RGB") }, + { "R'G'B' float", N_("RGB") }, + { "RGB double", N_("RGB") }, + { "R'G'B' double", N_("RGB") }, + + { "RGBA u8", N_("RGB-alpha") }, + { "R'G'B'A u8", N_("RGB-alpha") }, + { "RGBA u16", N_("RGB-alpha") }, + { "R'G'B'A u16", N_("RGB-alpha") }, + { "RGBA u32", N_("RGB-alpha") }, + { "R'G'B'A u32", N_("RGB-alpha") }, + { "RGBA half", N_("RGB-alpha") }, + { "R'G'B'A half", N_("RGB-alpha") }, + { "RGBA float", N_("RGB-alpha") }, + { "R'G'B'A float", N_("RGB-alpha") }, + { "RGBA double", N_("RGB-alpha") }, + { "R'G'B'A double", N_("RGB-alpha") }, + + { "Y u8", N_("Grayscale") }, + { "Y' u8", N_("Grayscale") }, + { "Y u16", N_("Grayscale") }, + { "Y' u16", N_("Grayscale") }, + { "Y u32", N_("Grayscale") }, + { "Y' u32", N_("Grayscale") }, + { "Y half", N_("Grayscale") }, + { "Y' half", N_("Grayscale") }, + { "Y float", N_("Grayscale") }, + { "Y' float", N_("Grayscale") }, + { "Y double", N_("Grayscale") }, + { "Y' double", N_("Grayscale") }, + + { "YA u8", N_("Grayscale-alpha") }, + { "Y'A u8", N_("Grayscale-alpha") }, + { "YA u16", N_("Grayscale-alpha") }, + { "Y'A u16", N_("Grayscale-alpha") }, + { "YA u32", N_("Grayscale-alpha") }, + { "Y'A u32", N_("Grayscale-alpha") }, + { "YA half", N_("Grayscale-alpha") }, + { "Y'A half", N_("Grayscale-alpha") }, + { "YA float", N_("Grayscale-alpha") }, + { "Y'A float", N_("Grayscale-alpha") }, + { "YA double", N_("Grayscale-alpha") }, + { "Y'A double", N_("Grayscale-alpha") }, + + { "R u8", N_("Red component") }, + { "R' u8", N_("Red component") }, + { "R u16", N_("Red component") }, + { "R' u16", N_("Red component") }, + { "R u32", N_("Red component") }, + { "R' u32", N_("Red component") }, + { "R half", N_("Red component") }, + { "R' half", N_("Red component") }, + { "R float", N_("Red component") }, + { "R' float", N_("Red component") }, + { "R double", N_("Red component") }, + { "R' double", N_("Red component") }, + + { "G u8", N_("Green component") }, + { "G' u8", N_("Green component") }, + { "G u16", N_("Green component") }, + { "G' u16", N_("Green component") }, + { "G u32", N_("Green component") }, + { "G' u32", N_("Green component") }, + { "G half", N_("Green component") }, + { "G' half", N_("Green component") }, + { "G float", N_("Green component") }, + { "G' float", N_("Green component") }, + { "G double", N_("Green component") }, + { "G' double", N_("Green component") }, + + { "B u8", N_("Blue component") }, + { "B' u8", N_("Blue component") }, + { "B u16", N_("Blue component") }, + { "B' u16", N_("Blue component") }, + { "B u32", N_("Blue component") }, + { "B' u32", N_("Blue component") }, + { "B half", N_("Blue component") }, + { "B' half", N_("Blue component") }, + { "B float", N_("Blue component") }, + { "B' float", N_("Blue component") }, + { "B double", N_("Blue component") }, + { "B' double", N_("Blue component") }, + + { "A u8", N_("Alpha component") }, + { "A u16", N_("Alpha component") }, + { "A u32", N_("Alpha component") }, + { "A half", N_("Alpha component") }, + { "A float", N_("Alpha component") }, + { "A double", N_("Alpha component") } +}; + +static GHashTable *babl_description_hash = NULL; + +const gchar * +gimp_babl_format_get_description (const Babl *babl) +{ + const gchar *description; + + g_return_val_if_fail (babl != NULL, NULL); + + if (G_UNLIKELY (! babl_description_hash)) + { + gint i; + + babl_description_hash = g_hash_table_new (g_str_hash, + g_str_equal); + + for (i = 0; i < G_N_ELEMENTS (babl_descriptions); i++) + g_hash_table_insert (babl_description_hash, + (gpointer) babl_descriptions[i].name, + gettext (babl_descriptions[i].description)); + } + + if (babl_format_is_palette (babl)) + { + if (babl_format_has_alpha (babl)) + return _("Indexed-alpha"); + else + return _("Indexed"); + } + + description = g_hash_table_lookup (babl_description_hash, + babl_get_name (babl)); + + if (description) + return description; + + return g_strconcat ("ERROR: unknown Babl format ", + babl_get_name (babl), NULL); +} + +GimpColorProfile * +gimp_babl_format_get_color_profile (const Babl *format) +{ + static GimpColorProfile *srgb_profile = NULL; + static GimpColorProfile *linear_rgb_profile = NULL; + static GimpColorProfile *gray_profile = NULL; + static GimpColorProfile *linear_gray_profile = NULL; + + g_return_val_if_fail (format != NULL, NULL); + + if (gimp_babl_format_get_base_type (format) == GIMP_GRAY) + { + if (gimp_babl_format_get_linear (format)) + { + if (! linear_gray_profile) + { + linear_gray_profile = gimp_color_profile_new_d65_gray_linear (); + g_object_add_weak_pointer (G_OBJECT (linear_gray_profile), + (gpointer) &linear_gray_profile); + } + + return linear_gray_profile; + } + else + { + if (! gray_profile) + { + gray_profile = gimp_color_profile_new_d65_gray_srgb_trc (); + g_object_add_weak_pointer (G_OBJECT (gray_profile), + (gpointer) &gray_profile); + } + + return gray_profile; + } + } + else + { + if (gimp_babl_format_get_linear (format)) + { + if (! linear_rgb_profile) + { + linear_rgb_profile = gimp_color_profile_new_rgb_srgb_linear (); + g_object_add_weak_pointer (G_OBJECT (linear_rgb_profile), + (gpointer) &linear_rgb_profile); + } + + return linear_rgb_profile; + } + else + { + if (! srgb_profile) + { + srgb_profile = gimp_color_profile_new_rgb_srgb (); + g_object_add_weak_pointer (G_OBJECT (srgb_profile), + (gpointer) &srgb_profile); + } + + return srgb_profile; + } + } +} + +GimpImageBaseType +gimp_babl_format_get_base_type (const Babl *format) +{ + const Babl *model; + + g_return_val_if_fail (format != NULL, -1); + + model = babl_format_get_model (format); + + if (model == babl_model ("Y") || + model == babl_model ("Y'") || + model == babl_model ("YA") || + model == babl_model ("Y'A")) + { + return GIMP_GRAY; + } + else if (model == babl_model ("RGB") || + model == babl_model ("R'G'B'") || + model == babl_model ("RGBA") || + model == babl_model ("R'G'B'A") || + model == babl_model ("RaGaBaA") || + model == babl_model ("R'aG'aB'aA")) + { + return GIMP_RGB; + } + else if (babl_format_is_palette (format)) + { + return GIMP_INDEXED; + } + + g_return_val_if_reached (-1); +} + +GimpComponentType +gimp_babl_format_get_component_type (const Babl *format) +{ + const Babl *type; + + g_return_val_if_fail (format != NULL, -1); + + type = babl_format_get_type (format, 0); + + if (type == babl_type ("u8")) + return GIMP_COMPONENT_TYPE_U8; + else if (type == babl_type ("u16")) + return GIMP_COMPONENT_TYPE_U16; + else if (type == babl_type ("u32")) + return GIMP_COMPONENT_TYPE_U32; + else if (type == babl_type ("half")) + return GIMP_COMPONENT_TYPE_HALF; + else if (type == babl_type ("float")) + return GIMP_COMPONENT_TYPE_FLOAT; + else if (type == babl_type ("double")) + return GIMP_COMPONENT_TYPE_DOUBLE; + + g_return_val_if_reached (-1); +} + +GimpPrecision +gimp_babl_format_get_precision (const Babl *format) +{ + const Babl *type; + + g_return_val_if_fail (format != NULL, -1); + + type = babl_format_get_type (format, 0); + + if (gimp_babl_format_get_linear (format)) + { + if (type == babl_type ("u8")) + return GIMP_PRECISION_U8_LINEAR; + else if (type == babl_type ("u16")) + return GIMP_PRECISION_U16_LINEAR; + else if (type == babl_type ("u32")) + return GIMP_PRECISION_U32_LINEAR; + else if (type == babl_type ("half")) + return GIMP_PRECISION_HALF_LINEAR; + else if (type == babl_type ("float")) + return GIMP_PRECISION_FLOAT_LINEAR; + else if (type == babl_type ("double")) + return GIMP_PRECISION_DOUBLE_LINEAR; + } + else + { + if (type == babl_type ("u8")) + return GIMP_PRECISION_U8_GAMMA; + else if (type == babl_type ("u16")) + return GIMP_PRECISION_U16_GAMMA; + else if (type == babl_type ("u32")) + return GIMP_PRECISION_U32_GAMMA; + else if (type == babl_type ("half")) + return GIMP_PRECISION_HALF_GAMMA; + else if (type == babl_type ("float")) + return GIMP_PRECISION_FLOAT_GAMMA; + else if (type == babl_type ("double")) + return GIMP_PRECISION_DOUBLE_GAMMA; + } + + g_return_val_if_reached (-1); +} + +gboolean +gimp_babl_format_get_linear (const Babl *format) +{ + const Babl *model; + + g_return_val_if_fail (format != NULL, FALSE); + + model = babl_format_get_model (format); + + if (model == babl_model ("Y") || + model == babl_model ("YA") || + model == babl_model ("RGB") || + model == babl_model ("RGBA") || + model == babl_model ("RaGaBaA")) + { + return TRUE; + } + else if (model == babl_model ("Y'") || + model == babl_model ("Y'A") || + model == babl_model ("R'G'B'") || + model == babl_model ("R'G'B'A") || + model == babl_model ("R'aG'aB'aA")) + { + return FALSE; + } + else if (babl_format_is_palette (format)) + { + return FALSE; + } + + g_return_val_if_reached (FALSE); +} + +GimpComponentType +gimp_babl_component_type (GimpPrecision precision) +{ + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + case GIMP_PRECISION_U8_GAMMA: + return GIMP_COMPONENT_TYPE_U8; + + case GIMP_PRECISION_U16_LINEAR: + case GIMP_PRECISION_U16_GAMMA: + return GIMP_COMPONENT_TYPE_U16; + + case GIMP_PRECISION_U32_LINEAR: + case GIMP_PRECISION_U32_GAMMA: + return GIMP_COMPONENT_TYPE_U32; + + case GIMP_PRECISION_HALF_LINEAR: + case GIMP_PRECISION_HALF_GAMMA: + return GIMP_COMPONENT_TYPE_HALF; + + case GIMP_PRECISION_FLOAT_LINEAR: + case GIMP_PRECISION_FLOAT_GAMMA: + return GIMP_COMPONENT_TYPE_FLOAT; + + case GIMP_PRECISION_DOUBLE_LINEAR: + case GIMP_PRECISION_DOUBLE_GAMMA: + return GIMP_COMPONENT_TYPE_DOUBLE; + } + + g_return_val_if_reached (-1); +} + +gboolean +gimp_babl_linear (GimpPrecision precision) +{ + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + case GIMP_PRECISION_U16_LINEAR: + case GIMP_PRECISION_U32_LINEAR: + case GIMP_PRECISION_HALF_LINEAR: + case GIMP_PRECISION_FLOAT_LINEAR: + case GIMP_PRECISION_DOUBLE_LINEAR: + return TRUE; + + case GIMP_PRECISION_U8_GAMMA: + case GIMP_PRECISION_U16_GAMMA: + case GIMP_PRECISION_U32_GAMMA: + case GIMP_PRECISION_HALF_GAMMA: + case GIMP_PRECISION_FLOAT_GAMMA: + case GIMP_PRECISION_DOUBLE_GAMMA: + return FALSE; + } + + g_return_val_if_reached (FALSE); +} + +GimpPrecision +gimp_babl_precision (GimpComponentType component, + gboolean linear) +{ + switch (component) + { + case GIMP_COMPONENT_TYPE_U8: + if (linear) + return GIMP_PRECISION_U8_LINEAR; + else + return GIMP_PRECISION_U8_GAMMA; + + case GIMP_COMPONENT_TYPE_U16: + if (linear) + return GIMP_PRECISION_U16_LINEAR; + else + return GIMP_PRECISION_U16_GAMMA; + + case GIMP_COMPONENT_TYPE_U32: + if (linear) + return GIMP_PRECISION_U32_LINEAR; + else + return GIMP_PRECISION_U32_GAMMA; + + case GIMP_COMPONENT_TYPE_HALF: + if (linear) + return GIMP_PRECISION_HALF_LINEAR; + else + return GIMP_PRECISION_HALF_GAMMA; + + case GIMP_COMPONENT_TYPE_FLOAT: + if (linear) + return GIMP_PRECISION_FLOAT_LINEAR; + else + return GIMP_PRECISION_FLOAT_GAMMA; + + case GIMP_COMPONENT_TYPE_DOUBLE: + if (linear) + return GIMP_PRECISION_DOUBLE_LINEAR; + else + return GIMP_PRECISION_DOUBLE_GAMMA; + + default: + break; + } + + g_return_val_if_reached (-1); +} + +gboolean +gimp_babl_is_valid (GimpImageBaseType base_type, + GimpPrecision precision) +{ + switch (base_type) + { + case GIMP_RGB: + case GIMP_GRAY: + return TRUE; + + case GIMP_INDEXED: + switch (precision) + { + case GIMP_PRECISION_U8_GAMMA: + return TRUE; + + default: + return FALSE; + } + } + + g_return_val_if_reached (FALSE); +} + +GimpComponentType +gimp_babl_is_bounded (GimpPrecision precision) +{ + switch (gimp_babl_component_type (precision)) + { + case GIMP_COMPONENT_TYPE_U8: + case GIMP_COMPONENT_TYPE_U16: + case GIMP_COMPONENT_TYPE_U32: + return TRUE; + + case GIMP_COMPONENT_TYPE_HALF: + case GIMP_COMPONENT_TYPE_FLOAT: + case GIMP_COMPONENT_TYPE_DOUBLE: + return FALSE; + } + + g_return_val_if_reached (FALSE); +} + +const Babl * +gimp_babl_format (GimpImageBaseType base_type, + GimpPrecision precision, + gboolean with_alpha) +{ + switch (base_type) + { + case GIMP_RGB: + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + if (with_alpha) + return babl_format ("RGBA u8"); + else + return babl_format ("RGB u8"); + + case GIMP_PRECISION_U8_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A u8"); + else + return babl_format ("R'G'B' u8"); + + case GIMP_PRECISION_U16_LINEAR: + if (with_alpha) + return babl_format ("RGBA u16"); + else + return babl_format ("RGB u16"); + + case GIMP_PRECISION_U16_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A u16"); + else + return babl_format ("R'G'B' u16"); + + case GIMP_PRECISION_U32_LINEAR: + if (with_alpha) + return babl_format ("RGBA u32"); + else + return babl_format ("RGB u32"); + + case GIMP_PRECISION_U32_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A u32"); + else + return babl_format ("R'G'B' u32"); + + case GIMP_PRECISION_HALF_LINEAR: + if (with_alpha) + return babl_format ("RGBA half"); + else + return babl_format ("RGB half"); + + case GIMP_PRECISION_HALF_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A half"); + else + return babl_format ("R'G'B' half"); + + case GIMP_PRECISION_FLOAT_LINEAR: + if (with_alpha) + return babl_format ("RGBA float"); + else + return babl_format ("RGB float"); + + case GIMP_PRECISION_FLOAT_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A float"); + else + return babl_format ("R'G'B' float"); + + case GIMP_PRECISION_DOUBLE_LINEAR: + if (with_alpha) + return babl_format ("RGBA double"); + else + return babl_format ("RGB double"); + + case GIMP_PRECISION_DOUBLE_GAMMA: + if (with_alpha) + return babl_format ("R'G'B'A double"); + else + return babl_format ("R'G'B' double"); + + default: + break; + } + break; + + case GIMP_GRAY: + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + if (with_alpha) + return babl_format ("YA u8"); + else + return babl_format ("Y u8"); + + case GIMP_PRECISION_U8_GAMMA: + if (with_alpha) + return babl_format ("Y'A u8"); + else + return babl_format ("Y' u8"); + + case GIMP_PRECISION_U16_LINEAR: + if (with_alpha) + return babl_format ("YA u16"); + else + return babl_format ("Y u16"); + + case GIMP_PRECISION_U16_GAMMA: + if (with_alpha) + return babl_format ("Y'A u16"); + else + return babl_format ("Y' u16"); + + case GIMP_PRECISION_U32_LINEAR: + if (with_alpha) + return babl_format ("YA u32"); + else + return babl_format ("Y u32"); + + case GIMP_PRECISION_U32_GAMMA: + if (with_alpha) + return babl_format ("Y'A u32"); + else + return babl_format ("Y' u32"); + + case GIMP_PRECISION_HALF_LINEAR: + if (with_alpha) + return babl_format ("YA half"); + else + return babl_format ("Y half"); + + case GIMP_PRECISION_HALF_GAMMA: + if (with_alpha) + return babl_format ("Y'A half"); + else + return babl_format ("Y' half"); + + case GIMP_PRECISION_FLOAT_LINEAR: + if (with_alpha) + return babl_format ("YA float"); + else + return babl_format ("Y float"); + + case GIMP_PRECISION_FLOAT_GAMMA: + if (with_alpha) + return babl_format ("Y'A float"); + else + return babl_format ("Y' float"); + + case GIMP_PRECISION_DOUBLE_LINEAR: + if (with_alpha) + return babl_format ("YA double"); + else + return babl_format ("Y double"); + + case GIMP_PRECISION_DOUBLE_GAMMA: + if (with_alpha) + return babl_format ("Y'A double"); + else + return babl_format ("Y' double"); + + default: + break; + } + break; + + case GIMP_INDEXED: + /* need to use the image's api for this */ + break; + } + + g_return_val_if_reached (NULL); +} + +const Babl * +gimp_babl_mask_format (GimpPrecision precision) +{ + switch (gimp_babl_component_type (precision)) + { + case GIMP_COMPONENT_TYPE_U8: return babl_format ("Y u8"); + case GIMP_COMPONENT_TYPE_U16: return babl_format ("Y u16"); + case GIMP_COMPONENT_TYPE_U32: return babl_format ("Y u32"); + case GIMP_COMPONENT_TYPE_HALF: return babl_format ("Y half"); + case GIMP_COMPONENT_TYPE_FLOAT: return babl_format ("Y float"); + case GIMP_COMPONENT_TYPE_DOUBLE: return babl_format ("Y double"); + } + + g_return_val_if_reached (NULL); +} + +const Babl * +gimp_babl_component_format (GimpImageBaseType base_type, + GimpPrecision precision, + gint index) +{ + switch (base_type) + { + case GIMP_RGB: + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + switch (index) + { + case 0: return babl_format ("R u8"); + case 1: return babl_format ("G u8"); + case 2: return babl_format ("B u8"); + case 3: return babl_format ("A u8"); + default: + break; + } + break; + + case GIMP_PRECISION_U8_GAMMA: + switch (index) + { + case 0: return babl_format ("R' u8"); + case 1: return babl_format ("G' u8"); + case 2: return babl_format ("B' u8"); + case 3: return babl_format ("A u8"); + default: + break; + } + break; + + case GIMP_PRECISION_U16_LINEAR: + switch (index) + { + case 0: return babl_format ("R u16"); + case 1: return babl_format ("G u16"); + case 2: return babl_format ("B u16"); + case 3: return babl_format ("A u16"); + default: + break; + } + break; + + case GIMP_PRECISION_U16_GAMMA: + switch (index) + { + case 0: return babl_format ("R' u16"); + case 1: return babl_format ("G' u16"); + case 2: return babl_format ("B' u16"); + case 3: return babl_format ("A u16"); + default: + break; + } + break; + + case GIMP_PRECISION_U32_LINEAR: + switch (index) + { + case 0: return babl_format ("R u32"); + case 1: return babl_format ("G u32"); + case 2: return babl_format ("B u32"); + case 3: return babl_format ("A u32"); + default: + break; + } + break; + + case GIMP_PRECISION_U32_GAMMA: + switch (index) + { + case 0: return babl_format ("R' u32"); + case 1: return babl_format ("G' u32"); + case 2: return babl_format ("B' u32"); + case 3: return babl_format ("A u32"); + default: + break; + } + break; + + case GIMP_PRECISION_HALF_LINEAR: + switch (index) + { + case 0: return babl_format ("R half"); + case 1: return babl_format ("G half"); + case 2: return babl_format ("B half"); + case 3: return babl_format ("A half"); + default: + break; + } + break; + + case GIMP_PRECISION_HALF_GAMMA: + switch (index) + { + case 0: return babl_format ("R' half"); + case 1: return babl_format ("G' half"); + case 2: return babl_format ("B' half"); + case 3: return babl_format ("A half"); + default: + break; + } + break; + + case GIMP_PRECISION_FLOAT_LINEAR: + switch (index) + { + case 0: return babl_format ("R float"); + case 1: return babl_format ("G float"); + case 2: return babl_format ("B float"); + case 3: return babl_format ("A float"); + default: + break; + } + break; + + case GIMP_PRECISION_FLOAT_GAMMA: + switch (index) + { + case 0: return babl_format ("R' float"); + case 1: return babl_format ("G' float"); + case 2: return babl_format ("B' float"); + case 3: return babl_format ("A float"); + default: + break; + } + break; + + case GIMP_PRECISION_DOUBLE_LINEAR: + switch (index) + { + case 0: return babl_format ("R double"); + case 1: return babl_format ("G double"); + case 2: return babl_format ("B double"); + case 3: return babl_format ("A double"); + default: + break; + } + break; + + case GIMP_PRECISION_DOUBLE_GAMMA: + switch (index) + { + case 0: return babl_format ("R' double"); + case 1: return babl_format ("G' double"); + case 2: return babl_format ("B' double"); + case 3: return babl_format ("A double"); + default: + break; + } + break; + + default: + break; + } + break; + + case GIMP_GRAY: + switch (precision) + { + case GIMP_PRECISION_U8_LINEAR: + switch (index) + { + case 0: return babl_format ("Y u8"); + case 1: return babl_format ("A u8"); + default: + break; + } + break; + + case GIMP_PRECISION_U8_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' u8"); + case 1: return babl_format ("A u8"); + default: + break; + } + break; + + case GIMP_PRECISION_U16_LINEAR: + switch (index) + { + case 0: return babl_format ("Y u16"); + case 1: return babl_format ("A u16"); + default: + break; + } + break; + + case GIMP_PRECISION_U16_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' u16"); + case 1: return babl_format ("A u16"); + default: + break; + } + break; + + case GIMP_PRECISION_U32_LINEAR: + switch (index) + { + case 0: return babl_format ("Y u32"); + case 1: return babl_format ("A u32"); + default: + break; + } + break; + + case GIMP_PRECISION_U32_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' u32"); + case 1: return babl_format ("A u32"); + default: + break; + } + break; + + case GIMP_PRECISION_HALF_LINEAR: + switch (index) + { + case 0: return babl_format ("Y half"); + case 1: return babl_format ("A half"); + default: + break; + } + break; + + case GIMP_PRECISION_HALF_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' half"); + case 1: return babl_format ("A half"); + default: + break; + } + break; + + case GIMP_PRECISION_FLOAT_LINEAR: + switch (index) + { + case 0: return babl_format ("Y float"); + case 1: return babl_format ("A float"); + default: + break; + } + break; + + case GIMP_PRECISION_FLOAT_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' float"); + case 1: return babl_format ("A float"); + default: + break; + } + break; + + case GIMP_PRECISION_DOUBLE_LINEAR: + switch (index) + { + case 0: return babl_format ("Y double"); + case 1: return babl_format ("A double"); + default: + break; + } + break; + + case GIMP_PRECISION_DOUBLE_GAMMA: + switch (index) + { + case 0: return babl_format ("Y' double"); + case 1: return babl_format ("A double"); + default: + break; + } + break; + + default: + break; + } + break; + + case GIMP_INDEXED: + /* need to use the image's api for this */ + break; + } + + g_return_val_if_reached (NULL); +} + +const Babl * +gimp_babl_format_change_component_type (const Babl *format, + GimpComponentType component) +{ + g_return_val_if_fail (format != NULL, NULL); + + return gimp_babl_format (gimp_babl_format_get_base_type (format), + gimp_babl_precision ( + component, + gimp_babl_format_get_linear (format)), + babl_format_has_alpha (format)); +} + +const Babl * +gimp_babl_format_change_linear (const Babl *format, + gboolean linear) +{ + g_return_val_if_fail (format != NULL, NULL); + + return gimp_babl_format (gimp_babl_format_get_base_type (format), + gimp_babl_precision ( + gimp_babl_format_get_component_type (format), + linear), + babl_format_has_alpha (format)); +} + +gchar ** +gimp_babl_print_pixel (const Babl *format, + gpointer pixel) +{ + GimpPrecision precision; + gint n_components; + guchar tmp_pixel[32]; + gchar **strings; + + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (pixel != NULL, NULL); + + precision = gimp_babl_format_get_precision (format); + + if (babl_format_is_palette (format)) + { + const Babl *f = gimp_babl_format (GIMP_RGB, precision, + babl_format_has_alpha (format)); + + babl_process (babl_fish (format, f), pixel, tmp_pixel, 1); + + format = f; + pixel = tmp_pixel; + } + + n_components = babl_format_get_n_components (format); + + strings = g_new0 (gchar *, n_components + 1); + + switch (gimp_babl_format_get_component_type (format)) + { + case GIMP_COMPONENT_TYPE_U8: + { + guchar *color = pixel; + gint i; + + for (i = 0; i < n_components; i++) + strings[i] = g_strdup_printf ("%d", color[i]); + } + break; + + case GIMP_COMPONENT_TYPE_U16: + { + guint16 *color = pixel; + gint i; + + for (i = 0; i < n_components; i++) + strings[i] = g_strdup_printf ("%u", color[i]); + } + break; + + case GIMP_COMPONENT_TYPE_U32: + { + guint32 *color = pixel; + gint i; + + for (i = 0; i < n_components; i++) + strings[i] = g_strdup_printf ("%u", color[i]); + } + break; + + case GIMP_COMPONENT_TYPE_HALF: + { + GimpPrecision p; + const Babl *f; + + p = gimp_babl_precision (GIMP_COMPONENT_TYPE_FLOAT, + gimp_babl_format_get_linear (format)); + + f = gimp_babl_format (gimp_babl_format_get_base_type (format), + p, + babl_format_has_alpha (format)); + + babl_process (babl_fish (format, f), pixel, tmp_pixel, 1); + + pixel = tmp_pixel; + } + /* fall through */ + + case GIMP_COMPONENT_TYPE_FLOAT: + { + gfloat *color = pixel; + gint i; + + for (i = 0; i < n_components; i++) + strings[i] = g_strdup_printf ("%0.6f", color[i]); + } + break; + + case GIMP_COMPONENT_TYPE_DOUBLE: + { + gdouble *color = pixel; + gint i; + + for (i = 0; i < n_components; i++) + strings[i] = g_strdup_printf ("%0.6f", color[i]); + } + break; + } + + return strings; +} diff --git a/app/gegl/gimp-babl.h b/app/gegl/gimp-babl.h new file mode 100644 index 0000000..3062e02 --- /dev/null +++ b/app/gegl/gimp-babl.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-babl.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BABL_H__ +#define __GIMP_BABL_H__ + + +void gimp_babl_init (void); +void gimp_babl_init_fishes (GimpInitStatusFunc status_callback); + +const gchar * gimp_babl_format_get_description (const Babl *format); +GimpColorProfile * gimp_babl_format_get_color_profile (const Babl *format); + +GimpImageBaseType gimp_babl_format_get_base_type (const Babl *format); +GimpComponentType gimp_babl_format_get_component_type (const Babl *format); +GimpPrecision gimp_babl_format_get_precision (const Babl *format); +gboolean gimp_babl_format_get_linear (const Babl *format); + +GimpComponentType gimp_babl_component_type (GimpPrecision precision); +gboolean gimp_babl_linear (GimpPrecision precision); +GimpPrecision gimp_babl_precision (GimpComponentType component, + gboolean linear); + +gboolean gimp_babl_is_valid (GimpImageBaseType base_type, + GimpPrecision precision); +GimpComponentType gimp_babl_is_bounded (GimpPrecision precision); + +const Babl * gimp_babl_format (GimpImageBaseType base_type, + GimpPrecision precision, + gboolean with_alpha); +const Babl * gimp_babl_mask_format (GimpPrecision precision); +const Babl * gimp_babl_component_format (GimpImageBaseType base_type, + GimpPrecision precision, + gint index); + +const Babl * gimp_babl_format_change_component_type (const Babl *format, + GimpComponentType component); +const Babl * gimp_babl_format_change_linear (const Babl *format, + gboolean linear); + +gchar ** gimp_babl_print_pixel (const Babl *format, + gpointer pixel); + + +#endif /* __GIMP_BABL_H__ */ diff --git a/app/gegl/gimp-gegl-apply-operation.c b/app/gegl/gimp-gegl-apply-operation.c new file mode 100644 index 0000000..d76b3b1 --- /dev/null +++ b/app/gegl/gimp-gegl-apply-operation.c @@ -0,0 +1,827 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-apply-operation.c + * Copyright (C) 2012 Øyvind Kolås + * Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "gimp-gegl-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" +#include "core/gimpchunkiterator.h" +#include "core/gimpprogress.h" + +#include "gimp-gegl-apply-operation.h" +#include "gimp-gegl-loops.h" +#include "gimp-gegl-nodes.h" +#include "gimp-gegl-utils.h" + + +/* iteration interval when applying an operation interactively + * (with progress indication) + */ +#define APPLY_OPERATION_INTERACTIVE_INTERVAL (1.0 / 8.0) /* seconds */ + +/* iteration interval when applying an operation non-interactively + * (without progress indication) + */ +#define APPLY_OPERATION_NON_INTERACTIVE_INTERVAL 1.0 /* seconds */ + + +void +gimp_gegl_apply_operation (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gboolean crop_input) +{ + gimp_gegl_apply_cached_operation (src_buffer, + progress, undo_desc, + operation, + src_buffer != NULL, + dest_buffer, + dest_rect, + crop_input, + NULL, NULL, 0, + FALSE); +} + +static void +gimp_gegl_apply_operation_cancel (GimpProgress *progress, + gboolean *cancel) +{ + *cancel = TRUE; +} + +gboolean +gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + gboolean connect_src_buffer, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gboolean crop_input, + GeglBuffer *cache, + const GeglRectangle *valid_rects, + gint n_valid_rects, + gboolean cancelable) +{ + GeglNode *gegl; + GeglNode *effect; + GeglNode *dest_node; + GeglNode *underlying_operation; + GeglNode *operation_src_node = NULL; + GeglBuffer *result_buffer; + GimpChunkIterator *iter; + cairo_region_t *region; + gboolean progress_started = FALSE; + gboolean cancel = FALSE; + gint64 all_pixels; + gint64 done_pixels; + + g_return_val_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (GEGL_IS_NODE (operation), FALSE); + g_return_val_if_fail (GEGL_IS_BUFFER (dest_buffer), FALSE); + g_return_val_if_fail (cache == NULL || GEGL_IS_BUFFER (cache), FALSE); + g_return_val_if_fail (valid_rects == NULL || cache != NULL, FALSE); + g_return_val_if_fail (valid_rects == NULL || n_valid_rects != 0, FALSE); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + if (progress) + { + if (gimp_progress_is_active (progress)) + { + if (undo_desc) + gimp_progress_set_text_literal (progress, undo_desc); + + progress_started = FALSE; + cancelable = FALSE; + } + else + { + gimp_progress_start (progress, cancelable, "%s", undo_desc); + + if (cancelable) + g_signal_connect (progress, "cancel", + G_CALLBACK (gimp_gegl_apply_operation_cancel), + &cancel); + + progress_started = TRUE; + } + } + else + { + cancelable = FALSE; + } + + gegl_buffer_freeze_changed (dest_buffer); + + underlying_operation = gimp_gegl_node_get_underlying_operation (operation); + + result_buffer = dest_buffer; + + if (result_buffer == src_buffer && + ! (gimp_gegl_node_is_point_operation (underlying_operation) || + gimp_gegl_node_is_source_operation (underlying_operation))) + { + /* Write the result to a temporary buffer, instead of directly to + * dest_buffer, since reading and writing the same buffer doesn't + * generally work with non-point ops when working in chunks. + * + * See bug #701875. + */ + + if (cache) + { + /* If we have a cache, use it directly as the temporary result + * buffer, and skip copying the cached results to result_buffer + * below. Instead, the cached results are copied together with the + * newly rendered results in a single step at the end of processing. + */ + + g_warn_if_fail (cache != dest_buffer); + + result_buffer = g_object_ref (cache); + + cache = NULL; + } + else + { + result_buffer = gegl_buffer_new ( + dest_rect, gegl_buffer_get_format (dest_buffer)); + } + } + + all_pixels = (gint64) dest_rect->width * (gint64) dest_rect->height; + done_pixels = 0; + + region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) dest_rect); + + if (n_valid_rects > 0) + { + gint i; + + for (i = 0; i < n_valid_rects; i++) + { + GeglRectangle valid_rect; + + if (! gegl_rectangle_intersect (&valid_rect, + &valid_rects[i], dest_rect)) + { + continue; + } + + if (cache) + { + gimp_gegl_buffer_copy ( + cache, &valid_rect, GEGL_ABYSS_NONE, + result_buffer, &valid_rect); + } + + cairo_region_subtract_rectangle (region, + (cairo_rectangle_int_t *) + &valid_rect); + + done_pixels += (gint64) valid_rect.width * (gint64) valid_rect.height; + + if (progress) + { + gimp_progress_set_value (progress, + (gdouble) done_pixels / + (gdouble) all_pixels); + } + } + } + + gegl = gegl_node_new (); + + if (! gegl_node_get_parent (operation)) + gegl_node_add_child (gegl, operation); + + effect = operation; + + if (connect_src_buffer || crop_input) + { + GeglNode *src_node; + + operation_src_node = gegl_node_get_producer (operation, "input", NULL); + + src_node = operation_src_node; + + if (connect_src_buffer) + { + src_node = gegl_node_new_child (gegl, + "operation", "gegl:buffer-source", + "buffer", src_buffer, + NULL); + } + + if (crop_input) + { + GeglNode *crop_node; + + crop_node = gegl_node_new_child (gegl, + "operation", "gegl:crop", + "x", (gdouble) dest_rect->x, + "y", (gdouble) dest_rect->y, + "width", (gdouble) dest_rect->width, + "height", (gdouble) dest_rect->height, + NULL); + + gegl_node_connect_to (src_node, "output", + crop_node, "input"); + + src_node = crop_node; + } + + if (! gegl_node_has_pad (operation, "input")) + { + effect = gegl_node_new_child (gegl, + "operation", "gimp:normal", + NULL); + + gegl_node_connect_to (operation, "output", + effect, "aux"); + } + + gegl_node_connect_to (src_node, "output", + effect, "input"); + } + + dest_node = gegl_node_new_child (gegl, + "operation", "gegl:write-buffer", + "buffer", result_buffer, + NULL); + + gegl_node_connect_to (effect, "output", + dest_node, "input"); + + iter = gimp_chunk_iterator_new (region); + + if (progress && + /* avoid the interactive iteration interval for area filters (or meta ops + * that potentially involve area filters), since their processing speed + * tends to be sensitive to the chunk size. + */ + ! gimp_gegl_node_is_area_filter_operation (underlying_operation)) + { + /* we use a shorter iteration interval for interactive use (when there's + * progress indication), to stay responsive. + */ + gimp_chunk_iterator_set_interval ( + iter, + APPLY_OPERATION_INTERACTIVE_INTERVAL); + } + else + { + /* we use a longer iteration interval for non-interactive use (when + * there's no progress indication), or when applying an area filter (see + * above), as this generally allows for faster processing. we don't + * avoid chunking altogether, since *some* chunking is still desirable to + * reduce the space needed for intermediate results. + */ + gimp_chunk_iterator_set_interval ( + iter, + APPLY_OPERATION_NON_INTERACTIVE_INTERVAL); + } + + while (gimp_chunk_iterator_next (iter)) + { + GeglRectangle render_rect; + + if (cancelable) + { + while (! cancel && g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + + if (cancel) + break; + } + + while (gimp_chunk_iterator_get_rect (iter, &render_rect)) + { + gegl_node_blit (dest_node, 1.0, &render_rect, NULL, NULL, 0, + GEGL_BLIT_DEFAULT); + + done_pixels += (gint64) render_rect.width * + (gint64) render_rect.height; + } + + if (progress) + { + gimp_progress_set_value (progress, + (gdouble) done_pixels / + (gdouble) all_pixels); + } + } + + if (result_buffer != dest_buffer) + { + if (! cancel) + gimp_gegl_buffer_copy (result_buffer, dest_rect, GEGL_ABYSS_NONE, + dest_buffer, dest_rect); + + g_object_unref (result_buffer); + } + + gegl_buffer_thaw_changed (dest_buffer); + + g_object_unref (gegl); + + if (operation_src_node) + { + gegl_node_connect_to (operation_src_node, "output", + operation, "input"); + } + + if (progress_started) + { + gimp_progress_end (progress); + + if (cancelable) + g_signal_handlers_disconnect_by_func (progress, + gimp_gegl_apply_operation_cancel, + &cancel); + } + + return ! cancel; +} + +void +gimp_gegl_apply_dither (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gint levels, + gint dither_type) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + levels = CLAMP (levels, 2, 65536); + + node = gegl_node_new_child (NULL, + "operation", "gegl:dither", + "red-levels", levels, + "green-levels", levels, + "blue-levels", levels, + "alpha-bits", levels, + "dither-method", dither_type, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_flatten (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GimpRGB *background, + GimpLayerColorSpace composite_space) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + g_return_if_fail (background != NULL); + + node = gimp_gegl_create_flatten_node (background, composite_space); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_feather (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock) +{ + GaussianBlurAbyssPolicy abyss_policy; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + if (edge_lock) + abyss_policy = GAUSSIAN_BLUR_ABYSS_CLAMP; + else + abyss_policy = GAUSSIAN_BLUR_ABYSS_NONE; + + /* 3.5 is completely magic and picked to visually match the old + * gaussian_blur_region() on a crappy laptop display + */ + gimp_gegl_apply_gaussian_blur (src_buffer, + progress, undo_desc, + dest_buffer, dest_rect, + radius_x / 3.5, + radius_y / 3.5, + abyss_policy); +} + +void +gimp_gegl_apply_border (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + switch (style) + { + case GIMP_CHANNEL_BORDER_STYLE_HARD: + case GIMP_CHANNEL_BORDER_STYLE_FEATHERED: + { + gboolean feather = style == GIMP_CHANNEL_BORDER_STYLE_FEATHERED; + + node = gegl_node_new_child (NULL, + "operation", "gimp:border", + "radius-x", radius_x, + "radius-y", radius_y, + "feather", feather, + "edge-lock", edge_lock, + NULL); + } + break; + + case GIMP_CHANNEL_BORDER_STYLE_SMOOTH: + { + GeglNode *input, *output; + GeglNode *grow, *shrink, *subtract; + + node = gegl_node_new (); + + input = gegl_node_get_input_proxy (node, "input"); + output = gegl_node_get_output_proxy (node, "output"); + + /* Duplicate special-case behavior of "gimp:border". */ + if (radius_x == 1 && radius_y == 1) + { + grow = gegl_node_new_child (node, + "operation", "gegl:nop", + NULL); + shrink = gegl_node_new_child (node, + "operation", "gimp:shrink", + "radius-x", 1, + "radius-y", 1, + "edge-lock", edge_lock, + NULL); + } + else + { + grow = gegl_node_new_child (node, + "operation", "gimp:grow", + "radius-x", radius_x, + "radius-y", radius_y, + NULL); + shrink = gegl_node_new_child (node, + "operation", "gimp:shrink", + "radius-x", radius_x + 1, + "radius-y", radius_y + 1, + "edge-lock", edge_lock, + NULL); + } + + subtract = gegl_node_new_child (node, + "operation", "gegl:subtract", + NULL); + + gegl_node_link_many (input, grow, subtract, output, NULL); + gegl_node_link (input, shrink); + gegl_node_connect_to (shrink, "output", subtract, "aux"); + } + break; + + default: + gimp_assert_not_reached (); + } + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect, TRUE); + g_object_unref (node); +} + +void +gimp_gegl_apply_grow (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gimp:grow", + "radius-x", radius_x, + "radius-y", radius_y, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect, TRUE); + g_object_unref (node); +} + +void +gimp_gegl_apply_shrink (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y, + gboolean edge_lock) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gimp:shrink", + "radius-x", radius_x, + "radius-y", radius_y, + "edge-lock", edge_lock, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect, TRUE); + g_object_unref (node); +} + +void +gimp_gegl_apply_flood (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gimp:flood", + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect, TRUE); + g_object_unref (node); +} + +void +gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble std_dev_x, + gdouble std_dev_y, + GaussianBlurAbyssPolicy abyss_policy) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:gaussian-blur", + "std-dev-x", std_dev_x, + "std-dev-y", std_dev_y, + "abyss-policy", abyss_policy, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_invert_gamma (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:invert-gamma", + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_invert_linear (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:invert-linear", + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_opacity (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GeglBuffer *mask, + gint mask_offset_x, + gint mask_offset_y, + gdouble opacity) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + g_return_if_fail (mask == NULL || GEGL_IS_BUFFER (mask)); + + node = gimp_gegl_create_apply_opacity_node (mask, + mask_offset_x, + mask_offset_y, + opacity); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_scale (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GimpInterpolationType interpolation_type, + gdouble x, + gdouble y) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:scale-ratio", + "origin-x", 0.0, + "origin-y", 0.0, + "sampler", interpolation_type, + "abyss-policy", GEGL_ABYSS_CLAMP, + "x", x, + "y", y, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_set_alpha (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gdouble value) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gimp:set-alpha", + "value", value, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_threshold (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gdouble value) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:threshold", + "value", value, + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} + +void +gimp_gegl_apply_transform (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GimpInterpolationType interpolation_type, + GimpMatrix3 *transform) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gegl:transform", + "near-z", GIMP_TRANSFORM_NEAR_Z, + "sampler", interpolation_type, + NULL); + + gimp_gegl_node_set_matrix (node, transform); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, NULL, FALSE); + g_object_unref (node); +} diff --git a/app/gegl/gimp-gegl-apply-operation.h b/app/gegl/gimp-gegl-apply-operation.h new file mode 100644 index 0000000..3b19ee0 --- /dev/null +++ b/app/gegl/gimp-gegl-apply-operation.h @@ -0,0 +1,172 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-apply-operation.h + * Copyright (C) 2012 Øyvind Kolås + * Sven Neumann + * Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_APPLY_OPERATION_H__ +#define __GIMP_GEGL_APPLY_OPERATION_H__ + + +/* generic functions, also used by the specific ones below */ + +void gimp_gegl_apply_operation (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gboolean crop_input); + +gboolean gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglNode *operation, + gboolean connect_src_buffer, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gboolean crop_input, + GeglBuffer *cache, + const GeglRectangle *valid_rects, + gint n_valid_rects, + gboolean cancellable); + + +/* apply specific operations */ + +void gimp_gegl_apply_dither (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gint levels, + gint dither_type); + +void gimp_gegl_apply_flatten (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GimpRGB *background, + GimpLayerColorSpace composite_space); + +void gimp_gegl_apply_feather (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble radius_x, + gdouble radius_y, + gboolean edge_lock); + +void gimp_gegl_apply_border (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y, + GimpChannelBorderStyle style, + gboolean edge_lock); + +void gimp_gegl_apply_grow (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y); + +void gimp_gegl_apply_shrink (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gint radius_x, + gint radius_y, + gboolean edge_lock); + +void gimp_gegl_apply_flood (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect); + +/* UGLY: private enum of gegl:gaussian-blur */ +typedef enum +{ + GAUSSIAN_BLUR_ABYSS_NONE, + GAUSSIAN_BLUR_ABYSS_CLAMP +} GaussianBlurAbyssPolicy; + +void gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble std_dev_x, + gdouble std_dev_y, + GaussianBlurAbyssPolicy abyss_policy); + +void gimp_gegl_apply_invert_gamma (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer); + +void gimp_gegl_apply_invert_linear (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer); + +void gimp_gegl_apply_opacity (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GeglBuffer *mask, + gint mask_offset_x, + gint mask_offset_y, + gdouble opacity); + +void gimp_gegl_apply_scale (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GimpInterpolationType interpolation_type, + gdouble x, + gdouble y); + +void gimp_gegl_apply_set_alpha (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gdouble value); + +void gimp_gegl_apply_threshold (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + gdouble value); + +void gimp_gegl_apply_transform (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + GimpInterpolationType interpolation_type, + GimpMatrix3 *transform); + + +#endif /* __GIMP_GEGL_APPLY_OPERATION_H__ */ diff --git a/app/gegl/gimp-gegl-enums.c b/app/gegl/gimp-gegl-enums.c new file mode 100644 index 0000000..de9c7b5 --- /dev/null +++ b/app/gegl/gimp-gegl-enums.c @@ -0,0 +1,43 @@ + +/* Generated data (by gimp-mkenums) */ + +#include "config.h" +#include +#include "libgimpbase/gimpbase.h" +#include "core/core-enums.h" +#include "gimp-gegl-enums.h" +#include "gimp-intl.h" + +/* enumerations from "gimp-gegl-enums.h" */ +GType +gimp_cage_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_CAGE_MODE_CAGE_CHANGE, "GIMP_CAGE_MODE_CAGE_CHANGE", "cage-change" }, + { GIMP_CAGE_MODE_DEFORM, "GIMP_CAGE_MODE_DEFORM", "deform" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_CAGE_MODE_CAGE_CHANGE, NC_("cage-mode", "Create or adjust the cage"), NULL }, + { GIMP_CAGE_MODE_DEFORM, NC_("cage-mode", "Deform the cage\nto deform the image"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpCageMode", values); + gimp_type_set_translation_context (type, "cage-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + + +/* Generated data ends here */ + diff --git a/app/gegl/gimp-gegl-enums.h b/app/gegl/gimp-gegl-enums.h new file mode 100644 index 0000000..022c93a --- /dev/null +++ b/app/gegl/gimp-gegl-enums.h @@ -0,0 +1,35 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-enums.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 . + */ + +#ifndef __GIMP_GEGL_ENUMS_H__ +#define __GIMP_GEGL_ENUMS_H__ + + +#define GIMP_TYPE_CAGE_MODE (gimp_cage_mode_get_type ()) + +GType gimp_cage_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_CAGE_MODE_CAGE_CHANGE, /*< desc="Create or adjust the cage" >*/ + GIMP_CAGE_MODE_DEFORM /*< desc="Deform the cage\nto deform the image" >*/ +} GimpCageMode; + + +#endif /* __GIMP_GEGL_ENUMS_H__ */ diff --git a/app/gegl/gimp-gegl-loops-sse2.c b/app/gegl/gimp-gegl-loops-sse2.c new file mode 100644 index 0000000..b9bf3ae --- /dev/null +++ b/app/gegl/gimp-gegl-loops-sse2.c @@ -0,0 +1,127 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-loops-sse2.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "gimp-gegl-types.h" + +#include "gimp-gegl-loops-sse2.h" + + +#if COMPILE_SSE2_INTRINISICS + +#include + + +/* helper function of gimp_gegl_smudge_with_paint_process_sse2() + * src and dest can be the same address + */ +static inline void +gimp_gegl_smudge_with_paint_blend_sse2 (const gfloat *src1, + gfloat src1_rate, + const gfloat *src2, + gfloat src2_rate, + gfloat *dest, + gboolean no_erasing_src2) +{ + /* 2017/4/13 shark0r : According to my test, SSE decreases about 25% + * execution time + */ + + __m128 v_src1 = _mm_loadu_ps (src1); + __m128 v_src2 = _mm_loadu_ps (src2); + __m128 *v_dest = (__v4sf *) dest; + + gfloat orginal_src2_alpha; + gfloat src1_alpha; + gfloat src2_alpha; + gfloat result_alpha; + + orginal_src2_alpha = v_src2[3]; + src1_alpha = src1_rate * v_src1[3]; + src2_alpha = src2_rate * orginal_src2_alpha; + result_alpha = src1_alpha + src2_alpha; + + if (result_alpha == 0) + { + *v_dest = _mm_set1_ps (0); + return; + } + + *v_dest = (v_src1 * _mm_set1_ps (src1_alpha) + + v_src2 * _mm_set1_ps (src2_alpha)) / + _mm_set1_ps (result_alpha); + + if (no_erasing_src2) + { + result_alpha = MAX (result_alpha, orginal_src2_alpha); + } + + dest[3] = result_alpha; +} + +/* helper function of gimp_gegl_smudge_with_paint() + * + * note that it's the caller's responsibility to verify that the buffers are + * properly aligned + */ +void +gimp_gegl_smudge_with_paint_process_sse2 (gfloat *accum, + const gfloat *canvas, + gfloat *paint, + gint count, + const gfloat *brush_color, + gfloat brush_a, + gboolean no_erasing, + gfloat flow, + gfloat rate) +{ + while (count--) + { + /* blend accum_buffer and canvas_buffer to accum_buffer */ + gimp_gegl_smudge_with_paint_blend_sse2 (accum, rate, canvas, 1 - rate, + accum, no_erasing); + + /* blend accum_buffer and brush color/pixmap to paint_buffer */ + if (brush_a == 0) /* pure smudge */ + { + memcpy (paint, accum, sizeof (gfloat) * 4); + } + else + { + const gfloat *src1 = brush_color ? brush_color : paint; + + gimp_gegl_smudge_with_paint_blend_sse2 (src1, flow, accum, 1 - flow, + paint, no_erasing); + } + + accum += 4; + canvas += 4; + paint += 4; + } +} + +#endif /* COMPILE_SSE2_INTRINISICS */ diff --git a/app/gegl/gimp-gegl-loops-sse2.h b/app/gegl/gimp-gegl-loops-sse2.h new file mode 100644 index 0000000..fc8100c --- /dev/null +++ b/app/gegl/gimp-gegl-loops-sse2.h @@ -0,0 +1,40 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-loops-sse2.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_LOOPS_SSE2_H__ +#define __GIMP_GEGL_LOOPS_SSE2_H__ + + +#if COMPILE_SSE2_INTRINISICS + +void gimp_gegl_smudge_with_paint_process_sse2 (gfloat *accum, + const gfloat *canvas, + gfloat *paint, + gint count, + const gfloat *brush_color, + gfloat brush_a, + gboolean no_erasing, + gfloat flow, + gfloat rate); + +#endif /* COMPILE_SSE2_INTRINISICS */ + + +#endif /* __GIMP_GEGL_LOOPS_SSE2_H__ */ diff --git a/app/gegl/gimp-gegl-loops.cc b/app/gegl/gimp-gegl-loops.cc new file mode 100644 index 0000000..4564bcd --- /dev/null +++ b/app/gegl/gimp-gegl-loops.cc @@ -0,0 +1,1089 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-loops.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +extern "C" +{ + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "gimp-gegl-types.h" + +#include "gimp-babl.h" +#include "gimp-gegl-loops.h" +#include "gimp-gegl-loops-sse2.h" + +#include "core/gimp-atomic.h" +#include "core/gimp-utils.h" +#include "core/gimpprogress.h" + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + +#define SHIFTED_AREA(dest, src) \ + const GeglRectangle dest##_area_ = { \ + src##_area->x + (dest##_rect->x - src##_rect->x), \ + src##_area->y + (dest##_rect->y - src##_rect->y), \ + src##_area->width, src##_area->height \ + }; \ + const GeglRectangle * const dest##_area = &dest##_area_ + + +void +gimp_gegl_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglAbyssPolicy abyss_policy, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect) +{ + GeglRectangle real_dest_rect; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = src_rect; + + real_dest_rect = *dest_rect; + real_dest_rect.width = src_rect->width; + real_dest_rect.height = src_rect->height; + + dest_rect = &real_dest_rect; + + if (gegl_buffer_get_format (src_buffer) == + gegl_buffer_get_format (dest_buffer)) + { + gboolean skip_abyss = FALSE; + GeglRectangle src_abyss; + GeglRectangle dest_abyss; + + if (abyss_policy == GEGL_ABYSS_NONE) + { + src_abyss = *gegl_buffer_get_abyss (src_buffer); + dest_abyss = *gegl_buffer_get_abyss (dest_buffer); + + skip_abyss = ! (gegl_rectangle_contains (&src_abyss, src_rect) && + gegl_rectangle_contains (&dest_abyss, dest_rect)); + } + + if (skip_abyss) + { + if (src_buffer < dest_buffer) + { + gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer)); + gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer)); + } + else + { + gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer)); + gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer)); + } + + gegl_buffer_set_abyss (src_buffer, src_rect); + gegl_buffer_set_abyss (dest_buffer, dest_rect); + } + + gegl_buffer_copy (src_buffer, src_rect, abyss_policy, + dest_buffer, dest_rect); + + if (skip_abyss) + { + gegl_buffer_set_abyss (src_buffer, &src_abyss); + gegl_buffer_set_abyss (dest_buffer, &dest_abyss); + + gegl_tile_handler_unlock (GEGL_TILE_HANDLER (src_buffer)); + gegl_tile_handler_unlock (GEGL_TILE_HANDLER (dest_buffer)); + } + } + else + { + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + SHIFTED_AREA (dest, src); + + gegl_buffer_copy (src_buffer, src_area, abyss_policy, + dest_buffer, dest_area); + }); + } +} + +void +gimp_gegl_clear (GeglBuffer *buffer, + const GeglRectangle *rect) +{ + const Babl *format; + gint bpp; + gint n_components; + gint bpc; + gint alpha_offset; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + format = gegl_buffer_get_format (buffer); + + if (! babl_format_has_alpha (format)) + return; + + bpp = babl_format_get_bytes_per_pixel (format); + n_components = babl_format_get_n_components (format); + bpc = bpp / n_components; + alpha_offset = (n_components - 1) * bpc; + + gegl_parallel_distribute_area ( + rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + GeglBufferIterator *iter; + + iter = gegl_buffer_iterator_new (buffer, area, 0, format, + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, + 1); + + while (gegl_buffer_iterator_next (iter)) + { + guint8 *data = (guint8 *) iter->items[0].data; + gint i; + + data += alpha_offset; + + for (i = 0; i < iter->length; i++) + { + memset (data, 0, bpc); + + data += bpp; + } + } + }); +} + +void +gimp_gegl_convolve (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + const gfloat *kernel, + gint kernel_size, + gdouble divisor, + GimpConvolutionType mode, + gboolean alpha_weighting) +{ + gfloat *src; + gint src_rowstride; + + const Babl *src_format; + const Babl *dest_format; + gint src_components; + gint dest_components; + gfloat offset; + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + src_format = gegl_buffer_get_format (src_buffer); + + if (babl_format_is_palette (src_format)) + src_format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (src_format)); + else + src_format = gimp_babl_format (gimp_babl_format_get_base_type (src_format), + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (src_format)); + + dest_format = gegl_buffer_get_format (dest_buffer); + + if (babl_format_is_palette (dest_format)) + dest_format = gimp_babl_format (GIMP_RGB, + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (dest_format)); + else + dest_format = gimp_babl_format (gimp_babl_format_get_base_type (dest_format), + GIMP_PRECISION_FLOAT_LINEAR, + babl_format_has_alpha (dest_format)); + + src_components = babl_format_get_n_components (src_format); + dest_components = babl_format_get_n_components (dest_format); + + /* Get source pixel data */ + src_rowstride = src_components * src_rect->width; + src = g_new (gfloat, src_rowstride * src_rect->height); + gegl_buffer_get (src_buffer, src_rect, 1.0, src_format, src, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + /* If the mode is NEGATIVE_CONVOL, the offset should be 0.5 */ + if (mode == GIMP_NEGATIVE_CONVOL) + { + offset = 0.5; + mode = GIMP_NORMAL_CONVOL; + } + else + { + offset = 0.0; + } + + gegl_parallel_distribute_area ( + dest_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *dest_area) + { + const gint components = src_components; + const gint a_component = components - 1; + const gint margin = kernel_size / 2; + GeglBufferIterator *dest_iter; + + /* Set up dest iterator */ + dest_iter = gegl_buffer_iterator_new (dest_buffer, dest_area, 0, dest_format, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (dest_iter)) + { + /* Convolve the src image using the convolution kernel, writing + * to dest Convolve is not tile-enabled--use accordingly + */ + gfloat *dest = (gfloat *) dest_iter->items[0].data; + const gint x1 = 0; + const gint y1 = 0; + const gint x2 = src_rect->width - 1; + const gint y2 = src_rect->height - 1; + const gint dest_x1 = dest_iter->items[0].roi.x; + const gint dest_y1 = dest_iter->items[0].roi.y; + const gint dest_x2 = dest_iter->items[0].roi.x + dest_iter->items[0].roi.width; + const gint dest_y2 = dest_iter->items[0].roi.y + dest_iter->items[0].roi.height; + gint x, y; + + for (y = dest_y1; y < dest_y2; y++) + { + gfloat *d = dest; + + if (alpha_weighting) + { + for (x = dest_x1; x < dest_x2; x++) + { + const gfloat *m = kernel; + gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; + gdouble weighted_divisor = 0.0; + gint i, j, b; + + for (j = y - margin; j <= y + margin; j++) + { + for (i = x - margin; i <= x + margin; i++, m++) + { + gint xx = CLAMP (i, x1, x2); + gint yy = CLAMP (j, y1, y2); + const gfloat *s = src + yy * src_rowstride + xx * components; + const gfloat a = s[a_component]; + + if (a) + { + gdouble mult_alpha = *m * a; + + weighted_divisor += mult_alpha; + + for (b = 0; b < a_component; b++) + total[b] += mult_alpha * s[b]; + + total[a_component] += mult_alpha; + } + } + } + + if (weighted_divisor == 0.0) + weighted_divisor = divisor; + + for (b = 0; b < a_component; b++) + total[b] /= weighted_divisor; + + total[a_component] /= divisor; + + for (b = 0; b < components; b++) + { + total[b] += offset; + + if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) + total[b] = - total[b]; + + *d++ = CLAMP (total[b], 0.0, 1.0); + } + } + } + else + { + for (x = dest_x1; x < dest_x2; x++) + { + const gfloat *m = kernel; + gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; + gint i, j, b; + + for (j = y - margin; j <= y + margin; j++) + { + for (i = x - margin; i <= x + margin; i++, m++) + { + gint xx = CLAMP (i, x1, x2); + gint yy = CLAMP (j, y1, y2); + const gfloat *s = src + yy * src_rowstride + xx * components; + + for (b = 0; b < components; b++) + total[b] += *m * s[b]; + } + } + + for (b = 0; b < components; b++) + { + total[b] = total[b] / divisor + offset; + + if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) + total[b] = - total[b]; + + *d++ = CLAMP (total[b], 0.0, 1.0); + } + } + } + + dest += dest_iter->items[0].roi.width * dest_components; + } + } + }); + + g_free (src); +} + +static inline gfloat +odd_powf (gfloat x, + gfloat y) +{ + if (x >= 0.0f) + return powf ( x, y); + else + return -powf (-x, y); +} + +void +gimp_gegl_dodgeburn (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble exposure, + GimpDodgeBurnType type, + GimpTransferMode mode) +{ + if (type == GIMP_DODGE_BURN_TYPE_BURN) + exposure = -exposure; + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, src); + + iter = gegl_buffer_iterator_new (src_buffer, src_area, 0, + babl_format ("R'G'B'A float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("R'G'B'A float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + switch (mode) + { + gfloat factor; + + case GIMP_TRANSFER_HIGHLIGHTS: + factor = 1.0 + exposure * (0.333333); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest++ = *src++ * factor; + *dest++ = *src++ * factor; + *dest++ = *src++ * factor; + + *dest++ = *src++; + } + } + break; + + case GIMP_TRANSFER_MIDTONES: + if (exposure < 0) + factor = 1.0 - exposure * (0.333333); + else + factor = 1.0 / (1.0 + exposure); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest++ = odd_powf (*src++, factor); + *dest++ = odd_powf (*src++, factor); + *dest++ = odd_powf (*src++, factor); + + *dest++ = *src++; + } + } + break; + + case GIMP_TRANSFER_SHADOWS: + if (exposure >= 0) + factor = 0.333333 * exposure; + else + factor = -0.333333 * exposure; + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *src = (gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + if (exposure >= 0) + { + gfloat s; + + s = *src++; *dest++ = factor + s - factor * s; + s = *src++; *dest++ = factor + s - factor * s; + s = *src++; *dest++ = factor + s - factor * s; + } + else + { + gfloat s; + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + + s = *src++; + if (s < factor) + *dest++ = 0; + else /* factor <= value <=1 */ + *dest++ = (s - factor) / (1.0 - factor); + } + + *dest++ = *src++; + } + } + break; + } + }); +} + +/* helper function of gimp_gegl_smudge_with_paint_process() + src and dest can be the same address + */ +static inline void +gimp_gegl_smudge_with_paint_blend (const gfloat *src1, + gfloat src1_rate, + const gfloat *src2, + gfloat src2_rate, + gfloat *dest, + gboolean no_erasing_src2) +{ + gfloat orginal_src2_alpha; + gfloat src1_alpha; + gfloat src2_alpha; + gfloat result_alpha; + gint b; + + orginal_src2_alpha = src2[3]; + src1_alpha = src1_rate * src1[3]; + src2_alpha = src2_rate * orginal_src2_alpha; + result_alpha = src1_alpha + src2_alpha; + + if (result_alpha == 0) + { + memset (dest, 0, sizeof (gfloat) * 4); + return; + } + + for (b = 0; b < 3; b++) + dest[b] = (src1[b] * src1_alpha + src2[b] * src2_alpha) / result_alpha; + + if (no_erasing_src2) + { + result_alpha = MAX (result_alpha, orginal_src2_alpha); + } + + dest[3] = result_alpha; +} + +/* helper function of gimp_gegl_smudge_with_paint() */ +static void +gimp_gegl_smudge_with_paint_process (gfloat *accum, + const gfloat *canvas, + gfloat *paint, + gint count, + const gfloat *brush_color, + gfloat brush_a, + gboolean no_erasing, + gfloat flow, + gfloat rate) +{ + while (count--) + { + /* blend accum_buffer and canvas_buffer to accum_buffer */ + gimp_gegl_smudge_with_paint_blend (accum, rate, canvas, 1 - rate, + accum, no_erasing); + + /* blend accum_buffer and brush color/pixmap to paint_buffer */ + if (brush_a == 0) /* pure smudge */ + { + memcpy (paint, accum, sizeof (gfloat) * 4); + } + else + { + const gfloat *src1 = brush_color ? brush_color : paint; + + gimp_gegl_smudge_with_paint_blend (src1, flow, accum, 1 - flow, + paint, no_erasing); + } + + accum += 4; + canvas += 4; + paint += 4; + } +} + +/* smudge painting calculation. Currently only smudge tool uses this function + * Accum = rate*Accum + (1-rate)*Canvas + * if brush_color!=NULL + * Paint = flow*brushColor + (1-flow)*Accum + * else + * Paint = flow*Paint + (1-flow)*Accum + */ +void +gimp_gegl_smudge_with_paint (GeglBuffer *accum_buffer, + const GeglRectangle *accum_rect, + GeglBuffer *canvas_buffer, + const GeglRectangle *canvas_rect, + const GimpRGB *brush_color, + GeglBuffer *paint_buffer, + gboolean no_erasing, + gdouble flow, + gdouble rate) +{ + gfloat brush_color_float[4]; + gfloat brush_a = flow; + GeglAccessMode paint_buffer_access_mode = (brush_color ? + GEGL_ACCESS_WRITE : + GEGL_ACCESS_READWRITE); +#if COMPILE_SSE2_INTRINISICS + gboolean sse2 = (gimp_cpu_accel_get_support () & + GIMP_CPU_ACCEL_X86_SSE2); +#endif + + if (! accum_rect) + accum_rect = gegl_buffer_get_extent (accum_buffer); + + if (! canvas_rect) + canvas_rect = gegl_buffer_get_extent (canvas_buffer); + + /* convert brush color from double to float */ + if (brush_color) + { + const gdouble *brush_color_ptr = &brush_color->r; + gint b; + + for (b = 0; b < 4; b++) + brush_color_float[b] = brush_color_ptr[b]; + + brush_a *= brush_color_ptr[3]; + } + + gegl_parallel_distribute_area ( + accum_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *accum_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (canvas, accum); + + iter = gegl_buffer_iterator_new (accum_buffer, accum_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 3); + + gegl_buffer_iterator_add (iter, canvas_buffer, canvas_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + + gegl_buffer_iterator_add (iter, paint_buffer, + GEGL_RECTANGLE (accum_area->x - accum_rect->x, + accum_area->y - accum_rect->y, + 0, 0), + 0, + babl_format ("RGBA float"), + paint_buffer_access_mode, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *accum = (gfloat *) iter->items[0].data; + const gfloat *canvas = (const gfloat *) iter->items[1].data; + gfloat *paint = (gfloat *) iter->items[2].data; + gint count = iter->length; + +#if COMPILE_SSE2_INTRINISICS + if (sse2 && ((guintptr) accum | + (guintptr) canvas | + (guintptr) (brush_color ? brush_color_float : paint) | + (guintptr) paint) % 16 == 0) + { + gimp_gegl_smudge_with_paint_process_sse2 (accum, canvas, paint, count, + brush_color ? brush_color_float : + NULL, + brush_a, + no_erasing, flow, rate); + } + else +#endif + { + gimp_gegl_smudge_with_paint_process (accum, canvas, paint, count, + brush_color ? brush_color_float : + NULL, + brush_a, + no_erasing, flow, rate); + } + } + }); +} + +void +gimp_gegl_apply_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("RGBA float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + dest[3] *= *mask * opacity; + + mask += 1; + dest += 4; + } + } + }); +} + +void +gimp_gegl_combine_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + *dest *= *mask * opacity; + + mask += 1; + dest += 1; + } + } + }); +} + +void +gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity, + gboolean stipple) +{ + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (dest, mask); + + iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *mask = (const gfloat *) iter->items[0].data; + gfloat *dest = (gfloat *) iter->items[1].data; + gint count = iter->length; + + if (stipple) + { + while (count--) + { + dest[0] += (1.0 - dest[0]) * *mask * opacity; + + mask += 1; + dest += 1; + } + } + else + { + while (count--) + { + if (opacity > dest[0]) + dest[0] += (opacity - dest[0]) * *mask * opacity; + + mask += 1; + dest += 1; + } + } + } + }); +} + +void +gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, + const GeglRectangle *indexed_rect, + const Babl *indexed_format, + GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + gint index) +{ + if (! indexed_rect) + indexed_rect = gegl_buffer_get_extent (indexed_buffer); + + if (! mask_rect) + mask_rect = gegl_buffer_get_extent (mask_buffer); + + gegl_parallel_distribute_area ( + indexed_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *indexed_area) + { + GeglBufferIterator *iter; + + SHIFTED_AREA (mask, indexed); + + iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0, + indexed_format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, mask_buffer, mask_area, 0, + babl_format ("Y float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *indexed = (const guchar *) iter->items[0].data; + gfloat *mask = (gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + if (*indexed == index) + *mask = 1.0; + else + *mask = 0.0; + + indexed++; + mask++; + } + } + }); +} + +static void +gimp_gegl_convert_color_profile_progress (GimpProgress *progress, + gdouble value) +{ + if (gegl_is_main_thread ()) + gimp_progress_set_value (progress, value); +} + +void +gimp_gegl_convert_color_profile (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GimpColorProfile *src_profile, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpColorTransform *transform; + guint flags = 0; + const Babl *src_format; + const Babl *dest_format; + + src_format = gegl_buffer_get_format (src_buffer); + dest_format = gegl_buffer_get_format (dest_buffer); + + if (bpc) + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; + + transform = gimp_color_transform_new (src_profile, src_format, + dest_profile, dest_format, + intent, + (GimpColorTransformFlags) flags); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); + + if (transform) + { + if (progress) + { + g_signal_connect_swapped ( + transform, "progress", + G_CALLBACK (gimp_gegl_convert_color_profile_progress), + progress); + } + + GIMP_TIMER_START (); + + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) + { + SHIFTED_AREA (dest, src); + + gimp_color_transform_process_buffer (transform, + src_buffer, src_area, + dest_buffer, dest_area); + }); + + GIMP_TIMER_END ("converting buffer"); + + g_object_unref (transform); + } + else + { + gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE, + dest_buffer, dest_rect); + + if (progress) + gimp_progress_set_value (progress, 1.0); + } +} + +void +gimp_gegl_average_color (GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean clip_to_buffer, + GeglAbyssPolicy abyss_policy, + const Babl *format, + gpointer color) +{ + typedef struct + { + gfloat color[4]; + gint n; + } Sum; + + const Babl *average_format = babl_format ("RaGaBaA float"); + GeglRectangle roi; + GSList * volatile sums = NULL; + GSList *list; + Sum average = {}; + gint c; + + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (color != NULL); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + if (! format) + format = gegl_buffer_get_format (buffer); + + if (clip_to_buffer) + gegl_rectangle_intersect (&roi, rect, gegl_buffer_get_extent (buffer)); + else + roi = *rect; + + gegl_parallel_distribute_area ( + &roi, PIXELS_PER_THREAD, + [&] (const GeglRectangle *area) + { + Sum *sum; + GeglBufferIterator *iter; + gfloat color[4] = {}; + gint n = 0; + + iter = gegl_buffer_iterator_new (buffer, area, 0, average_format, + GEGL_BUFFER_READ, abyss_policy, 1); + + while (gegl_buffer_iterator_next (iter)) + { + const gfloat *p = (const gfloat *) iter->items[0].data; + gint i; + + for (i = 0; i < iter->length; i++) + { + gint c; + + for (c = 0; c < 4; c++) + color[c] += p[c]; + + p += 4; + } + + n += iter->length; + } + + sum = g_slice_new (Sum); + + memcpy (sum->color, color, sizeof (color)); + sum->n = n; + + gimp_atomic_slist_push_head (&sums, sum); + }); + + for (list = sums; list; list = g_slist_next (list)) + { + Sum *sum = (Sum *) list->data; + + for (c = 0; c < 4; c++) + average.color[c] += sum->color[c]; + + average.n += sum->n; + + g_slice_free (Sum, sum); + } + + g_slist_free (sums); + + if (average.n > 0) + { + for (c = 0; c < 4; c++) + average.color[c] /= average.n; + } + + babl_process (babl_fish (average_format, format), average.color, color, 1); +} + +} /* extern "C" */ diff --git a/app/gegl/gimp-gegl-loops.h b/app/gegl/gimp-gegl-loops.h new file mode 100644 index 0000000..872c2b4 --- /dev/null +++ b/app/gegl/gimp-gegl-loops.h @@ -0,0 +1,109 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-loops.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_LOOPS_H__ +#define __GIMP_GEGL_LOOPS_H__ + + +void gimp_gegl_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglAbyssPolicy abyss_policy, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect); + +void gimp_gegl_clear (GeglBuffer *buffer, + const GeglRectangle *rect); + +/* this is a pretty stupid port of concolve_region() that only works + * on a linear source buffer + */ +void gimp_gegl_convolve (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + const gfloat *kernel, + gint kernel_size, + gdouble divisor, + GimpConvolutionType mode, + gboolean alpha_weighting); + +void gimp_gegl_dodgeburn (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble exposure, + GimpDodgeBurnType type, + GimpTransferMode mode); + +void gimp_gegl_smudge_with_paint (GeglBuffer *accum_buffer, + const GeglRectangle *accum_rect, + GeglBuffer *canvas_buffer, + const GeglRectangle *canvas_rect, + const GimpRGB *brush_color, + GeglBuffer *paint_buffer, + gboolean no_erasing, + gdouble flow, + gdouble rate); + +void gimp_gegl_apply_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity); + +void gimp_gegl_combine_mask (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity); + +void gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble opacity, + gboolean stipple); + +void gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, + const GeglRectangle *indexed_rect, + const Babl *indexed_format, + GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect, + gint index); + +void gimp_gegl_convert_color_profile (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GimpColorProfile *src_profile, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress); + +void gimp_gegl_average_color (GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean clip_to_buffer, + GeglAbyssPolicy abyss_policy, + const Babl *format, + gpointer color); + + +#endif /* __GIMP_GEGL_LOOPS_H__ */ diff --git a/app/gegl/gimp-gegl-mask-combine.cc b/app/gegl/gimp-gegl-mask-combine.cc new file mode 100644 index 0000000..0e61c0e --- /dev/null +++ b/app/gegl/gimp-gegl-mask-combine.cc @@ -0,0 +1,653 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +extern "C" +{ + +#include "gimp-gegl-types.h" + +#include "gimp-babl.h" +#include "gimp-gegl-loops.h" +#include "gimp-gegl-mask-combine.h" + + +#define EPSILON 1e-6 + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + + +gboolean +gimp_gegl_mask_combine_rect (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h) +{ + GeglRectangle rect; + gfloat value; + + g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); + + if (! gegl_rectangle_intersect (&rect, + GEGL_RECTANGLE (x, y, w, h), + gegl_buffer_get_abyss (mask))) + { + return FALSE; + } + + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + case GIMP_CHANNEL_OP_ADD: + value = 1.0f; + break; + + case GIMP_CHANNEL_OP_SUBTRACT: + value = 0.0f; + break; + + case GIMP_CHANNEL_OP_INTERSECT: + return TRUE; + } + + gegl_buffer_set_color_from_pixel (mask, &rect, &value, + babl_format ("Y float")); + + return TRUE; +} + +gboolean +gimp_gegl_mask_combine_ellipse (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias) +{ + return gimp_gegl_mask_combine_ellipse_rect (mask, op, x, y, w, h, + w / 2.0, h / 2.0, antialias); +} + +gboolean +gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gdouble rx, + gdouble ry, + gboolean antialias) +{ + GeglRectangle rect; + const Babl *format; + gint bpp; + gfloat one_f = 1.0f; + gpointer one; + gdouble cx; + gdouble cy; + gint left; + gint right; + gint top; + gint bottom; + + g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); + + if (rx <= EPSILON || ry <= EPSILON) + return gimp_gegl_mask_combine_rect (mask, op, x, y, w, h); + + left = x; + right = x + w; + top = y; + bottom = y + h; + + cx = (left + right) / 2.0; + cy = (top + bottom) / 2.0; + + rx = MIN (rx, w / 2.0); + ry = MIN (ry, h / 2.0); + + if (! gegl_rectangle_intersect (&rect, + GEGL_RECTANGLE (x, y, w, h), + gegl_buffer_get_abyss (mask))) + { + return FALSE; + } + + format = gegl_buffer_get_format (mask); + + if (antialias) + { + format = gimp_babl_format_change_component_type ( + format, GIMP_COMPONENT_TYPE_FLOAT); + } + + bpp = babl_format_get_bytes_per_pixel (format); + one = g_alloca (bpp); + + babl_process (babl_fish ("Y float", format), &one_f, one, 1); + + /* coordinate-system transforms. (x, y) coordinates are in the image + * coordinate-system, and (u, v) coordinates are in a coordinate-system + * aligned with the center of one of the elliptic corners, with the positive + * directions pointing away from the rectangle. when converting from (x, y) + * to (u, v), we use the closest elliptic corner. + */ + auto x_to_u = [=] (gdouble x) + { + if (x < cx) + return (left + rx) - x; + else + return x - (right - rx); + }; + + auto y_to_v = [=] (gdouble y) + { + if (y < cy) + return (top + ry) - y; + else + return y - (bottom - ry); + }; + + auto u_to_x_left = [=] (gdouble u) + { + return (left + rx) - u; + }; + + auto u_to_x_right = [=] (gdouble u) + { + return (right - rx) + u; + }; + + /* intersection of a horizontal line with the ellipse */ + auto v_to_u = [=] (gdouble v) + { + if (v > 0.0) + return sqrt (MAX (SQR (rx) - SQR (rx * v / ry), 0.0)); + else + return rx; + }; + + /* intersection of a vertical line with the ellipse */ + auto u_to_v = [=] (gdouble u) + { + if (u > 0.0) + return sqrt (MAX (SQR (ry) - SQR (ry * u / rx), 0.0)); + else + return ry; + }; + + /* signed, normalized distance of a point from the ellipse's circumference. + * the sign of the result determines if the point is inside (positive) or + * outside (negative) the ellipse. the result is normalized to the cross- + * section length of a pixel, in the direction of the closest point along the + * ellipse. + * + * we use the following method to approximate the distance: pass horizontal + * and vertical lines at the given point, P, and find their (positive) points + * of intersection with the ellipse, A and B. the segment AB is an + * approximation of the corresponding elliptic arc (see bug #147836). find + * the closest point, C, to P, along the segment AB. find the (positive) + * point of intersection, Q, of the line PC and the ellipse. Q is an + * approximation for the closest point to P along the ellipse, and the + * approximated distance is the distance from P to Q. + */ + auto ellipse_distance = [=] (gdouble u, + gdouble v) + { + gdouble du; + gdouble dv; + gdouble t; + gdouble a, b, c; + gdouble d; + + u = MAX (u, 0.0); + v = MAX (v, 0.0); + + du = v_to_u (v) - u; + dv = u_to_v (u) - v; + + t = SQR (du) / (SQR (du) + SQR (dv)); + + du *= 1.0 - t; + dv *= t; + + v *= rx / ry; + dv *= rx / ry; + + a = SQR (du) + SQR (dv); + b = u * du + v * dv; + c = SQR (u) + SQR (v) - SQR (rx); + + if (a <= EPSILON) + return 0.0; + + if (c < 0.0) + t = (-b + sqrt (MAX (SQR (b) - a * c, 0.0))) / a; + else + t = (-b - sqrt (MAX (SQR (b) - a * c, 0.0))) / a; + + dv *= ry / rx; + + d = sqrt (SQR (du * t) + SQR (dv * t)); + + if (c > 0.0) + d = -d; + + d /= sqrt (SQR (MIN (du / dv, dv / du)) + 1.0); + + return d; + }; + + /* anti-aliased value of a pixel */ + auto pixel_value = [=] (gint x, + gint y) + { + gdouble u = x_to_u (x + 0.5); + gdouble v = y_to_v (y + 0.5); + gdouble d = ellipse_distance (u, v); + + /* use the distance of the pixel's center from the ellipse to approximate + * the coverage + */ + d = CLAMP (0.5 + d, 0.0, 1.0); + + /* we're at the horizontal boundary of an elliptic corner */ + if (u < 0.5) + d = d * (0.5 + u) + (0.5 - u); + + /* we're at the vertical boundary of an elliptic corner */ + if (v < 0.5) + d = d * (0.5 + v) + (0.5 - v); + + /* opposite horizontal corners intersect the pixel */ + if (x == (right - 1) - (x - left)) + d = 2.0 * d - 1.0; + + /* opposite vertical corners intersect the pixel */ + if (y == (bottom - 1) - (y - top)) + d = 2.0 * d - 1.0; + + return d; + }; + + auto ellipse_range = [=] (gdouble y, + gdouble *x0, + gdouble *x1) + { + gdouble u = v_to_u (y_to_v (y)); + + *x0 = u_to_x_left (u); + *x1 = u_to_x_right (u); + }; + + auto fill0 = [=] (gpointer dest, + gint n) + { + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + case GIMP_CHANNEL_OP_INTERSECT: + memset (dest, 0, bpp * n); + break; + + case GIMP_CHANNEL_OP_ADD: + case GIMP_CHANNEL_OP_SUBTRACT: + break; + } + + return (gpointer) ((guint8 *) dest + bpp * n); + }; + + auto fill1 = [=] (gpointer dest, + gint n) + { + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + case GIMP_CHANNEL_OP_ADD: + gegl_memset_pattern (dest, one, bpp, n); + break; + + case GIMP_CHANNEL_OP_SUBTRACT: + memset (dest, 0, bpp * n); + break; + + case GIMP_CHANNEL_OP_INTERSECT: + break; + } + + return (gpointer) ((guint8 *) dest + bpp * n); + }; + + auto set = [=] (gpointer dest, + gfloat value) + { + gfloat *p = (gfloat *) dest; + + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + *p = value; + break; + + case GIMP_CHANNEL_OP_ADD: + *p = MIN (*p + value, 1.0); + break; + + case GIMP_CHANNEL_OP_SUBTRACT: + *p = MAX (*p - value, 0.0); + break; + + case GIMP_CHANNEL_OP_INTERSECT: + *p = MIN (*p, value); + break; + } + + return (gpointer) (p + 1); + }; + + gegl_parallel_distribute_area ( + &rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + GeglBufferIterator *iter; + + iter = gegl_buffer_iterator_new ( + mask, area, 0, format, + op == GIMP_CHANNEL_OP_REPLACE ? GEGL_ACCESS_WRITE : + GEGL_ACCESS_READWRITE, + GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (iter)) + { + const GeglRectangle *roi = &iter->items[0].roi; + gpointer d = iter->items[0].data; + gdouble tx0, ty0; + gdouble tx1, ty1; + gdouble x0; + gdouble x1; + gint y; + + /* tile bounds */ + tx0 = roi->x; + ty0 = roi->y; + + tx1 = roi->x + roi->width; + ty1 = roi->y + roi->height; + + if (! antialias) + { + tx0 += 0.5; + ty0 += 0.5; + + tx1 -= 0.5; + ty1 -= 0.5; + } + + /* if the tile is fully inside/outside the ellipse, fill it with 1/0, + * respectively, and skip the rest. + */ + ellipse_range (ty0, &x0, &x1); + + if (tx0 >= x0 && tx1 <= x1) + { + ellipse_range (ty1, &x0, &x1); + + if (tx0 >= x0 && tx1 <= x1) + { + fill1 (d, iter->length); + + continue; + } + } + else if (tx1 < x0 || tx0 > x1) + { + ellipse_range (ty1, &x0, &x1); + + if (tx1 < x0 || tx0 > x1) + { + if ((ty0 - cy) * (ty1 - cy) >= 0.0) + { + fill0 (d, iter->length); + + continue; + } + } + } + + for (y = roi->y; y < roi->y + roi->height; y++) + { + gint a, b; + + if (antialias) + { + gdouble v = y_to_v (y + 0.5); + gdouble u0 = v_to_u (v - 0.5); + gdouble u1 = v_to_u (v + 0.5); + gint x; + + a = floor (u_to_x_left (u0)) - roi->x; + a = CLAMP (a, 0, roi->width); + + b = ceil (u_to_x_left (u1)) - roi->x; + b = CLAMP (b, a, roi->width); + + d = fill0 (d, a); + + for (x = roi->x + a; x < roi->x + b; x++) + d = set (d, pixel_value (x, y)); + + a = floor (u_to_x_right (u1)) - roi->x; + a = CLAMP (a, b, roi->width); + + d = fill1 (d, a - b); + + b = ceil (u_to_x_right (u0)) - roi->x; + b = CLAMP (b, a, roi->width); + + for (x = roi->x + a; x < roi->x + b; x++) + d = set (d, pixel_value (x, y)); + + d = fill0 (d, roi->width - b); + } + else + { + ellipse_range (y + 0.5, &x0, &x1); + + a = ceil (x0 - 0.5) - roi->x; + a = CLAMP (a, 0, roi->width); + + b = floor (x1 + 0.5) - roi->x; + b = CLAMP (b, 0, roi->width); + + d = fill0 (d, a); + d = fill1 (d, b - a); + d = fill0 (d, roi->width - b); + } + } + } + }); + + return TRUE; +} + +gboolean +gimp_gegl_mask_combine_buffer (GeglBuffer *mask, + GeglBuffer *add_on, + GimpChannelOps op, + gint off_x, + gint off_y) +{ + GeglRectangle mask_rect; + GeglRectangle add_on_rect; + const Babl *mask_format; + const Babl *add_on_format; + + g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); + g_return_val_if_fail (GEGL_IS_BUFFER (add_on), FALSE); + + if (! gegl_rectangle_intersect (&mask_rect, + GEGL_RECTANGLE ( + off_x + gegl_buffer_get_x (add_on), + off_y + gegl_buffer_get_y (add_on), + gegl_buffer_get_width (add_on), + gegl_buffer_get_height (add_on)), + gegl_buffer_get_abyss (mask))) + { + return FALSE; + } + + add_on_rect = mask_rect; + add_on_rect.x -= off_x; + add_on_rect.y -= off_y; + + mask_format = gegl_buffer_get_format (mask); + add_on_format = gegl_buffer_get_format (add_on); + + if (op == GIMP_CHANNEL_OP_REPLACE && + (gimp_babl_is_bounded (gimp_babl_format_get_precision (add_on_format)) || + gimp_babl_is_bounded (gimp_babl_format_get_precision (mask_format)))) + { + /* See below: this additional hack is only needed for the + * gimp-channel-combine-masks procedure, it's the only place that + * allows to combine arbitrary channels with each other. + */ + gegl_buffer_set_format ( + add_on, + gimp_babl_format_change_linear ( + add_on_format, gimp_babl_format_get_linear (mask_format))); + + gimp_gegl_buffer_copy (add_on, &add_on_rect, GEGL_ABYSS_NONE, + mask, &mask_rect); + + gegl_buffer_set_format (add_on, NULL); + + return TRUE; + } + + /* This is a hack: all selections/layer masks/channels are always + * linear except for channels in 8-bit images. We don't want these + * "Y' u8" to be converted to "Y float" because that would cause a + * gamma canversion and give unexpected results for + * "add/subtract/etc channel from selection". Instead, use all + * channel values "as-is", which makes no differce except in the + * 8-bit case where we need it. + * + * See https://bugzilla.gnome.org/show_bug.cgi?id=791519 + */ + mask_format = gimp_babl_format_change_component_type ( + mask_format, GIMP_COMPONENT_TYPE_FLOAT); + + add_on_format = gimp_babl_format_change_component_type ( + add_on_format, GIMP_COMPONENT_TYPE_FLOAT); + + gegl_parallel_distribute_area ( + &mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) + { + GeglBufferIterator *iter; + GeglRectangle add_on_area; + + add_on_area = *mask_area; + add_on_area.x -= off_x; + add_on_area.y -= off_y; + + iter = gegl_buffer_iterator_new (mask, mask_area, 0, + mask_format, + op == GIMP_CHANNEL_OP_REPLACE ? + GEGL_ACCESS_WRITE : + GEGL_ACCESS_READWRITE, + GEGL_ABYSS_NONE, 2); + + gegl_buffer_iterator_add (iter, add_on, &add_on_area, 0, + add_on_format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + + auto process = [=] (auto value) + { + while (gegl_buffer_iterator_next (iter)) + { + gfloat *mask_data = (gfloat *) iter->items[0].data; + const gfloat *add_on_data = (const gfloat *) iter->items[1].data; + gint count = iter->length; + + while (count--) + { + const gfloat val = value (mask_data, add_on_data); + + *mask_data = CLAMP (val, 0.0f, 1.0f); + + add_on_data++; + mask_data++; + } + } + }; + + switch (op) + { + case GIMP_CHANNEL_OP_REPLACE: + process ([] (const gfloat *mask, + const gfloat *add_on) + { + return *add_on; + }); + break; + + case GIMP_CHANNEL_OP_ADD: + process ([] (const gfloat *mask, + const gfloat *add_on) + { + return *mask + *add_on; + }); + break; + + case GIMP_CHANNEL_OP_SUBTRACT: + process ([] (const gfloat *mask, + const gfloat *add_on) + { + return *mask - *add_on; + }); + break; + + case GIMP_CHANNEL_OP_INTERSECT: + process ([] (const gfloat *mask, + const gfloat *add_on) + { + return MIN (*mask, *add_on); + }); + break; + } + }); + + return TRUE; +} + +} /* extern "C" */ diff --git a/app/gegl/gimp-gegl-mask-combine.h b/app/gegl/gimp-gegl-mask-combine.h new file mode 100644 index 0000000..d8e0fa2 --- /dev/null +++ b/app/gegl/gimp-gegl-mask-combine.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_GEGL_MASK_COMBINE_H__ +#define __GIMP_GEGL_MASK_COMBINE_H__ + + +gboolean gimp_gegl_mask_combine_rect (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h); +gboolean gimp_gegl_mask_combine_ellipse (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias); +gboolean gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gdouble rx, + gdouble ry, + gboolean antialias); +gboolean gimp_gegl_mask_combine_buffer (GeglBuffer *mask, + GeglBuffer *add_on, + GimpChannelOps op, + gint off_x, + gint off_y); + + +#endif /* __GIMP_GEGL_MASK_COMBINE_H__ */ diff --git a/app/gegl/gimp-gegl-mask.c b/app/gegl/gimp-gegl-mask.c new file mode 100644 index 0000000..325289c --- /dev/null +++ b/app/gegl/gimp-gegl-mask.c @@ -0,0 +1,253 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "gimp-gegl-types.h" + +#include "gegl/gimp-gegl-mask.h" + + +gboolean +gimp_gegl_mask_bounds (GeglBuffer *buffer, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + GeglBufferIterator *iter; + const GeglRectangle *extent; + const GeglRectangle *roi; + const Babl *format; + gint bpp; + gint tx1, tx2, ty1, ty2; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (x1 != NULL, FALSE); + g_return_val_if_fail (y1 != NULL, FALSE); + g_return_val_if_fail (x2 != NULL, FALSE); + g_return_val_if_fail (y2 != NULL, FALSE); + + extent = gegl_buffer_get_extent (buffer); + + /* go through and calculate the bounds */ + tx1 = extent->x + extent->width; + ty1 = extent->y + extent->height; + tx2 = extent->x; + ty2 = extent->y; + + format = gegl_buffer_get_format (buffer); + bpp = babl_format_get_bytes_per_pixel (format); + + iter = gegl_buffer_iterator_new (buffer, NULL, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + roi = &iter->items[0].roi; + + while (gegl_buffer_iterator_next (iter)) + { + const guint8 *data_u8 = iter->items[0].data; + gint ex = roi->x + roi->width; + gint ey = roi->y + roi->height; + + /* only check the pixels if this tile is not fully within the + * currently computed bounds + */ + if (roi->x < tx1 || ex > tx2 || + roi->y < ty1 || ey > ty2) + { + /* Check upper left and lower right corners to see if we can + * avoid checking the rest of the pixels in this tile + */ + if (! gegl_memeq_zero (data_u8, bpp) && + ! gegl_memeq_zero (data_u8 + (iter->length - 1) * bpp, bpp)) + { + /* "ex/ey - 1" because the internal variables are the + * right/bottom pixel of the mask's contents, not one + * right/below it like the return values. + */ + + if (roi->x < tx1) tx1 = roi->x; + if (ex > tx2) tx2 = ex - 1; + + if (roi->y < ty1) ty1 = roi->y; + if (ey > ty2) ty2 = ey - 1; + } + else + { + #define FIND_BOUNDS(bpp, type) \ + G_STMT_START \ + { \ + const type *data; \ + gint y; \ + \ + if ((guintptr) data_u8 % bpp) \ + goto generic; \ + \ + data = (const type *) data_u8; \ + \ + for (y = roi->y; y < ey; y++) \ + { \ + gint x1; \ + \ + for (x1 = 0; x1 < roi->width; x1++) \ + { \ + if (data[x1]) \ + { \ + gint x2; \ + gint x2_end = MAX (x1, tx2 - roi->x); \ + \ + for (x2 = roi->width - 1; x2 > x2_end; x2--) \ + { \ + if (data[x2]) \ + break; \ + } \ + \ + x1 += roi->x; \ + x2 += roi->x; \ + \ + if (x1 < tx1) tx1 = x1; \ + if (x2 > tx2) tx2 = x2; \ + \ + if (y < ty1) ty1 = y; \ + if (y > ty2) ty2 = y; \ + \ + break; \ + } \ + } \ + \ + data += roi->width; \ + } \ + } \ + G_STMT_END + + switch (bpp) + { + case 1: + FIND_BOUNDS (1, guint8); + break; + + case 2: + FIND_BOUNDS (2, guint16); + break; + + case 4: + FIND_BOUNDS (4, guint32); + break; + + case 8: + FIND_BOUNDS (8, guint64); + break; + + default: + generic: + { + const guint8 *data = data_u8; + gint y; + + for (y = roi->y; y < ey; y++) + { + gint x1; + + for (x1 = 0; x1 < roi->width; x1++) + { + if (! gegl_memeq_zero (data + x1 * bpp, bpp)) + { + gint x2; + gint x2_end = MAX (x1, tx2 - roi->x); + + for (x2 = roi->width - 1; x2 > x2_end; x2--) + { + if (! gegl_memeq_zero (data + x2 * bpp, + bpp)) + { + break; + } + } + + x1 += roi->x; + x2 += roi->x; + + if (x1 < tx1) tx1 = x1; + if (x2 > tx2) tx2 = x2; + + if (y < ty1) ty1 = y; + if (y > ty2) ty2 = y; + } + } + + data += roi->width * bpp; + } + } + break; + } + + #undef FIND_BOUNDS + } + } + } + + tx2 = CLAMP (tx2 + 1, 0, gegl_buffer_get_width (buffer)); + ty2 = CLAMP (ty2 + 1, 0, gegl_buffer_get_height (buffer)); + + if (tx1 == gegl_buffer_get_width (buffer) && + ty1 == gegl_buffer_get_height (buffer)) + { + *x1 = 0; + *y1 = 0; + *x2 = gegl_buffer_get_width (buffer); + *y2 = gegl_buffer_get_height (buffer); + + return FALSE; + } + + *x1 = tx1; + *y1 = ty1; + *x2 = tx2; + *y2 = ty2; + + return TRUE; +} + +gboolean +gimp_gegl_mask_is_empty (GeglBuffer *buffer) +{ + GeglBufferIterator *iter; + const Babl *format; + gint bpp; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); + + format = gegl_buffer_get_format (buffer); + bpp = babl_format_get_bytes_per_pixel (format); + + iter = gegl_buffer_iterator_new (buffer, NULL, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (iter)) + { + if (! gegl_memeq_zero (iter->items[0].data, bpp * iter->length)) + { + gegl_buffer_iterator_stop (iter); + + return FALSE; + } + } + + return TRUE; +} diff --git a/app/gegl/gimp-gegl-mask.h b/app/gegl/gimp-gegl-mask.h new file mode 100644 index 0000000..e14d838 --- /dev/null +++ b/app/gegl/gimp-gegl-mask.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __GIMP_GEGL_MASK_H__ +#define __GIMP_GEGL_MASK_H__ + + +gboolean gimp_gegl_mask_bounds (GeglBuffer *buffer, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +gboolean gimp_gegl_mask_is_empty (GeglBuffer *buffer); + + +#endif /* __GIMP_GEGL_MASK_H__ */ diff --git a/app/gegl/gimp-gegl-nodes.c b/app/gegl/gimp-gegl-nodes.c new file mode 100644 index 0000000..5067da2 --- /dev/null +++ b/app/gegl/gimp-gegl-nodes.c @@ -0,0 +1,260 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-nodes.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "gimp-gegl-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gimp-gegl-nodes.h" +#include "gimp-gegl-utils.h" + + +GeglNode * +gimp_gegl_create_flatten_node (const GimpRGB *background, + GimpLayerColorSpace composite_space) +{ + GeglNode *node; + GeglNode *input; + GeglNode *output; + GeglNode *color; + GeglNode *mode; + GeglColor *c; + + g_return_val_if_fail (background != NULL, NULL); + g_return_val_if_fail (composite_space == GIMP_LAYER_COLOR_SPACE_RGB_LINEAR || + composite_space == GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + NULL); + + node = gegl_node_new (); + + input = gegl_node_get_input_proxy (node, "input"); + output = gegl_node_get_output_proxy (node, "output"); + + c = gimp_gegl_color_new (background); + color = gegl_node_new_child (node, + "operation", "gegl:color", + "value", c, + "format", gimp_layer_mode_get_format ( + GIMP_LAYER_MODE_NORMAL, + GIMP_LAYER_COLOR_SPACE_AUTO, + composite_space, + GIMP_LAYER_COMPOSITE_AUTO, + NULL), + NULL); + g_object_unref (c); + + gimp_gegl_node_set_underlying_operation (node, color); + + mode = gegl_node_new_child (node, + "operation", "gimp:normal", + NULL); + gimp_gegl_mode_node_set_mode (mode, + GIMP_LAYER_MODE_NORMAL, + GIMP_LAYER_COLOR_SPACE_AUTO, + composite_space, + GIMP_LAYER_COMPOSITE_AUTO); + + gegl_node_connect_to (input, "output", + mode, "aux"); + gegl_node_connect_to (color, "output", + mode, "input"); + gegl_node_connect_to (mode, "output", + output, "input"); + + return node; +} + +GeglNode * +gimp_gegl_create_apply_opacity_node (GeglBuffer *mask, + gint mask_offset_x, + gint mask_offset_y, + gdouble opacity) +{ + GeglNode *node; + GeglNode *input; + GeglNode *output; + GeglNode *opacity_node; + GeglNode *mask_source; + + g_return_val_if_fail (GEGL_IS_BUFFER (mask), NULL); + + node = gegl_node_new (); + + input = gegl_node_get_input_proxy (node, "input"); + output = gegl_node_get_output_proxy (node, "output"); + + opacity_node = gegl_node_new_child (node, + "operation", "gegl:opacity", + "value", opacity, + NULL); + + gimp_gegl_node_set_underlying_operation (node, opacity_node); + + mask_source = gimp_gegl_add_buffer_source (node, mask, + mask_offset_x, + mask_offset_y); + + gegl_node_connect_to (input, "output", + opacity_node, "input"); + gegl_node_connect_to (mask_source, "output", + opacity_node, "aux"); + gegl_node_connect_to (opacity_node, "output", + output, "input"); + + return node; +} + +GeglNode * +gimp_gegl_create_transform_node (const GimpMatrix3 *matrix) +{ + GeglNode *node; + + g_return_val_if_fail (matrix != NULL, NULL); + + node = gegl_node_new_child (NULL, + "operation", "gegl:transform", + NULL); + + gimp_gegl_node_set_matrix (node, matrix); + + return node; +} + +GeglNode * +gimp_gegl_add_buffer_source (GeglNode *parent, + GeglBuffer *buffer, + gint offset_x, + gint offset_y) +{ + GeglNode *buffer_source; + + g_return_val_if_fail (GEGL_IS_NODE (parent), NULL); + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + buffer_source = gegl_node_new_child (parent, + "operation", "gegl:buffer-source", + "buffer", buffer, + NULL); + + if (offset_x != 0 || offset_y != 0) + { + GeglNode *translate = + gegl_node_new_child (parent, + "operation", "gegl:translate", + "x", (gdouble) offset_x, + "y", (gdouble) offset_y, + NULL); + + gegl_node_connect_to (buffer_source, "output", + translate, "input"); + + buffer_source = translate; + } + + return buffer_source; +} + +void +gimp_gegl_mode_node_set_mode (GeglNode *node, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode) +{ + gdouble opacity; + + g_return_if_fail (GEGL_IS_NODE (node)); + + if (blend_space == GIMP_LAYER_COLOR_SPACE_AUTO) + blend_space = gimp_layer_mode_get_blend_space (mode); + + if (composite_space == GIMP_LAYER_COLOR_SPACE_AUTO) + composite_space = gimp_layer_mode_get_composite_space (mode); + + if (composite_mode == GIMP_LAYER_COMPOSITE_AUTO) + composite_mode = gimp_layer_mode_get_composite_mode (mode); + + gegl_node_get (node, + "opacity", &opacity, + NULL); + + /* setting the operation creates a new instance, so we have to set + * all its properties + */ + gegl_node_set (node, + "operation", gimp_layer_mode_get_operation (mode), + "layer-mode", mode, + "opacity", opacity, + "blend-space", blend_space, + "composite-space", composite_space, + "composite-mode", composite_mode, + NULL); +} + +void +gimp_gegl_mode_node_set_opacity (GeglNode *node, + gdouble opacity) +{ + g_return_if_fail (GEGL_IS_NODE (node)); + + gegl_node_set (node, + "opacity", opacity, + NULL); +} + +void +gimp_gegl_node_set_matrix (GeglNode *node, + const GimpMatrix3 *matrix) +{ + gchar *matrix_string; + + g_return_if_fail (GEGL_IS_NODE (node)); + g_return_if_fail (matrix != NULL); + + matrix_string = gegl_matrix3_to_string ((GeglMatrix3 *) matrix); + + gegl_node_set (node, + "transform", matrix_string, + NULL); + + g_free (matrix_string); +} + +void +gimp_gegl_node_set_color (GeglNode *node, + const GimpRGB *color) +{ + GeglColor *gegl_color; + + g_return_if_fail (GEGL_IS_NODE (node)); + g_return_if_fail (color != NULL); + + gegl_color = gimp_gegl_color_new (color); + + gegl_node_set (node, + "value", gegl_color, + NULL); + + g_object_unref (gegl_color); +} diff --git a/app/gegl/gimp-gegl-nodes.h b/app/gegl/gimp-gegl-nodes.h new file mode 100644 index 0000000..0f627ff --- /dev/null +++ b/app/gegl/gimp-gegl-nodes.h @@ -0,0 +1,52 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-nodes.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_NODES_H__ +#define __GIMP_GEGL_NODES_H__ + + +GeglNode * gimp_gegl_create_flatten_node (const GimpRGB *background, + GimpLayerColorSpace composite_space); +GeglNode * gimp_gegl_create_apply_opacity_node (GeglBuffer *mask, + gint mask_offset_x, + gint mask_offset_y, + gdouble opacity); +GeglNode * gimp_gegl_create_transform_node (const GimpMatrix3 *matrix); + +GeglNode * gimp_gegl_add_buffer_source (GeglNode *parent, + GeglBuffer *buffer, + gint offset_x, + gint offset_y); + +void gimp_gegl_mode_node_set_mode (GeglNode *node, + GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode); +void gimp_gegl_mode_node_set_opacity (GeglNode *node, + gdouble opacity); + +void gimp_gegl_node_set_matrix (GeglNode *node, + const GimpMatrix3 *matrix); +void gimp_gegl_node_set_color (GeglNode *node, + const GimpRGB *color); + + +#endif /* __GIMP_GEGL_NODES_H__ */ diff --git a/app/gegl/gimp-gegl-tile-compat.c b/app/gegl/gimp-gegl-tile-compat.c new file mode 100644 index 0000000..54d917c --- /dev/null +++ b/app/gegl/gimp-gegl-tile-compat.c @@ -0,0 +1,79 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-tile-compat.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "gimp-gegl-types.h" + +#include "gimp-gegl-tile-compat.h" + + +gint +gimp_gegl_buffer_get_n_tile_rows (GeglBuffer *buffer, + gint tile_height) +{ + return (gegl_buffer_get_height (buffer) + tile_height - 1) / tile_height; +} + +gint +gimp_gegl_buffer_get_n_tile_cols (GeglBuffer *buffer, + gint tile_width) +{ + return (gegl_buffer_get_width (buffer) + tile_width - 1) / tile_width; +} + +gboolean +gimp_gegl_buffer_get_tile_rect (GeglBuffer *buffer, + gint tile_width, + gint tile_height, + gint tile_num, + GeglRectangle *rect) +{ + gint n_tile_rows; + gint n_tile_columns; + gint tile_row; + gint tile_column; + + n_tile_rows = gimp_gegl_buffer_get_n_tile_rows (buffer, tile_height); + n_tile_columns = gimp_gegl_buffer_get_n_tile_cols (buffer, tile_width); + + if (tile_num > n_tile_rows * n_tile_columns - 1) + return FALSE; + + tile_row = tile_num / n_tile_columns; + tile_column = tile_num % n_tile_columns; + + rect->x = tile_column * tile_width; + rect->y = tile_row * tile_height; + + if (tile_column == n_tile_columns - 1) + rect->width = gegl_buffer_get_width (buffer) - rect->x; + else + rect->width = tile_width; + + if (tile_row == n_tile_rows - 1) + rect->height = gegl_buffer_get_height (buffer) - rect->y; + else + rect->height = tile_height; + + return TRUE; +} diff --git a/app/gegl/gimp-gegl-tile-compat.h b/app/gegl/gimp-gegl-tile-compat.h new file mode 100644 index 0000000..e35f288 --- /dev/null +++ b/app/gegl/gimp-gegl-tile-compat.h @@ -0,0 +1,36 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-tile-compat.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_TILE_COMPAT_H__ +#define __GIMP_GEGL_TILE_COMPAT_H__ + + +gint gimp_gegl_buffer_get_n_tile_rows (GeglBuffer *buffer, + gint tile_height); +gint gimp_gegl_buffer_get_n_tile_cols (GeglBuffer *buffer, + gint tile_width); +gboolean gimp_gegl_buffer_get_tile_rect (GeglBuffer *buffer, + gint tile_width, + gint tile_height, + gint tile_num, + GeglRectangle *rect); + + +#endif /* __GIMP_GEGL_TILE_COMPAT_H__ */ diff --git a/app/gegl/gimp-gegl-types.h b/app/gegl/gimp-gegl-types.h new file mode 100644 index 0000000..ecea87e --- /dev/null +++ b/app/gegl/gimp-gegl-types.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-types.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 . + */ + +#ifndef __GIMP_GEGL_TYPES_H__ +#define __GIMP_GEGL_TYPES_H__ + + +#include "core/core-types.h" + +#include "gegl/gimp-gegl-enums.h" + +#include "operations/operations-types.h" + + +typedef struct _GimpApplicator GimpApplicator; + + +#endif /* __GIMP_GEGL_TYPES_H__ */ diff --git a/app/gegl/gimp-gegl-utils.c b/app/gegl/gimp-gegl-utils.c new file mode 100644 index 0000000..efc3a1e --- /dev/null +++ b/app/gegl/gimp-gegl-utils.c @@ -0,0 +1,348 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-utils.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include + +#include "gimp-gegl-types.h" + +#include "core/gimpprogress.h" + +#include "gimp-gegl-loops.h" +#include "gimp-gegl-utils.h" + + +GType +gimp_gegl_get_op_enum_type (const gchar *operation, + const gchar *property) +{ + GeglNode *node; + GObject *op; + GParamSpec *pspec; + + g_return_val_if_fail (operation != NULL, G_TYPE_NONE); + g_return_val_if_fail (property != NULL, G_TYPE_NONE); + + node = g_object_new (GEGL_TYPE_NODE, + "operation", operation, + NULL); + g_object_get (node, "gegl-operation", &op, NULL); + g_object_unref (node); + + g_return_val_if_fail (op != NULL, G_TYPE_NONE); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (op), property); + + g_return_val_if_fail (G_IS_PARAM_SPEC_ENUM (pspec), G_TYPE_NONE); + + g_object_unref (op); + + return G_TYPE_FROM_CLASS (G_PARAM_SPEC_ENUM (pspec)->enum_class); +} + +GeglColor * +gimp_gegl_color_new (const GimpRGB *rgb) +{ + GeglColor *color; + + g_return_val_if_fail (rgb != NULL, NULL); + + color = gegl_color_new (NULL); + gegl_color_set_pixel (color, babl_format ("R'G'B'A double"), rgb); + + return color; +} + +static void +gimp_gegl_progress_callback (GObject *object, + gdouble value, + GimpProgress *progress) +{ + if (value == 0.0) + { + const gchar *text = g_object_get_data (object, "gimp-progress-text"); + + if (gimp_progress_is_active (progress)) + gimp_progress_set_text (progress, "%s", text); + else + gimp_progress_start (progress, FALSE, "%s", text); + } + else + { + gimp_progress_set_value (progress, value); + + if (value == 1.0) + gimp_progress_end (progress); + } +} + +void +gimp_gegl_progress_connect (GeglNode *node, + GimpProgress *progress, + const gchar *text) +{ + g_return_if_fail (GEGL_IS_NODE (node)); + g_return_if_fail (GIMP_IS_PROGRESS (progress)); + g_return_if_fail (text != NULL); + + g_signal_connect (node, "progress", + G_CALLBACK (gimp_gegl_progress_callback), + progress); + + g_object_set_data_full (G_OBJECT (node), + "gimp-progress-text", g_strdup (text), + (GDestroyNotify) g_free); +} + +gboolean +gimp_gegl_node_is_source_operation (GeglNode *node) +{ + GeglOperation *operation; + + g_return_val_if_fail (GEGL_IS_NODE (node), FALSE); + + operation = gegl_node_get_gegl_operation (node); + + if (! operation) + return FALSE; + + return GEGL_IS_OPERATION_SOURCE (operation); +} + +gboolean +gimp_gegl_node_is_point_operation (GeglNode *node) +{ + GeglOperation *operation; + + g_return_val_if_fail (GEGL_IS_NODE (node), FALSE); + + operation = gegl_node_get_gegl_operation (node); + + if (! operation) + return FALSE; + + return GEGL_IS_OPERATION_POINT_RENDER (operation) || + GEGL_IS_OPERATION_POINT_FILTER (operation) || + GEGL_IS_OPERATION_POINT_COMPOSER (operation) || + GEGL_IS_OPERATION_POINT_COMPOSER3 (operation); +} + +gboolean +gimp_gegl_node_is_area_filter_operation (GeglNode *node) +{ + GeglOperation *operation; + + g_return_val_if_fail (GEGL_IS_NODE (node), FALSE); + + operation = gegl_node_get_gegl_operation (node); + + if (! operation) + return FALSE; + + return GEGL_IS_OPERATION_AREA_FILTER (operation) || + /* be conservative and return TRUE for meta ops, since they may + * involve an area op + */ + GEGL_IS_OPERATION_META (operation); +} + +const gchar * +gimp_gegl_node_get_key (GeglNode *node, + const gchar *key) +{ + const gchar *operation_name; + + g_return_val_if_fail (GEGL_IS_NODE (node), NULL); + + operation_name = gegl_node_get_operation (node); + + if (operation_name) + return gegl_operation_get_key (operation_name, key); + else + return NULL; +} + +gboolean +gimp_gegl_node_has_key (GeglNode *node, + const gchar *key) +{ + return gimp_gegl_node_get_key (node, key) != NULL; +} + +const Babl * +gimp_gegl_node_get_format (GeglNode *node, + const gchar *pad_name) +{ + GeglOperation *op; + const Babl *format = NULL; + + g_return_val_if_fail (GEGL_IS_NODE (node), NULL); + g_return_val_if_fail (pad_name != NULL, NULL); + + g_object_get (node, "gegl-operation", &op, NULL); + + if (op) + { + format = gegl_operation_get_format (op, pad_name); + + g_object_unref (op); + } + + if (! format) + format = babl_format ("RGBA float"); + + return format; +} + +void +gimp_gegl_node_set_underlying_operation (GeglNode *node, + GeglNode *operation) +{ + g_return_if_fail (GEGL_IS_NODE (node)); + g_return_if_fail (operation == NULL || GEGL_IS_NODE (operation)); + + g_object_set_data (G_OBJECT (node), + "gimp-gegl-node-underlying-operation", operation); +} + +GeglNode * +gimp_gegl_node_get_underlying_operation (GeglNode *node) +{ + GeglNode *operation; + + g_return_val_if_fail (GEGL_IS_NODE (node), NULL); + + operation = g_object_get_data (G_OBJECT (node), + "gimp-gegl-node-underlying-operation"); + + if (operation) + return gimp_gegl_node_get_underlying_operation (operation); + else + return node; +} + +gboolean +gimp_gegl_param_spec_has_key (GParamSpec *pspec, + const gchar *key, + const gchar *value) +{ + const gchar *v = gegl_param_spec_get_property_key (pspec, key); + + if (v && ! strcmp (v, value)) + return TRUE; + + return FALSE; +} + +GeglBuffer * +gimp_gegl_buffer_dup (GeglBuffer *buffer) +{ + GeglBuffer *new_buffer; + const GeglRectangle *extent; + const GeglRectangle *abyss; + GeglRectangle rect; + gint shift_x; + gint shift_y; + gint tile_width; + gint tile_height; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + extent = gegl_buffer_get_extent (buffer); + abyss = gegl_buffer_get_abyss (buffer); + + g_object_get (buffer, + "shift-x", &shift_x, + "shift-y", &shift_y, + "tile-width", &tile_width, + "tile-height", &tile_height, + NULL); + + new_buffer = g_object_new (GEGL_TYPE_BUFFER, + "format", gegl_buffer_get_format (buffer), + "x", extent->x, + "y", extent->y, + "width", extent->width, + "height", extent->height, + "abyss-x", abyss->x, + "abyss-y", abyss->y, + "abyss-width", abyss->width, + "abyss-height", abyss->height, + "shift-x", shift_x, + "shift-y", shift_y, + "tile-width", tile_width, + "tile-height", tile_height, + NULL); + + gegl_rectangle_align_to_buffer (&rect, extent, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE, + new_buffer, &rect); + + return new_buffer; +} + +gboolean +gimp_gegl_buffer_set_extent (GeglBuffer *buffer, + const GeglRectangle *extent) +{ + GeglRectangle aligned_old_extent; + GeglRectangle aligned_extent; + GeglRectangle old_extent_rem; + GeglRectangle diff_rects[4]; + gint n_diff_rects; + gint i; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (extent != NULL, FALSE); + + gegl_rectangle_align_to_buffer (&aligned_old_extent, + gegl_buffer_get_extent (buffer), buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + gegl_rectangle_align_to_buffer (&aligned_extent, + extent, buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + n_diff_rects = gegl_rectangle_subtract (diff_rects, + &aligned_old_extent, + &aligned_extent); + + for (i = 0; i < n_diff_rects; i++) + gegl_buffer_clear (buffer, &diff_rects[i]); + + if (gegl_rectangle_intersect (&old_extent_rem, + gegl_buffer_get_extent (buffer), + &aligned_extent)) + { + n_diff_rects = gegl_rectangle_subtract (diff_rects, + &old_extent_rem, + extent); + + for (i = 0; i < n_diff_rects; i++) + gegl_buffer_clear (buffer, &diff_rects[i]); + } + + return gegl_buffer_set_extent (buffer, extent); +} diff --git a/app/gegl/gimp-gegl-utils.h b/app/gegl/gimp-gegl-utils.h new file mode 100644 index 0000000..cee725f --- /dev/null +++ b/app/gegl/gimp-gegl-utils.h @@ -0,0 +1,60 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl-utils.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_UTILS_H__ +#define __GIMP_GEGL_UTILS_H__ + + +GType gimp_gegl_get_op_enum_type (const gchar *operation, + const gchar *property); + +GeglColor * gimp_gegl_color_new (const GimpRGB *rgb); + +void gimp_gegl_progress_connect (GeglNode *node, + GimpProgress *progress, + const gchar *text); + +gboolean gimp_gegl_node_is_source_operation (GeglNode *node); +gboolean gimp_gegl_node_is_point_operation (GeglNode *node); +gboolean gimp_gegl_node_is_area_filter_operation (GeglNode *node); + +const gchar * gimp_gegl_node_get_key (GeglNode *node, + const gchar *key); +gboolean gimp_gegl_node_has_key (GeglNode *node, + const gchar *key); + +const Babl * gimp_gegl_node_get_format (GeglNode *node, + const gchar *pad_name); + +void gimp_gegl_node_set_underlying_operation (GeglNode *node, + GeglNode *operation); +GeglNode * gimp_gegl_node_get_underlying_operation (GeglNode *node); + +gboolean gimp_gegl_param_spec_has_key (GParamSpec *pspec, + const gchar *key, + const gchar *value); + +GeglBuffer * gimp_gegl_buffer_dup (GeglBuffer *buffer); + +gboolean gimp_gegl_buffer_set_extent (GeglBuffer *buffer, + const GeglRectangle *extent); + + +#endif /* __GIMP_GEGL_UTILS_H__ */ diff --git a/app/gegl/gimp-gegl.c b/app/gegl/gimp-gegl.c new file mode 100644 index 0000000..64046b6 --- /dev/null +++ b/app/gegl/gimp-gegl.c @@ -0,0 +1,171 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl.c + * Copyright (C) 2007 Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "gimp-gegl-types.h" + +#include "config/gimpgeglconfig.h" + +#include "operations/gimp-operations.h" + +#include "core/gimp.h" +#include "core/gimp-parallel.h" + +#include "gimp-babl.h" +#include "gimp-gegl.h" + +#include + + +static void gimp_gegl_notify_temp_path (GimpGeglConfig *config); +static void gimp_gegl_notify_swap_path (GimpGeglConfig *config); +static void gimp_gegl_notify_swap_compression (GimpGeglConfig *config); +static void gimp_gegl_notify_tile_cache_size (GimpGeglConfig *config); +static void gimp_gegl_notify_num_processors (GimpGeglConfig *config); +static void gimp_gegl_notify_use_opencl (GimpGeglConfig *config); + + +/* public functions */ + +void +gimp_gegl_init (Gimp *gimp) +{ + GimpGeglConfig *config; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + config = GIMP_GEGL_CONFIG (gimp->config); + + /* make sure temp and swap directories exist */ + gimp_gegl_notify_temp_path (config); + gimp_gegl_notify_swap_path (config); + + g_object_set (gegl_config (), + "swap-compression", config->swap_compression, + "tile-cache-size", (guint64) config->tile_cache_size, + "threads", config->num_processors, + "use-opencl", config->use_opencl, + NULL); + + gimp_parallel_init (gimp); + + g_signal_connect (config, "notify::temp-path", + G_CALLBACK (gimp_gegl_notify_temp_path), + NULL); + g_signal_connect (config, "notify::swap-path", + G_CALLBACK (gimp_gegl_notify_swap_path), + NULL); + g_signal_connect (config, "notify::swap-compression", + G_CALLBACK (gimp_gegl_notify_swap_compression), + NULL); + g_signal_connect (config, "notify::num-processors", + G_CALLBACK (gimp_gegl_notify_num_processors), + NULL); + g_signal_connect (config, "notify::tile-cache-size", + G_CALLBACK (gimp_gegl_notify_tile_cache_size), + NULL); + g_signal_connect (config, "notify::num-processors", + G_CALLBACK (gimp_gegl_notify_num_processors), + NULL); + g_signal_connect (config, "notify::use-opencl", + G_CALLBACK (gimp_gegl_notify_use_opencl), + NULL); + + gimp_babl_init (); + + gimp_operations_init (gimp); +} + +void +gimp_gegl_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_parallel_exit (gimp); +} + + +/* private functions */ + +static void +gimp_gegl_notify_temp_path (GimpGeglConfig *config) +{ + GFile *file = gimp_file_new_for_config_path (config->temp_path, NULL); + + if (! g_file_query_exists (file, NULL)) + g_file_make_directory_with_parents (file, NULL, NULL); + + g_object_unref (file); +} + +static void +gimp_gegl_notify_swap_path (GimpGeglConfig *config) +{ + GFile *file = gimp_file_new_for_config_path (config->swap_path, NULL); + gchar *path = g_file_get_path (file); + + if (! g_file_query_exists (file, NULL)) + g_file_make_directory_with_parents (file, NULL, NULL); + + g_object_set (gegl_config (), + "swap", path, + NULL); + + g_free (path); + g_object_unref (file); +} + +static void +gimp_gegl_notify_swap_compression (GimpGeglConfig *config) +{ + g_object_set (gegl_config (), + "swap-compression", config->swap_compression, + NULL); +} + +static void +gimp_gegl_notify_tile_cache_size (GimpGeglConfig *config) +{ + g_object_set (gegl_config (), + "tile-cache-size", (guint64) config->tile_cache_size, + NULL); +} + +static void +gimp_gegl_notify_num_processors (GimpGeglConfig *config) +{ + g_object_set (gegl_config (), + "threads", config->num_processors, + NULL); +} + +static void +gimp_gegl_notify_use_opencl (GimpGeglConfig *config) +{ + g_object_set (gegl_config (), + "use-opencl", config->use_opencl, + NULL); +} diff --git a/app/gegl/gimp-gegl.h b/app/gegl/gimp-gegl.h new file mode 100644 index 0000000..a8e12c2 --- /dev/null +++ b/app/gegl/gimp-gegl.h @@ -0,0 +1,29 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-gegl.h + * Copyright (C) 2007 Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_GEGL_H__ +#define __GIMP_GEGL_H__ + + +void gimp_gegl_init (Gimp *gimp); +void gimp_gegl_exit (Gimp *gimp); + + +#endif /* __GIMP_GEGL_H__ */ diff --git a/app/gegl/gimpapplicator.c b/app/gegl/gimpapplicator.c new file mode 100644 index 0000000..ac8c2db --- /dev/null +++ b/app/gegl/gimpapplicator.c @@ -0,0 +1,652 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpapplicator.c + * Copyright (C) 2012-2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "gimp-gegl-types.h" + +#include "gimp-gegl-nodes.h" +#include "gimpapplicator.h" + + +static void gimp_applicator_finalize (GObject *object); +static void gimp_applicator_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_applicator_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpApplicator, gimp_applicator, G_TYPE_OBJECT) + +#define parent_class gimp_applicator_parent_class + + +static void +gimp_applicator_class_init (GimpApplicatorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gimp_applicator_finalize; + object_class->set_property = gimp_applicator_set_property; + object_class->get_property = gimp_applicator_get_property; +} + +static void +gimp_applicator_init (GimpApplicator *applicator) +{ + applicator->active = TRUE; + applicator->opacity = 1.0; + applicator->paint_mode = GIMP_LAYER_MODE_NORMAL; + applicator->blend_space = GIMP_LAYER_COLOR_SPACE_AUTO; + applicator->composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + applicator->composite_mode = GIMP_LAYER_COMPOSITE_AUTO; + applicator->affect = GIMP_COMPONENT_MASK_ALL; +} + +static void +gimp_applicator_finalize (GObject *object) +{ + GimpApplicator *applicator = GIMP_APPLICATOR (object); + + g_clear_object (&applicator->node); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_applicator_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_applicator_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +GimpApplicator * +gimp_applicator_new (GeglNode *parent) +{ + GimpApplicator *applicator; + + g_return_val_if_fail (parent == NULL || GEGL_IS_NODE (parent), NULL); + + applicator = g_object_new (GIMP_TYPE_APPLICATOR, NULL); + + if (parent) + applicator->node = g_object_ref (parent); + else + applicator->node = gegl_node_new (); + + applicator->input_node = + gegl_node_get_input_proxy (applicator->node, "input"); + + applicator->aux_node = + gegl_node_get_input_proxy (applicator->node, "aux"); + + applicator->output_node = + gegl_node_get_output_proxy (applicator->node, "output"); + + applicator->mode_node = gegl_node_new_child (applicator->node, + "operation", "gimp:normal", + NULL); + + gimp_gegl_mode_node_set_mode (applicator->mode_node, + applicator->paint_mode, + applicator->blend_space, + applicator->composite_space, + applicator->composite_mode); + gimp_gegl_mode_node_set_opacity (applicator->mode_node, + applicator->opacity); + + gegl_node_connect_to (applicator->input_node, "output", + applicator->mode_node, "input"); + + applicator->apply_offset_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:translate", + NULL); + + gegl_node_link_many (applicator->aux_node, + applicator->apply_offset_node, + NULL); + + gegl_node_connect_to (applicator->apply_offset_node, "output", + applicator->mode_node, "aux"); + + applicator->mask_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:buffer-source", + NULL); + + applicator->mask_offset_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:translate", + NULL); + + gegl_node_connect_to (applicator->mask_node, "output", + applicator->mask_offset_node, "input"); + /* don't connect the the mask offset node to mode's aux2 yet */ + + applicator->affect_node = + gegl_node_new_child (applicator->node, + "operation", "gimp:mask-components", + "mask", applicator->affect, + NULL); + + applicator->convert_format_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:nop", + NULL); + + applicator->cache_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:nop", + NULL); + + applicator->crop_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:nop", + NULL); + + gegl_node_link_many (applicator->input_node, + applicator->affect_node, + applicator->convert_format_node, + applicator->cache_node, + applicator->crop_node, + applicator->output_node, + NULL); + + gegl_node_connect_to (applicator->mode_node, "output", + applicator->affect_node, "aux"); + + return applicator; +} + +void +gimp_applicator_set_active (GimpApplicator *applicator, + gboolean active) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (active != applicator->active) + { + applicator->active = active; + + if (active) + gegl_node_link (applicator->crop_node, applicator->output_node); + else + gegl_node_link (applicator->input_node, applicator->output_node); + } +} + +void +gimp_applicator_set_src_buffer (GimpApplicator *applicator, + GeglBuffer *src_buffer) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + g_return_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer)); + + if (src_buffer == applicator->src_buffer) + return; + + if (src_buffer) + { + if (! applicator->src_node) + { + applicator->src_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:buffer-source", + "buffer", src_buffer, + NULL); + } + else + { + gegl_node_set (applicator->src_node, + "buffer", src_buffer, + NULL); + } + + if (! applicator->src_buffer) + gegl_node_link (applicator->src_node, applicator->input_node); + } + else if (applicator->src_buffer) + { + gegl_node_disconnect (applicator->input_node, "input"); + + gegl_node_set (applicator->src_node, + "buffer", NULL, + NULL); + } + + applicator->src_buffer = src_buffer; +} + +void +gimp_applicator_set_dest_buffer (GimpApplicator *applicator, + GeglBuffer *dest_buffer) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + g_return_if_fail (dest_buffer == NULL || GEGL_IS_BUFFER (dest_buffer)); + + if (dest_buffer == applicator->dest_buffer) + return; + + if (dest_buffer) + { + if (! applicator->dest_node) + { + applicator->dest_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:write-buffer", + "buffer", dest_buffer, + NULL); + } + else + { + gegl_node_set (applicator->dest_node, + "buffer", dest_buffer, + NULL); + } + + if (! applicator->dest_buffer) + gegl_node_link (applicator->affect_node, applicator->dest_node); + } + else if (applicator->dest_buffer) + { + gegl_node_disconnect (applicator->dest_node, "input"); + + gegl_node_set (applicator->dest_node, + "buffer", NULL, + NULL); + } + + applicator->dest_buffer = dest_buffer; +} + +void +gimp_applicator_set_mask_buffer (GimpApplicator *applicator, + GeglBuffer *mask_buffer) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + g_return_if_fail (mask_buffer == NULL || GEGL_IS_BUFFER (mask_buffer)); + + if (applicator->mask_buffer == mask_buffer) + return; + + gegl_node_set (applicator->mask_node, + "buffer", mask_buffer, + NULL); + + if (mask_buffer) + { + gegl_node_connect_to (applicator->mask_offset_node, "output", + applicator->mode_node, "aux2"); + } + else + { + gegl_node_disconnect (applicator->mode_node, "aux2"); + } + + applicator->mask_buffer = mask_buffer; +} + +void +gimp_applicator_set_mask_offset (GimpApplicator *applicator, + gint mask_offset_x, + gint mask_offset_y) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->mask_offset_x != mask_offset_x || + applicator->mask_offset_y != mask_offset_y) + { + applicator->mask_offset_x = mask_offset_x; + applicator->mask_offset_y = mask_offset_y; + + gegl_node_set (applicator->mask_offset_node, + "x", (gdouble) mask_offset_x, + "y", (gdouble) mask_offset_y, + NULL); + } +} + +void +gimp_applicator_set_apply_buffer (GimpApplicator *applicator, + GeglBuffer *apply_buffer) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + g_return_if_fail (apply_buffer == NULL || GEGL_IS_BUFFER (apply_buffer)); + + if (apply_buffer == applicator->apply_buffer) + return; + + if (apply_buffer) + { + if (! applicator->apply_src_node) + { + applicator->apply_src_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:buffer-source", + "buffer", apply_buffer, + NULL); + } + else + { + gegl_node_set (applicator->apply_src_node, + "buffer", apply_buffer, + NULL); + } + + if (! applicator->apply_buffer) + { + gegl_node_connect_to (applicator->apply_src_node, "output", + applicator->apply_offset_node, "input"); + } + } + else if (applicator->apply_buffer) + { + gegl_node_connect_to (applicator->aux_node, "output", + applicator->apply_offset_node, "input"); + } + + applicator->apply_buffer = apply_buffer; +} + +void +gimp_applicator_set_apply_offset (GimpApplicator *applicator, + gint apply_offset_x, + gint apply_offset_y) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->apply_offset_x != apply_offset_x || + applicator->apply_offset_y != apply_offset_y) + { + applicator->apply_offset_x = apply_offset_x; + applicator->apply_offset_y = apply_offset_y; + + gegl_node_set (applicator->apply_offset_node, + "x", (gdouble) apply_offset_x, + "y", (gdouble) apply_offset_y, + NULL); + } +} + +void +gimp_applicator_set_opacity (GimpApplicator *applicator, + gdouble opacity) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->opacity != opacity) + { + applicator->opacity = opacity; + + gimp_gegl_mode_node_set_opacity (applicator->mode_node, + opacity); + } +} + +void +gimp_applicator_set_mode (GimpApplicator *applicator, + GimpLayerMode paint_mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->paint_mode != paint_mode || + applicator->blend_space != blend_space || + applicator->composite_space != composite_space || + applicator->composite_mode != composite_mode) + { + applicator->paint_mode = paint_mode; + applicator->blend_space = blend_space; + applicator->composite_space = composite_space; + applicator->composite_mode = composite_mode; + + gimp_gegl_mode_node_set_mode (applicator->mode_node, + paint_mode, blend_space, + composite_space, composite_mode); + } +} + +void +gimp_applicator_set_affect (GimpApplicator *applicator, + GimpComponentMask affect) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->affect != affect) + { + applicator->affect = affect; + + gegl_node_set (applicator->affect_node, + "mask", affect, + NULL); + } +} + +void +gimp_applicator_set_output_format (GimpApplicator *applicator, + const Babl *format) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->output_format != format) + { + if (format) + { + if (! applicator->output_format) + { + gegl_node_set (applicator->convert_format_node, + "operation", "gegl:convert-format", + "format", format, + NULL); + } + else + { + gegl_node_set (applicator->convert_format_node, + "format", format, + NULL); + } + } + else + { + gegl_node_set (applicator->convert_format_node, + "operation", "gegl:nop", + NULL); + } + + applicator->output_format = format; + } +} + +const Babl * +gimp_applicator_get_output_format (GimpApplicator *applicator) +{ + g_return_val_if_fail (GIMP_IS_APPLICATOR (applicator), NULL); + + return applicator->output_format; +} + +void +gimp_applicator_set_cache (GimpApplicator *applicator, + gboolean enable) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->cache_enabled != enable) + { + if (enable) + { + gegl_node_set (applicator->cache_node, + "operation", "gegl:cache", + NULL); + } + else + { + gegl_node_set (applicator->cache_node, + "operation", "gegl:nop", + NULL); + } + + applicator->cache_enabled = enable; + } +} + +gboolean +gimp_applicator_get_cache (GimpApplicator *applicator) +{ + g_return_val_if_fail (GIMP_IS_APPLICATOR (applicator), FALSE); + + return applicator->cache_enabled; +} + +gboolean gegl_buffer_list_valid_rectangles (GeglBuffer *buffer, + GeglRectangle **rectangles, + gint *n_rectangles); + +GeglBuffer * +gimp_applicator_get_cache_buffer (GimpApplicator *applicator, + GeglRectangle **rectangles, + gint *n_rectangles) +{ + g_return_val_if_fail (GIMP_IS_APPLICATOR (applicator), NULL); + g_return_val_if_fail (rectangles != NULL, NULL); + g_return_val_if_fail (n_rectangles != NULL, NULL); + + if (applicator->cache_enabled) + { + GeglBuffer *cache; + + gegl_node_get (applicator->cache_node, + "cache", &cache, + NULL); + + if (cache) + { + if (gegl_buffer_list_valid_rectangles (cache, + rectangles, n_rectangles)) + { + return cache; + } + + g_object_unref (cache); + } + } + + return NULL; +} + +void +gimp_applicator_set_crop (GimpApplicator *applicator, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + if (applicator->crop_enabled != (rect != NULL) || + (rect && ! gegl_rectangle_equal (&applicator->crop_rect, rect))) + { + if (rect) + { + if (! applicator->crop_enabled) + { + gegl_node_set (applicator->crop_node, + "operation", "gimp:compose-crop", + "x", rect->x, + "y", rect->y, + "width", rect->width, + "height", rect->height, + NULL); + + gegl_node_connect_to (applicator->input_node, "output", + applicator->crop_node, "aux"); + } + else + { + gegl_node_set (applicator->crop_node, + "x", rect->x, + "y", rect->y, + "width", rect->width, + "height", rect->height, + NULL); + } + + applicator->crop_enabled = TRUE; + applicator->crop_rect = *rect; + } + else + { + gegl_node_disconnect (applicator->crop_node, "aux"); + gegl_node_set (applicator->crop_node, + "operation", "gegl:nop", + NULL); + + applicator->crop_enabled = FALSE; + } + } +} + +const GeglRectangle * +gimp_applicator_get_crop (GimpApplicator *applicator) +{ + g_return_val_if_fail (GIMP_IS_APPLICATOR (applicator), NULL); + + if (applicator->crop_enabled) + return &applicator->crop_rect; + + return NULL; +} + +void +gimp_applicator_blit (GimpApplicator *applicator, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); + + gegl_node_blit (applicator->dest_node, 1.0, rect, + NULL, NULL, 0, GEGL_BLIT_DEFAULT); +} diff --git a/app/gegl/gimpapplicator.h b/app/gegl/gimpapplicator.h new file mode 100644 index 0000000..e2919c2 --- /dev/null +++ b/app/gegl/gimpapplicator.h @@ -0,0 +1,146 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpapplicator.h + * Copyright (C) 2012-2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_APPLICATOR_H__ +#define __GIMP_APPLICATOR_H__ + + +#define GIMP_TYPE_APPLICATOR (gimp_applicator_get_type ()) +#define GIMP_APPLICATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_APPLICATOR, GimpApplicator)) +#define GIMP_APPLICATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_APPLICATOR, GimpApplicatorClass)) +#define GIMP_IS_APPLICATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_APPLICATOR)) +#define GIMP_IS_APPLICATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_APPLICATOR)) +#define GIMP_APPLICATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_APPLICATOR, GimpApplicatorClass)) + + +typedef struct _GimpApplicatorClass GimpApplicatorClass; + +struct _GimpApplicator +{ + GObject parent_instance; + + GeglNode *node; + GeglNode *input_node; + GeglNode *aux_node; + GeglNode *output_node; + + gboolean active; + + GeglBuffer *apply_buffer; + GeglNode *apply_src_node; + + gint apply_offset_x; + gint apply_offset_y; + GeglNode *apply_offset_node; + + gdouble opacity; + GimpLayerMode paint_mode; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + GeglNode *mode_node; + + GimpComponentMask affect; + GeglNode *affect_node; + + const Babl *output_format; + GeglNode *convert_format_node; + + gboolean cache_enabled; + GeglNode *cache_node; + + gboolean crop_enabled; + GeglRectangle crop_rect; + GeglNode *crop_node; + + GeglBuffer *src_buffer; + GeglNode *src_node; + + GeglBuffer *dest_buffer; + GeglNode *dest_node; + + GeglBuffer *mask_buffer; + GeglNode *mask_node; + + gint mask_offset_x; + gint mask_offset_y; + GeglNode *mask_offset_node; +}; + +struct _GimpApplicatorClass +{ + GObjectClass parent_class; +}; + + +GType gimp_applicator_get_type (void) G_GNUC_CONST; + +GimpApplicator * gimp_applicator_new (GeglNode *parent); + +void gimp_applicator_set_active (GimpApplicator *applicator, + gboolean active); + +void gimp_applicator_set_src_buffer (GimpApplicator *applicator, + GeglBuffer *dest_buffer); +void gimp_applicator_set_dest_buffer (GimpApplicator *applicator, + GeglBuffer *dest_buffer); + +void gimp_applicator_set_mask_buffer (GimpApplicator *applicator, + GeglBuffer *mask_buffer); +void gimp_applicator_set_mask_offset (GimpApplicator *applicator, + gint mask_offset_x, + gint mask_offset_y); + +void gimp_applicator_set_apply_buffer (GimpApplicator *applicator, + GeglBuffer *apply_buffer); +void gimp_applicator_set_apply_offset (GimpApplicator *applicator, + gint apply_offset_x, + gint apply_offset_y); + +void gimp_applicator_set_opacity (GimpApplicator *applicator, + gdouble opacity); +void gimp_applicator_set_mode (GimpApplicator *applicator, + GimpLayerMode paint_mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode); +void gimp_applicator_set_affect (GimpApplicator *applicator, + GimpComponentMask affect); + +void gimp_applicator_set_output_format (GimpApplicator *applicator, + const Babl *format); +const Babl * gimp_applicator_get_output_format (GimpApplicator *applicator); + +void gimp_applicator_set_cache (GimpApplicator *applicator, + gboolean enable); +gboolean gimp_applicator_get_cache (GimpApplicator *applicator); +GeglBuffer * gimp_applicator_get_cache_buffer (GimpApplicator *applicator, + GeglRectangle **rectangles, + gint *n_rectangles); + +void gimp_applicator_set_crop (GimpApplicator *applicator, + const GeglRectangle *rect); +const GeglRectangle * gimp_applicator_get_crop (GimpApplicator *applicator); + +void gimp_applicator_blit (GimpApplicator *applicator, + const GeglRectangle *rect); + + +#endif /* __GIMP_APPLICATOR_H__ */ diff --git a/app/gegl/gimptilehandlervalidate.c b/app/gegl/gimptilehandlervalidate.c new file mode 100644 index 0000000..01915d5 --- /dev/null +++ b/app/gegl/gimptilehandlervalidate.c @@ -0,0 +1,766 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "gimp-gegl-types.h" + +#include "core/gimpchunkiterator.h" +#include "core/gimpmarshal.h" + +#include "gimp-gegl-loops.h" +#include "gimp-gegl-utils.h" +#include "gimptilehandlervalidate.h" + + +enum +{ + INVALIDATED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_FORMAT, + PROP_TILE_WIDTH, + PROP_TILE_HEIGHT, + PROP_WHOLE_TILE +}; + + +static void gimp_tile_handler_validate_finalize (GObject *object); +static void gimp_tile_handler_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tile_handler_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tile_handler_validate_real_begin_validate (GimpTileHandlerValidate *validate); +static void gimp_tile_handler_validate_real_end_validate (GimpTileHandlerValidate *validate); +static void gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + const Babl *format, + gpointer dest_buf, + gint dest_stride); +static void gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + GeglBuffer *buffer); + +static gpointer gimp_tile_handler_validate_command (GeglTileSource *source, + GeglTileCommand command, + gint x, + gint y, + gint z, + gpointer data); + + +G_DEFINE_TYPE (GimpTileHandlerValidate, gimp_tile_handler_validate, + GEGL_TYPE_TILE_HANDLER) + +#define parent_class gimp_tile_handler_validate_parent_class + +static guint gimp_tile_handler_validate_signals[LAST_SIGNAL]; + + +static void +gimp_tile_handler_validate_class_init (GimpTileHandlerValidateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gimp_tile_handler_validate_signals[INVALIDATED] = + g_signal_new ("invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpTileHandlerValidateClass, invalidated), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GEGL_TYPE_RECTANGLE); + + object_class->finalize = gimp_tile_handler_validate_finalize; + object_class->set_property = gimp_tile_handler_validate_set_property; + object_class->get_property = gimp_tile_handler_validate_get_property; + + klass->begin_validate = gimp_tile_handler_validate_real_begin_validate; + klass->end_validate = gimp_tile_handler_validate_real_end_validate; + klass->validate = gimp_tile_handler_validate_real_validate; + klass->validate_buffer = gimp_tile_handler_validate_real_validate_buffer; + + g_object_class_install_property (object_class, PROP_FORMAT, + g_param_spec_pointer ("format", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TILE_WIDTH, + g_param_spec_int ("tile-width", NULL, NULL, + 1, G_MAXINT, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TILE_HEIGHT, + g_param_spec_int ("tile-height", NULL, NULL, + 1, G_MAXINT, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_WHOLE_TILE, + g_param_spec_boolean ("whole-tile", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tile_handler_validate_init (GimpTileHandlerValidate *validate) +{ + GeglTileSource *source = GEGL_TILE_SOURCE (validate); + + source->command = gimp_tile_handler_validate_command; + + validate->dirty_region = cairo_region_create (); +} + +static void +gimp_tile_handler_validate_finalize (GObject *object) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + g_clear_object (&validate->graph); + g_clear_pointer (&validate->dirty_region, cairo_region_destroy); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tile_handler_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + switch (property_id) + { + case PROP_FORMAT: + validate->format = g_value_get_pointer (value); + break; + case PROP_TILE_WIDTH: + validate->tile_width = g_value_get_int (value); + break; + case PROP_TILE_HEIGHT: + validate->tile_height = g_value_get_int (value); + break; + case PROP_WHOLE_TILE: + validate->whole_tile = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tile_handler_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + switch (property_id) + { + case PROP_FORMAT: + g_value_set_pointer (value, (gpointer) validate->format); + break; + case PROP_TILE_WIDTH: + g_value_set_int (value, validate->tile_width); + break; + case PROP_TILE_HEIGHT: + g_value_set_int (value, validate->tile_height); + break; + case PROP_WHOLE_TILE: + g_value_set_boolean (value, validate->whole_tile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tile_handler_validate_real_begin_validate (GimpTileHandlerValidate *validate) +{ + validate->suspend_validate++; +} + +static void +gimp_tile_handler_validate_real_end_validate (GimpTileHandlerValidate *validate) +{ + validate->suspend_validate--; +} + +static void +gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + const Babl *format, + gpointer dest_buf, + gint dest_stride) +{ +#if 0 + g_printerr ("validating at %d %d %d %d\n", + rect.x, + rect.y, + rect.width, + rect.height); +#endif + + gegl_node_blit (validate->graph, 1.0, rect, format, + dest_buf, dest_stride, + GEGL_BLIT_DEFAULT); +} + +static void +gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + GeglBuffer *buffer) +{ + GimpTileHandlerValidateClass *klass; + + klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate); + + if (klass->validate == gimp_tile_handler_validate_real_validate) + { + gegl_node_blit_buffer (validate->graph, buffer, rect, 0, + GEGL_ABYSS_NONE); + } + else + { + const Babl *format = gegl_buffer_get_format (buffer); + gpointer data; + gint stride; + + data = gegl_buffer_linear_open (buffer, rect, &stride, format); + + klass->validate (validate, rect, format, data, stride); + + gegl_buffer_linear_close (buffer, data); + } +} + +static GeglTile * +gimp_tile_handler_validate_validate_tile (GeglTileSource *source, + gint x, + gint y) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (source); + GeglTile *tile; + cairo_rectangle_int_t tile_rect; + cairo_region_overlap_t overlap; + + if (validate->suspend_validate || + cairo_region_is_empty (validate->dirty_region)) + { + return gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + } + + tile_rect.x = x * validate->tile_width; + tile_rect.y = y * validate->tile_height; + tile_rect.width = validate->tile_width; + tile_rect.height = validate->tile_height; + + overlap = cairo_region_contains_rectangle (validate->dirty_region, + &tile_rect); + + if (overlap == CAIRO_REGION_OVERLAP_OUT) + { + return gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + } + + if (overlap == CAIRO_REGION_OVERLAP_IN || validate->whole_tile) + { + gint tile_bpp; + gint tile_stride; + + cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect); + + tile_bpp = babl_format_get_bytes_per_pixel (validate->format); + tile_stride = tile_bpp * validate->tile_width; + + tile = gegl_tile_handler_get_source_tile (GEGL_TILE_HANDLER (source), + x, y, 0, FALSE); + + gimp_tile_handler_validate_begin_validate (validate); + + gegl_tile_lock (tile); + + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate + (validate, + GEGL_RECTANGLE (tile_rect.x, + tile_rect.y, + tile_rect.width, + tile_rect.height), + validate->format, + gegl_tile_get_data (tile), + tile_stride); + + gegl_tile_unlock (tile); + + gimp_tile_handler_validate_end_validate (validate); + } + else + { + cairo_region_t *tile_region; + gint tile_bpp; + gint tile_stride; + gint n_rects; + gint i; + + tile_region = cairo_region_copy (validate->dirty_region); + cairo_region_intersect_rectangle (tile_region, &tile_rect); + + cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect); + + tile_bpp = babl_format_get_bytes_per_pixel (validate->format); + tile_stride = tile_bpp * validate->tile_width; + + tile = gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + + if (! tile) + { + tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), + x, y, 0); + + memset (gegl_tile_get_data (tile), + 0, tile_stride * validate->tile_height); + } + + gimp_tile_handler_validate_begin_validate (validate); + + gegl_tile_lock (tile); + + n_rects = cairo_region_num_rectangles (tile_region); + +#if 0 + g_printerr ("%d chunks\n", n_rects); +#endif + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t blit_rect; + gint tile_x; + gint tile_y; + + cairo_region_get_rectangle (tile_region, i, &blit_rect); + + tile_x = blit_rect.x % validate->tile_width; + if (tile_x < 0) tile_x += validate->tile_width; + + tile_y = blit_rect.y % validate->tile_height; + if (tile_y < 0) tile_y += validate->tile_height; + + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate + (validate, + GEGL_RECTANGLE (blit_rect.x, + blit_rect.y, + blit_rect.width, + blit_rect.height), + validate->format, + gegl_tile_get_data (tile) + + tile_y * tile_stride + + tile_x * tile_bpp, + tile_stride); + } + + gegl_tile_unlock (tile); + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_destroy (tile_region); + } + + return tile; +} + +static gpointer +gimp_tile_handler_validate_command (GeglTileSource *source, + GeglTileCommand command, + gint x, + gint y, + gint z, + gpointer data) +{ + if (command == GEGL_TILE_GET && z == 0) + return gimp_tile_handler_validate_validate_tile (source, x, y); + + return gegl_tile_handler_source_command (source, command, x, y, z, data); +} + + +/* public functions */ + +GeglTileHandler * +gimp_tile_handler_validate_new (GeglNode *graph) +{ + GimpTileHandlerValidate *validate; + + g_return_val_if_fail (GEGL_IS_NODE (graph), NULL); + + validate = g_object_new (GIMP_TYPE_TILE_HANDLER_VALIDATE, NULL); + + validate->graph = g_object_ref (graph); + + return GEGL_TILE_HANDLER (validate); +} + +void +gimp_tile_handler_validate_assign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == NULL); + + gegl_buffer_add_handler (buffer, validate); + + g_object_get (buffer, + "format", &validate->format, + "tile-width", &validate->tile_width, + "tile-height", &validate->tile_height, + NULL); + + g_object_set_data (G_OBJECT (buffer), + "gimp-tile-handler-validate", validate); +} + +void +gimp_tile_handler_validate_unassign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == validate); + + g_object_set_data (G_OBJECT (buffer), + "gimp-tile-handler-validate", NULL); + + gegl_buffer_remove_handler (buffer, validate); +} + +GimpTileHandlerValidate * +gimp_tile_handler_validate_get_assigned (GeglBuffer *buffer) +{ + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + return g_object_get_data (G_OBJECT (buffer), + "gimp-tile-handler-validate"); +} + +void +gimp_tile_handler_validate_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (rect != NULL); + + cairo_region_union_rectangle (validate->dirty_region, + (cairo_rectangle_int_t *) rect); + + gegl_tile_handler_damage_rect (GEGL_TILE_HANDLER (validate), rect); + + g_signal_emit (validate, gimp_tile_handler_validate_signals[INVALIDATED], + 0, rect, NULL); +} + +void +gimp_tile_handler_validate_undo_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (rect != NULL); + + cairo_region_subtract_rectangle (validate->dirty_region, + (cairo_rectangle_int_t *) rect); +} + +void +gimp_tile_handler_validate_begin_validate (GimpTileHandlerValidate *validate) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + + if (validate->validating++ == 0) + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->begin_validate (validate); +} + +void +gimp_tile_handler_validate_end_validate (GimpTileHandlerValidate *validate) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (validate->validating > 0); + + if (--validate->validating == 0) + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->end_validate (validate); +} + +void +gimp_tile_handler_validate_validate (GimpTileHandlerValidate *validate, + GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean intersect, + gboolean chunked) +{ + GimpTileHandlerValidateClass *klass; + cairo_region_t *region = NULL; + + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == + validate); + + klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + if (intersect) + { + region = cairo_region_copy (validate->dirty_region); + + cairo_region_intersect_rectangle (region, + (const cairo_rectangle_int_t *) rect); + } + else if (chunked) + { + region = cairo_region_create_rectangle ( + (const cairo_rectangle_int_t *) rect); + } + + if (region) + { + if (! cairo_region_is_empty (region)) + { + gimp_tile_handler_validate_begin_validate (validate); + + if (chunked) + { + GimpChunkIterator *iter; + + iter = gimp_chunk_iterator_new (region); + region = NULL; + + while (gimp_chunk_iterator_next (iter)) + { + GeglRectangle blit_rect; + + while (gimp_chunk_iterator_get_rect (iter, &blit_rect)) + klass->validate_buffer (validate, &blit_rect, buffer); + } + } + else + { + gint n_rects; + gint i; + + n_rects = cairo_region_num_rectangles (region); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t blit_rect; + + cairo_region_get_rectangle (region, i, &blit_rect); + + klass->validate_buffer (validate, + (const GeglRectangle *) &blit_rect, + buffer); + } + } + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_subtract_rectangle ( + validate->dirty_region, + (const cairo_rectangle_int_t *) rect); + } + + g_clear_pointer (®ion, cairo_region_destroy); + } + else + { + gimp_tile_handler_validate_begin_validate (validate); + + klass->validate_buffer (validate, rect, buffer); + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_subtract_rectangle ( + validate->dirty_region, + (const cairo_rectangle_int_t *) rect); + } +} + +gboolean +gimp_tile_handler_validate_buffer_set_extent (GeglBuffer *buffer, + const GeglRectangle *extent) +{ + GimpTileHandlerValidate *validate; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (extent != NULL, FALSE); + + validate = gimp_tile_handler_validate_get_assigned (buffer); + + g_return_val_if_fail (validate != NULL, FALSE); + + validate->suspend_validate++; + + if (gimp_gegl_buffer_set_extent (buffer, extent)) + { + validate->suspend_validate--; + + cairo_region_intersect_rectangle (validate->dirty_region, + (const cairo_rectangle_int_t *) extent); + + return TRUE; + } + + validate->suspend_validate--; + + return FALSE; +} + +void +gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dst_buffer, + const GeglRectangle *dst_rect) +{ + GimpTileHandlerValidate *src_validate; + GimpTileHandlerValidate *dst_validate; + GeglRectangle real_src_rect; + GeglRectangle real_dst_rect; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (GEGL_IS_BUFFER (dst_buffer)); + g_return_if_fail (src_rect != dst_rect); + + src_validate = gimp_tile_handler_validate_get_assigned (src_buffer); + dst_validate = gimp_tile_handler_validate_get_assigned (dst_buffer); + + g_return_if_fail (dst_validate != NULL); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dst_rect) + dst_rect = src_rect; + + real_src_rect = *src_rect; + + gegl_rectangle_intersect (&real_dst_rect, + dst_rect, gegl_buffer_get_extent (dst_buffer)); + + real_src_rect.x += real_dst_rect.x - dst_rect->x; + real_src_rect.y += real_dst_rect.y - dst_rect->y; + real_src_rect.width -= real_dst_rect.x - dst_rect->x; + real_src_rect.height -= real_dst_rect.y - dst_rect->y; + + real_src_rect.width = CLAMP (real_src_rect.width, 0, real_dst_rect.width); + real_src_rect.height = CLAMP (real_src_rect.height, 0, real_dst_rect.height); + + /* temporarily remove the source buffer's validate handler, so that + * gegl_buffer_copy() can use fast tile copying, using the TILE_COPY command. + * currently, gegl only uses TILE_COPY when the source buffer has no user- + * provided tile handlers. + */ + if (src_validate) + { + g_object_ref (src_validate); + + gimp_tile_handler_validate_unassign (src_validate, src_buffer); + } + + dst_validate->suspend_validate++; + + gimp_gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE, + dst_buffer, &real_dst_rect); + + dst_validate->suspend_validate--; + + if (src_validate) + { + gimp_tile_handler_validate_assign (src_validate, src_buffer); + + g_object_unref (src_validate); + } + + cairo_region_subtract_rectangle (dst_validate->dirty_region, + (cairo_rectangle_int_t *) &real_dst_rect); + + if (src_validate) + { + if (real_src_rect.x == real_dst_rect.x && + real_src_rect.y == real_dst_rect.y && + gegl_rectangle_equal (&real_src_rect, + gegl_buffer_get_extent (src_buffer))) + { + cairo_region_union (dst_validate->dirty_region, + src_validate->dirty_region); + } + else if (cairo_region_contains_rectangle ( + src_validate->dirty_region, + (cairo_rectangle_int_t *) &real_src_rect) != + CAIRO_REGION_OVERLAP_OUT) + { + cairo_region_t *region; + + region = cairo_region_copy (src_validate->dirty_region); + + if (! gegl_rectangle_equal (&real_src_rect, + gegl_buffer_get_extent (src_buffer))) + { + cairo_region_intersect_rectangle ( + region, (cairo_rectangle_int_t *) &real_src_rect); + } + + cairo_region_translate (region, + real_dst_rect.x - real_src_rect.x, + real_dst_rect.y - real_src_rect.y); + + if (cairo_region_is_empty (dst_validate->dirty_region)) + { + cairo_region_destroy (dst_validate->dirty_region); + + dst_validate->dirty_region = region; + } + else + { + cairo_region_union (dst_validate->dirty_region, region); + + cairo_region_destroy (region); + } + } + } +} diff --git a/app/gegl/gimptilehandlervalidate.h b/app/gegl/gimptilehandlervalidate.h new file mode 100644 index 0000000..998430f --- /dev/null +++ b/app/gegl/gimptilehandlervalidate.h @@ -0,0 +1,112 @@ +/* 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 . + */ + +#ifndef __GIMP_TILE_HANDLER_VALIDATE_H__ +#define __GIMP_TILE_HANDLER_VALIDATE_H__ + +#include + +/*** + * GimpTileHandlerValidate is a GeglTileHandler that renders the + * projection. + */ + +G_BEGIN_DECLS + +#define GIMP_TYPE_TILE_HANDLER_VALIDATE (gimp_tile_handler_validate_get_type ()) +#define GIMP_TILE_HANDLER_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TILE_HANDLER_VALIDATE, GimpTileHandlerValidate)) +#define GIMP_TILE_HANDLER_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TILE_HANDLER_VALIDATE, GimpTileHandlerValidateClass)) +#define GIMP_IS_TILE_HANDLER_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TILE_HANDLER_VALIDATE)) +#define GIMP_IS_TILE_HANDLER_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TILE_HANDLER_VALIDATE)) +#define GIMP_TILE_HANDLER_VALIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TILE_HANDLER_VALIDATE, GimpTileHandlerValidateClass)) + + +typedef struct _GimpTileHandlerValidate GimpTileHandlerValidate; +typedef struct _GimpTileHandlerValidateClass GimpTileHandlerValidateClass; + +struct _GimpTileHandlerValidate +{ + GeglTileHandler parent_instance; + + GeglNode *graph; + cairo_region_t *dirty_region; + const Babl *format; + gint tile_width; + gint tile_height; + gboolean whole_tile; + gint validating; + gint suspend_validate; +}; + +struct _GimpTileHandlerValidateClass +{ + GeglTileHandlerClass parent_class; + + /* signals */ + void (* invalidated) (GimpTileHandlerValidate *validate, + const GeglRectangle *rect); + + /* virtual functions */ + void (* begin_validate) (GimpTileHandlerValidate *validate); + void (* end_validate) (GimpTileHandlerValidate *validate); + void (* validate) (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + const Babl *format, + gpointer dest_buf, + gint dest_stride); + void (* validate_buffer) (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + GeglBuffer *buffer); +}; + + +GType gimp_tile_handler_validate_get_type (void) G_GNUC_CONST; + +GeglTileHandler * gimp_tile_handler_validate_new (GeglNode *graph); + +void gimp_tile_handler_validate_assign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer); +void gimp_tile_handler_validate_unassign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer); +GimpTileHandlerValidate * gimp_tile_handler_validate_get_assigned (GeglBuffer *buffer); + +void gimp_tile_handler_validate_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect); +void gimp_tile_handler_validate_undo_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect); + +void gimp_tile_handler_validate_begin_validate (GimpTileHandlerValidate *validate); +void gimp_tile_handler_validate_end_validate (GimpTileHandlerValidate *validate); + +void gimp_tile_handler_validate_validate (GimpTileHandlerValidate *validate, + GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean intersect, + gboolean chunked); + +gboolean gimp_tile_handler_validate_buffer_set_extent (GeglBuffer *buffer, + const GeglRectangle *extent); + +void gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dst_buffer, + const GeglRectangle *dst_rect); + + +G_END_DECLS + +#endif /* __GIMP_TILE_HANDLER_VALIDATE_H__ */ diff --git a/app/gimp-debug.c b/app/gimp-debug.c new file mode 100644 index 0000000..a5668ef --- /dev/null +++ b/app/gimp-debug.c @@ -0,0 +1,122 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-debug.c + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "core/core-types.h" + +#include "core/gimpobject.h" + +#include "gimp-debug.h" + + +static GHashTable *class_hash = NULL; + + +void +gimp_debug_enable_instances (void) +{ + g_return_if_fail (class_hash == NULL); + + class_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify ) g_hash_table_unref); +} + +void +gimp_debug_add_instance (GObject *instance, + GObjectClass *klass) +{ + if (class_hash) + { + GHashTable *instance_hash; + const gchar *type_name; + + type_name = g_type_name (G_TYPE_FROM_CLASS (klass)); + + instance_hash = g_hash_table_lookup (class_hash, type_name); + + if (! instance_hash) + { + instance_hash = g_hash_table_new (g_direct_hash, + g_direct_equal); + g_hash_table_insert (class_hash, (gchar *) type_name, instance_hash); + } + + g_hash_table_insert (instance_hash, instance, instance); + } +} + +void +gimp_debug_remove_instance (GObject *instance) +{ + if (class_hash) + { + GHashTable *instance_hash; + const gchar *type_name; + + type_name = g_type_name (G_OBJECT_TYPE (instance)); + + instance_hash = g_hash_table_lookup (class_hash, type_name); + + if (instance_hash) + { + g_hash_table_remove (instance_hash, instance); + + if (g_hash_table_size (instance_hash) == 0) + g_hash_table_remove (class_hash, type_name); + } + } +} + +static void +gimp_debug_instance_foreach (GObject *instance) +{ + g_printerr (" \'%s\': ref_count = %d\n", + GIMP_IS_OBJECT (instance) ? + gimp_object_get_name (instance) : "GObject", + instance->ref_count); +} + +static void +gimp_debug_class_foreach (const gchar *type_name, + GHashTable *instance_hash) +{ + g_printerr ("Leaked %s instances: %d\n", + type_name, g_hash_table_size (instance_hash)); + + g_hash_table_foreach (instance_hash, + (GHFunc) gimp_debug_instance_foreach, + NULL); +} + +void +gimp_debug_instances (void) +{ + if (class_hash) + { + g_hash_table_foreach (class_hash, + (GHFunc) gimp_debug_class_foreach, + NULL); + } +} diff --git a/app/gimp-debug.h b/app/gimp-debug.h new file mode 100644 index 0000000..59b43f8 --- /dev/null +++ b/app/gimp-debug.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-debug.h + * Copyright (C) 2010 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DEBUG_H__ +#define __GIMP_DEBUG_H__ + + +void gimp_debug_enable_instances (void); + +void gimp_debug_add_instance (GObject *instance, + GObjectClass *klass); +void gimp_debug_remove_instance (GObject *instance); + +void gimp_debug_instances (void); + + +#endif /* __GIMP_DEBUG_H__ */ diff --git a/app/gimp-intl.h b/app/gimp-intl.h new file mode 100644 index 0000000..178ae47 --- /dev/null +++ b/app/gimp-intl.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 . + */ + +#ifndef __GIMP_INTL_H__ +#define __GIMP_INTL_H__ + +#ifndef GETTEXT_PACKAGE +#error "config.h must be included prior to gimp-intl.h" +#endif + +#include + +#endif /* __GIMP_INTL_H__ */ diff --git a/app/gimp-log.c b/app/gimp-log.c new file mode 100644 index 0000000..30a74a4 --- /dev/null +++ b/app/gimp-log.c @@ -0,0 +1,219 @@ +/* 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 . + */ + +#include "config.h" + +#include "glib-object.h" + +#include "gimp-debug.h" +#include "gimp-log.h" + + +static const GDebugKey log_keys[] = +{ + { "tool-events", GIMP_LOG_TOOL_EVENTS }, + { "tool-focus", GIMP_LOG_TOOL_FOCUS }, + { "dnd", GIMP_LOG_DND }, + { "help", GIMP_LOG_HELP }, + { "dialog-factory", GIMP_LOG_DIALOG_FACTORY }, + { "menus", GIMP_LOG_MENUS }, + { "save-dialog", GIMP_LOG_SAVE_DIALOG }, + { "image-scale", GIMP_LOG_IMAGE_SCALE }, + { "shadow-tiles", GIMP_LOG_SHADOW_TILES }, + { "scale", GIMP_LOG_SCALE }, + { "wm", GIMP_LOG_WM }, + { "floating-selection", GIMP_LOG_FLOATING_SELECTION }, + { "shm", GIMP_LOG_SHM }, + { "text-editing", GIMP_LOG_TEXT_EDITING }, + { "key-events", GIMP_LOG_KEY_EVENTS }, + { "auto-tab-style", GIMP_LOG_AUTO_TAB_STYLE }, + { "instances", GIMP_LOG_INSTANCES }, + { "rectangle-tool", GIMP_LOG_RECTANGLE_TOOL }, + { "brush-cache", GIMP_LOG_BRUSH_CACHE }, + { "projection", GIMP_LOG_PROJECTION }, + { "xcf", GIMP_LOG_XCF } +}; + +static const gchar * const log_domains[] = +{ + "Gimp", + "Gimp-Actions", + "Gimp-Base", + "Gimp-Composite", + "Gimp-Config", + "Gimp-Core", + "Gimp-Dialogs", + "Gimp-Display", + "Gimp-File", + "Gimp-GEGL", + "Gimp-GUI", + "Gimp-Menus", + "Gimp-Operations", + "Gimp-PDB", + "Gimp-Paint", + "Gimp-Paint-Funcs", + "Gimp-Plug-In", + "Gimp-Text", + "Gimp-Tools", + "Gimp-Vectors", + "Gimp-Widgets", + "Gimp-XCF", + "LibGimpBase", + "LibGimpColor", + "LibGimpConfig", + "LibGimpMath", + "LibGimpModule", + "LibGimpThumb", + "LibGimpWidgets", + "GEGL", + NULL +}; + + +GimpLogFlags gimp_log_flags = 0; + + +void +gimp_log_init (void) +{ + const gchar *env_log_val = g_getenv ("GIMP_LOG"); + + if (! env_log_val) + env_log_val = g_getenv ("GIMP_DEBUG"); + + if (env_log_val) + g_setenv ("G_MESSAGES_DEBUG", env_log_val, TRUE); + + if (env_log_val) + { + /* g_parse_debug_string() has special treatment of the string 'help', + * but we want to use it for the GIMP_LOG_HELP domain. "list-all" + * is a replacement for "help" in GIMP. + */ + if (g_ascii_strcasecmp (env_log_val, "list-all") == 0) + gimp_log_flags = g_parse_debug_string ("help", + log_keys, + G_N_ELEMENTS (log_keys)); + else if (g_ascii_strcasecmp (env_log_val, "help") == 0) + gimp_log_flags = GIMP_LOG_HELP; + else + gimp_log_flags = g_parse_debug_string (env_log_val, + log_keys, + G_N_ELEMENTS (log_keys)); + + if (gimp_log_flags & GIMP_LOG_INSTANCES) + { + gimp_debug_enable_instances (); + } + else if (! gimp_log_flags) + { + /* If the environment variable was set but no log flags are + * set as a result, let's assume one is not sure how to use + * the log flags and output the list of keys as a helper. + */ + gimp_log_flags = g_parse_debug_string ("help", + log_keys, + G_N_ELEMENTS (log_keys)); + } + } +} + +void +gimp_log (GimpLogFlags flags, + const gchar *function, + gint line, + const gchar *format, + ...) +{ + va_list args; + + va_start (args, format); + gimp_logv (flags, function, line, format, args); + va_end (args); +} + +void +gimp_logv (GimpLogFlags flags, + const gchar *function, + gint line, + const gchar *format, + va_list args) +{ + const gchar *domain = "unknown"; + gchar *message; + gint i; + + for (i = 0; i < G_N_ELEMENTS (log_keys); i++) + if (log_keys[i].value == flags) + { + domain = log_keys[i].key; + break; + } + + if (format) + message = g_strdup_vprintf (format, args); + else + message = g_strdup ("called"); + + g_log (domain, G_LOG_LEVEL_DEBUG, + "%s(%d): %s", function, line, message); + + g_free (message); +} + +GimpLogHandler +gimp_log_set_handler (gboolean global, + GLogLevelFlags log_levels, + GLogFunc log_func, + gpointer user_data) +{ + GimpLogHandler handler; + gint n; + gint i; + + g_return_val_if_fail (log_func != NULL, NULL); + + n = G_N_ELEMENTS (log_domains) - (global ? 1 : 0); + + handler = g_new (guint, n + 1); + + handler[0] = n; + + for (i = 0; i < n; i++) + { + handler[i + 1] = g_log_set_handler (log_domains[i], log_levels, + log_func, user_data); + } + + return handler; +} + +void +gimp_log_remove_handler (GimpLogHandler handler) +{ + gint n; + gint i; + + g_return_if_fail (handler != NULL); + + n = handler[0]; + + for (i = 0; i < n; i++) + g_log_remove_handler (log_domains[i], handler[i + 1]); + + g_free (handler); +} diff --git a/app/gimp-log.h b/app/gimp-log.h new file mode 100644 index 0000000..41e0589 --- /dev/null +++ b/app/gimp-log.h @@ -0,0 +1,138 @@ +/* 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 . + */ + +#ifndef __GIMP_LOG_H__ +#define __GIMP_LOG_H__ + + +typedef guint *GimpLogHandler; + + +typedef enum +{ + GIMP_LOG_TOOL_EVENTS = 1 << 0, + GIMP_LOG_TOOL_FOCUS = 1 << 1, + GIMP_LOG_DND = 1 << 2, + GIMP_LOG_HELP = 1 << 3, + GIMP_LOG_DIALOG_FACTORY = 1 << 4, + GIMP_LOG_MENUS = 1 << 5, + GIMP_LOG_SAVE_DIALOG = 1 << 6, + GIMP_LOG_IMAGE_SCALE = 1 << 7, + GIMP_LOG_SHADOW_TILES = 1 << 8, + GIMP_LOG_SCALE = 1 << 9, + GIMP_LOG_WM = 1 << 10, + GIMP_LOG_FLOATING_SELECTION = 1 << 11, + GIMP_LOG_SHM = 1 << 12, + GIMP_LOG_TEXT_EDITING = 1 << 13, + GIMP_LOG_KEY_EVENTS = 1 << 14, + GIMP_LOG_AUTO_TAB_STYLE = 1 << 15, + GIMP_LOG_INSTANCES = 1 << 16, + GIMP_LOG_RECTANGLE_TOOL = 1 << 17, + GIMP_LOG_BRUSH_CACHE = 1 << 18, + GIMP_LOG_PROJECTION = 1 << 19, + GIMP_LOG_XCF = 1 << 20, + GIMP_LOG_MAGIC_MATCH = 1 << 21 +} GimpLogFlags; + + +extern GimpLogFlags gimp_log_flags; + + +void gimp_log_init (void); +void gimp_log (GimpLogFlags flags, + const gchar *function, + gint line, + const gchar *format, + ...) G_GNUC_PRINTF (4, 5); +void gimp_logv (GimpLogFlags flags, + const gchar *function, + gint line, + const gchar *format, + va_list args) G_GNUC_PRINTF (4, 0); + +GimpLogHandler gimp_log_set_handler (gboolean global, + GLogLevelFlags log_levels, + GLogFunc log_func, + gpointer user_data); +void gimp_log_remove_handler (GimpLogHandler handler); + + +#ifdef G_HAVE_ISO_VARARGS + +#define GIMP_LOG(type, ...) \ + G_STMT_START { \ + if (gimp_log_flags & GIMP_LOG_##type) \ + gimp_log (GIMP_LOG_##type, G_STRFUNC, __LINE__, __VA_ARGS__); \ + } G_STMT_END + +#elif defined(G_HAVE_GNUC_VARARGS) + +#define GIMP_LOG(type, format...) \ + G_STMT_START { \ + if (gimp_log_flags & GIMP_LOG_##type) \ + gimp_log (GIMP_LOG_##type, G_STRFUNC, __LINE__, format); \ + } G_STMT_END + +#else /* no varargs macros */ + +/* need to expand all the short forms + * to make them known constants at compile time + */ +#define TOOL_EVENTS GIMP_LOG_TOOL_EVENTS +#define TOOL_FOCUS GIMP_LOG_TOOL_FOCUS +#define DND GIMP_LOG_DND +#define HELP GIMP_LOG_HELP +#define DIALOG_FACTORY GIMP_LOG_DIALOG_FACTORY +#define MENUS GIMP_LOG_MENUS +#define SAVE_DIALOG GIMP_LOG_SAVE_DIALOG +#define IMAGE_SCALE GIMP_LOG_IMAGE_SCALE +#define SHADOW_TILES GIMP_LOG_SHADOW_TILES +#define SCALE GIMP_LOG_SCALE +#define WM GIMP_LOG_WM +#define FLOATING_SELECTION GIMP_LOG_FLOATING_SELECTION +#define SHM GIMP_LOG_SHM +#define TEXT_EDITING GIMP_LOG_TEXT_EDITING +#define KEY_EVENTS GIMP_LOG_KEY_EVENTS +#define AUTO_TAB_STYLE GIMP_LOG_AUTO_TAB_STYLE +#define INSTANCES GIMP_LOG_INSTANCES +#define RECTANGLE_TOOL GIMP_LOG_RECTANGLE_TOOL +#define BRUSH_CACHE GIMP_LOG_BRUSH_CACHE +#define PROJECTION GIMP_LOG_PROJECTION +#define XCF GIMP_LOG_XCF + +#if 0 /* last resort */ +# define GIMP_LOG /* nothing => no varargs, no log */ +#endif + +static void +GIMP_LOG (GimpLogFlags flags, + const gchar *format, + ...) +{ + va_list args; + va_start (args, format); + if (gimp_log_flags & flags) + gimp_logv (type, "", 0, format, args); + va_end (args); +} + +#endif /* !__GNUC__ */ + +#define geimnum(vienna) gimp_l##vienna##l_dialog() +#define fnord(kosmoso) void gimp_##kosmoso##bl_dialog(void); + +#endif /* __GIMP_LOG_H__ */ diff --git a/app/gimp-priorities.h b/app/gimp-priorities.h new file mode 100644 index 0000000..198f1d3 --- /dev/null +++ b/app/gimp-priorities.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 . + */ + +#ifndef __GIMP_PRIORITIES_H__ +#define __GIMP_PRIORITIES_H__ + + +/* #define G_PRIORITY_HIGH -100 */ + +/* #define G_PRIORITY_DEFAULT 0 */ + +/* #define G_PRIORITY_HIGH_IDLE 100 */ + +/* #define GTK_PRIORITY_REDRAW (G_PRIORITY_HIGH_IDLE + 20) */ + +/* a bit higher than projection construction */ +#define GIMP_PRIORITY_DISPLAY_SHELL_FILL_IDLE (G_PRIORITY_HIGH_IDLE + 21) +#define GIMP_PRIORITY_IMAGE_WINDOW_UPDATE_UI_MANAGER_IDLE (G_PRIORITY_HIGH_IDLE + 21) + +/* just a bit less than GDK_PRIORITY_REDRAW */ +#define GIMP_PRIORITY_PROJECTION_IDLE (G_PRIORITY_HIGH_IDLE + 22) + +/* #define G_PRIORITY_DEFAULT_IDLE 200 */ + +#define GIMP_PRIORITY_VIEWABLE_IDLE (G_PRIORITY_LOW) + +/* #define G_PRIORITY_LOW 300 */ + + +#endif /* __GIMP_PRIORITIES_H__ */ diff --git a/app/gimp-update.c b/app/gimp-update.c new file mode 100644 index 0000000..79d7b38 --- /dev/null +++ b/app/gimp-update.c @@ -0,0 +1,593 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-update.c + * Copyright (C) 2019 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#ifdef PLATFORM_OSX +#import +#endif /* PLATFORM_OSX */ + +#ifndef GIMP_CONSOLE_COMPILATION +#include +#endif + +#include "libgimpbase/gimpbase.h" + +#include "core/core-types.h" + +#include "config/gimpcoreconfig.h" + +#ifndef GIMP_CONSOLE_COMPILATION +#include "dialogs/about-dialog.h" +#endif + +#include "gimp-intl.h" +#include "gimp-update.h" +#include "gimp-version.h" + +static gboolean gimp_update_known (GimpCoreConfig *config, + const gchar *last_version, + gint64 release_timestamp, + gint build_revision, + const gchar *comment); +static void gimp_check_updates_process (const gchar *source, + gchar *file_contents, + gsize file_length, + GimpCoreConfig *config); +#ifndef PLATFORM_OSX +static void gimp_check_updates_callback (GObject *source, + GAsyncResult *result, + gpointer user_data); +#endif /* PLATFORM_OSX */ +static void gimp_update_about_dialog (GimpCoreConfig *config, + const GParamSpec *pspec, + gpointer user_data); + +static gboolean gimp_version_break (const gchar *v, + gint *major, + gint *minor, + gint *micro); + +static const gchar * gimp_get_version_url (void); + +#ifdef PLATFORM_OSX +static gint gimp_check_updates_process_idle (gpointer data); +#endif + +/* Private Functions */ + +/** + * gimp_update_known: + * @config: + * @last_version: + * @release_timestamp: must be non-zero is @last_version is not %NULL. + * @build_revision: + * @build_comment: + * + * Compare @last_version with currently running version. If the checked + * version is more recent than running version, then @config properties + * are updated appropriately (which may trigger a dialog depending on + * update policy settings). + * If @last_version is %NULL, the currently stored "last known release" + * is compared. Even if we haven't made any new remote checks, it is + * important to always compare again stored last release, otherwise we + * might warn of the same current version, or worse an older version. + * + * Returns: %TRUE is @last_version (or stored last known release if + * @last_version was %NULL) is newer than running version. + */ +static gboolean +gimp_update_known (GimpCoreConfig *config, + const gchar *last_version, + gint64 release_timestamp, + gint build_revision, + const gchar *build_comment) +{ + gboolean new_check = (last_version != NULL); + gint major; + gint minor; + gint micro; + + if (last_version && release_timestamp == 0) + { + /* I don't exit with a g_return_val_if_fail() assert because this + * is not necessarily a code bug. It may be data issues. So let's + * just return with an error printed on stderr. + */ + g_printerr ("%s: version %s with no release dates.\n", + G_STRFUNC, last_version); + return FALSE; + } + + if (last_version == NULL) + { + last_version = config->last_known_release; + release_timestamp = config->last_release_timestamp; + build_revision = config->last_revision; + build_comment = config->last_release_comment; + } + + if (last_version) + { + if (gimp_version_break (last_version, &major, &minor, µ)) + { + if (/* We are using a newer version than last check. This could + * happen if updating the config files without having + * re-checked the remote JSON file. + */ + (major < GIMP_MAJOR_VERSION || + (major == GIMP_MAJOR_VERSION && minor < GIMP_MINOR_VERSION) || + (major == GIMP_MAJOR_VERSION && minor == GIMP_MINOR_VERSION && micro < GIMP_MICRO_VERSION)) || + /* Already using the last officially released + * revision. */ + (major == GIMP_MAJOR_VERSION && + minor == GIMP_MINOR_VERSION && + micro == GIMP_MICRO_VERSION && + build_revision <= gimp_version_get_revision ())) + { + last_version = NULL; + } + } + else + { + /* If version is not properly parsed, something is wrong with + * upstream version number or parsing. This should not happen. + */ + g_printerr ("%s: version not properly formatted: %s\n", + G_STRFUNC, last_version); + + return FALSE; + } + } + + if (last_version == NULL) + { + release_timestamp = 0; + build_revision = 0; + build_comment = NULL; + } + + if (new_check) + g_object_set (config, + "check-update-timestamp", g_get_real_time() / G_USEC_PER_SEC, + NULL); + + g_object_set (config, + "last-release-timestamp", release_timestamp, + "last-known-release", last_version, + "last-revision", build_revision, + "last-release-comment", build_comment, + NULL); + + /* Are we running an old GIMP? */ + return (last_version != NULL); +} + +static gboolean +gimp_version_break (const gchar *v, + gint *major, + gint *minor, + gint *micro) +{ + gchar **versions; + + *major = 0; + *minor = 0; + *micro = 0; + + if (v == NULL) + return FALSE; + + versions = g_strsplit_set (v, ".", 3); + if (versions[0] != NULL) + { + *major = g_ascii_strtoll (versions[0], NULL, 10); + if (versions[1] != NULL) + { + *minor = g_ascii_strtoll (versions[1], NULL, 10); + if (versions[2] != NULL) + { + *micro = g_ascii_strtoll (versions[2], NULL, 10); + return TRUE; + } + } + } + g_strfreev (versions); + + return (*major > 0); +} + +static void +gimp_check_updates_process (const gchar *source, + gchar *file_contents, + gsize file_length, + GimpCoreConfig *config) +{ + const gchar *platform; + const gchar *last_version = NULL; + const gchar *release_date = NULL; + const gchar *build_comment = NULL; + GError *error = NULL; + gint64 release_timestamp = 0; + gint build_revision = 0; + JsonParser *parser; + JsonPath *path; + JsonNode *result; + JsonArray *versions; + gint i; + + /* For Windows and macOS, let's look if installers are available. + * For other platforms, let's just look for source release. + */ + if (g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "windows") == 0 || + g_strcmp0 (GIMP_BUILD_PLATFORM_FAMILY, "macos") == 0) + platform = GIMP_BUILD_PLATFORM_FAMILY; + else + platform = "source"; + + parser = json_parser_new (); + if (! json_parser_load_from_data (parser, file_contents, file_length, &error)) + { + g_printerr ("%s: parsing of %s failed: %s\n", G_STRFUNC, + source, error->message); + g_free (file_contents); + g_clear_object (&parser); + g_clear_error (&error); + + return; + } + + path = json_path_new (); + /* Ideally we could just use Json path filters like this to + * retrieve only released binaries for a given platform: + * g_strdup_printf ("$['STABLE'][?(@.%s)]['version']", platform); + * json_array_get_string_element (result, 0); + * And that would be it! We'd have our last release for given + * platform. + * Unfortunately json-glib does not support filter syntax, so we + * end up looping through releases. + */ + if (! json_path_compile (path, "$['STABLE'][*]", &error)) + { +#ifdef GIMP_UNSTABLE + g_printerr("Path compilation failed: %s\n", error->message); +#endif + g_free (file_contents); + g_clear_object (&parser); + g_clear_error (&error); + + return; + } + result = json_path_match (path, json_parser_get_root (parser)); + g_return_if_fail (JSON_NODE_HOLDS_ARRAY (result)); + + versions = json_node_get_array (result); + for (i = 0; i < (gint) json_array_get_length (versions); i++) + { + JsonObject *version; + + /* Note that we don't actually look for the highest version, + * but for the highest version for which a build for your + * platform (and optional build-id) is available. + * + * So we loop through the version list then the build array + * and break at first compatible release, since JSON arrays + * are ordered. + */ + version = json_array_get_object_element (versions, i); + if (json_object_has_member (version, platform)) + { + JsonArray *builds; + gint j; + + builds = json_object_get_array_member (version, platform); + + for (j = 0; j < (gint) json_array_get_length (builds); j++) + { + const gchar *build_id = NULL; + JsonObject *build; + + build = json_array_get_object_element (builds, j); + if (json_object_has_member (build, "build-id")) + build_id = json_object_get_string_member (build, "build-id"); + if (g_strcmp0 (build_id, GIMP_BUILD_ID) == 0 || + g_strcmp0 (platform, "source") == 0) + { + /* Release date is the build date if any set, + * otherwise the main version release date. + */ + if (json_object_has_member (build, "date")) + release_date = json_object_get_string_member (build, "date"); + else + release_date = json_object_get_string_member (version, "date"); + + /* These are optional data. */ + if (json_object_has_member (build, "revision")) + build_revision = json_object_get_int_member (build, "revision"); + if (json_object_has_member (build, "comment")) + build_comment = json_object_get_string_member (build, "comment"); + break; + } + } + + if (release_date) + { + last_version = json_object_get_string_member (version, "version"); + break; + } + } + } + + if (last_version && release_date) + { + GDateTime *datetime; + gchar *str; + + str = g_strdup_printf ("%s 00:00:00Z", release_date); + datetime = g_date_time_new_from_iso8601 (str, NULL); + g_free (str); + + if (datetime) + { + release_timestamp = g_date_time_to_unix (datetime); + g_date_time_unref (datetime); + } + else + { + /* JSON file data bug. */ + g_printerr ("%s: release date for version %s not properly formatted: %s\n", + G_STRFUNC, last_version, release_date); + + last_version = NULL; + release_date = NULL; + build_revision = 0; + build_comment = NULL; + } + } + gimp_update_known (config, last_version, release_timestamp, build_revision, build_comment); + + g_object_unref (path); + g_object_unref (parser); + g_free (file_contents); +} + +#ifndef PLATFORM_OSX +static void +gimp_check_updates_callback (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GimpCoreConfig *config = user_data; + char *file_contents = NULL; + gsize file_length = 0; + GError *error = NULL; + + if (g_file_load_contents_finish (G_FILE (source), result, + &file_contents, &file_length, + NULL, &error)) + { + gimp_check_updates_process (g_file_get_uri (G_FILE (source)), file_contents, file_length, config); + } + else + { + g_printerr("%s: loading of %s failed: %s\n", G_STRFUNC, + g_file_get_uri (G_FILE (source)), error->message); + g_clear_error (&error); + } +} +#endif /* PLATFORM_OSX */ + +static void +gimp_update_about_dialog (GimpCoreConfig *config, + const GParamSpec *pspec, + gpointer user_data) +{ + g_signal_handlers_disconnect_by_func (config, + (GCallback) gimp_update_about_dialog, + NULL); + + if (config->last_known_release != NULL) + { +#ifndef GIMP_CONSOLE_COMPILATION + gtk_widget_show (about_dialog_create (config)); +#else + g_warning (_("A new version of GIMP (%s) was released.\n" + "It is recommended to update."), + config->last_known_release); +#endif + } +} + +static const gchar * +gimp_get_version_url () +{ +#ifdef GIMP_RELEASE + return "https://www.gimp.org/gimp_versions.json"; +#else + if (g_getenv ("GIMP_DEV_VERSIONS_JSON")) + return g_getenv ("GIMP_DEV_VERSIONS_JSON"); + else + return "https://testing.gimp.org/gimp_versions.json"; +#endif +} + +#ifdef PLATFORM_OSX +typedef struct _GimpCheckUpdatesData +{ + const gchar *gimp_versions; + gchar *json_result; + gsize json_size; + GimpCoreConfig *config; +} GimpCheckUpdatesData; + +static int +gimp_check_updates_process_idle (gpointer data) +{ + GimpCheckUpdatesData *check_updates_data = (GimpCheckUpdatesData *) data; + + gimp_check_updates_process (check_updates_data->gimp_versions, + check_updates_data->json_result, + check_updates_data->json_size, + check_updates_data->config); + + g_free (check_updates_data); + + return FALSE; /* remove idle */ +} +#endif /* PLATFORM_OSX */ + +/* Public Functions */ + +/* + * gimp_update_auto_check: + * @config: + * + * Run the check for newer versions of GIMP if conditions are right. + * + * Returns: %TRUE if a check was actually run. + */ +gboolean +gimp_update_auto_check (GimpCoreConfig *config) +{ + gint64 prev_update_timestamp; + gint64 current_timestamp; + + /* Builds with update check deactivated just always return FALSE. */ +#ifdef CHECK_UPDATE + /* Allows to disable updates at package level with a build having the + * version check code built-in. + * For instance, it would allow to use the same Windows installer for + * the Windows Store (with update check disabled because it comes with + * its own update channel). + */ + if (! gimp_version_check_update () || + ! config->check_updates) +#endif + return FALSE; + + g_object_get (config, + "check-update-timestamp", &prev_update_timestamp, + NULL); + current_timestamp = g_get_real_time() / G_USEC_PER_SEC; + + /* Get rid of invalid saved timestamps. */ + if (prev_update_timestamp > current_timestamp) + prev_update_timestamp = -1; + +#ifdef GIMP_RELEASE + /* Do not check more than once a week. */ + if (current_timestamp - prev_update_timestamp < 3600L * 24L * 7L) + return FALSE; +#endif + + g_signal_connect (config, "notify::last-known-release", + (GCallback) gimp_update_about_dialog, + NULL); + + gimp_update_check (config); + + return TRUE; +} + +/* + * gimp_update_check: + * @config: + * + * Run the check for newer versions of GIMP inconditionnally. + */ +void +gimp_update_check (GimpCoreConfig *config) +{ +#ifdef PLATFORM_OSX + const gchar *gimp_versions; + + gimp_versions = gimp_get_version_url (); + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; + [request setURL:[NSURL URLWithString:@(gimp_versions)]]; + [request setHTTPMethod:@"GET"]; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + /* completionHandler is called on a background thread */ + [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + NSString *reply; + gchar *json_result; + GimpCheckUpdatesData *update_results; + + if (error) + { + g_printerr ("%s: gimp_update_check failed to get update from %s, with error: %s\n", + G_STRFUNC, + gimp_versions, + [error.localizedDescription UTF8String]); + return; + } + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + + if (statusCode != 200) + { + g_printerr ("%s: gimp_update_check failed to get update from %s, with status code: %d\n", + G_STRFUNC, + gimp_versions, + (int)statusCode); + return; + } + } + + reply = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + json_result = g_strdup ([reply UTF8String]); /* will be freed by gimp_check_updates_process */ + + update_results = g_new (GimpCheckUpdatesData, 1); + + update_results->gimp_versions = gimp_versions; + update_results->json_result = json_result; + update_results->json_size = [reply lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + update_results->config = config; + + g_idle_add ((GSourceFunc) gimp_check_updates_process_idle, (gpointer) update_results); + }] resume]; +#else + GFile *gimp_versions; + + gimp_versions = g_file_new_for_uri (gimp_get_version_url ()); + + g_file_load_contents_async (gimp_versions, NULL, gimp_check_updates_callback, config); + g_object_unref (gimp_versions); +#endif /* PLATFORM_OSX */ +} + +/* + * gimp_update_refresh: + * @config: + * + * Do not execute a remote check, but refresh the known release data as + * it may be outdated. + */ +void +gimp_update_refresh (GimpCoreConfig *config) +{ + gimp_update_known (config, NULL, 0, 0, NULL); +} diff --git a/app/gimp-update.h b/app/gimp-update.h new file mode 100644 index 0000000..836f2bc --- /dev/null +++ b/app/gimp-update.h @@ -0,0 +1,30 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-update.h + * Copyright (C) 2019 Jehan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __APP_GIMP_UPDATE_H__ +#define __APP_GIMP_UPDATE_H__ + + +gboolean gimp_update_auto_check (GimpCoreConfig *config); +void gimp_update_check (GimpCoreConfig *config); +void gimp_update_refresh (GimpCoreConfig *config); + + +#endif /* __APP_GIMP_UPDATE_H__ */ diff --git a/app/gimp-version.c b/app/gimp-version.c new file mode 100644 index 0000000..e56149a --- /dev/null +++ b/app/gimp-version.c @@ -0,0 +1,311 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef GIMP_CONSOLE_COMPILATION +#include +#endif + +#include "libgimpbase/gimpbase.h" + +#include "about.h" +#include "git-version.h" + +#include "gimp-intl.h" +#include "gimp-version.h" + + +static gchar * +gimp_library_version (const gchar *package, + gint build_time_major, + gint build_time_minor, + gint build_time_micro, + gint run_time_major, + gint run_time_minor, + gint run_time_micro, + gboolean localized) +{ + gchar *lib_version; + gchar *build_time_version; + gchar *run_time_version; + + build_time_version = g_strdup_printf ("%d.%d.%d", + build_time_major, + build_time_minor, + build_time_micro); + run_time_version = g_strdup_printf ("%d.%d.%d", + run_time_major, + run_time_minor, + run_time_micro); + + /* show versions of libraries used by GIMP */ + lib_version = g_strdup_printf (localized ? + _("using %s version %s (compiled against version %s)") : + "using %s version %s (compiled against version %s)", + package, run_time_version, build_time_version); + g_free (run_time_version); + g_free (build_time_version); + + return lib_version; +} + +static gchar * +gimp_library_versions (gboolean localized) +{ + gchar *lib_versions; + gchar *lib_version; + gchar *temp; + gint babl_major_version; + gint babl_minor_version; + gint babl_micro_version; + gint gegl_major_version; + gint gegl_minor_version; + gint gegl_micro_version; + + babl_get_version (&babl_major_version, + &babl_minor_version, + &babl_micro_version); + + lib_versions = gimp_library_version ("babl", + BABL_MAJOR_VERSION, + BABL_MINOR_VERSION, + BABL_MICRO_VERSION, + babl_major_version, + babl_minor_version, + babl_micro_version, + localized); + + gegl_get_version (&gegl_major_version, + &gegl_minor_version, + &gegl_micro_version); + + lib_version = gimp_library_version ("GEGL", + GEGL_MAJOR_VERSION, + GEGL_MINOR_VERSION, + GEGL_MICRO_VERSION, + gegl_major_version, + gegl_minor_version, + gegl_micro_version, + localized); + + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + + lib_version = gimp_library_version ("GLib", + GLIB_MAJOR_VERSION, + GLIB_MINOR_VERSION, + GLIB_MICRO_VERSION, + glib_major_version, + glib_minor_version, + glib_micro_version, + localized); + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + + lib_version = gimp_library_version ("GdkPixbuf", + GDK_PIXBUF_MAJOR, + GDK_PIXBUF_MINOR, + GDK_PIXBUF_MICRO, + gdk_pixbuf_major_version, + gdk_pixbuf_minor_version, + gdk_pixbuf_micro_version, + localized); + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + +#ifndef GIMP_CONSOLE_COMPILATION + lib_version = gimp_library_version ("GTK+", + GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION, + gtk_major_version, + gtk_minor_version, + gtk_micro_version, + localized); + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; +#endif + + lib_version = gimp_library_version ("Pango", + PANGO_VERSION_MAJOR, + PANGO_VERSION_MINOR, + PANGO_VERSION_MICRO, + pango_version () / 100 / 100, + pango_version () / 100 % 100, + pango_version () % 100, + localized); + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + + lib_version = gimp_library_version ("Fontconfig", + FC_MAJOR, FC_MINOR, FC_REVISION, + FcGetVersion () / 100 / 100, + FcGetVersion () / 100 % 100, + FcGetVersion () % 100, + localized); + temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + + lib_version = g_strdup_printf (localized ? + _("using %s version %s (compiled against version %s)") : + "using %s version %s (compiled against version %s)", + "Cairo", cairo_version_string (), CAIRO_VERSION_STRING); + temp = g_strdup_printf ("%s\n%s\n", lib_versions, lib_version); + g_free (lib_versions); + g_free (lib_version); + lib_versions = temp; + + return lib_versions; +} + +void +gimp_version_show (gboolean be_verbose) +{ + gchar *version = gimp_version (be_verbose, TRUE); + + g_print ("%s", version); + + g_free (version); +} + +gchar * +gimp_version (gboolean be_verbose, + gboolean localized) +{ + gchar *version; + gchar *temp; + + version = g_strdup_printf (localized ? _("%s version %s") : "%s version %s", + GIMP_NAME, GIMP_VERSION);; + temp = g_strconcat (version, "\n", NULL); + g_free (version); + version = temp; + + if (be_verbose) + { + gchar *verbose_info; + gchar *lib_versions; + gchar *flatpak_info = NULL; + + lib_versions = gimp_library_versions (localized); + verbose_info = g_strdup_printf ("git-describe: %s\n" + "Build: %s rev %d for %s\n" + "# C compiler #\n%s\n" + "# Libraries #\n%s", + GIMP_GIT_VERSION, + GIMP_BUILD_ID, + gimp_version_get_revision (), + GIMP_BUILD_PLATFORM_FAMILY, + CC_VERSION, + lib_versions); + g_free (lib_versions); + + /* This file should be available at root path in a flatpak + * environment. Just add its contents to the verbose output if it + * exists. Silently ignore otherwise. + */ + if (g_file_get_contents ("/.flatpak-info", &flatpak_info, NULL, NULL)) + { + temp = g_strdup_printf ("\n# Flatpak info #\n%s", + flatpak_info); + g_free (flatpak_info); + flatpak_info = temp; + } + temp = g_strconcat (version, verbose_info, flatpak_info, NULL); + g_free (version); + g_free (verbose_info); + g_free (flatpak_info); + + version = temp; + } + + return version; +} + +gint +gimp_version_get_revision (void) +{ + GKeyFile *key_file; + gchar *gimp_release; + gint revision = 0; + + key_file = g_key_file_new (); + + /* The gimp-release file is inspired by /etc/os-release and similar + * distribution files. Right now its main use is to number the package + * revision. This information is not a build variable because a new + * package version does not necessarily imply a rebuild (maybe just + * installed data or dependencies change). + */ + gimp_release = g_build_filename (gimp_data_directory (), "gimp-release", NULL); + /* Absence of the file is not an error. Actually most third-party + * builds probably won't install such file. + */ + if (g_key_file_load_from_file (key_file, gimp_release, G_KEY_FILE_NONE, NULL)) + { + if (g_key_file_has_key (key_file, "package", "revision", NULL)) + revision = g_key_file_get_integer (key_file, "package", "revision", NULL); + } + g_key_file_free (key_file); + g_free (gimp_release); + + return revision; +} + +gboolean +gimp_version_check_update (void) +{ + GKeyFile *key_file; + gchar *gimp_release; + gboolean check_update = FALSE; + + key_file = g_key_file_new (); + + gimp_release = g_build_filename (gimp_data_directory (), "gimp-release", NULL); + if (g_key_file_load_from_file (key_file, gimp_release, G_KEY_FILE_NONE, NULL)) + { + check_update = TRUE; + + if (g_key_file_has_key (key_file, "package", "check-update", NULL)) + check_update = g_key_file_get_boolean (key_file, "package", "check-update", NULL); + } + g_key_file_free (key_file); + g_free (gimp_release); + + return check_update; +} diff --git a/app/gimp-version.h b/app/gimp-version.h new file mode 100644 index 0000000..556a41a --- /dev/null +++ b/app/gimp-version.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __APP_GIMP_VERSION_H__ +#define __APP_GIMP_VERSION_H__ + + +void gimp_version_show (gboolean be_verbose); +gchar * gimp_version (gboolean be_verbose, + gboolean localized); + +gint gimp_version_get_revision (void); + +gboolean gimp_version_check_update (void); + + +#endif /* __APP_GIMP_VERSION_H__ */ diff --git a/app/gui/Makefile.am b/app/gui/Makefile.am new file mode 100644 index 0000000..579cd63 --- /dev/null +++ b/app/gui/Makefile.am @@ -0,0 +1,80 @@ +## Process this file with automake to produce Makefile.in + +if PLATFORM_OSX +xobjective_c = "-xobjective-c" +xobjective_cxx = "-xobjective-c++" +xnone = "-xnone" +endif + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-GUI\" \ + -DGIMP_COMMAND=\"@GIMP_COMMAND@\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GIO_UNIX_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GTK_MAC_INTEGRATION_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappgui.a + +libappgui_a_sources = \ + gimpdbusservice.c \ + gimpdbusservice.h \ + gimpuiconfigurer.c \ + gimpuiconfigurer.h \ + gui.c \ + gui.h \ + gui-message.c \ + gui-message.h \ + gui-unique.c \ + gui-unique.h \ + gui-vtable.c \ + gui-vtable.h \ + gui-types.h \ + icon-themes.c \ + icon-themes.h \ + session.c \ + session.h \ + splash.c \ + splash.h \ + themes.c \ + themes.h + +libappgui_a_built_sources = \ + gimpdbusservice-generated.c \ + gimpdbusservice-generated.h + +libappgui_a_SOURCES = $(libappgui_a_built_sources) $(libappgui_a_sources) + +BUILT_SOURCES = $(libappgui_a_built_sources) + +EXTRA_DIST = \ + dbus-service.xml + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = $(libappgui_a_built_sources) +CLEANFILES = $(gen_sources) + +$(srcdir)/gimpdbusservice.c: $(libappgui_a_built_sources) + +$(libappgui_a_built_sources): $(srcdir)/dbus-service.xml + $(GDBUS_CODEGEN) --interface-prefix org.gimp.GIMP. \ + --generate-c-code gimpdbusservice-generated \ + --c-namespace GimpDBusService \ + $(srcdir)/dbus-service.xml diff --git a/app/gui/Makefile.in b/app/gui/Makefile.in new file mode 100644 index 0000000..5bc3164 --- /dev/null +++ b/app/gui/Makefile.in @@ -0,0 +1,1018 @@ +# 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 = app/gui +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 = +libappgui_a_AR = $(AR) $(ARFLAGS) +libappgui_a_LIBADD = +am__objects_1 = gimpdbusservice-generated.$(OBJEXT) +am__objects_2 = gimpdbusservice.$(OBJEXT) gimpuiconfigurer.$(OBJEXT) \ + gui.$(OBJEXT) gui-message.$(OBJEXT) gui-unique.$(OBJEXT) \ + gui-vtable.$(OBJEXT) icon-themes.$(OBJEXT) session.$(OBJEXT) \ + splash.$(OBJEXT) themes.$(OBJEXT) +am_libappgui_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappgui_a_OBJECTS = $(am_libappgui_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)/gimpdbusservice-generated.Po \ + ./$(DEPDIR)/gimpdbusservice.Po ./$(DEPDIR)/gimpuiconfigurer.Po \ + ./$(DEPDIR)/gui-message.Po ./$(DEPDIR)/gui-unique.Po \ + ./$(DEPDIR)/gui-vtable.Po ./$(DEPDIR)/gui.Po \ + ./$(DEPDIR)/icon-themes.Po ./$(DEPDIR)/session.Po \ + ./$(DEPDIR)/splash.Po ./$(DEPDIR)/themes.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 = $(libappgui_a_SOURCES) +DIST_SOURCES = $(libappgui_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 +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@ +@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c" +@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++" +@PLATFORM_OSX_TRUE@xnone = "-xnone" +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-GUI\" \ + -DGIMP_COMMAND=\"@GIMP_COMMAND@\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GIO_UNIX_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GTK_MAC_INTEGRATION_CFLAGS) \ + -I$(includedir) + +AM_CFLAGS = \ + $(xobjective_c) + +AM_CXXFLAGS = \ + $(xobjective_cxx) + +AM_LDFLAGS = \ + $(xnone) + +noinst_LIBRARIES = libappgui.a +libappgui_a_sources = \ + gimpdbusservice.c \ + gimpdbusservice.h \ + gimpuiconfigurer.c \ + gimpuiconfigurer.h \ + gui.c \ + gui.h \ + gui-message.c \ + gui-message.h \ + gui-unique.c \ + gui-unique.h \ + gui-vtable.c \ + gui-vtable.h \ + gui-types.h \ + icon-themes.c \ + icon-themes.h \ + session.c \ + session.h \ + splash.c \ + splash.h \ + themes.c \ + themes.h + +libappgui_a_built_sources = \ + gimpdbusservice-generated.c \ + gimpdbusservice-generated.h + +libappgui_a_SOURCES = $(libappgui_a_built_sources) $(libappgui_a_sources) +BUILT_SOURCES = $(libappgui_a_built_sources) +EXTRA_DIST = \ + dbus-service.xml + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = $(libappgui_a_built_sources) +CLEANFILES = $(gen_sources) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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 app/gui/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/gui/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) + +libappgui.a: $(libappgui_a_OBJECTS) $(libappgui_a_DEPENDENCIES) $(EXTRA_libappgui_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappgui.a + $(AM_V_AR)$(libappgui_a_AR) libappgui.a $(libappgui_a_OBJECTS) $(libappgui_a_LIBADD) + $(AM_V_at)$(RANLIB) libappgui.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdbusservice-generated.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdbusservice.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpuiconfigurer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-message.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-unique.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-vtable.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icon-themes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splash.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/themes.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 $(LIBRARIES) +installdirs: +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-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gimpdbusservice-generated.Po + -rm -f ./$(DEPDIR)/gimpdbusservice.Po + -rm -f ./$(DEPDIR)/gimpuiconfigurer.Po + -rm -f ./$(DEPDIR)/gui-message.Po + -rm -f ./$(DEPDIR)/gui-unique.Po + -rm -f ./$(DEPDIR)/gui-vtable.Po + -rm -f ./$(DEPDIR)/gui.Po + -rm -f ./$(DEPDIR)/icon-themes.Po + -rm -f ./$(DEPDIR)/session.Po + -rm -f ./$(DEPDIR)/splash.Po + -rm -f ./$(DEPDIR)/themes.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)/gimpdbusservice-generated.Po + -rm -f ./$(DEPDIR)/gimpdbusservice.Po + -rm -f ./$(DEPDIR)/gimpuiconfigurer.Po + -rm -f ./$(DEPDIR)/gui-message.Po + -rm -f ./$(DEPDIR)/gui-unique.Po + -rm -f ./$(DEPDIR)/gui-vtable.Po + -rm -f ./$(DEPDIR)/gui.Po + -rm -f ./$(DEPDIR)/icon-themes.Po + -rm -f ./$(DEPDIR)/session.Po + -rm -f ./$(DEPDIR)/splash.Po + -rm -f ./$(DEPDIR)/themes.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: 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-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 + + +$(srcdir)/gimpdbusservice.c: $(libappgui_a_built_sources) + +$(libappgui_a_built_sources): $(srcdir)/dbus-service.xml + $(GDBUS_CODEGEN) --interface-prefix org.gimp.GIMP. \ + --generate-c-code gimpdbusservice-generated \ + --c-namespace GimpDBusService \ + $(srcdir)/dbus-service.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/app/gui/dbus-service.xml b/app/gui/dbus-service.xml new file mode 100644 index 0000000..5badd9b --- /dev/null +++ b/app/gui/dbus-service.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/gui/gimpdbusservice-generated.c b/app/gui/gimpdbusservice-generated.c new file mode 100644 index 0000000..9ede0fd --- /dev/null +++ b/app/gui/gimpdbusservice-generated.c @@ -0,0 +1,1721 @@ +/* + * This file is generated by gdbus-codegen, do not modify it. + * + * The license of this code is the same as for the D-Bus interface description + * it was derived from. Note that it links to GLib, so must comply with the + * LGPL linking clauses. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gimpdbusservice-generated.h" + +#include +#ifdef G_OS_UNIX +# include +#endif + +typedef struct +{ + GDBusArgInfo parent_struct; + gboolean use_gvariant; +} _ExtendedGDBusArgInfo; + +typedef struct +{ + GDBusMethodInfo parent_struct; + const gchar *signal_name; + gboolean pass_fdlist; +} _ExtendedGDBusMethodInfo; + +typedef struct +{ + GDBusSignalInfo parent_struct; + const gchar *signal_name; +} _ExtendedGDBusSignalInfo; + +typedef struct +{ + GDBusPropertyInfo parent_struct; + const gchar *hyphen_name; + guint use_gvariant : 1; + guint emits_changed_signal : 1; +} _ExtendedGDBusPropertyInfo; + +typedef struct +{ + GDBusInterfaceInfo parent_struct; + const gchar *hyphen_name; +} _ExtendedGDBusInterfaceInfo; + +typedef struct +{ + const _ExtendedGDBusPropertyInfo *info; + guint prop_id; + GValue orig_value; /* the value before the change */ +} ChangedProperty; + +static void +_changed_property_free (ChangedProperty *data) +{ + g_value_unset (&data->orig_value); + g_free (data); +} + +static gboolean +_g_strv_equal0 (gchar **a, gchar **b) +{ + gboolean ret = FALSE; + guint n; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + if (g_strv_length (a) != g_strv_length (b)) + goto out; + for (n = 0; a[n] != NULL; n++) + if (g_strcmp0 (a[n], b[n]) != 0) + goto out; + ret = TRUE; +out: + return ret; +} + +static gboolean +_g_variant_equal0 (GVariant *a, GVariant *b) +{ + gboolean ret = FALSE; + if (a == NULL && b == NULL) + { + ret = TRUE; + goto out; + } + if (a == NULL || b == NULL) + goto out; + ret = g_variant_equal (a, b); +out: + return ret; +} + +G_GNUC_UNUSED static gboolean +_g_value_equal (const GValue *a, const GValue *b) +{ + gboolean ret = FALSE; + g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b)); + switch (G_VALUE_TYPE (a)) + { + case G_TYPE_BOOLEAN: + ret = (g_value_get_boolean (a) == g_value_get_boolean (b)); + break; + case G_TYPE_UCHAR: + ret = (g_value_get_uchar (a) == g_value_get_uchar (b)); + break; + case G_TYPE_INT: + ret = (g_value_get_int (a) == g_value_get_int (b)); + break; + case G_TYPE_UINT: + ret = (g_value_get_uint (a) == g_value_get_uint (b)); + break; + case G_TYPE_INT64: + ret = (g_value_get_int64 (a) == g_value_get_int64 (b)); + break; + case G_TYPE_UINT64: + ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b)); + break; + case G_TYPE_DOUBLE: + { + /* Avoid -Wfloat-equal warnings by doing a direct bit compare */ + gdouble da = g_value_get_double (a); + gdouble db = g_value_get_double (b); + ret = memcmp (&da, &db, sizeof (gdouble)) == 0; + } + break; + case G_TYPE_STRING: + ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0); + break; + case G_TYPE_VARIANT: + ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b)); + break; + default: + if (G_VALUE_TYPE (a) == G_TYPE_STRV) + ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b)); + else + g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a))); + break; + } + return ret; +} + +/* ------------------------------------------------------------------------ + * Code for interface org.gimp.GIMP.UI + * ------------------------------------------------------------------------ + */ + +/** + * SECTION:GimpDBusServiceUI + * @title: GimpDBusServiceUI + * @short_description: Generated C code for the org.gimp.GIMP.UI D-Bus interface + * + * This section contains code for working with the org.gimp.GIMP.UI D-Bus interface in C. + */ + +/* ---- Introspection data for org.gimp.GIMP.UI ---- */ + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_IN_ARG_uri = +{ + { + -1, + (gchar *) "uri", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_IN_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_open_IN_ARG_uri.parent_struct, + NULL +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_OUT_ARG_success = +{ + { + -1, + (gchar *) "success", + (gchar *) "b", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_OUT_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_open_OUT_ARG_success.parent_struct, + NULL +}; + +static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_open = +{ + { + -1, + (gchar *) "Open", + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_IN_ARG_pointers, + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_OUT_ARG_pointers, + NULL + }, + "handle-open", + FALSE +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_uri = +{ + { + -1, + (gchar *) "uri", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_uri.parent_struct, + NULL +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_success = +{ + { + -1, + (gchar *) "success", + (gchar *) "b", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_success.parent_struct, + NULL +}; + +static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_open_as_new = +{ + { + -1, + (gchar *) "OpenAsNew", + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_pointers, + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_pointers, + NULL + }, + "handle-open-as-new", + FALSE +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_interpreter = +{ + { + -1, + (gchar *) "interpreter", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_command = +{ + { + -1, + (gchar *) "command", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_interpreter.parent_struct, + &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_command.parent_struct, + NULL +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_success = +{ + { + -1, + (gchar *) "success", + (gchar *) "b", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_success.parent_struct, + NULL +}; + +static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_batch_run = +{ + { + -1, + (gchar *) "BatchRun", + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_pointers, + (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_pointers, + NULL + }, + "handle-batch-run", + FALSE +}; + +static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_activate = +{ + { + -1, + (gchar *) "Activate", + NULL, + NULL, + NULL + }, + "handle-activate", + FALSE +}; + +static const GDBusMethodInfo * const _gimp_dbus_service_ui_method_info_pointers[] = +{ + &_gimp_dbus_service_ui_method_info_open.parent_struct, + &_gimp_dbus_service_ui_method_info_open_as_new.parent_struct, + &_gimp_dbus_service_ui_method_info_batch_run.parent_struct, + &_gimp_dbus_service_ui_method_info_activate.parent_struct, + NULL +}; + +static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_signal_info_opened_ARG_uri = +{ + { + -1, + (gchar *) "uri", + (gchar *) "s", + NULL + }, + FALSE +}; + +static const GDBusArgInfo * const _gimp_dbus_service_ui_signal_info_opened_ARG_pointers[] = +{ + &_gimp_dbus_service_ui_signal_info_opened_ARG_uri.parent_struct, + NULL +}; + +static const _ExtendedGDBusSignalInfo _gimp_dbus_service_ui_signal_info_opened = +{ + { + -1, + (gchar *) "Opened", + (GDBusArgInfo **) &_gimp_dbus_service_ui_signal_info_opened_ARG_pointers, + NULL + }, + "opened" +}; + +static const GDBusSignalInfo * const _gimp_dbus_service_ui_signal_info_pointers[] = +{ + &_gimp_dbus_service_ui_signal_info_opened.parent_struct, + NULL +}; + +static const _ExtendedGDBusInterfaceInfo _gimp_dbus_service_ui_interface_info = +{ + { + -1, + (gchar *) "org.gimp.GIMP.UI", + (GDBusMethodInfo **) &_gimp_dbus_service_ui_method_info_pointers, + (GDBusSignalInfo **) &_gimp_dbus_service_ui_signal_info_pointers, + NULL, + NULL + }, + "ui", +}; + + +/** + * gimp_dbus_service_ui_interface_info: + * + * Gets a machine-readable description of the org.gimp.GIMP.UI D-Bus interface. + * + * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free. + */ +GDBusInterfaceInfo * +gimp_dbus_service_ui_interface_info (void) +{ + return (GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct; +} + +/** + * gimp_dbus_service_ui_override_properties: + * @klass: The class structure for a #GObject derived class. + * @property_id_begin: The property id to assign to the first overridden property. + * + * Overrides all #GObject properties in the #GimpDBusServiceUI interface for a concrete class. + * The properties are overridden in the order they are defined. + * + * Returns: The last property id. + */ +guint +gimp_dbus_service_ui_override_properties (GObjectClass *klass, guint property_id_begin) +{ + return property_id_begin - 1; +} + + + +/** + * GimpDBusServiceUI: + * + * Abstract interface type for the D-Bus interface org.gimp.GIMP.UI. + */ + +/** + * GimpDBusServiceUIIface: + * @parent_iface: The parent interface. + * @handle_activate: Handler for the #GimpDBusServiceUI::handle-activate signal. + * @handle_batch_run: Handler for the #GimpDBusServiceUI::handle-batch-run signal. + * @handle_open: Handler for the #GimpDBusServiceUI::handle-open signal. + * @handle_open_as_new: Handler for the #GimpDBusServiceUI::handle-open-as-new signal. + * @opened: Handler for the #GimpDBusServiceUI::opened signal. + * + * Virtual table for the D-Bus interface org.gimp.GIMP.UI. + */ + +typedef GimpDBusServiceUIIface GimpDBusServiceUIInterface; +G_DEFINE_INTERFACE (GimpDBusServiceUI, gimp_dbus_service_ui, G_TYPE_OBJECT) + +static void +gimp_dbus_service_ui_default_init (GimpDBusServiceUIIface *iface) +{ + /* GObject signals for incoming D-Bus method calls: */ + /** + * GimpDBusServiceUI::handle-open: + * @object: A #GimpDBusServiceUI. + * @invocation: A #GDBusMethodInvocation. + * @arg_uri: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the Open() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_open() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-open", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_open), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /** + * GimpDBusServiceUI::handle-open-as-new: + * @object: A #GimpDBusServiceUI. + * @invocation: A #GDBusMethodInvocation. + * @arg_uri: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the OpenAsNew() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_open_as_new() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-open-as-new", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_open_as_new), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 2, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING); + + /** + * GimpDBusServiceUI::handle-batch-run: + * @object: A #GimpDBusServiceUI. + * @invocation: A #GDBusMethodInvocation. + * @arg_interpreter: Argument passed by remote caller. + * @arg_command: Argument passed by remote caller. + * + * Signal emitted when a remote caller is invoking the BatchRun() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_batch_run() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-batch-run", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_batch_run), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 3, + G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING); + + /** + * GimpDBusServiceUI::handle-activate: + * @object: A #GimpDBusServiceUI. + * @invocation: A #GDBusMethodInvocation. + * + * Signal emitted when a remote caller is invoking the Activate() D-Bus method. + * + * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_activate() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned. + * + * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run. + */ + g_signal_new ("handle-activate", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_activate), + g_signal_accumulator_true_handled, + NULL, + g_cclosure_marshal_generic, + G_TYPE_BOOLEAN, + 1, + G_TYPE_DBUS_METHOD_INVOCATION); + + /* GObject signals for received D-Bus signals: */ + /** + * GimpDBusServiceUI::opened: + * @object: A #GimpDBusServiceUI. + * @arg_uri: Argument. + * + * On the client-side, this signal is emitted whenever the D-Bus signal "Opened" is received. + * + * On the service-side, this signal can be used with e.g. g_signal_emit_by_name() to make the object emit the D-Bus signal. + */ + g_signal_new ("opened", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpDBusServiceUIIface, opened), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, G_TYPE_STRING); + +} + +/** + * gimp_dbus_service_ui_emit_opened: + * @object: A #GimpDBusServiceUI. + * @arg_uri: Argument to pass with the signal. + * + * Emits the "Opened" D-Bus signal. + */ +void +gimp_dbus_service_ui_emit_opened ( + GimpDBusServiceUI *object, + const gchar *arg_uri) +{ + g_signal_emit_by_name (object, "opened", arg_uri); +} + +/** + * gimp_dbus_service_ui_call_open: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_uri: Argument to pass with the method invocation. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Open() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_call_open_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_call_open_sync() for the synchronous, blocking version of this method. + */ +void +gimp_dbus_service_ui_call_open ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Open", + g_variant_new ("(s)", + arg_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * gimp_dbus_service_ui_call_open_finish: + * @proxy: A #GimpDBusServiceUIProxy. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_open(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with gimp_dbus_service_ui_call_open(). + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_open_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_open_sync: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_uri: Argument to pass with the method invocation. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Open() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_call_open() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_open_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + gboolean *out_success, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Open", + g_variant_new ("(s)", + arg_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_open_as_new: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_uri: Argument to pass with the method invocation. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the OpenAsNew() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_call_open_as_new_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_call_open_as_new_sync() for the synchronous, blocking version of this method. + */ +void +gimp_dbus_service_ui_call_open_as_new ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "OpenAsNew", + g_variant_new ("(s)", + arg_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * gimp_dbus_service_ui_call_open_as_new_finish: + * @proxy: A #GimpDBusServiceUIProxy. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_open_as_new(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with gimp_dbus_service_ui_call_open_as_new(). + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_open_as_new_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_open_as_new_sync: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_uri: Argument to pass with the method invocation. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the OpenAsNew() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_call_open_as_new() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_open_as_new_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + gboolean *out_success, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "OpenAsNew", + g_variant_new ("(s)", + arg_uri), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_batch_run: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_interpreter: Argument to pass with the method invocation. + * @arg_command: Argument to pass with the method invocation. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the BatchRun() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_call_batch_run_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_call_batch_run_sync() for the synchronous, blocking version of this method. + */ +void +gimp_dbus_service_ui_call_batch_run ( + GimpDBusServiceUI *proxy, + const gchar *arg_interpreter, + const gchar *arg_command, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "BatchRun", + g_variant_new ("(ss)", + arg_interpreter, + arg_command), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * gimp_dbus_service_ui_call_batch_run_finish: + * @proxy: A #GimpDBusServiceUIProxy. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_batch_run(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with gimp_dbus_service_ui_call_batch_run(). + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_batch_run_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_batch_run_sync: + * @proxy: A #GimpDBusServiceUIProxy. + * @arg_interpreter: Argument to pass with the method invocation. + * @arg_command: Argument to pass with the method invocation. + * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the BatchRun() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_call_batch_run() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_batch_run_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_interpreter, + const gchar *arg_command, + gboolean *out_success, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "BatchRun", + g_variant_new ("(ss)", + arg_interpreter, + arg_command), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "(b)", + out_success); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_activate: + * @proxy: A #GimpDBusServiceUIProxy. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL. + * @user_data: User data to pass to @callback. + * + * Asynchronously invokes the Activate() D-Bus method on @proxy. + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_call_activate_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_call_activate_sync() for the synchronous, blocking version of this method. + */ +void +gimp_dbus_service_ui_call_activate ( + GimpDBusServiceUI *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_proxy_call (G_DBUS_PROXY (proxy), + "Activate", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + callback, + user_data); +} + +/** + * gimp_dbus_service_ui_call_activate_finish: + * @proxy: A #GimpDBusServiceUIProxy. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_activate(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with gimp_dbus_service_ui_call_activate(). + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_activate_finish ( + GimpDBusServiceUI *proxy, + GAsyncResult *res, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_call_activate_sync: + * @proxy: A #GimpDBusServiceUIProxy. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously invokes the Activate() D-Bus method on @proxy. The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_call_activate() for the asynchronous version of this method. + * + * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set. + */ +gboolean +gimp_dbus_service_ui_call_activate_sync ( + GimpDBusServiceUI *proxy, + GCancellable *cancellable, + GError **error) +{ + GVariant *_ret; + _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), + "Activate", + g_variant_new ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + error); + if (_ret == NULL) + goto _out; + g_variant_get (_ret, + "()"); + g_variant_unref (_ret); +_out: + return _ret != NULL; +} + +/** + * gimp_dbus_service_ui_complete_open: + * @object: A #GimpDBusServiceUI. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @success: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the Open() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +gimp_dbus_service_ui_complete_open ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(b)", + success)); +} + +/** + * gimp_dbus_service_ui_complete_open_as_new: + * @object: A #GimpDBusServiceUI. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @success: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the OpenAsNew() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +gimp_dbus_service_ui_complete_open_as_new ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(b)", + success)); +} + +/** + * gimp_dbus_service_ui_complete_batch_run: + * @object: A #GimpDBusServiceUI. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * @success: Parameter to return. + * + * Helper function used in service implementations to finish handling invocations of the BatchRun() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +gimp_dbus_service_ui_complete_batch_run ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(b)", + success)); +} + +/** + * gimp_dbus_service_ui_complete_activate: + * @object: A #GimpDBusServiceUI. + * @invocation: (transfer full): A #GDBusMethodInvocation. + * + * Helper function used in service implementations to finish handling invocations of the Activate() D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar. + * + * This method will free @invocation, you cannot use it afterwards. + */ +void +gimp_dbus_service_ui_complete_activate ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("()")); +} + +/* ------------------------------------------------------------------------ */ + +/** + * GimpDBusServiceUIProxy: + * + * The #GimpDBusServiceUIProxy structure contains only private data and should only be accessed using the provided API. + */ + +/** + * GimpDBusServiceUIProxyClass: + * @parent_class: The parent class. + * + * Class structure for #GimpDBusServiceUIProxy. + */ + +struct _GimpDBusServiceUIProxyPrivate +{ + GData *qdata; +}; + +static void gimp_dbus_service_ui_proxy_iface_init (GimpDBusServiceUIIface *iface); + +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUIProxy, gimp_dbus_service_ui_proxy, G_TYPE_DBUS_PROXY, + G_ADD_PRIVATE (GimpDBusServiceUIProxy) + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_proxy_iface_init)) + +#else +G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUIProxy, gimp_dbus_service_ui_proxy, G_TYPE_DBUS_PROXY, + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_proxy_iface_init)) + +#endif +static void +gimp_dbus_service_ui_proxy_finalize (GObject *object) +{ + GimpDBusServiceUIProxy *proxy = GIMP_DBUS_SERVICE_UI_PROXY (object); + g_datalist_clear (&proxy->priv->qdata); + G_OBJECT_CLASS (gimp_dbus_service_ui_proxy_parent_class)->finalize (object); +} + +static void +gimp_dbus_service_ui_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +gimp_dbus_service_ui_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec G_GNUC_UNUSED) +{ +} + +static void +gimp_dbus_service_ui_proxy_g_signal (GDBusProxy *proxy, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *signal_name, + GVariant *parameters) +{ + _ExtendedGDBusSignalInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + gsize num_params; + gsize n; + guint signal_id; + info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, signal_name); + if (info == NULL) + return; + num_params = g_variant_n_children (parameters); + paramv = g_new0 (GValue, num_params + 1); + g_value_init (¶mv[0], GIMP_DBUS_SERVICE_TYPE_UI); + g_value_set_object (¶mv[0], proxy); + g_variant_iter_init (&iter, parameters); + n = 1; + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, GIMP_DBUS_SERVICE_TYPE_UI); + g_signal_emitv (paramv, signal_id, 0, NULL); + for (n = 0; n < num_params + 1; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static void +gimp_dbus_service_ui_proxy_g_properties_changed (GDBusProxy *_proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties) +{ + GimpDBusServiceUIProxy *proxy = GIMP_DBUS_SERVICE_UI_PROXY (_proxy); + guint n; + const gchar *key; + GVariantIter *iter; + _ExtendedGDBusPropertyInfo *info; + g_variant_get (changed_properties, "a{sv}", &iter); + while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, key); + g_datalist_remove_data (&proxy->priv->qdata, key); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } + g_variant_iter_free (iter); + for (n = 0; invalidated_properties[n] != NULL; n++) + { + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, invalidated_properties[n]); + g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]); + if (info != NULL) + g_object_notify (G_OBJECT (proxy), info->hyphen_name); + } +} + +static void +gimp_dbus_service_ui_proxy_init (GimpDBusServiceUIProxy *proxy) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + proxy->priv = gimp_dbus_service_ui_proxy_get_instance_private (proxy); +#else + proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyPrivate); +#endif + + g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), gimp_dbus_service_ui_interface_info ()); +} + +static void +gimp_dbus_service_ui_proxy_class_init (GimpDBusServiceUIProxyClass *klass) +{ + GObjectClass *gobject_class; + GDBusProxyClass *proxy_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = gimp_dbus_service_ui_proxy_finalize; + gobject_class->get_property = gimp_dbus_service_ui_proxy_get_property; + gobject_class->set_property = gimp_dbus_service_ui_proxy_set_property; + + proxy_class = G_DBUS_PROXY_CLASS (klass); + proxy_class->g_signal = gimp_dbus_service_ui_proxy_g_signal; + proxy_class->g_properties_changed = gimp_dbus_service_ui_proxy_g_properties_changed; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (GimpDBusServiceUIProxyPrivate)); +#endif +} + +static void +gimp_dbus_service_ui_proxy_iface_init (GimpDBusServiceUIIface *iface) +{ +} + +/** + * gimp_dbus_service_ui_proxy_new: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (nullable): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Asynchronously creates a proxy for the D-Bus interface org.gimp.GIMP.UI. See g_dbus_proxy_new() for more details. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_proxy_new_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_proxy_new_sync() for the synchronous, blocking version of this constructor. + */ +void +gimp_dbus_service_ui_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL); +} + +/** + * gimp_dbus_service_ui_proxy_new_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_proxy_new(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with gimp_dbus_service_ui_proxy_new(). + * + * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set. + */ +GimpDBusServiceUI * +gimp_dbus_service_ui_proxy_new_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return GIMP_DBUS_SERVICE_UI (ret); + else + return NULL; +} + +/** + * gimp_dbus_service_ui_proxy_new_sync: + * @connection: A #GDBusConnection. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: (nullable): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. + * @object_path: An object path. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Synchronously creates a proxy for the D-Bus interface org.gimp.GIMP.UI. See g_dbus_proxy_new_sync() for more details. + * + * The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_proxy_new() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set. + */ +GimpDBusServiceUI * +gimp_dbus_service_ui_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL); + if (ret != NULL) + return GIMP_DBUS_SERVICE_UI (ret); + else + return NULL; +} + + +/** + * gimp_dbus_service_ui_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @callback: A #GAsyncReadyCallback to call when the request is satisfied. + * @user_data: User data to pass to @callback. + * + * Like gimp_dbus_service_ui_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()). + * You can then call gimp_dbus_service_ui_proxy_new_for_bus_finish() to get the result of the operation. + * + * See gimp_dbus_service_ui_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor. + */ +void +gimp_dbus_service_ui_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL); +} + +/** + * gimp_dbus_service_ui_proxy_new_for_bus_finish: + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_proxy_new_for_bus(). + * @error: Return location for error or %NULL + * + * Finishes an operation started with gimp_dbus_service_ui_proxy_new_for_bus(). + * + * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set. + */ +GimpDBusServiceUI * +gimp_dbus_service_ui_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error) +{ + GObject *ret; + GObject *source_object; + source_object = g_async_result_get_source_object (res); + ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error); + g_object_unref (source_object); + if (ret != NULL) + return GIMP_DBUS_SERVICE_UI (ret); + else + return NULL; +} + +/** + * gimp_dbus_service_ui_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags from the #GDBusProxyFlags enumeration. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @cancellable: (nullable): A #GCancellable or %NULL. + * @error: Return location for error or %NULL + * + * Like gimp_dbus_service_ui_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * The calling thread is blocked until a reply is received. + * + * See gimp_dbus_service_ui_proxy_new_for_bus() for the asynchronous version of this constructor. + * + * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set. + */ +GimpDBusServiceUI * +gimp_dbus_service_ui_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error) +{ + GInitable *ret; + ret = g_initable_new (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL); + if (ret != NULL) + return GIMP_DBUS_SERVICE_UI (ret); + else + return NULL; +} + + +/* ------------------------------------------------------------------------ */ + +/** + * GimpDBusServiceUISkeleton: + * + * The #GimpDBusServiceUISkeleton structure contains only private data and should only be accessed using the provided API. + */ + +/** + * GimpDBusServiceUISkeletonClass: + * @parent_class: The parent class. + * + * Class structure for #GimpDBusServiceUISkeleton. + */ + +struct _GimpDBusServiceUISkeletonPrivate +{ + GValue *properties; + GList *changed_properties; + GSource *changed_properties_idle_source; + GMainContext *context; + GMutex lock; +}; + +static void +_gimp_dbus_service_ui_skeleton_handle_method_call ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data); + _ExtendedGDBusMethodInfo *info; + GVariantIter iter; + GVariant *child; + GValue *paramv; + gsize num_params; + guint num_extra; + gsize n; + guint signal_id; + GValue return_value = G_VALUE_INIT; + info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation); + g_assert (info != NULL); + num_params = g_variant_n_children (parameters); + num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra); + n = 0; + g_value_init (¶mv[n], GIMP_DBUS_SERVICE_TYPE_UI); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation))); +#else + g_assert_not_reached (); +#endif + } + g_variant_iter_init (&iter, parameters); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra]; + if (arg_info->use_gvariant) + { + g_value_init (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[n++]); + g_variant_unref (child); + } + signal_id = g_signal_lookup (info->signal_name, GIMP_DBUS_SERVICE_TYPE_UI); + g_value_init (&return_value, G_TYPE_BOOLEAN); + g_signal_emitv (paramv, signal_id, 0, &return_value); + if (!g_value_get_boolean (&return_value)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name); + g_value_unset (&return_value); + for (n = 0; n < num_params + num_extra; n++) + g_value_unset (¶mv[n]); + g_free (paramv); +} + +static GVariant * +_gimp_dbus_service_ui_skeleton_handle_get_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + GVariant *ret; + ret = NULL; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + g_value_init (&value, pspec->value_type); + g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value); + ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature)); + g_value_unset (&value); + } + return ret; +} + +static gboolean +_gimp_dbus_service_ui_skeleton_handle_set_property ( + GDBusConnection *connection G_GNUC_UNUSED, + const gchar *sender G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *property_name, + GVariant *variant, + GError **error, + gpointer user_data) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data); + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + _ExtendedGDBusPropertyInfo *info; + gboolean ret; + ret = FALSE; + info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, property_name); + g_assert (info != NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name); + if (pspec == NULL) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name); + } + else + { + if (info->use_gvariant) + g_value_set_variant (&value, variant); + else + g_dbus_gvariant_to_gvalue (variant, &value); + g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value); + g_value_unset (&value); + ret = TRUE; + } + return ret; +} + +static const GDBusInterfaceVTable _gimp_dbus_service_ui_skeleton_vtable = +{ + _gimp_dbus_service_ui_skeleton_handle_method_call, + _gimp_dbus_service_ui_skeleton_handle_get_property, + _gimp_dbus_service_ui_skeleton_handle_set_property, + {NULL} +}; + +static GDBusInterfaceInfo * +gimp_dbus_service_ui_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return gimp_dbus_service_ui_interface_info (); +} + +static GDBusInterfaceVTable * +gimp_dbus_service_ui_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED) +{ + return (GDBusInterfaceVTable *) &_gimp_dbus_service_ui_skeleton_vtable; +} + +static GVariant * +gimp_dbus_service_ui_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (_skeleton); + + GVariantBuilder builder; + guint n; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (_gimp_dbus_service_ui_interface_info.parent_struct.properties == NULL) + goto out; + for (n = 0; _gimp_dbus_service_ui_interface_info.parent_struct.properties[n] != NULL; n++) + { + GDBusPropertyInfo *info = _gimp_dbus_service_ui_interface_info.parent_struct.properties[n]; + if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + { + GVariant *value; + value = _gimp_dbus_service_ui_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.gimp.GIMP.UI", info->name, NULL, skeleton); + if (value != NULL) + { + g_variant_take_ref (value); + g_variant_builder_add (&builder, "{sv}", info->name, value); + g_variant_unref (value); + } + } + } +out: + return g_variant_builder_end (&builder); +} + +static void +gimp_dbus_service_ui_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton) +{ +} + +static void +_gimp_dbus_service_ui_on_signal_opened ( + GimpDBusServiceUI *object, + const gchar *arg_uri) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (object); + + GList *connections, *l; + GVariant *signal_variant; + connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton)); + + signal_variant = g_variant_ref_sink (g_variant_new ("(s)", + arg_uri)); + for (l = connections; l != NULL; l = l->next) + { + GDBusConnection *connection = l->data; + g_dbus_connection_emit_signal (connection, + NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.gimp.GIMP.UI", "Opened", + signal_variant, NULL); + } + g_variant_unref (signal_variant); + g_list_free_full (connections, g_object_unref); +} + +static void gimp_dbus_service_ui_skeleton_iface_init (GimpDBusServiceUIIface *iface); +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 +G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUISkeleton, gimp_dbus_service_ui_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_ADD_PRIVATE (GimpDBusServiceUISkeleton) + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_skeleton_iface_init)) + +#else +G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUISkeleton, gimp_dbus_service_ui_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON, + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_skeleton_iface_init)) + +#endif +static void +gimp_dbus_service_ui_skeleton_finalize (GObject *object) +{ + GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (object); + g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free); + if (skeleton->priv->changed_properties_idle_source != NULL) + g_source_destroy (skeleton->priv->changed_properties_idle_source); + g_main_context_unref (skeleton->priv->context); + g_mutex_clear (&skeleton->priv->lock); + G_OBJECT_CLASS (gimp_dbus_service_ui_skeleton_parent_class)->finalize (object); +} + +static void +gimp_dbus_service_ui_skeleton_init (GimpDBusServiceUISkeleton *skeleton) +{ +#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 + skeleton->priv = gimp_dbus_service_ui_skeleton_get_instance_private (skeleton); +#else + skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonPrivate); +#endif + + g_mutex_init (&skeleton->priv->lock); + skeleton->priv->context = g_main_context_ref_thread_default (); +} + +static void +gimp_dbus_service_ui_skeleton_class_init (GimpDBusServiceUISkeletonClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = gimp_dbus_service_ui_skeleton_finalize; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + skeleton_class->get_info = gimp_dbus_service_ui_skeleton_dbus_interface_get_info; + skeleton_class->get_properties = gimp_dbus_service_ui_skeleton_dbus_interface_get_properties; + skeleton_class->flush = gimp_dbus_service_ui_skeleton_dbus_interface_flush; + skeleton_class->get_vtable = gimp_dbus_service_ui_skeleton_dbus_interface_get_vtable; + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 + g_type_class_add_private (klass, sizeof (GimpDBusServiceUISkeletonPrivate)); +#endif +} + +static void +gimp_dbus_service_ui_skeleton_iface_init (GimpDBusServiceUIIface *iface) +{ + iface->opened = _gimp_dbus_service_ui_on_signal_opened; +} + +/** + * gimp_dbus_service_ui_skeleton_new: + * + * Creates a skeleton object for the D-Bus interface org.gimp.GIMP.UI. + * + * Returns: (transfer full) (type GimpDBusServiceUISkeleton): The skeleton object. + */ +GimpDBusServiceUI * +gimp_dbus_service_ui_skeleton_new (void) +{ + return GIMP_DBUS_SERVICE_UI (g_object_new (GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, NULL)); +} + diff --git a/app/gui/gimpdbusservice-generated.h b/app/gui/gimpdbusservice-generated.h new file mode 100644 index 0000000..5c8c465 --- /dev/null +++ b/app/gui/gimpdbusservice-generated.h @@ -0,0 +1,282 @@ +/* + * This file is generated by gdbus-codegen, do not modify it. + * + * The license of this code is the same as for the D-Bus interface description + * it was derived from. Note that it links to GLib, so must comply with the + * LGPL linking clauses. + */ + +#ifndef __GIMPDBUSSERVICE_GENERATED_H__ +#define __GIMPDBUSSERVICE_GENERATED_H__ + +#include + +G_BEGIN_DECLS + + +/* ------------------------------------------------------------------------ */ +/* Declarations for org.gimp.GIMP.UI */ + +#define GIMP_DBUS_SERVICE_TYPE_UI (gimp_dbus_service_ui_get_type ()) +#define GIMP_DBUS_SERVICE_UI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI, GimpDBusServiceUI)) +#define GIMP_DBUS_SERVICE_IS_UI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI)) +#define GIMP_DBUS_SERVICE_UI_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GIMP_DBUS_SERVICE_TYPE_UI, GimpDBusServiceUIIface)) + +struct _GimpDBusServiceUI; +typedef struct _GimpDBusServiceUI GimpDBusServiceUI; +typedef struct _GimpDBusServiceUIIface GimpDBusServiceUIIface; + +struct _GimpDBusServiceUIIface +{ + GTypeInterface parent_iface; + + + gboolean (*handle_activate) ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation); + + gboolean (*handle_batch_run) ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + const gchar *arg_interpreter, + const gchar *arg_command); + + gboolean (*handle_open) ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + const gchar *arg_uri); + + gboolean (*handle_open_as_new) ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + const gchar *arg_uri); + + void (*opened) ( + GimpDBusServiceUI *object, + const gchar *arg_uri); + +}; + +GType gimp_dbus_service_ui_get_type (void) G_GNUC_CONST; + +GDBusInterfaceInfo *gimp_dbus_service_ui_interface_info (void); +guint gimp_dbus_service_ui_override_properties (GObjectClass *klass, guint property_id_begin); + + +/* D-Bus method call completion functions: */ +void gimp_dbus_service_ui_complete_open ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success); + +void gimp_dbus_service_ui_complete_open_as_new ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success); + +void gimp_dbus_service_ui_complete_batch_run ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation, + gboolean success); + +void gimp_dbus_service_ui_complete_activate ( + GimpDBusServiceUI *object, + GDBusMethodInvocation *invocation); + + + +/* D-Bus signal emissions functions: */ +void gimp_dbus_service_ui_emit_opened ( + GimpDBusServiceUI *object, + const gchar *arg_uri); + + + +/* D-Bus method calls: */ +void gimp_dbus_service_ui_call_open ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gimp_dbus_service_ui_call_open_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error); + +gboolean gimp_dbus_service_ui_call_open_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + gboolean *out_success, + GCancellable *cancellable, + GError **error); + +void gimp_dbus_service_ui_call_open_as_new ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gimp_dbus_service_ui_call_open_as_new_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error); + +gboolean gimp_dbus_service_ui_call_open_as_new_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_uri, + gboolean *out_success, + GCancellable *cancellable, + GError **error); + +void gimp_dbus_service_ui_call_batch_run ( + GimpDBusServiceUI *proxy, + const gchar *arg_interpreter, + const gchar *arg_command, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gimp_dbus_service_ui_call_batch_run_finish ( + GimpDBusServiceUI *proxy, + gboolean *out_success, + GAsyncResult *res, + GError **error); + +gboolean gimp_dbus_service_ui_call_batch_run_sync ( + GimpDBusServiceUI *proxy, + const gchar *arg_interpreter, + const gchar *arg_command, + gboolean *out_success, + GCancellable *cancellable, + GError **error); + +void gimp_dbus_service_ui_call_activate ( + GimpDBusServiceUI *proxy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gimp_dbus_service_ui_call_activate_finish ( + GimpDBusServiceUI *proxy, + GAsyncResult *res, + GError **error); + +gboolean gimp_dbus_service_ui_call_activate_sync ( + GimpDBusServiceUI *proxy, + GCancellable *cancellable, + GError **error); + + + +/* ---- */ + +#define GIMP_DBUS_SERVICE_TYPE_UI_PROXY (gimp_dbus_service_ui_proxy_get_type ()) +#define GIMP_DBUS_SERVICE_UI_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxy)) +#define GIMP_DBUS_SERVICE_UI_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyClass)) +#define GIMP_DBUS_SERVICE_UI_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyClass)) +#define GIMP_DBUS_SERVICE_IS_UI_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY)) +#define GIMP_DBUS_SERVICE_IS_UI_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GIMP_DBUS_SERVICE_TYPE_UI_PROXY)) + +typedef struct _GimpDBusServiceUIProxy GimpDBusServiceUIProxy; +typedef struct _GimpDBusServiceUIProxyClass GimpDBusServiceUIProxyClass; +typedef struct _GimpDBusServiceUIProxyPrivate GimpDBusServiceUIProxyPrivate; + +struct _GimpDBusServiceUIProxy +{ + /*< private >*/ + GDBusProxy parent_instance; + GimpDBusServiceUIProxyPrivate *priv; +}; + +struct _GimpDBusServiceUIProxyClass +{ + GDBusProxyClass parent_class; +}; + +GType gimp_dbus_service_ui_proxy_get_type (void) G_GNUC_CONST; + +#if GLIB_CHECK_VERSION(2, 44, 0) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GimpDBusServiceUIProxy, g_object_unref) +#endif + +void gimp_dbus_service_ui_proxy_new ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_finish ( + GAsyncResult *res, + GError **error); +GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_sync ( + GDBusConnection *connection, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + +void gimp_dbus_service_ui_proxy_new_for_bus ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_for_bus_finish ( + GAsyncResult *res, + GError **error); +GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_for_bus_sync ( + GBusType bus_type, + GDBusProxyFlags flags, + const gchar *name, + const gchar *object_path, + GCancellable *cancellable, + GError **error); + + +/* ---- */ + +#define GIMP_DBUS_SERVICE_TYPE_UI_SKELETON (gimp_dbus_service_ui_skeleton_get_type ()) +#define GIMP_DBUS_SERVICE_UI_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeleton)) +#define GIMP_DBUS_SERVICE_UI_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonClass)) +#define GIMP_DBUS_SERVICE_UI_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonClass)) +#define GIMP_DBUS_SERVICE_IS_UI_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON)) +#define GIMP_DBUS_SERVICE_IS_UI_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON)) + +typedef struct _GimpDBusServiceUISkeleton GimpDBusServiceUISkeleton; +typedef struct _GimpDBusServiceUISkeletonClass GimpDBusServiceUISkeletonClass; +typedef struct _GimpDBusServiceUISkeletonPrivate GimpDBusServiceUISkeletonPrivate; + +struct _GimpDBusServiceUISkeleton +{ + /*< private >*/ + GDBusInterfaceSkeleton parent_instance; + GimpDBusServiceUISkeletonPrivate *priv; +}; + +struct _GimpDBusServiceUISkeletonClass +{ + GDBusInterfaceSkeletonClass parent_class; +}; + +GType gimp_dbus_service_ui_skeleton_get_type (void) G_GNUC_CONST; + +#if GLIB_CHECK_VERSION(2, 44, 0) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GimpDBusServiceUISkeleton, g_object_unref) +#endif + +GimpDBusServiceUI *gimp_dbus_service_ui_skeleton_new (void); + + +G_END_DECLS + +#endif /* __GIMPDBUSSERVICE_GENERATED_H__ */ diff --git a/app/gui/gimpdbusservice.c b/app/gui/gimpdbusservice.c new file mode 100644 index 0000000..462d7d4 --- /dev/null +++ b/app/gui/gimpdbusservice.c @@ -0,0 +1,457 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDBusService + * Copyright (C) 2007, 2008 Sven Neumann + * Copyright (C) 2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "gui-types.h" + +#include "core/gimp.h" +#include "core/gimp-batch.h" +#include "core/gimpcontainer.h" + +#include "file/file-open.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "gimpdbusservice.h" + + +typedef struct +{ + GFile *file; + gboolean as_new; + + gchar *interpreter; + gchar *command; +} IdleData; + +static void gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface); + +static void gimp_dbus_service_dispose (GObject *object); +static void gimp_dbus_service_finalize (GObject *object); + +static gboolean gimp_dbus_service_activate (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation); +static gboolean gimp_dbus_service_open (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri); + +static gboolean gimp_dbus_service_open_as_new (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri); + +static gboolean gimp_dbus_service_batch_run (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *batch_interpreter, + const gchar *batch_command); + +static void gimp_dbus_service_gimp_opened (Gimp *gimp, + GFile *file, + GimpDBusService *service); + +static gboolean gimp_dbus_service_queue_open (GimpDBusService *service, + const gchar *uri, + gboolean as_new); +static gboolean gimp_dbus_service_queue_batch (GimpDBusService *service, + const gchar *interpreter, + const gchar *command); + +static gboolean gimp_dbus_service_process_idle (GimpDBusService *service); +static IdleData * gimp_dbus_service_open_data_new (GimpDBusService *service, + const gchar *uri, + gboolean as_new); +static IdleData * gimp_dbus_service_batch_data_new (GimpDBusService *service, + const gchar *interpreter, + const gchar *command); +static void gimp_dbus_service_idle_data_free (IdleData *data); + + +G_DEFINE_TYPE_WITH_CODE (GimpDBusService, gimp_dbus_service, + GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, + gimp_dbus_service_ui_iface_init)) + +#define parent_class gimp_dbus_service_parent_class + + +static void +gimp_dbus_service_class_init (GimpDBusServiceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_dbus_service_dispose; + object_class->finalize = gimp_dbus_service_finalize; +} + +static void +gimp_dbus_service_init (GimpDBusService *service) +{ + service->queue = g_queue_new (); +} + +static void +gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface) +{ + iface->handle_activate = gimp_dbus_service_activate; + iface->handle_open = gimp_dbus_service_open; + iface->handle_open_as_new = gimp_dbus_service_open_as_new; + iface->handle_batch_run = gimp_dbus_service_batch_run; +} + +GObject * +gimp_dbus_service_new (Gimp *gimp) +{ + GimpDBusService *service; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + service = g_object_new (GIMP_TYPE_DBUS_SERVICE, NULL); + + service->gimp = gimp; + + g_signal_connect_object (gimp, "image-opened", + G_CALLBACK (gimp_dbus_service_gimp_opened), + service, 0); + + return G_OBJECT (service); +} + +static void +gimp_dbus_service_dispose (GObject *object) +{ + GimpDBusService *service = GIMP_DBUS_SERVICE (object); + + if (service->source) + { + g_source_remove (g_source_get_id (service->source)); + service->source = NULL; + service->timeout_source = FALSE; + } + + while (! g_queue_is_empty (service->queue)) + { + gimp_dbus_service_idle_data_free (g_queue_pop_head (service->queue)); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_dbus_service_finalize (GObject *object) +{ + GimpDBusService *service = GIMP_DBUS_SERVICE (object); + + if (service->queue) + { + g_queue_free (service->queue); + service->queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +gboolean +gimp_dbus_service_activate (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation) +{ + Gimp *gimp = GIMP_DBUS_SERVICE (service)->gimp; + + /* We want to be called again later in case that GIMP is not fully + * started yet. + */ + if (gimp_is_restored (gimp)) + { + GimpObject *display; + + display = gimp_container_get_first_child (gimp->displays); + + if (display) + gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display))); + } + + gimp_dbus_service_ui_complete_activate (service, invocation); + + return TRUE; +} + +gboolean +gimp_dbus_service_open (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri) +{ + gboolean success; + + success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service), + uri, FALSE); + + gimp_dbus_service_ui_complete_open (service, invocation, success); + + return TRUE; +} + +gboolean +gimp_dbus_service_open_as_new (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri) +{ + gboolean success; + + success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service), + uri, TRUE); + + gimp_dbus_service_ui_complete_open_as_new (service, invocation, success); + + return TRUE; +} + +gboolean +gimp_dbus_service_batch_run (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *batch_interpreter, + const gchar *batch_command) +{ + gboolean success; + + success = gimp_dbus_service_queue_batch (GIMP_DBUS_SERVICE (service), + batch_interpreter, + batch_command); + + gimp_dbus_service_ui_complete_batch_run (service, invocation, success); + + return TRUE; +} + +static void +gimp_dbus_service_gimp_opened (Gimp *gimp, + GFile *file, + GimpDBusService *service) +{ + gchar *uri = g_file_get_uri (file); + + g_signal_emit_by_name (service, "opened", uri); + + g_free (uri); +} + +/* + * Adds a request to open a file to the end of the queue and + * starts an idle source if it is not already running. + */ +static gboolean +gimp_dbus_service_queue_open (GimpDBusService *service, + const gchar *uri, + gboolean as_new) +{ + g_queue_push_tail (service->queue, + gimp_dbus_service_open_data_new (service, uri, as_new)); + + if (! service->source) + { + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + } + + /* The call always succeeds as it is handled in one way or another. + * Even presenting an error message is considered success ;-) + */ + return TRUE; +} + +/* + * Adds a request to run a batch command to the end of the queue and + * starts an idle source if it is not already running. + */ +static gboolean +gimp_dbus_service_queue_batch (GimpDBusService *service, + const gchar *interpreter, + const gchar *command) +{ + g_queue_push_tail (service->queue, + gimp_dbus_service_batch_data_new (service, + interpreter, + command)); + + if (! service->source) + { + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + } + + /* The call always succeeds as it is handled in one way or another. + * Even presenting an error message is considered success ;-) + */ + return TRUE; +} + +/* + * Idle callback that removes the first request from the queue and + * handles it. If there are no more requests, the idle source is + * removed. + */ +static gboolean +gimp_dbus_service_process_idle (GimpDBusService *service) +{ + IdleData *data; + + if (! service->gimp->initialized || ! service->gimp->restored) + { + if (! service->timeout_source) + { + /* We are probably starting the program. No need to spam GIMP with + * an idle handler (which might make GIMP slower to start even + * with low priority). + * Instead let's add a timeout of half a second. + */ + service->source = g_timeout_source_new (500); + service->timeout_source = TRUE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + + return G_SOURCE_REMOVE; + } + else + { + return G_SOURCE_CONTINUE; + } + } + + /* Process data as a FIFO. */ + data = g_queue_pop_head (service->queue); + + if (data) + { + if (data->file) + file_open_from_command_line (service->gimp, data->file, data->as_new, + NULL, /* FIXME monitor */ + 0 /* FIXME monitor */); + if (data->command) + { + const gchar *commands[2] = {data->command, 0}; + + gimp_batch_run (service->gimp, data->interpreter, + commands); + } + + gimp_dbus_service_idle_data_free (data); + + if (service->timeout_source) + { + /* Now GIMP is fully functional and can respond quickly to + * DBus calls. Switch to a usual idle source. + */ + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + + return G_SOURCE_REMOVE; + } + else + { + return G_SOURCE_CONTINUE; + } + } + + service->source = NULL; + + return G_SOURCE_REMOVE; +} + +static IdleData * +gimp_dbus_service_open_data_new (GimpDBusService *service, + const gchar *uri, + gboolean as_new) +{ + IdleData *data = g_slice_new (IdleData); + + data->file = g_file_new_for_uri (uri); + data->as_new = as_new; + data->interpreter = NULL; + data->command = NULL; + + return data; +} + +static IdleData * +gimp_dbus_service_batch_data_new (GimpDBusService *service, + const gchar *interpreter, + const gchar *command) +{ + IdleData *data = g_slice_new (IdleData); + + data->file = NULL; + data->as_new = FALSE; + + data->command = g_strdup (command); + + if (g_strcmp0 (interpreter, "") == 0) + data->interpreter = NULL; + else + data->interpreter = g_strdup (interpreter); + + return data; +} + +static void +gimp_dbus_service_idle_data_free (IdleData *data) +{ + if (data->file) + g_object_unref (data->file); + + if (data->command) + g_free (data->command); + if (data->interpreter) + g_free (data->interpreter); + + g_slice_free (IdleData, data); +} diff --git a/app/gui/gimpdbusservice.h b/app/gui/gimpdbusservice.h new file mode 100644 index 0000000..71f1493 --- /dev/null +++ b/app/gui/gimpdbusservice.h @@ -0,0 +1,69 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDBusService + * Copyright (C) 2007, 2008 Sven Neumann + * Copyright (C) 2013 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_DBUS_SERVICE_H__ +#define __GIMP_DBUS_SERVICE_H__ + + +#include "gimpdbusservice-generated.h" + +/* service name and path should really be org.gimp.GIMP and + * /org/gimp/GIMP and only the interface be called UI. + */ +#define GIMP_DBUS_SERVICE_NAME "org.gimp.GIMP.UI" +#define GIMP_DBUS_SERVICE_PATH "/org/gimp/GIMP/UI" +#define GIMP_DBUS_INTERFACE_NAME "org.gimp.GIMP.UI" +#define GIMP_DBUS_INTERFACE_PATH "/org/gimp/GIMP/UI" + + +#define GIMP_TYPE_DBUS_SERVICE (gimp_dbus_service_get_type ()) +#define GIMP_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DBUS_SERVICE, GimpDBusService)) +#define GIMP_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DBUS_SERVICE, GimpDBusServiceClass)) +#define GIMP_IS_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DBUS_SERVICE)) +#define GIMP_IS_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DBUS_SERVICE)) +#define GIMP_DBUS_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DBUS_SERVICE, GimpDBusServiceClass)) + + +typedef struct _GimpDBusService GimpDBusService; +typedef struct _GimpDBusServiceClass GimpDBusServiceClass; + +struct _GimpDBusService +{ + GimpDBusServiceUISkeleton parent_instance; + + Gimp *gimp; + GQueue *queue; + GSource *source; + gboolean timeout_source; +}; + +struct _GimpDBusServiceClass +{ + GimpDBusServiceUISkeletonClass parent_class; +}; + + +GType gimp_dbus_service_get_type (void) G_GNUC_CONST; + +GObject * gimp_dbus_service_new (Gimp *gimp); + + +#endif /* __GIMP_DBUS_SERVICE_H__ */ diff --git a/app/gui/gimpuiconfigurer.c b/app/gui/gimpuiconfigurer.c new file mode 100644 index 0000000..dd9aa5d --- /dev/null +++ b/app/gui/gimpuiconfigurer.c @@ -0,0 +1,622 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpuiconfigurer.c + * Copyright (C) 2009 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "gui-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockcolumns.h" +#include "widgets/gimpdockcontainer.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimptoolbox.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-appearance.h" +#include "display/gimpimagewindow.h" + +#include "gimpuiconfigurer.h" + + +enum +{ + PROP_0, + PROP_GIMP +}; + + +struct _GimpUIConfigurerPrivate +{ + Gimp *gimp; +}; + + +static void gimp_ui_configurer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_ui_configurer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer, + GimpImageWindow *uber_image_window); +static void gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window, + GimpImageWindow *target_image_window); +static void gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window); +static void gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer, + GimpDockColumns *dock_columns, + GimpAlignmentType screen_side); +static void gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window); +static void gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer); +static void gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer); +static GimpImageWindow * gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpUIConfigurer, gimp_ui_configurer, + GIMP_TYPE_OBJECT) + +#define parent_class gimp_ui_configurer_parent_class + + +static void +gimp_ui_configurer_class_init (GimpUIConfigurerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_ui_configurer_set_property; + object_class->get_property = gimp_ui_configurer_get_property; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_ui_configurer_init (GimpUIConfigurer *ui_configurer) +{ + ui_configurer->p = gimp_ui_configurer_get_instance_private (ui_configurer); +} + +static void +gimp_ui_configurer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object); + + switch (property_id) + { + case PROP_GIMP: + ui_configurer->p->gimp = g_value_get_object (value); /* don't ref */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_ui_configurer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, ui_configurer->p->gimp); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +gimp_ui_configurer_get_window_center_pos (GtkWindow *window, + gint *out_x, + gint *out_y) +{ + gint x, y, w, h; + gtk_window_get_position (window, &x, &y); + gtk_window_get_size (window, &w, &h); + + if (out_x) + *out_x = x + w / 2; + if (out_y) + *out_y = y + h / 2; +} + +/** + * gimp_ui_configurer_get_relative_window_pos: + * @window_a: + * @window_b: + * + * Returns: At what side @window_b is relative to @window_a. Either + * GIMP_ALIGN_LEFT or GIMP_ALIGN_RIGHT. + **/ +static GimpAlignmentType +gimp_ui_configurer_get_relative_window_pos (GtkWindow *window_a, + GtkWindow *window_b) +{ + gint a_x, b_x; + + gimp_ui_configurer_get_window_center_pos (window_a, &a_x, NULL); + gimp_ui_configurer_get_window_center_pos (window_b, &b_x, NULL); + + return b_x < a_x ? GIMP_ALIGN_LEFT : GIMP_ALIGN_RIGHT; +} + +static void +gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer, + GimpImageWindow *uber_image_window) +{ + GList *dialogs = NULL; + GList *dialog_iter = NULL; + + dialogs = + g_list_copy (gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ())); + + for (dialog_iter = dialogs; dialog_iter; dialog_iter = dialog_iter->next) + { + GimpDockWindow *dock_window; + GimpDockContainer *dock_container; + GimpDockColumns *dock_columns; + GList *docks; + GList *dock_iter; + + if (!GIMP_IS_DOCK_WINDOW (dialog_iter->data)) + continue; + + dock_window = GIMP_DOCK_WINDOW (dialog_iter->data); + + /* If the dock window is on the left side of the image window, + * move the docks to the left side. If the dock window is on the + * right side, move the docks to the right side of the image + * window. + */ + if (gimp_ui_configurer_get_relative_window_pos (GTK_WINDOW (uber_image_window), + GTK_WINDOW (dock_window)) == GIMP_ALIGN_LEFT) + dock_columns = gimp_image_window_get_left_docks (uber_image_window); + else + dock_columns = gimp_image_window_get_right_docks (uber_image_window); + + dock_container = GIMP_DOCK_CONTAINER (dock_window); + g_object_add_weak_pointer (G_OBJECT (dock_window), + (gpointer) &dock_window); + + docks = gimp_dock_container_get_docks (dock_container); + for (dock_iter = docks; dock_iter; dock_iter = dock_iter->next) + { + GimpDock *dock = GIMP_DOCK (dock_iter->data); + + /* Move the dock from the image window to the dock columns + * widget. Note that we need a ref while the dock is parentless + */ + g_object_ref (dock); + gimp_dock_window_remove_dock (dock_window, dock); + gimp_dock_columns_add_dock (dock_columns, dock, -1); + g_object_unref (dock); + } + g_list_free (docks); + + if (dock_window) + g_object_remove_weak_pointer (G_OBJECT (dock_window), + (gpointer) &dock_window); + + /* Kill the window if removing the dock didn't destroy it + * already. This will be the case for the toolbox dock window + */ + if (GTK_IS_WIDGET (dock_window)) + { + guint docks_len; + + docks = gimp_dock_container_get_docks (dock_container); + docks_len = g_list_length (docks); + + if (docks_len == 0) + { + gimp_dialog_factory_remove_dialog (gimp_dialog_factory_get_singleton (), + GTK_WIDGET (dock_window)); + gtk_widget_destroy (GTK_WIDGET (dock_window)); + } + + g_list_free (docks); + } + } + + g_list_free (dialogs); +} + +/** + * gimp_ui_configurer_move_shells: + * @ui_configurer: + * @source_image_window: + * @target_image_window: + * + * Move all display shells from one image window to the another. + **/ +static void +gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window, + GimpImageWindow *target_image_window) +{ + while (gimp_image_window_get_n_shells (source_image_window) > 0) + { + GimpDisplayShell *shell; + + shell = gimp_image_window_get_shell (source_image_window, 0); + + g_object_ref (shell); + gimp_image_window_remove_shell (source_image_window, shell); + gimp_image_window_add_shell (target_image_window, shell); + g_object_unref (shell); + } +} + +/** + * gimp_ui_configurer_separate_docks: + * @ui_configurer: + * @image_window: + * + * Move out the docks from the image window. + **/ +static void +gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer, + GimpImageWindow *image_window) +{ + GimpDockColumns *left_docks = NULL; + GimpDockColumns *right_docks = NULL; + + left_docks = gimp_image_window_get_left_docks (image_window); + right_docks = gimp_image_window_get_right_docks (image_window); + + gimp_ui_configurer_move_docks_to_window (ui_configurer, left_docks, GIMP_ALIGN_LEFT); + gimp_ui_configurer_move_docks_to_window (ui_configurer, right_docks, GIMP_ALIGN_RIGHT); +} + +/** + * gimp_ui_configurer_move_docks_to_window: + * @dock_columns: + * @screen_side: At what side of the screen the dock window should be put. + * + * Moves docks in @dock_columns into a new #GimpDockWindow and + * position it on the screen in a non-overlapping manner. + */ +static void +gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer, + GimpDockColumns *dock_columns, + GimpAlignmentType screen_side) +{ + GdkScreen *screen; + gint monitor; + GdkRectangle monitor_rect; + GList *docks; + GList *iter; + gboolean contains_toolbox = FALSE; + GtkWidget *dock_window; + GtkAllocation original_size; + gchar geometry[32]; + + docks = g_list_copy (gimp_dock_columns_get_docks (dock_columns)); + if (! docks) + return; + + screen = gtk_widget_get_screen (GTK_WIDGET (dock_columns)); + monitor = gimp_widget_get_monitor (GTK_WIDGET (dock_columns)); + + gdk_screen_get_monitor_workarea (screen, monitor, &monitor_rect); + + /* Remember the size so we can set the new dock window to the same + * size + */ + gtk_widget_get_allocation (GTK_WIDGET (dock_columns), &original_size); + + /* Do we need a toolbox window? */ + for (iter = docks; iter; iter = g_list_next (iter)) + { + GimpDock *dock = GIMP_DOCK (iter->data); + + if (GIMP_IS_TOOLBOX (dock)) + { + contains_toolbox = TRUE; + break; + } + } + + /* Create a dock window to put the dock in. Checking for + * GIMP_IS_TOOLBOX() is kind of ugly but not a disaster. We need + * the dock window correctly configured if we create it for the + * toolbox + */ + dock_window = + gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + screen, + monitor, + NULL /*ui_manager*/, + (contains_toolbox ? + "gimp-toolbox-window" : + "gimp-dock-window"), + -1 /*view_size*/, + FALSE /*present*/); + + for (iter = docks; iter; iter = g_list_next (iter)) + { + GimpDock *dock = GIMP_DOCK (iter->data); + + /* Move the dock to the window */ + g_object_ref (dock); + gimp_dock_columns_remove_dock (dock_columns, dock); + gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window), dock, -1); + g_object_unref (dock); + } + + /* Position the window */ + if (screen_side == GIMP_ALIGN_LEFT) + { + g_snprintf (geometry, sizeof (geometry), "%+d%+d", + monitor_rect.x, + monitor_rect.y); + } + else if (screen_side == GIMP_ALIGN_RIGHT) + { + g_snprintf (geometry, sizeof (geometry), "%+d%+d", + monitor_rect.x + monitor_rect.width - original_size.width, + monitor_rect.y); + } + else + { + gimp_assert_not_reached (); + } + + gtk_window_parse_geometry (GTK_WINDOW (dock_window), geometry); + + /* Try to keep the same size */ + gtk_window_set_default_size (GTK_WINDOW (dock_window), + original_size.width, + original_size.height); + + /* Don't forget to show the window */ + gtk_widget_show (dock_window); + + g_list_free (docks); +} + +/** + * gimp_ui_configurer_separate_shells: + * @ui_configurer: + * @source_image_window: + * + * Create one image window per display shell and move it there. + **/ +static void +gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window) +{ + GimpDisplayShell *active_shell = gimp_image_window_get_active_shell (source_image_window); + GimpImageWindow *active_window = NULL; + + /* The last display shell remains in its window */ + while (gimp_image_window_get_n_shells (source_image_window) > 1) + { + GimpImageWindow *new_image_window; + GimpDisplayShell *shell; + + /* Create a new image window */ + new_image_window = gimp_image_window_new (ui_configurer->p->gimp, + NULL, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (GTK_WIDGET (source_image_window)), + gimp_widget_get_monitor (GTK_WIDGET (source_image_window))); + /* Move the shell there */ + shell = gimp_image_window_get_shell (source_image_window, 1); + + if (shell == active_shell) + active_window = new_image_window; + + g_object_ref (shell); + gimp_image_window_remove_shell (source_image_window, shell); + gimp_image_window_add_shell (new_image_window, shell); + g_object_unref (shell); + + /* FIXME: If we don't set a size request here the window will be + * too small. Get rid of this hack and fix it the proper way + */ + gtk_widget_set_size_request (GTK_WIDGET (new_image_window), 640, 480); + + /* Show after we have added the shell */ + gtk_widget_show (GTK_WIDGET (new_image_window)); + } + + /* If none of the shells were active, I assume the first one is. */ + if (active_window == NULL) + active_window = source_image_window; + + /* The active tab must stay at the top of the windows stack. */ + gtk_window_present (GTK_WINDOW (active_window)); +} + +/** + * gimp_ui_configurer_configure_for_single_window: + * @ui_configurer: + * + * Move docks and display shells into a single window. + **/ +static void +gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *iter = NULL; + GimpImageWindow *uber_image_window = NULL; + GimpDisplay *active_display = gimp_context_get_display (gimp_get_user_context (gimp)); + GimpDisplayShell *active_shell = gimp_display_get_shell (active_display); + + /* Get and setup the window to put everything in */ + uber_image_window = gimp_ui_configurer_get_uber_window (ui_configurer); + + /* Mve docks to the left and right side of the image window */ + gimp_ui_configurer_move_docks_to_columns (ui_configurer, + uber_image_window); + + /* Move image shells from other windows to the uber image window */ + for (iter = windows; iter; iter = g_list_next (iter)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data); + + /* Don't move stuff to itself */ + if (image_window == uber_image_window) + continue; + + /* Put the displays in the rest of the image windows into + * the uber image window + */ + gimp_ui_configurer_move_shells (ui_configurer, + image_window, + uber_image_window); + /* Destroy the window */ + gimp_image_window_destroy (image_window); + } + + /* Ensure the context shell remains active after mode switch. */ + gimp_image_window_set_active_shell (uber_image_window, active_shell); + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_configure_for_multi_window: + * @ui_configurer: + * + * Moves all display shells into their own image window. + **/ +static void +gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *iter = NULL; + + for (iter = windows; iter; iter = g_list_next (iter)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data); + + gimp_ui_configurer_separate_docks (ui_configurer, image_window); + + gimp_ui_configurer_separate_shells (ui_configurer, image_window); + } + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_get_uber_window: + * @ui_configurer: + * + * Returns: The window to be used as the main window for single-window + * mode. + **/ +static GimpImageWindow * +gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GimpDisplay *display = gimp_get_display_iter (gimp)->data; + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImageWindow *image_window = gimp_display_shell_get_window (shell); + + return image_window; +} + +/** + * gimp_ui_configurer_update_appearance: + * @ui_configurer: + * + * Updates the appearance of all shells in all image windows, so they + * do whatever they deem necessary to fit the new UI mode mode. + **/ +static void +gimp_ui_configurer_update_appearance (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *list; + + for (list = windows; list; list = g_list_next (list)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (list->data); + gint n_shells; + gint i; + + n_shells = gimp_image_window_get_n_shells (image_window); + + for (i = 0; i < n_shells; i++) + { + GimpDisplayShell *shell; + + shell = gimp_image_window_get_shell (image_window, i); + + gimp_display_shell_appearance_update (shell); + } + } + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_configure: + * @ui_configurer: + * @single_window_mode: + * + * Configure the UI. + **/ +void +gimp_ui_configurer_configure (GimpUIConfigurer *ui_configurer, + gboolean single_window_mode) +{ + if (single_window_mode) + gimp_ui_configurer_configure_for_single_window (ui_configurer); + else + gimp_ui_configurer_configure_for_multi_window (ui_configurer); + + gimp_ui_configurer_update_appearance (ui_configurer); +} diff --git a/app/gui/gimpuiconfigurer.h b/app/gui/gimpuiconfigurer.h new file mode 100644 index 0000000..db22ffc --- /dev/null +++ b/app/gui/gimpuiconfigurer.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpuiconfigurer.h + * Copyright (C) 2009 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 . + */ + +#ifndef __GIMP_UI_CONFIGURER_H__ +#define __GIMP_UI_CONFIGURER_H__ + + +#include "core/gimpobject.h" + + +#define GIMP_TYPE_UI_CONFIGURER (gimp_ui_configurer_get_type ()) +#define GIMP_UI_CONFIGURER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurer)) +#define GIMP_UI_CONFIGURER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurerClass)) +#define GIMP_IS_UI_CONFIGURER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UI_CONFIGURER)) +#define GIMP_IS_UI_CONFIGURER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GIMP_TYPE_UI_CONFIGURER)) +#define GIMP_UI_CONFIGURER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurerClass)) + + +typedef struct _GimpUIConfigurerClass GimpUIConfigurerClass; +typedef struct _GimpUIConfigurerPrivate GimpUIConfigurerPrivate; + +struct _GimpUIConfigurer +{ + GimpObject parent_instance; + + GimpUIConfigurerPrivate *p; +}; + +struct _GimpUIConfigurerClass +{ + GimpObjectClass parent_class; +}; + + +GType gimp_ui_configurer_get_type (void) G_GNUC_CONST; +void gimp_ui_configurer_configure (GimpUIConfigurer *ui_configurer, + gboolean single_window_mode); + + +#endif /* __GIMP_UI_CONFIGURER_H__ */ diff --git a/app/gui/gui-message.c b/app/gui/gui-message.c new file mode 100644 index 0000000..90e6314 --- /dev/null +++ b/app/gui/gui-message.c @@ -0,0 +1,501 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "gui-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpprogress.h" + +#include "plug-in/gimpplugin.h" + +#include "widgets/gimpcriticaldialog.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdockable.h" +#include "widgets/gimperrorconsole.h" +#include "widgets/gimperrordialog.h" +#include "widgets/gimpprogressdialog.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimpwindowstrategy.h" + +#include "about.h" + +#include "gui-message.h" + +#include "gimp-intl.h" + + +#define MAX_TRACES 3 +#define MAX_ERRORS 10 + + +typedef struct +{ + Gimp *gimp; + gchar *domain; + gchar *message; + gchar *trace; + GObject *handler; + GimpMessageSeverity severity; +} GimpLogMessageData; + + +static gboolean gui_message_idle (gpointer user_data); +static gboolean gui_message_error_console (Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); +static gboolean gui_message_error_dialog (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message, + const gchar *trace); +static void gui_message_console (GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); +static gchar * gui_message_format (GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + +static GtkWidget * global_error_dialog (void); +static GtkWidget * global_critical_dialog (void); + +static void gui_message_reset_errors (GObject *object, + gpointer user_data); + + +static GMutex mutex; +static gint n_traces = 0; +static gint n_errors = 0; + + +void +gui_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + gchar *trace = NULL; + gboolean gen_trace = FALSE; + + switch (gimp->message_handler) + { + case GIMP_ERROR_CONSOLE: + if (gui_message_error_console (gimp, severity, domain, message)) + return; + + gimp->message_handler = GIMP_MESSAGE_BOX; + /* fallthru */ + + case GIMP_MESSAGE_BOX: + if (severity >= GIMP_MESSAGE_BUG_WARNING) + { + g_mutex_lock (&mutex); + /* Trace creation can be time consuming so don't block the + * mutex for too long and only increment and set a boolean + * here. + */ + if (n_traces < MAX_TRACES) + { + gen_trace = TRUE; + n_traces++; + } + g_mutex_unlock (&mutex); + } + + if (gen_trace) + { + /* We need to create the trace here because for multi-thread + * errors (i.e. non-GIMP ones), the backtrace after a idle + * function will simply be useless. It needs to happen in the + * buggy thread to be meaningful. + */ + gimp_stack_trace_print (NULL, NULL, &trace); + } + + if (g_strcmp0 (GIMP_ACRONYM, domain) != 0) + { + /* Handle non-GIMP messages in a multi-thread safe way, + * because we can't know for sure whether the log message may + * not have been called from a thread other than the main one. + */ + GimpLogMessageData *data; + + data = g_new0 (GimpLogMessageData, 1); + data->gimp = gimp; + data->domain = g_strdup (domain); + data->message = g_strdup (message); + data->trace = trace; + data->handler = handler? g_object_ref (handler) : NULL; + data->severity = severity; + + gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, + gui_message_idle, + data, g_free); + return; + } + if (gui_message_error_dialog (gimp, handler, severity, + domain, message, trace)) + break; + + gimp->message_handler = GIMP_CONSOLE; + /* fallthru */ + + case GIMP_CONSOLE: + gui_message_console (severity, domain, message); + break; + } + if (trace) + g_free (trace); +} + +static gboolean +gui_message_idle (gpointer user_data) +{ + GimpLogMessageData *data = (GimpLogMessageData *) user_data; + + if (! gui_message_error_dialog (data->gimp, + data->handler, + data->severity, + data->domain, + data->message, + data->trace)) + { + gui_message_console (data->severity, + data->domain, + data->message); + } + g_free (data->domain); + g_free (data->message); + if (data->trace) + g_free (data->trace); + if (data->handler) + g_object_unref (data->handler); + + return FALSE; +} + +static gboolean +gui_message_error_console (Gimp *gimp, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + GtkWidget *dockable; + + dockable = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (), + "gimp-error-console"); + + /* avoid raising the error console for unhighlighted messages */ + if (dockable) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (dockable)); + + if (GIMP_ERROR_CONSOLE (child)->highlight[severity]) + dockable = NULL; + } + + if (! dockable) + { + GdkScreen *screen; + gint monitor; + + monitor = gimp_get_monitor_at_pointer (&screen); + + dockable = + gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)), + gimp, + gimp_dialog_factory_get_singleton (), + screen, monitor, + "gimp-error-console"); + } + + if (dockable) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (dockable)); + + gimp_error_console_add (GIMP_ERROR_CONSOLE (child), + severity, domain, message); + + return TRUE; + } + + return FALSE; +} + +static void +progress_error_dialog_unset (GimpProgress *progress) +{ + g_object_set_data (G_OBJECT (progress), "gimp-error-dialog", NULL); +} + +static GtkWidget * +progress_error_dialog (GimpProgress *progress) +{ + GtkWidget *dialog; + + g_return_val_if_fail (GIMP_IS_PROGRESS (progress), NULL); + + dialog = g_object_get_data (G_OBJECT (progress), "gimp-error-dialog"); + + if (! dialog) + { + dialog = gimp_error_dialog_new (_("GIMP Message")); + + g_object_set_data (G_OBJECT (progress), "gimp-error-dialog", dialog); + + g_signal_connect_object (dialog, "destroy", + G_CALLBACK (progress_error_dialog_unset), + progress, G_CONNECT_SWAPPED); + + if (GTK_IS_WIDGET (progress)) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress)); + + if (GTK_IS_WINDOW (toplevel)) + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (toplevel)); + } + else + { + guint32 window_id = gimp_progress_get_window_id (progress); + + if (window_id) + gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); + } + } + + return dialog; +} + +static gboolean +gui_message_error_dialog (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message, + const gchar *trace) +{ + GtkWidget *dialog; + GtkMessageType type = GTK_MESSAGE_ERROR; + + switch (severity) + { + case GIMP_MESSAGE_INFO: + type = GTK_MESSAGE_INFO; + break; + case GIMP_MESSAGE_WARNING: + type = GTK_MESSAGE_WARNING; + break; + case GIMP_MESSAGE_ERROR: + type = GTK_MESSAGE_ERROR; + break; + case GIMP_MESSAGE_BUG_WARNING: + case GIMP_MESSAGE_BUG_CRITICAL: + type = GTK_MESSAGE_OTHER; + break; + } + + if (severity >= GIMP_MESSAGE_BUG_WARNING) + { + /* Process differently programming errors. + * The reason is that we will generate traces, which will take + * significant place, and cannot be processed as a progress + * message or in the global dialog. It will require its own + * dedicated dialog which will encourage people to report the bug. + */ + gboolean gui_error = FALSE; + + g_mutex_lock (&mutex); + if (n_errors < MAX_ERRORS) + { + gui_error = TRUE; + n_errors++; + } + g_mutex_unlock (&mutex); + + if (gui_error || trace) + { + gchar *text; + + dialog = global_critical_dialog (); + + text = gui_message_format (severity, domain, message); + gimp_critical_dialog_add (dialog, text, trace, FALSE, NULL, 0); + + gtk_widget_show (dialog); + + g_free (text); + + return TRUE; + } + else + { + const gchar *reason = "Message"; + + gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity, + NULL, NULL, &reason, NULL); + + /* Since we overridden glib default's WARNING and CRITICAL + * handler, if we decide not to handle this error in the end, + * let's just print it in terminal in a similar fashion as + * glib's default handler (though without the fancy terminal + * colors right now). + */ + g_printerr ("%s-%s: %s\n", domain, reason, message); + + return TRUE; + } + } + else if (GIMP_IS_PROGRESS (handler)) + { + /* If there's already an error dialog associated with this + * progress, then continue without trying gimp_progress_message(). + */ + if (! g_object_get_data (handler, "gimp-error-dialog") && + gimp_progress_message (GIMP_PROGRESS (handler), gimp, + severity, domain, message)) + { + return TRUE; + } + } + else if (GTK_IS_WIDGET (handler)) + { + GtkWidget *parent = GTK_WIDGET (handler); + + dialog = + gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (parent)), + GTK_DIALOG_DESTROY_WITH_PARENT, + type, GTK_BUTTONS_OK, + "%s", message); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_widget_show (dialog); + + return TRUE; + } + + if (GIMP_IS_PROGRESS (handler) && ! GIMP_IS_PROGRESS_DIALOG (handler)) + dialog = progress_error_dialog (GIMP_PROGRESS (handler)); + else + dialog = global_error_dialog (); + + if (dialog) + { + gimp_error_dialog_add (GIMP_ERROR_DIALOG (dialog), + gimp_get_message_icon_name (severity), + domain, message); + gtk_window_present (GTK_WINDOW (dialog)); + + return TRUE; + } + + return FALSE; +} + +static void +gui_message_console (GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + gchar *formatted_message; + + formatted_message = gui_message_format (severity, domain, message); + g_printerr ("%s\n\n", formatted_message); + g_free (formatted_message); +} + +static gchar * +gui_message_format (GimpMessageSeverity severity, + const gchar *domain, + const gchar *message) +{ + const gchar *desc = "Message"; + gchar *formatted_message; + + gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity, + NULL, NULL, &desc, NULL); + + formatted_message = g_strdup_printf ("%s-%s: %s", domain, desc, message); + + return formatted_message; +} + +static GtkWidget * +global_error_dialog (void) +{ + GdkScreen *screen; + gint monitor; + + monitor = gimp_get_monitor_at_pointer (&screen); + + return gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + screen, monitor, + NULL /*ui_manager*/, + "gimp-error-dialog", -1, + FALSE); +} + +static GtkWidget * +global_critical_dialog (void) +{ + GtkWidget *dialog; + GdkScreen *screen; + gint monitor; + + monitor = gimp_get_monitor_at_pointer (&screen); + + dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + screen, monitor, + NULL /*ui_manager*/, + "gimp-critical-dialog", -1, + FALSE); + g_signal_handlers_disconnect_by_func (dialog, + gui_message_reset_errors, + NULL); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gui_message_reset_errors), + NULL); + return dialog; +} + +static void +gui_message_reset_errors (GObject *object, + gpointer user_data) +{ + g_mutex_lock (&mutex); + n_errors = 0; + n_traces = 0; + g_mutex_unlock (&mutex); +} diff --git a/app/gui/gui-message.h b/app/gui/gui-message.h new file mode 100644 index 0000000..e866f2a --- /dev/null +++ b/app/gui/gui-message.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 . + */ + +#ifndef __GUI_MESSAGE_H__ +#define __GUI_MESSAGE_H__ + + +void gui_message (Gimp *gimp, + GObject *handler, + GimpMessageSeverity severity, + const gchar *domain, + const gchar *message); + + +#endif /* __GUI_VTABLE_H__ */ diff --git a/app/gui/gui-types.h b/app/gui/gui-types.h new file mode 100644 index 0000000..82e9f20 --- /dev/null +++ b/app/gui/gui-types.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 . + */ + +#ifndef __GUI_TYPES_H__ +#define __GUI_TYPES_H__ + + +#include "tools/tools-types.h" +#include "dialogs/dialogs-types.h" +#include "menus/menus-types.h" + + +#endif /* __GUI_TYPES_H__ */ diff --git a/app/gui/gui-unique.c b/app/gui/gui-unique.c new file mode 100644 index 0000000..c54a05b --- /dev/null +++ b/app/gui/gui-unique.c @@ -0,0 +1,442 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#ifdef G_OS_WIN32 +#include +#endif + +#ifdef GDK_WINDOWING_QUARTZ +#import +#include +#endif + +#include "gui/gui-types.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpimagewindow.h" + +#include "file/file-open.h" + +#include "gimpdbusservice.h" +#include "gui-unique.h" + + +#ifdef G_OS_WIN32 + +static void gui_unique_win32_init (Gimp *gimp); +static void gui_unique_win32_exit (void); + +static Gimp *unique_gimp = NULL; +static HWND proxy_window = NULL; + +#elif defined (GDK_WINDOWING_QUARTZ) + +static void gui_unique_quartz_init (Gimp *gimp); +static void gui_unique_quartz_exit (void); + +@interface GimpAppleEventHandler : NSObject {} +- (void) handleEvent:(NSAppleEventDescriptor *) inEvent + andReplyWith:(NSAppleEventDescriptor *) replyEvent; +@end + +static Gimp *unique_gimp = NULL; +static GimpAppleEventHandler *event_handler = NULL; + +#else + +static void gui_dbus_service_init (Gimp *gimp); +static void gui_dbus_service_exit (void); + +static GDBusObjectManagerServer *dbus_manager = NULL; +static guint dbus_name_id = 0; + +#endif + + +void +gui_unique_init (Gimp *gimp) +{ +#ifdef G_OS_WIN32 + gui_unique_win32_init (gimp); +#elif defined (GDK_WINDOWING_QUARTZ) + gui_unique_quartz_init (gimp); +#else + gui_dbus_service_init (gimp); +#endif +} + +void +gui_unique_exit (void) +{ +#ifdef G_OS_WIN32 + gui_unique_win32_exit (); +#elif defined (GDK_WINDOWING_QUARTZ) + gui_unique_quartz_exit (); +#else + gui_dbus_service_exit (); +#endif +} + + +#ifdef G_OS_WIN32 + +typedef struct +{ + GFile *file; + gboolean as_new; +} IdleOpenData; + +static IdleOpenData * +idle_open_data_new (GFile *file, + gboolean as_new) +{ + IdleOpenData *data = g_slice_new0 (IdleOpenData); + + data->file = g_object_ref (file); + data->as_new = as_new; + + return data; +} + +static void +idle_open_data_free (IdleOpenData *data) +{ + g_object_unref (data->file); + g_slice_free (IdleOpenData, data); +} + +static gboolean +gui_unique_win32_idle_open (IdleOpenData *data) +{ + /* We want to be called again later in case that GIMP is not fully + * started yet. + */ + if (! gimp_is_restored (unique_gimp)) + return TRUE; + + if (data->file) + { + file_open_from_command_line (unique_gimp, data->file, + data->as_new, NULL, 0); + } + else + { + /* raise the first display */ + GimpObject *display; + + display = gimp_container_get_first_child (unique_gimp->displays); + + gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display))); + } + + return FALSE; +} + +static LRESULT CALLBACK +gui_unique_win32_message_handler (HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_COPYDATA: + if (unique_gimp) + { + COPYDATASTRUCT *copydata = (COPYDATASTRUCT *) lParam; + GimpObject *display; + + if (copydata->cbData > 0) + { + GSource *source; + GClosure *closure; + GFile *file; + IdleOpenData *data; + + file = g_file_new_for_uri (copydata->lpData); + + data = idle_open_data_new (file, + copydata->dwData != 0); + + g_object_unref (file); + + closure = g_cclosure_new (G_CALLBACK (gui_unique_win32_idle_open), + data, + (GClosureNotify) idle_open_data_free); + + g_object_watch_closure (G_OBJECT (unique_gimp), closure); + + source = g_idle_source_new (); + g_source_set_priority (source, G_PRIORITY_LOW); + g_source_set_closure (source, closure); + g_source_attach (source, NULL); + g_source_unref (source); + } + + /* Deiconify the window if minimized. */ + display = gimp_container_get_first_child (unique_gimp->displays); + if (display) + gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display))); + } + return TRUE; + + default: + return DefWindowProcW (hWnd, uMsg, wParam, lParam); + } +} + +static void +gui_unique_win32_init (Gimp *gimp) +{ + WNDCLASSW wc; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (unique_gimp == NULL); + + unique_gimp = gimp; + + /* register window class for proxy window */ + memset (&wc, 0, sizeof (wc)); + + wc.hInstance = GetModuleHandle (NULL); + wc.lpfnWndProc = gui_unique_win32_message_handler; + wc.lpszClassName = GIMP_UNIQUE_WIN32_WINDOW_CLASS; + + RegisterClassW (&wc); + + proxy_window = CreateWindowExW (0, + GIMP_UNIQUE_WIN32_WINDOW_CLASS, + GIMP_UNIQUE_WIN32_WINDOW_NAME, + WS_POPUP, 0, 0, 1, 1, NULL, NULL, wc.hInstance, NULL); +} + +static void +gui_unique_win32_exit (void) +{ + g_return_if_fail (GIMP_IS_GIMP (unique_gimp)); + + unique_gimp = NULL; + + DestroyWindow (proxy_window); +} + +#elif defined (GDK_WINDOWING_QUARTZ) + +static gboolean +gui_unique_quartz_idle_open (GFile *file) +{ + /* We want to be called again later in case that GIMP is not fully + * started yet. + */ + if (! gimp_is_restored (unique_gimp)) + return TRUE; + + if (file) + { + file_open_from_command_line (unique_gimp, file, FALSE, NULL, 0); + } + + return FALSE; +} + +static gboolean +gui_unique_quartz_nsopen_file_callback (GtkosxApplication *osx_app, + gchar *path, + gpointer user_data) +{ + GSource *source; + GClosure *closure; + + closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open), + g_file_new_for_path (path), + (GClosureNotify) g_object_unref); + + g_object_watch_closure (G_OBJECT (unique_gimp), closure); + + source = g_idle_source_new (); + + g_source_set_priority (source, G_PRIORITY_LOW); + g_source_set_closure (source, closure); + g_source_attach (source, NULL); + g_source_unref (source); + + return TRUE; +} + +@implementation GimpAppleEventHandler +- (void) handleEvent: (NSAppleEventDescriptor *) inEvent + andReplyWith: (NSAppleEventDescriptor *) replyEvent +{ + NSAutoreleasePool *urlpool; + NSInteger count; + NSInteger i; + + urlpool = [[NSAutoreleasePool alloc] init]; + + count = [inEvent numberOfItems]; + + for (i = 1; i <= count; i++) + { + NSURL *url; + const gchar *path; + GSource *source; + GClosure *closure; + + url = [NSURL URLWithString: [[inEvent descriptorAtIndex: i] stringValue]]; + path = [[url path] UTF8String]; + + closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open), + g_file_new_for_path (path), + (GClosureNotify) g_object_unref); + + g_object_watch_closure (G_OBJECT (unique_gimp), closure); + + source = g_idle_source_new (); + g_source_set_priority (source, G_PRIORITY_LOW); + g_source_set_closure (source, closure); + g_source_attach (source, NULL); + g_source_unref (source); + } + + [urlpool drain]; +} +@end + +static void +gui_unique_quartz_init (Gimp *gimp) +{ + GtkosxApplication *osx_app; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (unique_gimp == NULL); + + osx_app = gtkosx_application_get (); + + unique_gimp = gimp; + + g_signal_connect (osx_app, "NSApplicationOpenFile", + G_CALLBACK (gui_unique_quartz_nsopen_file_callback), + gimp); + + /* Using the event handler is a hack, it is necessary because + * gtkosx_application will drop the file open events if any + * event processing is done before gtkosx_application_ready is + * called, which we unfortuantly can't avoid doing right now. + */ + event_handler = [[GimpAppleEventHandler alloc] init]; + + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler: event_handler + andSelector: @selector (handleEvent: andReplyWith:) + forEventClass: kCoreEventClass + andEventID: kAEOpenDocuments]; +} + +static void +gui_unique_quartz_exit (void) +{ + g_return_if_fail (GIMP_IS_GIMP (unique_gimp)); + + unique_gimp = NULL; + + [[NSAppleEventManager sharedAppleEventManager] + removeEventHandlerForEventClass: kCoreEventClass + andEventID: kAEOpenDocuments]; + + [event_handler release]; + + event_handler = NULL; +} + +#else + +static void +gui_dbus_bus_acquired (GDBusConnection *connection, + const gchar *name, + Gimp *gimp) +{ + GDBusObjectSkeleton *object; + GObject *service; + + /* this should use GIMP_DBUS_SERVICE_PATH, but that's historically wrong */ + dbus_manager = g_dbus_object_manager_server_new ("/org/gimp/GIMP"); + + object = g_dbus_object_skeleton_new (GIMP_DBUS_INTERFACE_PATH); + + service = gimp_dbus_service_new (gimp); + g_dbus_object_skeleton_add_interface (object, + G_DBUS_INTERFACE_SKELETON (service)); + g_object_unref (service); + + g_dbus_object_manager_server_export (dbus_manager, object); + g_object_unref (object); + + g_dbus_object_manager_server_set_connection (dbus_manager, connection); +} + +static void +gui_dbus_name_acquired (GDBusConnection *connection, + const gchar *name, + Gimp *gimp) +{ +} + +static void +gui_dbus_name_lost (GDBusConnection *connection, + const gchar *name, + Gimp *gimp) +{ + if (connection == NULL) + g_printerr ("%s: connection to the bus cannot be established.\n", + G_STRFUNC); + else + g_printerr ("%s: the name \"%s\" could not be acquired on the bus.\n", + G_STRFUNC, name); +} + +static void +gui_dbus_service_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (dbus_name_id == 0); + + dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, + GIMP_DBUS_SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + (GBusAcquiredCallback) gui_dbus_bus_acquired, + (GBusNameAcquiredCallback) gui_dbus_name_acquired, + (GBusNameLostCallback) gui_dbus_name_lost, + gimp, NULL); +} + +static void +gui_dbus_service_exit (void) +{ + g_bus_unown_name (dbus_name_id); + g_clear_object (&dbus_manager); +} + +#endif diff --git a/app/gui/gui-unique.h b/app/gui/gui-unique.h new file mode 100644 index 0000000..73b9231 --- /dev/null +++ b/app/gui/gui-unique.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GUI_UNIQUE_H__ +#define __GUI_UNIQUE_H__ + +#ifdef G_OS_WIN32 +#define GIMP_UNIQUE_WIN32_WINDOW_CLASS L"GimpWin32UniqueHandler" +#define GIMP_UNIQUE_WIN32_WINDOW_NAME L"GimpProxy" +#endif + + +void gui_unique_init (Gimp *gimp); +void gui_unique_exit (void); + + +#endif /* __GUI_UNIQUE_H__ */ diff --git a/app/gui/gui-vtable.c b/app/gui/gui-vtable.c new file mode 100644 index 0000000..28c999a --- /dev/null +++ b/app/gui/gui-vtable.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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#include + +#ifndef pipe +#define pipe(fds) _pipe(fds, 4096, _O_BINARY) +#endif +#else +#include +#include +#include +#include +#endif + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "gui-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimp-parallel.h" +#include "core/gimp-spawn.h" +#include "core/gimp-utils.h" +#include "core/gimpasync.h" +#include "core/gimpbrush.h" +#include "core/gimpcancelable.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimpimagefile.h" +#include "core/gimplist.h" +#include "core/gimppalette.h" +#include "core/gimppattern.h" +#include "core/gimpprogress.h" +#include "core/gimpwaitable.h" + +#include "text/gimpfont.h" + +#include "pdb/gimppdb.h" +#include "pdb/gimpprocedure.h" + +#include "plug-in/gimppluginmanager-file.h" + +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpbrushselect.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdocked.h" +#include "widgets/gimpfontselect.h" +#include "widgets/gimpgradientselect.h" +#include "widgets/gimphelp.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimppaletteselect.h" +#include "widgets/gimppatternselect.h" +#include "widgets/gimpprogressdialog.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpsinglewindowstrategy.h" +#include "display/gimpmultiwindowstrategy.h" + +#include "actions/plug-in-actions.h" + +#include "menus/menus.h" + +#include "dialogs/color-profile-import-dialog.h" + +#include "gui.h" +#include "gui-message.h" +#include "gui-vtable.h" +#include "icon-themes.h" +#include "themes.h" + + +/* local function prototypes */ + +static void gui_ungrab (Gimp *gimp); + +static void gui_threads_enter (Gimp *gimp); +static void gui_threads_leave (Gimp *gimp); + +static void gui_set_busy (Gimp *gimp); +static void gui_unset_busy (Gimp *gimp); + +static void gui_help (Gimp *gimp, + GimpProgress *progress, + const gchar *help_domain, + const gchar *help_id); +static const gchar * gui_get_program_class (Gimp *gimp); +static gchar * gui_get_display_name (Gimp *gimp, + gint display_ID, + GObject **screen, + gint *monitor); +static guint32 gui_get_user_time (Gimp *gimp); +static GFile * gui_get_theme_dir (Gimp *gimp); +static GFile * gui_get_icon_theme_dir (Gimp *gimp); +static GimpObject * gui_get_window_strategy (Gimp *gimp); +static GimpObject * gui_get_empty_display (Gimp *gimp); +static GimpObject * gui_display_get_by_ID (Gimp *gimp, + gint ID); +static gint gui_display_get_ID (GimpObject *display); +static guint32 gui_display_get_window_id (GimpObject *display); +static GimpObject * gui_display_create (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GObject *screen, + gint monitor); +static void gui_display_delete (GimpObject *display); +static void gui_displays_reconnect (Gimp *gimp, + GimpImage *old_image, + GimpImage *new_image); +static gboolean gui_wait (Gimp *gimp, + GimpWaitable *waitable, + const gchar *message); +static GimpProgress * gui_new_progress (Gimp *gimp, + GimpObject *display); +static void gui_free_progress (Gimp *gimp, + GimpProgress *progress); +static gboolean gui_pdb_dialog_new (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpContainer *container, + const gchar *title, + const gchar *callback_name, + const gchar *object_name, + va_list args); +static gboolean gui_pdb_dialog_set (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name, + const gchar *object_name, + va_list args); +static gboolean gui_pdb_dialog_close (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name); +static gboolean gui_recent_list_add_file (Gimp *gimp, + GFile *file, + const gchar *mime_type); +static void gui_recent_list_load (Gimp *gimp); + +static GMountOperation + * gui_get_mount_operation (Gimp *gimp, + GimpProgress *progress); + +static GimpColorProfilePolicy + gui_query_profile_policy (Gimp *gimp, + GimpImage *image, + GimpContext *context, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask); + + +/* public functions */ + +void +gui_vtable_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp->gui.ungrab = gui_ungrab; + gimp->gui.threads_enter = gui_threads_enter; + gimp->gui.threads_leave = gui_threads_leave; + gimp->gui.set_busy = gui_set_busy; + gimp->gui.unset_busy = gui_unset_busy; + gimp->gui.show_message = gui_message; + gimp->gui.help = gui_help; + gimp->gui.get_program_class = gui_get_program_class; + gimp->gui.get_display_name = gui_get_display_name; + gimp->gui.get_user_time = gui_get_user_time; + gimp->gui.get_theme_dir = gui_get_theme_dir; + gimp->gui.get_icon_theme_dir = gui_get_icon_theme_dir; + gimp->gui.get_window_strategy = gui_get_window_strategy; + gimp->gui.get_empty_display = gui_get_empty_display; + gimp->gui.display_get_by_id = gui_display_get_by_ID; + gimp->gui.display_get_id = gui_display_get_ID; + gimp->gui.display_get_window_id = gui_display_get_window_id; + gimp->gui.display_create = gui_display_create; + gimp->gui.display_delete = gui_display_delete; + gimp->gui.displays_reconnect = gui_displays_reconnect; + gimp->gui.wait = gui_wait; + gimp->gui.progress_new = gui_new_progress; + gimp->gui.progress_free = gui_free_progress; + gimp->gui.pdb_dialog_new = gui_pdb_dialog_new; + gimp->gui.pdb_dialog_set = gui_pdb_dialog_set; + gimp->gui.pdb_dialog_close = gui_pdb_dialog_close; + gimp->gui.recent_list_add_file = gui_recent_list_add_file; + gimp->gui.recent_list_load = gui_recent_list_load; + gimp->gui.get_mount_operation = gui_get_mount_operation; + gimp->gui.query_profile_policy = gui_query_profile_policy; +} + + +/* private functions */ + +static void +gui_ungrab (Gimp *gimp) +{ + GdkDisplay *display = gdk_display_get_default (); + + if (display) + { + gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); + gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); + } +} + +static void +gui_threads_enter (Gimp *gimp) +{ + GDK_THREADS_ENTER (); +} + +static void +gui_threads_leave (Gimp *gimp) +{ + GDK_THREADS_LEAVE (); +} + +static void +gui_set_busy (Gimp *gimp) +{ + gimp_displays_set_busy (gimp); + gimp_dialog_factory_set_busy (gimp_dialog_factory_get_singleton ()); + + gdk_flush (); +} + +static void +gui_unset_busy (Gimp *gimp) +{ + gimp_displays_unset_busy (gimp); + gimp_dialog_factory_unset_busy (gimp_dialog_factory_get_singleton ()); + + gdk_flush (); +} + +static void +gui_help (Gimp *gimp, + GimpProgress *progress, + const gchar *help_domain, + const gchar *help_id) +{ + gimp_help_show (gimp, progress, help_domain, help_id); +} + +static const gchar * +gui_get_program_class (Gimp *gimp) +{ + return gdk_get_program_class (); +} + +static gchar * +gui_get_display_name (Gimp *gimp, + gint display_ID, + GObject **screen, + gint *monitor) +{ + GimpDisplay *display = NULL; + GdkScreen *my_screen = NULL; + + if (display_ID > 0) + display = gimp_display_get_by_ID (gimp, display_ID); + + if (display) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (shell)); + + my_screen = gtk_widget_get_screen (GTK_WIDGET (shell)); + *monitor = gdk_screen_get_monitor_at_window (my_screen, window); + } + else + { + *monitor = gui_get_initial_monitor (gimp, &my_screen); + + if (*monitor == -1) + *monitor = gimp_get_monitor_at_pointer (&my_screen); + } + + *screen = G_OBJECT (my_screen); + + if (my_screen) + return gdk_screen_make_display_name (my_screen); + + return NULL; +} + +static guint32 +gui_get_user_time (Gimp *gimp) +{ +#ifdef GDK_WINDOWING_X11 + return gdk_x11_display_get_user_time (gdk_display_get_default ()); +#endif + return 0; +} + +static GFile * +gui_get_theme_dir (Gimp *gimp) +{ + return themes_get_theme_dir (gimp, GIMP_GUI_CONFIG (gimp->config)->theme); +} + +static GFile * +gui_get_icon_theme_dir (Gimp *gimp) +{ + return icon_themes_get_theme_dir (gimp, GIMP_GUI_CONFIG (gimp->config)->icon_theme); +} + +static GimpObject * +gui_get_window_strategy (Gimp *gimp) +{ + if (GIMP_GUI_CONFIG (gimp->config)->single_window_mode) + return gimp_single_window_strategy_get_singleton (); + else + return gimp_multi_window_strategy_get_singleton (); +} + +static GimpObject * +gui_get_empty_display (Gimp *gimp) +{ + GimpObject *display = NULL; + + if (gimp_container_get_n_children (gimp->displays) == 1) + { + display = gimp_container_get_first_child (gimp->displays); + + if (gimp_display_get_image (GIMP_DISPLAY (display))) + { + /* The display was not empty */ + display = NULL; + } + } + + return display; +} + +static GimpObject * +gui_display_get_by_ID (Gimp *gimp, + gint ID) +{ + return (GimpObject *) gimp_display_get_by_ID (gimp, ID); +} + +static gint +gui_display_get_ID (GimpObject *display) +{ + return gimp_display_get_ID (GIMP_DISPLAY (display)); +} + +static guint32 +gui_display_get_window_id (GimpObject *display) +{ + GimpDisplay *disp = GIMP_DISPLAY (display); + GimpDisplayShell *shell = gimp_display_get_shell (disp); + + if (shell) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + + if (GTK_IS_WINDOW (toplevel)) + return gimp_window_get_native_id (GTK_WINDOW (toplevel)); + } + + return 0; +} + +static GimpObject * +gui_display_create (Gimp *gimp, + GimpImage *image, + GimpUnit unit, + gdouble scale, + GObject *screen, + gint monitor) +{ + GimpContext *context = gimp_get_user_context (gimp); + GimpDisplay *display = GIMP_DISPLAY (gui_get_empty_display (gimp)); + + if (! screen) + monitor = gimp_get_monitor_at_pointer ((GdkScreen **) &screen); + + if (display) + { + gimp_display_fill (display, image, unit, scale); + } + else + { + GList *image_managers = gimp_ui_managers_from_name (""); + + g_return_val_if_fail (image_managers != NULL, NULL); + + display = gimp_display_new (gimp, image, unit, scale, + image_managers->data, + gimp_dialog_factory_get_singleton (), + GDK_SCREEN (screen), + monitor); + } + + if (gimp_context_get_display (context) == display) + { + gimp_context_set_image (context, image); + gimp_context_display_changed (context); + } + else + { + gimp_context_set_display (context, display); + } + + return GIMP_OBJECT (display); +} + +static void +gui_display_delete (GimpObject *display) +{ + gimp_display_close (GIMP_DISPLAY (display)); +} + +static void +gui_displays_reconnect (Gimp *gimp, + GimpImage *old_image, + GimpImage *new_image) +{ + gimp_displays_reconnect (gimp, old_image, new_image); +} + +static void +gui_wait_input_async (GimpAsync *async, + const gint input_pipe[2]) +{ + guint8 buffer[1]; + + while (read (input_pipe[0], buffer, sizeof (buffer)) == -1 && + errno == EINTR); + + gimp_async_finish (async, NULL); +} + +static gboolean +gui_wait (Gimp *gimp, + GimpWaitable *waitable, + const gchar *message) +{ + GimpProcedure *procedure; + GimpValueArray *args; + GimpAsync *input_async = NULL; + GError *error = NULL; + gint input_pipe[2]; + gint output_pipe[2]; + + procedure = gimp_pdb_lookup_procedure (gimp->pdb, "plug-in-busy-dialog"); + + if (! procedure) + return FALSE; + + if (pipe (input_pipe)) + return FALSE; + + if (pipe (output_pipe)) + { + close (input_pipe[0]); + close (input_pipe[1]); + + return FALSE; + } + + gimp_spawn_set_cloexec (input_pipe[0]); + gimp_spawn_set_cloexec (output_pipe[1]); + + args = gimp_procedure_get_arguments (procedure); + gimp_value_array_truncate (args, 5); + + g_value_set_int (gimp_value_array_index (args, 0), + GIMP_RUN_INTERACTIVE); + g_value_set_int (gimp_value_array_index (args, 1), + output_pipe[0]); + g_value_set_int (gimp_value_array_index (args, 2), + input_pipe[1]); + g_value_set_string (gimp_value_array_index (args, 3), + message); + g_value_set_int (gimp_value_array_index (args, 4), + GIMP_IS_CANCELABLE (waitable)); + + gimp_procedure_execute_async (procedure, gimp, + gimp_get_user_context (gimp), + NULL, args, NULL, &error); + + gimp_value_array_unref (args); + + close (input_pipe[1]); + close (output_pipe[0]); + + if (error) + { + g_clear_error (&error); + + close (input_pipe[0]); + close (output_pipe[1]); + + return FALSE; + } + + if (GIMP_IS_CANCELABLE (waitable)) + { + /* listens for a cancellation request */ + input_async = gimp_parallel_run_async_independent ( + (GimpRunAsyncFunc) gui_wait_input_async, + input_pipe); + + while (! gimp_waitable_wait_for (waitable, 0.1 * G_TIME_SPAN_SECOND)) + { + /* check for a cancellation request */ + if (gimp_waitable_try_wait (GIMP_WAITABLE (input_async))) + { + gimp_cancelable_cancel (GIMP_CANCELABLE (waitable)); + + break; + } + } + } + + gimp_waitable_wait (waitable); + + /* signal completion to the plug-in */ + close (output_pipe[1]); + + if (input_async) + { + gimp_waitable_wait (GIMP_WAITABLE (input_async)); + + g_object_unref (input_async); + } + + close (input_pipe[0]); + + return TRUE; +} + +static GimpProgress * +gui_new_progress (Gimp *gimp, + GimpObject *display) +{ + g_return_val_if_fail (display == NULL || GIMP_IS_DISPLAY (display), NULL); + + if (display) + return GIMP_PROGRESS (display); + + return GIMP_PROGRESS (gimp_progress_dialog_new ()); +} + +static void +gui_free_progress (Gimp *gimp, + GimpProgress *progress) +{ + g_return_if_fail (GIMP_IS_PROGRESS_DIALOG (progress)); + + if (GIMP_IS_PROGRESS_DIALOG (progress)) + gtk_widget_destroy (GTK_WIDGET (progress)); +} + +static gboolean +gui_pdb_dialog_present (GtkWindow *window) +{ + gtk_window_present (window); + + return FALSE; +} + +static gboolean +gui_pdb_dialog_new (Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + GimpContainer *container, + const gchar *title, + const gchar *callback_name, + const gchar *object_name, + va_list args) +{ + GType dialog_type = G_TYPE_NONE; + const gchar *dialog_role = NULL; + const gchar *help_id = NULL; + + if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH) + { + dialog_type = GIMP_TYPE_BRUSH_SELECT; + dialog_role = "gimp-brush-selection"; + help_id = GIMP_HELP_BRUSH_DIALOG; + } + else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT) + { + dialog_type = GIMP_TYPE_FONT_SELECT; + dialog_role = "gimp-font-selection"; + help_id = GIMP_HELP_FONT_DIALOG; + } + else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT) + { + dialog_type = GIMP_TYPE_GRADIENT_SELECT; + dialog_role = "gimp-gradient-selection"; + help_id = GIMP_HELP_GRADIENT_DIALOG; + } + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE) + { + dialog_type = GIMP_TYPE_PALETTE_SELECT; + dialog_role = "gimp-palette-selection"; + help_id = GIMP_HELP_PALETTE_DIALOG; + } + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN) + { + dialog_type = GIMP_TYPE_PATTERN_SELECT; + dialog_role = "gimp-pattern-selection"; + help_id = GIMP_HELP_PATTERN_DIALOG; + } + + if (dialog_type != G_TYPE_NONE) + { + GimpObject *object = NULL; + + if (object_name && strlen (object_name)) + object = gimp_container_get_child_by_name (container, object_name); + + if (! object) + object = gimp_context_get_by_type (context, + gimp_container_get_children_type (container)); + + if (object) + { + gint n_properties = 0; + gchar **names = NULL; + GValue *values = NULL; + GtkWidget *dialog; + GtkWidget *view; + + names = gimp_properties_append (dialog_type, + &n_properties, names, &values, + "title", title, + "role", dialog_role, + "help-func", gimp_standard_help_func, + "help-id", help_id, + "pdb", gimp->pdb, + "context", context, + "select-type", gimp_container_get_children_type (container), + "initial-object", object, + "callback-name", callback_name, + "menu-factory", global_menu_factory, + NULL); + + names = gimp_properties_append_valist (dialog_type, + &n_properties, names, &values, + args); + + dialog = (GtkWidget *) + g_object_new_with_properties (dialog_type, + n_properties, + (const gchar **) names, + (const GValue *) values); + + gimp_properties_free (n_properties, names, values); + + view = GIMP_PDB_DIALOG (dialog)->view; + if (view) + gimp_docked_set_show_button_bar (GIMP_DOCKED (view), FALSE); + + if (progress) + { + guint32 window_id = gimp_progress_get_window_id (progress); + + if (window_id) + gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); + } + + gtk_widget_show (dialog); + + /* workaround for bug #360106 */ + { + GSource *source = g_timeout_source_new (100); + GClosure *closure; + + closure = g_cclosure_new_object (G_CALLBACK (gui_pdb_dialog_present), + G_OBJECT (dialog)); + + g_source_set_closure (source, closure); + g_source_attach (source, NULL); + g_source_unref (source); + } + + return TRUE; + } + } + + return FALSE; +} + +static gboolean +gui_pdb_dialog_set (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name, + const gchar *object_name, + va_list args) +{ + GimpPdbDialogClass *klass = NULL; + + if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH) + klass = g_type_class_peek (GIMP_TYPE_BRUSH_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT) + klass = g_type_class_peek (GIMP_TYPE_FONT_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT) + klass = g_type_class_peek (GIMP_TYPE_GRADIENT_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE) + klass = g_type_class_peek (GIMP_TYPE_PALETTE_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN) + klass = g_type_class_peek (GIMP_TYPE_PATTERN_SELECT); + + if (klass) + { + GimpPdbDialog *dialog; + + dialog = gimp_pdb_dialog_get_by_callback (klass, callback_name); + + if (dialog && dialog->select_type == gimp_container_get_children_type (container)) + { + GimpObject *object; + + object = gimp_container_get_child_by_name (container, object_name); + + if (object) + { + const gchar *prop_name = va_arg (args, const gchar *); + + gimp_context_set_by_type (dialog->context, dialog->select_type, + object); + + if (prop_name) + g_object_set_valist (G_OBJECT (dialog), prop_name, args); + + gtk_window_present (GTK_WINDOW (dialog)); + + return TRUE; + } + } + } + + return FALSE; +} + +static gboolean +gui_pdb_dialog_close (Gimp *gimp, + GimpContainer *container, + const gchar *callback_name) +{ + GimpPdbDialogClass *klass = NULL; + + if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH) + klass = g_type_class_peek (GIMP_TYPE_BRUSH_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT) + klass = g_type_class_peek (GIMP_TYPE_FONT_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT) + klass = g_type_class_peek (GIMP_TYPE_GRADIENT_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE) + klass = g_type_class_peek (GIMP_TYPE_PALETTE_SELECT); + else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN) + klass = g_type_class_peek (GIMP_TYPE_PATTERN_SELECT); + + if (klass) + { + GimpPdbDialog *dialog; + + dialog = gimp_pdb_dialog_get_by_callback (klass, callback_name); + + if (dialog && dialog->select_type == gimp_container_get_children_type (container)) + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + return TRUE; + } + } + + return FALSE; +} + +static gboolean +gui_recent_list_add_file (Gimp *gimp, + GFile *file, + const gchar *mime_type) +{ + GtkRecentData recent; + const gchar *groups[2] = { "Graphics", NULL }; + gchar *uri; + gboolean success; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + + /* use last part of the URI */ + recent.display_name = NULL; + + /* no special description */ + recent.description = NULL; + recent.mime_type = (mime_type ? + (gchar *) mime_type : "application/octet-stream"); + recent.app_name = "GNU Image Manipulation Program"; + recent.app_exec = GIMP_COMMAND " %u"; + recent.groups = (gchar **) groups; + recent.is_private = FALSE; + + uri = g_file_get_uri (file); + + success = gtk_recent_manager_add_full (gtk_recent_manager_get_default (), + uri, &recent); + + g_free (uri); + + return success; +} + +static gint +gui_recent_list_compare (gconstpointer a, + gconstpointer b) +{ + return (gtk_recent_info_get_modified ((GtkRecentInfo *) a) - + gtk_recent_info_get_modified ((GtkRecentInfo *) b)); +} + +static void +gui_recent_list_load (Gimp *gimp) +{ + GList *items; + GList *list; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_container_freeze (gimp->documents); + gimp_container_clear (gimp->documents); + + items = gtk_recent_manager_get_items (gtk_recent_manager_get_default ()); + + items = g_list_sort (items, gui_recent_list_compare); + + for (list = items; list; list = list->next) + { + GtkRecentInfo *info = list->data; + + if (gtk_recent_info_has_application (info, + "GNU Image Manipulation Program")) + { + const gchar *mime_type = gtk_recent_info_get_mime_type (info); + + if (mime_type && + gimp_plug_in_manager_file_procedure_find_by_mime_type (gimp->plug_in_manager, + GIMP_FILE_PROCEDURE_GROUP_OPEN, + mime_type)) + { + GimpImagefile *imagefile; + GFile *file; + + file = g_file_new_for_uri (gtk_recent_info_get_uri (info)); + imagefile = gimp_imagefile_new (gimp, file); + g_object_unref (file); + + gimp_imagefile_set_mime_type (imagefile, mime_type); + + gimp_container_add (gimp->documents, GIMP_OBJECT (imagefile)); + g_object_unref (imagefile); + } + } + + gtk_recent_info_unref (info); + } + + g_list_free (items); + + gimp_container_thaw (gimp->documents); +} + +static GMountOperation * +gui_get_mount_operation (Gimp *gimp, + GimpProgress *progress) +{ + GtkWidget *toplevel = NULL; + + if (GTK_IS_WIDGET (progress)) + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress)); + + return gtk_mount_operation_new (GTK_WINDOW (toplevel)); +} + +static GimpColorProfilePolicy +gui_query_profile_policy (Gimp *gimp, + GimpImage *image, + GimpContext *context, + GimpColorProfile **dest_profile, + GimpColorRenderingIntent *intent, + gboolean *bpc, + gboolean *dont_ask) +{ + return color_profile_import_dialog_run (image, context, NULL, + dest_profile, + intent, bpc, + dont_ask); +} diff --git a/app/gui/gui-vtable.h b/app/gui/gui-vtable.h new file mode 100644 index 0000000..e52009b --- /dev/null +++ b/app/gui/gui-vtable.h @@ -0,0 +1,31 @@ +/* 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 . + */ + +#ifndef __GUI_VTABLE_H__ +#define __GUI_VTABLE_H__ + + +void gui_vtable_init (Gimp *gimp); + +/* this function lives in gui.c but must only be used from gui-vtable.c; + * also, gui.h can't contain any Gdk types. + */ +gint gui_get_initial_monitor (Gimp *gimp, + GdkScreen **screen); + + +#endif /* __GUI_VTABLE_H__ */ diff --git a/app/gui/gui.c b/app/gui/gui.c new file mode 100644 index 0000000..e5928eb --- /dev/null +++ b/app/gui/gui.c @@ -0,0 +1,1077 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpwidgets/gimpwidgets.h" +#include "libgimpwidgets/gimpwidgets-private.h" + +#include "gui-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpcontainer.h" +#include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "core/gimptoolinfo.h" + +#include "plug-in/gimpenvirontable.h" +#include "plug-in/gimppluginmanager.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplay-foreach.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpstatusbar.h" + +#include "tools/gimp-tools.h" +#include "tools/gimptool.h" +#include "tools/tool_manager.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactiongroup.h" +#include "widgets/gimpaction-history.h" +#include "widgets/gimpclipboard.h" +#include "widgets/gimpcolorselectorpalette.h" +#include "widgets/gimpcontrollers.h" +#include "widgets/gimpdevices.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdnd.h" +#include "widgets/gimprender.h" +#include "widgets/gimphelp.h" +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpmenufactory.h" +#include "widgets/gimpmessagebox.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpuimanager.h" +#include "widgets/gimpwidgets-utils.h" +#include "widgets/gimplanguagestore-parser.h" + +#include "actions/actions.h" +#include "actions/windows-commands.h" + +#include "menus/menus.h" + +#include "dialogs/dialogs.h" + +#include "gimpuiconfigurer.h" +#include "gui.h" +#include "gui-unique.h" +#include "gui-vtable.h" +#include "icon-themes.h" +#include "session.h" +#include "splash.h" +#include "themes.h" + +#ifdef GDK_WINDOWING_QUARTZ +#import +#include +#include + +/* Forward declare since we are building against old SDKs. */ +#if !defined(MAC_OS_X_VERSION_10_12) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 + +@interface NSWindow(ForwardDeclarations) ++ (void)setAllowsAutomaticWindowTabbing:(BOOL)allow; +@end + +#endif + +#endif /* GDK_WINDOWING_QUARTZ */ + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static gchar * gui_sanity_check (void); +static void gui_help_func (const gchar *help_id, + gpointer help_data); +static gboolean gui_get_background_func (GimpRGB *color); +static gboolean gui_get_foreground_func (GimpRGB *color); + +static void gui_initialize_after_callback (Gimp *gimp, + GimpInitStatusFunc callback); + +static void gui_restore_callback (Gimp *gimp, + GimpInitStatusFunc callback); +static void gui_restore_after_callback (Gimp *gimp, + GimpInitStatusFunc callback); + +static gboolean gui_exit_callback (Gimp *gimp, + gboolean force); +static gboolean gui_exit_after_callback (Gimp *gimp, + gboolean force); + +static void gui_show_tooltips_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + Gimp *gimp); +static void gui_show_help_button_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + Gimp *gimp); +static void gui_user_manual_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + Gimp *gimp); +static void gui_single_window_mode_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + GimpUIConfigurer *ui_configurer); +static void gui_tearoff_menus_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + GtkUIManager *manager); + +static void gui_clipboard_changed (Gimp *gimp); + +static void gui_menu_show_tooltip (GimpUIManager *manager, + const gchar *tooltip, + Gimp *gimp); +static void gui_menu_hide_tooltip (GimpUIManager *manager, + Gimp *gimp); + +static void gui_display_changed (GimpContext *context, + GimpDisplay *display, + Gimp *gimp); + +static void gui_compare_accelerator (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed); +static void gui_check_unique_accelerator (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed); +static gboolean gui_check_action_exists (const gchar *accel_path); + + +/* private variables */ + +static Gimp *the_gui_gimp = NULL; +static GimpUIManager *image_ui_manager = NULL; +static GimpUIConfigurer *ui_configurer = NULL; +static GdkScreen *initial_screen = NULL; +static gint initial_monitor = -1; + + +/* public functions */ + +void +gui_libs_init (GOptionContext *context) +{ + g_return_if_fail (context != NULL); + + g_option_context_add_group (context, gtk_get_option_group (TRUE)); +} + +void +gui_abort (const gchar *abort_message) +{ + GtkWidget *dialog; + GtkWidget *box; + + g_return_if_fail (abort_message != NULL); + + dialog = gimp_dialog_new (_("GIMP Message"), "gimp-abort", + NULL, GTK_DIALOG_MODAL, NULL, NULL, + + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + box = g_object_new (GIMP_TYPE_MESSAGE_BOX, + "icon-name", GIMP_ICON_WILBER_EEK, + "border-width", 12, + NULL); + + gimp_message_box_set_text (GIMP_MESSAGE_BOX (box), "%s", abort_message); + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + box, TRUE, TRUE, 0); + gtk_widget_show (box); + + gimp_dialog_run (GIMP_DIALOG (dialog)); + + exit (EXIT_FAILURE); +} + +GimpInitStatusFunc +gui_init (Gimp *gimp, + gboolean no_splash) +{ + GimpInitStatusFunc status_callback = NULL; + gchar *abort_message; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (the_gui_gimp == NULL, NULL); + + abort_message = gui_sanity_check (); + if (abort_message) + gui_abort (abort_message); + + the_gui_gimp = gimp; + + /* TRANSLATORS: there is no need to translate this in GIMP. This uses + * "gtk20" domain as a special trick to determine language direction, + * but xgettext extracts it anyway mistakenly into GIMP po files. + * Leave an empty string as translation. It does not matter. + */ + if (g_strcmp0 (dgettext ("gtk20", "default:LTR"), "default:RTL") == 0) + /* Normally this should have been taken care of during command line + * parsing as a post-parse hook of gtk_get_option_group(), using the + * system locales. + * But user config may have overridden the language, therefore we must + * check the widget directions again. + */ + gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL); + else + gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR); + + gui_unique_init (gimp); + gimp_language_store_parser_init (); + + /* initialize icon themes before gimp_widgets_init() so we avoid + * setting the configured theme twice + */ + icon_themes_init (gimp); + + gimp_widgets_init (gui_help_func, + gui_get_foreground_func, + gui_get_background_func, + NULL); + + g_type_class_ref (GIMP_TYPE_COLOR_SELECT); + + /* disable automatic startup notification */ + gtk_window_set_auto_startup_notification (FALSE); + +#ifdef GDK_WINDOWING_QUARTZ + /* Before the first window is created (typically the splash window), + * we need to disable automatic tabbing behavior introduced on Sierra. + * This is known to cause all kinds of weird issues (see for instance + * Bugzilla #776294) and needs proper GTK+ support if we would want to + * enable it. + */ + if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)]) + [NSWindow setAllowsAutomaticWindowTabbing:NO]; + + /* MacOS 11 (Big Sur) has added a new, dynamic "accent" as default. + * This uses a 10-bit colorspace so every GIMP drawing operation + * has the additional cost of an 8-bit (ARGB) to 10-bit conversion. + * Let's disable this mode to regain the lost performance. + */ + if (gdk_quartz_osx_version () >= GDK_OSX_BIG_SUR) + { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + [userDefaults setBool: NO forKey:@"NSViewUsesAutomaticLayerBackingStores"]; + } + +#endif /* GDK_WINDOWING_QUARTZ */ + + gimp_dnd_init (gimp); + + themes_init (gimp); + + initial_monitor = gimp_get_monitor_at_pointer (&initial_screen); + gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (initial_screen)); + + if (! no_splash) + { + splash_create (gimp->be_verbose, initial_screen, initial_monitor); + status_callback = splash_update; + } + + g_signal_connect_after (gimp, "initialize", + G_CALLBACK (gui_initialize_after_callback), + NULL); + + g_signal_connect (gimp, "restore", + G_CALLBACK (gui_restore_callback), + NULL); + g_signal_connect_after (gimp, "restore", + G_CALLBACK (gui_restore_after_callback), + NULL); + + g_signal_connect (gimp, "exit", + G_CALLBACK (gui_exit_callback), + NULL); + g_signal_connect_after (gimp, "exit", + G_CALLBACK (gui_exit_after_callback), + NULL); + + return status_callback; +} + +/* + * gui_recover: + * @n_recoveries: number of recovered files. + * + * Query the user interactively if files were saved from a previous + * crash, asking whether to try and recover or discard them. + * + * Returns: TRUE if answer is to try and recover, FALSE otherwise. + */ +gboolean +gui_recover (gint n_recoveries) +{ + GtkWidget *dialog; + GtkWidget *box; + gboolean recover; + + dialog = gimp_dialog_new (_("Image Recovery"), "gimp-recovery", + NULL, GTK_DIALOG_MODAL, NULL, NULL, + _("_Discard"), GTK_RESPONSE_CANCEL, + _("_Recover"), GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_OK); + + box = gimp_message_box_new (GIMP_ICON_WILBER_EEK); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + box, TRUE, TRUE, 0); + gtk_widget_show (box); + + gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (box), + _("Eeek! It looks like GIMP recovered from a crash!")); + + gimp_message_box_set_text (GIMP_MESSAGE_BOX (box), + /* TRANSLATORS: even if English singular form does + * not use %d, you can use %d for translation in + * any singular/plural form of your language if + * suited. It will just work and be replaced by the + * number of images as expected. + */ + ngettext ("An image was salvaged from the crash. " + "Do you want to try and recover it?", + "%d images were salvaged from the crash. " + "Do you want to try and recover them?", + n_recoveries), n_recoveries); + + recover = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); + gtk_widget_destroy (dialog); + + return recover; +} + +gint +gui_get_initial_monitor (Gimp *gimp, + GdkScreen **screen) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0); + g_return_val_if_fail (screen != NULL, 0); + + *screen = initial_screen; + + return initial_monitor; +} + + +/* private functions */ + +static gchar * +gui_sanity_check (void) +{ +#define GTK_REQUIRED_MAJOR 2 +#define GTK_REQUIRED_MINOR 24 +#define GTK_REQUIRED_MICRO 10 + + const gchar *mismatch = gtk_check_version (GTK_REQUIRED_MAJOR, + GTK_REQUIRED_MINOR, + GTK_REQUIRED_MICRO); + + if (mismatch) + { + return g_strdup_printf + ("%s\n\n" + "GIMP requires GTK+ version %d.%d.%d or later.\n" + "Installed GTK+ version is %d.%d.%d.\n\n" + "Somehow you or your software packager managed\n" + "to install GIMP with an older GTK+ version.\n\n" + "Please upgrade to GTK+ version %d.%d.%d or later.", + mismatch, + GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO, + gtk_major_version, gtk_minor_version, gtk_micro_version, + GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO); + } + +#undef GTK_REQUIRED_MAJOR +#undef GTK_REQUIRED_MINOR +#undef GTK_REQUIRED_MICRO + + return NULL; +} + +static void +gui_help_func (const gchar *help_id, + gpointer help_data) +{ + g_return_if_fail (GIMP_IS_GIMP (the_gui_gimp)); + + gimp_help (the_gui_gimp, NULL, NULL, help_id); +} + +static gboolean +gui_get_foreground_func (GimpRGB *color) +{ + g_return_val_if_fail (color != NULL, FALSE); + g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE); + + gimp_context_get_foreground (gimp_get_user_context (the_gui_gimp), color); + + return TRUE; +} + +static gboolean +gui_get_background_func (GimpRGB *color) +{ + g_return_val_if_fail (color != NULL, FALSE); + g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE); + + gimp_context_get_background (gimp_get_user_context (the_gui_gimp), color); + + return TRUE; +} + +static void +gui_initialize_after_callback (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + const gchar *name = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + +#if defined (GDK_WINDOWING_X11) + name = "DISPLAY"; +#elif defined (GDK_WINDOWING_DIRECTFB) || defined (GDK_WINDOWING_FB) + name = "GDK_DISPLAY"; +#endif + + /* TODO: Need to care about display migration with GTK+ 2.2 at some point */ + + if (name) + { + gchar *display = gdk_get_display (); + + gimp_environ_table_add (gimp->plug_in_manager->environ_table, + name, display, NULL); + g_free (display); + } + + gimp_tools_init (gimp); + + gimp_context_set_tool (gimp_get_user_context (gimp), + gimp_tool_info_get_standard (gimp)); +} + +static void +gui_restore_callback (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (gimp->config); + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config); + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + gui_vtable_init (gimp); + + if (! gui_config->show_tooltips) + gimp_help_disable_tooltips (); + + g_signal_connect (gui_config, "notify::show-tooltips", + G_CALLBACK (gui_show_tooltips_notify), + gimp); + + gimp_dialogs_show_help_button (gui_config->use_help && + gui_config->show_help_button); + + g_signal_connect (gui_config, "notify::use-help", + G_CALLBACK (gui_show_help_button_notify), + gimp); + g_signal_connect (gui_config, "notify::user-manual-online", + G_CALLBACK (gui_user_manual_notify), + gimp); + g_signal_connect (gui_config, "notify::show-help-button", + G_CALLBACK (gui_show_help_button_notify), + gimp); + + g_signal_connect (gimp_get_user_context (gimp), "display-changed", + G_CALLBACK (gui_display_changed), + gimp); + + /* make sure the monitor resolution is valid */ + if (display_config->monitor_res_from_gdk || + display_config->monitor_xres < GIMP_MIN_RESOLUTION || + display_config->monitor_yres < GIMP_MIN_RESOLUTION) + { + gdouble xres, yres; + + gimp_get_monitor_resolution (initial_screen, + initial_monitor, + &xres, &yres); + + g_object_set (gimp->config, + "monitor-xresolution", xres, + "monitor-yresolution", yres, + "monitor-resolution-from-windowing-system", TRUE, + NULL); + } + + actions_init (gimp); + menus_init (gimp, global_action_factory); + gimp_render_init (gimp); + + dialogs_init (gimp, global_menu_factory); + + gimp_clipboard_init (gimp); + if (gimp_get_clipboard_image (gimp)) + gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp)); + else + gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp)); + + g_signal_connect (gimp, "clipboard-changed", + G_CALLBACK (gui_clipboard_changed), + NULL); + + gimp_devices_init (gimp); + gimp_controllers_init (gimp); + session_init (gimp); + + g_type_class_unref (g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_PALETTE)); + + status_callback (NULL, _("Tool Options"), 1.0); + gimp_tools_restore (gimp); +} + +#ifdef GDK_WINDOWING_QUARTZ +static void +gui_add_to_app_menu (GimpUIManager *ui_manager, + GtkosxApplication *osx_app, + const gchar *action_path, + gint index) +{ + GtkWidget *item; + + item = gimp_ui_manager_get_widget (ui_manager, action_path); + + if (GTK_IS_MENU_ITEM (item)) + gtkosx_application_insert_app_menu_item (osx_app, GTK_WIDGET (item), index); +} + +static gboolean +gui_quartz_quit_callback (GtkosxApplication *osx_app, + GimpUIManager *ui_manager) +{ + gimp_ui_manager_activate_action (ui_manager, "file", "file-quit"); + + return TRUE; +} +#endif + +static void +gui_restore_after_callback (Gimp *gimp, + GimpInitStatusFunc status_callback) +{ + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config); + GimpDisplay *display; + + if (gimp->be_verbose) + g_print ("INIT: %s\n", G_STRFUNC); + + gimp->message_handler = GIMP_MESSAGE_BOX; + + /* load the recent documents after gimp_real_restore() because we + * need the mime-types implemented by plug-ins + */ + status_callback (NULL, _("Documents"), 0.9); + gimp_recent_list_load (gimp); + + /* enable this to always have icons everywhere */ + if (g_getenv ("GIMP_ICONS_LIKE_A_BOSS")) + { + GdkScreen *screen = gdk_screen_get_default (); + + g_object_set (G_OBJECT (gtk_settings_get_for_screen (screen)), + "gtk-button-images", TRUE, + "gtk-menu-images", TRUE, + NULL); + } + + if (gui_config->restore_accels) + menus_restore (gimp); + + ui_configurer = g_object_new (GIMP_TYPE_UI_CONFIGURER, + "gimp", gimp, + NULL); + + image_ui_manager = gimp_menu_factory_manager_new (global_menu_factory, + "", + gimp, + gui_config->tearoff_menus); + gimp_ui_manager_update (image_ui_manager, gimp); + + /* Check that every accelerator is unique. */ + gtk_accel_map_foreach_unfiltered (NULL, + gui_check_unique_accelerator); + + gimp_action_history_init (gimp); + + g_signal_connect_object (gui_config, "notify::single-window-mode", + G_CALLBACK (gui_single_window_mode_notify), + ui_configurer, 0); + g_signal_connect_object (gui_config, "notify::tearoff-menus", + G_CALLBACK (gui_tearoff_menus_notify), + image_ui_manager, 0); + g_signal_connect (image_ui_manager, "show-tooltip", + G_CALLBACK (gui_menu_show_tooltip), + gimp); + g_signal_connect (image_ui_manager, "hide-tooltip", + G_CALLBACK (gui_menu_hide_tooltip), + gimp); + + gimp_devices_restore (gimp); + gimp_controllers_restore (gimp, image_ui_manager); + + if (status_callback == splash_update) + splash_destroy (); + + if (gimp_get_show_gui (gimp)) + { + GimpDisplayShell *shell; + GtkWidget *toplevel; + + /* create the empty display */ + display = GIMP_DISPLAY (gimp_create_display (gimp, NULL, + GIMP_UNIT_PIXEL, 1.0, + G_OBJECT (initial_screen), + initial_monitor)); + + shell = gimp_display_get_shell (display); + + if (gui_config->restore_session) + session_restore (gimp, + initial_screen, + initial_monitor); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); + +#ifdef GDK_WINDOWING_QUARTZ + { + GtkosxApplication *osx_app; + GtkWidget *menu; + GtkWidget *item; + + [[NSUserDefaults standardUserDefaults] setObject:@"NO" + forKey:@"NSTreatUnknownArgumentsAsOpen"]; + + osx_app = gtkosx_application_get (); + + menu = gimp_ui_manager_get_widget (image_ui_manager, + "/image-menubar"); + /* menu should have window parent for accelerator support */ + gtk_widget_set_parent(menu, toplevel); + + if (GTK_IS_MENU_ITEM (menu)) + menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu)); + + /* do not activate OSX menu if tests are running */ + if (! g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR")) + gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu)); + + gtkosx_application_set_use_quartz_accelerators (osx_app, FALSE); + + gui_add_to_app_menu (image_ui_manager, osx_app, + "/image-menubar/Help/dialogs-about", 0); + gui_add_to_app_menu (image_ui_manager, osx_app, + "/image-menubar/Help/dialogs-search-action", 1); + +#define PREFERENCES "/image-menubar/Edit/Preferences/" + + gui_add_to_app_menu (image_ui_manager, osx_app, + PREFERENCES "dialogs-preferences", 3); + gui_add_to_app_menu (image_ui_manager, osx_app, + PREFERENCES "dialogs-input-devices", 4); + gui_add_to_app_menu (image_ui_manager, osx_app, + PREFERENCES "dialogs-keyboard-shortcuts", 5); + gui_add_to_app_menu (image_ui_manager, osx_app, + PREFERENCES "dialogs-module-dialog", 6); + gui_add_to_app_menu (image_ui_manager, osx_app, + PREFERENCES "plug-in-unit-editor", 7); + +#undef PREFERENCES + + item = gtk_separator_menu_item_new (); + gtkosx_application_insert_app_menu_item (osx_app, item, 8); + + item = gimp_ui_manager_get_widget (image_ui_manager, + "/image-menubar/File/file-quit"); + gtk_widget_hide (item); + + g_signal_connect (osx_app, "NSApplicationBlockTermination", + G_CALLBACK (gui_quartz_quit_callback), + image_ui_manager); + + gtkosx_application_ready (osx_app); + } +#endif /* GDK_WINDOWING_QUARTZ */ + + /* move keyboard focus to the display */ + gtk_window_present (GTK_WINDOW (toplevel)); + } + + /* indicate that the application has finished loading */ + gdk_notify_startup_complete (); + + /* clear startup monitor variables */ + initial_screen = NULL; + initial_monitor = -1; +} + +static gboolean +gui_exit_callback (Gimp *gimp, + gboolean force) +{ + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config); + GimpTool *active_tool; + + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + if (! force && gimp_displays_dirty (gimp)) + { + GdkScreen *screen; + gint monitor; + + monitor = gimp_get_monitor_at_pointer (&screen); + + gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (), + screen, monitor, + "gimp-quit-dialog", -1); + + return TRUE; /* stop exit for now */ + } + + gimp->message_handler = GIMP_CONSOLE; + + gui_unique_exit (); + + /* If any modifier is set when quitting (typically when exiting with + * Ctrl-q for instance!), when serializing the tool options, it will + * save any alternate value instead of the main one. Make sure that + * any modifier is reset before saving options. + */ + active_tool = tool_manager_get_active (gimp); + if (active_tool && active_tool->focus_display) + gimp_tool_set_modifier_state (active_tool, 0, active_tool->focus_display); + + if (gui_config->save_session_info) + session_save (gimp, FALSE); + + if (gui_config->save_device_status) + gimp_devices_save (gimp, FALSE); + + if (TRUE /* gui_config->save_controllers */) + gimp_controllers_save (gimp); + + g_signal_handlers_disconnect_by_func (gimp_get_user_context (gimp), + gui_display_changed, + gimp); + + gimp_displays_delete (gimp); + + if (gui_config->save_accels) + menus_save (gimp, FALSE); + + gimp_tools_save (gimp, gui_config->save_tool_options, FALSE); + gimp_tools_exit (gimp); + + gimp_language_store_parser_clean (); + + return FALSE; /* continue exiting */ +} + +static gboolean +gui_exit_after_callback (Gimp *gimp, + gboolean force) +{ + if (gimp->be_verbose) + g_print ("EXIT: %s\n", G_STRFUNC); + + g_signal_handlers_disconnect_by_func (gimp->config, + gui_show_help_button_notify, + gimp); + g_signal_handlers_disconnect_by_func (gimp->config, + gui_user_manual_notify, + gimp); + g_signal_handlers_disconnect_by_func (gimp->config, + gui_show_tooltips_notify, + gimp); + + gimp_action_history_exit (gimp); + + g_object_unref (image_ui_manager); + image_ui_manager = NULL; + + g_object_unref (ui_configurer); + ui_configurer = NULL; + + /* exit the clipboard before shutting down the GUI because it runs + * a whole lot of code paths. See bug #731389. + */ + g_signal_handlers_disconnect_by_func (gimp, + G_CALLBACK (gui_clipboard_changed), + NULL); + gimp_clipboard_exit (gimp); + + session_exit (gimp); + menus_exit (gimp); + actions_exit (gimp); + gimp_render_exit (gimp); + + gimp_controllers_exit (gimp); + gimp_devices_exit (gimp); + dialogs_exit (gimp); + themes_exit (gimp); + + g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT)); + + return FALSE; /* continue exiting */ +} + +static void +gui_show_tooltips_notify (GimpGuiConfig *gui_config, + GParamSpec *param_spec, + Gimp *gimp) +{ + if (gui_config->show_tooltips) + gimp_help_enable_tooltips (); + else + gimp_help_disable_tooltips (); +} + +static void +gui_show_help_button_notify (GimpGuiConfig *gui_config, + GParamSpec *param_spec, + Gimp *gimp) +{ + gimp_dialogs_show_help_button (gui_config->use_help && + gui_config->show_help_button); +} + +static void +gui_user_manual_notify (GimpGuiConfig *gui_config, + GParamSpec *param_spec, + Gimp *gimp) +{ + gimp_help_user_manual_changed (gimp); +} + +static void +gui_single_window_mode_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + GimpUIConfigurer *ui_configurer) +{ + gimp_ui_configurer_configure (ui_configurer, + gui_config->single_window_mode); +} +static void +gui_tearoff_menus_notify (GimpGuiConfig *gui_config, + GParamSpec *pspec, + GtkUIManager *manager) +{ + gtk_ui_manager_set_add_tearoffs (manager, gui_config->tearoff_menus); +} + +static void +gui_clipboard_changed (Gimp *gimp) +{ + if (gimp_get_clipboard_image (gimp)) + gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp)); + else + gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp)); +} + +static void +gui_menu_show_tooltip (GimpUIManager *manager, + const gchar *tooltip, + Gimp *gimp) +{ + GimpContext *context = gimp_get_user_context (gimp); + GimpDisplay *display = gimp_context_get_display (context); + + if (display) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_push (statusbar, "menu-tooltip", + NULL, "%s", tooltip); + } +} + +static void +gui_menu_hide_tooltip (GimpUIManager *manager, + Gimp *gimp) +{ + GimpContext *context = gimp_get_user_context (gimp); + GimpDisplay *display = gimp_context_get_display (context); + + if (display) + { + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell); + + gimp_statusbar_pop (statusbar, "menu-tooltip"); + } +} + +static void +gui_display_changed (GimpContext *context, + GimpDisplay *display, + Gimp *gimp) +{ + if (! display) + { + GimpImage *image = gimp_context_get_image (context); + + if (image) + { + GList *list; + + for (list = gimp_get_display_iter (gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display2 = list->data; + + if (gimp_display_get_image (display2) == image) + { + gimp_context_set_display (context, display2); + + /* stop the emission of the original signal + * (the emission of the recursive signal is finished) + */ + g_signal_stop_emission_by_name (context, "display-changed"); + return; + } + } + + gimp_context_set_image (context, NULL); + } + } + + gimp_ui_manager_update (image_ui_manager, display); +} + +typedef struct +{ + const gchar *path; + guint key; + GdkModifierType mods; +} +accelData; + +static void +gui_compare_accelerator (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed) +{ + accelData *accel = data; + + if (accel->key == accel_key && accel->mods == accel_mods && + g_strcmp0 (accel->path, accel_path)) + { + g_printerr ("Actions \"%s\" and \"%s\" use the same accelerator.\n" + " Disabling the accelerator on \"%s\".\n", + accel->path, accel_path, accel_path); + gtk_accel_map_change_entry (accel_path, 0, 0, FALSE); + } +} + +static void +gui_check_unique_accelerator (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed) +{ + if (gtk_accelerator_valid (accel_key, accel_mods) && + gui_check_action_exists (accel_path)) + { + accelData accel; + + accel.path = accel_path; + accel.key = accel_key; + accel.mods = accel_mods; + + gtk_accel_map_foreach_unfiltered (&accel, + gui_compare_accelerator); + } +} + +static gboolean +gui_check_action_exists (const gchar *accel_path) +{ + GimpUIManager *manager; + gboolean action_exists = FALSE; + GList *list; + + manager = gimp_ui_managers_from_name ("")->data; + + for (list = gimp_ui_manager_get_action_groups (manager); + list; + list = g_list_next (list)) + { + GimpActionGroup *group = list->data; + GList *actions = NULL; + GList *list2; + + actions = gimp_action_group_list_actions (group); + + for (list2 = actions; list2; list2 = g_list_next (list2)) + { + GimpAction *action = list2->data; + const gchar *path = gimp_action_get_accel_path (action); + + if (g_strcmp0 (path, accel_path) == 0) + { + action_exists = TRUE; + break; + } + } + + g_list_free (actions); + + if (action_exists) + break; + } + + return action_exists; +} diff --git a/app/gui/gui.h b/app/gui/gui.h new file mode 100644 index 0000000..4b0435d --- /dev/null +++ b/app/gui/gui.h @@ -0,0 +1,30 @@ +/* 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 . + */ + +#ifndef __GUI_H__ +#define __GUI_H__ + + +void gui_libs_init (GOptionContext *context); +void gui_abort (const gchar *abort_message); + +GimpInitStatusFunc gui_init (Gimp *gimp, + gboolean no_splash); + +gboolean gui_recover (gint n_recoveries); + +#endif /* __GUI_H__ */ diff --git a/app/gui/icon-themes.c b/app/gui/icon-themes.c new file mode 100644 index 0000000..27c39eb --- /dev/null +++ b/app/gui/icon-themes.c @@ -0,0 +1,250 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * icon-themes.c + * Copyright (C) 2015 Benoit 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "gui-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "icon-themes.h" + +#include "gimp-intl.h" + + +static void icons_apply_theme (Gimp *gimp, + const gchar *icon_theme_name); +static void icons_list_icons_foreach (gpointer key, + gpointer value, + gpointer data); +static gint icons_name_compare (const void *p1, + const void *p2); +static void icons_theme_change_notify (GimpGuiConfig *config, + GParamSpec *pspec, + Gimp *gimp); + + +static GHashTable *icon_themes_hash = NULL; + + +void +icon_themes_init (Gimp *gimp) +{ + GimpGuiConfig *config; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + config = GIMP_GUI_CONFIG (gimp->config); + + icon_themes_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + if (config->icon_theme_path) + { + GList *path; + GList *list; + + path = gimp_config_path_expand_to_files (config->icon_theme_path, NULL); + + for (list = path; list; list = g_list_next (list)) + { + GFile *dir = list->data; + GFileEnumerator *enumerator; + + enumerator = + g_file_enumerate_children (dir, + 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))) + { + if (! g_file_info_get_is_hidden (info) && + g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + { + GFile *file; + GFile *index_theme; + + file = g_file_enumerator_get_child (enumerator, info); + + /* make sure there is a hicolor/index.theme file */ + index_theme = g_file_get_child (file, "index.theme"); + + if (g_file_query_exists (index_theme, NULL)) + { + const gchar *name; + gchar *basename; + + name = gimp_file_get_utf8_name (file); + basename = g_path_get_basename (name); + + if (strcmp ("hicolor", basename)) + { + if (gimp->be_verbose) + g_print ("Adding icon theme '%s' (%s)\n", + basename, name); + + g_hash_table_insert (icon_themes_hash, basename, + g_object_ref (file)); + } + else + { + g_free (basename); + } + } + + g_object_unref (index_theme); + g_object_unref (file); + } + + g_object_unref (info); + } + + g_object_unref (enumerator); + } + } + + g_list_free_full (path, (GDestroyNotify) g_object_unref); + } + + g_signal_connect (config, "notify::icon-theme", + G_CALLBACK (icons_theme_change_notify), + gimp); + + icons_theme_change_notify (config, NULL, gimp); +} + +void +icon_themes_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (icon_themes_hash) + { + g_signal_handlers_disconnect_by_func (gimp->config, + icons_theme_change_notify, + gimp); + + g_hash_table_destroy (icon_themes_hash); + icon_themes_hash = NULL; + } +} + +gchar ** +icon_themes_list_themes (Gimp *gimp, + gint *n_icon_themes) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (n_icon_themes != NULL, NULL); + + *n_icon_themes = g_hash_table_size (icon_themes_hash); + + if (*n_icon_themes > 0) + { + gchar **icon_themes; + gchar **index; + + icon_themes = g_new0 (gchar *, *n_icon_themes + 1); + + index = icon_themes; + + g_hash_table_foreach (icon_themes_hash, icons_list_icons_foreach, &index); + + qsort (icon_themes, *n_icon_themes, sizeof (gchar *), icons_name_compare); + + return icon_themes; + } + + return NULL; +} + +GFile * +icon_themes_get_theme_dir (Gimp *gimp, + const gchar *icon_theme_name) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (! icon_theme_name) + icon_theme_name = GIMP_CONFIG_DEFAULT_ICON_THEME; + + return g_hash_table_lookup (icon_themes_hash, icon_theme_name); +} + +static void +icons_apply_theme (Gimp *gimp, + const gchar *icon_theme_name) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (! icon_theme_name) + icon_theme_name = GIMP_CONFIG_DEFAULT_ICON_THEME; + + if (gimp->be_verbose) + g_print ("Loading icon theme '%s'\n", icon_theme_name); + + gimp_icons_set_icon_theme (icon_themes_get_theme_dir (gimp, icon_theme_name)); +} + +static void +icons_list_icons_foreach (gpointer key, + gpointer value, + gpointer data) +{ + gchar ***index = data; + + **index = g_strdup ((gchar *) key); + + (*index)++; +} + +static gint +icons_name_compare (const void *p1, + const void *p2) +{ + return strcmp (* (char **) p1, * (char **) p2); +} + +static void +icons_theme_change_notify (GimpGuiConfig *config, + GParamSpec *pspec, + Gimp *gimp) +{ + icons_apply_theme (gimp, config->icon_theme); +} diff --git a/app/gui/icon-themes.h b/app/gui/icon-themes.h new file mode 100644 index 0000000..14ecef9 --- /dev/null +++ b/app/gui/icon-themes.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * icon-themes.h + * Copyright (C) 2015 Benoit 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 . + */ + +#ifndef __ICONS_THEMES_H__ +#define __ICONS_THEMES_H__ + + +void icon_themes_init (Gimp *gimp); +void icon_themes_exit (Gimp *gimp); + +gchar ** icon_themes_list_themes (Gimp *gimp, + gint *n_themes); +GFile * icon_themes_get_theme_dir (Gimp *gimp, + const gchar *theme_name); + + +#endif /* __ICONS_THEMES_H__ */ diff --git a/app/gui/session.c b/app/gui/session.c new file mode 100644 index 0000000..cfbc1c8 --- /dev/null +++ b/app/gui/session.c @@ -0,0 +1,486 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Session-managment stuff + * Copyright (C) 1998 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "gui-types.h" + +#include "config/gimpconfig-file.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimperror.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpwidgets-utils.h" + +#include "dialogs/dialogs.h" + +#include "session.h" +#include "gimp-log.h" + +#include "gimp-intl.h" + + +enum +{ + SESSION_INFO = 1, + HIDE_DOCKS, + SINGLE_WINDOW_MODE, + SHOW_TABS, + TABS_POSITION, + LAST_TIP_SHOWN +}; + + +static GFile * session_file (Gimp *gimp); + + +/* private variables */ + +static gboolean sessionrc_deleted = FALSE; + + +/* public functions */ + +void +session_init (Gimp *gimp) +{ + GFile *file; + GScanner *scanner; + GTokenType token; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + file = session_file (gimp); + + scanner = gimp_scanner_new_gfile (file, &error); + + if (! scanner && error->code == GIMP_CONFIG_ERROR_OPEN_ENOENT) + { + g_clear_error (&error); + g_object_unref (file); + + file = gimp_sysconf_directory_file ("sessionrc", NULL); + + scanner = gimp_scanner_new_gfile (file, NULL); + } + + if (! scanner) + { + g_clear_error (&error); + g_object_unref (file); + return; + } + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + g_scanner_scope_add_symbol (scanner, 0, "session-info", + GINT_TO_POINTER (SESSION_INFO)); + g_scanner_scope_add_symbol (scanner, 0, "hide-docks", + GINT_TO_POINTER (HIDE_DOCKS)); + g_scanner_scope_add_symbol (scanner, 0, "single-window-mode", + GINT_TO_POINTER (SINGLE_WINDOW_MODE)); + g_scanner_scope_add_symbol (scanner, 0, "show-tabs", + GINT_TO_POINTER (SHOW_TABS)); + g_scanner_scope_add_symbol (scanner, 0, "tabs-position", + GINT_TO_POINTER (TABS_POSITION)); + g_scanner_scope_add_symbol (scanner, 0, "last-tip-shown", + GINT_TO_POINTER (LAST_TIP_SHOWN)); + + token = G_TOKEN_LEFT_PAREN; + + while (g_scanner_peek_next_token (scanner) == token) + { + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_SYMBOL: + if (scanner->value.v_symbol == GINT_TO_POINTER (SESSION_INFO)) + { + GimpDialogFactory *factory = NULL; + GimpSessionInfo *info = NULL; + gchar *factory_name = NULL; + gchar *entry_name = NULL; + GimpDialogFactoryEntry *entry = NULL; + + token = G_TOKEN_STRING; + + if (! gimp_scanner_parse_string (scanner, &factory_name)) + break; + + /* In versions <= GIMP 2.6 there was a "toolbox", a + * "dock", a "display" and a "toplevel" factory. These + * are now merged to a single gimp_dialog_factory_get_singleton (). We + * need the legacy name though, so keep it around. + */ + factory = gimp_dialog_factory_get_singleton (); + + info = gimp_session_info_new (); + + /* GIMP 2.6 has the entry name as part of the + * session-info header, so try to get it + */ + gimp_scanner_parse_string (scanner, &entry_name); + if (entry_name) + { + /* Previously, GimpDock was a toplevel. That is why + * versions <= GIMP 2.6 has "dock" as the entry name. We + * want "dock" to be interpreted as 'dock window' + * however so have some special-casing for that. When + * the entry name is "dock" the factory name is either + * "dock" or "toolbox". + */ + if (strcmp (entry_name, "dock") == 0) + { + entry = + gimp_dialog_factory_find_entry (factory, + (strcmp (factory_name, "toolbox") == 0 ? + "gimp-toolbox-window" : + "gimp-dock-window")); + } + else + { + entry = gimp_dialog_factory_find_entry (factory, + entry_name); + } + } + + /* We're done with these now */ + g_free (factory_name); + g_free (entry_name); + + /* We can get the factory entry either now (the GIMP <= + * 2.6 way), or when we deserialize (the GIMP 2.8 way) + */ + if (entry) + { + gimp_session_info_set_factory_entry (info, entry); + } + + /* Always try to deserialize */ + if (gimp_config_deserialize (GIMP_CONFIG (info), scanner, 1, NULL)) + { + /* Make sure we got a factory entry either the 2.6 + * or 2.8 way + */ + if (gimp_session_info_get_factory_entry (info)) + { + GIMP_LOG (DIALOG_FACTORY, + "successfully parsed and added session info %p", + info); + + gimp_dialog_factory_add_session_info (factory, info); + } + else + { + GIMP_LOG (DIALOG_FACTORY, + "failed to parse session info %p, not adding", + info); + } + + g_object_unref (info); + } + else + { + g_object_unref (info); + + /* set token to left paren to we won't set another + * error below, gimp_config_deserialize() already did + */ + token = G_TOKEN_LEFT_PAREN; + goto error; + } + } + else if (scanner->value.v_symbol == GINT_TO_POINTER (HIDE_DOCKS)) + { + gboolean hide_docks; + + token = G_TOKEN_IDENTIFIER; + + if (! gimp_scanner_parse_boolean (scanner, &hide_docks)) + break; + + g_object_set (gimp->config, + "hide-docks", hide_docks, + NULL); + } + else if (scanner->value.v_symbol == GINT_TO_POINTER (SINGLE_WINDOW_MODE)) + { + gboolean single_window_mode; + + token = G_TOKEN_IDENTIFIER; + + if (! gimp_scanner_parse_boolean (scanner, &single_window_mode)) + break; + + g_object_set (gimp->config, + "single-window-mode", single_window_mode, + NULL); + } + else if (scanner->value.v_symbol == GINT_TO_POINTER (SHOW_TABS)) + { + gboolean show_tabs; + + token = G_TOKEN_IDENTIFIER; + + if (! gimp_scanner_parse_boolean (scanner, &show_tabs)) + break; + + g_object_set (gimp->config, + "show-tabs", show_tabs, + NULL); + } + else if (scanner->value.v_symbol == GINT_TO_POINTER (TABS_POSITION)) + { + gint tabs_position; + + token = G_TOKEN_INT; + + if (! gimp_scanner_parse_int (scanner, &tabs_position)) + break; + + g_object_set (gimp->config, + "tabs-position", tabs_position, + NULL); + } + else if (scanner->value.v_symbol == GINT_TO_POINTER (LAST_TIP_SHOWN)) + { + gint last_tip_shown; + + token = G_TOKEN_INT; + + if (! gimp_scanner_parse_int (scanner, &last_tip_shown)) + break; + + g_object_set (gimp->config, + "last-tip-shown", last_tip_shown, + NULL); + } + token = G_TOKEN_RIGHT_PAREN; + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + error: + + if (token != G_TOKEN_LEFT_PAREN) + { + g_scanner_get_next_token (scanner); + g_scanner_unexp_token (scanner, token, NULL, NULL, NULL, + _("fatal parse error"), TRUE); + } + + if (error) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + + gimp_config_file_backup_on_error (file, "sessionrc", NULL); + } + + gimp_scanner_destroy (scanner); + g_object_unref (file); + + dialogs_load_recent_docks (gimp); +} + +void +session_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); +} + +void +session_restore (Gimp *gimp, + GdkScreen *screen, + gint monitor) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GDK_IS_SCREEN (screen)); + + gimp_dialog_factory_restore (gimp_dialog_factory_get_singleton (), + screen, monitor); + + /* make sure GimpImageWindow acts upon hide-docks at the right time, + * see bug #678043. + */ + if (GIMP_GUI_CONFIG (gimp->config)->single_window_mode && + GIMP_GUI_CONFIG (gimp->config)->hide_docks) + { + g_object_notify (G_OBJECT (gimp->config), "hide-docks"); + } +} + +void +session_save (Gimp *gimp, + gboolean always_save) +{ + GimpConfigWriter *writer; + GFile *file; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (sessionrc_deleted && ! always_save) + return; + + file = session_file (gimp); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + writer = + gimp_config_writer_new_gfile (file, + TRUE, + "GIMP sessionrc\n\n" + "This file takes session-specific info " + "(that is info, you want to keep between " + "two GIMP sessions). You are not supposed " + "to edit it manually, but of course you " + "can do. The sessionrc will be entirely " + "rewritten every time you quit GIMP. " + "If this file isn't found, defaults are " + "used.", + NULL); + g_object_unref (file); + + if (!writer) + return; + + gimp_dialog_factory_save (gimp_dialog_factory_get_singleton (), writer); + gimp_config_writer_linefeed (writer); + + gimp_config_writer_open (writer, "hide-docks"); + gimp_config_writer_identifier (writer, + GIMP_GUI_CONFIG (gimp->config)->hide_docks ? + "yes" : "no"); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "single-window-mode"); + gimp_config_writer_identifier (writer, + GIMP_GUI_CONFIG (gimp->config)->single_window_mode ? + "yes" : "no"); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "show-tabs"); + gimp_config_writer_printf (writer, + GIMP_GUI_CONFIG (gimp->config)->show_tabs ? + "yes" : "no"); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "tabs-position"); + gimp_config_writer_printf (writer, "%d", + GIMP_GUI_CONFIG (gimp->config)->tabs_position); + gimp_config_writer_close (writer); + + gimp_config_writer_open (writer, "last-tip-shown"); + gimp_config_writer_printf (writer, "%d", + GIMP_GUI_CONFIG (gimp->config)->last_tip_shown); + gimp_config_writer_close (writer); + + if (! gimp_config_writer_finish (writer, "end of sessionrc", &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } + + dialogs_save_recent_docks (gimp); + + sessionrc_deleted = FALSE; +} + +gboolean +session_clear (Gimp *gimp, + GError **error) +{ + GFile *file; + GError *my_error = NULL; + gboolean success = TRUE; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = session_file (gimp); + + if (! g_file_delete (file, NULL, &my_error) && + my_error->code != G_IO_ERROR_NOT_FOUND) + { + success = FALSE; + + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + _("Deleting \"%s\" failed: %s"), + gimp_file_get_utf8_name (file), my_error->message); + } + else + { + sessionrc_deleted = TRUE; + } + + g_clear_error (&my_error); + g_object_unref (file); + + return success; +} + + +static GFile * +session_file (Gimp *gimp) +{ + const gchar *basename; + gchar *filename; + GFile *file; + + basename = g_getenv ("GIMP_TESTING_SESSIONRC_NAME"); + if (! basename) + basename = "sessionrc"; + + if (gimp->session_name) + filename = g_strconcat (basename, ".", gimp->session_name, NULL); + else + filename = g_strdup (basename); + + file = gimp_directory_file (filename, NULL); + + g_free (filename); + + return file; +} diff --git a/app/gui/session.h b/app/gui/session.h new file mode 100644 index 0000000..aff1145 --- /dev/null +++ b/app/gui/session.h @@ -0,0 +1,35 @@ +/* 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 . + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + + +void session_init (Gimp *gimp); +void session_exit (Gimp *gimp); + +void session_restore (Gimp *gimp, + GdkScreen *screen, + gint monitor); +void session_save (Gimp *gimp, + gboolean always_save); + +gboolean session_clear (Gimp *gimp, + GError **error); + + +#endif /* __SESSION_H__ */ diff --git a/app/gui/splash.c b/app/gui/splash.c new file mode 100644 index 0000000..aec664e --- /dev/null +++ b/app/gui/splash.c @@ -0,0 +1,736 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "gui-types.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "splash.h" + +#include "gimp-intl.h" + + +#define MEASURE_UPPER "1235678901234567890" +#define MEASURE_LOWER "12356789012345678901234567890" + + +typedef struct +{ + GtkWidget *window; + GtkWidget *area; + gint width; + gint height; + GtkWidget *progress; + GdkColor color; + PangoLayout *upper; + gint upper_x; + gint upper_y; + PangoLayout *lower; + gint lower_x; + gint lower_y; + + gdouble percentage; + gchar *text1; + gchar *text2; + + /* debug timer */ + GTimer *timer; + gdouble last_time; +} GimpSplash; + +static GimpSplash *splash = NULL; + + +static void splash_position_layouts (GimpSplash *splash, + const gchar *text1, + const gchar *text2, + GdkRectangle *area); +static gboolean splash_area_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpSplash *splash); +static void splash_rectangle_union (GdkRectangle *dest, + PangoRectangle *pango_rect, + gint offset_x, + gint offset_y); +static gboolean splash_average_text_area (GimpSplash *splash, + GdkPixbuf *pixbuf, + GdkColor *color); + +static GdkPixbufAnimation * + splash_image_load (gint max_width, + gint max_height, + gboolean be_verbose); +static GdkPixbufAnimation * + splash_image_load_from_path (const gchar *filename, + gint max_width, + gint max_height, + gboolean be_verbose); +static GdkPixbufAnimation * + splash_image_load_from_file (GFile *file, + gint max_width, + gint max_height, + gboolean be_verbose); +static GdkPixbufAnimation * + splash_image_pick_from_dirs (GList *dirs, + gint max_width, + gint max_height, + gboolean be_verbose); + +static void splash_timer_elapsed (void); + + +/* public functions */ + +void +splash_create (gboolean be_verbose, + GdkScreen *screen, + gint monitor) +{ + GtkWidget *frame; + GtkWidget *vbox; + GdkPixbufAnimation *pixbuf; + PangoRectangle ink; + gint max_width; + gint max_height; + + g_return_if_fail (splash == NULL); + g_return_if_fail (GDK_IS_SCREEN (screen)); + + max_width = gdk_screen_get_width (screen) / 2; + max_height = gdk_screen_get_height (screen) / 2; + pixbuf = splash_image_load (max_width, max_height, be_verbose); + + if (! pixbuf) + return; + + splash = g_slice_new0 (GimpSplash); + + splash->window = + g_object_new (GTK_TYPE_WINDOW, + "type", GTK_WINDOW_TOPLEVEL, + "type-hint", GDK_WINDOW_TYPE_HINT_SPLASHSCREEN, + "title", _("GIMP Startup"), + "role", "gimp-startup", + "screen", screen, + "window-position", GTK_WIN_POS_CENTER, + "resizable", FALSE, + NULL); + + /* Don't remove this call, it's necessary to remove decorations on Windows + * (which is the natural state of splash-screens). Looks like the + * GDK_WINDOW_TYPE_HINT_SPLASHSCREEN hint is not used on some platforms. + */ + gtk_window_set_decorated (GTK_WINDOW (splash->window), FALSE); + + g_signal_connect_swapped (splash->window, "delete-event", + G_CALLBACK (exit), + GINT_TO_POINTER (0)); + + splash->width = MIN (gdk_pixbuf_animation_get_width (pixbuf), + gdk_screen_get_width (screen)); + splash->height = MIN (gdk_pixbuf_animation_get_height (pixbuf), + gdk_screen_get_height (screen)); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_container_add (GTK_CONTAINER (splash->window), frame); + gtk_widget_show (frame); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + splash->area = gtk_image_new_from_animation (pixbuf); + gtk_box_pack_start (GTK_BOX (vbox), splash->area, TRUE, TRUE, 0); + gtk_widget_show (splash->area); + + gtk_widget_set_size_request (splash->area, splash->width, splash->height); + + /* create the pango layouts */ + splash->upper = gtk_widget_create_pango_layout (splash->area, + MEASURE_UPPER); + pango_layout_get_pixel_extents (splash->upper, &ink, NULL); + + if (splash->width > 4 * ink.width) + gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_X_LARGE); + else if (splash->width > 3 * ink.width) + gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_LARGE); + else if (splash->width > 2 * ink.width) + gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_MEDIUM); + else + gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_SMALL); + + splash->lower = gtk_widget_create_pango_layout (splash->area, + MEASURE_LOWER); + pango_layout_get_pixel_extents (splash->lower, &ink, NULL); + + if (splash->width > 4 * ink.width) + gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_LARGE); + else if (splash->width > 3 * ink.width) + gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_MEDIUM); + else if (splash->width > 2 * ink.width) + gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_SMALL); + else + gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_X_SMALL); + + /* this sets the initial layout positions */ + splash_position_layouts (splash, "", "", NULL); + + splash_average_text_area (splash, + gdk_pixbuf_animation_get_static_image (pixbuf), + &splash->color); + + g_object_unref (pixbuf); + + g_signal_connect_after (splash->area, "expose-event", + G_CALLBACK (splash_area_expose), + splash); + + /* add a progress bar */ + splash->progress = gtk_progress_bar_new (); + gtk_box_pack_end (GTK_BOX (vbox), splash->progress, FALSE, FALSE, 0); + gtk_widget_show (splash->progress); + + gtk_widget_show (splash->window); + + if (FALSE) + splash->timer = g_timer_new (); +} + +void +splash_destroy (void) +{ + if (! splash) + return; + + gtk_widget_destroy (splash->window); + + g_object_unref (splash->upper); + g_object_unref (splash->lower); + + g_free (splash->text1); + g_free (splash->text2); + + if (splash->timer) + g_timer_destroy (splash->timer); + + g_slice_free (GimpSplash, splash); + splash = NULL; +} + +void +splash_update (const gchar *text1, + const gchar *text2, + gdouble percentage) +{ + static GdkRectangle prev_expose = { 0, 0, 0, 0 }; + GdkRectangle expose = { 0, 0, 0, 0 }; + + g_return_if_fail (percentage >= 0.0 && percentage <= 1.0); + + if (! splash) + return; + + splash_position_layouts (splash, text1, text2, &expose); + gdk_rectangle_union (&expose, &prev_expose, &expose); + + if (expose.width > 0 && expose.height > 0) + gtk_widget_queue_draw_area (splash->area, + expose.x, expose.y, + expose.width, expose.height); + + prev_expose = expose; + + if ((text1 == NULL || ! g_strcmp0 (text1, splash->text1)) && + (text2 == NULL || ! g_strcmp0 (text2, splash->text2)) && + percentage == splash->percentage) + { + if (text1) + { + g_free (splash->text1); + splash->text1 = g_strdup (text1); + } + + if (text2) + { + g_free (splash->text2); + splash->text2 = g_strdup (text2); + } + + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (splash->progress)); + } + else + { + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (splash->progress), + percentage); + } + + splash->percentage = percentage; + + if (splash->timer) + splash_timer_elapsed (); + + if (gtk_events_pending ()) + gtk_main_iteration (); +} + + +/* private functions */ + +static gboolean +splash_area_expose (GtkWidget *widget, + GdkEventExpose *event, + GimpSplash *splash) +{ + cairo_t *cr = gdk_cairo_create (event->window); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + gdk_cairo_set_source_color (cr, &splash->color); + + cairo_move_to (cr, splash->upper_x, splash->upper_y); + pango_cairo_show_layout (cr, splash->upper); + + cairo_move_to (cr, splash->lower_x, splash->lower_y); + pango_cairo_show_layout (cr, splash->lower); + + cairo_destroy (cr); + + return FALSE; +} + +/* area returns the union of the previous and new ink rectangles */ +static void +splash_position_layouts (GimpSplash *splash, + const gchar *text1, + const gchar *text2, + GdkRectangle *area) +{ + PangoRectangle upper_ink; + PangoRectangle lower_ink; + gint text_height = 0; + + if (text1) + { + pango_layout_get_pixel_extents (splash->upper, &upper_ink, NULL); + + if (area) + splash_rectangle_union (area, &upper_ink, + splash->upper_x, splash->upper_y); + + pango_layout_set_text (splash->upper, text1, -1); + pango_layout_get_pixel_extents (splash->upper, + &upper_ink, NULL); + + splash->upper_x = (splash->width - upper_ink.width) / 2; + text_height += upper_ink.height; + } + + if (text2) + { + pango_layout_get_pixel_extents (splash->lower, &lower_ink, NULL); + + if (area) + splash_rectangle_union (area, &lower_ink, + splash->lower_x, splash->lower_y); + + pango_layout_set_text (splash->lower, text2, -1); + pango_layout_get_pixel_extents (splash->lower, + &lower_ink, NULL); + + splash->lower_x = (splash->width - lower_ink.width) / 2; + text_height += lower_ink.height; + } + + /* For pretty printing, let's say we want at least double space. */ + text_height *= 2; + + /* The ordinates are computed in 2 steps, because we are first + * checking the minimal height needed for text (text_height). + * + * Ideally we are printing in the bottom quarter of the splash image, + * with well centered positions. But if this zone appears to be too + * small, we will end up using this previously computed text_height + * instead. Since splash images are designed to have text in the lower + * quarter, this may end up a bit uglier, but at least top and bottom + * texts won't overlay each other. + */ + if (text1) + { + splash->upper_y = MIN (splash->height - text_height, + splash->height * 13 / 16 - + upper_ink.height / 2); + + if (area) + splash_rectangle_union (area, &upper_ink, + splash->upper_x, splash->upper_y); + } + + if (text2) + { + splash->lower_y = ((splash->height + splash->upper_y) / 2 - + lower_ink.height / 2); + + if (area) + splash_rectangle_union (area, &lower_ink, + splash->lower_x, splash->lower_y); + } +} + +static void +splash_rectangle_union (GdkRectangle *dest, + PangoRectangle *pango_rect, + gint offset_x, + gint offset_y) +{ + GdkRectangle rect; + + rect.x = pango_rect->x + offset_x; + rect.y = pango_rect->y + offset_y; + rect.width = pango_rect->width; + rect.height = pango_rect->height; + + if (dest->width > 0 && dest->height > 0) + gdk_rectangle_union (dest, &rect, dest); + else + *dest = rect; +} + +/* This function chooses a gray value for the text color, based on + * the average luminance of the text area of the splash image. + */ +static gboolean +splash_average_text_area (GimpSplash *splash, + GdkPixbuf *pixbuf, + GdkColor *color) +{ + const guchar *pixels; + gint rowstride; + gint channels; + gint luminance = 0; + guint sum[3] = { 0, 0, 0 }; + GdkRectangle image = { 0, 0, 0, 0 }; + GdkRectangle area = { 0, 0, 0, 0 }; + + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); + g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8, FALSE); + + image.width = gdk_pixbuf_get_width (pixbuf); + image.height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + channels = gdk_pixbuf_get_n_channels (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + splash_position_layouts (splash, MEASURE_UPPER, MEASURE_LOWER, &area); + splash_position_layouts (splash, "", "", NULL); + + if (gdk_rectangle_intersect (&image, &area, &area)) + { + const gint count = area.width * area.height; + gint x, y; + + pixels += area.x * channels; + pixels += area.y * rowstride; + + for (y = 0; y < area.height; y++) + { + const guchar *src = pixels; + + for (x = 0; x < area.width; x++) + { + sum[0] += src[0]; + sum[1] += src[1]; + sum[2] += src[2]; + + src += channels; + } + + pixels += rowstride; + } + + luminance = GIMP_RGB_LUMINANCE (sum[0] / count, + sum[1] / count, + sum[2] / count); + + luminance = CLAMP0255 (luminance > 127 ? + luminance - 223 : luminance + 223); + + } + + color->red = color->green = color->blue = (luminance << 8 | luminance); + + return gdk_colormap_alloc_color (gtk_widget_get_colormap (splash->area), + color, FALSE, TRUE); +} + +static GdkPixbufAnimation * +splash_image_load (gint max_width, + gint max_height, + gboolean be_verbose) +{ + GdkPixbufAnimation *animation = NULL; + gchar *filename; + GFile *file; + GList *list; + + /* File "gimp-splash.png" in personal configuration directory. */ + filename = gimp_personal_rc_file ("gimp-splash.png"); + animation = splash_image_load_from_path (filename, + max_width, max_height, + be_verbose); + g_free (filename); + if (animation) + return animation; + + /* Random image under splashes/ directory in personal config dir. */ + filename = gimp_personal_rc_file ("splashes"); + file = g_file_new_for_path (filename); + g_free (filename); + list = NULL; + list = g_list_prepend (list, file); + animation = splash_image_pick_from_dirs (list, + max_width, max_height, + be_verbose); + g_list_free_full (list, g_object_unref); + if (animation) + return animation; + + /* Release splash image. */ + filename = g_build_filename (gimp_data_directory (), + "images", "gimp-splash.png", NULL); + animation = splash_image_load_from_path (filename, + max_width, max_height, + be_verbose); + g_free (filename); + if (animation) + return animation; + + /* Random release image in installed splashes/ directory. */ + filename = g_build_filename (gimp_data_directory (), "splashes", NULL); + file = g_file_new_for_path (filename); + g_free (filename); + list = NULL; + list = g_list_prepend (list, file); + animation = splash_image_pick_from_dirs (list, + max_width, max_height, + be_verbose); + g_list_free_full (list, g_object_unref); + + return animation; +} + +static GdkPixbufAnimation * +splash_image_load_from_path (const gchar *filename, + gint max_width, + gint max_height, + gboolean be_verbose) +{ + GdkPixbufAnimation *animation; + GFile *file; + + file = g_file_new_for_path (filename); + animation = splash_image_load_from_file (file, + max_width, max_height, + be_verbose); + g_object_unref (file); + + return animation; +} + +static GdkPixbufAnimation * +splash_image_load_from_file (GFile *file, + gint max_width, + gint max_height, + gboolean be_verbose) +{ + GdkPixbufAnimation *animation = NULL; + GFileInfo *info; + GFileInputStream *input; + gboolean is_svg = FALSE; + + if (be_verbose) + { + gchar *path; + + path = g_file_get_path (file); + + g_printerr ("Trying splash '%s' ... ", path); + + g_free (path); + } + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, NULL, NULL); + if (info) + { + const gchar *content_type; + + content_type = g_file_info_get_content_type (info); + if (content_type) + { + gchar *mime_type; + + mime_type = g_content_type_get_mime_type (content_type); + if (mime_type) + { + if (g_strcmp0 (mime_type, "image/svg+xml") == 0) + { + /* We want to treat vector images differently than + * pixel images. We only scale down bitmaps, but we + * don't try to scale them up. + * On the other hand, we can always scale down and up + * vector images so that they end up in an ideal size + * in all cases. + */ + is_svg = TRUE; + } + g_free (mime_type); + } + } + g_object_unref (info); + } + + input = g_file_read (file, NULL, NULL); + if (input) + { + animation = gdk_pixbuf_animation_new_from_stream (G_INPUT_STREAM (input), + NULL, NULL); + g_object_unref (input); + } + + /* FIXME Right now, we only try to scale static images. + * Animated images may end up bigger than the expected max dimensions. + */ + if (animation && gdk_pixbuf_animation_is_static_image (animation) && + (gdk_pixbuf_animation_get_width (animation) > max_width || + gdk_pixbuf_animation_get_height (animation) > max_height || + is_svg)) + { + GdkPixbuf *pixbuf; + + input = g_file_read (file, NULL, NULL); + pixbuf = gdk_pixbuf_new_from_stream_at_scale (G_INPUT_STREAM (input), + max_width, max_height, + TRUE, NULL, NULL); + g_object_unref (input); + if (pixbuf) + { + GdkPixbufSimpleAnim *simple_anim = NULL; + + simple_anim = gdk_pixbuf_simple_anim_new (gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + 1.0); + if (simple_anim) + { + gdk_pixbuf_simple_anim_add_frame (simple_anim, pixbuf); + + g_object_unref (animation); + animation = GDK_PIXBUF_ANIMATION (simple_anim); + } + g_object_unref (pixbuf); + } + } + + if (be_verbose) + g_printerr (animation ? "OK\n" : "failed\n"); + + return animation; +} + +static GdkPixbufAnimation * +splash_image_pick_from_dirs (GList *dirs, + gint max_width, + gint max_height, + gboolean be_verbose) +{ + GdkPixbufAnimation *animation = NULL; + GList *splashes = NULL; + GList *iter; + + for (iter = dirs; iter; iter = iter->next) + { + GFileEnumerator *enumerator; + + enumerator = g_file_enumerate_children (iter->data, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (enumerator) + { + GFileInfo *info; + + while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) + { + GFile *child; + + child = g_file_enumerator_get_child (enumerator, info); + if (g_file_query_file_type (child, + G_FILE_QUERY_INFO_NONE, + NULL) == G_FILE_TYPE_REGULAR) + splashes = g_list_prepend (splashes, child); + else + g_object_unref (child); + g_object_unref (info); + } + + g_object_unref (enumerator); + } + } + + if (splashes) + { + gint32 i = g_random_int_range (0, g_list_length (splashes)); + + animation = splash_image_load_from_file (g_list_nth_data (splashes, i), + max_width, max_height, + be_verbose); + g_list_free_full (splashes, (GDestroyNotify) g_object_unref); + } + + return animation; +} + +static void +splash_timer_elapsed (void) +{ + gdouble elapsed = g_timer_elapsed (splash->timer, NULL); + + g_printerr ("%8g %8g - %s %g%% - %s\n", + elapsed, + elapsed - splash->last_time, + splash->text1 ? splash->text1 : "", + splash->percentage * 100.0, + splash->text2 ? splash->text2 : ""); + + splash->last_time = elapsed; +} diff --git a/app/gui/splash.h b/app/gui/splash.h new file mode 100644 index 0000000..a44f9b3 --- /dev/null +++ b/app/gui/splash.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 . + */ + +#ifndef __SPLASH_H__ +#define __SPLASH_H__ + + +void splash_create (gboolean be_verbose, + GdkScreen *screen, + gint monitor); +void splash_destroy (void); + +void splash_update (const gchar *label1, + const gchar *label2, + gdouble percentage); + + +#endif /* __SPLASH_H__ */ diff --git a/app/gui/themes.c b/app/gui/themes.c new file mode 100644 index 0000000..0307094 --- /dev/null +++ b/app/gui/themes.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 . + */ + +#include "config.h" + +#include + +#include +#ifdef GDK_DISABLE_DEPRECATED +#undef GDK_DISABLE_DEPRECATED +#endif +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" + +#include "gui-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "themes.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void themes_write_style (GimpGuiConfig *config, + GOutputStream *output, + GError **error); +static void themes_apply_theme (Gimp *gimp, + GimpGuiConfig *config); +static void themes_list_themes_foreach (gpointer key, + gpointer value, + gpointer data); +static gint themes_name_compare (const void *p1, + const void *p2); +static void themes_theme_change_notify (GimpGuiConfig *config, + GParamSpec *pspec, + Gimp *gimp); + +static void themes_fix_pixbuf_style (void); +static void themes_draw_pixbuf_layout (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + gboolean use_text, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint x, + gint y, + PangoLayout *layout); + +/* private variables */ + +static GHashTable *themes_hash = NULL; +static GtkStyleClass *pixbuf_style_class = NULL; + + +/* public functions */ + +void +themes_init (Gimp *gimp) +{ + GimpGuiConfig *config; + gchar *themerc; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + config = GIMP_GUI_CONFIG (gimp->config); + + themes_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + if (config->theme_path) + { + GList *path; + GList *list; + + path = gimp_config_path_expand_to_files (config->theme_path, NULL); + + for (list = path; list; list = g_list_next (list)) + { + GFile *dir = list->data; + GFileEnumerator *enumerator; + + enumerator = + g_file_enumerate_children (dir, + 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))) + { + if (! g_file_info_get_is_hidden (info) && + g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + { + GFile *file; + const gchar *name; + gchar *basename; + + file = g_file_enumerator_get_child (enumerator, info); + name = gimp_file_get_utf8_name (file); + + basename = g_path_get_basename (name); + + if (gimp->be_verbose) + g_print ("Adding theme '%s' (%s)\n", + basename, name); + + g_hash_table_insert (themes_hash, basename, file); + } + + g_object_unref (info); + } + + g_object_unref (enumerator); + } + } + + g_list_free_full (path, (GDestroyNotify) g_object_unref); + } + + themes_apply_theme (gimp, config); + + themerc = gimp_personal_rc_file ("themerc"); + gtk_rc_parse (themerc); + g_free (themerc); + + themes_fix_pixbuf_style (); + + g_signal_connect (config, "notify::theme", + G_CALLBACK (themes_theme_change_notify), + gimp); + g_signal_connect (config, "notify::compact-sliders", + G_CALLBACK (themes_theme_change_notify), + gimp); +} + +void +themes_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (themes_hash) + { + g_signal_handlers_disconnect_by_func (gimp->config, + themes_theme_change_notify, + gimp); + + g_hash_table_destroy (themes_hash); + themes_hash = NULL; + } + + g_clear_pointer (&pixbuf_style_class, g_type_class_unref); +} + +gchar ** +themes_list_themes (Gimp *gimp, + gint *n_themes) +{ + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (n_themes != NULL, NULL); + + *n_themes = g_hash_table_size (themes_hash); + + if (*n_themes > 0) + { + gchar **themes; + gchar **index; + + themes = g_new0 (gchar *, *n_themes + 1); + + index = themes; + + g_hash_table_foreach (themes_hash, themes_list_themes_foreach, &index); + + qsort (themes, *n_themes, sizeof (gchar *), themes_name_compare); + + return themes; + } + + return NULL; +} + +GFile * +themes_get_theme_dir (Gimp *gimp, + const gchar *theme_name) +{ + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + if (! theme_name) + theme_name = GIMP_CONFIG_DEFAULT_THEME; + + return g_hash_table_lookup (themes_hash, theme_name); +} + +GFile * +themes_get_theme_file (Gimp *gimp, + const gchar *first_component, + ...) +{ + GimpGuiConfig *gui_config; + GFile *file; + const gchar *component; + va_list args; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (first_component != NULL, NULL); + + gui_config = GIMP_GUI_CONFIG (gimp->config); + + file = g_object_ref (themes_get_theme_dir (gimp, gui_config->theme)); + component = first_component; + + va_start (args, first_component); + + do + { + GFile *tmp = g_file_get_child (file, component); + g_object_unref (file); + file = tmp; + } + while ((component = va_arg (args, gchar *))); + + va_end (args); + + if (! g_file_query_exists (file, NULL)) + { + g_object_unref (file); + + file = g_object_ref (themes_get_theme_dir (gimp, NULL)); + component = first_component; + + va_start (args, first_component); + + do + { + GFile *tmp = g_file_get_child (file, component); + g_object_unref (file); + file = tmp; + } + while ((component = va_arg (args, gchar *))); + + va_end (args); + } + + return file; +} + + +/* private functions */ + +static void +themes_write_style (GimpGuiConfig *config, + GOutputStream *output, + GError **error) +{ + if (! *error) + { + g_output_stream_printf ( + output, NULL, NULL, error, + "style \"gimp-spin-scale-style\"\n" + "{\n" + " GimpSpinScale::compact = %d\n" + "}\n" + "\n" + "class \"GimpSpinScale\" style \"gimp-spin-scale-style\"\n" + "\n", + config->compact_sliders); + } +} + +static void +themes_apply_theme (Gimp *gimp, + GimpGuiConfig *config) +{ + GFile *themerc; + GOutputStream *output; + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + themerc = gimp_directory_file ("themerc", NULL); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (themerc)); + + output = G_OUTPUT_STREAM (g_file_replace (themerc, + NULL, FALSE, G_FILE_CREATE_NONE, + NULL, &error)); + if (! output) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message); + g_clear_error (&error); + } + else + { + GFile *theme_dir = themes_get_theme_dir (gimp, config->theme); + GFile *gtkrc_user; + GSList *gtkrc_files = NULL; + GSList *iter; + + if (theme_dir) + { + gtkrc_files = g_slist_prepend ( + gtkrc_files, + g_file_get_child (theme_dir, "gtkrc")); + } + else + { + /* get the hardcoded default theme gtkrc */ + gtkrc_files = g_slist_prepend ( + gtkrc_files, + g_file_new_for_path (gimp_gtkrc ())); + } + + gtkrc_files = g_slist_prepend ( + gtkrc_files, + gimp_sysconf_directory_file ("gtkrc", NULL)); + + gtkrc_user = gimp_directory_file ("gtkrc", NULL); + gtkrc_files = g_slist_prepend ( + gtkrc_files, + gtkrc_user); + + gtkrc_files = g_slist_reverse (gtkrc_files); + + g_output_stream_printf ( + output, NULL, NULL, &error, + "# GIMP themerc\n" + "#\n" + "# This file is written on GIMP startup and on every theme change.\n" + "# It is NOT supposed to be edited manually. Edit your personal\n" + "# gtkrc file instead (%s).\n" + "\n", + gimp_file_get_utf8_name (gtkrc_user)); + + themes_write_style (config, output, &error); + + for (iter = gtkrc_files; ! error && iter; iter = g_slist_next (iter)) + { + GFile *file = iter->data; + + if (g_file_query_exists (file, NULL)) + { + gchar *path; + gchar *esc_path; + + path = g_file_get_path (file); + esc_path = g_strescape (path, NULL); + g_free (path); + + g_output_stream_printf ( + output, NULL, NULL, &error, + "include \"%s\"\n", + esc_path); + + g_free (esc_path); + } + } + + if (! error) + { + g_output_stream_printf ( + output, NULL, NULL, &error, + "\n" + "# end of themerc\n"); + } + + if (error) + { + GCancellable *cancellable = g_cancellable_new (); + + gimp_message (gimp, NULL, GIMP_MESSAGE_ERROR, + _("Error writing '%s': %s"), + gimp_file_get_utf8_name (themerc), error->message); + g_clear_error (&error); + + /* Cancel the overwrite initiated by g_file_replace(). */ + g_cancellable_cancel (cancellable); + g_output_stream_close (output, cancellable, NULL); + g_object_unref (cancellable); + } + else if (! g_output_stream_close (output, NULL, &error)) + { + gimp_message (gimp, NULL, GIMP_MESSAGE_ERROR, + _("Error closing '%s': %s"), + gimp_file_get_utf8_name (themerc), error->message); + g_clear_error (&error); + } + + g_slist_free_full (gtkrc_files, g_object_unref); + g_object_unref (output); + } + + g_object_unref (themerc); +} + +static void +themes_list_themes_foreach (gpointer key, + gpointer value, + gpointer data) +{ + gchar ***index = data; + + **index = g_strdup ((gchar *) key); + + (*index)++; +} + +static gint +themes_name_compare (const void *p1, + const void *p2) +{ + return strcmp (* (char **) p1, * (char **) p2); +} + +static void +themes_theme_change_notify (GimpGuiConfig *config, + GParamSpec *pspec, + Gimp *gimp) +{ + themes_apply_theme (gimp, config); + + gtk_rc_reparse_all (); + + themes_fix_pixbuf_style (); +} + +static void +themes_fix_pixbuf_style (void) +{ + /* This is a "quick'n dirty" trick to get appropriate colors for + * themes in GTK+2, and in particular dark themes which would display + * insensitive items with a barely readable layout. + * + * This piece of code partly duplicates code from GTK+2 (slightly + * modified to get readable insensitive items) and will likely have to + * be removed for GIMP 3. + * + * See https://bugzilla.gnome.org/show_bug.cgi?id=770424 + */ + + if (! pixbuf_style_class) + { + GType type = g_type_from_name ("PixbufStyle"); + + if (type) + { + pixbuf_style_class = g_type_class_ref (type); + + if (pixbuf_style_class) + pixbuf_style_class->draw_layout = themes_draw_pixbuf_layout; + } + } +} + +static void +themes_draw_pixbuf_layout (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + gboolean use_text, + GdkRectangle *area, + GtkWidget *widget, + const gchar *detail, + gint x, + gint y, + PangoLayout *layout) +{ + GdkGC *gc; + + gc = use_text ? style->text_gc[state_type] : style->fg_gc[state_type]; + + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + if (state_type == GTK_STATE_INSENSITIVE) + { + GdkGC *copy = gdk_gc_new (window); + GdkGCValues orig; + GdkColor fore; + guint16 r, g, b; + + gdk_gc_copy (copy, gc); + gdk_gc_get_values (gc, &orig); + + r = 0x40 + (((orig.foreground.pixel >> 16) & 0xff) >> 1); + g = 0x40 + (((orig.foreground.pixel >> 8) & 0xff) >> 1); + b = 0x40 + (((orig.foreground.pixel >> 0) & 0xff) >> 1); + + fore.pixel = (r << 16) | (g << 8) | b; + fore.red = r * 257; + fore.green = g * 257; + fore.blue = b * 257; + + gdk_gc_set_foreground (copy, &fore); + gdk_draw_layout (window, copy, x, y, layout); + + g_object_unref (copy); + } + else + { + gdk_draw_layout (window, gc, x, y, layout); + } + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); +} diff --git a/app/gui/themes.h b/app/gui/themes.h new file mode 100644 index 0000000..0b7e74f --- /dev/null +++ b/app/gui/themes.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 . + */ + +#ifndef __THEMES_H__ +#define __THEMES_H__ + + +void themes_init (Gimp *gimp); +void themes_exit (Gimp *gimp); + +gchar ** themes_list_themes (Gimp *gimp, + gint *n_themes); +GFile * themes_get_theme_dir (Gimp *gimp, + const gchar *theme_name); +GFile * themes_get_theme_file (Gimp *gimp, + const gchar *first_component, + ...) G_GNUC_NULL_TERMINATED; + + +#endif /* __THEMES_H__ */ diff --git a/app/language.c b/app/language.c new file mode 100644 index 0000000..581ff67 --- /dev/null +++ b/app/language.c @@ -0,0 +1,739 @@ +/* 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 . + */ + +/* Win32 language lookup table: + * Copyright (C) 2007-2008 Dieter Verfaillie + */ + +#include "config.h" + +#include + +#include + +#ifdef G_OS_WIN32 +#include +#include +#endif + +#include "language.h" + + +void +language_init (const gchar *language) +{ +#ifdef G_OS_WIN32 + if (! language && + g_getenv ("LANG") == NULL && + g_getenv ("LC_MESSAGES") == NULL && + g_getenv ("LC_ALL") == NULL && + g_getenv ("LANGUAGE") == NULL) + { + /* FIXME: This is a hack. gettext doesn't pick the right language + * by default on Windows, so we enforce the right one. The + * following code is an adaptation of Python code from + * pynicotine. For reasons why this approach is needed, and why + * the GetLocaleInfo() approach in other libs falls flat, see: + * http://blogs.msdn.com/b/michkap/archive/2007/04/15/2146890.aspx + */ + + switch (GetUserDefaultUILanguage()) + { + case 1078: + language = "af"; /* Afrikaans - South Africa */ + break; + case 1052: + language = "sq"; /* Albanian - Albania */ + break; + case 1118: + language = "am"; /* Amharic - Ethiopia */ + break; + case 1025: + language = "ar_SA"; /* Arabic - Saudi Arabia */ + break; + case 5121: + language = "ar_DZ"; /* Arabic - Algeria */ + break; + case 15361: + language = "ar_BH"; /* Arabic - Bahrain */ + break; + case 3073: + language = "ar_EG"; /* Arabic - Egypt */ + break; + case 2049: + language = "ar_IQ"; /* Arabic - Iraq */ + break; + case 11265: + language = "ar_JO"; /* Arabic - Jordan */ + break; + case 13313: + language = "ar_KW"; /* Arabic - Kuwait */ + break; + case 12289: + language = "ar_LB"; /* Arabic - Lebanon */ + break; + case 4097: + language = "ar_LY"; /* Arabic - Libya */ + break; + case 6145: + language = "ar_MO"; /* Arabic - Morocco */ + break; + case 8193: + language = "ar_OM"; /* Arabic - Oman */ + break; + case 16385: + language = "ar_QA"; /* Arabic - Qatar */ + break; + case 10241: + language = "ar_SY"; /* Arabic - Syria */ + break; + case 7169: + language = "ar_TN"; /* Arabic - Tunisia */ + break; + case 14337: + language = "ar_AE"; /* Arabic - U.A.E. */ + break; + case 9217: + language = "ar_YE"; /* Arabic - Yemen */ + break; + case 1067: + language = "hy"; /* Armenian - Armenia */ + break; + case 1101: + language = "as"; /* Assamese */ + break; + case 2092: + language = NULL; /* Azeri (Cyrillic) */ + break; + case 1068: + language = NULL; /* Azeri (Latin) */ + break; + case 1069: + language = "eu"; /* Basque */ + break; + case 1059: + language = "be"; /* Belarusian */ + break; + case 1093: + language = "bn_IN"; /* Bengali (India) */ + break; + case 2117: + language = "bn_BD"; /* Bengali (Bangladesh) */ + break; + case 5146: + language = "bs"; /* Bosnian (Bosnia/Herzegovina) */ + break; + case 1026: + language = "bg"; /* Bulgarian */ + break; + case 1109: + language = "my"; /* Burmese */ + break; + case 1027: + language = "ca"; /* Catalan */ + break; + case 1116: + language = NULL; /* Cherokee - United States */ + break; + case 2052: + language = "zh_CN"; /* Chinese - People"s Republic of China */ + break; + case 4100: + language = "zh_SG"; /* Chinese - Singapore */ + break; + case 1028: + language = "zh_TW"; /* Chinese - Taiwan */ + break; + case 3076: + language = "zh_HK"; /* Chinese - Hong Kong SAR */ + break; + case 5124: + language = "zh_MO"; /* Chinese - Macao SAR */ + break; + case 1050: + language = "hr_HR"; /* Croatian */ + break; + case 4122: + language = "hr_BA"; /* Croatian (Bosnia/Herzegovina) */ + break; + case 1029: + language = "cs"; /* Czech */ + break; + case 1030: + language = "da"; /* Danish */ + break; + case 1125: + language = "dv"; /* Divehi */ + break; + case 1043: + language = "nl_NL"; /* Dutch - Netherlands */ + break; + case 2067: + language = "nl_BE"; /* Dutch - Belgium */ + break; + case 1126: + language = NULL; /* Edo */ + break; + case 1033: + language = "en_US"; /* English - United States */ + break; + case 2057: + language = "en_UK"; /* English - United Kingdom */ + break; + case 3081: + language = "en_AU"; /* English - Australia */ + break; + case 10249: + language = "en_BZ"; /* English - Belize */ + break; + case 4105: + language = "en_CA"; /* English - Canada */ + break; + case 9225: + language = "en"; /* English - Caribbean */ + break; + case 15369: + language = "en_HK"; /* English - Hong Kong SAR */ + break; + case 16393: + language = "en_IN"; /* English - India */ + break; + case 14345: + language = "en_ID"; /* English - Indonesia */ + break; + case 6153: + language = "en_IR"; /* English - Ireland */ + break; + case 8201: + language = "en_JM"; /* English - Jamaica */ + break; + case 17417: + language = "en_MW"; /* English - Malaysia */ + break; + case 5129: + language = "en_NZ"; /* English - New Zealand */ + break; + case 13321: + language = "en_PH"; /* English - Philippines */ + break; + case 18441: + language = "en_SG"; /* English - Singapore */ + break; + case 7177: + language = "en_ZA"; /* English - South Africa */ + break; + case 11273: + language = "en_TT"; /* English - Trinidad */ + break; + case 12297: + language = "en_ZW"; /* English - Zimbabwe */ + break; + case 1061: + language = "et"; /* Estonian */ + break; + case 1080: + language = "fo"; /* Faroese */ + break; + case 1065: + language = "fa"; /* Farsi */ + break; + case 1124: + language = NULL; /* Filipino */ + break; + case 1035: + language = "fi"; /* Finnish */ + break; + case 1036: + language = "fr_FR"; /* French - France */ + break; + case 2060: + language = "fr_BE"; /* French - Belgium */ + break; + case 11276: + language = "fr_CM"; /* French - Cameroon */ + break; + case 3084: + language = "fr_CA"; /* French - Canada */ + break; + case 9228: + language = "fr_CD"; /* French - Democratic Rep. of Congo */ + break; + case 12300: + language = "fr_CI"; /* French - Cote d"Ivoire */ + break; + case 15372: + language = "fr_HT"; /* French - Haiti */ + break; + case 5132: + language = "fr_LU"; /* French - Luxembourg */ + break; + case 13324: + language = "fr_ML"; /* French - Mali */ + break; + case 6156: + language = "fr_MC"; /* French - Monaco */ + break; + case 14348: + language = "fr_MA"; /* French - Morocco */ + break; + case 58380: + language = "fr"; /* French - North Africa */ + break; + case 8204: + language = "fr_RE"; /* French - Reunion */ + break; + case 10252: + language = "fr_SN"; /* French - Senegal */ + break; + case 4108: + language = "fr_CH"; /* French - Switzerland */ + break; + case 7180: + language = "fr"; /* French - West Indies */ + break; + case 1122: + language = "fy"; /* Frisian - Netherlands */ + break; + case 1127: + language = NULL; /* Fulfulde - Nigeria */ + break; + case 1071: + language = "mk"; /* FYRO Macedonian */ + break; + case 2108: + language = "ga"; /* Gaelic (Ireland) */ + break; + case 1084: + language = "gd"; /* Gaelic (Scotland) */ + break; + case 1110: + language = "gl"; /* Galician */ + break; + case 1079: + language = "ka"; /* Georgian */ + break; + case 1031: + language = "de_DE"; /* German - Germany */ + break; + case 3079: + language = "de_AT"; /* German - Austria */ + break; + case 5127: + language = "de_LI"; /* German - Liechtenstein */ + break; + case 4103: + language = "de_LU"; /* German - Luxembourg */ + break; + case 2055: + language = "de_CH"; /* German - Switzerland */ + break; + case 1032: + language = "el"; /* Greek */ + break; + case 1140: + language = "gn"; /* Guarani - Paraguay */ + break; + case 1095: + language = "gu"; /* Gujarati */ + break; + case 1128: + language = "ha"; /* Hausa - Nigeria */ + break; + case 1141: + language = NULL; /* Hawaiian - United States */ + break; + case 1037: + language = "he"; /* Hebrew */ + break; + case 1081: + language = "hi"; /* Hindi */ + break; + case 1038: + language = "hu"; /* Hungarian */ + break; + case 1129: + language = NULL; /* Ibibio - Nigeria */ + break; + case 1039: + language = "is"; /* Icelandic */ + break; + case 1136: + language = "ig"; /* Igbo - Nigeria */ + break; + case 1057: + language = "id"; /* Indonesian */ + break; + case 1117: + language = "iu"; /* Inuktitut */ + break; + case 1040: + language = "it_IT"; /* Italian - Italy */ + break; + case 2064: + language = "it_CH"; /* Italian - Switzerland */ + break; + case 1041: + language = "ja"; /* Japanese */ + break; + case 1099: + language = "kn"; /* Kannada */ + break; + case 1137: + language = "kr"; /* Kanuri - Nigeria */ + break; + case 2144: + language = "ks"; /* Kashmiri */ + break; + case 1120: + language = "ks"; /* Kashmiri (Arabic) */ + break; + case 1087: + language = "kk"; /* Kazakh */ + break; + case 1107: + language = "km"; /* Khmer */ + break; + case 1111: + language = NULL; /* Konkani */ + break; + case 1042: + language = "ko"; /* Korean */ + break; + case 1088: + language = "ky"; /* Kyrgyz (Cyrillic) */ + break; + case 1108: + language = "lo"; /* Lao */ + break; + case 1142: + language = "la"; /* Latin */ + break; + case 1062: + language = "lv"; /* Latvian */ + break; + case 1063: + language = "lt"; /* Lithuanian */ + break; + case 1086: + language = "ms_MY"; /* Malay - Malaysia */ + break; + case 2110: + language = "ms_BN"; /* Malay - Brunei Darussalam */ + break; + case 1100: + language = "ml"; /* Malayalam */ + break; + case 1082: + language = "mt"; /* Maltese */ + break; + case 1112: + language = NULL; /* Manipuri */ + break; + case 1153: + language = "mi"; /* Maori - New Zealand */ + break; + case 1102: + language = "mr"; /* Marathi */ + break; + case 1104: + language = "mn"; /* Mongolian (Cyrillic) */ + break; + case 2128: + language = "mn"; /* Mongolian (Mongolian) */ + break; + case 1121: + language = "ne_NP"; /* Nepali */ + break; + case 2145: + language = "ne_IN"; /* Nepali - India */ + break; + case 1044: + language = "no"; /* Norwegian (Bokmᅢᆬl) */ + break; + case 2068: + language = "no"; /* Norwegian (Nynorsk) */ + break; + case 1096: + language = "or"; /* Oriya */ + break; + case 1138: + language = "om"; /* Oromo */ + break; + case 1145: + language = NULL; /* Papiamentu */ + break; + case 1123: + language = "ps"; /* Pashto */ + break; + case 1045: + language = "pl"; /* Polish */ + break; + case 1046: + language = "pt_BR"; /* Portuguese - Brazil */ + break; + case 2070: + language = "pt_PT"; /* Portuguese - Portugal */ + break; + case 1094: + language = "pa"; /* Punjabi */ + break; + case 2118: + language = "pa_PK"; /* Punjabi (Pakistan) */ + break; + case 1131: + language = "qu_BO"; /* Quecha - Bolivia */ + break; + case 2155: + language = "qu_EC"; /* Quecha - Ecuador */ + break; + case 3179: + language = "qu_PE"; /* Quecha - Peru */ + break; + case 1047: + language = "rm"; /* Rhaeto-Romanic */ + break; + case 1048: + language = "ro_RO"; /* Romanian */ + break; + case 2072: + language = "ro_MD"; /* Romanian - Moldava */ + break; + case 1049: + language = "ru_RU"; /* Russian */ + break; + case 2073: + language = "ru_MD"; /* Russian - Moldava */ + break; + case 1083: + language = NULL; /* Sami (Lappish) */ + break; + case 1103: + language = "sa"; /* Sanskrit */ + break; + case 1132: + language = NULL; /* Sepedi */ + break; + case 3098: + language = "sr"; /* Serbian (Cyrillic) */ + break; + case 2074: + language = "sr@latin"; /* Serbian (Latin) */ + break; + case 1113: + language = "sd_IN"; /* Sindhi - India */ + break; + case 2137: + language = "sd_PK"; /* Sindhi - Pakistan */ + break; + case 1115: + language = "si"; /* Sinhalese - Sri Lanka */ + break; + case 1051: + language = "sk"; /* Slovak */ + break; + case 1060: + language = "sl"; /* Slovenian */ + break; + case 1143: + language = "so"; /* Somali */ + break; + case 1070: + language = NULL; /* Sorbian */ + break; + case 3082: + language = "es"; /* Spanish - Spain (Modern Sort) */ + break; + case 1034: + language = "es"; /* Spanish - Spain (Traditional Sort) */ + break; + case 11274: + language = "es_AR"; /* Spanish - Argentina */ + break; + case 16394: + language = "es_BO"; /* Spanish - Bolivia */ + break; + case 13322: + language = "es_CL"; /* Spanish - Chile */ + break; + case 9226: + language = "es_CO"; /* Spanish - Colombia */ + break; + case 5130: + language = "es_CR"; /* Spanish - Costa Rica */ + break; + case 7178: + language = "es_DO"; /* Spanish - Dominican Republic */ + break; + case 12298: + language = "es_EC"; /* Spanish - Ecuador */ + break; + case 17418: + language = "es_SV"; /* Spanish - El Salvador */ + break; + case 4106: + language = "es_GT"; /* Spanish - Guatemala */ + break; + case 18442: + language = "es_HN"; /* Spanish - Honduras */ + break; + case 58378: + language = "es"; /* Spanish - Latin America */ + break; + case 2058: + language = "es_MX"; /* Spanish - Mexico */ + break; + case 19466: + language = "es_NI"; /* Spanish - Nicaragua */ + break; + case 6154: + language = "es_PA"; /* Spanish - Panama */ + break; + case 15370: + language = "es_PY"; /* Spanish - Paraguay */ + break; + case 10250: + language = "es_PE"; /* Spanish - Peru */ + break; + case 20490: + language = "es_PR"; /* Spanish - Puerto Rico */ + break; + case 21514: + language = "es_US"; /* Spanish - United States */ + break; + case 14346: + language = "es_UY"; /* Spanish - Uruguay */ + break; + case 8202: + language = "es_VE"; /* Spanish - Venezuela */ + break; + case 1072: + language = NULL; /* Sutu */ + break; + case 1089: + language = "sw"; /* Swahili */ + break; + case 1053: + language = "sv_SE"; /* Swedish */ + break; + case 2077: + language = "sv_FI"; /* Swedish - Finland */ + break; + case 1114: + language = NULL; /* Syriac */ + break; + case 1064: + language = "tg"; /* Tajik */ + break; + case 1119: + language = NULL; /* Tamazight (Arabic) */ + break; + case 2143: + language = NULL; /* Tamazight (Latin) */ + break; + case 1097: + language = "ta"; /* Tamil */ + break; + case 1092: + language = "tt"; /* Tatar */ + break; + case 1098: + language = "te"; /* Telugu */ + break; + case 1054: + language = "th"; /* Thai */ + break; + case 2129: + language = "bo_BT"; /* Tibetan - Bhutan */ + break; + case 1105: + language = "bo_CN"; /* Tibetan - People"s Republic of China */ + break; + case 2163: + language = "ti_ER"; /* Tigrigna - Eritrea */ + break; + case 1139: + language = "ti_ET"; /* Tigrigna - Ethiopia */ + break; + case 1073: + language = "ts"; /* Tsonga */ + break; + case 1074: + language = "tn"; /* Tswana */ + break; + case 1055: + language = "tr"; /* Turkish */ + break; + case 1090: + language = "tk"; /* Turkmen */ + break; + case 1152: + language = "ug"; /* Uighur - China */ + break; + case 1058: + language = "uk"; /* Ukrainian */ + break; + case 1056: + language = "ur"; /* Urdu */ + break; + case 2080: + language = "ur_IN"; /* Urdu - India */ + break; + case 2115: + language = "uz"; /* Uzbek (Cyrillic) */ + break; + case 1091: + language = "uz@latin"; /* Uzbek (Latin) */ + break; + case 1075: + language = "ve"; /* Venda */ + break; + case 1066: + language = "vi"; /* Vietnamese */ + break; + case 1106: + language = "cy"; /* Welsh */ + break; + case 1076: + language = "xh"; /* Xhosa */ + break; + case 1144: + language = NULL; /* Yi */ + break; + case 1085: + language = "yi"; /* Yiddish */ + break; + case 1130: + language = "yo"; /* Yoruba */ + break; + case 1077: + language = "zu"; /* Zulu */ + break; + default: + language = NULL; + } + } +#endif + + /* We already set the locale according to the environment, so just + * return early if no language is set in gimprc. + */ + if (! language) + return; + + g_setenv ("LANGUAGE", language, TRUE); + setlocale (LC_ALL, ""); +} diff --git a/app/language.h b/app/language.h new file mode 100644 index 0000000..39d6929 --- /dev/null +++ b/app/language.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 . + */ + +#ifndef __LANGUAGE_H__ +#define __LANGUAGE_H__ + +#ifndef GIMP_APP_GLUE_COMPILATION +#error You must not #include "language.h" from a subdir +#endif + + +void language_init (const gchar *language); + + +#endif /* __LANGUAGE_H__ */ diff --git a/app/main.c b/app/main.c new file mode 100644 index 0000000..ba38758 --- /dev/null +++ b/app/main.c @@ -0,0 +1,953 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef __GLIBC__ +#include +#endif + +#include + +#include + +#ifdef G_OS_WIN32 +#include /* get_osfhandle */ + +#endif /* G_OS_WIN32 */ + +#if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) +#include /* dirname */ +#include +#endif /* __APPLE__ */ + +#ifndef GIMP_CONSOLE_COMPILATION +#include +#else +#include +#endif + +#include + +#include "libgimpbase/gimpbase.h" + +#include "pdb/pdb-types.h" + +#include "config/gimpconfig-dump.h" + +#include "core/gimp.h" +#include "core/gimpbacktrace.h" + +#include "pdb/gimppdb.h" +#include "pdb/gimpprocedure.h" +#include "pdb/internal-procs.h" + +#include "about.h" +#include "app.h" +#include "sanity.h" +#include "signals.h" +#include "unique.h" + +#ifdef G_OS_WIN32 +/* To get PROCESS_DEP_* defined we need _WIN32_WINNT at 0x0601. We still + * use the API optionally only if present, though. + */ +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 +#include +#include +#endif + +#include "gimp-log.h" +#include "gimp-intl.h" +#include "gimp-version.h" + + +static gboolean gimp_option_fatal_warnings (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); +static gboolean gimp_option_stack_trace_mode (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); +static gboolean gimp_option_pdb_compat_mode (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); +static gboolean gimp_option_dump_gimprc (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); +static gboolean gimp_option_dump_pdb_procedures_deprecated + (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error); + +static void gimp_show_version_and_exit (void) G_GNUC_NORETURN; +static void gimp_show_license_and_exit (void) G_GNUC_NORETURN; + +static void gimp_init_i18n (void); +static void gimp_init_malloc (void); + +#if defined (G_OS_WIN32) && !defined (GIMP_CONSOLE_COMPILATION) +static void gimp_open_console_window (void); +#else +#define gimp_open_console_window() /* as nothing */ +#endif + +static const gchar *system_gimprc = NULL; +static const gchar *user_gimprc = NULL; +static const gchar *session_name = NULL; +static const gchar *batch_interpreter = NULL; +static const gchar **batch_commands = NULL; +static const gchar **filenames = NULL; +static gboolean as_new = FALSE; +static gboolean no_interface = FALSE; +static gboolean no_data = FALSE; +static gboolean no_fonts = FALSE; +static gboolean no_splash = FALSE; +static gboolean be_verbose = FALSE; +static gboolean new_instance = FALSE; +#if defined (USE_SYSV_SHM) || defined (USE_POSIX_SHM) || defined (G_OS_WIN32) +static gboolean use_shm = TRUE; +#else +static gboolean use_shm = FALSE; +#endif +static gboolean use_cpu_accel = TRUE; +static gboolean console_messages = FALSE; +static gboolean use_debug_handler = FALSE; + +#ifdef GIMP_UNSTABLE +static gboolean show_playground = TRUE; +static gboolean show_debug_menu = TRUE; +static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_QUERY; +static GimpPDBCompatMode pdb_compat_mode = GIMP_PDB_COMPAT_WARN; +#else +static gboolean show_playground = FALSE; +static gboolean show_debug_menu = FALSE; +static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_NEVER; +static GimpPDBCompatMode pdb_compat_mode = GIMP_PDB_COMPAT_ON; +#endif + + +static const GOptionEntry main_entries[] = +{ + { "version", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (GOptionArgFunc) gimp_show_version_and_exit, + N_("Show version information and exit"), NULL + }, + { + "license", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (GOptionArgFunc) gimp_show_license_and_exit, + N_("Show license information and exit"), NULL + }, + { + "verbose", 0, 0, + G_OPTION_ARG_NONE, &be_verbose, + N_("Be more verbose"), NULL + }, + { + "new-instance", 'n', 0, + G_OPTION_ARG_NONE, &new_instance, + N_("Start a new GIMP instance"), NULL + }, + { + "as-new", 'a', 0, + G_OPTION_ARG_NONE, &as_new, + N_("Open images as new"), NULL + }, + { + "no-interface", 'i', 0, + G_OPTION_ARG_NONE, &no_interface, + N_("Run without a user interface"), NULL + }, + { + "no-data", 'd', 0, + G_OPTION_ARG_NONE, &no_data, + N_("Do not load brushes, gradients, patterns, ..."), NULL + }, + { + "no-fonts", 'f', 0, + G_OPTION_ARG_NONE, &no_fonts, + N_("Do not load any fonts"), NULL + }, + { + "no-splash", 's', 0, + G_OPTION_ARG_NONE, &no_splash, + N_("Do not show a splash screen"), NULL + }, + { + "no-shm", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &use_shm, + N_("Do not use shared memory between GIMP and plug-ins"), NULL + }, + { + "no-cpu-accel", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &use_cpu_accel, + N_("Do not use special CPU acceleration functions"), NULL + }, + { + "session", 0, 0, + G_OPTION_ARG_FILENAME, &session_name, + N_("Use an alternate sessionrc file"), "" + }, + { + "gimprc", 'g', 0, + G_OPTION_ARG_FILENAME, &user_gimprc, + N_("Use an alternate user gimprc file"), "" + }, + { + "system-gimprc", 0, 0, + G_OPTION_ARG_FILENAME, &system_gimprc, + N_("Use an alternate system gimprc file"), "" + }, + { + "batch", 'b', 0, + G_OPTION_ARG_STRING_ARRAY, &batch_commands, + N_("Batch command to run (can be used multiple times)"), "" + }, + { + "batch-interpreter", 0, 0, + G_OPTION_ARG_STRING, &batch_interpreter, + N_("The procedure to process batch commands with"), "" + }, + { + "console-messages", 'c', 0, + G_OPTION_ARG_NONE, &console_messages, + N_("Send messages to console instead of using a dialog"), NULL + }, + { + "pdb-compat-mode", 0, 0, + G_OPTION_ARG_CALLBACK, gimp_option_pdb_compat_mode, + /* don't translate the mode names (off|on|warn) */ + N_("PDB compatibility mode (off|on|warn)"), "" + }, + { + "stack-trace-mode", 0, 0, + G_OPTION_ARG_CALLBACK, gimp_option_stack_trace_mode, + /* don't translate the mode names (never|query|always) */ + N_("Debug in case of a crash (never|query|always)"), "" + }, + { + "debug-handlers", 0, 0, + G_OPTION_ARG_NONE, &use_debug_handler, + N_("Enable non-fatal debugging signal handlers"), NULL + }, + { + "g-fatal-warnings", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, gimp_option_fatal_warnings, + N_("Make all warnings fatal"), NULL + }, + { + "dump-gimprc", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, + N_("Output a gimprc file with default settings"), NULL + }, + { + "dump-gimprc-system", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, + NULL, NULL + }, + { + "dump-gimprc-manpage", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, + NULL, NULL + }, + { + "dump-pdb-procedures-deprecated", 0, + G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, gimp_option_dump_pdb_procedures_deprecated, + N_("Output a sorted list of deprecated procedures in the PDB"), NULL + }, + { + "show-playground", 0, 0, + G_OPTION_ARG_NONE, &show_playground, + N_("Show a preferences page with experimental features"), NULL + }, + { + "show-debug-menu", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &show_debug_menu, + N_("Show an image submenu with debug actions"), NULL + }, + { + G_OPTION_REMAINING, 0, 0, + G_OPTION_ARG_FILENAME_ARRAY, &filenames, + NULL, NULL + }, + { NULL } +}; + +#if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) +static void +gimp_macos_setenv (const char * progname) +{ + /* helper to set environment variables for GIMP to be relocatable. + * Due to the latest changes it is not recommended to set it in the shell + * wrapper anymore. + */ + gchar *resolved_path; + /* on some OSX installations open file limit is 256 and GIMP needs more */ + struct rlimit limit; + + limit.rlim_cur = 10000; + limit.rlim_max = 10000; + setrlimit (RLIMIT_NOFILE, &limit); + resolved_path = g_canonicalize_filename (progname, NULL); + if (resolved_path && ! g_getenv ("GIMP_NO_WRAPPER")) + { + /* set path to the app folder to make sure that our python is called + * instead of system one + */ + static gboolean show_playground = TRUE; + + gchar *path; + gchar *tmp; + gchar *app_dir; + gchar *res_dir; + size_t path_len; + struct stat sb; + + app_dir = g_path_get_dirname (resolved_path); + tmp = g_strdup_printf ("%s/../Resources", app_dir); + res_dir = g_canonicalize_filename (tmp, NULL); + g_free (tmp); + if (res_dir && !stat (res_dir, &sb) && S_ISDIR (sb.st_mode)) + { + g_print ("GIMP is started as MacOS application\n"); + } + else + { + g_free (res_dir); + return; + } + + path_len = strlen (g_getenv ("PATH") ? g_getenv ("PATH") : "") + strlen (app_dir) + 2; + path = g_try_malloc (path_len); + if (path == NULL) + { + g_warning ("Failed to allocate memory"); + app_exit (EXIT_FAILURE); + } + if (g_getenv ("PATH")) + g_snprintf (path, path_len, "%s:%s", app_dir, g_getenv ("PATH")); + else + g_snprintf (path, path_len, "%s", app_dir); + g_free (app_dir); + g_setenv ("PATH", path, TRUE); + g_free (path); + tmp = g_strdup_printf ("%s/lib/gtk-2.0/2.10.0", res_dir); + g_setenv ("GTK_PATH", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/etc/gtk-2.0/gtk.immodules", res_dir); + g_setenv ("GTK_IM_MODULE_FILE", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/lib/gegl-0.4", res_dir); + g_setenv ("GEGL_PATH", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/lib/babl-0.1", res_dir); + g_setenv ("BABL_PATH", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache", res_dir); + g_setenv ("GDK_PIXBUF_MODULE_FILE", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/etc/fonts", res_dir); + g_setenv ("FONTCONFIG_PATH", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s", res_dir); + g_setenv ("PYTHONHOME", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/lib/python2.7:%s/lib/gimp/2.0/python", res_dir, res_dir); + g_setenv ("PYTHONPATH", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/lib/gio/modules", res_dir); + g_setenv ("GIO_MODULE_DIR", tmp, TRUE); + g_free (tmp); + tmp = g_strdup_printf ("%s/share/libwmf/fonts", res_dir); + g_setenv ("WMF_FONTDIR", tmp, TRUE); + g_free (tmp); + if (g_getenv ("HOME") != NULL) + { + tmp = g_strdup_printf ("%s/Library/Application Support/GIMP/2.10/cache", + g_getenv ("HOME")); + g_setenv ("XDG_CACHE_HOME", tmp, TRUE); + g_free (tmp); + } + g_free (res_dir); + } + g_free (resolved_path); +} +#endif + +int +main (int argc, + char **argv) +{ + GOptionContext *context; + GError *error = NULL; + const gchar *abort_message; + gchar *basename; + GFile *system_gimprc_file = NULL; + GFile *user_gimprc_file = NULL; + gchar *backtrace_file = NULL; + gint i; + +#ifdef ENABLE_WIN32_DEBUG_CONSOLE + gimp_open_console_window (); +#endif +#if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) + /* remove MacOS session identifier from the command line args */ + gint newargc = 0; + for (gint i = 0; i < argc; i++) + { + if (!g_str_has_prefix (argv[i], "-psn_")) + { + argv[newargc] = argv[i]; + newargc++; + } + } + if (argc > newargc) + { + argv[newargc] = NULL; /* glib expects NULL terminated array */ + argc = newargc; + } + + gimp_macos_setenv (argv[0]); +#endif + +#if defined (__GNUC__) && defined (_WIN64) + /* mingw-w64, at least the unstable build from late July 2008, + * starts subsystem:windows programs in main(), but passes them + * bogus argc and argv. __argc and __argv are OK, though, so just + * use them. + */ + argc = __argc; + argv = __argv; +#endif + + /* Initialize GimpBacktrace early on. In particular, we want the + * Windows backend to catch the SET_THREAD_NAME exceptions of newly + * created threads. + */ + gimp_backtrace_init (); + + /* Start signal handlers early. */ + gimp_init_signal_handlers (&backtrace_file); + +#ifdef G_OS_WIN32 + /* Reduce risks */ + { + typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName); + t_SetDllDirectoryA p_SetDllDirectoryA; + + p_SetDllDirectoryA = + (t_SetDllDirectoryA) GetProcAddress (GetModuleHandle ("kernel32.dll"), + "SetDllDirectoryA"); + if (p_SetDllDirectoryA) + (*p_SetDllDirectoryA) (""); + } + + /* On Windows, set DLL search path to $INSTALLDIR/bin so that .exe + plug-ins in the plug-ins directory can find libgimp and file + library DLLs without needing to set external PATH. */ + { + const gchar *install_dir; + gchar *bin_dir; + LPWSTR w_bin_dir; + int n; + + w_bin_dir = NULL; + install_dir = gimp_installation_directory (); + bin_dir = g_build_filename (install_dir, "bin", NULL); + + n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + bin_dir, -1, NULL, 0); + if (n == 0) + goto out; + + w_bin_dir = g_malloc_n (n + 1, sizeof (wchar_t)); + n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + bin_dir, -1, + w_bin_dir, (n + 1) * sizeof (wchar_t)); + if (n == 0) + goto out; + + SetDllDirectoryW (w_bin_dir); + + out: + if (w_bin_dir) + g_free (w_bin_dir); + g_free (bin_dir); + } + +#ifndef _WIN64 + { + typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags); + t_SetProcessDEPPolicy p_SetProcessDEPPolicy; + + p_SetProcessDEPPolicy = + (t_SetProcessDEPPolicy) GetProcAddress (GetModuleHandle ("kernel32.dll"), + "SetProcessDEPPolicy"); + if (p_SetProcessDEPPolicy) + (*p_SetProcessDEPPolicy) (PROCESS_DEP_ENABLE|PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION); + } +#endif + + /* Group all our windows together on the taskbar */ + { + typedef HRESULT (WINAPI *t_SetCurrentProcessExplicitAppUserModelID) (PCWSTR lpPathName); + t_SetCurrentProcessExplicitAppUserModelID p_SetCurrentProcessExplicitAppUserModelID; + + p_SetCurrentProcessExplicitAppUserModelID = + (t_SetCurrentProcessExplicitAppUserModelID) GetProcAddress (GetModuleHandle ("shell32.dll"), + "SetCurrentProcessExplicitAppUserModelID"); + if (p_SetCurrentProcessExplicitAppUserModelID) + (*p_SetCurrentProcessExplicitAppUserModelID) (L"gimp.GimpApplication"); + } +#endif + + gimp_init_malloc (); + + gimp_env_init (FALSE); + + gimp_log_init (); + + gimp_init_i18n (); + + g_set_application_name (GIMP_NAME); + +#ifdef G_OS_WIN32 + argv = g_win32_get_command_line (); +#else + argv = g_strdupv (argv); +#endif + + basename = g_path_get_basename (argv[0]); + g_set_prgname (basename); + g_free (basename); + + /* Check argv[] for "--verbose" first */ + for (i = 1; i < argc; i++) + { + const gchar *arg = argv[i]; + + if (arg[0] != '-') + continue; + + if ((strcmp (arg, "--verbose") == 0) || (strcmp (arg, "-v") == 0)) + { + be_verbose = TRUE; + } + } + + /* Check argv[] for "--no-interface" before trying to initialize gtk+. */ + for (i = 1; i < argc; i++) + { + const gchar *arg = argv[i]; + + if (arg[0] != '-') + continue; + + if ((strcmp (arg, "--no-interface") == 0) || (strcmp (arg, "-i") == 0)) + { + no_interface = TRUE; + } + else if ((strcmp (arg, "--version") == 0) || (strcmp (arg, "-v") == 0)) + { + gimp_show_version_and_exit (); + } +#if defined (G_OS_WIN32) && !defined (GIMP_CONSOLE_COMPILATION) + else if ((strcmp (arg, "--help") == 0) || + (strcmp (arg, "-?") == 0) || + (strncmp (arg, "--help-", 7) == 0)) + { + gimp_open_console_window (); + } +#endif + } + +#ifdef GIMP_CONSOLE_COMPILATION + no_interface = TRUE; +#endif + + context = g_option_context_new (_("[FILE|URI...]")); + g_option_context_set_summary (context, GIMP_NAME); + + g_option_context_add_main_entries (context, main_entries, GETTEXT_PACKAGE); + + app_libs_init (context, no_interface); + + if (! g_option_context_parse_strv (context, &argv, &error)) + { + if (error) + { + gimp_open_console_window (); + g_print ("%s\n", error->message); + g_error_free (error); + } + else + { + g_print ("%s\n", + _("GIMP could not initialize the graphical user interface.\n" + "Make sure a proper setup for your display environment " + "exists.")); + } + + app_exit (EXIT_FAILURE); + } + + if (no_interface || be_verbose || console_messages || batch_commands != NULL) + gimp_open_console_window (); + + if (no_interface) + new_instance = TRUE; + +#ifndef GIMP_CONSOLE_COMPILATION + if (! new_instance && gimp_unique_open (filenames, as_new)) + { + if (be_verbose) + g_print ("%s\n", + _("Another GIMP instance is already running.")); + + if (batch_commands) + gimp_unique_batch_run (batch_interpreter, batch_commands); + + gdk_notify_startup_complete (); + + return EXIT_SUCCESS; + } +#endif + + abort_message = sanity_check_early (); + if (abort_message) + app_abort (no_interface, abort_message); + + if (system_gimprc) + system_gimprc_file = g_file_new_for_commandline_arg (system_gimprc); + + if (user_gimprc) + user_gimprc_file = g_file_new_for_commandline_arg (user_gimprc); + + app_run (argv[0], + filenames, + system_gimprc_file, + user_gimprc_file, + session_name, + batch_interpreter, + batch_commands, + as_new, + no_interface, + no_data, + no_fonts, + no_splash, + be_verbose, + use_shm, + use_cpu_accel, + console_messages, + use_debug_handler, + show_playground, + show_debug_menu, + stack_trace_mode, + pdb_compat_mode, + backtrace_file); + + if (backtrace_file) + g_free (backtrace_file); + + if (system_gimprc_file) + g_object_unref (system_gimprc_file); + + if (user_gimprc_file) + g_object_unref (user_gimprc_file); + + g_strfreev (argv); + + g_option_context_free (context); + + return EXIT_SUCCESS; +} + + +#ifdef G_OS_WIN32 + +/* Provide WinMain in case we build GIMP as a subsystem:windows + * application. Well, we do. When built with mingw, though, user code + * execution still starts in main() in that case. So WinMain() gets + * used on MSVC builds only. + */ + +#ifdef __GNUC__ +# ifndef _stdcall +# define _stdcall __attribute__((stdcall)) +# endif +#endif + +int _stdcall +WinMain (struct HINSTANCE__ *hInstance, + struct HINSTANCE__ *hPrevInstance, + char *lpszCmdLine, + int nCmdShow) +{ + return main (__argc, __argv); +} + +#ifndef GIMP_CONSOLE_COMPILATION + +static void +wait_console_window (void) +{ + FILE *console = fopen ("CONOUT$", "w"); + + SetConsoleTitleW (g_utf8_to_utf16 (_("GIMP output. Type any character to close this window."), -1, NULL, NULL, NULL)); + fprintf (console, _("(Type any character to close this window)\n")); + fflush (console); + _getch (); +} + +static void +gimp_open_console_window (void) +{ + if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE || + (HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ()) + { + if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE) + freopen ("CONOUT$", "w", stdout); + + if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) + freopen ("CONOUT$", "w", stderr); + + SetConsoleTitleW (g_utf8_to_utf16 (_("GIMP output. You can minimize this window, but don't close it."), -1, NULL, NULL, NULL)); + + atexit (wait_console_window); + } +} +#endif + +#endif /* G_OS_WIN32 */ + + +static gboolean +gimp_option_fatal_warnings (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + GLogLevelFlags fatal_mask; + + fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); + fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; + + g_log_set_always_fatal (fatal_mask); + + return TRUE; +} + +static gboolean +gimp_option_stack_trace_mode (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + if (strcmp (value, "never") == 0) + stack_trace_mode = GIMP_STACK_TRACE_NEVER; + else if (strcmp (value, "query") == 0) + stack_trace_mode = GIMP_STACK_TRACE_QUERY; + else if (strcmp (value, "always") == 0) + stack_trace_mode = GIMP_STACK_TRACE_ALWAYS; + else + return FALSE; + + return TRUE; +} + +static gboolean +gimp_option_pdb_compat_mode (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + if (! strcmp (value, "off")) + pdb_compat_mode = GIMP_PDB_COMPAT_OFF; + else if (! strcmp (value, "on")) + pdb_compat_mode = GIMP_PDB_COMPAT_ON; + else if (! strcmp (value, "warn")) + pdb_compat_mode = GIMP_PDB_COMPAT_WARN; + else + return FALSE; + + return TRUE; +} + +static gboolean +gimp_option_dump_gimprc (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + GimpConfigDumpFormat format = GIMP_CONFIG_DUMP_NONE; + + gimp_open_console_window (); + + if (strcmp (option_name, "--dump-gimprc") == 0) + format = GIMP_CONFIG_DUMP_GIMPRC; + if (strcmp (option_name, "--dump-gimprc-system") == 0) + format = GIMP_CONFIG_DUMP_GIMPRC_SYSTEM; + else if (strcmp (option_name, "--dump-gimprc-manpage") == 0) + format = GIMP_CONFIG_DUMP_GIMPRC_MANPAGE; + + if (format) + { + Gimp *gimp; + gboolean success; + + babl_init (); + gimp = g_object_new (GIMP_TYPE_GIMP, NULL); + gimp_load_config (gimp, NULL, NULL); + + success = gimp_config_dump (G_OBJECT (gimp), format); + + g_object_unref (gimp); + + app_exit (success ? EXIT_SUCCESS : EXIT_FAILURE); + } + + return FALSE; +} + +static gboolean +gimp_option_dump_pdb_procedures_deprecated (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + Gimp *gimp; + GList *deprecated_procs; + GList *iter; + + babl_init (); + gimp = g_object_new (GIMP_TYPE_GIMP, NULL); + gimp_load_config (gimp, NULL, NULL); + + /* Make sure to turn on compatibility mode so deprecated procedures + * are included + */ + gimp->pdb_compat_mode = GIMP_PDB_COMPAT_ON; + + /* Initialize the list of procedures */ + internal_procs_init (gimp->pdb); + + /* Get deprecated procedures */ + deprecated_procs = gimp_pdb_get_deprecated_procedures (gimp->pdb); + + for (iter = deprecated_procs; iter; iter = g_list_next (iter)) + { + GimpProcedure *procedure = GIMP_PROCEDURE (iter->data); + + g_print ("%s\n", procedure->original_name); + } + + g_list_free (deprecated_procs); + + g_object_unref (gimp); + + app_exit (EXIT_SUCCESS); + + return FALSE; +} + +static void +gimp_show_version_and_exit (void) +{ + gimp_open_console_window (); + gimp_version_show (be_verbose); + + app_exit (EXIT_SUCCESS); +} + +static void +gimp_show_license_and_exit (void) +{ + gimp_open_console_window (); + gimp_version_show (be_verbose); + + g_print ("\n"); + g_print (GIMP_LICENSE); + g_print ("\n\n"); + + app_exit (EXIT_SUCCESS); +} + +static void +gimp_init_malloc (void) +{ +#ifdef GIMP_GLIB_MEM_PROFILER + g_mem_set_vtable (glib_mem_profiler_table); + g_atexit (g_mem_profile); +#endif + +#ifdef __GLIBC__ + /* Tweak memory allocation so that memory allocated in chunks >= 4k + * (64x64 pixel 1bpp tile) gets returned to the system when free()'d. + * + * The default value for M_MMAP_THRESHOLD in glibc-2.3 is 128k. + * This is said to be an empirically derived value that works well + * in most systems. Lowering it to 4k is thus probably not the ideal + * solution. + * + * An alternative to tuning this parameter would be to use + * malloc_trim(), for example after releasing a large tile-manager. + */ +#if 0 + mallopt (M_MMAP_THRESHOLD, TILE_WIDTH * TILE_HEIGHT); +#endif +#endif +} + +static void +gimp_init_i18n (void) +{ + /* We may change the locale later if the user specifies a language + * in the gimprc file. Here we are just initializing the locale + * according to the environment variables and set up the paths to + * the message catalogs. + */ + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE"-libgimp", gimp_locale_directory ()); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset (GETTEXT_PACKAGE"-libgimp", "UTF-8"); +#endif + + bindtextdomain (GETTEXT_PACKAGE, gimp_locale_directory ()); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif + + textdomain (GETTEXT_PACKAGE); +} diff --git a/app/menus/Makefile.am b/app/menus/Makefile.am new file mode 100644 index 0000000..32968bb --- /dev/null +++ b/app/menus/Makefile.am @@ -0,0 +1,34 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Menus\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappmenus.a + +libappmenus_a_SOURCES = \ + menus-types.h \ + menus.c \ + menus.h \ + dockable-menu.c \ + dockable-menu.h \ + file-menu.c \ + file-menu.h \ + filters-menu.c \ + filters-menu.h \ + image-menu.c \ + image-menu.h \ + plug-in-menus.c \ + plug-in-menus.h \ + tool-options-menu.c \ + tool-options-menu.h \ + window-menu.c \ + window-menu.h \ + windows-menu.c \ + windows-menu.h diff --git a/app/menus/Makefile.in b/app/menus/Makefile.in new file mode 100644 index 0000000..50da784 --- /dev/null +++ b/app/menus/Makefile.in @@ -0,0 +1,963 @@ +# 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 = app/menus +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 = +libappmenus_a_AR = $(AR) $(ARFLAGS) +libappmenus_a_LIBADD = +am_libappmenus_a_OBJECTS = menus.$(OBJEXT) dockable-menu.$(OBJEXT) \ + file-menu.$(OBJEXT) filters-menu.$(OBJEXT) \ + image-menu.$(OBJEXT) plug-in-menus.$(OBJEXT) \ + tool-options-menu.$(OBJEXT) window-menu.$(OBJEXT) \ + windows-menu.$(OBJEXT) +libappmenus_a_OBJECTS = $(am_libappmenus_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)/dockable-menu.Po \ + ./$(DEPDIR)/file-menu.Po ./$(DEPDIR)/filters-menu.Po \ + ./$(DEPDIR)/image-menu.Po ./$(DEPDIR)/menus.Po \ + ./$(DEPDIR)/plug-in-menus.Po ./$(DEPDIR)/tool-options-menu.Po \ + ./$(DEPDIR)/window-menu.Po ./$(DEPDIR)/windows-menu.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 = $(libappmenus_a_SOURCES) +DIST_SOURCES = $(libappmenus_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Menus\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libappmenus.a +libappmenus_a_SOURCES = \ + menus-types.h \ + menus.c \ + menus.h \ + dockable-menu.c \ + dockable-menu.h \ + file-menu.c \ + file-menu.h \ + filters-menu.c \ + filters-menu.h \ + image-menu.c \ + image-menu.h \ + plug-in-menus.c \ + plug-in-menus.h \ + tool-options-menu.c \ + tool-options-menu.h \ + window-menu.c \ + window-menu.h \ + windows-menu.c \ + windows-menu.h + +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 app/menus/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/menus/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) + +libappmenus.a: $(libappmenus_a_OBJECTS) $(libappmenus_a_DEPENDENCIES) $(EXTRA_libappmenus_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappmenus.a + $(AM_V_AR)$(libappmenus_a_AR) libappmenus.a $(libappmenus_a_OBJECTS) $(libappmenus_a_LIBADD) + $(AM_V_at)$(RANLIB) libappmenus.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dockable-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filters-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-menus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-options-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-menu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windows-menu.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)/dockable-menu.Po + -rm -f ./$(DEPDIR)/file-menu.Po + -rm -f ./$(DEPDIR)/filters-menu.Po + -rm -f ./$(DEPDIR)/image-menu.Po + -rm -f ./$(DEPDIR)/menus.Po + -rm -f ./$(DEPDIR)/plug-in-menus.Po + -rm -f ./$(DEPDIR)/tool-options-menu.Po + -rm -f ./$(DEPDIR)/window-menu.Po + -rm -f ./$(DEPDIR)/windows-menu.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)/dockable-menu.Po + -rm -f ./$(DEPDIR)/file-menu.Po + -rm -f ./$(DEPDIR)/filters-menu.Po + -rm -f ./$(DEPDIR)/image-menu.Po + -rm -f ./$(DEPDIR)/menus.Po + -rm -f ./$(DEPDIR)/plug-in-menus.Po + -rm -f ./$(DEPDIR)/tool-options-menu.Po + -rm -f ./$(DEPDIR)/window-menu.Po + -rm -f ./$(DEPDIR)/windows-menu.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/app/menus/dockable-menu.c b/app/menus/dockable-menu.c new file mode 100644 index 0000000..ccc6537 --- /dev/null +++ b/app/menus/dockable-menu.c @@ -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 . + */ + +#include "config.h" + +#include +#include + +#include "menus-types.h" + +#include "dockable-menu.h" +#include "window-menu.h" + + +void +dockable_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + window_menu_setup (manager, "dock", ui_path); +} diff --git a/app/menus/dockable-menu.h b/app/menus/dockable-menu.h new file mode 100644 index 0000000..1fd2483 --- /dev/null +++ b/app/menus/dockable-menu.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 . + */ + +#ifndef __DOCKABLE_MENU_H__ +#define __DOCKABLE_MENU_H__ + + +void dockable_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __DOCKABLE_MENU_H__ */ diff --git a/app/menus/file-menu.c b/app/menus/file-menu.c new file mode 100644 index 0000000..019b6b0 --- /dev/null +++ b/app/menus/file-menu.c @@ -0,0 +1,120 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpthumb/gimpthumb.h" + +#include "menus-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpviewable.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactionimpl.h" +#include "widgets/gimpuimanager.h" + +#include "file-menu.h" + + +static gboolean file_menu_open_recent_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + GimpAction *action); + + +void +file_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + gint n_entries; + guint merge_id; + gint i; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + + n_entries = GIMP_GUI_CONFIG (manager->gimp->config)->last_opened_size; + + merge_id = gimp_ui_manager_new_merge_id (manager); + + for (i = 0; i < n_entries; i++) + { + GtkWidget *widget; + gchar *action_name; + gchar *action_path; + gchar *full_path; + + action_name = g_strdup_printf ("file-open-recent-%02d", i + 1); + action_path = g_strdup_printf ("%s/File/Open Recent/Files", ui_path); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + full_path = g_strconcat (action_path, "/", action_name, NULL); + + widget = gimp_ui_manager_get_widget (manager, full_path); + + if (widget) + { + GimpAction *action; + + action = gimp_ui_manager_find_action (manager, "file", action_name); + + g_signal_connect_object (widget, "query-tooltip", + G_CALLBACK (file_menu_open_recent_query_tooltip), + action, 0); + } + + g_free (action_name); + g_free (action_path); + g_free (full_path); + } +} + +static gboolean +file_menu_open_recent_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + GimpAction *action) +{ + GimpActionImpl *impl = GIMP_ACTION_IMPL (action); + gchar *text; + + text = gtk_widget_get_tooltip_text (widget); + gtk_tooltip_set_text (tooltip, text); + g_free (text); + + gtk_tooltip_set_icon (tooltip, + gimp_viewable_get_pixbuf (impl->viewable, + impl->context, + GIMP_THUMB_SIZE_NORMAL, + GIMP_THUMB_SIZE_NORMAL)); + + return TRUE; +} diff --git a/app/menus/file-menu.h b/app/menus/file-menu.h new file mode 100644 index 0000000..e439e8d --- /dev/null +++ b/app/menus/file-menu.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 . + */ + +#ifndef __FILE_MENU_H__ +#define __FILE_MENU_H__ + + +void file_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __FILE_MENU_H__ */ diff --git a/app/menus/filters-menu.c b/app/menus/filters-menu.c new file mode 100644 index 0000000..8ffbcc3 --- /dev/null +++ b/app/menus/filters-menu.c @@ -0,0 +1,64 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "menus-types.h" + +#include "core/gimp.h" +#include "core/gimp-filter-history.h" + +#include "widgets/gimpuimanager.h" + +#include "filters-menu.h" + + +/* public functions */ + +void +filters_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + guint merge_id; + gint i; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + + merge_id = gimp_ui_manager_new_merge_id (manager); + + for (i = 0; i < gimp_filter_history_size (manager->gimp); i++) + { + gchar *action_name; + gchar *action_path; + + action_name = g_strdup_printf ("filters-recent-%02d", i + 1); + action_path = g_strdup_printf ("%s/Filters/Recently Used/Filters", + ui_path); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + g_free (action_path); + } +} diff --git a/app/menus/filters-menu.h b/app/menus/filters-menu.h new file mode 100644 index 0000000..9c1a705 --- /dev/null +++ b/app/menus/filters-menu.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 . + */ + +#ifndef __FILTERS_MENU_H__ +#define __FILTERS_MENU_H__ + + +void filters_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __FILTERS_MENU_H__ */ diff --git a/app/menus/image-menu.c b/app/menus/image-menu.c new file mode 100644 index 0000000..64bd9cd --- /dev/null +++ b/app/menus/image-menu.c @@ -0,0 +1,52 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "menus-types.h" + +#include "file-menu.h" +#include "filters-menu.h" +#include "image-menu.h" +#include "plug-in-menus.h" +#include "window-menu.h" +#include "windows-menu.h" + + +void +image_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + gchar *path; + + if (! strcmp (ui_path, "/dummy-menubar")) + ui_path = "/dummy-menubar/image-popup"; + + file_menu_setup (manager, ui_path); + windows_menu_setup (manager, ui_path); + plug_in_menus_setup (manager, ui_path); + filters_menu_setup (manager, ui_path); + + path = g_strconcat (ui_path, "/View", NULL); + window_menu_setup (manager, "view", path); + g_free (path); +} diff --git a/app/menus/image-menu.h b/app/menus/image-menu.h new file mode 100644 index 0000000..0ae5418 --- /dev/null +++ b/app/menus/image-menu.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 . + */ + +#ifndef __IMAGE_MENU_H__ +#define __IMAGE_MENU_H__ + + +void image_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __IMAGE_MENU_H__ */ diff --git a/app/menus/menus-types.h b/app/menus/menus-types.h new file mode 100644 index 0000000..1c05529 --- /dev/null +++ b/app/menus/menus-types.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 . + */ + +#ifndef __MENUS_TYPES_H__ +#define __MENUS_TYPES_H__ + + +#include "actions/actions-types.h" + + +#endif /* __MENUS_TYPES_H__ */ diff --git a/app/menus/menus.c b/app/menus/menus.c new file mode 100644 index 0000000..79a3f25 --- /dev/null +++ b/app/menus/menus.c @@ -0,0 +1,519 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "menus-types.h" + +#include "config/gimpconfig-file.h" +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "widgets/gimpactionfactory.h" +#include "widgets/gimpdashboard.h" +#include "widgets/gimpmenufactory.h" + +#include "dockable-menu.h" +#include "image-menu.h" +#include "menus.h" +#include "plug-in-menus.h" +#include "tool-options-menu.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void menus_can_change_accels (GimpGuiConfig *config); +static void menus_remove_accels (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed); + + +/* global variables */ + +GimpMenuFactory * global_menu_factory = NULL; + + +/* private variables */ + +static gboolean menurc_deleted = FALSE; + + +/* public functions */ + +void +menus_init (Gimp *gimp, + GimpActionFactory *action_factory) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_ACTION_FACTORY (action_factory)); + g_return_if_fail (global_menu_factory == NULL); + + /* We need to make sure the property is installed before using it */ + g_type_class_ref (GTK_TYPE_MENU); + + menus_can_change_accels (GIMP_GUI_CONFIG (gimp->config)); + + g_signal_connect (gimp->config, "notify::can-change-accels", + G_CALLBACK (menus_can_change_accels), NULL); + + global_menu_factory = gimp_menu_factory_new (gimp, action_factory); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "file", + "context", + "debug", + "help", + "edit", + "select", + "view", + "image", + "drawable", + "layers", + "channels", + "vectors", + "tools", + "dialogs", + "windows", + "plug-in", + "filters", + "quick-mask", + NULL, + "/image-menubar", + "image-menu.xml", image_menu_setup, + "/dummy-menubar", + "image-menu.xml", image_menu_setup, + "/quick-mask-popup", + "quick-mask-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "file", + "context", + "help", + "edit", + "select", + "view", + "image", + "drawable", + "layers", + "channels", + "vectors", + "tools", + "windows", + "dialogs", + "plug-in", + "filters", + "quick-mask", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "file", + "context", + "edit", + "select", + "view", + "image", + "drawable", + "layers", + "channels", + "vectors", + "tools", + "windows", + "dialogs", + "plug-in", + "quick-mask", + "dock", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "layers", + "plug-in", + "filters", + NULL, + "/layers-popup", + "layers-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "channels", + "plug-in", + "filters", + NULL, + "/channels-popup", + "channels-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "vectors", + "plug-in", + NULL, + "/vectors-popup", + "vectors-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "colormap", + "plug-in", + NULL, + "/colormap-popup", + "colormap-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "dockable", + "dock", + NULL, + "/dockable-popup", + "dockable-menu.xml", dockable_menu_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "brushes", + "plug-in", + NULL, + "/brushes-popup", + "brushes-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "dynamics", + "plug-in", + NULL, + "/dynamics-popup", + "dynamics-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "mypaint-brushes", + "plug-in", + NULL, + "/mypaint-brushes-popup", + "mypaint-brushes-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "patterns", + "plug-in", + NULL, + "/patterns-popup", + "patterns-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "gradients", + "plug-in", + NULL, + "/gradients-popup", + "gradients-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "palettes", + "plug-in", + NULL, + "/palettes-popup", + "palettes-menu.xml", plug_in_menus_setup, + NULL); + + + gimp_menu_factory_manager_register (global_menu_factory, "", + "tool-presets", + "plug-in", + NULL, + "/tool-presets-popup", + "tool-presets-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "fonts", + "plug-in", + NULL, + "/fonts-popup", + "fonts-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "buffers", + "plug-in", + NULL, + "/buffers-popup", + "buffers-menu.xml", plug_in_menus_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "documents", + NULL, + "/documents-popup", + "documents-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "templates", + NULL, + "/templates-popup", + "templates-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "images", + NULL, + "/images-popup", + "images-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "brush-editor", + NULL, + "/brush-editor-popup", + "brush-editor-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "dynamics-editor", + NULL, + "/dynamics-editor-popup", + "dynamics-editor-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "gradient-editor", + NULL, + "/gradient-editor-popup", + "gradient-editor-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "palette-editor", + NULL, + "/palette-editor-popup", + "palette-editor-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "tool-preset-editor", + NULL, + "/tool-preset-editor-popup", + "tool-preset-editor-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "select", + "vectors", + NULL, + "/selection-popup", + "selection-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "view", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "edit", + NULL, + "/undo-popup", + "undo-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "error-console", + NULL, + "/error-console-popup", + "error-console-menu.xml", NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "tool-options", + NULL, + "/tool-options-popup", + "tool-options-menu.xml", + tool_options_menu_setup, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "text-editor", + NULL, + "/text-editor-toolbar", + "text-editor-toolbar.xml", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "text-tool", + NULL, + "/text-tool-popup", + "text-tool-menu.xml", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "cursor-info", + NULL, + "/cursor-info-popup", + "cursor-info-menu.xml", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "sample-points", + NULL, + "/sample-points-popup", + "sample-points-menu.xml", + NULL, + NULL); + + gimp_menu_factory_manager_register (global_menu_factory, "", + "dashboard", + NULL, + "/dashboard-popup", + "dashboard-menu.xml", gimp_dashboard_menu_setup, + NULL); +} + +void +menus_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (global_menu_factory != NULL); + + g_object_unref (global_menu_factory); + global_menu_factory = NULL; + + g_signal_handlers_disconnect_by_func (gimp->config, + menus_can_change_accels, + NULL); +} + +void +menus_restore (Gimp *gimp) +{ + gchar *filename; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + filename = gimp_personal_rc_file ("menurc"); + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_filename_to_utf8 (filename)); + + gtk_accel_map_load (filename); + g_free (filename); +} + +void +menus_save (Gimp *gimp, + gboolean always_save) +{ + gchar *filename; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + if (menurc_deleted && ! always_save) + return; + + filename = gimp_personal_rc_file ("menurc"); + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_filename_to_utf8 (filename)); + + gtk_accel_map_save (filename); + g_free (filename); + + menurc_deleted = FALSE; +} + +gboolean +menus_clear (Gimp *gimp, + GError **error) +{ + GFile *file; + GFile *source; + gboolean success = TRUE; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = gimp_directory_file ("menurc", NULL); + source = gimp_sysconf_directory_file ("menurc", NULL); + + if (g_file_copy (source, file, G_FILE_COPY_OVERWRITE, + NULL, NULL, NULL, NULL)) + { + menurc_deleted = TRUE; + } + else if (! g_file_delete (file, NULL, &my_error) && + my_error->code != G_IO_ERROR_NOT_FOUND) + { + g_set_error (error, my_error->domain, my_error->code, + _("Deleting \"%s\" failed: %s"), + gimp_file_get_utf8_name (file), my_error->message); + success = FALSE; + } + else + { + menurc_deleted = TRUE; + } + + g_clear_error (&my_error); + g_object_unref (source); + g_object_unref (file); + + return success; +} + +void +menus_remove (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gtk_accel_map_foreach (gimp, menus_remove_accels); +} + + +/* private functions */ + +static void +menus_can_change_accels (GimpGuiConfig *config) +{ + g_object_set (gtk_settings_get_for_screen (gdk_screen_get_default ()), + "gtk-can-change-accels", config->can_change_accels, + NULL); +} + +static void +menus_remove_accels (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed) +{ + gtk_accel_map_change_entry (accel_path, 0, 0, TRUE); +} diff --git a/app/menus/menus.h b/app/menus/menus.h new file mode 100644 index 0000000..ba59045 --- /dev/null +++ b/app/menus/menus.h @@ -0,0 +1,38 @@ +/* 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 . + */ + +#ifndef __MENUS_H__ +#define __MENUS_H__ + + +extern GimpMenuFactory *global_menu_factory; + + +void menus_init (Gimp *gimp, + GimpActionFactory *action_factory); +void menus_exit (Gimp *gimp); + +void menus_restore (Gimp *gimp); +void menus_save (Gimp *gimp, + gboolean always_save); + +gboolean menus_clear (Gimp *gimp, + GError **error); +void menus_remove (Gimp *gimp); + + +#endif /* __MENUS_H__ */ diff --git a/app/menus/plug-in-menus.c b/app/menus/plug-in-menus.c new file mode 100644 index 0000000..8efdeb4 --- /dev/null +++ b/app/menus/plug-in-menus.c @@ -0,0 +1,574 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "menus-types.h" + +#include "config/gimpcoreconfig.h" + +#include "core/gimp.h" + +#include "plug-in/gimppluginmanager.h" +#include "plug-in/gimppluginmanager-locale-domain.h" +#include "plug-in/gimppluginprocedure.h" + +#include "widgets/gimpuimanager.h" + +#include "plug-in-menus.h" + +#include "gimp-log.h" +#include "gimp-intl.h" + + +typedef struct _PlugInMenuEntry PlugInMenuEntry; + +struct _PlugInMenuEntry +{ + GimpPlugInProcedure *proc; + const gchar *menu_path; +}; + + +/* local function prototypes */ + +static void plug_in_menus_register_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpUIManager *manager); +static void plug_in_menus_unregister_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpUIManager *manager); +static void plug_in_menus_menu_path_added (GimpPlugInProcedure *plug_in_proc, + const gchar *menu_path, + GimpUIManager *manager); +static void plug_in_menus_add_proc (GimpUIManager *manager, + const gchar *ui_path, + GimpPlugInProcedure *proc, + const gchar *menu_path); +static void plug_in_menus_tree_insert (GTree *entries, + const gchar * path, + PlugInMenuEntry *entry); +static gboolean plug_in_menus_tree_traverse (gpointer key, + PlugInMenuEntry *entry, + GimpUIManager *manager); +static gchar * plug_in_menus_build_path (GimpUIManager *manager, + const gchar *ui_path, + guint merge_id, + const gchar *menu_path, + gboolean for_menu); +static void plug_in_menu_entry_free (PlugInMenuEntry *entry); + + +/* public functions */ + +void +plug_in_menus_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + GimpPlugInManager *plug_in_manager; + GTree *menu_entries; + GSList *list; + guint merge_id; + gint i; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + + plug_in_manager = manager->gimp->plug_in_manager; + + merge_id = gimp_ui_manager_new_merge_id (manager); + + for (i = 0; i < manager->gimp->config->filter_history_size; i++) + { + gchar *action_name; + gchar *action_path; + + action_name = g_strdup_printf ("filter-recent-%02d", i + 1); + action_path = g_strdup_printf ("%s/Filters/Recently Used/Plug-ins", + ui_path); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + g_free (action_path); + } + + menu_entries = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, + g_free, + (GDestroyNotify) plug_in_menu_entry_free); + + for (list = plug_in_manager->plug_in_procedures; + list; + list = g_slist_next (list)) + { + GimpPlugInProcedure *plug_in_proc = list->data; + + if (! plug_in_proc->file) + continue; + + g_signal_connect_object (plug_in_proc, "menu-path-added", + G_CALLBACK (plug_in_menus_menu_path_added), + manager, 0); + + if (plug_in_proc->menu_paths && + ! plug_in_proc->file_proc) + { + GList *path; + + for (path = plug_in_proc->menu_paths; path; path = g_list_next (path)) + { + if (g_str_has_prefix (path->data, manager->name)) + { + PlugInMenuEntry *entry = g_slice_new0 (PlugInMenuEntry); + GFile *file; + const gchar *locale_domain; + + entry->proc = plug_in_proc; + entry->menu_path = path->data; + + file = gimp_plug_in_procedure_get_file (plug_in_proc); + + locale_domain = + gimp_plug_in_manager_get_locale_domain (plug_in_manager, + file, NULL); + + if (plug_in_proc->menu_label) + { + gchar *menu; + + menu = g_strconcat (dgettext (locale_domain, + path->data), + "/", + dgettext (locale_domain, + plug_in_proc->menu_label), + NULL); + + plug_in_menus_tree_insert (menu_entries, menu, entry); + g_free (menu); + } + else + { + plug_in_menus_tree_insert (menu_entries, + dgettext (locale_domain, + path->data), + entry); + } + } + } + } + } + + g_object_set_data (G_OBJECT (manager), "ui-path", (gpointer) ui_path); + + g_tree_foreach (menu_entries, + (GTraverseFunc) plug_in_menus_tree_traverse, + manager); + + g_object_set_data (G_OBJECT (manager), "ui-path", NULL); + + g_tree_destroy (menu_entries); + + g_signal_connect_object (manager->gimp->pdb, "register-procedure", + G_CALLBACK (plug_in_menus_register_procedure), + manager, 0); + g_signal_connect_object (manager->gimp->pdb, "unregister-procedure", + G_CALLBACK (plug_in_menus_unregister_procedure), + manager, 0); +} + + +/* private functions */ + +static void +plug_in_menus_register_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpUIManager *manager) +{ + if (GIMP_IS_PLUG_IN_PROCEDURE (procedure)) + { + GimpPlugInProcedure *plug_in_proc = GIMP_PLUG_IN_PROCEDURE (procedure); + + g_signal_connect_object (plug_in_proc, "menu-path-added", + G_CALLBACK (plug_in_menus_menu_path_added), + manager, 0); + + if ((plug_in_proc->menu_label || plug_in_proc->menu_paths) && + ! plug_in_proc->file_proc) + { + GList *list; + + + GIMP_LOG (MENUS, "register procedure: %s", + gimp_object_get_name (procedure)); + + for (list = plug_in_proc->menu_paths; list; list = g_list_next (list)) + plug_in_menus_menu_path_added (plug_in_proc, list->data, manager); + } + } +} + +static void +plug_in_menus_unregister_procedure (GimpPDB *pdb, + GimpProcedure *procedure, + GimpUIManager *manager) +{ + if (GIMP_IS_PLUG_IN_PROCEDURE (procedure)) + { + GimpPlugInProcedure *plug_in_proc = GIMP_PLUG_IN_PROCEDURE (procedure); + + g_signal_handlers_disconnect_by_func (plug_in_proc, + plug_in_menus_menu_path_added, + manager); + + if ((plug_in_proc->menu_label || plug_in_proc->menu_paths) && + ! plug_in_proc->file_proc) + { + GList *list; + + GIMP_LOG (MENUS, "unregister procedure: %s", + gimp_object_get_name (procedure)); + + for (list = plug_in_proc->menu_paths; list; list = g_list_next (list)) + { + if (g_str_has_prefix (list->data, manager->name)) + { + gchar *merge_key; + guint merge_id; + + merge_key = g_strdup_printf ("%s-merge-id", + gimp_object_get_name (plug_in_proc)); + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + g_free (merge_key); + + if (merge_id) + gimp_ui_manager_remove_ui (manager, merge_id); + + break; + } + } + } + } +} + +static void +plug_in_menus_menu_path_added (GimpPlugInProcedure *plug_in_proc, + const gchar *menu_path, + GimpUIManager *manager) +{ + GIMP_LOG (MENUS, "menu path added: %s (%s)", + gimp_object_get_name (plug_in_proc), menu_path); + + if (g_str_has_prefix (menu_path, manager->name)) + { + if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/image-menubar", + plug_in_proc, menu_path); + plug_in_menus_add_proc (manager, "/dummy-menubar/image-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/layers-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/channels-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/vectors-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/colormap-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/brushes-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/dynamics-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/mypaint-brushes-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/gradients-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/palettes-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/patterns-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/tool-presets-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/fonts-popup", + plug_in_proc, menu_path); + } + else if (! strcmp (manager->name, "")) + { + plug_in_menus_add_proc (manager, "/buffers-popup", + plug_in_proc, menu_path); + } + } +} + +static void +plug_in_menus_add_proc (GimpUIManager *manager, + const gchar *ui_path, + GimpPlugInProcedure *proc, + const gchar *menu_path) +{ + gchar *path; + gchar *merge_key; + gchar *stripped_path; + gchar *action_path; + guint merge_id; + guint menu_merge_id; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc)); + + path = g_strdup (menu_path); + + if (! proc->menu_label) + { + gchar *p; + + if (! path) + return; + + p = strrchr (path, '/'); + if (! p) + { + g_free (path); + return; + } + + *p = '\0'; + } + + merge_key = g_strdup_printf ("%s-merge-id", gimp_object_get_name (proc)); + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + + if (! merge_id) + { + merge_id = gimp_ui_manager_new_merge_id (manager); + g_object_set_data (G_OBJECT (manager), merge_key, + GUINT_TO_POINTER (merge_id)); + } + + g_free (merge_key); + + menu_merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + "plug-in-menu-merge-id")); + + if (! menu_merge_id) + { + menu_merge_id = gimp_ui_manager_new_merge_id (manager); + g_object_set_data (G_OBJECT (manager), "plug-in-menu-merge-id", + GUINT_TO_POINTER (menu_merge_id)); + } + + stripped_path = gimp_strip_uline (path); + action_path = plug_in_menus_build_path (manager, ui_path, menu_merge_id, + stripped_path, FALSE); + g_free (stripped_path); + + if (! action_path) + { + g_free (path); + return; + } + + GIMP_LOG (MENUS, "adding menu item for '%s' (@ %s)", + gimp_object_get_name (proc), action_path); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, + gimp_object_get_name (proc), + gimp_object_get_name (proc), + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_path); + g_free (path); +} + +static void +plug_in_menus_tree_insert (GTree *entries, + const gchar *path, + PlugInMenuEntry *entry) +{ + gchar *strip = gimp_strip_uline (path); + gchar *key; + + /* Append the procedure name to the menu path in order to get a unique + * key even if two procedures are installed to the same menu entry. + */ + key = g_strconcat (strip, gimp_object_get_name (entry->proc), NULL); + + g_tree_insert (entries, g_utf8_collate_key (key, -1), entry); + + g_free (key); + g_free (strip); +} + +static gboolean +plug_in_menus_tree_traverse (gpointer key, + PlugInMenuEntry *entry, + GimpUIManager *manager) +{ + const gchar *ui_path = g_object_get_data (G_OBJECT (manager), "ui-path"); + + plug_in_menus_add_proc (manager, ui_path, entry->proc, entry->menu_path); + + return FALSE; +} + +static gchar * +plug_in_menus_build_path (GimpUIManager *manager, + const gchar *ui_path, + guint merge_id, + const gchar *menu_path, + gboolean for_menu) +{ + gchar *action_path; + + if (! strchr (menu_path, '/')) + { + action_path = g_strdup (ui_path); + goto make_placeholder; + } + + action_path = g_strdup_printf ("%s%s", ui_path, strchr (menu_path, '/')); + + if (! gimp_ui_manager_get_widget (manager, action_path)) + { + gchar *parent_menu_path = g_strdup (menu_path); + gchar *parent_action_path = NULL; + gchar *menu_item_name; + + menu_item_name = strrchr (parent_menu_path, '/'); + + if (menu_item_name) + { + *menu_item_name++ = '\0'; + + parent_action_path = plug_in_menus_build_path (manager, + ui_path, merge_id, + parent_menu_path, TRUE); + } + + if (parent_action_path) + { + g_free (action_path); + action_path = g_strdup_printf ("%s/%s", + parent_action_path, menu_item_name); + + if (! gimp_ui_manager_get_widget (manager, action_path)) + { + GIMP_LOG (MENUS, "adding menu '%s' at path '%s' for action '%s'", + menu_item_name, action_path, menu_path); + + gimp_ui_manager_add_ui (manager, merge_id, + parent_action_path, menu_item_name, + menu_path, + GTK_UI_MANAGER_MENU, + FALSE); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, "Menus", NULL, + GTK_UI_MANAGER_PLACEHOLDER, + FALSE); + gimp_ui_manager_add_ui (manager, merge_id, + action_path, "Separator", NULL, + GTK_UI_MANAGER_SEPARATOR, + FALSE); + } + + g_free (parent_action_path); + } + else + { + g_free (action_path); + action_path = NULL; + } + + g_free (parent_menu_path); + } + + make_placeholder: + + if (action_path && for_menu) + { + gchar *placeholder_path = g_strdup_printf ("%s/%s", action_path, "Menus"); + + if (gimp_ui_manager_get_widget (manager, placeholder_path)) + { + g_free (action_path); + + return placeholder_path; + } + + g_free (placeholder_path); + } + + return action_path; +} + +static void +plug_in_menu_entry_free (PlugInMenuEntry *entry) +{ + g_slice_free (PlugInMenuEntry, entry); +} diff --git a/app/menus/plug-in-menus.h b/app/menus/plug-in-menus.h new file mode 100644 index 0000000..4e07e2e --- /dev/null +++ b/app/menus/plug-in-menus.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 . + */ + +#ifndef __PLUG_IN_MENUS_H__ +#define __PLUG_IN_MENUS_H__ + + +void plug_in_menus_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __PLUG_IN_MENUS_H__ */ diff --git a/app/menus/tool-options-menu.c b/app/menus/tool-options-menu.c new file mode 100644 index 0000000..6ba21a1 --- /dev/null +++ b/app/menus/tool-options-menu.c @@ -0,0 +1,161 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpwidgets/gimpwidgets.h" + +#include "menus-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimplist.h" +#include "core/gimptoolinfo.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpuimanager.h" + +#include "tool-options-menu.h" + + +/* local function prototypes */ + +static void tool_options_menu_update (GimpUIManager *manager, + gpointer update_data, + const gchar *ui_path); +static void tool_options_menu_update_after (GimpUIManager *manager, + gpointer update_data, + const gchar *ui_path); +static void tool_options_menu_update_presets (GimpUIManager *manager, + guint merge_id, + const gchar *ui_path, + const gchar *menu_path, + const gchar *which_action, + GimpContainer *presets); + + +/* public functions */ + +void +tool_options_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + g_signal_connect (manager, "update", + G_CALLBACK (tool_options_menu_update), + (gpointer) ui_path); + g_signal_connect_after (manager, "update", + G_CALLBACK (tool_options_menu_update_after), + (gpointer) ui_path); +} + + +/* private functions */ + +static void +tool_options_menu_update (GimpUIManager *manager, + gpointer update_data, + const gchar *ui_path) +{ + guint merge_id; + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + "tool-options-merge-id")); + + if (merge_id) + { + gimp_ui_manager_remove_ui (manager, merge_id); + + g_object_set_data (G_OBJECT (manager), "tool-options-merge-id", + GINT_TO_POINTER (0)); + + gimp_ui_manager_ensure_update (manager); + } +} + +static void +tool_options_menu_update_after (GimpUIManager *manager, + gpointer update_data, + const gchar *ui_path) +{ + GimpContext *context; + GimpToolInfo *tool_info; + guint merge_id; + + context = gimp_get_user_context (manager->gimp); + tool_info = gimp_context_get_tool (context); + + if (! tool_info->presets) + return; + + merge_id = gimp_ui_manager_new_merge_id (manager); + + g_object_set_data (G_OBJECT (manager), "tool-options-merge-id", + GUINT_TO_POINTER (merge_id)); + + tool_options_menu_update_presets (manager, merge_id, ui_path, + "Save", "save", + tool_info->presets); + + tool_options_menu_update_presets (manager, merge_id, ui_path, + "Restore", "restore", + tool_info->presets); + + tool_options_menu_update_presets (manager, merge_id, ui_path, + "Edit", "edit", + tool_info->presets); + + tool_options_menu_update_presets (manager, merge_id, ui_path, + "Delete", "delete", + tool_info->presets); + + gimp_ui_manager_ensure_update (manager); +} + +static void +tool_options_menu_update_presets (GimpUIManager *manager, + guint merge_id, + const gchar *ui_path, + const gchar *menu_path, + const gchar *which_action, + GimpContainer *presets) +{ + gint n_children; + gint i; + + n_children = gimp_container_get_n_children (presets); + + for (i = 0; i < n_children; i++) + { + gchar *action_name; + gchar *path; + + action_name = g_strdup_printf ("tool-options-%s-preset-%03d", + which_action, i); + path = g_strdup_printf ("%s/%s", ui_path, menu_path); + + gimp_ui_manager_add_ui (manager, merge_id, + path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + g_free (path); + } +} diff --git a/app/menus/tool-options-menu.h b/app/menus/tool-options-menu.h new file mode 100644 index 0000000..9fbc291 --- /dev/null +++ b/app/menus/tool-options-menu.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 . + */ + +#ifndef __TOOL_OPTIONS_MENU_H__ +#define __TOOL_OPTIONS_MENU_H__ + + +void tool_options_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __TOOL_OPTIONS_MENU_H__ */ diff --git a/app/menus/window-menu.c b/app/menus/window-menu.c new file mode 100644 index 0000000..7391e92 --- /dev/null +++ b/app/menus/window-menu.c @@ -0,0 +1,168 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "menus-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" + +#include "widgets/gimpuimanager.h" + +#include "window-menu.h" + + +/* private functions */ + +static void window_menu_display_opened (GdkDisplayManager *disp_manager, + GdkDisplay *display, + GimpUIManager *manager); +static void window_menu_display_closed (GdkDisplay *display, + gboolean is_error, + GimpUIManager *manager); + + +/* public functions */ + +void +window_menu_setup (GimpUIManager *manager, + const gchar *group_name, + const gchar *ui_path) +{ + GdkDisplayManager *disp_manager = gdk_display_manager_get (); + GSList *displays; + GSList *list; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + + g_object_set_data_full (G_OBJECT (manager), "move-to-screen-group-name", + g_strdup (group_name), + (GDestroyNotify) g_free); + g_object_set_data_full (G_OBJECT (manager), "move-to-screen-ui-path", + g_strdup (ui_path), + (GDestroyNotify) g_free); + + displays = gdk_display_manager_list_displays (disp_manager); + + /* present displays in the order in which they were opened */ + displays = g_slist_reverse (displays); + + for (list = displays; list; list = g_slist_next (list)) + { + window_menu_display_opened (disp_manager, list->data, manager); + } + + g_slist_free (displays); + + g_signal_connect_object (disp_manager, "display-opened", + G_CALLBACK (window_menu_display_opened), + G_OBJECT (manager), 0); +} + + +/* private functions */ + +static void +window_menu_display_opened (GdkDisplayManager *disp_manager, + GdkDisplay *display, + GimpUIManager *manager) +{ + const gchar *group_name; + const gchar *ui_path; + const gchar *display_name; + gchar *action_path; + gchar *merge_key; + guint merge_id; + gint n_screens; + gint i; + + group_name = g_object_get_data (G_OBJECT (manager), + "move-to-screen-group-name"); + ui_path = g_object_get_data (G_OBJECT (manager), + "move-to-screen-ui-path"); + + action_path = g_strdup_printf ("%s/Move to Screen", ui_path); + + display_name = gdk_display_get_name (display); + if (! display_name) + display_name = "eek"; + + merge_key = g_strdup_printf ("%s-display-merge-id", display_name); + + merge_id = gimp_ui_manager_new_merge_id (manager); + g_object_set_data (G_OBJECT (manager), merge_key, + GUINT_TO_POINTER (merge_id)); + + g_free (merge_key); + + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) + { + GdkScreen *screen; + gchar *screen_name; + gchar *action_name; + + screen = gdk_display_get_screen (display, i); + + screen_name = gdk_screen_make_display_name (screen); + action_name = g_strdup_printf ("%s-move-to-screen-%s", + group_name, screen_name); + g_free (screen_name); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + } + + g_free (action_path); + + g_signal_connect_object (display, "closed", + G_CALLBACK (window_menu_display_closed), + G_OBJECT (manager), 0); +} + +static void +window_menu_display_closed (GdkDisplay *display, + gboolean is_error, + GimpUIManager *manager) +{ + const gchar *display_name; + gchar *merge_key; + guint merge_id; + + display_name = gdk_display_get_name (display); + if (! display_name) + display_name = "eek"; + + merge_key = g_strdup_printf ("%s-display-merge-id", display_name); + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + g_free (merge_key); + + if (merge_id) + gimp_ui_manager_remove_ui (manager, merge_id); +} diff --git a/app/menus/window-menu.h b/app/menus/window-menu.h new file mode 100644 index 0000000..acce261 --- /dev/null +++ b/app/menus/window-menu.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 . + */ + +#ifndef __WINDOW_MENU_H__ +#define __WINDOW_MENU_H__ + + +void window_menu_setup (GimpUIManager *manager, + const gchar *group_name, + const gchar *ui_path); + + +#endif /* __WINDOW_MENU_H__ */ diff --git a/app/menus/windows-menu.c b/app/menus/windows-menu.c new file mode 100644 index 0000000..9e203e0 --- /dev/null +++ b/app/menus/windows-menu.c @@ -0,0 +1,440 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpthumb/gimpthumb.h" + +#include "menus-types.h" + +#include "config/gimpguiconfig.h" + +#include "core/gimp.h" +#include "core/gimpimage.h" +#include "core/gimplist.h" +#include "core/gimpviewable.h" + +#include "widgets/gimpaction.h" +#include "widgets/gimpactionimpl.h" +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimpsessioninfo.h" +#include "widgets/gimpuimanager.h" + +#include "display/gimpdisplay.h" + +#include "dialogs/dialogs.h" + +#include "actions/windows-actions.h" + +#include "windows-menu.h" + + +static void windows_menu_display_add (GimpContainer *container, + GimpDisplay *display, + GimpUIManager *manager); +static void windows_menu_display_remove (GimpContainer *container, + GimpDisplay *display, + GimpUIManager *manager); +static void windows_menu_display_reorder (GimpContainer *container, + GimpDisplay *display, + gint new_index, + GimpUIManager *manager); +static void windows_menu_image_notify (GimpDisplay *display, + const GParamSpec *unused, + GimpUIManager *manager); +static void windows_menu_dock_window_added (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpUIManager *manager); +static void windows_menu_dock_window_removed (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpUIManager *manager); +static gchar * windows_menu_dock_window_to_merge_id (GimpDockWindow *dock_window); +static void windows_menu_recent_add (GimpContainer *container, + GimpSessionInfo *info, + GimpUIManager *manager); +static void windows_menu_recent_remove (GimpContainer *container, + GimpSessionInfo *info, + GimpUIManager *manager); +static gboolean windows_menu_display_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + GimpAction *action); + + +void +windows_menu_setup (GimpUIManager *manager, + const gchar *ui_path) +{ + GList *list; + + g_return_if_fail (GIMP_IS_UI_MANAGER (manager)); + g_return_if_fail (ui_path != NULL); + + g_object_set_data (G_OBJECT (manager), "image-menu-ui-path", + (gpointer) ui_path); + + g_signal_connect_object (manager->gimp->displays, "add", + G_CALLBACK (windows_menu_display_add), + manager, 0); + g_signal_connect_object (manager->gimp->displays, "remove", + G_CALLBACK (windows_menu_display_remove), + manager, 0); + g_signal_connect_object (manager->gimp->displays, "reorder", + G_CALLBACK (windows_menu_display_reorder), + manager, 0); + + for (list = gimp_get_display_iter (manager->gimp); + list; + list = g_list_next (list)) + { + GimpDisplay *display = list->data; + + windows_menu_display_add (manager->gimp->displays, display, manager); + } + + g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-added", + G_CALLBACK (windows_menu_dock_window_added), + manager, 0); + g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-removed", + G_CALLBACK (windows_menu_dock_window_removed), + manager, 0); + + for (list = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ()); + list; + list = g_list_next (list)) + { + GimpDockWindow *dock_window = list->data; + + if (GIMP_IS_DOCK_WINDOW (dock_window)) + windows_menu_dock_window_added (gimp_dialog_factory_get_singleton (), + dock_window, + manager); + } + + g_signal_connect_object (global_recent_docks, "add", + G_CALLBACK (windows_menu_recent_add), + manager, 0); + g_signal_connect_object (global_recent_docks, "remove", + G_CALLBACK (windows_menu_recent_remove), + manager, 0); + + for (list = g_list_last (GIMP_LIST (global_recent_docks)->queue->head); + list; + list = g_list_previous (list)) + { + GimpSessionInfo *info = list->data; + + windows_menu_recent_add (global_recent_docks, info, manager); + } +} + + +/* private functions */ + +static void +windows_menu_display_add (GimpContainer *container, + GimpDisplay *display, + GimpUIManager *manager) +{ + g_signal_connect_object (display, "notify::image", + G_CALLBACK (windows_menu_image_notify), + manager, 0); + + if (gimp_display_get_image (display)) + windows_menu_image_notify (display, NULL, manager); +} + +static void +windows_menu_display_remove (GimpContainer *container, + GimpDisplay *display, + GimpUIManager *manager) +{ + gchar *merge_key = g_strdup_printf ("windows-display-%04d-merge-id", + gimp_display_get_ID (display)); + guint merge_id; + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + + if (merge_id) + gimp_ui_manager_remove_ui (manager, merge_id); + + g_object_set_data (G_OBJECT (manager), merge_key, NULL); + + g_free (merge_key); +} + +static void +windows_menu_display_reorder (GimpContainer *container, + GimpDisplay *display, + gint new_index, + GimpUIManager *manager) +{ + gint n_display = gimp_container_get_n_children (container); + gint i; + + for (i = new_index; i < n_display; i++) + { + GimpObject *d = gimp_container_get_child_by_index (container, i); + + windows_menu_display_remove (container, GIMP_DISPLAY (d), manager); + } + + /* If I don't ensure the menu items are effectively removed, adding + * the same ones may simply cancel the effect of the removal, hence + * losing the menu reordering. + */ + gimp_ui_manager_ensure_update (manager); + + for (i = new_index; i < n_display; i++) + { + GimpObject *d = gimp_container_get_child_by_index (container, i); + + windows_menu_display_add (container, GIMP_DISPLAY (d), manager); + } +} + +static void +windows_menu_image_notify (GimpDisplay *display, + const GParamSpec *unused, + GimpUIManager *manager) +{ + if (gimp_display_get_image (display)) + { + gchar *merge_key = g_strdup_printf ("windows-display-%04d-merge-id", + gimp_display_get_ID (display)); + guint merge_id; + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + + if (! merge_id) + { + GtkWidget *widget; + const gchar *ui_path; + gchar *action_name; + gchar *action_path; + gchar *full_path; + + ui_path = g_object_get_data (G_OBJECT (manager), + "image-menu-ui-path"); + + action_name = gimp_display_get_action_name (display); + action_path = g_strdup_printf ("%s/Windows/Images", ui_path); + + merge_id = gimp_ui_manager_new_merge_id (manager); + + g_object_set_data (G_OBJECT (manager), merge_key, + GUINT_TO_POINTER (merge_id)); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + full_path = g_strconcat (action_path, "/", action_name, NULL); + + widget = gimp_ui_manager_get_widget (manager, full_path); + + if (widget) + { + GimpAction *action; + + action = gimp_ui_manager_find_action (manager, + "windows", action_name); + + g_signal_connect_object (widget, "query-tooltip", + G_CALLBACK (windows_menu_display_query_tooltip), + action, 0); + } + + g_free (action_name); + g_free (action_path); + g_free (full_path); + } + + g_free (merge_key); + } + else + { + windows_menu_display_remove (manager->gimp->displays, display, manager); + } +} + +static void +windows_menu_dock_window_added (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpUIManager *manager) +{ + const gchar *ui_path; + gchar *action_name; + gchar *action_path; + gchar *merge_key; + guint merge_id; + + ui_path = g_object_get_data (G_OBJECT (manager), "image-menu-ui-path"); + + action_name = windows_actions_dock_window_to_action_name (dock_window); + action_path = g_strdup_printf ("%s/Windows/Docks", + ui_path); + + merge_key = windows_menu_dock_window_to_merge_id (dock_window); + merge_id = gimp_ui_manager_new_merge_id (manager); + + g_object_set_data (G_OBJECT (manager), merge_key, + GUINT_TO_POINTER (merge_id)); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (merge_key); + g_free (action_path); + g_free (action_name); +} + +static void +windows_menu_dock_window_removed (GimpDialogFactory *factory, + GimpDockWindow *dock_window, + GimpUIManager *manager) +{ + gchar *merge_key = windows_menu_dock_window_to_merge_id (dock_window); + guint merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + if (merge_id) + gimp_ui_manager_remove_ui (manager, merge_id); + + g_object_set_data (G_OBJECT (manager), merge_key, NULL); + + g_free (merge_key); +} + +static gchar * +windows_menu_dock_window_to_merge_id (GimpDockWindow *dock_window) +{ + return g_strdup_printf ("windows-dock-%04d-merge-id", + gimp_dock_window_get_id (dock_window)); +} + +static void +windows_menu_recent_add (GimpContainer *container, + GimpSessionInfo *info, + GimpUIManager *manager) +{ + const gchar *ui_path; + gchar *action_name; + gchar *action_path; + gint info_id; + gchar *merge_key; + guint merge_id; + + ui_path = g_object_get_data (G_OBJECT (manager), "image-menu-ui-path"); + + info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), + "recent-action-id")); + + action_name = g_strdup_printf ("windows-recent-%04d", info_id); + action_path = g_strdup_printf ("%s/Windows/Recently Closed Docks", ui_path); + + merge_key = g_strdup_printf ("windows-recent-%04d-merge-id", info_id); + merge_id = gimp_ui_manager_new_merge_id (manager); + + g_object_set_data (G_OBJECT (manager), merge_key, + GUINT_TO_POINTER (merge_id)); + + gimp_ui_manager_add_ui (manager, merge_id, + action_path, action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + TRUE); + + g_free (merge_key); + g_free (action_path); + g_free (action_name); +} + +static void +windows_menu_recent_remove (GimpContainer *container, + GimpSessionInfo *info, + GimpUIManager *manager) +{ + gint info_id; + gchar *merge_key; + guint merge_id; + + info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), + "recent-action-id")); + + merge_key = g_strdup_printf ("windows-recent-%04d-merge-id", info_id); + + merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (manager), + merge_key)); + + if (merge_id) + gimp_ui_manager_remove_ui (manager, merge_id); + + g_object_set_data (G_OBJECT (manager), merge_key, NULL); + + g_free (merge_key); +} + +static gboolean +windows_menu_display_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + GimpAction *action) +{ + GimpActionImpl *impl = GIMP_ACTION_IMPL (action); + GimpImage *image = GIMP_IMAGE (impl->viewable); + gchar *text; + gdouble xres; + gdouble yres; + gint width; + gint height; + + if (! image) + return FALSE; + + text = gtk_widget_get_tooltip_text (widget); + gtk_tooltip_set_text (tooltip, text); + g_free (text); + + gimp_image_get_resolution (image, &xres, &yres); + + gimp_viewable_calc_preview_size (gimp_image_get_width (image), + gimp_image_get_height (image), + GIMP_VIEW_SIZE_HUGE, GIMP_VIEW_SIZE_HUGE, + FALSE, xres, yres, + &width, &height, NULL); + + gtk_tooltip_set_icon (tooltip, + gimp_viewable_get_pixbuf (impl->viewable, + impl->context, + width, height)); + + return TRUE; +} diff --git a/app/menus/windows-menu.h b/app/menus/windows-menu.h new file mode 100644 index 0000000..e57ac79 --- /dev/null +++ b/app/menus/windows-menu.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 . + */ + +#ifndef __WINDOWS_MENU_H__ +#define __WINDOWS_MENU_H__ + + +void windows_menu_setup (GimpUIManager *manager, + const gchar *ui_path); + + +#endif /* __WINDOWS_MENU_H__ */ diff --git a/app/operations/Makefile.am b/app/operations/Makefile.am new file mode 100644 index 0000000..24bfe6d --- /dev/null +++ b/app/operations/Makefile.am @@ -0,0 +1,140 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = \ + layer-modes \ + layer-modes-legacy \ + tests + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Operations\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libappoperations.a + +libappoperations_a_sources = \ + operations-types.h \ + operations-enums.h \ + gimp-operations.c \ + gimp-operations.h \ + \ + gimp-operation-config.c \ + gimp-operation-config.h \ + gimpoperationsettings.c \ + gimpoperationsettings.h \ + gimpbrightnesscontrastconfig.c \ + gimpbrightnesscontrastconfig.h \ + gimpcageconfig.c \ + gimpcageconfig.h \ + gimpcolorbalanceconfig.c \ + gimpcolorbalanceconfig.h \ + gimpcurvesconfig.c \ + gimpcurvesconfig.h \ + gimphuesaturationconfig.c \ + gimphuesaturationconfig.h \ + gimplevelsconfig.c \ + gimplevelsconfig.h \ + \ + gimpoperationborder.c \ + gimpoperationborder.h \ + gimpoperationbuffersourcevalidate.c \ + gimpoperationbuffersourcevalidate.h \ + gimpoperationcagecoefcalc.c \ + gimpoperationcagecoefcalc.h \ + gimpoperationcagetransform.c \ + gimpoperationcagetransform.h \ + gimpoperationcomposecrop.c \ + gimpoperationcomposecrop.h \ + gimpoperationequalize.c \ + gimpoperationequalize.h \ + gimpoperationfillsource.c \ + gimpoperationfillsource.h \ + gimpoperationflood.c \ + gimpoperationflood.h \ + gimpoperationgradient.c \ + gimpoperationgradient.h \ + gimpoperationgrow.c \ + gimpoperationgrow.h \ + gimpoperationhistogramsink.c \ + gimpoperationhistogramsink.h \ + gimpoperationmaskcomponents.cc \ + gimpoperationmaskcomponents.h \ + gimpoperationoffset.c \ + gimpoperationoffset.h \ + gimpoperationprofiletransform.c \ + gimpoperationprofiletransform.h \ + gimpoperationscalarmultiply.c \ + gimpoperationscalarmultiply.h \ + gimpoperationsemiflatten.c \ + gimpoperationsemiflatten.h \ + gimpoperationsetalpha.c \ + gimpoperationsetalpha.h \ + gimpoperationshrink.c \ + gimpoperationshrink.h \ + gimpoperationthresholdalpha.c \ + gimpoperationthresholdalpha.h \ + \ + gimpoperationpointfilter.c \ + gimpoperationpointfilter.h \ + gimpoperationbrightnesscontrast.c \ + gimpoperationbrightnesscontrast.h \ + gimpoperationcolorbalance.c \ + gimpoperationcolorbalance.h \ + gimpoperationcolorize.c \ + gimpoperationcolorize.h \ + gimpoperationcurves.c \ + gimpoperationcurves.h \ + gimpoperationdesaturate.c \ + gimpoperationdesaturate.h \ + gimpoperationhuesaturation.c \ + gimpoperationhuesaturation.h \ + gimpoperationlevels.c \ + gimpoperationlevels.h \ + gimpoperationposterize.c \ + gimpoperationposterize.h \ + gimpoperationthreshold.c \ + gimpoperationthreshold.h + +libappoperations_a_built_sources = operations-enums.c + +libappoperations_a_SOURCES = \ + $(libappoperations_a_built_sources) \ + $(libappoperations_a_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-oec +CLEANFILES = $(gen_sources) + +xgen-oec: $(srcdir)/operations-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"operations-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/operations-enums.c: xgen-oec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/operations/Makefile.in b/app/operations/Makefile.in new file mode 100644 index 0000000..e960ea3 --- /dev/null +++ b/app/operations/Makefile.in @@ -0,0 +1,1376 @@ +# 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 = app/operations +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 = +libappoperations_a_AR = $(AR) $(ARFLAGS) +libappoperations_a_LIBADD = +am__objects_1 = operations-enums.$(OBJEXT) +am__objects_2 = gimp-operations.$(OBJEXT) \ + gimp-operation-config.$(OBJEXT) \ + gimpoperationsettings.$(OBJEXT) \ + gimpbrightnesscontrastconfig.$(OBJEXT) \ + gimpcageconfig.$(OBJEXT) gimpcolorbalanceconfig.$(OBJEXT) \ + gimpcurvesconfig.$(OBJEXT) gimphuesaturationconfig.$(OBJEXT) \ + gimplevelsconfig.$(OBJEXT) gimpoperationborder.$(OBJEXT) \ + gimpoperationbuffersourcevalidate.$(OBJEXT) \ + gimpoperationcagecoefcalc.$(OBJEXT) \ + gimpoperationcagetransform.$(OBJEXT) \ + gimpoperationcomposecrop.$(OBJEXT) \ + gimpoperationequalize.$(OBJEXT) \ + gimpoperationfillsource.$(OBJEXT) gimpoperationflood.$(OBJEXT) \ + gimpoperationgradient.$(OBJEXT) gimpoperationgrow.$(OBJEXT) \ + gimpoperationhistogramsink.$(OBJEXT) \ + gimpoperationmaskcomponents.$(OBJEXT) \ + gimpoperationoffset.$(OBJEXT) \ + gimpoperationprofiletransform.$(OBJEXT) \ + gimpoperationscalarmultiply.$(OBJEXT) \ + gimpoperationsemiflatten.$(OBJEXT) \ + gimpoperationsetalpha.$(OBJEXT) gimpoperationshrink.$(OBJEXT) \ + gimpoperationthresholdalpha.$(OBJEXT) \ + gimpoperationpointfilter.$(OBJEXT) \ + gimpoperationbrightnesscontrast.$(OBJEXT) \ + gimpoperationcolorbalance.$(OBJEXT) \ + gimpoperationcolorize.$(OBJEXT) gimpoperationcurves.$(OBJEXT) \ + gimpoperationdesaturate.$(OBJEXT) \ + gimpoperationhuesaturation.$(OBJEXT) \ + gimpoperationlevels.$(OBJEXT) gimpoperationposterize.$(OBJEXT) \ + gimpoperationthreshold.$(OBJEXT) +am_libappoperations_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libappoperations_a_OBJECTS = $(am_libappoperations_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)/gimp-operation-config.Po \ + ./$(DEPDIR)/gimp-operations.Po \ + ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po \ + ./$(DEPDIR)/gimpcageconfig.Po \ + ./$(DEPDIR)/gimpcolorbalanceconfig.Po \ + ./$(DEPDIR)/gimpcurvesconfig.Po \ + ./$(DEPDIR)/gimphuesaturationconfig.Po \ + ./$(DEPDIR)/gimplevelsconfig.Po \ + ./$(DEPDIR)/gimpoperationborder.Po \ + ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po \ + ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po \ + ./$(DEPDIR)/gimpoperationcagecoefcalc.Po \ + ./$(DEPDIR)/gimpoperationcagetransform.Po \ + ./$(DEPDIR)/gimpoperationcolorbalance.Po \ + ./$(DEPDIR)/gimpoperationcolorize.Po \ + ./$(DEPDIR)/gimpoperationcomposecrop.Po \ + ./$(DEPDIR)/gimpoperationcurves.Po \ + ./$(DEPDIR)/gimpoperationdesaturate.Po \ + ./$(DEPDIR)/gimpoperationequalize.Po \ + ./$(DEPDIR)/gimpoperationfillsource.Po \ + ./$(DEPDIR)/gimpoperationflood.Po \ + ./$(DEPDIR)/gimpoperationgradient.Po \ + ./$(DEPDIR)/gimpoperationgrow.Po \ + ./$(DEPDIR)/gimpoperationhistogramsink.Po \ + ./$(DEPDIR)/gimpoperationhuesaturation.Po \ + ./$(DEPDIR)/gimpoperationlevels.Po \ + ./$(DEPDIR)/gimpoperationmaskcomponents.Po \ + ./$(DEPDIR)/gimpoperationoffset.Po \ + ./$(DEPDIR)/gimpoperationpointfilter.Po \ + ./$(DEPDIR)/gimpoperationposterize.Po \ + ./$(DEPDIR)/gimpoperationprofiletransform.Po \ + ./$(DEPDIR)/gimpoperationscalarmultiply.Po \ + ./$(DEPDIR)/gimpoperationsemiflatten.Po \ + ./$(DEPDIR)/gimpoperationsetalpha.Po \ + ./$(DEPDIR)/gimpoperationsettings.Po \ + ./$(DEPDIR)/gimpoperationshrink.Po \ + ./$(DEPDIR)/gimpoperationthreshold.Po \ + ./$(DEPDIR)/gimpoperationthresholdalpha.Po \ + ./$(DEPDIR)/operations-enums.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 = +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 = $(libappoperations_a_SOURCES) +DIST_SOURCES = $(libappoperations_a_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)/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 = @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 = \ + layer-modes \ + layer-modes-legacy \ + tests + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Operations\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libappoperations.a + +libappoperations_a_sources = \ + operations-types.h \ + operations-enums.h \ + gimp-operations.c \ + gimp-operations.h \ + \ + gimp-operation-config.c \ + gimp-operation-config.h \ + gimpoperationsettings.c \ + gimpoperationsettings.h \ + gimpbrightnesscontrastconfig.c \ + gimpbrightnesscontrastconfig.h \ + gimpcageconfig.c \ + gimpcageconfig.h \ + gimpcolorbalanceconfig.c \ + gimpcolorbalanceconfig.h \ + gimpcurvesconfig.c \ + gimpcurvesconfig.h \ + gimphuesaturationconfig.c \ + gimphuesaturationconfig.h \ + gimplevelsconfig.c \ + gimplevelsconfig.h \ + \ + gimpoperationborder.c \ + gimpoperationborder.h \ + gimpoperationbuffersourcevalidate.c \ + gimpoperationbuffersourcevalidate.h \ + gimpoperationcagecoefcalc.c \ + gimpoperationcagecoefcalc.h \ + gimpoperationcagetransform.c \ + gimpoperationcagetransform.h \ + gimpoperationcomposecrop.c \ + gimpoperationcomposecrop.h \ + gimpoperationequalize.c \ + gimpoperationequalize.h \ + gimpoperationfillsource.c \ + gimpoperationfillsource.h \ + gimpoperationflood.c \ + gimpoperationflood.h \ + gimpoperationgradient.c \ + gimpoperationgradient.h \ + gimpoperationgrow.c \ + gimpoperationgrow.h \ + gimpoperationhistogramsink.c \ + gimpoperationhistogramsink.h \ + gimpoperationmaskcomponents.cc \ + gimpoperationmaskcomponents.h \ + gimpoperationoffset.c \ + gimpoperationoffset.h \ + gimpoperationprofiletransform.c \ + gimpoperationprofiletransform.h \ + gimpoperationscalarmultiply.c \ + gimpoperationscalarmultiply.h \ + gimpoperationsemiflatten.c \ + gimpoperationsemiflatten.h \ + gimpoperationsetalpha.c \ + gimpoperationsetalpha.h \ + gimpoperationshrink.c \ + gimpoperationshrink.h \ + gimpoperationthresholdalpha.c \ + gimpoperationthresholdalpha.h \ + \ + gimpoperationpointfilter.c \ + gimpoperationpointfilter.h \ + gimpoperationbrightnesscontrast.c \ + gimpoperationbrightnesscontrast.h \ + gimpoperationcolorbalance.c \ + gimpoperationcolorbalance.h \ + gimpoperationcolorize.c \ + gimpoperationcolorize.h \ + gimpoperationcurves.c \ + gimpoperationcurves.h \ + gimpoperationdesaturate.c \ + gimpoperationdesaturate.h \ + gimpoperationhuesaturation.c \ + gimpoperationhuesaturation.h \ + gimpoperationlevels.c \ + gimpoperationlevels.h \ + gimpoperationposterize.c \ + gimpoperationposterize.h \ + gimpoperationthreshold.c \ + gimpoperationthreshold.h + +libappoperations_a_built_sources = operations-enums.c +libappoperations_a_SOURCES = \ + $(libappoperations_a_built_sources) \ + $(libappoperations_a_sources) + + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-oec +CLEANFILES = $(gen_sources) +all: all-recursive + +.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 app/operations/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/operations/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) + +libappoperations.a: $(libappoperations_a_OBJECTS) $(libappoperations_a_DEPENDENCIES) $(EXTRA_libappoperations_a_DEPENDENCIES) + $(AM_V_at)-rm -f libappoperations.a + $(AM_V_AR)$(libappoperations_a_AR) libappoperations.a $(libappoperations_a_OBJECTS) $(libappoperations_a_LIBADD) + $(AM_V_at)$(RANLIB) libappoperations.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-operation-config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-operations.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrightnesscontrastconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcageconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorbalanceconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurvesconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphuesaturationconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplevelsconfig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationborder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbrightnesscontrast.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcagecoefcalc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcagetransform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcolorbalance.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcolorize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcomposecrop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcurves.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdesaturate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationequalize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationfillsource.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationflood.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgradient.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrow.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhistogramsink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhuesaturation.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlevels.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmaskcomponents.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationoffset.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationpointfilter.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationposterize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationprofiletransform.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationscalarmultiply.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsemiflatten.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsetalpha.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsettings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationshrink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationthreshold.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationthresholdalpha.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/operations-enums.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 + +# 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 $(LIBRARIES) +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: + -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-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/gimp-operation-config.Po + -rm -f ./$(DEPDIR)/gimp-operations.Po + -rm -f ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po + -rm -f ./$(DEPDIR)/gimpcageconfig.Po + -rm -f ./$(DEPDIR)/gimpcolorbalanceconfig.Po + -rm -f ./$(DEPDIR)/gimpcurvesconfig.Po + -rm -f ./$(DEPDIR)/gimphuesaturationconfig.Po + -rm -f ./$(DEPDIR)/gimplevelsconfig.Po + -rm -f ./$(DEPDIR)/gimpoperationborder.Po + -rm -f ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po + -rm -f ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po + -rm -f ./$(DEPDIR)/gimpoperationcagecoefcalc.Po + -rm -f ./$(DEPDIR)/gimpoperationcagetransform.Po + -rm -f ./$(DEPDIR)/gimpoperationcolorbalance.Po + -rm -f ./$(DEPDIR)/gimpoperationcolorize.Po + -rm -f ./$(DEPDIR)/gimpoperationcomposecrop.Po + -rm -f ./$(DEPDIR)/gimpoperationcurves.Po + -rm -f ./$(DEPDIR)/gimpoperationdesaturate.Po + -rm -f ./$(DEPDIR)/gimpoperationequalize.Po + -rm -f ./$(DEPDIR)/gimpoperationfillsource.Po + -rm -f ./$(DEPDIR)/gimpoperationflood.Po + -rm -f ./$(DEPDIR)/gimpoperationgradient.Po + -rm -f ./$(DEPDIR)/gimpoperationgrow.Po + -rm -f ./$(DEPDIR)/gimpoperationhistogramsink.Po + -rm -f ./$(DEPDIR)/gimpoperationhuesaturation.Po + -rm -f ./$(DEPDIR)/gimpoperationlevels.Po + -rm -f ./$(DEPDIR)/gimpoperationmaskcomponents.Po + -rm -f ./$(DEPDIR)/gimpoperationoffset.Po + -rm -f ./$(DEPDIR)/gimpoperationpointfilter.Po + -rm -f ./$(DEPDIR)/gimpoperationposterize.Po + -rm -f ./$(DEPDIR)/gimpoperationprofiletransform.Po + -rm -f ./$(DEPDIR)/gimpoperationscalarmultiply.Po + -rm -f ./$(DEPDIR)/gimpoperationsemiflatten.Po + -rm -f ./$(DEPDIR)/gimpoperationsetalpha.Po + -rm -f ./$(DEPDIR)/gimpoperationsettings.Po + -rm -f ./$(DEPDIR)/gimpoperationshrink.Po + -rm -f ./$(DEPDIR)/gimpoperationthreshold.Po + -rm -f ./$(DEPDIR)/gimpoperationthresholdalpha.Po + -rm -f ./$(DEPDIR)/operations-enums.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-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)/gimp-operation-config.Po + -rm -f ./$(DEPDIR)/gimp-operations.Po + -rm -f ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po + -rm -f ./$(DEPDIR)/gimpcageconfig.Po + -rm -f ./$(DEPDIR)/gimpcolorbalanceconfig.Po + -rm -f ./$(DEPDIR)/gimpcurvesconfig.Po + -rm -f ./$(DEPDIR)/gimphuesaturationconfig.Po + -rm -f ./$(DEPDIR)/gimplevelsconfig.Po + -rm -f ./$(DEPDIR)/gimpoperationborder.Po + -rm -f ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po + -rm -f ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po + -rm -f ./$(DEPDIR)/gimpoperationcagecoefcalc.Po + -rm -f ./$(DEPDIR)/gimpoperationcagetransform.Po + -rm -f ./$(DEPDIR)/gimpoperationcolorbalance.Po + -rm -f ./$(DEPDIR)/gimpoperationcolorize.Po + -rm -f ./$(DEPDIR)/gimpoperationcomposecrop.Po + -rm -f ./$(DEPDIR)/gimpoperationcurves.Po + -rm -f ./$(DEPDIR)/gimpoperationdesaturate.Po + -rm -f ./$(DEPDIR)/gimpoperationequalize.Po + -rm -f ./$(DEPDIR)/gimpoperationfillsource.Po + -rm -f ./$(DEPDIR)/gimpoperationflood.Po + -rm -f ./$(DEPDIR)/gimpoperationgradient.Po + -rm -f ./$(DEPDIR)/gimpoperationgrow.Po + -rm -f ./$(DEPDIR)/gimpoperationhistogramsink.Po + -rm -f ./$(DEPDIR)/gimpoperationhuesaturation.Po + -rm -f ./$(DEPDIR)/gimpoperationlevels.Po + -rm -f ./$(DEPDIR)/gimpoperationmaskcomponents.Po + -rm -f ./$(DEPDIR)/gimpoperationoffset.Po + -rm -f ./$(DEPDIR)/gimpoperationpointfilter.Po + -rm -f ./$(DEPDIR)/gimpoperationposterize.Po + -rm -f ./$(DEPDIR)/gimpoperationprofiletransform.Po + -rm -f ./$(DEPDIR)/gimpoperationscalarmultiply.Po + -rm -f ./$(DEPDIR)/gimpoperationsemiflatten.Po + -rm -f ./$(DEPDIR)/gimpoperationsetalpha.Po + -rm -f ./$(DEPDIR)/gimpoperationsettings.Po + -rm -f ./$(DEPDIR)/gimpoperationshrink.Po + -rm -f ./$(DEPDIR)/gimpoperationthreshold.Po + -rm -f ./$(DEPDIR)/gimpoperationthresholdalpha.Po + -rm -f ./$(DEPDIR)/operations-enums.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: + +.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-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 \ + 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 + +.PRECIOUS: Makefile + + +xgen-oec: $(srcdir)/operations-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"operations-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/operations-enums.c: xgen-oec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/operations/gimp-operation-config.c b/app/operations/gimp-operation-config.c new file mode 100644 index 0000000..492cefd --- /dev/null +++ b/app/operations/gimp-operation-config.c @@ -0,0 +1,827 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "core/gimp.h" + +#include "core/gimplist.h" +#include "core/gimpparamspecs-duplicate.h" +#include "core/gimpviewable.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "gimp-operation-config.h" +#include "gimpoperationsettings.h" + + +/* local function prototypes */ + +static void gimp_operation_config_config_sync (GObject *config, + const GParamSpec *gimp_pspec, + GeglNode *node); +static void gimp_operation_config_config_notify (GObject *config, + const GParamSpec *gimp_pspec, + GeglNode *node); +static void gimp_operation_config_node_notify (GeglNode *node, + const GParamSpec *gegl_pspec, + GObject *config); + +static GFile * gimp_operation_config_get_file (GType config_type); +static void gimp_operation_config_add_sep (GimpContainer *container); +static void gimp_operation_config_remove_sep (GimpContainer *container); + + +/* public functions */ + +static GHashTable * +gimp_operation_config_get_type_table (Gimp *gimp) +{ + static GHashTable *config_types = NULL; + + if (! config_types) + config_types = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + + return config_types; +} + +static GHashTable * +gimp_operation_config_get_container_table (Gimp *gimp) +{ + static GHashTable *config_containers = NULL; + + if (! config_containers) + config_containers = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref); + + return config_containers; +} + +static GValue * +gimp_operation_config_value_new (GParamSpec *pspec) +{ + GValue *value = g_slice_new0 (GValue); + + g_value_init (value, pspec->value_type); + g_param_value_set_default (pspec, value); + + return value; +} + +static void +gimp_operation_config_value_free (GValue *value) +{ + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GHashTable * +gimp_operation_config_get_properties (GObject *object) +{ + GHashTable *properties = g_object_get_data (object, "properties"); + + if (! properties) + { + properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gimp_operation_config_value_free); + + g_object_set_data_full (object, "properties", properties, + (GDestroyNotify) g_hash_table_unref); + } + + return properties; +} + +static GValue * +gimp_operation_config_value_get (GObject *object, + GParamSpec *pspec) +{ + GHashTable *properties = gimp_operation_config_get_properties (object); + GValue *value; + + value = g_hash_table_lookup (properties, pspec->name); + + if (! value) + { + value = gimp_operation_config_value_new (pspec); + g_hash_table_insert (properties, g_strdup (pspec->name), value); + } + + return value; +} + +static void +gimp_operation_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GValue *val = gimp_operation_config_value_get (object, pspec); + + g_value_copy (value, val); +} + +static void +gimp_operation_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GValue *val = gimp_operation_config_value_get (object, pspec); + + g_value_copy (val, value); +} + +static void +gimp_operation_config_class_init (GObjectClass *klass, + const gchar *operation) +{ + GParamSpec **pspecs; + guint n_pspecs; + gint i; + + klass->set_property = gimp_operation_config_set_property; + klass->get_property = gimp_operation_config_get_property; + + pspecs = gegl_operation_list_properties (operation, &n_pspecs); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + + if ((pspec->flags & G_PARAM_READABLE) && + (pspec->flags & G_PARAM_WRITABLE) && + strcmp (pspec->name, "input") && + strcmp (pspec->name, "output")) + { + GParamSpec *copy = gimp_param_spec_duplicate (pspec); + + if (copy) + { + g_object_class_install_property (klass, i + 1, copy); + } + } + } + + g_free (pspecs); +} + +static gboolean +gimp_operation_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GList *diff; + gboolean equal = TRUE; + + diff = gimp_config_diff (G_OBJECT (a), G_OBJECT (b), + GIMP_CONFIG_PARAM_SERIALIZE); + + if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b)) + { + GList *list; + + for (list = diff; list; list = g_list_next (list)) + { + GParamSpec *pspec = list->data; + + if (g_type_is_a (pspec->owner_type, GIMP_TYPE_OPERATION_SETTINGS)) + { + equal = FALSE; + break; + } + } + } + else if (diff) + { + equal = FALSE; + } + + g_list_free (diff); + + return equal; +} + +static void +gimp_operation_config_config_iface_init (GimpConfigInterface *iface) +{ + iface->equal = gimp_operation_config_equal; +} + + +/* public functions */ + +void +gimp_operation_config_register (Gimp *gimp, + const gchar *operation, + GType config_type) +{ + GHashTable *config_types; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (operation != NULL); + g_return_if_fail (g_type_is_a (config_type, GIMP_TYPE_OBJECT)); + + config_types = gimp_operation_config_get_type_table (gimp); + + g_hash_table_insert (config_types, + g_strdup (operation), + (gpointer) config_type); + } + +GType +gimp_operation_config_get_type (Gimp *gimp, + const gchar *operation, + const gchar *icon_name, + GType parent_type) +{ + GHashTable *config_types; + GType config_type; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), G_TYPE_NONE); + g_return_val_if_fail (operation != NULL, G_TYPE_NONE); + + config_types = gimp_operation_config_get_type_table (gimp); + + config_type = (GType) g_hash_table_lookup (config_types, operation); + + if (! config_type) + { + GTypeQuery query; + + g_return_val_if_fail (g_type_is_a (parent_type, GIMP_TYPE_OBJECT), + G_TYPE_NONE); + + g_type_query (parent_type, &query); + + { + GTypeInfo info = + { + query.class_size, + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gimp_operation_config_class_init, + NULL, /* class_finalize */ + operation, + query.instance_size, + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + const GInterfaceInfo config_info = + { + (GInterfaceInitFunc) gimp_operation_config_config_iface_init, + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + gchar *type_name = g_strdup_printf ("GimpGegl-%s-config", + operation); + + g_strcanon (type_name, + G_CSET_DIGITS "-" G_CSET_a_2_z G_CSET_A_2_Z, '-'); + + config_type = g_type_register_static (parent_type, type_name, + &info, 0); + + g_free (type_name); + + g_type_add_interface_static (config_type, GIMP_TYPE_CONFIG, + &config_info); + + if (icon_name && g_type_is_a (config_type, GIMP_TYPE_VIEWABLE)) + { + GimpViewableClass *viewable_class = g_type_class_ref (config_type); + + viewable_class->default_icon_name = g_strdup (icon_name); + + g_type_class_unref (viewable_class); + } + + gimp_operation_config_register (gimp, operation, config_type); + } + } + + return config_type; +} + +GimpContainer * +gimp_operation_config_get_container (Gimp *gimp, + GType config_type, + GCompareFunc sort_func) +{ + GHashTable *config_containers; + GimpContainer *container; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (g_type_is_a (config_type, GIMP_TYPE_OBJECT), NULL); + + config_containers = gimp_operation_config_get_container_table (gimp); + + container = g_hash_table_lookup (config_containers, (gpointer) config_type); + + if (! container) + { + container = gimp_list_new (config_type, TRUE); + gimp_list_set_sort_func (GIMP_LIST (container), sort_func); + + g_hash_table_insert (config_containers, + (gpointer) config_type, container); + + gimp_operation_config_deserialize (gimp, container, NULL); + + if (gimp_container_get_n_children (container) == 0) + { + GFile *file = gimp_operation_config_get_file (config_type); + + if (! g_file_query_exists (file, NULL)) + { + GQuark quark = g_quark_from_static_string ("compat-file"); + GFile *compat_file; + + compat_file = g_type_get_qdata (config_type, quark); + + if (compat_file) + { + if (! g_file_move (compat_file, file, 0, + NULL, NULL, NULL, NULL)) + { + gimp_operation_config_deserialize (gimp, container, + compat_file); + } + else + { + gimp_operation_config_deserialize (gimp, container, NULL); + } + } + } + + g_object_unref (file); + } + + gimp_operation_config_add_sep (container); + } + + return container; +} + +void +gimp_operation_config_serialize (Gimp *gimp, + GimpContainer *container, + GFile *file) +{ + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_CONTAINER (container)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + if (file) + { + g_object_ref (file); + } + else + { + GType config_type = gimp_container_get_children_type (container); + + file = gimp_operation_config_get_file (config_type); + } + + if (gimp->be_verbose) + g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file)); + + gimp_operation_config_remove_sep (container); + + if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (container), + file, + "settings", + "end of settings", + NULL, &error)) + { + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, + error->message); + g_clear_error (&error); + } + + gimp_operation_config_add_sep (container); + + g_object_unref (file); +} + +void +gimp_operation_config_deserialize (Gimp *gimp, + GimpContainer *container, + GFile *file) +{ + GError *error = NULL; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (GIMP_IS_CONTAINER (container)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + if (file) + { + g_object_ref (file); + } + else + { + GType config_type = gimp_container_get_children_type (container); + + file = gimp_operation_config_get_file (config_type); + } + + if (gimp->be_verbose) + g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file)); + + if (! gimp_config_deserialize_gfile (GIMP_CONFIG (container), + file, + NULL, &error)) + { + if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT) + gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, + error->message); + + g_clear_error (&error); + } + + g_object_unref (file); +} + +void +gimp_operation_config_sync_node (GObject *config, + GeglNode *node) +{ + GParamSpec **pspecs; + gchar *operation; + guint n_pspecs; + gint i; + + g_return_if_fail (G_IS_OBJECT (config)); + g_return_if_fail (GEGL_IS_NODE (node)); + + gegl_node_get (node, + "operation", &operation, + NULL); + + g_return_if_fail (operation != NULL); + + pspecs = gegl_operation_list_properties (operation, &n_pspecs); + g_free (operation); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *gegl_pspec = pspecs[i]; + GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), + gegl_pspec->name); + + /* if the operation has an object property of the config's + * type, use the config object directly + */ + if (G_IS_PARAM_SPEC_OBJECT (gegl_pspec) && + gegl_pspec->value_type == G_TYPE_FROM_INSTANCE (config)) + { + gegl_node_set (node, + gegl_pspec->name, config, + NULL); + } + else if (gimp_pspec) + { + GValue value = G_VALUE_INIT; + + g_value_init (&value, gimp_pspec->value_type); + + g_object_get_property (G_OBJECT (config), gimp_pspec->name, + &value); + + if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec)) + { + GimpRGB gimp_color; + GeglColor *gegl_color; + + gimp_value_get_rgb (&value, &gimp_color); + g_value_unset (&value); + + gegl_color = gimp_gegl_color_new (&gimp_color); + + g_value_init (&value, gegl_pspec->value_type); + g_value_take_object (&value, gegl_color); + } + + gegl_node_set_property (node, gegl_pspec->name, + &value); + + g_value_unset (&value); + } + } + + g_free (pspecs); +} + +void +gimp_operation_config_connect_node (GObject *config, + GeglNode *node) +{ + GParamSpec **pspecs; + gchar *operation; + guint n_pspecs; + gint i; + + g_return_if_fail (G_IS_OBJECT (config)); + g_return_if_fail (GEGL_IS_NODE (node)); + + gegl_node_get (node, + "operation", &operation, + NULL); + + g_return_if_fail (operation != NULL); + + pspecs = gegl_operation_list_properties (operation, &n_pspecs); + g_free (operation); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + + /* if the operation has an object property of the config's + * type, connect it to a special callback and done + */ + if (G_IS_PARAM_SPEC_OBJECT (pspec) && + pspec->value_type == G_TYPE_FROM_INSTANCE (config)) + { + g_signal_connect_object (config, "notify", + G_CALLBACK (gimp_operation_config_config_sync), + node, 0); + g_free (pspecs); + return; + } + } + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *gegl_pspec = pspecs[i]; + GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), + gegl_pspec->name); + + if (gimp_pspec) + { + gchar *notify_name = g_strconcat ("notify::", gimp_pspec->name, NULL); + + g_signal_connect_object (config, notify_name, + G_CALLBACK (gimp_operation_config_config_notify), + node, 0); + + g_signal_connect_object (node, notify_name, + G_CALLBACK (gimp_operation_config_node_notify), + config, 0); + + g_free (notify_name); + } + } + + g_free (pspecs); +} + +GParamSpec ** +gimp_operation_config_list_properties (GObject *config, + GType owner_type, + GParamFlags flags, + guint *n_pspecs) +{ + GParamSpec **param_specs; + guint n_param_specs; + gint i, j; + + g_return_val_if_fail (G_IS_OBJECT (config), NULL); + + param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), + &n_param_specs); + + for (i = 0, j = 0; i < n_param_specs; i++) + { + GParamSpec *pspec = param_specs[i]; + + /* ignore properties of parent classes of owner_type */ + if (! g_type_is_a (pspec->owner_type, owner_type)) + continue; + + if (flags && ((pspec->flags & flags) != flags)) + continue; + + if (gimp_gegl_param_spec_has_key (pspec, "role", "output-extent")) + continue; + + param_specs[j] = param_specs[i]; + j++; + } + + if (n_pspecs) + *n_pspecs = j; + + if (j == 0) + { + g_free (param_specs); + param_specs = NULL; + } + + return param_specs; +} + + +/* private functions */ + +static void +gimp_operation_config_config_sync (GObject *config, + const GParamSpec *gimp_pspec, + GeglNode *node) +{ + gimp_operation_config_sync_node (config, node); +} + +static void +gimp_operation_config_config_notify (GObject *config, + const GParamSpec *gimp_pspec, + GeglNode *node) +{ + GParamSpec *gegl_pspec = gegl_node_find_property (node, gimp_pspec->name); + + if (gegl_pspec) + { + GValue value = G_VALUE_INIT; + gulong handler; + + g_value_init (&value, gimp_pspec->value_type); + g_object_get_property (config, gimp_pspec->name, &value); + + if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec)) + { + GimpRGB gimp_color; + GeglColor *gegl_color; + + gimp_value_get_rgb (&value, &gimp_color); + g_value_unset (&value); + + gegl_color = gimp_gegl_color_new (&gimp_color); + + g_value_init (&value, gegl_pspec->value_type); + g_value_take_object (&value, gegl_color); + } + + handler = g_signal_handler_find (node, + G_SIGNAL_MATCH_DETAIL | + G_SIGNAL_MATCH_FUNC | + G_SIGNAL_MATCH_DATA, + 0, + g_quark_from_string (gegl_pspec->name), + NULL, + gimp_operation_config_node_notify, + config); + + if (handler) + g_signal_handler_block (node, handler); + + gegl_node_set_property (node, gegl_pspec->name, &value); + g_value_unset (&value); + + if (handler) + g_signal_handler_unblock (node, handler); + + } +} + +static void +gimp_operation_config_node_notify (GeglNode *node, + const GParamSpec *gegl_pspec, + GObject *config) +{ + GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), + gegl_pspec->name); + + if (gimp_pspec) + { + GValue value = G_VALUE_INIT; + gulong handler; + + g_value_init (&value, gegl_pspec->value_type); + gegl_node_get_property (node, gegl_pspec->name, &value); + + if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec)) + { + GeglColor *gegl_color; + GimpRGB gimp_color; + + gegl_color = g_value_dup_object (&value); + g_value_unset (&value); + + if (gegl_color) + { + gegl_color_get_rgba (gegl_color, + &gimp_color.r, + &gimp_color.g, + &gimp_color.b, + &gimp_color.a); + g_object_unref (gegl_color); + } + else + { + gimp_rgba_set (&gimp_color, 0.0, 0.0, 0.0, 1.0); + } + + g_value_init (&value, gimp_pspec->value_type); + gimp_value_set_rgb (&value, &gimp_color); + } + + handler = g_signal_handler_find (config, + G_SIGNAL_MATCH_DETAIL | + G_SIGNAL_MATCH_FUNC | + G_SIGNAL_MATCH_DATA, + 0, + g_quark_from_string (gimp_pspec->name), + NULL, + gimp_operation_config_config_notify, + node); + + if (handler) + g_signal_handler_block (config, handler); + + g_object_set_property (config, gimp_pspec->name, &value); + g_value_unset (&value); + + if (handler) + g_signal_handler_unblock (config, handler); + } +} + +static GFile * +gimp_operation_config_get_file (GType config_type) +{ + GFile *file; + gchar *basename; + + basename = g_strconcat (g_type_name (config_type), ".settings", NULL); + file = gimp_directory_file ("filters", basename, NULL); + g_free (basename); + + return file; +} + +static void +gimp_operation_config_add_sep (GimpContainer *container) +{ + GimpObject *sep = g_object_get_data (G_OBJECT (container), "separator"); + + if (! sep) + { + sep = g_object_new (gimp_container_get_children_type (container), + NULL); + + gimp_container_add (container, sep); + g_object_unref (sep); + + g_object_set_data (G_OBJECT (container), "separator", sep); + } +} + +static void +gimp_operation_config_remove_sep (GimpContainer *container) +{ + GimpObject *sep = g_object_get_data (G_OBJECT (container), "separator"); + + if (sep) + { + gimp_container_remove (container, sep); + + g_object_set_data (G_OBJECT (container), "separator", NULL); + } +} diff --git a/app/operations/gimp-operation-config.h b/app/operations/gimp-operation-config.h new file mode 100644 index 0000000..5f920e0 --- /dev/null +++ b/app/operations/gimp-operation-config.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __GIMP_OPERATION_CONFIG_H__ +#define __GIMP_OPERATION_CONFIG_H__ + + +void gimp_operation_config_register (Gimp *gimp, + const gchar *operation, + GType config_type); + +GType gimp_operation_config_get_type (Gimp *gimp, + const gchar *operation, + const gchar *icon_name, + GType parent_type); + +GimpContainer * gimp_operation_config_get_container (Gimp *gimp, + GType config_type, + GCompareFunc sort_func); + +void gimp_operation_config_serialize (Gimp *gimp, + GimpContainer *container, + GFile *file); +void gimp_operation_config_deserialize (Gimp *gimp, + GimpContainer *container, + GFile *file); + +void gimp_operation_config_sync_node (GObject *config, + GeglNode *node); +void gimp_operation_config_connect_node (GObject *config, + GeglNode *node); + +GParamSpec ** gimp_operation_config_list_properties (GObject *config, + GType owner_type, + GParamFlags flags, + guint *n_pspecs); + + +#endif /* __GIMP_OPERATION_CONFIG_H__ */ diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c new file mode 100644 index 0000000..7cb30fd --- /dev/null +++ b/app/operations/gimp-operations.c @@ -0,0 +1,225 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-operations.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "operations-types.h" + +#include "core/gimp.h" + +#include "gimp-operations.h" + +#include "gimpoperationborder.h" +#include "gimpoperationbuffersourcevalidate.h" +#include "gimpoperationcagecoefcalc.h" +#include "gimpoperationcagetransform.h" +#include "gimpoperationcomposecrop.h" +#include "gimpoperationequalize.h" +#include "gimpoperationfillsource.h" +#include "gimpoperationflood.h" +#include "gimpoperationgradient.h" +#include "gimpoperationgrow.h" +#include "gimpoperationhistogramsink.h" +#include "gimpoperationmaskcomponents.h" +#include "gimpoperationoffset.h" +#include "gimpoperationprofiletransform.h" +#include "gimpoperationscalarmultiply.h" +#include "gimpoperationsemiflatten.h" +#include "gimpoperationsetalpha.h" +#include "gimpoperationshrink.h" +#include "gimpoperationthresholdalpha.h" + +#include "gimpoperationbrightnesscontrast.h" +#include "gimpoperationcolorbalance.h" +#include "gimpoperationcolorize.h" +#include "gimpoperationcurves.h" +#include "gimpoperationdesaturate.h" +#include "gimpoperationhuesaturation.h" +#include "gimpoperationlevels.h" +#include "gimpoperationposterize.h" +#include "gimpoperationthreshold.h" + +#include "gimp-operation-config.h" +#include "gimpbrightnesscontrastconfig.h" +#include "gimpcolorbalanceconfig.h" +#include "gimpcurvesconfig.h" +#include "gimphuesaturationconfig.h" +#include "gimplevelsconfig.h" + +#include "layer-modes-legacy/gimpoperationadditionlegacy.h" +#include "layer-modes-legacy/gimpoperationburnlegacy.h" +#include "layer-modes-legacy/gimpoperationdarkenonlylegacy.h" +#include "layer-modes-legacy/gimpoperationdifferencelegacy.h" +#include "layer-modes-legacy/gimpoperationdividelegacy.h" +#include "layer-modes-legacy/gimpoperationdodgelegacy.h" +#include "layer-modes-legacy/gimpoperationgrainextractlegacy.h" +#include "layer-modes-legacy/gimpoperationgrainmergelegacy.h" +#include "layer-modes-legacy/gimpoperationhardlightlegacy.h" +#include "layer-modes-legacy/gimpoperationhslcolorlegacy.h" +#include "layer-modes-legacy/gimpoperationhsvhuelegacy.h" +#include "layer-modes-legacy/gimpoperationhsvsaturationlegacy.h" +#include "layer-modes-legacy/gimpoperationhsvvaluelegacy.h" +#include "layer-modes-legacy/gimpoperationlightenonlylegacy.h" +#include "layer-modes-legacy/gimpoperationmultiplylegacy.h" +#include "layer-modes-legacy/gimpoperationscreenlegacy.h" +#include "layer-modes-legacy/gimpoperationsoftlightlegacy.h" +#include "layer-modes-legacy/gimpoperationsubtractlegacy.h" + +#include "layer-modes/gimp-layer-modes.h" +#include "layer-modes/gimpoperationantierase.h" +#include "layer-modes/gimpoperationbehind.h" +#include "layer-modes/gimpoperationdissolve.h" +#include "layer-modes/gimpoperationerase.h" +#include "layer-modes/gimpoperationmerge.h" +#include "layer-modes/gimpoperationnormal.h" +#include "layer-modes/gimpoperationpassthrough.h" +#include "layer-modes/gimpoperationreplace.h" +#include "layer-modes/gimpoperationsplit.h" + + +static void +set_compat_file (GType type, + const gchar *basename) +{ + GFile *file = gimp_directory_file ("tool-options", basename, NULL); + GQuark quark = g_quark_from_static_string ("compat-file"); + + g_type_set_qdata (type, quark, file); +} + +static void +set_settings_folder (GType type, + const gchar *basename) +{ + GFile *file = gimp_directory_file (basename, NULL); + GQuark quark = g_quark_from_static_string ("settings-folder"); + + g_type_set_qdata (type, quark, file); +} + +void +gimp_operations_init (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_layer_modes_init (); + + g_type_class_ref (GIMP_TYPE_OPERATION_BORDER); + g_type_class_ref (GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE); + g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_COEF_CALC); + g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_TRANSFORM); + g_type_class_ref (GIMP_TYPE_OPERATION_COMPOSE_CROP); + g_type_class_ref (GIMP_TYPE_OPERATION_EQUALIZE); + g_type_class_ref (GIMP_TYPE_OPERATION_FILL_SOURCE); + g_type_class_ref (GIMP_TYPE_OPERATION_FLOOD); + g_type_class_ref (GIMP_TYPE_OPERATION_GRADIENT); + g_type_class_ref (GIMP_TYPE_OPERATION_GROW); + g_type_class_ref (GIMP_TYPE_OPERATION_HISTOGRAM_SINK); + g_type_class_ref (GIMP_TYPE_OPERATION_MASK_COMPONENTS); + g_type_class_ref (GIMP_TYPE_OPERATION_OFFSET); + g_type_class_ref (GIMP_TYPE_OPERATION_PROFILE_TRANSFORM); + g_type_class_ref (GIMP_TYPE_OPERATION_SCALAR_MULTIPLY); + g_type_class_ref (GIMP_TYPE_OPERATION_SEMI_FLATTEN); + g_type_class_ref (GIMP_TYPE_OPERATION_SET_ALPHA); + g_type_class_ref (GIMP_TYPE_OPERATION_SHRINK); + g_type_class_ref (GIMP_TYPE_OPERATION_THRESHOLD_ALPHA); + + g_type_class_ref (GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST); + g_type_class_ref (GIMP_TYPE_OPERATION_COLOR_BALANCE); + g_type_class_ref (GIMP_TYPE_OPERATION_COLORIZE); + g_type_class_ref (GIMP_TYPE_OPERATION_CURVES); + g_type_class_ref (GIMP_TYPE_OPERATION_DESATURATE); + g_type_class_ref (GIMP_TYPE_OPERATION_HUE_SATURATION); + g_type_class_ref (GIMP_TYPE_OPERATION_LEVELS); + g_type_class_ref (GIMP_TYPE_OPERATION_POSTERIZE); + g_type_class_ref (GIMP_TYPE_OPERATION_THRESHOLD); + + g_type_class_ref (GIMP_TYPE_OPERATION_NORMAL); + g_type_class_ref (GIMP_TYPE_OPERATION_DISSOLVE); + g_type_class_ref (GIMP_TYPE_OPERATION_BEHIND); + g_type_class_ref (GIMP_TYPE_OPERATION_MULTIPLY_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_SCREEN_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_ADDITION_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_SUBTRACT_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_DARKEN_ONLY_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_HSV_HUE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_DIVIDE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_DODGE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_BURN_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY); + g_type_class_ref (GIMP_TYPE_OPERATION_ERASE); + g_type_class_ref (GIMP_TYPE_OPERATION_MERGE); + g_type_class_ref (GIMP_TYPE_OPERATION_SPLIT); + g_type_class_ref (GIMP_TYPE_OPERATION_PASS_THROUGH); + g_type_class_ref (GIMP_TYPE_OPERATION_REPLACE); + g_type_class_ref (GIMP_TYPE_OPERATION_ANTI_ERASE); + + gimp_operation_config_register (gimp, + "gimp:brightness-contrast", + GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG); + set_compat_file (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, + "gimp-brightness-contrast-tool.settings"); + set_settings_folder (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, + "brightness-contrast"); + + gimp_operation_config_register (gimp, + "gimp:color-balance", + GIMP_TYPE_COLOR_BALANCE_CONFIG); + set_compat_file (GIMP_TYPE_COLOR_BALANCE_CONFIG, + "gimp-color-balance-tool.settings"); + set_settings_folder (GIMP_TYPE_COLOR_BALANCE_CONFIG, + "color-balance"); + + gimp_operation_config_register (gimp, + "gimp:curves", + GIMP_TYPE_CURVES_CONFIG); + set_compat_file (GIMP_TYPE_CURVES_CONFIG, + "gimp-curves-tool.settings"); + set_settings_folder (GIMP_TYPE_CURVES_CONFIG, + "curves"); + + gimp_operation_config_register (gimp, + "gimp:hue-saturation", + GIMP_TYPE_HUE_SATURATION_CONFIG); + set_compat_file (GIMP_TYPE_HUE_SATURATION_CONFIG, + "gimp-hue-saturation-tool.settings"); + set_settings_folder (GIMP_TYPE_HUE_SATURATION_CONFIG, + "hue-saturation"); + + gimp_operation_config_register (gimp, + "gimp:levels", + GIMP_TYPE_LEVELS_CONFIG); + set_compat_file (GIMP_TYPE_LEVELS_CONFIG, + "gimp-levels-tool.settings"); + set_settings_folder (GIMP_TYPE_LEVELS_CONFIG, + "levels"); +} diff --git a/app/operations/gimp-operations.h b/app/operations/gimp-operations.h new file mode 100644 index 0000000..6f2b222 --- /dev/null +++ b/app/operations/gimp-operations.h @@ -0,0 +1,27 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-operations.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 . + */ + +#ifndef __GIMP_OPERATIONS_H__ +#define __GIMP_OPERATIONS_H__ + + +void gimp_operations_init (Gimp *gimp); + + +#endif /* __GIMP_OPERATIONS_H__ */ diff --git a/app/operations/gimpbrightnesscontrastconfig.c b/app/operations/gimpbrightnesscontrastconfig.c new file mode 100644 index 0000000..75ee36a --- /dev/null +++ b/app/operations/gimpbrightnesscontrastconfig.c @@ -0,0 +1,248 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrightnesscontrastconfig.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimpbrightnesscontrastconfig.h" +#include "gimplevelsconfig.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_BRIGHTNESS, + PROP_CONTRAST +}; + + +static void gimp_brightness_contrast_config_iface_init (GimpConfigInterface *iface); + +static void gimp_brightness_contrast_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_brightness_contrast_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_brightness_contrast_config_equal (GimpConfig *a, + GimpConfig *b); + + +G_DEFINE_TYPE_WITH_CODE (GimpBrightnessContrastConfig, + gimp_brightness_contrast_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_brightness_contrast_config_iface_init)) + +#define parent_class gimp_brightness_contrast_config_parent_class + + +static void +gimp_brightness_contrast_config_class_init (GimpBrightnessContrastConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->set_property = gimp_brightness_contrast_config_set_property; + object_class->get_property = gimp_brightness_contrast_config_get_property; + + viewable_class->default_icon_name = "gimp-tool-brightness-contrast"; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRIGHTNESS, + "brightness", + _("Brightness"), + _("Brightness"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CONTRAST, + "contrast", + _("Contrast"), + _("Contrast"), + -1.0, 1.0, 0.0, 0); +} + +static void +gimp_brightness_contrast_config_iface_init (GimpConfigInterface *iface) +{ + iface->equal = gimp_brightness_contrast_config_equal; +} + +static void +gimp_brightness_contrast_config_init (GimpBrightnessContrastConfig *self) +{ +} + +static void +gimp_brightness_contrast_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpBrightnessContrastConfig *self = GIMP_BRIGHTNESS_CONTRAST_CONFIG (object); + + switch (property_id) + { + case PROP_BRIGHTNESS: + g_value_set_double (value, self->brightness); + break; + + case PROP_CONTRAST: + g_value_set_double (value, self->contrast); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_brightness_contrast_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpBrightnessContrastConfig *self = GIMP_BRIGHTNESS_CONTRAST_CONFIG (object); + + switch (property_id) + { + case PROP_BRIGHTNESS: + self->brightness = g_value_get_double (value); + break; + + case PROP_CONTRAST: + self->contrast = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_brightness_contrast_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpBrightnessContrastConfig *config_a = GIMP_BRIGHTNESS_CONTRAST_CONFIG (a); + GimpBrightnessContrastConfig *config_b = GIMP_BRIGHTNESS_CONTRAST_CONFIG (b); + + if (! gimp_operation_settings_config_equal_base (a, b) || + config_a->brightness != config_b->brightness || + config_a->contrast != config_b->contrast) + { + return FALSE; + } + + return TRUE; +} + + +/* public functions */ + +GimpLevelsConfig * +gimp_brightness_contrast_config_to_levels_config (GimpBrightnessContrastConfig *config) +{ + GimpLevelsConfig *levels; + gdouble brightness; + gdouble slant; + gdouble value; + + g_return_val_if_fail (GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG (config), NULL); + + levels = g_object_new (GIMP_TYPE_LEVELS_CONFIG, NULL); + + gimp_operation_settings_config_copy_base (GIMP_CONFIG (config), + GIMP_CONFIG (levels), + 0); + + brightness = config->brightness / 2.0; + slant = tan ((config->contrast + 1) * G_PI_4); + + if (config->brightness >= 0) + { + value = -0.5 * slant + brightness * slant + 0.5; + + if (value < 0.0) + { + value = 0.0; + + /* this slightly convoluted math follows by inverting the + * calculation of the brightness/contrast LUT in base/lut-funcs.h */ + + levels->low_input[GIMP_HISTOGRAM_VALUE] = + (- brightness * slant + 0.5 * slant - 0.5) / (slant - brightness * slant); + } + + levels->low_output[GIMP_HISTOGRAM_VALUE] = value; + + value = 0.5 * slant + 0.5; + + if (value > 1.0) + { + value = 1.0; + + levels->high_input[GIMP_HISTOGRAM_VALUE] = + (- brightness * slant + 0.5 * slant + 0.5) / (slant - brightness * slant); + } + + levels->high_output[GIMP_HISTOGRAM_VALUE] = value; + } + else + { + value = 0.5 - 0.5 * slant; + + if (value < 0.0) + { + value = 0.0; + + levels->low_input[GIMP_HISTOGRAM_VALUE] = + (0.5 * slant - 0.5) / (slant + brightness * slant); + } + + levels->low_output[GIMP_HISTOGRAM_VALUE] = value; + + value = slant * brightness + slant * 0.5 + 0.5; + + if (value > 1.0) + { + value = 1.0; + + levels->high_input[GIMP_HISTOGRAM_VALUE] = + (0.5 * slant + 0.5) / (slant + brightness * slant); + } + + levels->high_output[GIMP_HISTOGRAM_VALUE] = value; + } + + return levels; +} diff --git a/app/operations/gimpbrightnesscontrastconfig.h b/app/operations/gimpbrightnesscontrastconfig.h new file mode 100644 index 0000000..bab1a03 --- /dev/null +++ b/app/operations/gimpbrightnesscontrastconfig.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpbrightnesscontrastconfig.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__ +#define __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +#define GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG (gimp_brightness_contrast_config_get_type ()) +#define GIMP_BRIGHTNESS_CONTRAST_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfig)) +#define GIMP_BRIGHTNESS_CONTRAST_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfigClass)) +#define GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG)) +#define GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG)) +#define GIMP_BRIGHTNESS_CONTRAST_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfigClass)) + + +typedef struct _GimpBrightnessContrastConfigClass GimpBrightnessContrastConfigClass; + +struct _GimpBrightnessContrastConfig +{ + GimpOperationSettings parent_instance; + + gdouble brightness; + gdouble contrast; +}; + +struct _GimpBrightnessContrastConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_brightness_contrast_config_get_type (void) G_GNUC_CONST; + +GimpLevelsConfig * +gimp_brightness_contrast_config_to_levels_config (GimpBrightnessContrastConfig *config); + + +#endif /* __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__ */ diff --git a/app/operations/gimpcageconfig.c b/app/operations/gimpcageconfig.c new file mode 100644 index 0000000..17548cf --- /dev/null +++ b/app/operations/gimpcageconfig.c @@ -0,0 +1,832 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpcageconfig.c + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" + +#include "operations-types.h" + +#include "gimpcageconfig.h" + + +/*#define DEBUG_CAGE */ + +/* This DELTA is aimed to not have handle on exact pixel during computation, + * to avoid particular case. It shouldn't be so useful, but it's a double + * safety. */ +#define DELTA 0.010309278351 + + +static void gimp_cage_config_finalize (GObject *object); +static void gimp_cage_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_cage_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_cage_config_compute_scaling_factor (GimpCageConfig *gcc); +static void gimp_cage_config_compute_edges_normal (GimpCageConfig *gcc); + + +G_DEFINE_TYPE_WITH_CODE (GimpCageConfig, gimp_cage_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + NULL)) + +#define parent_class gimp_cage_config_parent_class + +#ifdef DEBUG_CAGE +static void +print_cage (GimpCageConfig *gcc) +{ + gint i; + GeglRectangle bounding_box; + GimpCagePoint *point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + bounding_box = gimp_cage_config_get_bounding_box (gcc); + + for (i = 0; i < gcc->cage_points->len; i++) + { + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + g_printerr ("cgx: %.0f cgy: %.0f cvdx: %.0f cvdy: %.0f sf: %.2f normx: %.2f normy: %.2f %s\n", + point->src_point.x + ((gcc->cage_mode==GIMP_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_x:0), + point->src_point.y + ((gcc->cage_mode==GIMP_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_y:0), + point->dest_point.x + ((gcc->cage_mode==GIMP_CAGE_MODE_DEFORM)?gcc->displacement_x:0), + point->dest_point.y + ((gcc->cage_mode==GIMP_CAGE_MODE_DEFORM)?gcc->displacement_y:0), + point->edge_scaling_factor, + point->edge_normal.x, + point->edge_normal.y, + ((point->selected) ? "S" : "NS")); + } + + g_printerr ("bounding box: x: %d y: %d width: %d height: %d\n", bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height); + g_printerr ("disp x: %f disp y: %f\n", gcc->displacement_x, gcc->displacement_y); + g_printerr ("done\n"); +} +#endif + +static void +gimp_cage_config_class_init (GimpCageConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_cage_config_set_property; + object_class->get_property = gimp_cage_config_get_property; + + object_class->finalize = gimp_cage_config_finalize; +} + +static void +gimp_cage_config_init (GimpCageConfig *self) +{ + /*pre-allocation for 50 vertices for the cage.*/ + self->cage_points = g_array_sized_new (FALSE, FALSE, sizeof(GimpCagePoint), 50); +} + +static void +gimp_cage_config_finalize (GObject *object) +{ + GimpCageConfig *gcc = GIMP_CAGE_CONFIG (object); + + g_array_free (gcc->cage_points, TRUE); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_cage_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_cage_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gimp_cage_config_get_n_points: + * @gcc: the cage config + * + * Returns: the number of points of the cage + */ +guint +gimp_cage_config_get_n_points (GimpCageConfig *gcc) +{ + return gcc->cage_points->len; +} + +/** + * gimp_cage_config_add_cage_point: + * @gcc: the cage config + * @x: x value of the new point + * @y: y value of the new point + * + * Add a new point in the last index of the polygon of the cage. + * Point is added in both source and destination cage + */ +void +gimp_cage_config_add_cage_point (GimpCageConfig *gcc, + gdouble x, + gdouble y) +{ + gimp_cage_config_insert_cage_point (gcc, gcc->cage_points->len, x, y); +} + +/** + * gimp_cage_config_insert_cage_point: + * @gcc: the cage config + * @point_number: index where the point will be inserted + * @x: x value of the new point + * @y: y value of the new point + * + * Insert a new point in the polygon of the cage at the given index. + * Point is added in both source and destination cage + */ +void +gimp_cage_config_insert_cage_point (GimpCageConfig *gcc, + gint point_number, + gdouble x, + gdouble y) +{ + GimpCagePoint point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + g_return_if_fail (point_number <= gcc->cage_points->len); + g_return_if_fail (point_number >= 0); + + point.src_point.x = x + DELTA; + point.src_point.y = y + DELTA; + + point.dest_point.x = x + DELTA; + point.dest_point.y = y + DELTA; + + g_array_insert_val (gcc->cage_points, point_number, point); + + gimp_cage_config_compute_scaling_factor (gcc); + gimp_cage_config_compute_edges_normal (gcc); +} + +/** + * gimp_cage_config_remove_last_cage_point: + * @gcc: the cage config + * + * Remove the last point of the cage, in both source and destination cage + */ +void +gimp_cage_config_remove_last_cage_point (GimpCageConfig *gcc) +{ + gimp_cage_config_remove_cage_point (gcc, gcc->cage_points->len - 1); +} + +/** + * gimp_cage_config_remove_cage_point: + * @gcc: the cage config + * @point_number: the index of the point to remove + * + * Remove the given point from the cage + */ +void +gimp_cage_config_remove_cage_point (GimpCageConfig *gcc, + gint point_number) +{ + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + g_return_if_fail (point_number < gcc->cage_points->len); + g_return_if_fail (point_number >= 0); + + if (gcc->cage_points->len > 0) + g_array_remove_index (gcc->cage_points, gcc->cage_points->len - 1); + + gimp_cage_config_compute_scaling_factor (gcc); + gimp_cage_config_compute_edges_normal (gcc); +} + +/** + * gimp_cage_config_remove_selected_points: + * @gcc: the cage config + * + * Remove all the selected points from the cage + */ +void +gimp_cage_config_remove_selected_points (GimpCageConfig *gcc) +{ + gint i; + GimpCagePoint *point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + for (i = 0; i < gcc->cage_points->len; i++) + { + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + + if (point->selected) + { + g_array_remove_index (gcc->cage_points, i); + i--; + } + } + + gimp_cage_config_compute_scaling_factor (gcc); + gimp_cage_config_compute_edges_normal (gcc); +} + +/** + * gimp_cage_config_get_point_coordinate: + * @gcc: the cage config + * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM + * @point_number: the index of the point to return + * + * Returns: the real position of the given point, as a GimpVector2 + */ +GimpVector2 +gimp_cage_config_get_point_coordinate (GimpCageConfig *gcc, + GimpCageMode mode, + gint point_number) +{ + GimpVector2 result = { 0.0, 0.0 }; + GimpCagePoint *point; + + g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), result); + g_return_val_if_fail (point_number < gcc->cage_points->len, result); + g_return_val_if_fail (point_number >= 0, result); + + point = &g_array_index (gcc->cage_points, GimpCagePoint, point_number); + + if (point->selected) + { + if (mode == GIMP_CAGE_MODE_CAGE_CHANGE) + { + result.x = point->src_point.x + gcc->displacement_x; + result.y = point->src_point.y + gcc->displacement_y; + } + else + { + result.x = point->dest_point.x + gcc->displacement_x; + result.y = point->dest_point.y + gcc->displacement_y; + } + } + else + { + if (mode == GIMP_CAGE_MODE_CAGE_CHANGE) + { + result.x = point->src_point.x; + result.y = point->src_point.y; + } + else + { + result.x = point->dest_point.x; + result.y = point->dest_point.y; + } + } + + return result; +} + +/** + * gimp_cage_config_add_displacement: + * @gcc: the cage config + * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM + * @point_number: the point of the cage to move + * @x: x displacement value + * @y: y displacement value + * + * Add a displacement for all selected points of the cage. + * This displacement need to be committed to become effective. + */ +void +gimp_cage_config_add_displacement (GimpCageConfig *gcc, + GimpCageMode mode, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + gcc->cage_mode = mode; + gcc->displacement_x = x; + gcc->displacement_y = y; + + #ifdef DEBUG_CAGE + print_cage (gcc); + #endif +} + +/** + * gimp_cage_config_commit_displacement: + * @gcc: the cage config + * + * Apply the displacement to the cage + */ +void +gimp_cage_config_commit_displacement (GimpCageConfig *gcc) +{ + gint i; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + for (i = 0; i < gcc->cage_points->len; i++) + { + GimpCagePoint *point; + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + + if (point->selected) + { + if (gcc->cage_mode == GIMP_CAGE_MODE_CAGE_CHANGE) + { + point->src_point.x += gcc->displacement_x; + point->src_point.y += gcc->displacement_y; + point->dest_point.x += gcc->displacement_x; + point->dest_point.y += gcc->displacement_y; + } + else + { + point->dest_point.x += gcc->displacement_x; + point->dest_point.y += gcc->displacement_y; + } + } + } + + gimp_cage_config_compute_scaling_factor (gcc); + gimp_cage_config_compute_edges_normal (gcc); + gimp_cage_config_reset_displacement (gcc); +} + +/** + * gimp_cage_config_reset_displacement: + * @gcc: the cage config + * + * Set the displacement to zero. + */ +void +gimp_cage_config_reset_displacement (GimpCageConfig *gcc) +{ + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + gcc->displacement_x = 0.0; + gcc->displacement_y = 0.0; +} + +/** + * gimp_cage_config_get_bounding_box: + * @gcc: the cage config + * + * Compute the bounding box of the source cage + * + * Returns: the bounding box of the source cage, as a GeglRectangle + */ +GeglRectangle +gimp_cage_config_get_bounding_box (GimpCageConfig *gcc) +{ + GeglRectangle bounding_box = { 0, 0, 0, 0}; + gint i; + GimpCagePoint *point; + + g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), bounding_box); + + if (gcc->cage_points->len == 0) + return bounding_box; + + point = &g_array_index (gcc->cage_points, GimpCagePoint, 0); + + if (point->selected) + { + bounding_box.x = point->src_point.x + gcc->displacement_x; + bounding_box.y = point->src_point.y + gcc->displacement_y; + } + else + { + bounding_box.x = point->src_point.x; + bounding_box.y = point->src_point.y; + } + + for (i = 1; i < gcc->cage_points->len; i++) + { + gdouble x,y; + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + + if (point->selected) + { + x = point->src_point.x + gcc->displacement_x; + y = point->src_point.y + gcc->displacement_y; + } + else + { + x = point->src_point.x; + y = point->src_point.y; + } + + if (x < bounding_box.x) + { + bounding_box.width += bounding_box.x - x; + bounding_box.x = x; + } + + if (y < bounding_box.y) + { + bounding_box.height += bounding_box.y - y; + bounding_box.y = y; + } + + if (x > bounding_box.x + bounding_box.width) + { + bounding_box.width = x - bounding_box.x; + } + + if (y > bounding_box.y + bounding_box.height) + { + bounding_box.height = y - bounding_box.y; + } + } + + return bounding_box; +} + +/** + * gimp_cage_config_reverse_cage: + * @gcc: the cage config + * + * When using non-simple cage (like a cage in 8), user may want to + * manually inverse inside and outside of the cage. This function + * reverse the cage + */ +void +gimp_cage_config_reverse_cage (GimpCageConfig *gcc) +{ + GimpCagePoint temp; + gint i; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + for (i = 0; i < gcc->cage_points->len / 2; i++) + { + temp = g_array_index (gcc->cage_points, GimpCagePoint, i); + + g_array_index (gcc->cage_points, GimpCagePoint, i) = + g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - i - 1); + + g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - i - 1) = temp; + } + + gimp_cage_config_compute_scaling_factor (gcc); + gimp_cage_config_compute_edges_normal (gcc); +} + +/** + * gimp_cage_config_reverse_cage_if_needed: + * @gcc: the cage config + * + * Since the cage need to be defined counter-clockwise to have the + * topological inside in the actual 'physical' inside of the cage, + * this function compute if the cage is clockwise or not, and reverse + * the cage if needed. + * + * This function does not take into account an eventual displacement + */ +void +gimp_cage_config_reverse_cage_if_needed (GimpCageConfig *gcc) +{ + gint i; + gdouble sum; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + sum = 0.0; + + /* this is a bit crappy, but should works most of the case */ + /* we do the sum of the projection of each point to the previous + segment, and see the final sign */ + for (i = 0; i < gcc->cage_points->len ; i++) + { + GimpVector2 P1, P2, P3; + gdouble z; + + P1 = (g_array_index (gcc->cage_points, GimpCagePoint, i)).src_point; + P2 = (g_array_index (gcc->cage_points, GimpCagePoint, (i+1) % gcc->cage_points->len)).src_point; + P3 = (g_array_index (gcc->cage_points, GimpCagePoint, (i+2) % gcc->cage_points->len)).src_point; + + z = P1.x * (P2.y - P3.y) + P2.x * (P3.y - P1.y) + P3.x * (P1.y - P2.y); + + sum += z; + } + + /* sum > 0 mean a cage defined counter-clockwise, so we reverse it */ + if (sum > 0) + { + gimp_cage_config_reverse_cage (gcc); + } +} + +/** + * gimp_cage_config_compute_scaling_factor: + * @gcc: the cage config + * + * Update Green Coordinate scaling factor for the destination cage. + * This function does not take into account an eventual displacement. + */ +static void +gimp_cage_config_compute_scaling_factor (GimpCageConfig *gcc) +{ + GimpVector2 edge; + gdouble length, length_d; + gint i; + GimpCagePoint *current, *last; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + if (gcc->cage_points->len < 2) + return; + + last = &g_array_index (gcc->cage_points, GimpCagePoint, 0); + + for (i = 1; i <= gcc->cage_points->len; i++) + { + current = &g_array_index (gcc->cage_points, GimpCagePoint, i % gcc->cage_points->len); + + gimp_vector2_sub (&edge, + &(last->src_point), + &(current->src_point)); + length = gimp_vector2_length (&edge); + + gimp_vector2_sub (&edge, + &(last->dest_point), + &(current->dest_point)); + length_d = gimp_vector2_length (&edge); + + last->edge_scaling_factor = length_d / length; + last = current; + } +} + +/** + * gimp_cage_config_compute_edges_normal: + * @gcc: the cage config + * + * Update edges normal for the destination cage. + * This function does not take into account an eventual displacement. + */ +static void +gimp_cage_config_compute_edges_normal (GimpCageConfig *gcc) +{ + GimpVector2 normal; + gint i; + GimpCagePoint *current, *last; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + last = &g_array_index (gcc->cage_points, GimpCagePoint, 0); + + for (i = 1; i <= gcc->cage_points->len; i++) + { + current = &g_array_index (gcc->cage_points, GimpCagePoint, i % gcc->cage_points->len); + + gimp_vector2_sub (&normal, + &(current->dest_point), + &(last->dest_point)); + + last->edge_normal = gimp_vector2_normal (&normal); + last = current; + } +} + +/** + * gimp_cage_config_point_inside: + * @gcc: the cage config + * @x: x coordinate of the point to test + * @y: y coordinate of the point to test + * + * Check if the given point is inside the cage. This test is done in + * the regard of the topological inside of the source cage. + * + * Returns: TRUE if the point is inside, FALSE if not. + * This function does not take into account an eventual displacement. + */ +gboolean +gimp_cage_config_point_inside (GimpCageConfig *gcc, + gfloat x, + gfloat y) +{ + GimpVector2 *last, *current; + gboolean inside = FALSE; + gint i; + + g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), FALSE); + + last = &((g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - 1)).src_point); + + for (i = 0; i < gcc->cage_points->len; i++) + { + current = &((g_array_index (gcc->cage_points, GimpCagePoint, i)).src_point); + + if ((((current->y <= y) && (y < last->y)) + || ((last->y <= y) && (y < current->y))) + && (x < (last->x - current->x) * (y - current->y) / (last->y - current->y) + current->x)) + { + inside = !inside; + } + + last = current; + } + + return inside; +} + +/** + * gimp_cage_config_select_point: + * @gcc: the cage config + * @point_number: the index of the point to select + * + * Select the given point of the cage, and deselect the others. + */ +void +gimp_cage_config_select_point (GimpCageConfig *gcc, + gint point_number) +{ + gint i; + GimpCagePoint *point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + g_return_if_fail (point_number < gcc->cage_points->len); + g_return_if_fail (point_number >= 0); + + for (i = 0; i < gcc->cage_points->len; i++) + { + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + + if (i == point_number) + { + point->selected = TRUE; + } + else + { + point->selected = FALSE; + } + } +} + +/** + * gimp_cage_config_select_area: + * @gcc: the cage config + * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM + * @area: the area to select + * + * Select cage's point inside the given area and deselect others + */ +void +gimp_cage_config_select_area (GimpCageConfig *gcc, + GimpCageMode mode, + GeglRectangle area) +{ + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + gimp_cage_config_deselect_points (gcc); + gimp_cage_config_select_add_area (gcc, mode, area); +} + +/** + * gimp_cage_config_select_add_area: + * @gcc: the cage config + * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM + * @area: the area to select + * + * Select cage's point inside the given area. Already selected point stay selected. + */ +void +gimp_cage_config_select_add_area (GimpCageConfig *gcc, + GimpCageMode mode, + GeglRectangle area) +{ + gint i; + GimpCagePoint *point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + for (i = 0; i < gcc->cage_points->len; i++) + { + point = &g_array_index (gcc->cage_points, GimpCagePoint, i); + + if (mode == GIMP_CAGE_MODE_CAGE_CHANGE) + { + if (point->src_point.x >= area.x && + point->src_point.x <= area.x + area.width && + point->src_point.y >= area.y && + point->src_point.y <= area.y + area.height) + { + point->selected = TRUE; + } + } + else + { + if (point->dest_point.x >= area.x && + point->dest_point.x <= area.x + area.width && + point->dest_point.y >= area.y && + point->dest_point.y <= area.y + area.height) + { + point->selected = TRUE; + } + } + } +} + +/** + * gimp_cage_config_toggle_point_selection: + * @gcc: the cage config + * @point_number: the index of the point to toggle selection + * + * Toggle the selection of the given cage point + */ +void +gimp_cage_config_toggle_point_selection (GimpCageConfig *gcc, + gint point_number) +{ + GimpCagePoint *point; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + g_return_if_fail (point_number < gcc->cage_points->len); + g_return_if_fail (point_number >= 0); + + point = &g_array_index (gcc->cage_points, GimpCagePoint, point_number); + point->selected = ! point->selected; +} + +/** + * gimp_cage_deselect_points: + * @gcc: the cage config + * + * Deselect all cage points. + */ +void +gimp_cage_config_deselect_points (GimpCageConfig *gcc) +{ + gint i; + + g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc)); + + for (i = 0; i < gcc->cage_points->len; i++) + { + (g_array_index (gcc->cage_points, GimpCagePoint, i)).selected = FALSE; + } +} + +/** + * gimp_cage_config_point_is_selected: + * @gcc: the cage config + * @point_number: the index of the point to test + * + * Returns: TRUE if the point is selected, FALSE otherwise. + */ +gboolean +gimp_cage_config_point_is_selected (GimpCageConfig *gcc, + gint point_number) +{ + GimpCagePoint *point; + + g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), FALSE); + g_return_val_if_fail (point_number < gcc->cage_points->len, FALSE); + g_return_val_if_fail (point_number >= 0, FALSE); + + point = &(g_array_index (gcc->cage_points, GimpCagePoint, point_number)); + + return point->selected; +} diff --git a/app/operations/gimpcageconfig.h b/app/operations/gimpcageconfig.h new file mode 100644 index 0000000..8106d52 --- /dev/null +++ b/app/operations/gimpcageconfig.h @@ -0,0 +1,108 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpcageconfig.h + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CAGE_CONFIG_H__ +#define __GIMP_CAGE_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +struct _GimpCagePoint +{ + GimpVector2 src_point; + GimpVector2 dest_point; + GimpVector2 edge_normal; + gdouble edge_scaling_factor; + gboolean selected; +}; + + +#define GIMP_TYPE_CAGE_CONFIG (gimp_cage_config_get_type ()) +#define GIMP_CAGE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CAGE_CONFIG, GimpCageConfig)) +#define GIMP_CAGE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CAGE_CONFIG, GimpCageConfigClass)) +#define GIMP_IS_CAGE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CAGE_CONFIG)) +#define GIMP_IS_CAGE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CAGE_CONFIG)) +#define GIMP_CAGE_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CAGE_CONFIG, GimpCageConfigClass)) + + +typedef struct _GimpCageConfigClass GimpCageConfigClass; + +struct _GimpCageConfig +{ + GimpOperationSettings parent_instance; + + GArray *cage_points; + + gdouble displacement_x; + gdouble displacement_y; + GimpCageMode cage_mode; /* Cage mode, used to commit displacement */ +}; + +struct _GimpCageConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_cage_config_get_type (void) G_GNUC_CONST; + +guint gimp_cage_config_get_n_points (GimpCageConfig *gcc); +void gimp_cage_config_add_cage_point (GimpCageConfig *gcc, + gdouble x, + gdouble y); +void gimp_cage_config_insert_cage_point (GimpCageConfig *gcc, + gint point_number, + gdouble x, + gdouble y); +void gimp_cage_config_remove_last_cage_point (GimpCageConfig *gcc); +void gimp_cage_config_remove_cage_point (GimpCageConfig *gcc, + gint point_number); +void gimp_cage_config_remove_selected_points (GimpCageConfig *gcc); +GimpVector2 gimp_cage_config_get_point_coordinate (GimpCageConfig *gcc, + GimpCageMode mode, + gint point_number); +void gimp_cage_config_add_displacement (GimpCageConfig *gcc, + GimpCageMode mode, + gdouble x, + gdouble y); +void gimp_cage_config_commit_displacement (GimpCageConfig *gcc); +void gimp_cage_config_reset_displacement (GimpCageConfig *gcc); +GeglRectangle gimp_cage_config_get_bounding_box (GimpCageConfig *gcc); +void gimp_cage_config_reverse_cage_if_needed (GimpCageConfig *gcc); +void gimp_cage_config_reverse_cage (GimpCageConfig *gcc); +gboolean gimp_cage_config_point_inside (GimpCageConfig *gcc, + gfloat x, + gfloat y); +void gimp_cage_config_select_point (GimpCageConfig *gcc, + gint point_number); +void gimp_cage_config_select_area (GimpCageConfig *gcc, + GimpCageMode mode, + GeglRectangle area); +void gimp_cage_config_select_add_area (GimpCageConfig *gcc, + GimpCageMode mode, + GeglRectangle area); +void gimp_cage_config_toggle_point_selection (GimpCageConfig *gcc, + gint point_number); +void gimp_cage_config_deselect_points (GimpCageConfig *gcc); +gboolean gimp_cage_config_point_is_selected (GimpCageConfig *gcc, + gint point_number); + + +#endif /* __GIMP_CAGE_CONFIG_H__ */ diff --git a/app/operations/gimpcolorbalanceconfig.c b/app/operations/gimpcolorbalanceconfig.c new file mode 100644 index 0000000..7011a86 --- /dev/null +++ b/app/operations/gimpcolorbalanceconfig.c @@ -0,0 +1,382 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcolorbalanceconfig.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimpcolorbalanceconfig.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_RANGE, + PROP_CYAN_RED, + PROP_MAGENTA_GREEN, + PROP_YELLOW_BLUE, + PROP_PRESERVE_LUMINOSITY +}; + + +static void gimp_color_balance_config_iface_init (GimpConfigInterface *iface); + +static void gimp_color_balance_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_color_balance_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_color_balance_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_color_balance_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_color_balance_config_equal (GimpConfig *a, + GimpConfig *b); +static void gimp_color_balance_config_reset (GimpConfig *config); +static gboolean gimp_color_balance_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + + +G_DEFINE_TYPE_WITH_CODE (GimpColorBalanceConfig, gimp_color_balance_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_color_balance_config_iface_init)) + +#define parent_class gimp_color_balance_config_parent_class + + +static void +gimp_color_balance_config_class_init (GimpColorBalanceConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->set_property = gimp_color_balance_config_set_property; + object_class->get_property = gimp_color_balance_config_get_property; + + viewable_class->default_icon_name = "gimp-tool-color-balance"; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_RANGE, + "range", + _("Range"), + _("The affected range"), + GIMP_TYPE_TRANSFER_MODE, + GIMP_TRANSFER_MIDTONES, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CYAN_RED, + "cyan-red", + _("Cyan-Red"), + _("Cyan-Red"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_MAGENTA_GREEN, + "magenta-green", + _("Magenta-Green"), + _("Magenta-Green"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YELLOW_BLUE, + "yellow-blue", + _("Yellow-Blue"), + _("Yellow-Blue"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PRESERVE_LUMINOSITY, + "preserve-luminosity", + _("Preserve Luminosity"), + _("Preserve Luminosity"), + TRUE, 0); +} + +static void +gimp_color_balance_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_color_balance_config_serialize; + iface->deserialize = gimp_color_balance_config_deserialize; + iface->equal = gimp_color_balance_config_equal; + iface->reset = gimp_color_balance_config_reset; + iface->copy = gimp_color_balance_config_copy; +} + +static void +gimp_color_balance_config_init (GimpColorBalanceConfig *self) +{ + gimp_config_reset (GIMP_CONFIG (self)); +} + +static void +gimp_color_balance_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpColorBalanceConfig *self = GIMP_COLOR_BALANCE_CONFIG (object); + + switch (property_id) + { + case PROP_RANGE: + g_value_set_enum (value, self->range); + break; + + case PROP_CYAN_RED: + g_value_set_double (value, self->cyan_red[self->range]); + break; + + case PROP_MAGENTA_GREEN: + g_value_set_double (value, self->magenta_green[self->range]); + break; + + case PROP_YELLOW_BLUE: + g_value_set_double (value, self->yellow_blue[self->range]); + break; + + case PROP_PRESERVE_LUMINOSITY: + g_value_set_boolean (value, self->preserve_luminosity); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_color_balance_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpColorBalanceConfig *self = GIMP_COLOR_BALANCE_CONFIG (object); + + switch (property_id) + { + case PROP_RANGE: + self->range = g_value_get_enum (value); + g_object_notify (object, "cyan-red"); + g_object_notify (object, "magenta-green"); + g_object_notify (object, "yellow-blue"); + break; + + case PROP_CYAN_RED: + self->cyan_red[self->range] = g_value_get_double (value); + break; + + case PROP_MAGENTA_GREEN: + self->magenta_green[self->range] = g_value_get_double (value); + break; + + case PROP_YELLOW_BLUE: + self->yellow_blue[self->range] = g_value_get_double (value); + break; + + case PROP_PRESERVE_LUMINOSITY: + self->preserve_luminosity = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_color_balance_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + GimpColorBalanceConfig *bc_config = GIMP_COLOR_BALANCE_CONFIG (config); + GimpTransferMode range; + GimpTransferMode old_range; + gboolean success = TRUE; + + if (! gimp_operation_settings_config_serialize_base (config, writer, data)) + return FALSE; + + old_range = bc_config->range; + + for (range = GIMP_TRANSFER_SHADOWS; + range <= GIMP_TRANSFER_HIGHLIGHTS; + range++) + { + bc_config->range = range; + + success = (gimp_config_serialize_property_by_name (config, + "range", + writer) && + gimp_config_serialize_property_by_name (config, + "cyan-red", + writer) && + gimp_config_serialize_property_by_name (config, + "magenta-green", + writer) && + gimp_config_serialize_property_by_name (config, + "yellow-blue", + writer)); + + if (! success) + break; + } + + if (success) + success = gimp_config_serialize_property_by_name (config, + "preserve-luminosity", + writer); + + bc_config->range = old_range; + + return success; +} + +static gboolean +gimp_color_balance_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpColorBalanceConfig *cb_config = GIMP_COLOR_BALANCE_CONFIG (config); + GimpTransferMode old_range; + gboolean success = TRUE; + + old_range = cb_config->range; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + g_object_set (config, "range", old_range, NULL); + + return success; +} + +static gboolean +gimp_color_balance_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpColorBalanceConfig *config_a = GIMP_COLOR_BALANCE_CONFIG (a); + GimpColorBalanceConfig *config_b = GIMP_COLOR_BALANCE_CONFIG (b); + GimpTransferMode range; + + if (! gimp_operation_settings_config_equal_base (a, b)) + return FALSE; + + for (range = GIMP_TRANSFER_SHADOWS; + range <= GIMP_TRANSFER_HIGHLIGHTS; + range++) + { + if (config_a->cyan_red[range] != config_b->cyan_red[range] || + config_a->magenta_green[range] != config_b->magenta_green[range] || + config_a->yellow_blue[range] != config_b->yellow_blue[range]) + return FALSE; + } + + /* don't compare "range" */ + + if (config_a->preserve_luminosity != config_b->preserve_luminosity) + return FALSE; + + return TRUE; +} + +static void +gimp_color_balance_config_reset (GimpConfig *config) +{ + GimpColorBalanceConfig *cb_config = GIMP_COLOR_BALANCE_CONFIG (config); + GimpTransferMode range; + + gimp_operation_settings_config_reset_base (config); + + for (range = GIMP_TRANSFER_SHADOWS; + range <= GIMP_TRANSFER_HIGHLIGHTS; + range++) + { + cb_config->range = range; + gimp_color_balance_config_reset_range (cb_config); + } + + gimp_config_reset_property (G_OBJECT (config), "range"); + gimp_config_reset_property (G_OBJECT (config), "preserve-luminosity"); +} + +static gboolean +gimp_color_balance_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpColorBalanceConfig *src_config = GIMP_COLOR_BALANCE_CONFIG (src); + GimpColorBalanceConfig *dest_config = GIMP_COLOR_BALANCE_CONFIG (dest); + GimpTransferMode range; + + if (! gimp_operation_settings_config_copy_base (src, dest, flags)) + return FALSE; + + for (range = GIMP_TRANSFER_SHADOWS; + range <= GIMP_TRANSFER_HIGHLIGHTS; + range++) + { + dest_config->cyan_red[range] = src_config->cyan_red[range]; + dest_config->magenta_green[range] = src_config->magenta_green[range]; + dest_config->yellow_blue[range] = src_config->yellow_blue[range]; + } + + g_object_notify (G_OBJECT (dest), "cyan-red"); + g_object_notify (G_OBJECT (dest), "magenta-green"); + g_object_notify (G_OBJECT (dest), "yellow-blue"); + + dest_config->range = src_config->range; + dest_config->preserve_luminosity = src_config->preserve_luminosity; + + g_object_notify (G_OBJECT (dest), "range"); + g_object_notify (G_OBJECT (dest), "preserve-luminosity"); + + return TRUE; +} + + +/* public functions */ + +void +gimp_color_balance_config_reset_range (GimpColorBalanceConfig *config) +{ + g_return_if_fail (GIMP_IS_COLOR_BALANCE_CONFIG (config)); + + g_object_freeze_notify (G_OBJECT (config)); + + gimp_config_reset_property (G_OBJECT (config), "cyan-red"); + gimp_config_reset_property (G_OBJECT (config), "magenta-green"); + gimp_config_reset_property (G_OBJECT (config), "yellow-blue"); + + g_object_thaw_notify (G_OBJECT (config)); +} diff --git a/app/operations/gimpcolorbalanceconfig.h b/app/operations/gimpcolorbalanceconfig.h new file mode 100644 index 0000000..4c58cea --- /dev/null +++ b/app/operations/gimpcolorbalanceconfig.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcolorbalanceconfig.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_COLOR_BALANCE_CONFIG_H__ +#define __GIMP_COLOR_BALANCE_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +#define GIMP_TYPE_COLOR_BALANCE_CONFIG (gimp_color_balance_config_get_type ()) +#define GIMP_COLOR_BALANCE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfig)) +#define GIMP_COLOR_BALANCE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfigClass)) +#define GIMP_IS_COLOR_BALANCE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG)) +#define GIMP_IS_COLOR_BALANCE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_BALANCE_CONFIG)) +#define GIMP_COLOR_BALANCE_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfigClass)) + + +typedef struct _GimpColorBalanceConfigClass GimpColorBalanceConfigClass; + +struct _GimpColorBalanceConfig +{ + GimpOperationSettings parent_instance; + + GimpTransferMode range; + + gdouble cyan_red[3]; + gdouble magenta_green[3]; + gdouble yellow_blue[3]; + + gboolean preserve_luminosity; +}; + +struct _GimpColorBalanceConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_color_balance_config_get_type (void) G_GNUC_CONST; + +void gimp_color_balance_config_reset_range (GimpColorBalanceConfig *config); + + +#endif /* __GIMP_COLOR_BALANCE_CONFIG_H__ */ diff --git a/app/operations/gimpcurvesconfig.c b/app/operations/gimpcurvesconfig.c new file mode 100644 index 0000000..b63bcf3 --- /dev/null +++ b/app/operations/gimpcurvesconfig.c @@ -0,0 +1,695 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcurvesconfig.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpcurve.h" +#include "core/gimphistogram.h" + +#include "gimpcurvesconfig.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_LINEAR, + PROP_CHANNEL, + PROP_CURVE +}; + + +static void gimp_curves_config_iface_init (GimpConfigInterface *iface); + +static void gimp_curves_config_finalize (GObject *object); +static void gimp_curves_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_curves_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_curves_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_curves_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_curves_config_equal (GimpConfig *a, + GimpConfig *b); +static void gimp_curves_config_reset (GimpConfig *config); +static gboolean gimp_curves_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + +static void gimp_curves_config_curve_dirty (GimpCurve *curve, + GimpCurvesConfig *config); + + +G_DEFINE_TYPE_WITH_CODE (GimpCurvesConfig, gimp_curves_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_curves_config_iface_init)) + +#define parent_class gimp_curves_config_parent_class + + +static void +gimp_curves_config_class_init (GimpCurvesConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->finalize = gimp_curves_config_finalize; + object_class->set_property = gimp_curves_config_set_property; + object_class->get_property = gimp_curves_config_get_property; + + viewable_class->default_icon_name = "gimp-tool-curves"; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LINEAR, + "linear", + _("Linear"), + _("Work on linear RGB"), + FALSE, 0); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL, + "channel", + _("Channel"), + _("The affected channel"), + GIMP_TYPE_HISTOGRAM_CHANNEL, + GIMP_HISTOGRAM_VALUE, 0); + + GIMP_CONFIG_PROP_OBJECT (object_class, PROP_CURVE, + "curve", + _("Curve"), + _("Curve"), + GIMP_TYPE_CURVE, + GIMP_CONFIG_PARAM_AGGREGATE); +} + +static void +gimp_curves_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_curves_config_serialize; + iface->deserialize = gimp_curves_config_deserialize; + iface->equal = gimp_curves_config_equal; + iface->reset = gimp_curves_config_reset; + iface->copy = gimp_curves_config_copy; +} + +static void +gimp_curves_config_init (GimpCurvesConfig *self) +{ + GimpHistogramChannel channel; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + self->curve[channel] = GIMP_CURVE (gimp_curve_new ("curves config")); + + g_signal_connect_object (self->curve[channel], "dirty", + G_CALLBACK (gimp_curves_config_curve_dirty), + self, 0); + } + + gimp_config_reset (GIMP_CONFIG (self)); +} + +static void +gimp_curves_config_finalize (GObject *object) +{ + GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object); + GimpHistogramChannel channel; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + g_object_unref (self->curve[channel]); + self->curve[channel] = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_curves_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object); + + switch (property_id) + { + case PROP_LINEAR: + g_value_set_boolean (value, self->linear); + break; + + case PROP_CHANNEL: + g_value_set_enum (value, self->channel); + break; + + case PROP_CURVE: + g_value_set_object (value, self->curve[self->channel]); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_curves_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object); + + switch (property_id) + { + case PROP_LINEAR: + self->linear = g_value_get_boolean (value); + break; + + case PROP_CHANNEL: + self->channel = g_value_get_enum (value); + g_object_notify (object, "curve"); + break; + + case PROP_CURVE: + { + GimpCurve *src_curve = g_value_get_object (value); + GimpCurve *dest_curve = self->curve[self->channel]; + + if (src_curve && dest_curve) + { + gimp_config_copy (GIMP_CONFIG (src_curve), + GIMP_CONFIG (dest_curve), 0); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_curves_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config); + GimpHistogramChannel channel; + GimpHistogramChannel old_channel; + gboolean success = TRUE; + + if (! gimp_operation_settings_config_serialize_base (config, writer, data) || + ! gimp_config_serialize_property_by_name (config, "linear", writer)) + return FALSE; + + old_channel = c_config->channel; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + c_config->channel = channel; + + /* serialize the channel properties manually (not using + * gimp_config_serialize_properties()), so the parent class' + * properties don't end up in the config file one per channel. + * See bug #700653. + */ + success = + (gimp_config_serialize_property_by_name (config, "channel", writer) && + gimp_config_serialize_property_by_name (config, "curve", writer)); + + if (! success) + break; + } + + c_config->channel = old_channel; + + return success; +} + +static gboolean +gimp_curves_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config); + GimpHistogramChannel old_channel; + gboolean success = TRUE; + + old_channel = c_config->channel; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + g_object_set (config, "channel", old_channel, NULL); + + return success; +} + +static gboolean +gimp_curves_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpCurvesConfig *config_a = GIMP_CURVES_CONFIG (a); + GimpCurvesConfig *config_b = GIMP_CURVES_CONFIG (b); + GimpHistogramChannel channel; + + if (! gimp_operation_settings_config_equal_base (a, b) || + config_a->linear != config_b->linear) + return FALSE; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + GimpCurve *curve_a = config_a->curve[channel]; + GimpCurve *curve_b = config_b->curve[channel]; + + if (curve_a && curve_b) + { + if (! gimp_config_is_equal_to (GIMP_CONFIG (curve_a), + GIMP_CONFIG (curve_b))) + return FALSE; + } + else if (curve_a || curve_b) + { + return FALSE; + } + } + + /* don't compare "channel" */ + + return TRUE; +} + +static void +gimp_curves_config_reset (GimpConfig *config) +{ + GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config); + GimpHistogramChannel channel; + + gimp_operation_settings_config_reset_base (config); + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + c_config->channel = channel; + gimp_curves_config_reset_channel (c_config); + } + + gimp_config_reset_property (G_OBJECT (config), "linear"); + gimp_config_reset_property (G_OBJECT (config), "channel"); +} + +static gboolean +gimp_curves_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpCurvesConfig *src_config = GIMP_CURVES_CONFIG (src); + GimpCurvesConfig *dest_config = GIMP_CURVES_CONFIG (dest); + GimpHistogramChannel channel; + + if (! gimp_operation_settings_config_copy_base (src, dest, flags)) + return FALSE; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + gimp_config_copy (GIMP_CONFIG (src_config->curve[channel]), + GIMP_CONFIG (dest_config->curve[channel]), + flags); + } + + dest_config->linear = src_config->linear; + dest_config->channel = src_config->channel; + + g_object_notify (G_OBJECT (dest), "linear"); + g_object_notify (G_OBJECT (dest), "channel"); + + return TRUE; +} + +static void +gimp_curves_config_curve_dirty (GimpCurve *curve, + GimpCurvesConfig *config) +{ + g_object_notify (G_OBJECT (config), "curve"); +} + + +/* public functions */ + +GObject * +gimp_curves_config_new_spline (gint32 channel, + const gdouble *points, + gint n_points) +{ + GimpCurvesConfig *config; + GimpCurve *curve; + gint i; + + g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE && + channel <= GIMP_HISTOGRAM_ALPHA, NULL); + g_return_val_if_fail (points != NULL, NULL); + g_return_val_if_fail (n_points >= 2 && n_points <= 1024, NULL); + + config = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL); + + curve = config->curve[channel]; + + gimp_data_freeze (GIMP_DATA (curve)); + + gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); + gimp_curve_clear_points (curve); + + for (i = 0; i < n_points; i++) + gimp_curve_add_point (curve, + (gdouble) points[i * 2], + (gdouble) points[i * 2 + 1]); + + gimp_data_thaw (GIMP_DATA (curve)); + + return G_OBJECT (config); +} + +GObject * +gimp_curves_config_new_explicit (gint32 channel, + const gdouble *samples, + gint n_samples) +{ + GimpCurvesConfig *config; + GimpCurve *curve; + gint i; + + g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE && + channel <= GIMP_HISTOGRAM_ALPHA, NULL); + g_return_val_if_fail (samples != NULL, NULL); + g_return_val_if_fail (n_samples >= 2 && n_samples <= 4096, NULL); + + config = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL); + + curve = config->curve[channel]; + + gimp_data_freeze (GIMP_DATA (curve)); + + gimp_curve_set_curve_type (curve, GIMP_CURVE_FREE); + gimp_curve_set_n_samples (curve, n_samples); + + for (i = 0; i < n_samples; i++) + gimp_curve_set_curve (curve, + (gdouble) i / (gdouble) (n_samples - 1), + (gdouble) samples[i]); + + gimp_data_thaw (GIMP_DATA (curve)); + + return G_OBJECT (config); +} + +GObject * +gimp_curves_config_new_spline_cruft (gint32 channel, + const guint8 *points, + gint n_points) +{ + GObject *config; + gdouble *d_points; + gint i; + + g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE && + channel <= GIMP_HISTOGRAM_ALPHA, NULL); + g_return_val_if_fail (points != NULL, NULL); + g_return_val_if_fail (n_points >= 2 && n_points <= 1024, NULL); + + d_points = g_new (gdouble, 2 * n_points); + + for (i = 0; i < n_points; i++) + { + d_points[i * 2] = (gdouble) points[i * 2] / 255.0; + d_points[i * 2 + 1] = (gdouble) points[i * 2 + 1] / 255.0; + } + + config = gimp_curves_config_new_spline (channel, d_points, n_points); + + g_free (d_points); + + return config; +} + +GObject * +gimp_curves_config_new_explicit_cruft (gint32 channel, + const guint8 *samples, + gint n_samples) +{ + GObject *config; + gdouble *d_samples; + gint i; + + g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE && + channel <= GIMP_HISTOGRAM_ALPHA, NULL); + g_return_val_if_fail (samples != NULL, NULL); + g_return_val_if_fail (n_samples >= 2 && n_samples <= 4096, NULL); + + d_samples = g_new (gdouble, n_samples); + + for (i = 0; i < n_samples; i++) + { + d_samples[i] = (gdouble) samples[i] / 255.0; + } + + config = gimp_curves_config_new_explicit (channel, d_samples, n_samples); + + g_free (d_samples); + + return config; +} + +void +gimp_curves_config_reset_channel (GimpCurvesConfig *config) +{ + g_return_if_fail (GIMP_IS_CURVES_CONFIG (config)); + + gimp_config_reset (GIMP_CONFIG (config->curve[config->channel])); +} + +#define GIMP_CURVE_N_CRUFT_POINTS 17 + +gboolean +gimp_curves_config_load_cruft (GimpCurvesConfig *config, + GInputStream *input, + GError **error) +{ + GDataInputStream *data_input; + gint index[5][GIMP_CURVE_N_CRUFT_POINTS]; + gint value[5][GIMP_CURVE_N_CRUFT_POINTS]; + gchar *line; + gsize line_len; + gint i, j; + + g_return_val_if_fail (GIMP_IS_CURVES_CONFIG (config), FALSE); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + data_input = g_data_input_stream_new (input); + + line_len = 64; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + return FALSE; + + if (strcmp (line, "# GIMP Curves File") != 0) + { + g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("not a GIMP Curves file")); + g_object_unref (data_input); + g_free (line); + return FALSE; + } + + for (i = 0; i < 5; i++) + { + for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++) + { + gchar *x_str = NULL; + gchar *y_str = NULL; + + if (! (x_str = g_data_input_stream_read_upto (data_input, " ", -1, + NULL, NULL, error)) || + ! g_data_input_stream_read_byte (data_input, NULL, error) || + ! (y_str = g_data_input_stream_read_upto (data_input, " ", -1, + NULL, NULL, error)) || + ! g_data_input_stream_read_byte (data_input, NULL, error)) + { + g_free (x_str); + g_free (y_str); + g_object_unref (data_input); + return FALSE; + } + + if (sscanf (x_str, "%d", &index[i][j]) != 1 || + sscanf (y_str, "%d", &value[i][j]) != 1) + { + g_set_error_literal (error, + GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("Parse error, didn't find 2 integers")); + g_free (x_str); + g_free (y_str); + g_object_unref (data_input); + return FALSE; + } + + g_free (x_str); + g_free (y_str); + } + } + + g_object_unref (data_input); + + g_object_freeze_notify (G_OBJECT (config)); + + for (i = 0; i < 5; i++) + { + GimpCurve *curve = config->curve[i]; + + gimp_data_freeze (GIMP_DATA (curve)); + + gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); + gimp_curve_clear_points (curve); + + for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++) + { + gdouble x; + gdouble y; + + x = (gdouble) index[i][j] / 255.0; + y = (gdouble) value[i][j] / 255.0; + + if (x >= 0.0) + gimp_curve_add_point (curve, x, y); + } + + gimp_data_thaw (GIMP_DATA (curve)); + } + + config->linear = FALSE; + + g_object_notify (G_OBJECT (config), "linear"); + + g_object_thaw_notify (G_OBJECT (config)); + + return TRUE; +} + +gboolean +gimp_curves_config_save_cruft (GimpCurvesConfig *config, + GOutputStream *output, + GError **error) +{ + GString *string; + gint i; + + g_return_val_if_fail (GIMP_IS_CURVES_CONFIG (config), FALSE); + g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + string = g_string_new ("# GIMP Curves File\n"); + + for (i = 0; i < 5; i++) + { + GimpCurve *curve = config->curve[i]; + gint j; + + if (curve->curve_type == GIMP_CURVE_SMOOTH) + { + g_object_ref (curve); + } + else + { + curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve))); + + gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); + } + + for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++) + { + gint x = -1; + gint y = -1; + + if (j < gimp_curve_get_n_points (curve)) + { + gdouble point_x; + gdouble point_y; + + gimp_curve_get_point (curve, j, &point_x, &point_y); + + x = floor (point_x * 255.999); + y = floor (point_y * 255.999); + } + + g_string_append_printf (string, "%d %d ", x, y); + } + + g_string_append_printf (string, "\n"); + + g_object_unref (curve); + } + + if (! g_output_stream_write_all (output, string->str, string->len, + NULL, NULL, error)) + { + g_prefix_error (error, _("Writing curves file failed: ")); + g_string_free (string, TRUE); + return FALSE; + } + + g_string_free (string, TRUE); + + return TRUE; +} diff --git a/app/operations/gimpcurvesconfig.h b/app/operations/gimpcurvesconfig.h new file mode 100644 index 0000000..c6241fe --- /dev/null +++ b/app/operations/gimpcurvesconfig.h @@ -0,0 +1,81 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcurvesconfig.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_CURVES_CONFIG_H__ +#define __GIMP_CURVES_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +#define GIMP_TYPE_CURVES_CONFIG (gimp_curves_config_get_type ()) +#define GIMP_CURVES_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfig)) +#define GIMP_CURVES_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfigClass)) +#define GIMP_IS_CURVES_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CURVES_CONFIG)) +#define GIMP_IS_CURVES_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CURVES_CONFIG)) +#define GIMP_CURVES_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfigClass)) + + +typedef struct _GimpCurvesConfigClass GimpCurvesConfigClass; + +struct _GimpCurvesConfig +{ + GimpOperationSettings parent_instance; + + gboolean linear; + + GimpHistogramChannel channel; + + GimpCurve *curve[5]; +}; + +struct _GimpCurvesConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_curves_config_get_type (void) G_GNUC_CONST; + +GObject * gimp_curves_config_new_spline (gint32 channel, + const gdouble *points, + gint n_points); +GObject * gimp_curves_config_new_explicit (gint32 channel, + const gdouble *samples, + gint n_samples); + +GObject * gimp_curves_config_new_spline_cruft (gint32 channel, + const guint8 *points, + gint n_points); +GObject * gimp_curves_config_new_explicit_cruft (gint32 channel, + const guint8 *samples, + gint n_samples); + +void gimp_curves_config_reset_channel (GimpCurvesConfig *config); + +gboolean gimp_curves_config_load_cruft (GimpCurvesConfig *config, + GInputStream *input, + GError **error); +gboolean gimp_curves_config_save_cruft (GimpCurvesConfig *config, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_CURVES_CONFIG_H__ */ diff --git a/app/operations/gimphuesaturationconfig.c b/app/operations/gimphuesaturationconfig.c new file mode 100644 index 0000000..4f0a397 --- /dev/null +++ b/app/operations/gimphuesaturationconfig.c @@ -0,0 +1,367 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphuesaturationconfig.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimphuesaturationconfig.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_RANGE, + PROP_HUE, + PROP_SATURATION, + PROP_LIGHTNESS, + PROP_OVERLAP +}; + + +static void gimp_hue_saturation_config_iface_init (GimpConfigInterface *iface); + +static void gimp_hue_saturation_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_hue_saturation_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_hue_saturation_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_hue_saturation_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_hue_saturation_config_equal (GimpConfig *a, + GimpConfig *b); +static void gimp_hue_saturation_config_reset (GimpConfig *config); +static gboolean gimp_hue_saturation_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + + +G_DEFINE_TYPE_WITH_CODE (GimpHueSaturationConfig, gimp_hue_saturation_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_hue_saturation_config_iface_init)) + +#define parent_class gimp_hue_saturation_config_parent_class + + +static void +gimp_hue_saturation_config_class_init (GimpHueSaturationConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->set_property = gimp_hue_saturation_config_set_property; + object_class->get_property = gimp_hue_saturation_config_get_property; + + viewable_class->default_icon_name = "gimp-tool-hue-saturation"; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_RANGE, + "range", + _("Range"), + _("The affected range"), + GIMP_TYPE_HUE_RANGE, + GIMP_HUE_RANGE_ALL, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HUE, + "hue", + _("Hue"), + _("Hue"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SATURATION, + "saturation", + _("Saturation"), + _("Saturation"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LIGHTNESS, + "lightness", + _("Lightness"), + _("Lightness"), + -1.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OVERLAP, + "overlap", + _("Overlap"), + _("Overlap"), + 0.0, 1.0, 0.0, 0); +} + +static void +gimp_hue_saturation_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_hue_saturation_config_serialize; + iface->deserialize = gimp_hue_saturation_config_deserialize; + iface->equal = gimp_hue_saturation_config_equal; + iface->reset = gimp_hue_saturation_config_reset; + iface->copy = gimp_hue_saturation_config_copy; +} + +static void +gimp_hue_saturation_config_init (GimpHueSaturationConfig *self) +{ + gimp_config_reset (GIMP_CONFIG (self)); +} + +static void +gimp_hue_saturation_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpHueSaturationConfig *self = GIMP_HUE_SATURATION_CONFIG (object); + + switch (property_id) + { + case PROP_RANGE: + g_value_set_enum (value, self->range); + break; + + case PROP_HUE: + g_value_set_double (value, self->hue[self->range]); + break; + + case PROP_SATURATION: + g_value_set_double (value, self->saturation[self->range]); + break; + + case PROP_LIGHTNESS: + g_value_set_double (value, self->lightness[self->range]); + break; + + case PROP_OVERLAP: + g_value_set_double (value, self->overlap); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_hue_saturation_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpHueSaturationConfig *self = GIMP_HUE_SATURATION_CONFIG (object); + + switch (property_id) + { + case PROP_RANGE: + self->range = g_value_get_enum (value); + g_object_notify (object, "hue"); + g_object_notify (object, "saturation"); + g_object_notify (object, "lightness"); + break; + + case PROP_HUE: + self->hue[self->range] = g_value_get_double (value); + break; + + case PROP_SATURATION: + self->saturation[self->range] = g_value_get_double (value); + break; + + case PROP_LIGHTNESS: + self->lightness[self->range] = g_value_get_double (value); + break; + + case PROP_OVERLAP: + self->overlap = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_hue_saturation_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config); + GimpHueRange range; + GimpHueRange old_range; + gboolean success = TRUE; + + if (! gimp_operation_settings_config_serialize_base (config, writer, data)) + return FALSE; + + old_range = hs_config->range; + + for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++) + { + hs_config->range = range; + + success = (gimp_config_serialize_property_by_name (config, "range", + writer) && + gimp_config_serialize_property_by_name (config, "hue", + writer) && + gimp_config_serialize_property_by_name (config, "saturation", + writer) && + gimp_config_serialize_property_by_name (config, "lightness", + writer)); + + if (! success) + break; + } + + if (success) + success = gimp_config_serialize_property_by_name (config, "overlap", + writer); + + hs_config->range = old_range; + + return success; +} + +static gboolean +gimp_hue_saturation_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config); + GimpHueRange old_range; + gboolean success = TRUE; + + old_range = hs_config->range; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + g_object_set (config, "range", old_range, NULL); + + return success; +} + +static gboolean +gimp_hue_saturation_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpHueSaturationConfig *config_a = GIMP_HUE_SATURATION_CONFIG (a); + GimpHueSaturationConfig *config_b = GIMP_HUE_SATURATION_CONFIG (b); + GimpHueRange range; + + if (! gimp_operation_settings_config_equal_base (a, b)) + return FALSE; + + for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++) + { + if (config_a->hue[range] != config_b->hue[range] || + config_a->saturation[range] != config_b->saturation[range] || + config_a->lightness[range] != config_b->lightness[range]) + return FALSE; + } + + /* don't compare "range" */ + + if (config_a->overlap != config_b->overlap) + return FALSE; + + return TRUE; +} + +static void +gimp_hue_saturation_config_reset (GimpConfig *config) +{ + GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config); + GimpHueRange range; + + gimp_operation_settings_config_reset_base (config); + + for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++) + { + hs_config->range = range; + gimp_hue_saturation_config_reset_range (hs_config); + } + + gimp_config_reset_property (G_OBJECT (config), "range"); + gimp_config_reset_property (G_OBJECT (config), "overlap"); +} + +static gboolean +gimp_hue_saturation_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpHueSaturationConfig *src_config = GIMP_HUE_SATURATION_CONFIG (src); + GimpHueSaturationConfig *dest_config = GIMP_HUE_SATURATION_CONFIG (dest); + GimpHueRange range; + + if (! gimp_operation_settings_config_copy_base (src, dest, flags)) + return FALSE; + + for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++) + { + dest_config->hue[range] = src_config->hue[range]; + dest_config->saturation[range] = src_config->saturation[range]; + dest_config->lightness[range] = src_config->lightness[range]; + } + + g_object_notify (G_OBJECT (dest), "hue"); + g_object_notify (G_OBJECT (dest), "saturation"); + g_object_notify (G_OBJECT (dest), "lightness"); + + dest_config->range = src_config->range; + dest_config->overlap = src_config->overlap; + + g_object_notify (G_OBJECT (dest), "range"); + g_object_notify (G_OBJECT (dest), "overlap"); + + return TRUE; +} + + +/* public functions */ + +void +gimp_hue_saturation_config_reset_range (GimpHueSaturationConfig *config) +{ + g_return_if_fail (GIMP_IS_HUE_SATURATION_CONFIG (config)); + + g_object_freeze_notify (G_OBJECT (config)); + + gimp_config_reset_property (G_OBJECT (config), "hue"); + gimp_config_reset_property (G_OBJECT (config), "saturation"); + gimp_config_reset_property (G_OBJECT (config), "lightness"); + + g_object_thaw_notify (G_OBJECT (config)); +} diff --git a/app/operations/gimphuesaturationconfig.h b/app/operations/gimphuesaturationconfig.h new file mode 100644 index 0000000..151ac05 --- /dev/null +++ b/app/operations/gimphuesaturationconfig.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimphuesaturationconfig.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_HUE_SATURATION_CONFIG_H__ +#define __GIMP_HUE_SATURATION_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +#define GIMP_TYPE_HUE_SATURATION_CONFIG (gimp_hue_saturation_config_get_type ()) +#define GIMP_HUE_SATURATION_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfig)) +#define GIMP_HUE_SATURATION_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfigClass)) +#define GIMP_IS_HUE_SATURATION_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG)) +#define GIMP_IS_HUE_SATURATION_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HUE_SATURATION_CONFIG)) +#define GIMP_HUE_SATURATION_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfigClass)) + + +typedef struct _GimpHueSaturationConfigClass GimpHueSaturationConfigClass; + +struct _GimpHueSaturationConfig +{ + GimpOperationSettings parent_instance; + + GimpHueRange range; + + gdouble hue[7]; + gdouble saturation[7]; + gdouble lightness[7]; + + gdouble overlap; +}; + +struct _GimpHueSaturationConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_hue_saturation_config_get_type (void) G_GNUC_CONST; + +void gimp_hue_saturation_config_reset_range (GimpHueSaturationConfig *config); + + +#endif /* __GIMP_HUE_SATURATION_CONFIG_H__ */ diff --git a/app/operations/gimplevelsconfig.c b/app/operations/gimplevelsconfig.c new file mode 100644 index 0000000..df5d349 --- /dev/null +++ b/app/operations/gimplevelsconfig.c @@ -0,0 +1,964 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimplevelsconfig.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpcurve.h" +#include "core/gimphistogram.h" + +#include "gimpcurvesconfig.h" +#include "gimplevelsconfig.h" +#include "gimpoperationlevels.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_LINEAR, + PROP_CHANNEL, + PROP_LOW_INPUT, + PROP_HIGH_INPUT, + PROP_CLAMP_INPUT, + PROP_GAMMA, + PROP_LOW_OUTPUT, + PROP_HIGH_OUTPUT, + PROP_CLAMP_OUTPUT +}; + + +static void gimp_levels_config_iface_init (GimpConfigInterface *iface); + +static void gimp_levels_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_levels_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_levels_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +static gboolean gimp_levels_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data); +static gboolean gimp_levels_config_equal (GimpConfig *a, + GimpConfig *b); +static void gimp_levels_config_reset (GimpConfig *config); +static gboolean gimp_levels_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + + +G_DEFINE_TYPE_WITH_CODE (GimpLevelsConfig, gimp_levels_config, + GIMP_TYPE_OPERATION_SETTINGS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_levels_config_iface_init)) + +#define parent_class gimp_levels_config_parent_class + + +static void +gimp_levels_config_class_init (GimpLevelsConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); + + object_class->set_property = gimp_levels_config_set_property; + object_class->get_property = gimp_levels_config_get_property; + + viewable_class->default_icon_name = "gimp-tool-levels"; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LINEAR, + "linear", + _("Linear"), + _("Work on linear RGB"), + FALSE, 0); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL, + "channel", + _("Channel"), + _("The affected channel"), + GIMP_TYPE_HISTOGRAM_CHANNEL, + GIMP_HISTOGRAM_VALUE, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW_INPUT, + "low-input", + _("Low Input"), + _("Low Input"), + 0.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH_INPUT, + "high-input", + _("High Input"), + _("High Input"), + 0.0, 1.0, 1.0, 0); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CLAMP_INPUT, + "clamp-input", + _("Clamp Input"), + _("Clamp input values before applying output mapping."), + FALSE, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_GAMMA, + "gamma", + _("Gamma"), + _("Gamma"), + 0.1, 10.0, 1.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW_OUTPUT, + "low-output", + _("Low Output"), + _("Low Output"), + 0.0, 1.0, 0.0, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH_OUTPUT, + "high-output", + _("High Output"), + _("High Output"), + 0.0, 1.0, 1.0, 0); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CLAMP_OUTPUT, + "clamp-output", + _("Clamp Output"), + _("Clamp final output values."), + FALSE, 0); +} + +static void +gimp_levels_config_iface_init (GimpConfigInterface *iface) +{ + iface->serialize = gimp_levels_config_serialize; + iface->deserialize = gimp_levels_config_deserialize; + iface->equal = gimp_levels_config_equal; + iface->reset = gimp_levels_config_reset; + iface->copy = gimp_levels_config_copy; +} + +static void +gimp_levels_config_init (GimpLevelsConfig *self) +{ + gimp_config_reset (GIMP_CONFIG (self)); +} + +static void +gimp_levels_config_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpLevelsConfig *self = GIMP_LEVELS_CONFIG (object); + + switch (property_id) + { + case PROP_LINEAR: + g_value_set_boolean (value, self->linear); + break; + + case PROP_CHANNEL: + g_value_set_enum (value, self->channel); + break; + + case PROP_LOW_INPUT: + g_value_set_double (value, self->low_input[self->channel]); + break; + + case PROP_HIGH_INPUT: + g_value_set_double (value, self->high_input[self->channel]); + break; + + case PROP_CLAMP_INPUT: + g_value_set_boolean (value, self->clamp_input); + break; + + case PROP_GAMMA: + g_value_set_double (value, self->gamma[self->channel]); + break; + + case PROP_LOW_OUTPUT: + g_value_set_double (value, self->low_output[self->channel]); + break; + + case PROP_HIGH_OUTPUT: + g_value_set_double (value, self->high_output[self->channel]); + break; + + case PROP_CLAMP_OUTPUT: + g_value_set_boolean (value, self->clamp_output); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_levels_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpLevelsConfig *self = GIMP_LEVELS_CONFIG (object); + + switch (property_id) + { + case PROP_LINEAR: + self->linear = g_value_get_boolean (value); + break; + + case PROP_CHANNEL: + self->channel = g_value_get_enum (value); + g_object_notify (object, "low-input"); + g_object_notify (object, "high-input"); + g_object_notify (object, "gamma"); + g_object_notify (object, "low-output"); + g_object_notify (object, "high-output"); + break; + + case PROP_LOW_INPUT: + self->low_input[self->channel] = g_value_get_double (value); + break; + + case PROP_HIGH_INPUT: + self->high_input[self->channel] = g_value_get_double (value); + break; + + case PROP_CLAMP_INPUT: + self->clamp_input = g_value_get_boolean (value); + break; + + case PROP_GAMMA: + self->gamma[self->channel] = g_value_get_double (value); + break; + + case PROP_LOW_OUTPUT: + self->low_output[self->channel] = g_value_get_double (value); + break; + + case PROP_HIGH_OUTPUT: + self->high_output[self->channel] = g_value_get_double (value); + break; + + case PROP_CLAMP_OUTPUT: + self->clamp_output = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_levels_config_serialize (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config); + GimpHistogramChannel channel; + GimpHistogramChannel old_channel; + gboolean success = TRUE; + + if (! gimp_operation_settings_config_serialize_base (config, writer, data) || + ! gimp_config_serialize_property_by_name (config, "linear", writer) || + ! gimp_config_serialize_property_by_name (config, "clamp-input", writer) || + ! gimp_config_serialize_property_by_name (config, "clamp-output", writer)) + return FALSE; + + old_channel = l_config->channel; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + l_config->channel = channel; + + /* serialize the channel properties manually (not using + * gimp_config_serialize_properties()), so the parent class' + * properties don't end up in the config file one per channel. + * See bug #700653. + */ + success = + (gimp_config_serialize_property_by_name (config, "channel", writer) && + gimp_config_serialize_property_by_name (config, "low-input", writer) && + gimp_config_serialize_property_by_name (config, "high-input", writer) && + gimp_config_serialize_property_by_name (config, "gamma", writer) && + gimp_config_serialize_property_by_name (config, "low-output", writer) && + gimp_config_serialize_property_by_name (config, "high-output", writer)); + + if (! success) + break; + } + + l_config->channel = old_channel; + + return success; +} + +static gboolean +gimp_levels_config_deserialize (GimpConfig *config, + GScanner *scanner, + gint nest_level, + gpointer data) +{ + GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config); + GimpHistogramChannel old_channel; + gboolean success = TRUE; + + old_channel = l_config->channel; + + success = gimp_config_deserialize_properties (config, scanner, nest_level); + + g_object_set (config, "channel", old_channel, NULL); + + return success; +} + +static gboolean +gimp_levels_config_equal (GimpConfig *a, + GimpConfig *b) +{ + GimpLevelsConfig *config_a = GIMP_LEVELS_CONFIG (a); + GimpLevelsConfig *config_b = GIMP_LEVELS_CONFIG (b); + GimpHistogramChannel channel; + + if (! gimp_operation_settings_config_equal_base (a, b) || + config_a->linear != config_b->linear || + config_a->clamp_input != config_b->clamp_input || + config_a->clamp_output != config_b->clamp_output) + return FALSE; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + if (config_a->gamma[channel] != config_b->gamma[channel] || + config_a->low_input[channel] != config_b->low_input[channel] || + config_a->high_input[channel] != config_b->high_input[channel] || + config_a->low_output[channel] != config_b->low_output[channel] || + config_a->high_output[channel] != config_b->high_output[channel]) + return FALSE; + } + + /* don't compare "channel" */ + + return TRUE; +} + +static void +gimp_levels_config_reset (GimpConfig *config) +{ + GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config); + GimpHistogramChannel channel; + + gimp_operation_settings_config_reset_base (config); + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + l_config->channel = channel; + gimp_levels_config_reset_channel (l_config); + } + + gimp_config_reset_property (G_OBJECT (config), "linear"); + gimp_config_reset_property (G_OBJECT (config), "channel"); + gimp_config_reset_property (G_OBJECT (config), "clamp-input"); + gimp_config_reset_property (G_OBJECT (config), "clamp_output"); +} + +static gboolean +gimp_levels_config_copy (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + GimpLevelsConfig *src_config = GIMP_LEVELS_CONFIG (src); + GimpLevelsConfig *dest_config = GIMP_LEVELS_CONFIG (dest); + GimpHistogramChannel channel; + + if (! gimp_operation_settings_config_copy_base (src, dest, flags)) + return FALSE; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + dest_config->gamma[channel] = src_config->gamma[channel]; + dest_config->low_input[channel] = src_config->low_input[channel]; + dest_config->high_input[channel] = src_config->high_input[channel]; + dest_config->low_output[channel] = src_config->low_output[channel]; + dest_config->high_output[channel] = src_config->high_output[channel]; + } + + g_object_notify (G_OBJECT (dest), "gamma"); + g_object_notify (G_OBJECT (dest), "low-input"); + g_object_notify (G_OBJECT (dest), "high-input"); + g_object_notify (G_OBJECT (dest), "low-output"); + g_object_notify (G_OBJECT (dest), "high-output"); + + dest_config->linear = src_config->linear; + dest_config->channel = src_config->channel; + dest_config->clamp_input = src_config->clamp_input; + dest_config->clamp_output = src_config->clamp_output; + + g_object_notify (G_OBJECT (dest), "linear"); + g_object_notify (G_OBJECT (dest), "channel"); + g_object_notify (G_OBJECT (dest), "clamp-input"); + g_object_notify (G_OBJECT (dest), "clamp-output"); + + return TRUE; +} + + +/* public functions */ + +void +gimp_levels_config_reset_channel (GimpLevelsConfig *config) +{ + g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config)); + + g_object_freeze_notify (G_OBJECT (config)); + + gimp_config_reset_property (G_OBJECT (config), "gamma"); + gimp_config_reset_property (G_OBJECT (config), "low-input"); + gimp_config_reset_property (G_OBJECT (config), "high-input"); + gimp_config_reset_property (G_OBJECT (config), "low-output"); + gimp_config_reset_property (G_OBJECT (config), "high-output"); + + g_object_thaw_notify (G_OBJECT (config)); +} + +void +gimp_levels_config_stretch (GimpLevelsConfig *config, + GimpHistogram *histogram, + gboolean is_color) +{ + g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config)); + g_return_if_fail (histogram != NULL); + + g_object_freeze_notify (G_OBJECT (config)); + + if (is_color) + { + GimpHistogramChannel channel; + + /* Set the overall value to defaults */ + channel = config->channel; + config->channel = GIMP_HISTOGRAM_VALUE; + gimp_levels_config_reset_channel (config); + config->channel = channel; + + for (channel = GIMP_HISTOGRAM_RED; + channel <= GIMP_HISTOGRAM_BLUE; + channel++) + { + gimp_levels_config_stretch_channel (config, histogram, channel); + } + } + else + { + gimp_levels_config_stretch_channel (config, histogram, + GIMP_HISTOGRAM_VALUE); + } + + g_object_thaw_notify (G_OBJECT (config)); +} + +void +gimp_levels_config_stretch_channel (GimpLevelsConfig *config, + GimpHistogram *histogram, + GimpHistogramChannel channel) +{ + gdouble count; + gdouble bias = 0.006; + gint n_bins; + gint i; + + g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config)); + g_return_if_fail (histogram != NULL); + + g_object_freeze_notify (G_OBJECT (config)); + + config->gamma[channel] = 1.0; + config->low_output[channel] = 0.0; + config->high_output[channel] = 1.0; + + n_bins = gimp_histogram_n_bins (histogram); + count = gimp_histogram_get_count (histogram, channel, 0, n_bins - 1); + + if (count == 0.0) + { + config->low_input[channel] = 0.0; + config->high_input[channel] = 0.0; + } + else + { + gdouble new_count; + gdouble percentage; + gdouble next_percentage; + + /* Set the low input */ + new_count = 0.0; + + for (i = 0; i < (n_bins - 1); i++) + { + new_count += gimp_histogram_get_value (histogram, channel, i); + percentage = new_count / count; + next_percentage = (new_count + + gimp_histogram_get_value (histogram, + channel, + i + 1)) / count; + + if (fabs (percentage - bias) < fabs (next_percentage - bias)) + { + config->low_input[channel] = (gdouble) (i + 1) / (n_bins - 1); + break; + } + } + + /* Set the high input */ + new_count = 0.0; + + for (i = (n_bins - 1); i > 0; i--) + { + new_count += gimp_histogram_get_value (histogram, channel, i); + percentage = new_count / count; + next_percentage = (new_count + + gimp_histogram_get_value (histogram, + channel, + i - 1)) / count; + + if (fabs (percentage - bias) < fabs (next_percentage - bias)) + { + config->high_input[channel] = (gdouble) (i - 1) / (n_bins - 1); + break; + } + } + } + + g_object_notify (G_OBJECT (config), "gamma"); + g_object_notify (G_OBJECT (config), "low-input"); + g_object_notify (G_OBJECT (config), "high-input"); + g_object_notify (G_OBJECT (config), "low-output"); + g_object_notify (G_OBJECT (config), "high-output"); + + g_object_thaw_notify (G_OBJECT (config)); +} + +static gdouble +gimp_levels_config_input_from_color (GimpHistogramChannel channel, + const GimpRGB *color) +{ + switch (channel) + { + case GIMP_HISTOGRAM_VALUE: + return MAX (MAX (color->r, color->g), color->b); + + case GIMP_HISTOGRAM_RED: + return color->r; + + case GIMP_HISTOGRAM_GREEN: + return color->g; + + case GIMP_HISTOGRAM_BLUE: + return color->b; + + case GIMP_HISTOGRAM_ALPHA: + return color->a; + + case GIMP_HISTOGRAM_RGB: + return MIN (MIN (color->r, color->g), color->b); + + case GIMP_HISTOGRAM_LUMINANCE: + return GIMP_RGB_LUMINANCE (color->r, color->g, color->b); + } + + return 0.0; +} + +void +gimp_levels_config_adjust_by_colors (GimpLevelsConfig *config, + GimpHistogramChannel channel, + const GimpRGB *black, + const GimpRGB *gray, + const GimpRGB *white) +{ + g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config)); + + g_object_freeze_notify (G_OBJECT (config)); + + if (black) + { + config->low_input[channel] = gimp_levels_config_input_from_color (channel, + black); + g_object_notify (G_OBJECT (config), "low-input"); + } + + + if (white) + { + config->high_input[channel] = gimp_levels_config_input_from_color (channel, + white); + g_object_notify (G_OBJECT (config), "high-input"); + } + + if (gray) + { + gdouble input; + gdouble range; + gdouble inten; + gdouble out_light; + gdouble lightness; + + /* Calculate lightness value */ + lightness = GIMP_RGB_LUMINANCE (gray->r, gray->g, gray->b); + + input = gimp_levels_config_input_from_color (channel, gray); + + range = config->high_input[channel] - config->low_input[channel]; + if (range <= 0) + goto out; + + input -= config->low_input[channel]; + if (input < 0) + goto out; + + /* Normalize input and lightness */ + inten = input / range; + out_light = lightness / range; + + /* See bug 622054: picking pure black or white as gamma doesn't + * work. But we cannot compare to 0.0 or 1.0 because cpus and + * compilers are shit. If you try to check out_light using + * printf() it will give exact 0.0 or 1.0 anyway, probably + * because the generated code is different and out_light doesn't + * live in a register. That must be why the cpu/compiler mafia + * invented epsilon and defined this shit to be the programmer's + * responsibility. + */ + if (out_light <= 0.0001 || out_light >= 0.9999) + goto out; + + /* Map selected color to corresponding lightness */ + config->gamma[channel] = log (inten) / log (out_light); + config->gamma[channel] = CLAMP (config->gamma[channel], 0.1, 10.0); + g_object_notify (G_OBJECT (config), "gamma"); + } + + out: + g_object_thaw_notify (G_OBJECT (config)); +} + +GimpCurvesConfig * +gimp_levels_config_to_curves_config (GimpLevelsConfig *config) +{ + GimpCurvesConfig *curves; + GimpHistogramChannel channel; + + g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), NULL); + + curves = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL); + + gimp_operation_settings_config_copy_base (GIMP_CONFIG (config), + GIMP_CONFIG (curves), + 0); + + curves->linear = config->linear; + + for (channel = GIMP_HISTOGRAM_VALUE; + channel <= GIMP_HISTOGRAM_ALPHA; + channel++) + { + GimpCurve *curve = curves->curve[channel]; + static const gint n = 8; + gdouble gamma = config->gamma[channel]; + gdouble delta_in; + gdouble delta_out; + gdouble x, y; + + /* clear the points set by default */ + gimp_curve_clear_points (curve); + + delta_in = config->high_input[channel] - config->low_input[channel]; + delta_out = config->high_output[channel] - config->low_output[channel]; + + x = config->low_input[channel]; + y = config->low_output[channel]; + + gimp_curve_add_point (curve, x, y); + + if (delta_out != 0 && gamma != 1.0) + { + /* The Levels tool performs gamma adjustment, which is a + * power law, while the Curves tool uses cubic Bézier + * curves. Here we try to approximate this gamma adjustment + * with a Bézier curve with 5 control points. Two of them + * must be (low_input, low_output) and (high_input, + * high_output), so we need to add 3 more control points in + * the middle. + */ + gint i; + + if (gamma > 1) + { + /* Case no. 1: γ > 1 + * + * The curve should look like a horizontal + * parabola. Since its curvature is greatest when x is + * small, we add more control points there, so the + * approximation is more accurate. I decided to set the + * length of the consecutive segments to x₀, γ⋅x₀, γ²⋅x₀ + * and γ³⋅x₀ and I saw that the curves looked + * good. Still, this is completely arbitrary. + */ + gdouble dx = 0; + gdouble x0; + + for (i = 0; i < n; ++i) + dx = dx * gamma + 1; + x0 = delta_in / dx; + + dx = 0; + for (i = 1; i < n; ++i) + { + dx = dx * gamma + x0; + x = config->low_input[channel] + dx; + y = config->low_output[channel] + delta_out * + gimp_operation_levels_map_input (config, channel, x); + gimp_curve_add_point (curve, x, y); + } + } + else + { + /* Case no. 2: γ < 1 + * + * The curve is the same as the one in case no. 1, + * observed through a reflexion along the y = x axis. So + * if we invert γ and swap the x and y axes we can use + * the same method as in case no. 1. + */ + GimpLevelsConfig *config_inv; + gdouble dy = 0; + gdouble y0; + const gdouble gamma_inv = 1 / gamma; + + config_inv = gimp_config_duplicate (GIMP_CONFIG (config)); + + config_inv->gamma[channel] = gamma_inv; + config_inv->low_input[channel] = config->low_output[channel]; + config_inv->low_output[channel] = config->low_input[channel]; + config_inv->high_input[channel] = config->high_output[channel]; + config_inv->high_output[channel] = config->high_input[channel]; + + for (i = 0; i < n; ++i) + dy = dy * gamma_inv + 1; + y0 = delta_out / dy; + + dy = 0; + for (i = 1; i < n; ++i) + { + dy = dy * gamma_inv + y0; + y = config->low_output[channel] + dy; + x = config->low_input[channel] + delta_in * + gimp_operation_levels_map_input (config_inv, channel, y); + gimp_curve_add_point (curve, x, y); + } + + g_object_unref (config_inv); + } + } + + x = config->high_input[channel]; + y = config->high_output[channel]; + + gimp_curve_add_point (curve, x, y); + } + + return curves; +} + +gboolean +gimp_levels_config_load_cruft (GimpLevelsConfig *config, + GInputStream *input, + GError **error) +{ + GDataInputStream *data_input; + gint low_input[5]; + gint high_input[5]; + gint low_output[5]; + gint high_output[5]; + gdouble gamma[5]; + gchar *line; + gsize line_len; + gint i; + + g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + data_input = g_data_input_stream_new (input); + + line_len = 64; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + return FALSE; + + if (strcmp (line, "# GIMP Levels File") != 0) + { + g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("not a GIMP Levels file")); + g_object_unref (data_input); + g_free (line); + return FALSE; + } + + g_free (line); + + for (i = 0; i < 5; i++) + { + gchar float_buf[32]; + gchar *endp; + gint fields; + + line_len = 64; + line = gimp_data_input_stream_read_line_always (data_input, &line_len, + NULL, error); + if (! line) + { + g_object_unref (data_input); + return FALSE; + } + + fields = sscanf (line, "%d %d %d %d %31s", + &low_input[i], + &high_input[i], + &low_output[i], + &high_output[i], + float_buf); + + g_free (line); + + if (fields != 5) + goto error; + + gamma[i] = g_ascii_strtod (float_buf, &endp); + + if (endp == float_buf || errno == ERANGE) + goto error; + } + + g_object_unref (data_input); + + g_object_freeze_notify (G_OBJECT (config)); + + for (i = 0; i < 5; i++) + { + config->low_input[i] = low_input[i] / 255.0; + config->high_input[i] = high_input[i] / 255.0; + config->gamma[i] = gamma[i]; + config->low_output[i] = low_output[i] / 255.0; + config->high_output[i] = high_output[i] / 255.0; + } + + config->linear = FALSE; + config->clamp_input = TRUE; + config->clamp_output = TRUE; + + g_object_notify (G_OBJECT (config), "linear"); + g_object_notify (G_OBJECT (config), "low-input"); + g_object_notify (G_OBJECT (config), "high-input"); + g_object_notify (G_OBJECT (config), "clamp-input"); + g_object_notify (G_OBJECT (config), "gamma"); + g_object_notify (G_OBJECT (config), "low-output"); + g_object_notify (G_OBJECT (config), "high-output"); + g_object_notify (G_OBJECT (config), "clamp-output"); + + g_object_thaw_notify (G_OBJECT (config)); + + return TRUE; + + error: + g_object_unref (data_input); + + g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("parse error")); + return FALSE; +} + +gboolean +gimp_levels_config_save_cruft (GimpLevelsConfig *config, + GOutputStream *output, + GError **error) +{ + GString *string; + gint i; + + g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE); + g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + string = g_string_new ("# GIMP Levels File\n"); + + for (i = 0; i < 5; i++) + { + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + + g_string_append_printf (string, + "%d %d %d %d %s\n", + (gint) (config->low_input[i] * 255.999), + (gint) (config->high_input[i] * 255.999), + (gint) (config->low_output[i] * 255.999), + (gint) (config->high_output[i] * 255.999), + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, + config->gamma[i])); + } + + if (! g_output_stream_write_all (output, string->str, string->len, + NULL, NULL, error)) + { + g_prefix_error (error, _("Writing levels file failed: ")); + g_string_free (string, TRUE); + return FALSE; + } + + g_string_free (string, TRUE); + + return TRUE; +} diff --git a/app/operations/gimplevelsconfig.h b/app/operations/gimplevelsconfig.h new file mode 100644 index 0000000..2e7569c --- /dev/null +++ b/app/operations/gimplevelsconfig.h @@ -0,0 +1,92 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimplevelsconfig.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_LEVELS_CONFIG_H__ +#define __GIMP_LEVELS_CONFIG_H__ + + +#include "gimpoperationsettings.h" + + +#define GIMP_TYPE_LEVELS_CONFIG (gimp_levels_config_get_type ()) +#define GIMP_LEVELS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfig)) +#define GIMP_LEVELS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfigClass)) +#define GIMP_IS_LEVELS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LEVELS_CONFIG)) +#define GIMP_IS_LEVELS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LEVELS_CONFIG)) +#define GIMP_LEVELS_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfigClass)) + + +typedef struct _GimpLevelsConfigClass GimpLevelsConfigClass; + +struct _GimpLevelsConfig +{ + GimpOperationSettings parent_instance; + + gboolean linear; + + GimpHistogramChannel channel; + + gdouble low_input[5]; + gdouble high_input[5]; + + gboolean clamp_input; + + gdouble gamma[5]; + + gdouble low_output[5]; + gdouble high_output[5]; + + gboolean clamp_output; +}; + +struct _GimpLevelsConfigClass +{ + GimpOperationSettingsClass parent_class; +}; + + +GType gimp_levels_config_get_type (void) G_GNUC_CONST; + +void gimp_levels_config_reset_channel (GimpLevelsConfig *config); + +void gimp_levels_config_stretch (GimpLevelsConfig *config, + GimpHistogram *histogram, + gboolean is_color); +void gimp_levels_config_stretch_channel (GimpLevelsConfig *config, + GimpHistogram *histogram, + GimpHistogramChannel channel); +void gimp_levels_config_adjust_by_colors (GimpLevelsConfig *config, + GimpHistogramChannel channel, + const GimpRGB *black, + const GimpRGB *gray, + const GimpRGB *white); + +GimpCurvesConfig * + gimp_levels_config_to_curves_config (GimpLevelsConfig *config); + +gboolean gimp_levels_config_load_cruft (GimpLevelsConfig *config, + GInputStream *input, + GError **error); +gboolean gimp_levels_config_save_cruft (GimpLevelsConfig *config, + GOutputStream *output, + GError **error); + + +#endif /* __GIMP_LEVELS_CONFIG_H__ */ diff --git a/app/operations/gimpoperationborder.c b/app/operations/gimpoperationborder.c new file mode 100644 index 0000000..e08481c --- /dev/null +++ b/app/operations/gimpoperationborder.c @@ -0,0 +1,748 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationborder.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationborder.h" + + +enum +{ + PROP_0, + PROP_RADIUS_X, + PROP_RADIUS_Y, + PROP_FEATHER, + PROP_EDGE_LOCK +}; + + +static void gimp_operation_border_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_border_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static GeglRectangle +gimp_operation_border_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static GeglRectangle + gimp_operation_border_get_cached_region (GeglOperation *self, + const GeglRectangle *roi); +static void gimp_operation_border_prepare (GeglOperation *operation); +static gboolean gimp_operation_border_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationBorder, gimp_operation_border, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_border_parent_class + + +static void +gimp_operation_border_class_init (GimpOperationBorderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_border_set_property; + object_class->get_property = gimp_operation_border_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:border", + "categories", "gimp", + "description", "GIMP Border operation", + NULL); + + operation_class->prepare = gimp_operation_border_prepare; + operation_class->get_required_for_output = gimp_operation_border_get_required_for_output; + operation_class->get_cached_region = gimp_operation_border_get_cached_region; + operation_class->threaded = FALSE; + + filter_class->process = gimp_operation_border_process; + + g_object_class_install_property (object_class, PROP_RADIUS_X, + g_param_spec_int ("radius-x", + "Radius X", + "Border radius in X diection", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RADIUS_Y, + g_param_spec_int ("radius-y", + "Radius Y", + "Border radius in Y diection", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FEATHER, + g_param_spec_boolean ("feather", + "Feather", + "Feather the border", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_EDGE_LOCK, + g_param_spec_boolean ("edge-lock", + "Edge Lock", + "Shrink from border", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_border_init (GimpOperationBorder *self) +{ +} + +static void +gimp_operation_border_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationBorder *self = GIMP_OPERATION_BORDER (object); + + switch (property_id) + { + case PROP_RADIUS_X: + g_value_set_int (value, self->radius_x); + break; + + case PROP_RADIUS_Y: + g_value_set_int (value, self->radius_y); + break; + + case PROP_FEATHER: + g_value_set_boolean (value, self->feather); + break; + + case PROP_EDGE_LOCK: + g_value_set_boolean (value, self->edge_lock); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_border_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationBorder *self = GIMP_OPERATION_BORDER (object); + + switch (property_id) + { + case PROP_RADIUS_X: + self->radius_x = g_value_get_int (value); + break; + + case PROP_RADIUS_Y: + self->radius_y = g_value_get_int (value); + break; + + case PROP_FEATHER: + self->feather = g_value_get_boolean (value); + break; + + case PROP_EDGE_LOCK: + self->edge_lock = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_border_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space)); +} + +static GeglRectangle +gimp_operation_border_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static GeglRectangle +gimp_operation_border_get_cached_region (GeglOperation *self, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static inline void +rotate_pointers (gfloat **p, + guint32 n) +{ + guint32 i; + gfloat *tmp; + + tmp = p[0]; + + for (i = 0; i < n - 1; i++) + p[i] = p[i + 1]; + + p[i] = tmp; +} + +/* Computes whether pixels in `buf[1]', if they are selected, have neighbouring + pixels that are unselected. Put result in `transition'. */ +static void +compute_transition (gfloat *transition, + gfloat **buf, + gint32 width, + gboolean edge_lock) +{ + register gint32 x = 0; + + if (width == 1) + { + if (buf[1][0] >= 0.5 && (buf[0][0] < 0.5 || buf[2][0] < 0.5)) + transition[0] = 1.0; + else + transition[0] = 0.0; + return; + } + + if (buf[1][0] >= 0.5 && edge_lock) + { + /* The pixel to the left (outside of the canvas) is considered selected, + so we check if there are any unselected pixels in neighbouring pixels + _on_ the canvas. */ + if (buf[0][x] < 0.5 || buf[0][x + 1] < 0.5 || + buf[1][x + 1] < 0.5 || + buf[2][x] < 0.5 || buf[2][x + 1] < 0.5 ) + { + transition[x] = 1.0; + } + else + { + transition[x] = 0.0; + } + } + else if (buf[1][0] >= 0.5 && !edge_lock) + { + /* We must not care about neighbouring pixels on the image canvas since + there always are unselected pixels to the left (which is outside of + the image canvas). */ + transition[x] = 1.0; + } + else + { + transition[x] = 0.0; + } + + for (x = 1; x < width - 1; x++) + { + if (buf[1][x] >= 0.5) + { + if (buf[0][x - 1] < 0.5 || buf[0][x] < 0.5 || buf[0][x + 1] < 0.5 || + buf[1][x - 1] < 0.5 || buf[1][x + 1] < 0.5 || + buf[2][x - 1] < 0.5 || buf[2][x] < 0.5 || buf[2][x + 1] < 0.5) + transition[x] = 1.0; + else + transition[x] = 0.0; + } + else + { + transition[x] = 0.0; + } + } + + if (buf[1][width - 1] >= 0.5 && edge_lock) + { + /* The pixel to the right (outside of the canvas) is considered selected, + so we check if there are any unselected pixels in neighbouring pixels + _on_ the canvas. */ + if ( buf[0][x - 1] < 0.5 || buf[0][x] < 0.5 || + buf[1][x - 1] < 0.5 || + buf[2][x - 1] < 0.5 || buf[2][x] < 0.5) + { + transition[width - 1] = 1.0; + } + else + { + transition[width - 1] = 0.0; + } + } + else if (buf[1][width - 1] >= 0.5 && !edge_lock) + { + /* We must not care about neighbouring pixels on the image canvas since + there always are unselected pixels to the right (which is outside of + the image canvas). */ + transition[width - 1] = 1.0; + } + else + { + transition[width - 1] = 0.0; + } +} + +static gboolean +gimp_operation_border_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + /* This function has no bugs, but if you imagine some you can blame + * them on jaycox@gimp.org + */ + GimpOperationBorder *self = GIMP_OPERATION_BORDER (operation); + const Babl *input_format = gegl_operation_get_format (operation, "input"); + const Babl *output_format = gegl_operation_get_format (operation, "output"); + + gint32 i, j, x, y; + + /* A cache used in the algorithm as it works its way down. `buf[1]' is the + current row. Thus, at algorithm initialization, `buf[0]' represents the + row 'above' the first row of the region. */ + gfloat *buf[3]; + + /* The resulting selection is calculated row by row, and this buffer holds the + output for each individual row, on each iteration. */ + gfloat *out; + + /* Keeps track of transitional pixels (pixels that are selected and have + unselected neighbouring pixels). */ + gfloat **transition; + + /* TODO: Figure out role clearly in algorithm. */ + gint16 *max; + + /* TODO: Figure out role clearly in algorithm. */ + gfloat **density; + + gint16 last_index; + + /* optimize this case specifically */ + if (self->radius_x == 1 && self->radius_y == 1) + { + gfloat *transition; + gfloat *source[3]; + + for (i = 0; i < 3; i++) + source[i] = g_new (gfloat, roi->width); + + transition = g_new (gfloat, roi->width); + + /* With `self->edge_lock', initialize row above image as + * selected, otherwise, initialize as unselected. + */ + if (self->edge_lock) + { + for (i = 0; i < roi->width; i++) + source[0][i] = 1.0; + } + else + { + memset (source[0], 0, roi->width * sizeof (gfloat)); + } + + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + 0, + roi->width, 1), + 1.0, input_format, source[1], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + if (roi->height > 1) + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + 1, + roi->width, 1), + 1.0, input_format, source[2], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + else + memcpy (source[2], source[1], roi->width * sizeof (gfloat)); + + compute_transition (transition, source, roi->width, self->edge_lock); + gegl_buffer_set (output, + GEGL_RECTANGLE (roi->x, roi->y, + roi->width, 1), + 0, output_format, transition, + GEGL_AUTO_ROWSTRIDE); + + for (y = 1; y < roi->height; y++) + { + rotate_pointers (source, 3); + + if (y + 1 < roi->height) + { + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + y + 1, + roi->width, 1), + 1.0, input_format, source[2], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + else + { + /* Depending on `self->edge_lock', set the row below the + * image as either selected or non-selected. + */ + if (self->edge_lock) + { + for (i = 0; i < roi->width; i++) + source[2][i] = 1.0; + } + else + { + memset (source[2], 0, roi->width * sizeof (gfloat)); + } + } + + compute_transition (transition, source, roi->width, self->edge_lock); + gegl_buffer_set (output, + GEGL_RECTANGLE (roi->x, roi->y + y, + roi->width, 1), + 0, output_format, transition, + GEGL_AUTO_ROWSTRIDE); + } + + for (i = 0; i < 3; i++) + g_free (source[i]); + + g_free (transition); + + /* Finished handling the radius = 1 special case, return here. */ + return TRUE; + } + + max = g_new (gint16, roi->width + 2 * self->radius_x); + + for (i = 0; i < (roi->width + 2 * self->radius_x); i++) + max[i] = self->radius_y + 2; + + max += self->radius_x; + + for (i = 0; i < 3; i++) + buf[i] = g_new (gfloat, roi->width); + + transition = g_new (gfloat *, self->radius_y + 1); + + for (i = 0; i < self->radius_y + 1; i++) + { + transition[i] = g_new (gfloat, roi->width + 2 * self->radius_x); + memset (transition[i], 0, + (roi->width + 2 * self->radius_x) * sizeof (gfloat)); + transition[i] += self->radius_x; + } + + out = g_new (gfloat, roi->width); + + density = g_new (gfloat *, 2 * self->radius_x + 1); + density += self->radius_x; + + /* allocate density[][] */ + for (x = 0; x < (self->radius_x + 1); x++) + { + density[ x] = g_new (gfloat, 2 * self->radius_y + 1); + density[ x] += self->radius_y; + density[-x] = density[x]; + } + + /* compute density[][] */ + for (x = 0; x < (self->radius_x + 1); x++) + { + gdouble tmpx, tmpy, dist; + gfloat a; + + if (x > 0) + tmpx = x - 0.5; + else if (x < 0) + tmpx = x + 0.5; + else + tmpx = 0.0; + + for (y = 0; y < (self->radius_y + 1); y++) + { + if (y > 0) + tmpy = y - 0.5; + else if (y < 0) + tmpy = y + 0.5; + else + tmpy = 0.0; + + dist = ((tmpy * tmpy) / (self->radius_y * self->radius_y) + + (tmpx * tmpx) / (self->radius_x * self->radius_x)); + + if (dist < 1.0) + { + if (self->feather) + a = 1.0 - sqrt (dist); + else + a = 1.0; + } + else + { + a = 0.0; + } + + density[ x][ y] = a; + density[ x][-y] = a; + density[-x][ y] = a; + density[-x][-y] = a; + } + } + + /* Since the algorithm considerers `buf[0]' to be 'over' the row + * currently calculated, we must start with `buf[0]' as non-selected + * if there is no `self->edge_lock. If there is an + * 'self->edge_lock', initialize the first row to 'selected'. Refer + * to bug #350009. + */ + if (self->edge_lock) + { + for (i = 0; i < roi->width; i++) + buf[0][i] = 1.0; + } + else + { + memset (buf[0], 0, roi->width * sizeof (gfloat)); + } + + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + 0, + roi->width, 1), + 1.0, input_format, buf[1], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + if (roi->height > 1) + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + 1, + roi->width, 1), + 1.0, input_format, buf[2], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + else + memcpy (buf[2], buf[1], roi->width * sizeof (gfloat)); + + compute_transition (transition[1], buf, roi->width, self->edge_lock); + + /* set up top of image */ + for (y = 1; y < self->radius_y && y + 1 < roi->height; y++) + { + rotate_pointers (buf, 3); + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + y + 1, + roi->width, 1), + 1.0, input_format, buf[2], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + compute_transition (transition[y + 1], buf, roi->width, self->edge_lock); + } + + /* set up max[] for top of image */ + for (x = 0; x < roi->width; x++) + { + max[x] = -(self->radius_y + 7); + + for (j = 1; j < self->radius_y + 1; j++) + if (transition[j][x]) + { + max[x] = j; + break; + } + } + + /* main calculation loop */ + for (y = 0; y < roi->height; y++) + { + rotate_pointers (buf, 3); + rotate_pointers (transition, self->radius_y + 1); + + if (y < roi->height - (self->radius_y + 1)) + { + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, + roi->y + y + self->radius_y + 1, + roi->width, 1), + 1.0, input_format, buf[2], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock); + } + else + { + if (self->edge_lock) + { + memcpy (transition[self->radius_y], transition[self->radius_y - 1], roi->width * sizeof (gfloat)); + } + else + { + /* No edge lock, set everything 'below canvas' as seen + * from the algorithm as unselected. + */ + memset (buf[2], 0, roi->width * sizeof (gfloat)); + compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock); + } + } + + /* update max array */ + for (x = 0; x < roi->width; x++) + { + if (max[x] < 1) + { + if (max[x] <= -self->radius_y) + { + if (transition[self->radius_y][x]) + max[x] = self->radius_y; + else + max[x]--; + } + else + { + if (transition[-max[x]][x]) + max[x] = -max[x]; + else if (transition[-max[x] + 1][x]) + max[x] = -max[x] + 1; + else + max[x]--; + } + } + else + { + max[x]--; + } + + if (max[x] < -self->radius_y - 1) + max[x] = -self->radius_y - 1; + } + + last_index = 1; + + /* render scan line */ + for (x = 0 ; x < roi->width; x++) + { + gfloat last_max; + + last_index--; + + if (last_index >= 0) + { + last_max = 0.0; + + for (i = self->radius_x; i >= 0; i--) + if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y && + density[i][max[x+i]] > last_max) + { + last_max = density[i][max[x + i]]; + last_index = i; + } + + out[x] = last_max; + } + else + { + last_max = 0.0; + + for (i = self->radius_x; i >= -self->radius_x; i--) + if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y && + density[i][max[x + i]] > last_max) + { + last_max = density[i][max[x + i]]; + last_index = i; + } + + out[x] = last_max; + } + + if (last_max <= 0.0) + { + for (i = x + 1; i < roi->width; i++) + { + if (max[i] >= -self->radius_y) + break; + } + + if (i - x > self->radius_x) + { + for (; x < i - self->radius_x; x++) + out[x] = 0; + + x--; + } + + last_index = self->radius_x; + } + } + + gegl_buffer_set (output, + GEGL_RECTANGLE (roi->x, roi->y + y, + roi->width, 1), + 0, output_format, out, + GEGL_AUTO_ROWSTRIDE); + } + + g_free (out); + + for (i = 0; i < 3; i++) + g_free (buf[i]); + + max -= self->radius_x; + g_free (max); + + for (i = 0; i < self->radius_y + 1; i++) + { + transition[i] -= self->radius_x; + g_free (transition[i]); + } + + g_free (transition); + + for (i = 0; i < self->radius_x + 1 ; i++) + { + density[i] -= self->radius_y; + g_free (density[i]); + } + + density -= self->radius_x; + g_free (density); + + return TRUE; +} diff --git a/app/operations/gimpoperationborder.h b/app/operations/gimpoperationborder.h new file mode 100644 index 0000000..83eb9fa --- /dev/null +++ b/app/operations/gimpoperationborder.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationborder.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_BORDER_H__ +#define __GIMP_OPERATION_BORDER_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_BORDER (gimp_operation_border_get_type ()) +#define GIMP_OPERATION_BORDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorder)) +#define GIMP_OPERATION_BORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass)) +#define GIMP_IS_OPERATION_BORDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BORDER)) +#define GIMP_IS_OPERATION_BORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BORDER)) +#define GIMP_OPERATION_BORDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass)) + + +typedef struct _GimpOperationBorder GimpOperationBorder; +typedef struct _GimpOperationBorderClass GimpOperationBorderClass; + +struct _GimpOperationBorder +{ + GeglOperationFilter parent_instance; + + gint radius_x; + gint radius_y; + gboolean feather; + gboolean edge_lock; +}; + +struct _GimpOperationBorderClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_border_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_BORDER_H__ */ diff --git a/app/operations/gimpoperationbrightnesscontrast.c b/app/operations/gimpoperationbrightnesscontrast.c new file mode 100644 index 0000000..1dd9186 --- /dev/null +++ b/app/operations/gimpoperationbrightnesscontrast.c @@ -0,0 +1,140 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbrightnesscontrast.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpbrightnesscontrastconfig.h" +#include "gimpoperationbrightnesscontrast.h" + +#include "gimp-intl.h" + + +static gboolean gimp_operation_brightness_contrast_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationBrightnessContrast, gimp_operation_brightness_contrast, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_brightness_contrast_parent_class + + +static void +gimp_operation_brightness_contrast_class_init (GimpOperationBrightnessContrastClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_point_filter_set_property; + object_class->get_property = gimp_operation_point_filter_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:brightness-contrast", + "categories", "color", + "description", _("Adjust brightness and contrast"), + NULL); + + point_class->process = gimp_operation_brightness_contrast_process; + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "The config object", + GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_brightness_contrast_init (GimpOperationBrightnessContrast *self) +{ +} + +static inline gfloat +gimp_operation_brightness_contrast_map (gfloat value, + gdouble brightness, + gdouble slant) +{ + /* apply brightness */ + if (brightness < 0.0) + value = value * (1.0 + brightness); + else + value = value + ((1.0 - value) * brightness); + + value = (value - 0.5) * slant + 0.5; + + return value; +} + +static gboolean +gimp_operation_brightness_contrast_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation); + GimpBrightnessContrastConfig *config = GIMP_BRIGHTNESS_CONTRAST_CONFIG (point->config); + gfloat *src = in_buf; + gfloat *dest = out_buf; + gdouble brightness; + gdouble slant; + + if (! config) + return FALSE; + + brightness = config->brightness / 2.0; + slant = tan ((config->contrast + 1) * G_PI_4); + + while (samples--) + { + dest[RED] = gimp_operation_brightness_contrast_map (src[RED], + brightness, + slant); + dest[GREEN] = gimp_operation_brightness_contrast_map (src[GREEN], + brightness, + slant); + dest[BLUE] = gimp_operation_brightness_contrast_map (src[BLUE], + brightness, + slant); + dest[ALPHA] = src[ALPHA]; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationbrightnesscontrast.h b/app/operations/gimpoperationbrightnesscontrast.h new file mode 100644 index 0000000..d93ff20 --- /dev/null +++ b/app/operations/gimpoperationbrightnesscontrast.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbrightnesscontrast.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__ +#define __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST (gimp_operation_brightness_contrast_get_type ()) +#define GIMP_OPERATION_BRIGHTNESS_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrast)) +#define GIMP_OPERATION_BRIGHTNESS_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrastClass)) +#define GIMP_IS_OPERATION_BRIGHTNESS_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST)) +#define GIMP_IS_OPERATION_BRIGHTNESS_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST)) +#define GIMP_OPERATION_BRIGHTNESS_CONTRAST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrastClass)) + + +typedef struct _GimpOperationBrightnessContrast GimpOperationBrightnessContrast; +typedef struct _GimpOperationBrightnessContrastClass GimpOperationBrightnessContrastClass; + +struct _GimpOperationBrightnessContrast +{ + GimpOperationPointFilter parent_instance; +}; + +struct _GimpOperationBrightnessContrastClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_brightness_contrast_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__ */ diff --git a/app/operations/gimpoperationbuffersourcevalidate.c b/app/operations/gimpoperationbuffersourcevalidate.c new file mode 100644 index 0000000..d277653 --- /dev/null +++ b/app/operations/gimpoperationbuffersourcevalidate.c @@ -0,0 +1,307 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbuffersourcevalidate.c + * Copyright (C) 2017 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "operations-types.h" + +#include "gegl/gimptilehandlervalidate.h" + +#include "gimpoperationbuffersourcevalidate.h" + + +enum +{ + PROP_0, + PROP_BUFFER +}; + + +static void gimp_operation_buffer_source_validate_dispose (GObject *object); +static void gimp_operation_buffer_source_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_buffer_source_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static GeglRectangle gimp_operation_buffer_source_validate_get_bounding_box (GeglOperation *operation); +static void gimp_operation_buffer_source_validate_prepare (GeglOperation *operation); +static gboolean gimp_operation_buffer_source_validate_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level); + +static void gimp_operation_buffer_source_validate_invalidate (gpointer object, + const GeglRectangle *rect, + GimpOperationBufferSourceValidate *buffer_source_validate); + + +G_DEFINE_TYPE (GimpOperationBufferSourceValidate, gimp_operation_buffer_source_validate, + GEGL_TYPE_OPERATION_SOURCE) + +#define parent_class gimp_operation_buffer_source_validate_parent_class + + +static void +gimp_operation_buffer_source_validate_class_init (GimpOperationBufferSourceValidateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + + object_class->dispose = gimp_operation_buffer_source_validate_dispose; + object_class->set_property = gimp_operation_buffer_source_validate_set_property; + object_class->get_property = gimp_operation_buffer_source_validate_get_property; + + operation_class->get_bounding_box = gimp_operation_buffer_source_validate_get_bounding_box; + operation_class->prepare = gimp_operation_buffer_source_validate_prepare; + operation_class->process = gimp_operation_buffer_source_validate_process; + + operation_class->threaded = FALSE; + operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:buffer-source-validate", + "categories", "gimp", + "description", "GIMP Buffer-Source Validate operation", + NULL); + + g_object_class_install_property (object_class, PROP_BUFFER, + g_param_spec_object ("buffer", + "Buffer", + "Input buffer", + GEGL_TYPE_BUFFER, + G_PARAM_READWRITE)); +} + +static void +gimp_operation_buffer_source_validate_init (GimpOperationBufferSourceValidate *self) +{ +} + +static void +gimp_operation_buffer_source_validate_dispose (GObject *object) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object); + + if (buffer_source_validate->buffer) + { + GimpTileHandlerValidate *validate_handler; + + validate_handler = gimp_tile_handler_validate_get_assigned ( + buffer_source_validate->buffer); + + if (validate_handler) + { + g_signal_connect ( + validate_handler, + "invalidated", + G_CALLBACK (gimp_operation_buffer_source_validate_invalidate), + buffer_source_validate); + } + + g_signal_handlers_disconnect_by_func ( + buffer_source_validate->buffer, + gimp_operation_buffer_source_validate_invalidate, + buffer_source_validate); + + g_clear_object (&buffer_source_validate->buffer); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_operation_buffer_source_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object); + + switch (property_id) + { + case PROP_BUFFER: + g_value_set_object (value, buffer_source_validate->buffer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_buffer_source_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object); + + switch (property_id) + { + case PROP_BUFFER: + { + if (buffer_source_validate->buffer) + { + GimpTileHandlerValidate *validate_handler; + + validate_handler = gimp_tile_handler_validate_get_assigned ( + buffer_source_validate->buffer); + + gimp_operation_buffer_source_validate_invalidate ( + buffer_source_validate->buffer, + gegl_buffer_get_extent (buffer_source_validate->buffer), + buffer_source_validate); + + g_signal_handlers_disconnect_by_func ( + buffer_source_validate->buffer, + gimp_operation_buffer_source_validate_invalidate, + buffer_source_validate); + + if (validate_handler) + { + g_signal_handlers_disconnect_by_func ( + validate_handler, + gimp_operation_buffer_source_validate_invalidate, + buffer_source_validate); + } + + g_clear_object (&buffer_source_validate->buffer); + } + + buffer_source_validate->buffer = g_value_dup_object (value); + + if (buffer_source_validate->buffer) + { + GimpTileHandlerValidate *validate_handler; + + validate_handler = gimp_tile_handler_validate_get_assigned ( + buffer_source_validate->buffer); + + if (validate_handler) + { + g_signal_connect ( + validate_handler, + "invalidated", + G_CALLBACK (gimp_operation_buffer_source_validate_invalidate), + buffer_source_validate); + } + + gegl_buffer_signal_connect ( + buffer_source_validate->buffer, + "changed", + G_CALLBACK (gimp_operation_buffer_source_validate_invalidate), + buffer_source_validate); + + gimp_operation_buffer_source_validate_invalidate ( + buffer_source_validate->buffer, + gegl_buffer_get_extent (buffer_source_validate->buffer), + buffer_source_validate); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GeglRectangle +gimp_operation_buffer_source_validate_get_bounding_box (GeglOperation *operation) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation); + + GeglRectangle result = {}; + + if (buffer_source_validate->buffer) + result = *gegl_buffer_get_extent (buffer_source_validate->buffer); + + return result; +} + +static void +gimp_operation_buffer_source_validate_prepare (GeglOperation *operation) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation); + const Babl *format = NULL; + + if (buffer_source_validate->buffer) + format = gegl_buffer_get_format (buffer_source_validate->buffer); + + gegl_operation_set_format (operation, "output", format); +} + +static gboolean +gimp_operation_buffer_source_validate_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level) +{ + GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation); + GeglBuffer *buffer = buffer_source_validate->buffer; + + if (buffer) + { + GimpTileHandlerValidate *validate_handler; + + validate_handler = gimp_tile_handler_validate_get_assigned (buffer); + + if (validate_handler) + { + GeglRectangle rect; + + /* align the rectangle to the tile grid */ + gegl_rectangle_align_to_buffer ( + &rect, result, buffer_source_validate->buffer, + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + gimp_tile_handler_validate_validate (validate_handler, + buffer_source_validate->buffer, + &rect, + TRUE, FALSE); + } + + gegl_operation_context_set_object (context, "output", G_OBJECT (buffer)); + + gegl_object_set_has_forked (G_OBJECT (buffer)); + } + + return TRUE; +} + +static void +gimp_operation_buffer_source_validate_invalidate (gpointer object, + const GeglRectangle *rect, + GimpOperationBufferSourceValidate *buffer_source_validate) +{ + gegl_operation_invalidate (GEGL_OPERATION (buffer_source_validate), + rect, FALSE); +} diff --git a/app/operations/gimpoperationbuffersourcevalidate.h b/app/operations/gimpoperationbuffersourcevalidate.h new file mode 100644 index 0000000..c47c333 --- /dev/null +++ b/app/operations/gimpoperationbuffersourcevalidate.h @@ -0,0 +1,52 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbuffersourcevalidate.h + * Copyright (C) 2017 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 . + */ + +#ifndef __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__ +#define __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__ + + +#define GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE (gimp_operation_buffer_source_validate_get_type ()) +#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidate)) +#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidateClass)) +#define GIMP_IS_OPERATION_BUFFER_SOURCE_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE)) +#define GIMP_IS_OPERATION_BUFFER_SOURCE_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE)) +#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidateClass)) + + +typedef struct _GimpOperationBufferSourceValidate GimpOperationBufferSourceValidate; +typedef struct _GimpOperationBufferSourceValidateClass GimpOperationBufferSourceValidateClass; + +struct _GimpOperationBufferSourceValidate +{ + GeglOperationSource parent_instance; + + GeglBuffer *buffer; +}; + +struct _GimpOperationBufferSourceValidateClass +{ + GeglOperationSourceClass parent_class; +}; + + +GType gimp_operation_buffer_source_validate_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__ */ diff --git a/app/operations/gimpoperationcagecoefcalc.c b/app/operations/gimpoperationcagecoefcalc.c new file mode 100644 index 0000000..9c78054 --- /dev/null +++ b/app/operations/gimpoperationcagecoefcalc.c @@ -0,0 +1,294 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpoperationcagecoefcalc.c + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationcagecoefcalc.h" +#include "gimpcageconfig.h" + +#include "gimp-intl.h" + + +static void gimp_operation_cage_coef_calc_finalize (GObject *object); +static void gimp_operation_cage_coef_calc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_cage_coef_calc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_cage_coef_calc_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation *operation); +static gboolean gimp_operation_cage_coef_calc_process (GeglOperation *operation, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationCageCoefCalc, gimp_operation_cage_coef_calc, + GEGL_TYPE_OPERATION_SOURCE) + +#define parent_class gimp_operation_cage_coef_calc_parent_class + + +static void +gimp_operation_cage_coef_calc_class_init (GimpOperationCageCoefCalcClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationSourceClass *source_class = GEGL_OPERATION_SOURCE_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:cage-coef-calc", + "categories", "transform", + "description", _("Compute a set of coefficient buffer for the GIMP cage tool"), + NULL); + + operation_class->prepare = gimp_operation_cage_coef_calc_prepare; + operation_class->get_bounding_box = gimp_operation_cage_coef_calc_get_bounding_box; + operation_class->cache_policy = GEGL_CACHE_POLICY_ALWAYS; + operation_class->get_cached_region = NULL; + + source_class->process = gimp_operation_cage_coef_calc_process; + + object_class->get_property = gimp_operation_cage_coef_calc_get_property; + object_class->set_property = gimp_operation_cage_coef_calc_set_property; + object_class->finalize = gimp_operation_cage_coef_calc_finalize; + + g_object_class_install_property (object_class, + GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "A GimpCageConfig object, that define the transformation", + GIMP_TYPE_CAGE_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_cage_coef_calc_init (GimpOperationCageCoefCalc *self) +{ +} + +static void +gimp_operation_cage_coef_calc_finalize (GObject *object) +{ + GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object); + + g_clear_object (&self->config); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_cage_coef_calc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object); + + switch (property_id) + { + case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG: + g_value_set_object (value, self->config); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_cage_coef_calc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object); + + switch (property_id) + { + case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG: + if (self->config) + g_object_unref (self->config); + self->config = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_operation_cage_coef_calc_is_on_straight (GimpVector2 *d1, + GimpVector2 *d2, + GimpVector2 *p) +{ + GimpVector2 v1, v2; + gfloat deter; + + v1.x = p->x - d1->x; + v1.y = p->y - d1->y; + v2.x = d2->x - d1->x; + v2.y = d2->y - d1->y; + + gimp_vector2_normalize (&v1); + gimp_vector2_normalize (&v2); + + deter = v1.x * v2.y - v2.x * v1.y; + + return (deter < 0.000000001) && (deter > -0.000000001); +} + +static void +gimp_operation_cage_coef_calc_prepare (GeglOperation *operation) +{ + GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation); + GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config); + + gegl_operation_set_format (operation, + "output", + babl_format_n (babl_type ("float"), + 2 * gimp_cage_config_get_n_points (config))); +} + +static GeglRectangle +gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation *operation) +{ + GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation); + GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config); + + return gimp_cage_config_get_bounding_box (config); +} + +static gboolean +gimp_operation_cage_coef_calc_process (GeglOperation *operation, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation); + GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config); + + const Babl *format; + + GeglBufferIterator *it; + guint n_cage_vertices; + GimpCagePoint *current, *last; + + if (! config) + return FALSE; + + format = babl_format_n (babl_type ("float"), 2 * gimp_cage_config_get_n_points (config)); + + n_cage_vertices = gimp_cage_config_get_n_points (config); + + it = gegl_buffer_iterator_new (output, roi, 0, format, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + + while (gegl_buffer_iterator_next (it)) + { + /* iterate inside the roi */ + gfloat *coef = it->items[0].data; + gint n_pixels = it->length; + gint x = it->items[0].roi.x; /* initial x */ + gint y = it->items[0].roi.y; /* and y coordinates */ + gint j; + + memset (coef, 0, sizeof * coef * n_pixels * 2 * n_cage_vertices); + while(n_pixels--) + { + if (gimp_cage_config_point_inside(config, x, y)) + { + last = &(g_array_index (config->cage_points, GimpCagePoint, 0)); + + for( j = 0; j < n_cage_vertices; j++) + { + GimpVector2 v1,v2,a,b,p; + gdouble BA,SRT,L0,L1,A0,A1,A10,L10, Q,S,R, absa; + + current = &(g_array_index (config->cage_points, GimpCagePoint, (j+1) % n_cage_vertices)); + v1 = last->src_point; + v2 = current->src_point; + p.x = x; + p.y = y; + a.x = v2.x - v1.x; + a.y = v2.y - v1.y; + absa = gimp_vector2_length (&a); + + b.x = v1.x - x; + b.y = v1.y - y; + Q = a.x * a.x + a.y * a.y; + S = b.x * b.x + b.y * b.y; + R = 2.0 * (a.x * b.x + a.y * b.y); + BA = b.x * a.y - b.y * a.x; + SRT = sqrt(4.0 * S * Q - R * R); + + L0 = log(S); + L1 = log(S + Q + R); + A0 = atan2(R, SRT) / SRT; + A1 = atan2(2.0 * Q + R, SRT) / SRT; + A10 = A1 - A0; + L10 = L1 - L0; + + /* edge coef */ + coef[j + n_cage_vertices] = (-absa / (4.0 * G_PI)) * ((4.0*S-(R*R)/Q) * A10 + (R / (2.0 * Q)) * L10 + L1 - 2.0); + + if (isnan(coef[j + n_cage_vertices])) + { + coef[j + n_cage_vertices] = 0.0; + } + + /* vertice coef */ + if (!gimp_operation_cage_coef_calc_is_on_straight (&v1, &v2, &p)) + { + coef[j] += (BA / (2.0 * G_PI)) * (L10 /(2.0*Q) - A10 * (2.0 + R / Q)); + coef[(j+1)%n_cage_vertices] -= (BA / (2.0 * G_PI)) * (L10 / (2.0 * Q) - A10 * (R / Q)); + } + + last = current; + } + } + + coef += 2 * n_cage_vertices; + + /* update x and y coordinates */ + x++; + if (x >= (it->items[0].roi.x + it->items[0].roi.width)) + { + x = it->items[0].roi.x; + y++; + } + } + } + + return TRUE; +} diff --git a/app/operations/gimpoperationcagecoefcalc.h b/app/operations/gimpoperationcagecoefcalc.h new file mode 100644 index 0000000..e63be27 --- /dev/null +++ b/app/operations/gimpoperationcagecoefcalc.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpoperationcagecoefcalc.h + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_CAGE_COEF_CALC_H__ +#define __GIMP_OPERATION_CAGE_COEF_CALC_H__ + + +#include +#include + + +enum +{ + GIMP_OPERATION_CAGE_COEF_CALC_PROP_0, + GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG +}; + + +#define GIMP_TYPE_OPERATION_CAGE_COEF_CALC (gimp_operation_cage_coef_calc_get_type ()) +#define GIMP_OPERATION_CAGE_COEF_CALC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalc)) +#define GIMP_OPERATION_CAGE_COEF_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalcClass)) +#define GIMP_IS_OPERATION_CAGE_COEF_CALC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC)) +#define GIMP_IS_OPERATION_CAGE_COEF_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CAGE_COEF_CALC)) +#define GIMP_OPERATION_CAGE_COEF_CALC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalcClass)) + + +typedef struct _GimpOperationCageCoefCalc GimpOperationCageCoefCalc; +typedef struct _GimpOperationCageCoefCalcClass GimpOperationCageCoefCalcClass; + +struct _GimpOperationCageCoefCalc +{ + GeglOperationSource parent_instance; + + GimpCageConfig *config; +}; + +struct _GimpOperationCageCoefCalcClass +{ + GeglOperationSourceClass parent_class; +}; + + +GType gimp_operation_cage_coef_calc_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_CAGE_COEF_CALC_H__ */ diff --git a/app/operations/gimpoperationcagetransform.c b/app/operations/gimpoperationcagetransform.c new file mode 100644 index 0000000..d2f0450 --- /dev/null +++ b/app/operations/gimpoperationcagetransform.c @@ -0,0 +1,603 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpoperationcage.c + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationcagetransform.h" +#include "gimpcageconfig.h" + +#include "gimp-intl.h" + +enum +{ + PROP_0, + PROP_CONFIG, + PROP_FILL, +}; + + +static void gimp_operation_cage_transform_finalize (GObject *object); +static void gimp_operation_cage_transform_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_cage_transform_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_operation_cage_transform_prepare (GeglOperation *operation); +static gboolean gimp_operation_cage_transform_process (GeglOperation *operation, + GeglBuffer *in_buf, + GeglBuffer *aux_buf, + GeglBuffer *out_buf, + const GeglRectangle *roi, + gint level); +static void gimp_operation_cage_transform_interpolate_source_coords_recurs + (GimpOperationCageTransform *oct, + GeglBuffer *out_buf, + const GeglRectangle *roi, + GimpVector2 p1_s, + GimpVector2 p1_d, + GimpVector2 p2_s, + GimpVector2 p2_d, + GimpVector2 p3_s, + GimpVector2 p3_d, + gint recursion_depth, + gfloat *coords); +static GimpVector2 gimp_cage_transform_compute_destination (GimpCageConfig *config, + gfloat *coef, + GeglSampler *coef_sampler, + GimpVector2 coords); +GeglRectangle gimp_operation_cage_transform_get_cached_region (GeglOperation *operation, + const GeglRectangle *roi); +GeglRectangle gimp_operation_cage_transform_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *roi); +GeglRectangle gimp_operation_cage_transform_get_bounding_box (GeglOperation *operation); + + +G_DEFINE_TYPE (GimpOperationCageTransform, gimp_operation_cage_transform, + GEGL_TYPE_OPERATION_COMPOSER) + +#define parent_class gimp_operation_cage_transform_parent_class + + +static void +gimp_operation_cage_transform_class_init (GimpOperationCageTransformClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationComposerClass *filter_class = GEGL_OPERATION_COMPOSER_CLASS (klass); + + object_class->get_property = gimp_operation_cage_transform_get_property; + object_class->set_property = gimp_operation_cage_transform_set_property; + object_class->finalize = gimp_operation_cage_transform_finalize; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:cage-transform", + "categories", "transform", + "description", _("Convert a set of coefficient buffer to a coordinate buffer for the GIMP cage tool"), + NULL); + + operation_class->prepare = gimp_operation_cage_transform_prepare; + + operation_class->get_required_for_output = gimp_operation_cage_transform_get_required_for_output; + operation_class->get_cached_region = gimp_operation_cage_transform_get_cached_region; + operation_class->get_bounding_box = gimp_operation_cage_transform_get_bounding_box; + /* XXX Temporarily disable multi-threading on this operation because + * it is much faster when single-threaded. See bug 787663. + */ + operation_class->threaded = FALSE; + + filter_class->process = gimp_operation_cage_transform_process; + + g_object_class_install_property (object_class, PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "A GimpCageConfig object, that define the transformation", + GIMP_TYPE_CAGE_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FILL, + g_param_spec_boolean ("fill-plain-color", + _("Fill with plain color"), + _("Fill the original position of the cage with a plain color"), + FALSE, + G_PARAM_READWRITE)); +} + +static void +gimp_operation_cage_transform_init (GimpOperationCageTransform *self) +{ + self->format_coords = babl_format_n(babl_type("float"), 2); +} + +static void +gimp_operation_cage_transform_finalize (GObject *object) +{ + GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object); + + g_clear_object (&self->config); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_cage_transform_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object); + + switch (property_id) + { + case PROP_CONFIG: + g_value_set_object (value, self->config); + break; + case PROP_FILL: + g_value_set_boolean (value, self->fill_plain_color); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_cage_transform_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object); + + switch (property_id) + { + case PROP_CONFIG: + if (self->config) + g_object_unref (self->config); + self->config = g_value_dup_object (value); + break; + case PROP_FILL: + self->fill_plain_color = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_cage_transform_prepare (GeglOperation *operation) +{ + GimpOperationCageTransform *oct = GIMP_OPERATION_CAGE_TRANSFORM (operation); + GimpCageConfig *config = GIMP_CAGE_CONFIG (oct->config); + + gegl_operation_set_format (operation, "input", + babl_format_n (babl_type ("float"), + 2 * gimp_cage_config_get_n_points (config))); + gegl_operation_set_format (operation, "output", + babl_format_n (babl_type ("float"), 2)); +} + +static gboolean +gimp_operation_cage_transform_process (GeglOperation *operation, + GeglBuffer *in_buf, + GeglBuffer *aux_buf, + GeglBuffer *out_buf, + const GeglRectangle *roi, + gint level) +{ + GimpOperationCageTransform *oct = GIMP_OPERATION_CAGE_TRANSFORM (operation); + GimpCageConfig *config = GIMP_CAGE_CONFIG (oct->config); + GeglRectangle cage_bb; + gfloat *coords; + gfloat *coef; + const Babl *format_coef; + GeglSampler *coef_sampler; + GimpVector2 plain_color; + GeglBufferIterator *it; + gint x, y; + gboolean output_set; + GimpCagePoint *point; + guint n_cage_vertices; + + /* pre-fill the out buffer with no-displacement coordinate */ + it = gegl_buffer_iterator_new (out_buf, roi, 0, NULL, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + cage_bb = gimp_cage_config_get_bounding_box (config); + + point = &(g_array_index (config->cage_points, GimpCagePoint, 0)); + plain_color.x = (gint) point->src_point.x; + plain_color.y = (gint) point->src_point.y; + + n_cage_vertices = gimp_cage_config_get_n_points (config); + + while (gegl_buffer_iterator_next (it)) + { + /* iterate inside the roi */ + gint n_pixels = it->length; + gfloat *output = it->items[0].data; + + x = it->items[0].roi.x; /* initial x */ + y = it->items[0].roi.y; /* and y coordinates */ + + while (n_pixels--) + { + output_set = FALSE; + if (oct->fill_plain_color) + { + if (x > cage_bb.x && + y > cage_bb.y && + x < cage_bb.x + cage_bb.width && + y < cage_bb.y + cage_bb.height) + { + if (gimp_cage_config_point_inside (config, x, y)) + { + output[0] = plain_color.x; + output[1] = plain_color.y; + output_set = TRUE; + } + } + } + if (!output_set) + { + output[0] = x + 0.5; + output[1] = y + 0.5; + } + + output += 2; + + /* update x and y coordinates */ + x++; + if (x >= (it->items[0].roi.x + it->items[0].roi.width)) + { + x = it->items[0].roi.x; + y++; + } + } + } + + if (! aux_buf) + return TRUE; + + gegl_operation_progress (operation, 0.0, ""); + + /* pre-allocate memory outside of the loop */ + coords = g_slice_alloc (2 * sizeof (gfloat)); + coef = g_malloc (n_cage_vertices * 2 * sizeof (gfloat)); + format_coef = babl_format_n (babl_type ("float"), 2 * n_cage_vertices); + coef_sampler = gegl_buffer_sampler_new (aux_buf, + format_coef, GEGL_SAMPLER_NEAREST); + + /* compute, reverse and interpolate the transformation */ + for (y = cage_bb.y; y < cage_bb.y + cage_bb.height - 1; y++) + { + GimpVector2 p1_d, p2_d, p3_d, p4_d; + GimpVector2 p1_s, p2_s, p3_s, p4_s; + + p1_s.y = y; + p2_s.y = y+1; + p3_s.y = y+1; + p3_s.x = cage_bb.x; + p4_s.y = y; + p4_s.x = cage_bb.x; + + p3_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p3_s); + p4_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p4_s); + + for (x = cage_bb.x; x < cage_bb.x + cage_bb.width - 1; x++) + { + p1_s = p4_s; + p2_s = p3_s; + p3_s.x = x+1; + p4_s.x = x+1; + + p1_d = p4_d; + p2_d = p3_d; + p3_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p3_s); + p4_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p4_s); + + if (gimp_cage_config_point_inside (config, x, y)) + { + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + p1_s, p1_d, + p2_s, p2_d, + p3_s, p3_d, + 0, + coords); + + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + p1_s, p1_d, + p3_s, p3_d, + p4_s, p4_d, + 0, + coords); + } + } + + if ((y - cage_bb.y) % 20 == 0) + { + gdouble fraction = ((gdouble) (y - cage_bb.y) / + (gdouble) (cage_bb.height)); + + /* 0.0 and 1.0 indicate progress start/end, so avoid them */ + if (fraction > 0.0 && fraction < 1.0) + { + gegl_operation_progress (operation, fraction, ""); + } + } + } + + g_object_unref (coef_sampler); + g_free (coef); + g_slice_free1 (2 * sizeof (gfloat), coords); + + gegl_operation_progress (operation, 1.0, ""); + + return TRUE; +} + + +static void +gimp_operation_cage_transform_interpolate_source_coords_recurs (GimpOperationCageTransform *oct, + GeglBuffer *out_buf, + const GeglRectangle *roi, + GimpVector2 p1_s, + GimpVector2 p1_d, + GimpVector2 p2_s, + GimpVector2 p2_d, + GimpVector2 p3_s, + GimpVector2 p3_d, + gint recursion_depth, + gfloat *coords) +{ + gint xmin, xmax, ymin, ymax, x, y; + + /* Stop recursion if all 3 vertices of the triangle are outside the + * ROI (left/right or above/below). + */ + if (p1_d.x >= roi->x + roi->width && + p2_d.x >= roi->x + roi->width && + p3_d.x >= roi->x + roi->width) return; + if (p1_d.y >= roi->y + roi->height && + p2_d.y >= roi->y + roi->height && + p3_d.y >= roi->y + roi->height) return; + + if (p1_d.x < roi->x && + p2_d.x < roi->x && + p3_d.x < roi->x) return; + if (p1_d.y < roi->y && + p2_d.y < roi->y && + p3_d.y < roi->y) return; + + xmin = xmax = lrint (p1_d.x); + ymin = ymax = lrint (p1_d.y); + + x = lrint (p2_d.x); + xmin = MIN (x, xmin); + xmax = MAX (x, xmax); + + x = lrint (p3_d.x); + xmin = MIN (x, xmin); + xmax = MAX (x, xmax); + + y = lrint (p2_d.y); + ymin = MIN (y, ymin); + ymax = MAX (y, ymax); + + y = lrint (p3_d.y); + ymin = MIN (y, ymin); + ymax = MAX (y, ymax); + + /* test if there is no more pixel in the triangle */ + if (xmin == xmax || ymin == ymax) + return; + + /* test if the triangle is implausibly large as manifested by too deep recursion */ + if (recursion_depth > 5) + return; + + /* test if the triangle is small enough. + * + * if yes, we compute the coefficient of the barycenter for the + * pixel (x,y) and see if a pixel is inside (ie the 3 coef have the + * same sign). + */ + if (xmax - xmin == 1 && ymax - ymin == 1) + { + gdouble a, b, c, denom, x, y; + + x = (gdouble) xmin + 0.5; + y = (gdouble) ymin + 0.5; + + denom = (p2_d.x - p1_d.x) * p3_d.y + (p1_d.x - p3_d.x) * p2_d.y + (p3_d.x - p2_d.x) * p1_d.y; + a = ((p2_d.x - x) * p3_d.y + (x - p3_d.x) * p2_d.y + (p3_d.x - p2_d.x) * y) / denom; + b = - ((p1_d.x - x) * p3_d.y + (x - p3_d.x) * p1_d.y + (p3_d.x - p1_d.x) * y) / denom; + c = 1.0 - a - b; + + /* if a pixel is inside, we compute its source coordinate and + * set it in the output buffer + */ + if ((a > 0 && b > 0 && c > 0) || (a < 0 && b < 0 && c < 0)) + { + GeglRectangle rect = { 0, 0, 1, 1 }; + gfloat coords[2]; + + rect.x = xmin; + rect.y = ymin; + + coords[0] = (a * p1_s.x + b * p2_s.x + c * p3_s.x); + coords[1] = (a * p1_s.y + b * p2_s.y + c * p3_s.y); + + gegl_buffer_set (out_buf, + &rect, + 0, + oct->format_coords, + coords, + GEGL_AUTO_ROWSTRIDE); + } + + return; + } + else + { + /* we cut the triangle in 4 sub-triangle and treat it recursively */ + /* + * /\ + * /__\ + * /\ /\ + * /__\/__\ + * + */ + + GimpVector2 pm1_d, pm2_d, pm3_d; + GimpVector2 pm1_s, pm2_s, pm3_s; + gint next_depth = recursion_depth + 1; + + pm1_d.x = (p1_d.x + p2_d.x) / 2.0; + pm1_d.y = (p1_d.y + p2_d.y) / 2.0; + + pm2_d.x = (p2_d.x + p3_d.x) / 2.0; + pm2_d.y = (p2_d.y + p3_d.y) / 2.0; + + pm3_d.x = (p3_d.x + p1_d.x) / 2.0; + pm3_d.y = (p3_d.y + p1_d.y) / 2.0; + + pm1_s.x = (p1_s.x + p2_s.x) / 2.0; + pm1_s.y = (p1_s.y + p2_s.y) / 2.0; + + pm2_s.x = (p2_s.x + p3_s.x) / 2.0; + pm2_s.y = (p2_s.y + p3_s.y) / 2.0; + + pm3_s.x = (p3_s.x + p1_s.x) / 2.0; + pm3_s.y = (p3_s.y + p1_s.y) / 2.0; + + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + p1_s, p1_d, + pm1_s, pm1_d, + pm3_s, pm3_d, + next_depth, + coords); + + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + pm1_s, pm1_d, + p2_s, p2_d, + pm2_s, pm2_d, + next_depth, + coords); + + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + pm1_s, pm1_d, + pm2_s, pm2_d, + pm3_s, pm3_d, + next_depth, + coords); + + gimp_operation_cage_transform_interpolate_source_coords_recurs (oct, + out_buf, + roi, + pm3_s, pm3_d, + pm2_s, pm2_d, + p3_s, p3_d, + next_depth, + coords); + } +} + +static GimpVector2 +gimp_cage_transform_compute_destination (GimpCageConfig *config, + gfloat *coef, + GeglSampler *coef_sampler, + GimpVector2 coords) +{ + GimpVector2 result = {0, 0}; + gint n_cage_vertices = gimp_cage_config_get_n_points (config); + gint i; + GimpCagePoint *point; + + gegl_sampler_get (coef_sampler, + coords.x, coords.y, NULL, coef, GEGL_ABYSS_NONE); + + for (i = 0; i < n_cage_vertices; i++) + { + point = &g_array_index (config->cage_points, GimpCagePoint, i); + + result.x += coef[i] * point->dest_point.x; + result.y += coef[i] * point->dest_point.y; + + result.x += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.x; + result.y += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.y; + } + + return result; +} + +GeglRectangle +gimp_operation_cage_transform_get_cached_region (GeglOperation *operation, + const GeglRectangle *roi) +{ + GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, + "input"); + + return result; +} + +GeglRectangle +gimp_operation_cage_transform_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *roi) +{ + GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, + "input"); + + return result; +} + +GeglRectangle +gimp_operation_cage_transform_get_bounding_box (GeglOperation *operation) +{ + GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, + "input"); + + return result; +} diff --git a/app/operations/gimpoperationcagetransform.h b/app/operations/gimpoperationcagetransform.h new file mode 100644 index 0000000..8ee9286 --- /dev/null +++ b/app/operations/gimpoperationcagetransform.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * + * gimpoperationcagetransform.h + * Copyright (C) 2010 Michael Muré + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_CAGE_TRANSFORM_H__ +#define __GIMP_OPERATION_CAGE_TRANSFORM_H__ + + +#include +#include + + +#define GIMP_TYPE_OPERATION_CAGE_TRANSFORM (gimp_operation_cage_transform_get_type ()) +#define GIMP_OPERATION_CAGE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransform)) +#define GIMP_OPERATION_CAGE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransformClass)) +#define GIMP_IS_OPERATION_CAGE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM)) +#define GIMP_IS_OPERATION_CAGE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CAGE_TRANSFORM)) +#define GIMP_OPERATION_CAGE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransformClass)) + + +typedef struct _GimpOperationCageTransform GimpOperationCageTransform; +typedef struct _GimpOperationCageTransformClass GimpOperationCageTransformClass; + +struct _GimpOperationCageTransform +{ + GeglOperationComposer parent_instance; + + GimpCageConfig *config; + gboolean fill_plain_color; + + const Babl *format_coords; +}; + +struct _GimpOperationCageTransformClass +{ + GeglOperationComposerClass parent_class; +}; + + +GType gimp_operation_cage_transform_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_CAGE_TRANSFORM_H__ */ diff --git a/app/operations/gimpoperationcolorbalance.c b/app/operations/gimpoperationcolorbalance.c new file mode 100644 index 0000000..9988158 --- /dev/null +++ b/app/operations/gimpoperationcolorbalance.c @@ -0,0 +1,198 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcolorbalance.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpcolorbalanceconfig.h" +#include "gimpoperationcolorbalance.h" + +#include "gimp-intl.h" + + +static gboolean gimp_operation_color_balance_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationColorBalance, gimp_operation_color_balance, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_color_balance_parent_class + + +static void +gimp_operation_color_balance_class_init (GimpOperationColorBalanceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_point_filter_set_property; + object_class->get_property = gimp_operation_point_filter_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:color-balance", + "categories", "color", + "description", _("Adjust color distribution"), + NULL); + + point_class->process = gimp_operation_color_balance_process; + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "The config object", + GIMP_TYPE_COLOR_BALANCE_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_color_balance_init (GimpOperationColorBalance *self) +{ +} + +static inline gfloat +gimp_operation_color_balance_map (gfloat value, + gdouble lightness, + gdouble shadows, + gdouble midtones, + gdouble highlights) +{ + /* Apply masks to the corrections for shadows, midtones and + * highlights so that each correction affects only one range. + * Those masks look like this: + * ‾\___ + * _/‾\_ + * ___/‾ + * with ramps of width a at x = b and x = 1 - b. + * + * The sum of these masks equals 1 for x in 0..1, so applying the + * same correction in the shadows and in the midtones is equivalent + * to applying this correction on a virtual shadows_and_midtones + * range. + */ + static const gdouble a = 0.25, b = 0.333, scale = 0.7; + + shadows *= CLAMP ((lightness - b) / -a + 0.5, 0, 1) * scale; + midtones *= CLAMP ((lightness - b) / a + 0.5, 0, 1) * + CLAMP ((lightness + b - 1) / -a + 0.5, 0, 1) * scale; + highlights *= CLAMP ((lightness + b - 1) / a + 0.5, 0, 1) * scale; + + value += shadows; + value += midtones; + value += highlights; + value = CLAMP (value, 0.0, 1.0); + + return value; +} + +static gboolean +gimp_operation_color_balance_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation); + GimpColorBalanceConfig *config = GIMP_COLOR_BALANCE_CONFIG (point->config); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + if (! config) + return FALSE; + + while (samples--) + { + gfloat r = src[RED]; + gfloat g = src[GREEN]; + gfloat b = src[BLUE]; + gfloat r_n; + gfloat g_n; + gfloat b_n; + + GimpRGB rgb = { r, g, b}; + GimpHSL hsl; + + gimp_rgb_to_hsl (&rgb, &hsl); + + r_n = gimp_operation_color_balance_map (r, hsl.l, + config->cyan_red[GIMP_TRANSFER_SHADOWS], + config->cyan_red[GIMP_TRANSFER_MIDTONES], + config->cyan_red[GIMP_TRANSFER_HIGHLIGHTS]); + + g_n = gimp_operation_color_balance_map (g, hsl.l, + config->magenta_green[GIMP_TRANSFER_SHADOWS], + config->magenta_green[GIMP_TRANSFER_MIDTONES], + config->magenta_green[GIMP_TRANSFER_HIGHLIGHTS]); + + b_n = gimp_operation_color_balance_map (b, hsl.l, + config->yellow_blue[GIMP_TRANSFER_SHADOWS], + config->yellow_blue[GIMP_TRANSFER_MIDTONES], + config->yellow_blue[GIMP_TRANSFER_HIGHLIGHTS]); + + if (config->preserve_luminosity) + { + GimpHSL hsl2; + + rgb.r = r_n; + rgb.g = g_n; + rgb.b = b_n; + gimp_rgb_to_hsl (&rgb, &hsl); + + rgb.r = r; + rgb.g = g; + rgb.b = b; + gimp_rgb_to_hsl (&rgb, &hsl2); + + hsl.l = hsl2.l; + + gimp_hsl_to_rgb (&hsl, &rgb); + + r_n = rgb.r; + g_n = rgb.g; + b_n = rgb.b; + } + + dest[RED] = r_n; + dest[GREEN] = g_n; + dest[BLUE] = b_n; + dest[ALPHA] = src[ALPHA]; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationcolorbalance.h b/app/operations/gimpoperationcolorbalance.h new file mode 100644 index 0000000..a6ba386 --- /dev/null +++ b/app/operations/gimpoperationcolorbalance.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcolorbalance.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_COLOR_BALANCE_H__ +#define __GIMP_OPERATION_COLOR_BALANCE_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_COLOR_BALANCE (gimp_operation_color_balance_get_type ()) +#define GIMP_OPERATION_COLOR_BALANCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalance)) +#define GIMP_OPERATION_COLOR_BALANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalanceClass)) +#define GIMP_IS_OPERATION_COLOR_BALANCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE)) +#define GIMP_IS_OPERATION_COLOR_BALANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COLOR_BALANCE)) +#define GIMP_OPERATION_COLOR_BALANCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalanceClass)) + + +typedef struct _GimpOperationColorBalance GimpOperationColorBalance; +typedef struct _GimpOperationColorBalanceClass GimpOperationColorBalanceClass; + +struct _GimpOperationColorBalance +{ + GimpOperationPointFilter parent_instance; +}; + +struct _GimpOperationColorBalanceClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_color_balance_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_COLOR_BALANCE_H__ */ diff --git a/app/operations/gimpoperationcolorize.c b/app/operations/gimpoperationcolorize.c new file mode 100644 index 0000000..fd64840 --- /dev/null +++ b/app/operations/gimpoperationcolorize.c @@ -0,0 +1,274 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcolorize.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimpoperationcolorize.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_HUE, + PROP_SATURATION, + PROP_LIGHTNESS, + PROP_COLOR +}; + + +static void gimp_operation_colorize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_colorize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_operation_colorize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationColorize, gimp_operation_colorize, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_colorize_parent_class + + +static void +gimp_operation_colorize_class_init (GimpOperationColorizeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + GimpHSL hsl; + GimpRGB rgb; + + object_class->set_property = gimp_operation_colorize_set_property; + object_class->get_property = gimp_operation_colorize_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:colorize", + "categories", "color", + "description", _("Colorize the image"), + NULL); + + point_class->process = gimp_operation_colorize_process; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HUE, + "hue", + _("Hue"), + _("Hue"), + 0.0, 1.0, 0.5, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SATURATION, + "saturation", + _("Saturation"), + _("Saturation"), + 0.0, 1.0, 0.5, 0); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LIGHTNESS, + "lightness", + _("Lightness"), + _("Lightness"), + -1.0, 1.0, 0.0, 0); + + gimp_hsl_set (&hsl, 0.5, 0.5, 0.5); + gimp_hsl_set_alpha (&hsl, 1.0); + gimp_hsl_to_rgb (&hsl, &rgb); + + g_object_class_install_property (object_class, PROP_COLOR, + gimp_param_spec_rgb ("color", + _("Color"), + _("Color"), + FALSE, &rgb, + G_PARAM_READWRITE)); +} + +static void +gimp_operation_colorize_init (GimpOperationColorize *self) +{ +} + +static void +gimp_operation_colorize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object); + + switch (property_id) + { + case PROP_HUE: + g_value_set_double (value, self->hue); + break; + + case PROP_SATURATION: + g_value_set_double (value, self->saturation); + break; + + case PROP_LIGHTNESS: + g_value_set_double (value, self->lightness); + break; + + case PROP_COLOR: + { + GimpHSL hsl; + GimpRGB rgb; + + gimp_hsl_set (&hsl, + self->hue, + self->saturation, + (self->lightness + 1.0) / 2.0); + gimp_hsl_set_alpha (&hsl, 1.0); + gimp_hsl_to_rgb (&hsl, &rgb); + gimp_value_set_rgb (value, &rgb); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_colorize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object); + + switch (property_id) + { + case PROP_HUE: + self->hue = g_value_get_double (value); + g_object_notify (object, "color"); + break; + + case PROP_SATURATION: + self->saturation = g_value_get_double (value); + g_object_notify (object, "color"); + break; + + case PROP_LIGHTNESS: + self->lightness = g_value_get_double (value); + g_object_notify (object, "color"); + break; + + case PROP_COLOR: + { + GimpRGB rgb; + GimpHSL hsl; + + gimp_value_get_rgb (value, &rgb); + gimp_rgb_to_hsl (&rgb, &hsl); + + if (hsl.h == -1) + hsl.h = self->hue; + + if (hsl.l == 0.0 || hsl.l == 1.0) + hsl.s = self->saturation; + + g_object_set (self, + "hue", hsl.h, + "saturation", hsl.s, + "lightness", hsl.l * 2.0 - 1.0, + NULL); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_operation_colorize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + GimpHSL hsl; + + hsl.h = colorize->hue; + hsl.s = colorize->saturation; + + while (samples--) + { + GimpRGB rgb; + gfloat lum = GIMP_RGB_LUMINANCE (src[RED], + src[GREEN], + src[BLUE]); + + if (colorize->lightness > 0) + { + lum = lum * (1.0 - colorize->lightness); + + lum += 1.0 - (1.0 - colorize->lightness); + } + else if (colorize->lightness < 0) + { + lum = lum * (colorize->lightness + 1.0); + } + + hsl.l = lum; + + gimp_hsl_to_rgb (&hsl, &rgb); + + /* the code in base/colorize.c would multiply r,b,g with lum, + * but this is a bug since it should multiply with 255. We + * don't repeat this bug here (this is the reason why the gegl + * colorize is brighter than the legacy one). + */ + dest[RED] = rgb.r; /* * lum */ + dest[GREEN] = rgb.g; /* * lum */ + dest[BLUE] = rgb.b; /* * lum */ + dest[ALPHA] = src[ALPHA]; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationcolorize.h b/app/operations/gimpoperationcolorize.h new file mode 100644 index 0000000..cfb5545 --- /dev/null +++ b/app/operations/gimpoperationcolorize.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcolorize.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_COLORIZE_H__ +#define __GIMP_OPERATION_COLORIZE_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_COLORIZE (gimp_operation_colorize_get_type ()) +#define GIMP_OPERATION_COLORIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorize)) +#define GIMP_OPERATION_COLORIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorizeClass)) +#define GIMP_IS_OPERATION_COLORIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COLORIZE)) +#define GIMP_IS_OPERATION_COLORIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COLORIZE)) +#define GIMP_OPERATION_COLORIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorizeClass)) + + +typedef struct _GimpOperationColorize GimpOperationColorize; +typedef struct _GimpOperationColorizeClass GimpOperationColorizeClass; + +struct _GimpOperationColorize +{ + GimpOperationPointFilter parent_instance; + + gdouble hue; + gdouble saturation; + gdouble lightness; +}; + +struct _GimpOperationColorizeClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_colorize_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_COLORIZE_H__ */ diff --git a/app/operations/gimpoperationcomposecrop.c b/app/operations/gimpoperationcomposecrop.c new file mode 100644 index 0000000..25e6247 --- /dev/null +++ b/app/operations/gimpoperationcomposecrop.c @@ -0,0 +1,329 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcomposecrop.c + * Copyright (C) 2012 Michael Natterer + * Copyright (C) 2016 Massimo Valentini + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "gimpoperationcomposecrop.h" + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT +}; + + +static void gimp_operation_compose_crop_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_compose_crop_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_compose_crop_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_compose_crop_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *output_roi); +static gboolean gimp_operation_compose_crop_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *roi, + gint level); + +static gboolean gimp_operation_compose_crop_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationComposeCrop, gimp_operation_compose_crop, + GEGL_TYPE_OPERATION_POINT_COMPOSER) + +#define parent_class gimp_operation_compose_crop_parent_class + + +static void +gimp_operation_compose_crop_class_init (GimpOperationComposeCropClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointComposerClass *point_class = GEGL_OPERATION_POINT_COMPOSER_CLASS (klass); + + object_class->set_property = gimp_operation_compose_crop_set_property; + object_class->get_property = gimp_operation_compose_crop_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:compose-crop", + "categories", "gimp", + "description", "Selectively pick components from src or aux", + NULL); + + operation_class->prepare = gimp_operation_compose_crop_prepare; + operation_class->get_invalidated_by_change = gimp_operation_compose_crop_get_required_for_output; + operation_class->get_required_for_output = gimp_operation_compose_crop_get_required_for_output; + operation_class->process = gimp_operation_compose_crop_parent_process; + + point_class->process = gimp_operation_compose_crop_process; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_int ("x", + "x", + "x", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_int ("y", + "y", + "y", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", + "width", + "width", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", + "height", + "height", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_compose_crop_init (GimpOperationComposeCrop *self) +{ +} + +static void +gimp_operation_compose_crop_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (object); + + switch (property_id) + { + case PROP_X: + g_value_set_int (value, self->rect.x); + break; + case PROP_Y: + g_value_set_int (value, self->rect.y); + break; + case PROP_WIDTH: + g_value_set_int (value, self->rect.width); + break; + case PROP_HEIGHT: + g_value_set_int (value, self->rect.height); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_compose_crop_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (object); + + switch (property_id) + { + case PROP_X: + self->rect.x = g_value_get_int (value); + break; + case PROP_Y: + self->rect.y = g_value_get_int (value); + break; + case PROP_WIDTH: + self->rect.width = g_value_get_int (value); + break; + case PROP_HEIGHT: + self->rect.height = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_compose_crop_prepare (GeglOperation *operation) +{ + const Babl *input_format = gegl_operation_get_source_format (operation, "input"); + const Babl *aux_format = gegl_operation_get_source_format (operation, "aux"); + const Babl *format; + + if (input_format) + { + if (input_format == aux_format) + { + format = input_format; + } + else + { + const Babl *model = babl_format_get_model (input_format); + + if (model == babl_model ("R'G'B'A")) + format = babl_format_with_space ("R'G'B'A float", input_format); + else + format = babl_format_with_space ("RGBA float", input_format); + } + } + else + { + format = babl_format_with_space ("RGBA float", input_format); + } + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "aux", format); + gegl_operation_set_format (operation, "output", format); +} + +static GeglRectangle +gimp_operation_compose_crop_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *output_roi) +{ + GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation); + GeglRectangle result; + + if (! strcmp (input_pad, "input")) + gegl_rectangle_intersect (&result, output_roi, &self->rect); + else if (! strcmp (input_pad, "aux")) + gegl_rectangle_subtract_bounding_box (&result, output_roi, &self->rect); + else + g_return_val_if_reached (*output_roi); + + return result; +} + +static gboolean +gimp_operation_compose_crop_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *roi, + gint level) +{ + GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation); + + if (gegl_rectangle_contains (&self->rect, roi)) + { + GObject *input; + + input = gegl_operation_context_get_object (context, "input"); + gegl_operation_context_set_object (context, "output", input); + + return TRUE; + } + else if (! gegl_rectangle_intersect (NULL, &self->rect, roi)) + { + GObject *aux; + + aux = gegl_operation_context_get_object (context, "aux"); + gegl_operation_context_set_object (context, "output", aux); + + return TRUE; + } + + return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, + output_pad, roi, level); +} + +static gboolean +gimp_operation_compose_crop_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation); + const Babl *format = gegl_operation_get_format (operation, "output"); + gint bpp = babl_format_get_bytes_per_pixel (format); + const guchar *in = in_buf; + const guchar *aux = aux_buf; + guchar *out = out_buf; + gint x0, x1; + gint y0, y1; + gint y; + +#define COPY(src, n) \ + do \ + { \ + gint size = (n) * bpp; \ + \ + if (src) \ + memcpy (out, (src), size); \ + else \ + memset (out, 0, size); \ + \ + in += size; \ + if (aux) aux += size; \ + out += size; \ + } \ + while (FALSE) + + x0 = CLAMP (self->rect.x, roi->x, roi->x + roi->width); + x1 = CLAMP (self->rect.x + self->rect.width, roi->x, roi->x + roi->width); + + y0 = CLAMP (self->rect.y, roi->y, roi->y + roi->height); + y1 = CLAMP (self->rect.y + self->rect.height, roi->y, roi->y + roi->height); + + COPY (aux, (y0 - roi->y) * roi->width); + + for (y = y0; y < y1; y++) + { + COPY (aux, x0 - roi->x); + COPY (in, x1 - x0); + COPY (aux, roi->x + roi->width - x1); + } + + COPY (aux, (roi->y + roi->height - y1) * roi->width); + +#undef COPY + + return TRUE; +} diff --git a/app/operations/gimpoperationcomposecrop.h b/app/operations/gimpoperationcomposecrop.h new file mode 100644 index 0000000..83b7613 --- /dev/null +++ b/app/operations/gimpoperationcomposecrop.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcomposecrop.h + * Copyright (C) 2012 Michael Natterer + * Copyright (C) 2016 Massimo Valentini + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_COMPOSE_CROP_H__ +#define __GIMP_OPERATION_COMPOSE_CROP_H__ + +#include + + +#define GIMP_TYPE_OPERATION_COMPOSE_CROP (gimp_operation_compose_crop_get_type ()) +#define GIMP_OPERATION_COMPOSE_CROP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCrop)) +#define GIMP_OPERATION_COMPOSE_CROP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCropClass)) +#define GIMP_IS_OPERATION_COMPOSE_CROP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP)) +#define GIMP_IS_OPERATION_COMPOSE_CROP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COMPOSE_CROP)) +#define GIMP_OPERATION_COMPOSE_CROP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCropClass)) + + +typedef struct _GimpOperationComposeCrop GimpOperationComposeCrop; +typedef struct _GimpOperationComposeCropClass GimpOperationComposeCropClass; + +struct _GimpOperationComposeCrop +{ + GeglOperationPointComposer parent_instance; + + GeglRectangle rect; +}; + +struct _GimpOperationComposeCropClass +{ + GeglOperationPointComposerClass parent_class; +}; + + +GType gimp_operation_compose_crop_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_COMPOSE_CROP_H__ */ diff --git a/app/operations/gimpoperationcurves.c b/app/operations/gimpoperationcurves.c new file mode 100644 index 0000000..e83b142 --- /dev/null +++ b/app/operations/gimpoperationcurves.c @@ -0,0 +1,118 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcurves.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "core/gimpcurve.h" +#include "core/gimpcurve-map.h" + +#include "gimpcurvesconfig.h" +#include "gimpoperationcurves.h" + +#include "gimp-intl.h" + + +static gboolean gimp_operation_curves_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationCurves, gimp_operation_curves, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_curves_parent_class + + +static void +gimp_operation_curves_class_init (GimpOperationCurvesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_point_filter_set_property; + object_class->get_property = gimp_operation_point_filter_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:curves", + "categories", "color", + "description", _("Adjust color curves"), + NULL); + + point_class->process = gimp_operation_curves_process; + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_LINEAR, + g_param_spec_boolean ("linear", + "Linear", + "Whether to operate on linear RGB", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "The config object", + GIMP_TYPE_CURVES_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_curves_init (GimpOperationCurves *self) +{ +} + +static gboolean +gimp_operation_curves_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation); + GimpCurvesConfig *config = GIMP_CURVES_CONFIG (point->config); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + if (! config) + return FALSE; + + gimp_curve_map_pixels (config->curve[0], + config->curve[1], + config->curve[2], + config->curve[3], + config->curve[4], src, dest, samples); + + return TRUE; +} diff --git a/app/operations/gimpoperationcurves.h b/app/operations/gimpoperationcurves.h new file mode 100644 index 0000000..ab097b2 --- /dev/null +++ b/app/operations/gimpoperationcurves.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcurves.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_CURVES_H__ +#define __GIMP_OPERATION_CURVES_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_CURVES (gimp_operation_curves_get_type ()) +#define GIMP_OPERATION_CURVES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurves)) +#define GIMP_OPERATION_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurvesClass)) +#define GIMP_IS_OPERATION_CURVES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CURVES)) +#define GIMP_IS_OPERATION_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CURVES)) +#define GIMP_OPERATION_CURVES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurvesClass)) + + +typedef struct _GimpOperationCurves GimpOperationCurves; +typedef struct _GimpOperationCurvesClass GimpOperationCurvesClass; + +struct _GimpOperationCurves +{ + GimpOperationPointFilter parent_instance; +}; + +struct _GimpOperationCurvesClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_curves_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_CURVES_H__ */ diff --git a/app/operations/gimpoperationdesaturate.c b/app/operations/gimpoperationdesaturate.c new file mode 100644 index 0000000..9f833db --- /dev/null +++ b/app/operations/gimpoperationdesaturate.c @@ -0,0 +1,257 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdesaturate.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimpoperationdesaturate.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_MODE +}; + + +static void gimp_operation_desaturate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_desaturate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_desaturate_prepare (GeglOperation *operation); +static gboolean gimp_operation_desaturate_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationDesaturate, gimp_operation_desaturate, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_desaturate_parent_class + + +static void +gimp_operation_desaturate_class_init (GimpOperationDesaturateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_desaturate_set_property; + object_class->get_property = gimp_operation_desaturate_get_property; + + operation_class->prepare = gimp_operation_desaturate_prepare; + + point_class->process = gimp_operation_desaturate_process; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:desaturate", + "categories", "color", + "description", _("Turn colors into shades of gray"), + NULL); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE, + "mode", + _("Mode"), + _("Choose shade of gray based on"), + GIMP_TYPE_DESATURATE_MODE, + GIMP_DESATURATE_LUMINANCE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_operation_desaturate_init (GimpOperationDesaturate *self) +{ +} + +static void +gimp_operation_desaturate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object); + + switch (property_id) + { + case PROP_MODE: + g_value_set_enum (value, desaturate->mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_desaturate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object); + + switch (property_id) + { + case PROP_MODE: + desaturate->mode = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_desaturate_prepare (GeglOperation *operation) +{ + GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation); + const Babl *format = gegl_operation_get_source_format (operation, "input"); + + if (desaturate->mode == GIMP_DESATURATE_LUMINANCE) + { + format = babl_format_with_space ("RGBA float", format); + } + else + { + format = babl_format_with_space ("R'G'B'A float", format); + } + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "output", format); +} + +static gboolean +gimp_operation_desaturate_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + switch (desaturate->mode) + { + case GIMP_DESATURATE_LIGHTNESS: + /* This is the formula for Lightness in the HSL "bi-hexcone" + * model: https://en.wikipedia.org/wiki/HSL_and_HSV + */ + while (samples--) + { + gfloat min, max, value; + + max = MAX (src[0], src[1]); + max = MAX (max, src[2]); + min = MIN (src[0], src[1]); + min = MIN (min, src[2]); + + value = (max + min) / 2; + + dest[0] = value; + dest[1] = value; + dest[2] = value; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case GIMP_DESATURATE_LUMA: + case GIMP_DESATURATE_LUMINANCE: + while (samples--) + { + gfloat value = GIMP_RGB_LUMINANCE (src[0], src[1], src[2]); + + dest[0] = value; + dest[1] = value; + dest[2] = value; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case GIMP_DESATURATE_AVERAGE: + /* This is the formula for Intensity in the HSI model: + * https://en.wikipedia.org/wiki/HSL_and_HSV + */ + while (samples--) + { + gfloat value = (src[0] + src[1] + src[2]) / 3; + + dest[0] = value; + dest[1] = value; + dest[2] = value; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + + case GIMP_DESATURATE_VALUE: + /* This is the formula for Value in the HSV model: + * https://en.wikipedia.org/wiki/HSL_and_HSV + */ + while (samples--) + { + gfloat value; + + value = MAX (src[0], src[1]); + value = MAX (value, src[2]); + + dest[0] = value; + dest[1] = value; + dest[2] = value; + dest[3] = src[3]; + + src += 4; + dest += 4; + } + break; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationdesaturate.h b/app/operations/gimpoperationdesaturate.h new file mode 100644 index 0000000..3117a61 --- /dev/null +++ b/app/operations/gimpoperationdesaturate.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdesaturate.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DESATURATE_H__ +#define __GIMP_OPERATION_DESATURATE_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_DESATURATE (gimp_operation_desaturate_get_type ()) +#define GIMP_OPERATION_DESATURATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturate)) +#define GIMP_OPERATION_DESATURATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturateClass)) +#define GIMP_IS_OPERATION_DESATURATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DESATURATE)) +#define GIMP_IS_OPERATION_DESATURATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DESATURATE)) +#define GIMP_OPERATION_DESATURATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturateClass)) + + +typedef struct _GimpOperationDesaturate GimpOperationDesaturate; +typedef struct _GimpOperationDesaturateClass GimpOperationDesaturateClass; + +struct _GimpOperationDesaturate +{ + GimpOperationPointFilter parent_instance; + + GimpDesaturateMode mode; +}; + +struct _GimpOperationDesaturateClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_desaturate_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DESATURATE_H__ */ diff --git a/app/operations/gimpoperationequalize.c b/app/operations/gimpoperationequalize.c new file mode 100644 index 0000000..119be23 --- /dev/null +++ b/app/operations/gimpoperationequalize.c @@ -0,0 +1,248 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationequalize.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "core/gimphistogram.h" + +#include "gimpoperationequalize.h" + + +enum +{ + PROP_0, + PROP_HISTOGRAM +}; + + +static void gimp_operation_equalize_finalize (GObject *object); +static void gimp_operation_equalize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_equalize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_operation_equalize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationEqualize, gimp_operation_equalize, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_equalize_parent_class + + +static void +gimp_operation_equalize_class_init (GimpOperationEqualizeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->finalize = gimp_operation_equalize_finalize; + object_class->set_property = gimp_operation_equalize_set_property; + object_class->get_property = gimp_operation_equalize_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:equalize", + "categories", "color", + "description", "GIMP Equalize operation", + NULL); + + point_class->process = gimp_operation_equalize_process; + + g_object_class_install_property (object_class, PROP_HISTOGRAM, + g_param_spec_object ("histogram", + "Histogram", + "The histogram", + GIMP_TYPE_HISTOGRAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_operation_equalize_init (GimpOperationEqualize *self) +{ + self->values = NULL; + self->n_bins = 0; +} + +static void +gimp_operation_equalize_finalize (GObject *object) +{ + GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object); + + g_clear_pointer (&self->values, g_free); + g_clear_object (&self->histogram); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_equalize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object); + + switch (property_id) + { + case PROP_HISTOGRAM: + g_value_set_pointer (value, self->histogram); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_equalize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object); + + switch (property_id) + { + case PROP_HISTOGRAM: + if (self->histogram) + g_object_unref (self->histogram); + + self->histogram = g_value_dup_object (value); + + if (self->histogram) + { + gdouble pixels; + gint n_bins; + gint max; + gint k; + + n_bins = gimp_histogram_n_bins (self->histogram); + + if ((self->values != NULL) && (self->n_bins != n_bins)) + { + g_free (self->values); + self->values = NULL; + } + + if (self->values == NULL) + { + self->values = g_new (gdouble, 3 * n_bins); + } + + self->n_bins = n_bins; + + pixels = gimp_histogram_get_count (self->histogram, + GIMP_HISTOGRAM_VALUE, 0, n_bins - 1); + + if (gimp_histogram_n_components (self->histogram) == 1 || + gimp_histogram_n_components (self->histogram) == 2) + max = 1; + else + max = 3; + + for (k = 0; k < 3; k++) + { + gdouble sum = 0; + gint i; + + for (i = 0; i < n_bins; i++) + { + gdouble histi; + + histi = gimp_histogram_get_component (self->histogram, k, i); + + sum += histi; + + self->values[k * n_bins + i] = sum / pixels; + + if (max == 1) + { + self->values[n_bins + i] = self->values[i]; + self->values[2 * n_bins + i] = self->values[i]; + } + } + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static inline float +gimp_operation_equalize_map (GimpOperationEqualize *self, + gint component, + gfloat value) +{ + gint index; + index = component * self->n_bins + \ + (gint) (CLAMP (value * (self->n_bins - 1), 0.0, self->n_bins - 1)); + + return self->values[index]; +} + +static gboolean +gimp_operation_equalize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + while (samples--) + { + dest[RED] = gimp_operation_equalize_map (self, RED, src[RED]); + dest[GREEN] = gimp_operation_equalize_map (self, GREEN, src[GREEN]); + dest[BLUE] = gimp_operation_equalize_map (self, BLUE, src[BLUE]); + dest[ALPHA] = src[ALPHA]; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationequalize.h b/app/operations/gimpoperationequalize.h new file mode 100644 index 0000000..ea03b18 --- /dev/null +++ b/app/operations/gimpoperationequalize.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationequalize.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_EQUALIZE_H__ +#define __GIMP_OPERATION_EQUALIZE_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_EQUALIZE (gimp_operation_equalize_get_type ()) +#define GIMP_OPERATION_EQUALIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualize)) +#define GIMP_OPERATION_EQUALIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualizeClass)) +#define GIMP_IS_OPERATION_EQUALIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_EQUALIZE)) +#define GIMP_IS_OPERATION_EQUALIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_EQUALIZE)) +#define GIMP_OPERATION_EQUALIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualizeClass)) + + +typedef struct _GimpOperationEqualize GimpOperationEqualize; +typedef struct _GimpOperationEqualizeClass GimpOperationEqualizeClass; + +struct _GimpOperationEqualize +{ + GimpOperationPointFilter parent_instance; + + GimpHistogram *histogram; + gdouble *values; + gint n_bins; +}; + +struct _GimpOperationEqualizeClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_equalize_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_EQUALIZE_H__ */ diff --git a/app/operations/gimpoperationfillsource.c b/app/operations/gimpoperationfillsource.c new file mode 100644 index 0000000..5811611 --- /dev/null +++ b/app/operations/gimpoperationfillsource.c @@ -0,0 +1,254 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationfillsource.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "operations-types.h" + +#include "core/gimpdrawable.h" +#include "core/gimpfilloptions.h" + +#include "gimpoperationfillsource.h" + + +enum +{ + PROP_0, + PROP_OPTIONS, + PROP_DRAWABLE, + PROP_PATTERN_OFFSET_X, + PROP_PATTERN_OFFSET_Y, +}; + + +static void gimp_operation_fill_source_dispose (GObject *object); +static void gimp_operation_fill_source_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_fill_source_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static GeglRectangle gimp_operation_fill_source_get_bounding_box (GeglOperation *operation); +static void gimp_operation_fill_source_prepare (GeglOperation *operation); +static gboolean gimp_operation_fill_source_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level); + + +G_DEFINE_TYPE (GimpOperationFillSource, gimp_operation_fill_source, + GEGL_TYPE_OPERATION_SOURCE) + +#define parent_class gimp_operation_fill_source_parent_class + + +static void +gimp_operation_fill_source_class_init (GimpOperationFillSourceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + + object_class->dispose = gimp_operation_fill_source_dispose; + object_class->set_property = gimp_operation_fill_source_set_property; + object_class->get_property = gimp_operation_fill_source_get_property; + + operation_class->get_bounding_box = gimp_operation_fill_source_get_bounding_box; + operation_class->prepare = gimp_operation_fill_source_prepare; + operation_class->process = gimp_operation_fill_source_process; + + operation_class->threaded = FALSE; + operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:fill-source", + "categories", "gimp", + "description", "GIMP Fill Source operation", + NULL); + + g_object_class_install_property (object_class, PROP_OPTIONS, + g_param_spec_object ("options", + "Options", + "Fill options", + GIMP_TYPE_FILL_OPTIONS, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_DRAWABLE, + g_param_spec_object ("drawable", + "Drawable", + "Fill drawable", + GIMP_TYPE_DRAWABLE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_PATTERN_OFFSET_X, + g_param_spec_int ("pattern-offset-x", + "Pattern X-offset", + "Pattern X-offset", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PATTERN_OFFSET_Y, + g_param_spec_int ("pattern-offset-y", + "Pattern Y-offset", + "Pattern Y-offset", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_fill_source_init (GimpOperationFillSource *self) +{ +} + +static void +gimp_operation_fill_source_dispose (GObject *object) +{ + GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object); + + g_clear_object (&fill_source->options); + g_clear_object (&fill_source->drawable); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_operation_fill_source_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object); + + switch (property_id) + { + case PROP_OPTIONS: + g_value_set_object (value, fill_source->options); + break; + + case PROP_DRAWABLE: + g_value_set_object (value, fill_source->drawable); + break; + + case PROP_PATTERN_OFFSET_X: + g_value_set_int (value, fill_source->pattern_offset_x); + break; + + case PROP_PATTERN_OFFSET_Y: + g_value_set_int (value, fill_source->pattern_offset_y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_fill_source_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object); + + switch (property_id) + { + case PROP_OPTIONS: + g_set_object (&fill_source->options, g_value_get_object (value)); + break; + + case PROP_DRAWABLE: + g_set_object (&fill_source->drawable, g_value_get_object (value)); + break; + + case PROP_PATTERN_OFFSET_X: + fill_source->pattern_offset_x = g_value_get_int (value); + break; + + case PROP_PATTERN_OFFSET_Y: + fill_source->pattern_offset_y = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GeglRectangle +gimp_operation_fill_source_get_bounding_box (GeglOperation *operation) +{ + return gegl_rectangle_infinite_plane (); +} + +static void +gimp_operation_fill_source_prepare (GeglOperation *operation) +{ + GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (operation); + const Babl *format = NULL; + + if (fill_source->options && fill_source->drawable) + { + format = gimp_fill_options_get_format (fill_source->options, + fill_source->drawable); + } + + gegl_operation_set_format (operation, "output", format); +} + +static gboolean +gimp_operation_fill_source_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level) +{ + GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (operation); + + if (fill_source->options && fill_source->drawable) + { + GeglBuffer *buffer; + GeglRectangle rect; + + gegl_rectangle_align_to_buffer ( + &rect, result, + gimp_drawable_get_buffer (fill_source->drawable), + GEGL_RECTANGLE_ALIGNMENT_SUPERSET); + + buffer = gimp_fill_options_create_buffer (fill_source->options, + fill_source->drawable, + &rect, + fill_source->pattern_offset_x, + fill_source->pattern_offset_y); + + gegl_operation_context_take_object (context, "output", G_OBJECT (buffer)); + } + + return TRUE; +} diff --git a/app/operations/gimpoperationfillsource.h b/app/operations/gimpoperationfillsource.h new file mode 100644 index 0000000..6a9f468 --- /dev/null +++ b/app/operations/gimpoperationfillsource.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationfillsource.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_OPERATION_FILL_SOURCE_H__ +#define __GIMP_OPERATION_FILL_SOURCE_H__ + + +#define GIMP_TYPE_OPERATION_FILL_SOURCE (gimp_operation_fill_source_get_type ()) +#define GIMP_OPERATION_FILL_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSource)) +#define GIMP_OPERATION_FILL_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSourceClass)) +#define GIMP_IS_OPERATION_FILL_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE)) +#define GIMP_IS_OPERATION_FILL_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_FILL_SOURCE)) +#define GIMP_OPERATION_FILL_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSourceClass)) + + +typedef struct _GimpOperationFillSource GimpOperationFillSource; +typedef struct _GimpOperationFillSourceClass GimpOperationFillSourceClass; + +struct _GimpOperationFillSource +{ + GeglOperationSource parent_instance; + + GimpFillOptions *options; + GimpDrawable *drawable; + gint pattern_offset_x; + gint pattern_offset_y; +}; + +struct _GimpOperationFillSourceClass +{ + GeglOperationSourceClass parent_class; +}; + + +GType gimp_operation_fill_source_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_FILL_SOURCE_H__ */ diff --git a/app/operations/gimpoperationflood.c b/app/operations/gimpoperationflood.c new file mode 100644 index 0000000..b35fe2e --- /dev/null +++ b/app/operations/gimpoperationflood.c @@ -0,0 +1,1104 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationflood.c + * Copyright (C) 2016 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 . + */ + + +/* Implementation of the Flood algorithm. + * See https://wiki.gimp.org/wiki/Algorithms:Flood for details. + */ + + +#include "config.h" + +#include /* For `memcpy()`. */ + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "operations-types.h" + +#include "gimpoperationflood.h" + + +/* Maximal gap, in pixels, between consecutive dirty ranges, below (and + * including) which they are coalesced, at the beginning of the distribution + * step. + */ +#define GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP 32 + + +typedef struct _GimpOperationFloodSegment GimpOperationFloodSegment; +typedef struct _GimpOperationFloodDirtyRange GimpOperationFloodDirtyRange; +typedef struct _GimpOperationFloodContext GimpOperationFloodContext; + + +/* A segment. */ +struct _GimpOperationFloodSegment +{ + /* A boolean flag indicating whether the image- and ROI-virtual coordinate + * systems should be transposed when processing this segment. TRUE iff the + * segment is vertical. + */ + guint transpose : 1; + + /* The y-coordinate of the segment, in the ROI-virtual coordinate system. */ + guint y : 8 * sizeof (guint) - 3; + /* The difference between the y-coordinates of the source segment and this + * segment, in the ROI-virtual coordinate system. Either -1 or +1 for + * ordinary segments, and 0 for seed segments, as a special case. + * + * Note the use of `signed` as the type specifier. The C standard doesn't + * specify the signedness of bit-fields whose type specifier is `int`, or a + * typedef-name defined as `int`, such as `gint`. + */ + signed source_y_delta : 2; + + /* The x-coordinates of the first and last pixels of the segment, in the ROI- + * virtual coordinate system. Note that this is a closed range: + * [x[0], x[1]]. + */ + gint x[2]; +}; +/* Make sure the maximal image dimension fits in + * `GimpOperationFloodSegment::y`. + */ +G_STATIC_ASSERT (GIMP_MAX_IMAGE_SIZE <= (1 << (8 * sizeof (guint) - 3))); + +/* A dirty range of the current segment. */ +struct _GimpOperationFloodDirtyRange +{ + /* A boolean flag indicating whether the range was extended, or its existing + * pixels were modified, during the horizontal propagation step. + */ + gboolean modified; + + /* The x-coordinates of the first and last pixels of the range, in the ROI- + * virtual coordinate system. Note that this is a closed range: + * [x[0], x[1]]. + */ + gint x[2]; +}; + +/* Common parameters for the various parts of the algorithm. */ +struct _GimpOperationFloodContext +{ + /* Input image. */ + GeglBuffer *input; + /* Input image format. */ + const Babl *input_format; + /* Output image. */ + GeglBuffer *output; + /* Output image format. */ + const Babl *output_format; + + /* Region of interset. */ + GeglRectangle roi; + + /* Current segment. */ + GimpOperationFloodSegment segment; + + /* The following arrays hold the ground- and water-level of the current- and + * source-segments. The vertical- and horizontal-propagation steps don't + * generally access the input and output GEGL buffers directly, but rather + * read from, and write to, these arrays, for efficiency. These arrays are + * read-from, and written-to, the corresponding GEGL buffers before and after + * these steps. + */ + + /* Ground level of the current segment, indexed by x-coordinate in the ROI- + * virtual coordinate system. Only valid inside the range + * `[segment.x[0], segment.x[1]]`. + */ + gfloat *ground; + /* Water level of the current segment, indexed by x-coordinate in the ROI- + * virtual coordinate system. Initially only valid inside the range + * `[segment.x[0], segment.x[1]]`, but may be written-to outside this range + * during horizontal propagation, if the dirty ranges are extended past the + * bounds of the segment. + */ + gfloat *water; + /* Water level of the source segment, indexed by x-coordinate in the ROI- + * virtual coordinate system. Only valid inside the range + * `[segment.x[0], segment.x[1]]`. + */ + gfloat *source_water; + + /* A common buffer for the water level of the current- and source-segments. + * `water` and `source_water` are pointers into this buffer. This buffer is + * used as an optimization, in order to read the water level of both segments + * from the output GEGL buffer in a single call, and is otherwise not used + * directly (`water` and `source_water` are used to access the water level + * instead.) + */ + gfloat *water_buffer; +}; + + +static void gimp_operation_flood_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_flood_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static GeglRectangle gimp_operation_flood_get_cached_region (GeglOperation *self, + const GeglRectangle *roi); + +static void gimp_operation_flood_process_push (GQueue *queue, + gboolean transpose, + gint y, + gint source_y_delta, + gint x0, + gint x1); +static void gimp_operation_flood_process_seed (GQueue *queue, + const GeglRectangle *roi); +static void gimp_operation_flood_process_transform_rect (const GimpOperationFloodContext *ctx, + GeglRectangle *dest, + const GeglRectangle *src); +static void gimp_operation_flood_process_fetch (GimpOperationFloodContext *ctx); +static gint gimp_operation_flood_process_propagate_vertical (GimpOperationFloodContext *ctx, + GimpOperationFloodDirtyRange *dirty_ranges); +static void gimp_operation_flood_process_propagate_horizontal (GimpOperationFloodContext *ctx, + gint dir, + GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count); +static gint gimp_operation_flood_process_coalesce (const GimpOperationFloodContext *ctx, + GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count, + gint gap); +static void gimp_operation_flood_process_commit (const GimpOperationFloodContext *ctx, + const GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count); +static void gimp_operation_flood_process_distribute (const GimpOperationFloodContext *ctx, + GQueue *queue, + const GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count); +static gboolean gimp_operation_flood_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationFlood, gimp_operation_flood, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_flood_parent_class + + +/* GEGL graph for the test case. */ +static const gchar* reference_xml = "" +"" +" " +"" +" " +" flood-input.png" +" " +"" +""; + + +static void +gimp_operation_flood_class_init (GimpOperationFloodClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + /* The input and output buffers must be different, since we generally need to + * be able to access the input-image values after having written to the + * output buffer. + */ + operation_class->want_in_place = FALSE; + /* We don't want `GeglOperationFilter` to split the image across multiple + * threads, since this operation depends on, and affects, the image as a + * whole. + */ + operation_class->threaded = FALSE; + /* Note that both of these options are the default; we set them here for + * explicitness. + */ + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:flood", + "categories", "gimp", + "description", "GIMP Flood operation", + "reference", "https://wiki.gimp.org/wiki/Algorithms:Flood", + "reference-image", "flood-output.png", + "reference-composition", reference_xml, + NULL); + + operation_class->prepare = gimp_operation_flood_prepare; + operation_class->get_required_for_output = gimp_operation_flood_get_required_for_output; + operation_class->get_cached_region = gimp_operation_flood_get_cached_region; + + filter_class->process = gimp_operation_flood_process; +} + +static void +gimp_operation_flood_init (GimpOperationFlood *self) +{ +} + +static void +gimp_operation_flood_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space)); +} + +static GeglRectangle +gimp_operation_flood_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static GeglRectangle +gimp_operation_flood_get_cached_region (GeglOperation *self, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + + +/* Pushes a single segment into the queue. */ +static void +gimp_operation_flood_process_push (GQueue *queue, + gboolean transpose, + gint y, + gint source_y_delta, + gint x0, + gint x1) +{ + GimpOperationFloodSegment *segment; + + segment = g_slice_new (GimpOperationFloodSegment); + + segment->transpose = transpose; + segment->y = y; + segment->source_y_delta = source_y_delta; + segment->x[0] = x0; + segment->x[1] = x1; + + g_queue_push_tail (queue, segment); +} + +/* Pushes the seed segments into the queue. Recall that the seed segments are + * indicated by having their `source_y_delta` field equal 0. + * + * `roi` is given in the image-physical coordinate system. + */ +static void +gimp_operation_flood_process_seed (GQueue *queue, + const GeglRectangle *roi) +{ + if (roi->width == 0 || roi->height == 0) + return; + + /* Top edge. */ + gimp_operation_flood_process_push (queue, + /* transpose = */ FALSE, + /* y = */ 0, + /* source_y_delta = */ 0, + /* x0 = */ 0, + /* x1 = */ roi->width - 1); + + if (roi->height == 1) + return; + + /* Bottom edge. */ + gimp_operation_flood_process_push (queue, + /* transpose = */ FALSE, + /* y = */ roi->height - 1, + /* source_y_delta = */ 0, + /* x0 = */ 0, + /* x1 = */ roi->width - 1); + + if (roi->height == 2) + return; + + /* Left edge. */ + gimp_operation_flood_process_push (queue, + /* transpose = */ TRUE, + /* y = */ 0, + /* source_y_delta = */ 0, + /* x0 = */ 1, + /* x1 = */ roi->height - 2); + + if (roi->width == 1) + return; + + /* Right edge. */ + gimp_operation_flood_process_push (queue, + /* transpose = */ TRUE, + /* y = */ roi->width - 1, + /* source_y_delta = */ 0, + /* x0 = */ 1, + /* x1 = */ roi->height - 2); +} + +/* Transforms a `GeglRectangle` between the image-physical and image-virtual + * coordinate systems, in either direction, based on the attributes of the + * current segment (namely, its `transpose` flag.) + * + * Takes the input rectangle through `src`, and stores the result in `dest`. + * Both parameters may refer to the same object. + */ +static void +gimp_operation_flood_process_transform_rect (const GimpOperationFloodContext *ctx, + GeglRectangle *dest, + const GeglRectangle *src) +{ + if (! ctx->segment.transpose) + *dest = *src; + else + { + gint temp; + + temp = src->x; + dest->x = src->y; + dest->y = temp; + + temp = src->width; + dest->width = src->height; + dest->height = temp; + } +} + +/* Reads the ground- and water-level for the current- and source-segments from + * the GEGL buffers into the corresponding arrays. Sets up the `water` and + * `source_water` pointers of `ctx` to point to the right location in + * `water_buffer`. + */ +static void +gimp_operation_flood_process_fetch (GimpOperationFloodContext *ctx) +{ + /* Image-virtual and image-physical rectangles, respectively. */ + GeglRectangle iv_rect, ip_rect; + + /* Set the horizontal extent of the rectangle to span the entire segment. */ + iv_rect.x = ctx->roi.x + ctx->segment.x[0]; + iv_rect.width = ctx->segment.x[1] - ctx->segment.x[0] + 1; + + /* For reading the water level, we treat ordinary (non-seed) and seed + * segments differently. + */ + if (ctx->segment.source_y_delta != 0) + { + /* Ordinary segment. */ + + /* We set the vertical extent of the rectangle to span both the current- + * and the source-segments, and set the `water` and `source_water` + * pointers to point to two consecutive rows of the `water_buffer` array + * (the y-coordinate of the rectangle, and which row is above which, + * depends on whether the source segment is above, or below, the current + * one.) + */ + if (ctx->segment.source_y_delta < 0) + { + iv_rect.y = ctx->roi.y + ctx->segment.y - 1; + ctx->water = ctx->water_buffer + ctx->roi.width; + ctx->source_water = ctx->water_buffer; + } + else + { + iv_rect.y = ctx->roi.y + ctx->segment.y; + ctx->water = ctx->water_buffer; + ctx->source_water = ctx->water_buffer + ctx->roi.width; + } + iv_rect.height = 2; + + /* Transform `iv_rect` to the image-physical coordinate system, and store + * the result in `ip_rect`. + */ + gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect); + + /* Read the water level from the output GEGL buffer into `water_buffer`. + * + * Notice the stride: If the current segment is horizontal, then we're + * reading a pair of rows directly into the correct locations inside + * `water_buffer` (i.e., `water` and `source_water`). On the other hand, + * if the current segment is vertical, then we're reading a pair of + * *columns*; we set the stride to 2-pixels so that the current- and + * source-water levels are interleaved in `water_buffer`, and reorder + * them below. + */ + gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format, + ctx->water_buffer + ctx->segment.x[0], + sizeof (gfloat) * + (ctx->segment.transpose ? 2 : ctx->roi.width), + GEGL_ABYSS_NONE); + + /* As mentioned above, if the current segment is vertical, then the + * water levels of the current- and source-segments are interleaved in + * `water_buffer`. We deinterleave the water levels into `water` and + * `source_water`, using the yet-to-be-written-to `ground` array as a + * temporary buffer, as necessary. + */ + if (ctx->segment.transpose) + { + const gfloat *src; + gfloat *dest1, *dest2, *temp; + gint size, temp_size; + gint i; + + src = ctx->water_buffer + ctx->segment.x[0]; + + dest1 = ctx->water_buffer + ctx->segment.x[0]; + dest2 = ctx->water_buffer + ctx->roi.width + ctx->segment.x[0]; + temp = ctx->ground; + + size = ctx->segment.x[1] - ctx->segment.x[0] + 1; + temp_size = MAX (0, 2 * size - ctx->roi.width); + + for (i = 0; i < temp_size; i++) + { + dest1[i] = src[2 * i]; + temp[i] = src[2 * i + 1]; + } + for (; i < size; i++) + { + dest1[i] = src[2 * i]; + dest2[i] = src[2 * i + 1]; + } + + memcpy (dest2, temp, sizeof (gfloat) * temp_size); + } + } + else + { + /* Seed segment. */ + + gint x; + + /* Set the `water` and `source_water` pointers to point to consecutive + * rows of the `water_buffer` array. + */ + ctx->water = ctx->water_buffer; + ctx->source_water = ctx->water_buffer + ctx->roi.width; + + /* Set the vertical extent of the rectangle to span a the current + * segment's row. + */ + iv_rect.y = ctx->roi.y + ctx->segment.y; + iv_rect.height = 1; + + /* Transform `iv_rect` to the image-physical coordinate system, and store + * the result in `ip_rect`. + */ + gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect); + + /* Read the water level of the current segment from the output GEGL + * buffer into `water`. + */ + gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format, + ctx->water + ctx->segment.x[0], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + /* Initialize `source_water` to 0, as this is a seed segment. */ + for (x = ctx->segment.x[0]; x <= ctx->segment.x[1]; x++) + ctx->source_water[x] = 0.0; + } + + /* Set the vertical extent of the rectangle to span a the current segment's + * row. + */ + iv_rect.y = ctx->roi.y + ctx->segment.y; + iv_rect.height = 1; + + /* Transform `iv_rect` to the image-physical coordinate system, and store the + * result in `ip_rect`. + */ + gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect); + + /* Read the ground level of the current segment from the input GEGL buffer + * into `ground`. + */ + gegl_buffer_get (ctx->input, &ip_rect, 1.0, ctx->input_format, + ctx->ground + ctx->segment.x[0], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); +} + +/* Performs the vertical propagation step of the algorithm. Writes the dirty + * ranges to the `dirty_ranges` parameter, and returns the number of dirty + * ranges as the function's result. + */ +static gint +gimp_operation_flood_process_propagate_vertical (GimpOperationFloodContext *ctx, + GimpOperationFloodDirtyRange *dirty_ranges) +{ + GimpOperationFloodDirtyRange *range = dirty_ranges; + gint x; + + for (x = ctx->segment.x[0]; x <= ctx->segment.x[1]; x++) + { + /* Scan the segment until we find a pixel whose water level needs to be + * updated. + */ + if (ctx->source_water[x] < ctx->water[x] && + ctx->ground[x] < ctx->water[x]) + { + /* Compute and update the water level. */ + gfloat level = MAX (ctx->source_water[x], ctx->ground[x]); + + ctx->water[x] = level; + + /* Start a new dirty range at the current pixel. */ + range->x[0] = x; + range->modified = FALSE; + + for (x++; x <= ctx->segment.x[1]; x++) + { + /* Keep scanning the segment while the water level of consecutive + * pixels needs to be updated. + */ + if (ctx->source_water[x] < ctx->water[x] && + ctx->ground[x] < ctx->water[x]) + { + /* Compute and update the water level. */ + gfloat other_level = MAX (ctx->source_water[x], + ctx->ground[x]); + + ctx->water[x] = other_level; + + /* If the water level of the current pixel, `other_level`, + * equals the water level of the current dirty range, + * `level`, we keep scanning, making the current pixel part + * of the current range. On the other hand, if the current + * pixel's water level is different than the that of the + * current range, we finalize the range, and start a new one + * at the current pixel. + */ + if (other_level != level) + { + range->x[1] = x - 1; + range++; + + range->x[0] = x; + range->modified = FALSE; + level = other_level; + } + } + else + break; + } + + /* Finalize the current dirty range. */ + range->x[1] = x - 1; + range++; + + /* Make sure we don't over-increment `x` on the continuation of the + * loop. + */ + if (x > ctx->segment.x[1]) + break; + } + } + + /* Return the number of dirty ranges. */ + return range - dirty_ranges; +} + +/* Performs a single pass of the horizontal propagation step of the algorithm. + * `dir` controls the direction of the pass: either +1 for a left-to-right + * pass, or -1 for a right-to-left pass. The dirty ranges are passed through + * the `dirty_ranges` array (and their number in `range_count`), and are + * modified in-place. + */ +static void +gimp_operation_flood_process_propagate_horizontal (GimpOperationFloodContext *ctx, + gint dir, + GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count) +{ + /* The index of the terminal (i.e., "`dir`-most") component of the `x[]` + * array of `GimpOperationFloodSegment` and `GimpOperationFloodDirtyRange`, + * based on the scan direction. Equals 1 (i.e., the right component) when + * `dir` is +1 (i.e., left-to-right), and equals 0 (i.e., the left component) + * when `dir` is -1 (i.e., right-to-left). + */ + gint x_component; + /* One-past the final x-coordinate of the ROI, in the ROI-virtual coordinate + * system, based on the scan direction. That is, the x-coordinate of the + * pixel to the right of the rightmost pixel, for a left-to-right scan, and + * of the pixel to the left of the leftmost pixel, for a right-to-left scan. + */ + gint roi_lim; + /* One-past the final x-coordinate of the segment, in the ROI-virtual + * coordinate system, based on the scan direction, in a similar fashion to + * `roi_lim`. + */ + gint segment_lim; + /* The indices of the first, and one-past-the-last dirty ranges, based on the + * direction of the scan. Recall that when scanning right-to-left, we + * iterate over the ranges in reverse. + */ + gint first_range, last_range; + /* Index of the current dirty range. */ + gint range_index; + /* Image-virtual and image-physical rectangles, respectively. */ + GeglRectangle iv_rect, ip_rect; + + /* Initialize the above variables based on the scan direction. */ + if (dir > 0) + { + /* Left-to-right. */ + x_component = 1; + roi_lim = ctx->roi.width; + first_range = 0; + last_range = range_count; + } + else + { + /* Right-to-left. */ + x_component = 0; + roi_lim = -1; + first_range = range_count - 1; + last_range = -1; + } + segment_lim = ctx->segment.x[x_component] + dir; + + /* We loop over the dirty ranges, in the direction of the scan. For each + * range, we iterate over the pixels, in the scan direction, starting at the + * outer edge of the range, and update the water level, considering only the + * water level of the previous and current pixels, until we arrive at a pixel + * whose water level remains the same, at which point we move to the next + * range, as described in the algorithm overview. + */ + for (range_index = first_range; + range_index != last_range; + range_index += dir) + { + /* Current dirty range. */ + GimpOperationFloodDirtyRange *range; + /* Current pixel, in the ROI-virtual coordinate system. */ + gint x; + /* We use `level` to compute the water level of the current pixel. At + * the beginning of each iteration, it holds the water level of the + * previous pixel. + */ + gfloat level; + /* The `inside` flag indicates whether `x` is inside the current segment. + * Recall that we may iterate past the bounds of the current segment, in + * which case we need to read the ground- and water-levels from the GEGL + * buffers directly, instead of the corresponding arrays. + */ + gboolean inside; + /* Loop limit. */ + gint lim; + + range = &dirty_ranges[range_index]; + /* Last x-coordinate of the range, in the direction of the scan. */ + x = range->x[x_component]; + /* We start iterating on the pixel after `x`; initialize `level` to the + * water level of the previous pixel. + */ + level = ctx->water[x]; + /* The ranges produced by the vertical propagation step are all within + * the bounds of the segment; the horizontal propagation step may only + * extend them in the direction of the scan. Therefore, on both passes + * of the horizontal propagation step, the last pixel of each range, in + * the direction of the scan, is initially inside the segment. + */ + inside = TRUE; + /* If this isn't the last range, break the loop at the beginning of the + * next range. Otherwise, break the loop at the edge of the ROI. + */ + if (range_index + dir != last_range) + lim = (range + dir)->x[1 - x_component]; + else + lim = roi_lim; + + /* Loop over the pixels between the edge of the current range, and the + * beginning of the next range (or the edge of the ROI). + */ + for (x += dir; x != lim; x += dir) + { + gfloat ground_level, water_level; + + /* Recall that `segment_lim` is one-past the last pixel of the + * segment. If we hit it, we've gone outside the segment bounds. + */ + if (x == segment_lim) + { + inside = FALSE; + /* Initialize the rectangle to sample pixels directly from the + * GEGL buffers. + */ + iv_rect.y = ctx->roi.y + ctx->segment.y; + iv_rect.width = 1; + iv_rect.height = 1; + } + + /* If we're inside the segment, read the ground- and water-levels + * from the corresponding arrays; otherwise, read them from the GEGL + * buffers directly. Note that, on each pass, we may only write to + * pixels outside the segment *in direction of the scan* (in which + * case, the new values are written to the `water` array, but not + * directly to the output GEGL buffer), hence, when reading from the + * GEGL buffers, there's no danger of reading stale values, that were + * changed on the previous pass. + */ + if (inside) + { + ground_level = ctx->ground[x]; + water_level = ctx->water[x]; + } + else + { + iv_rect.x = ctx->roi.x + x; + + /* Transform `iv_rect` to the image-physical coordinate system, + * and store the result in `ip_rect`. + */ + gimp_operation_flood_process_transform_rect (ctx, + &ip_rect, &iv_rect); + + /* Read the current pixel's ground level. */ + gegl_buffer_get (ctx->input, &ip_rect, 1.0, ctx->input_format, + &ground_level, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + /* Read the current pixel's water level. */ + gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format, + &water_level, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + /* The new water level is the maximum of the current ground level, + * and the minimum of the current and previous water levels. Recall + * that `level` holds the previous water level, and that the current + * water level is never less than the ground level. + */ + if (level < ground_level) + level = ground_level; + if (level < water_level) + { + /* The water level changed. Update the current pixel, and set + * the `modified` flag of the current range, since it will be + * extended to include the current pixel. + */ + ctx->water[x] = level; + range->modified = TRUE; + } + else + /* The water level stayed the same. Break the loop. */ + break; + } + + /* Extend the current dirty range to include the last modified pixel, if + * any. + */ + range->x[x_component] = x - dir; + + /* If we stopped the loop before hitting the edge of the next range, or + * if we're at the last range, continue to the next range (or quit). + */ + if (x != lim || range_index + dir == last_range) + continue; + + /* If we hit the edge of the next range, we keep propagating the changes + * *inside* the next range, until we hit its other edge, or until the + * water level stays the same. + */ + range += dir; + lim = range->x[x_component] + dir; + + for (; x != lim; x += dir) + { + /* Note that we're necessarily inside the segment right now, since + * the only range that could have been extended past the edge of the + * segment by the previous pass, is the first range of the current + * pass, while the range we're currently inside is at least the + * second. + */ + if (level < ctx->ground[x]) + level = ctx->ground[x]; + if (level < ctx->water[x]) + { + ctx->water[x] = level; + /* Set the `modified` flag of the range, since the water level of + * its existing pixels changed. + */ + range->modified = TRUE; + } + else + break; + } + } +} + +/* Coalesces consecutive dirty ranges that are separated by a gap less-than or + * equal-to `max_gap`, in-place, and returns the new number of ranges. + */ +static gint +gimp_operation_flood_process_coalesce (const GimpOperationFloodContext *ctx, + GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count, + gint max_gap) +{ + /* First and last ranges to coalesce, respectively. */ + const GimpOperationFloodDirtyRange *first_range, *last_range; + /* Destination range. */ + GimpOperationFloodDirtyRange *range = dirty_ranges; + + for (first_range = dirty_ranges; + first_range != dirty_ranges + range_count; + first_range++) + { + /* The `modified` flag of the coalesced range -- the logical-OR of the + * `modified` flags of the individual ranges. + */ + gboolean modified = first_range->modified; + + /* Find all consecutive ranges with a small-enough gap. */ + for (last_range = first_range; + last_range + 1 != dirty_ranges + range_count; + last_range++) + { + if ((last_range + 1)->x[0] - last_range->x[1] > max_gap) + break; + + modified |= (last_range + 1)->modified; + } + + /* Write the coalesced range, or copy the current range, to the + * destination range. + */ + if (first_range != last_range || first_range != range) + { + range->x[0] = first_range->x[0]; + range->x[1] = last_range->x[1]; + range->modified = modified; + } + + first_range = last_range; + range++; + } + + /* Return the new range count. */ + return range - dirty_ranges; +} + +/* Writes the updated water level of the dirty ranges back to the output GEGL + * buffer. + */ +static void +gimp_operation_flood_process_commit (const GimpOperationFloodContext *ctx, + const GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count) +{ + const GimpOperationFloodDirtyRange *range; + /* Image-virtual and image-physical rectangles, respectively. */ + GeglRectangle iv_rect, ip_rect; + + /* Set the vertical extent of the rectangle to span a the current segment's + * row. + */ + iv_rect.y = ctx->roi.y + ctx->segment.y; + iv_rect.height = 1; + + for (range = dirty_ranges; range != dirty_ranges + range_count; range++) + { + /* Set the horizontal extent of the rectangle to span the dirty range. */ + iv_rect.x = ctx->roi.x + range->x[0]; + iv_rect.width = range->x[1] - range->x[0] + 1; + + /* Transform `iv_rect` to the image-physical coordinate system, and store + * the result in `ip_rect`. + */ + gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect); + + /* Write the updated water level to the output GEGL buffer. */ + gegl_buffer_set (ctx->output, &ip_rect, 0, ctx->output_format, + ctx->water + range->x[0], + GEGL_AUTO_ROWSTRIDE); + } +} + +/* Pushes the new segments, corresponding to the dirty ranges of the current + * segment, into the queue. + */ +static void +gimp_operation_flood_process_distribute (const GimpOperationFloodContext *ctx, + GQueue *queue, + const GimpOperationFloodDirtyRange *dirty_ranges, + gint range_count) +{ + const GimpOperationFloodDirtyRange *range; + static const gint y_deltas[] = {-1, +1}; + gint i; + + /* For each neighboring row... */ + for (i = 0; i < G_N_ELEMENTS (y_deltas); i++) + { + /* The difference between the negihboring row's y-coordinate and the + * current row's y-corindate, in the ROI-virtual coordinate system. + */ + gint y_delta = y_deltas[i]; + /* The negihboring row's y-coordinate in the ROI-virtual coordinate + * system. + */ + gint y = ctx->segment.y + y_delta; + + /* If the neighboring row is outside the ROI, skip it. */ + if (y < 0 || y >= ctx->roi.height) + continue; + + /* For each dirty range... */ + for (range = dirty_ranges; range != dirty_ranges + range_count; range++) + { + /* If the range was modified during horizontal propagation, or if the + * neighboring row is not the source segment's row... (note that the + * latter is always true for seed segments.) + */ + if (range->modified || y_delta != ctx->segment.source_y_delta) + { + /* Push a new segment into the queue, spanning the same pixels as + * the dirty range on the neighboring row, using the current row + * as its source segment. + */ + gimp_operation_flood_process_push (queue, + ctx->segment.transpose, + y, + -y_delta, + range->x[0], + range->x[1]); + } + } + } +} + +/* Main algorithm. */ +static gboolean +gimp_operation_flood_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + const Babl *input_format = gegl_operation_get_format (operation, "input"); + const Babl *output_format = gegl_operation_get_format (operation, "output"); + GeglColor *color; + gint max_size; + GimpOperationFloodContext ctx; + GimpOperationFloodDirtyRange *dirty_ranges; + GQueue *queue; + + /* Make sure the input- and output-buffers are different. */ + g_return_val_if_fail (input != output, FALSE); + + /* Make sure the ROI is small enough for the `GimpOperationFloodSegment::y` + * field. + */ + g_return_val_if_fail (roi->width <= GIMP_MAX_IMAGE_SIZE && + roi->height <= GIMP_MAX_IMAGE_SIZE, FALSE); + + ctx.input = input; + ctx.input_format = input_format; + ctx.output = output; + ctx.output_format = output_format; + + /* All buffers need to have enough capacity to process a full row, or a full + * column, since, when processing vertical segments, we treat the image as + * transposed. + */ + max_size = MAX (roi->width, roi->height); + ctx.ground = g_new (gfloat, max_size); + /* The `water_buffer` array needs to be able to hold two rows (or columns). */ + ctx.water_buffer = g_new (gfloat, 2 * max_size); + dirty_ranges = g_new (GimpOperationFloodDirtyRange, max_size); + + /* Initialize the water level to 1 everywhere. */ + color = gegl_color_new ("#fff"); + gegl_buffer_set_color (output, roi, color); + g_object_unref (color); + + /* Create the queue and push the seed segments. */ + queue = g_queue_new (); + gimp_operation_flood_process_seed (queue, roi); + + /* While there are segments to process in the queue... */ + while (! g_queue_is_empty (queue)) + { + GimpOperationFloodSegment *segment; + gint range_count; + + /* Pop a segment off the top of the queue, copy it to `ctx.segment`, and + * free its memory. + */ + segment = (GimpOperationFloodSegment *) g_queue_pop_head (queue); + ctx.segment = *segment; + g_slice_free (GimpOperationFloodSegment, segment); + + /* Transform the ROI from the image-physical coordinate system to the + * image-virtual coordinate system, and store the result in `ctx.roi`. + */ + gimp_operation_flood_process_transform_rect (&ctx, &ctx.roi, roi); + + /* Read the ground- and water-levels of the current- and source-segments + * from the corresponding GEGL buffers to the corresponding arrays. + */ + gimp_operation_flood_process_fetch (&ctx); + + /* Perform the vertical propagation step. */ + range_count = gimp_operation_flood_process_propagate_vertical (&ctx, + dirty_ranges); + /* If no dirty ranges were produced during vertical propagation, then the + * water level of the current segment didn't change, and we can short- + * circuit early. + */ + if (range_count == 0) + continue; + + /* Perform both passes of the horizontal propagation step. */ + gimp_operation_flood_process_propagate_horizontal (&ctx, + /* Left-to-right */ +1, + dirty_ranges, + range_count); + gimp_operation_flood_process_propagate_horizontal (&ctx, + /* Right-to-left */ -1, + dirty_ranges, + range_count); + + /* Coalesce consecutive dirty ranges separated by a gap less-than or + * equal-to `GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP`. + */ + range_count = gimp_operation_flood_process_coalesce (&ctx, + dirty_ranges, + range_count, + GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP); + + /* Write the updated water level back to the output GEGL buffer. */ + gimp_operation_flood_process_commit (&ctx, dirty_ranges, range_count); + + /* Push the new segments into the queue. */ + gimp_operation_flood_process_distribute (&ctx, queue, + dirty_ranges, range_count); + } + + g_queue_free (queue); + + g_free (dirty_ranges); + g_free (ctx.water_buffer); + g_free (ctx.ground); + + return TRUE; +} diff --git a/app/operations/gimpoperationflood.h b/app/operations/gimpoperationflood.h new file mode 100644 index 0000000..ed2c1c6 --- /dev/null +++ b/app/operations/gimpoperationflood.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationflood.h + * Copyright (C) 2016 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 . + */ + +#ifndef __GIMP_OPERATION_FLOOD_H__ +#define __GIMP_OPERATION_FLOOD_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_FLOOD (gimp_operation_flood_get_type ()) +#define GIMP_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFlood)) +#define GIMP_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass)) +#define GIMP_IS_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_FLOOD)) +#define GIMP_IS_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_FLOOD)) +#define GIMP_OPERATION_FLOOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass)) + + +typedef struct _GimpOperationFlood GimpOperationFlood; +typedef struct _GimpOperationFloodClass GimpOperationFloodClass; + +struct _GimpOperationFlood +{ + GeglOperationFilter parent_instance; +}; + +struct _GimpOperationFloodClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_flood_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_FLOOD_H__ */ diff --git a/app/operations/gimpoperationgradient.c b/app/operations/gimpoperationgradient.c new file mode 100644 index 0000000..2dbb746 --- /dev/null +++ b/app/operations/gimpoperationgradient.c @@ -0,0 +1,1283 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Largely based on gimpdrawable-gradient.c + * + * gimpoperationgradient.c + * Copyright (C) 2014 Michael Henning + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "core/gimpgradient.h" + +#include "gimpoperationgradient.h" + + +#define GRADIENT_CACHE_N_SUPERSAMPLES 4 +#define GRADIENT_CACHE_MAX_SIZE ((1 << 20) / sizeof (GimpRGB)) + + +enum +{ + PROP_0, + PROP_CONTEXT, + PROP_GRADIENT, + PROP_START_X, + PROP_START_Y, + PROP_END_X, + PROP_END_Y, + PROP_GRADIENT_TYPE, + PROP_GRADIENT_REPEAT, + PROP_OFFSET, + PROP_GRADIENT_REVERSE, + PROP_GRADIENT_BLEND_COLOR_SPACE, + PROP_SUPERSAMPLE, + PROP_SUPERSAMPLE_DEPTH, + PROP_SUPERSAMPLE_THRESHOLD, + PROP_DITHER +}; + +typedef struct +{ + GimpGradient *gradient; + gboolean reverse; + GimpGradientBlendColorSpace blend_color_space; + GimpRGB *gradient_cache; + gint gradient_cache_size; + GimpGradientSegment *last_seg; + gdouble offset; + gdouble sx, sy; + GimpGradientType gradient_type; + gdouble dist; + gdouble vec[2]; + GimpRepeatMode repeat; + GeglSampler *dist_sampler; +} RenderBlendData; + + +typedef struct +{ + gfloat *data; + GeglRectangle roi; + GRand *dither_rand; +} PutPixelData; + + +/* local function prototypes */ + +static void gimp_operation_gradient_dispose (GObject *gobject); +static void gimp_operation_gradient_finalize (GObject *gobject); +static void gimp_operation_gradient_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_gradient_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_gradient_prepare (GeglOperation *operation); + +static GeglRectangle gimp_operation_gradient_get_bounding_box (GeglOperation *operation); + +static gdouble gradient_calc_conical_sym_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_conical_asym_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_square_factor (gdouble dist, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_radial_factor (gdouble dist, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_linear_factor (gdouble dist, + gdouble *vec, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_bilinear_factor (gdouble dist, + gdouble *vec, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_spiral_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y, + gboolean clockwise); + +static gdouble gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y); +static gdouble gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y); + +static void gradient_render_pixel (gdouble x, + gdouble y, + GimpRGB *color, + gpointer render_data); + +static void gradient_put_pixel (gint x, + gint y, + GimpRGB *color, + gpointer put_pixel_data); + +static void gradient_dither_pixel (GimpRGB *color, + GRand *dither_rand, + gfloat *dest); + +static gboolean gimp_operation_gradient_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *result, + gint level); + +static void gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self); +static void gimp_operation_gradient_validate_cache (GimpOperationGradient *self); + + +G_DEFINE_TYPE (GimpOperationGradient, gimp_operation_gradient, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_gradient_parent_class + + +static void +gimp_operation_gradient_class_init (GimpOperationGradientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + object_class->dispose = gimp_operation_gradient_dispose; + object_class->finalize = gimp_operation_gradient_finalize; + object_class->set_property = gimp_operation_gradient_set_property; + object_class->get_property = gimp_operation_gradient_get_property; + + operation_class->prepare = gimp_operation_gradient_prepare; + operation_class->get_bounding_box = gimp_operation_gradient_get_bounding_box; + + filter_class->process = gimp_operation_gradient_process; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:gradient", + "categories", "gimp", + "description", "GIMP Gradient operation", + NULL); + + g_object_class_install_property (object_class, PROP_CONTEXT, + g_param_spec_object ("context", + "Context", + "A GimpContext", + GIMP_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GRADIENT, + g_param_spec_object ("gradient", + "Gradient", + "A GimpGradient to render", + GIMP_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_START_X, + g_param_spec_double ("start-x", + "Start X", + "X coordinate of the first point", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_START_Y, + g_param_spec_double ("start-y", + "Start Y", + "Y coordinate of the first point", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_END_X, + g_param_spec_double ("end-x", + "End X", + "X coordinate of the second point", + -G_MAXDOUBLE, G_MAXDOUBLE, 200, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_END_Y, + g_param_spec_double ("end-y", + "End Y", + "Y coordinate of the second point", + -G_MAXDOUBLE, G_MAXDOUBLE, 200, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GRADIENT_TYPE, + g_param_spec_enum ("gradient-type", + "Gradient Type", + "The type of gradient to render", + GIMP_TYPE_GRADIENT_TYPE, + GIMP_GRADIENT_LINEAR, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GRADIENT_REPEAT, + g_param_spec_enum ("gradient-repeat", + "Repeat mode", + "Repeat mode", + GIMP_TYPE_REPEAT_MODE, + GIMP_REPEAT_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OFFSET, + g_param_spec_double ("offset", + "Offset", + "Offset relates to the starting and ending coordinates " + "specified for the blend. This parameter is mode dependent.", + 0, G_MAXDOUBLE, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GRADIENT_REVERSE, + g_param_spec_boolean ("gradient-reverse", + "Reverse", + "Reverse the gradient", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_GRADIENT_BLEND_COLOR_SPACE, + g_param_spec_enum ("gradient-blend-color-space", + "Blend Color Space", + "Which color space to use when blending RGB gradient segments", + GIMP_TYPE_GRADIENT_BLEND_COLOR_SPACE, + GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SUPERSAMPLE, + g_param_spec_boolean ("supersample", + "Supersample", + "Do adaptive supersampling", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SUPERSAMPLE_DEPTH, + g_param_spec_int ("supersample-depth", + "Max depth", + "Maximum recursion levels for supersampling", + 1, 9, 3, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SUPERSAMPLE_THRESHOLD, + g_param_spec_double ("supersample-threshold", + "Threshold", + "Supersampling threshold", + 0, 4, 0.20, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DITHER, + g_param_spec_boolean ("dither", + "Dither", + "Use dithering to reduce banding", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_gradient_init (GimpOperationGradient *self) +{ + g_mutex_init (&self->gradient_cache_mutex); +} + +static void +gimp_operation_gradient_dispose (GObject *object) +{ + GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object); + + gimp_operation_gradient_invalidate_cache (self); + + g_clear_object (&self->gradient); + g_clear_object (&self->context); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_operation_gradient_finalize (GObject *object) +{ + GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object); + + g_mutex_clear (&self->gradient_cache_mutex); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_gradient_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object); + + switch (property_id) + { + case PROP_CONTEXT: + g_value_set_object (value, self->context); + break; + + case PROP_GRADIENT: + g_value_set_object (value, self->gradient); + break; + + case PROP_START_X: + g_value_set_double (value, self->start_x); + break; + + case PROP_START_Y: + g_value_set_double (value, self->start_y); + break; + + case PROP_END_X: + g_value_set_double (value, self->end_x); + break; + + case PROP_END_Y: + g_value_set_double (value, self->end_y); + break; + + case PROP_GRADIENT_TYPE: + g_value_set_enum (value, self->gradient_type); + break; + + case PROP_GRADIENT_REPEAT: + g_value_set_enum (value, self->gradient_repeat); + break; + + case PROP_OFFSET: + g_value_set_double (value, self->offset); + break; + + case PROP_GRADIENT_REVERSE: + g_value_set_boolean (value, self->gradient_reverse); + break; + + case PROP_GRADIENT_BLEND_COLOR_SPACE: + g_value_set_enum (value, self->gradient_blend_color_space); + break; + + case PROP_SUPERSAMPLE: + g_value_set_boolean (value, self->supersample); + break; + + case PROP_SUPERSAMPLE_DEPTH: + g_value_set_int (value, self->supersample_depth); + break; + + case PROP_SUPERSAMPLE_THRESHOLD: + g_value_set_double (value, self->supersample_threshold); + break; + + case PROP_DITHER: + g_value_set_boolean (value, self->dither); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_gradient_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object); + + switch (property_id) + { + case PROP_CONTEXT: + if (self->context) + g_object_unref (self->context); + + self->context = g_value_dup_object (value); + break; + + case PROP_GRADIENT: + { + GimpGradient *gradient = g_value_get_object (value); + + g_clear_object (&self->gradient); + + if (gradient) + { + if (gimp_gradient_has_fg_bg_segments (gradient)) + self->gradient = gimp_gradient_flatten (gradient, self->context); + else + self->gradient = g_object_ref (gradient); + } + + gimp_operation_gradient_invalidate_cache (self); + } + break; + + case PROP_START_X: + self->start_x = g_value_get_double (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_START_Y: + self->start_y = g_value_get_double (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_END_X: + self->end_x = g_value_get_double (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_END_Y: + self->end_y = g_value_get_double (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_GRADIENT_TYPE: + self->gradient_type = g_value_get_enum (value); + break; + + case PROP_GRADIENT_REPEAT: + self->gradient_repeat = g_value_get_enum (value); + break; + + case PROP_OFFSET: + self->offset = g_value_get_double (value); + break; + + case PROP_GRADIENT_REVERSE: + self->gradient_reverse = g_value_get_boolean (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_GRADIENT_BLEND_COLOR_SPACE: + self->gradient_blend_color_space = g_value_get_enum (value); + + gimp_operation_gradient_invalidate_cache (self); + break; + + case PROP_SUPERSAMPLE: + self->supersample = g_value_get_boolean (value); + break; + + case PROP_SUPERSAMPLE_DEPTH: + self->supersample_depth = g_value_get_int (value); + break; + + case PROP_SUPERSAMPLE_THRESHOLD: + self->supersample_threshold = g_value_get_double (value); + break; + + case PROP_DITHER: + self->dither = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_gradient_prepare (GeglOperation *operation) +{ + gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float")); +} + +static GeglRectangle +gimp_operation_gradient_get_bounding_box (GeglOperation *operation) +{ + return gegl_rectangle_infinite_plane (); +} + +static gdouble +gradient_calc_conical_sym_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else if ((x != 0) || (y != 0)) + { + gdouble vec[2]; + gdouble r; + gdouble rat; + + /* Calculate offset from the start in pixels */ + + r = sqrt (SQR (x) + SQR (y)); + + vec[0] = x / r; + vec[1] = y / r; + + rat = axis[0] * vec[0] + axis[1] * vec[1]; /* Dot product */ + + if (rat > 1.0) + rat = 1.0; + else if (rat < -1.0) + rat = -1.0; + + /* This cool idea is courtesy Josh MacDonald, + * Ali Rahimi --- two more XCF losers. */ + + rat = acos (rat) / G_PI; + rat = pow (rat, (offset / 10.0) + 1.0); + + return CLAMP (rat, 0.0, 1.0); + } + else + { + return 0.5; + } +} + +static gdouble +gradient_calc_conical_asym_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else if (x != 0 || y != 0) + { + gdouble ang0, ang1; + gdouble ang; + gdouble rat; + + ang0 = atan2 (axis[0], axis[1]) + G_PI; + + ang1 = atan2 (x, y) + G_PI; + + ang = ang1 - ang0; + + if (ang < 0.0) + ang += (2.0 * G_PI); + + rat = ang / (2.0 * G_PI); + rat = pow (rat, (offset / 10.0) + 1.0); + + return CLAMP (rat, 0.0, 1.0); + } + else + { + return 0.5; /* We are on middle point */ + } +} + +static gdouble +gradient_calc_square_factor (gdouble dist, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else + { + gdouble r; + gdouble rat; + + /* Calculate offset from start as a value in [0, 1] */ + + offset = offset / 100.0; + + r = MAX (fabs (x), fabs (y)); + rat = r / dist; + + if (rat < offset) + return 0.0; + else if (offset == 1.0) + return (rat >= 1.0) ? 1.0 : 0.0; + else + return (rat - offset) / (1.0 - offset); + } +} + +static gdouble +gradient_calc_radial_factor (gdouble dist, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else + { + gdouble r; + gdouble rat; + + /* Calculate radial offset from start as a value in [0, 1] */ + + offset = offset / 100.0; + + r = sqrt (SQR (x) + SQR (y)); + rat = r / dist; + + if (rat < offset) + return 0.0; + else if (offset == 1.0) + return (rat >= 1.0) ? 1.0 : 0.0; + else + return (rat - offset) / (1.0 - offset); + } +} + +static gdouble +gradient_calc_linear_factor (gdouble dist, + gdouble *vec, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else + { + gdouble r; + gdouble rat; + + offset = offset / 100.0; + + r = vec[0] * x + vec[1] * y; + rat = r / dist; + + if (rat >= 0.0 && rat < offset) + return 0.0; + else if (offset == 1.0) + return (rat >= 1.0) ? 1.0 : 0.0; + else if (rat < 0.0) + return rat / (1.0 - offset); + else + return (rat - offset) / (1.0 - offset); + } +} + +static gdouble +gradient_calc_bilinear_factor (gdouble dist, + gdouble *vec, + gdouble offset, + gdouble x, + gdouble y) +{ + if (dist == 0.0) + { + return 0.0; + } + else + { + gdouble r; + gdouble rat; + + /* Calculate linear offset from the start line outward */ + + offset = offset / 100.0; + + r = vec[0] * x + vec[1] * y; + rat = r / dist; + + if (fabs (rat) < offset) + return 0.0; + else if (offset == 1.0) + return (rat == 1.0) ? 1.0 : 0.0; + else + return (fabs (rat) - offset) / (1.0 - offset); + } +} + +static gdouble +gradient_calc_spiral_factor (gdouble dist, + gdouble *axis, + gdouble offset, + gdouble x, + gdouble y, + gboolean clockwise) +{ + if (dist == 0.0) + { + return 0.0; + } + else if (x != 0.0 || y != 0.0) + { + gdouble ang0, ang1; + gdouble ang; + double r; + + offset = offset / 100.0; + + ang0 = atan2 (axis[0], axis[1]) + G_PI; + ang1 = atan2 (x, y) + G_PI; + + if (clockwise) + ang = ang1 - ang0; + else + ang = ang0 - ang1; + + if (ang < 0.0) + ang += (2.0 * G_PI); + + r = sqrt (SQR (x) + SQR (y)) / dist; + + return fmod (ang / (2.0 * G_PI) + r + offset, 1.0); + } + else + { + return 0.5 ; /* We are on the middle point */ + } +} + +static gdouble +gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y) +{ + gfloat value; + + offset = offset / 100.0; + + gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE); + + value = 1.0 - value; + + if (value < offset) + value = 0.0; + else if (offset == 1.0) + value = (value >= 1.0) ? 1.0 : 0.0; + else + value = (value - offset) / (1.0 - offset); + + return value; +} + + +static gdouble +gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y) +{ + gfloat value; + + offset = 1.0 - offset / 100.0; + + gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE); + + if (value > offset) + value = 1.0; + else if (offset == 0.0) + value = (value <= 0.0) ? 0.0 : 1.0; + else + value = value / offset; + + value = 1.0 - sin (0.5 * G_PI * value); + + return value; +} + + +static gdouble +gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler, + gdouble offset, + gdouble x, + gdouble y) +{ + gfloat value; + + offset = 1.0 - offset / 100.0; + + gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE); + + if (value > offset) + value = 1.0; + else if (offset == 0.0) + value = (value <= 0.0) ? 0.0 : 1.0; + else + value = value / offset; + + value = cos (0.5 * G_PI * value); + + return value; +} + +static void +gradient_render_pixel (gdouble x, + gdouble y, + GimpRGB *color, + gpointer render_data) +{ + RenderBlendData *rbd = render_data; + gdouble factor; + + /* we want to calculate the color at the pixel's center */ + x += 0.5; + y += 0.5; + + /* Calculate blending factor */ + + switch (rbd->gradient_type) + { + case GIMP_GRADIENT_LINEAR: + factor = gradient_calc_linear_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_BILINEAR: + factor = gradient_calc_bilinear_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_RADIAL: + factor = gradient_calc_radial_factor (rbd->dist, + rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_SQUARE: + factor = gradient_calc_square_factor (rbd->dist, rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_CONICAL_SYMMETRIC: + factor = gradient_calc_conical_sym_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_CONICAL_ASYMMETRIC: + factor = gradient_calc_conical_asym_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy); + break; + + case GIMP_GRADIENT_SHAPEBURST_ANGULAR: + factor = gradient_calc_shapeburst_angular_factor (rbd->dist_sampler, + rbd->offset, + x, y); + break; + + case GIMP_GRADIENT_SHAPEBURST_SPHERICAL: + factor = gradient_calc_shapeburst_spherical_factor (rbd->dist_sampler, + rbd->offset, + x, y); + break; + + case GIMP_GRADIENT_SHAPEBURST_DIMPLED: + factor = gradient_calc_shapeburst_dimpled_factor (rbd->dist_sampler, + rbd->offset, + x, y); + break; + + case GIMP_GRADIENT_SPIRAL_CLOCKWISE: + factor = gradient_calc_spiral_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy, TRUE); + break; + + case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE: + factor = gradient_calc_spiral_factor (rbd->dist, + rbd->vec, rbd->offset, + x - rbd->sx, y - rbd->sy, FALSE); + break; + + default: + g_return_if_reached (); + break; + } + + /* Adjust for repeat */ + + switch (rbd->repeat) + { + case GIMP_REPEAT_NONE: + break; + + case GIMP_REPEAT_SAWTOOTH: + factor = factor - floor (factor); + break; + + case GIMP_REPEAT_TRIANGULAR: + { + guint ifactor; + + if (factor < 0.0) + factor = -factor; + + ifactor = (guint) factor; + factor = factor - floor (factor); + + if (ifactor & 1) + factor = 1.0 - factor; + } + break; + + case GIMP_REPEAT_TRUNCATE: + if (factor < 0.0 || factor > 1.0) + { + gimp_rgba_set (color, 0.0, 0.0, 0.0, 0.0); + return; + } + break; + } + + /* Blend the colors */ + + if (rbd->gradient_cache) + { + factor = CLAMP (factor, 0.0, 1.0); + + *color = + rbd->gradient_cache[ROUND (factor * (rbd->gradient_cache_size - 1))]; + } + else + { + rbd->last_seg = gimp_gradient_get_color_at (rbd->gradient, NULL, + rbd->last_seg, factor, + rbd->reverse, + rbd->blend_color_space, + color); + } +} + +static void +gradient_put_pixel (gint x, + gint y, + GimpRGB *color, + gpointer put_pixel_data) +{ + PutPixelData *ppd = put_pixel_data; + const gint index = (y - ppd->roi.y) * ppd->roi.width + (x - ppd->roi.x); + gfloat *dest = ppd->data + 4 * index; + + if (ppd->dither_rand) + { + gradient_dither_pixel (color, ppd->dither_rand, dest); + + dest += 4; + } + else + { + *dest++ = color->r; + *dest++ = color->g; + *dest++ = color->b; + *dest++ = color->a; + } +} + +static void +gradient_dither_pixel (GimpRGB *color, + GRand *dither_rand, + gfloat *dest) +{ + gfloat r, g, b, a; + guint i; + + i = g_rand_int (dither_rand); + + r = color->r + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8; + g = color->g + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8; + b = color->b + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8; + + if (color->a > 0.0 && color->a < 1.0) + a = color->a + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; + else + a = color->a; + + *dest++ = CLAMP (r, 0.0, 1.0); + *dest++ = CLAMP (g, 0.0, 1.0); + *dest++ = CLAMP (b, 0.0, 1.0); + *dest++ = CLAMP (a, 0.0, 1.0); +} + +static gboolean +gimp_operation_gradient_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *result, + gint level) +{ + GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (operation); + + const gdouble sx = self->start_x; + const gdouble sy = self->start_y; + const gdouble ex = self->end_x; + const gdouble ey = self->end_y; + + RenderBlendData rbd = { 0, }; + + GeglBufferIterator *iter; + GeglRectangle *roi; + GRand *dither_rand = NULL; + + if (! self->gradient) + return TRUE; + + gimp_operation_gradient_validate_cache (self); + + rbd.gradient = self->gradient; + rbd.reverse = self->gradient_reverse; + rbd.blend_color_space = self->gradient_blend_color_space; + rbd.gradient_cache = self->gradient_cache; + rbd.gradient_cache_size = self->gradient_cache_size; + + /* Calculate type-specific parameters */ + + switch (self->gradient_type) + { + case GIMP_GRADIENT_RADIAL: + rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy)); + break; + + case GIMP_GRADIENT_SQUARE: + rbd.dist = MAX (fabs (ex - sx), fabs (ey - sy)); + break; + + case GIMP_GRADIENT_CONICAL_SYMMETRIC: + case GIMP_GRADIENT_CONICAL_ASYMMETRIC: + case GIMP_GRADIENT_SPIRAL_CLOCKWISE: + case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE: + case GIMP_GRADIENT_LINEAR: + case GIMP_GRADIENT_BILINEAR: + rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy)); + + if (rbd.dist > 0.0) + { + rbd.vec[0] = (ex - sx) / rbd.dist; + rbd.vec[1] = (ey - sy) / rbd.dist; + } + + break; + + case GIMP_GRADIENT_SHAPEBURST_ANGULAR: + case GIMP_GRADIENT_SHAPEBURST_SPHERICAL: + case GIMP_GRADIENT_SHAPEBURST_DIMPLED: + rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy)); + rbd.dist_sampler = gegl_buffer_sampler_new_at_level ( + input, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, level); + break; + + default: + g_return_val_if_reached (FALSE); + break; + } + + /* Initialize render data */ + + rbd.offset = self->offset; + rbd.sx = self->start_x; + rbd.sy = self->start_y; + rbd.gradient_type = self->gradient_type; + rbd.repeat = self->gradient_repeat; + + /* Render the gradient! */ + + iter = gegl_buffer_iterator_new (output, result, 0, + babl_format ("R'G'B'A float"), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + roi = &iter->items[0].roi; + + if (self->dither) + dither_rand = g_rand_new (); + + if (self->supersample) + { + PutPixelData ppd; + + ppd.dither_rand = dither_rand; + + while (gegl_buffer_iterator_next (iter)) + { + ppd.data = iter->items[0].data; + ppd.roi = *roi; + + gimp_adaptive_supersample_area (roi->x, roi->y, + roi->x + roi->width - 1, + roi->y + roi->height - 1, + self->supersample_depth, + self->supersample_threshold, + gradient_render_pixel, &rbd, + gradient_put_pixel, &ppd, + NULL, + NULL); + } + } + else + { + while (gegl_buffer_iterator_next (iter)) + { + gfloat *dest = iter->items[0].data; + gint endx = roi->x + roi->width; + gint endy = roi->y + roi->height; + gint x, y; + + if (dither_rand) + { + for (y = roi->y; y < endy; y++) + for (x = roi->x; x < endx; x++) + { + GimpRGB color = { 0.0, 0.0, 0.0, 1.0 }; + + gradient_render_pixel (x, y, &color, &rbd); + gradient_dither_pixel (&color, dither_rand, dest); + + dest += 4; + } + } + else + { + for (y = roi->y; y < endy; y++) + for (x = roi->x; x < endx; x++) + { + GimpRGB color = { 0.0, 0.0, 0.0, 1.0 }; + + gradient_render_pixel (x, y, &color, &rbd); + + *dest++ = color.r; + *dest++ = color.g; + *dest++ = color.b; + *dest++ = color.a; + } + } + } + } + + if (self->dither) + g_rand_free (dither_rand); + + g_clear_object (&rbd.dist_sampler); + + return TRUE; +} + +static void +gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self) +{ + g_clear_pointer (&self->gradient_cache, g_free); +} + +static void +gimp_operation_gradient_validate_cache (GimpOperationGradient *self) +{ + GimpGradientSegment *last_seg = NULL; + gint cache_size; + gint i; + + if (! self->gradient) + return; + + g_mutex_lock (&self->gradient_cache_mutex); + + if (self->gradient_cache) + { + g_mutex_unlock (&self->gradient_cache_mutex); + + return; + } + + cache_size = ceil (hypot (self->start_x - self->end_x, + self->start_y - self->end_y)) * + GRADIENT_CACHE_N_SUPERSAMPLES; + + /* have at least two values in the cache */ + cache_size = MAX (cache_size, 2); + + /* don't use a cache if its necessary size is too big */ + if (cache_size > GRADIENT_CACHE_MAX_SIZE) + { + g_mutex_unlock (&self->gradient_cache_mutex); + + return; + } + + self->gradient_cache = g_new0 (GimpRGB, cache_size); + self->gradient_cache_size = cache_size; + + for (i = 0; i < self->gradient_cache_size; i++) + { + gdouble factor = (gdouble) i / (gdouble) (self->gradient_cache_size - 1); + + last_seg = gimp_gradient_get_color_at (self->gradient, NULL, last_seg, + factor, + self->gradient_reverse, + self->gradient_blend_color_space, + self->gradient_cache + i); + } + + g_mutex_unlock (&self->gradient_cache_mutex); +} diff --git a/app/operations/gimpoperationgradient.h b/app/operations/gimpoperationgradient.h new file mode 100644 index 0000000..e2aa19c --- /dev/null +++ b/app/operations/gimpoperationgradient.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgradient.h + * Copyright (C) 2014 Michael Henning + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_GRADIENT_H__ +#define __GIMP_OPERATION_GRADIENT_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_GRADIENT (gimp_operation_gradient_get_type ()) +#define GIMP_OPERATION_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradient)) +#define GIMP_OPERATION_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradientClass)) +#define GIMP_IS_OPERATION_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRADIENT)) +#define GIMP_IS_OPERATION_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRADIENT)) +#define GIMP_OPERATION_GRADIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradientClass)) + + +typedef struct _GimpOperationGradient GimpOperationGradient; +typedef struct _GimpOperationGradientClass GimpOperationGradientClass; + +struct _GimpOperationGradient +{ + GeglOperationFilter parent_instance; + + GimpContext *context; + + GimpGradient *gradient; + gdouble start_x, start_y, end_x, end_y; + GimpGradientType gradient_type; + GimpRepeatMode gradient_repeat; + gdouble offset; + gboolean gradient_reverse; + GimpGradientBlendColorSpace gradient_blend_color_space; + + gboolean supersample; + gint supersample_depth; + gdouble supersample_threshold; + + gboolean dither; + + GimpRGB *gradient_cache; + gint gradient_cache_size; + GMutex gradient_cache_mutex; +}; + +struct _GimpOperationGradientClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_gradient_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_GRADIENT_H__ */ diff --git a/app/operations/gimpoperationgrow.c b/app/operations/gimpoperationgrow.c new file mode 100644 index 0000000..2aabcee --- /dev/null +++ b/app/operations/gimpoperationgrow.c @@ -0,0 +1,391 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrow.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationgrow.h" + + +enum +{ + PROP_0, + PROP_RADIUS_X, + PROP_RADIUS_Y +}; + + +static void gimp_operation_grow_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_grow_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_grow_prepare (GeglOperation *operation); +static GeglRectangle + gimp_operation_grow_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static GeglRectangle + gimp_operation_grow_get_cached_region (GeglOperation *self, + const GeglRectangle *roi); + +static gboolean gimp_operation_grow_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationGrow, gimp_operation_grow, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_grow_parent_class + + +static void +gimp_operation_grow_class_init (GimpOperationGrowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_grow_set_property; + object_class->get_property = gimp_operation_grow_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:grow", + "categories", "gimp", + "description", "GIMP Grow operation", + NULL); + + operation_class->prepare = gimp_operation_grow_prepare; + operation_class->get_required_for_output = gimp_operation_grow_get_required_for_output; + operation_class->get_cached_region = gimp_operation_grow_get_cached_region; + operation_class->threaded = FALSE; + + filter_class->process = gimp_operation_grow_process; + + g_object_class_install_property (object_class, PROP_RADIUS_X, + g_param_spec_int ("radius-x", + "Radius X", + "Grow radius in X direction", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RADIUS_Y, + g_param_spec_int ("radius-y", + "Radius Y", + "Grow radius in Y direction", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_grow_init (GimpOperationGrow *self) +{ +} + +static void +gimp_operation_grow_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationGrow *self = GIMP_OPERATION_GROW (object); + + switch (property_id) + { + case PROP_RADIUS_X: + g_value_set_int (value, self->radius_x); + break; + + case PROP_RADIUS_Y: + g_value_set_int (value, self->radius_y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_grow_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationGrow *self = GIMP_OPERATION_GROW (object); + + switch (property_id) + { + case PROP_RADIUS_X: + self->radius_x = g_value_get_int (value); + break; + + case PROP_RADIUS_Y: + self->radius_y = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_grow_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space)); +} + +static GeglRectangle +gimp_operation_grow_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static GeglRectangle +gimp_operation_grow_get_cached_region (GeglOperation *self, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static void +compute_border (gint16 *circ, + guint16 xradius, + guint16 yradius) +{ + gint32 i; + gint32 diameter = xradius * 2 + 1; + gdouble tmp; + + for (i = 0; i < diameter; i++) + { + if (i > xradius) + tmp = (i - xradius) - 0.5; + else if (i < xradius) + tmp = (xradius - i) - 0.5; + else + tmp = 0.0; + + circ[i] = RINT (yradius / + (gdouble) xradius * sqrt (SQR (xradius) - SQR (tmp))); + } +} + +static inline void +rotate_pointers (gfloat **p, + guint32 n) +{ + guint32 i; + gfloat *tmp; + + tmp = p[0]; + + for (i = 0; i < n - 1; i++) + p[i] = p[i + 1]; + + p[i] = tmp; +} + +static gboolean +gimp_operation_grow_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + /* Any bugs in this function are probably also in thin_region. + * Blame all bugs in this function on jaycox@gimp.org + */ + GimpOperationGrow *self = GIMP_OPERATION_GROW (operation); + const Babl *input_format = gegl_operation_get_format (operation, "input"); + const Babl *output_format = gegl_operation_get_format (operation, "output"); + gint32 i, j, x, y; + gfloat **buf; /* caches the region's pixel data */ + gfloat *out; /* holds the new scan line we are computing */ + gfloat **max; /* caches the largest values for each column */ + gint16 *circ; /* holds the y coords of the filter's mask */ + gfloat last_max; + gint16 last_index; + gfloat *buffer; + + max = g_new (gfloat *, roi->width + 2 * self->radius_x); + buf = g_new (gfloat *, self->radius_y + 1); + + for (i = 0; i < self->radius_y + 1; i++) + buf[i] = g_new (gfloat, roi->width); + + buffer = g_new (gfloat, + (roi->width + 2 * self->radius_x) * (self->radius_y + 1)); + + for (i = 0; i < roi->width + 2 * self->radius_x; i++) + { + if (i < self->radius_x) + max[i] = buffer; + else if (i < roi->width + self->radius_x) + max[i] = &buffer[(self->radius_y + 1) * (i - self->radius_x)]; + else + max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x - 1)]; + + for (j = 0; j < self->radius_y + 1; j++) + max[i][j] = 0.0; + } + + /* offset the max pointer by self->radius_x so the range of the + * array is [-self->radius_x] to [roi->width + self->radius_x] + */ + max += self->radius_x; + + out = g_new (gfloat, roi->width); + + circ = g_new (gint16, 2 * self->radius_x + 1); + compute_border (circ, self->radius_x, self->radius_y); + + /* offset the circ pointer by self->radius_x so the range of the + * array is [-self->radius_x] to [self->radius_x] + */ + circ += self->radius_x; + + memset (buf[0], 0, roi->width * sizeof (gfloat)); + + for (i = 0; i < self->radius_y && i < roi->height; i++) /* load top of image */ + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + i, + roi->width, 1), + 1.0, input_format, buf[i + 1], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + for (x = 0; x < roi->width; x++) /* set up max for top of image */ + { + max[x][0] = 0.0; /* buf[0][x] is always 0 */ + max[x][1] = buf[1][x]; /* MAX (buf[1][x], max[x][0]) always = buf[1][x]*/ + + for (j = 2; j < self->radius_y + 1; j++) + max[x][j] = MAX (buf[j][x], max[x][j - 1]); + } + + for (y = 0; y < roi->height; y++) + { + rotate_pointers (buf, self->radius_y + 1); + + if (y < roi->height - (self->radius_y)) + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + y + self->radius_y, + roi->width, 1), + 1.0, input_format, buf[self->radius_y], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + else + memset (buf[self->radius_y], 0, roi->width * sizeof (gfloat)); + + for (x = 0; x < roi->width; x++) /* update max array */ + { + for (i = self->radius_y; i > 0; i--) + max[x][i] = MAX (MAX (max[x][i - 1], buf[i - 1][x]), buf[i][x]); + + max[x][0] = buf[0][x]; + } + + last_max = max[0][circ[-1]]; + last_index = 1; + + for (x = 0; x < roi->width; x++) /* render scan line */ + { + last_index--; + + if (last_index >= 0) + { + if (last_max >= 1.0) + { + out[x] = 1.0; + } + else + { + last_max = 0.0; + + for (i = self->radius_x; i >= 0; i--) + if (last_max < max[x + i][circ[i]]) + { + last_max = max[x + i][circ[i]]; + last_index = i; + } + + out[x] = last_max; + } + } + else + { + last_index = self->radius_x; + last_max = max[x + self->radius_x][circ[self->radius_x]]; + + for (i = self->radius_x - 1; i >= -self->radius_x; i--) + if (last_max < max[x + i][circ[i]]) + { + last_max = max[x + i][circ[i]]; + last_index = i; + } + + out[x] = last_max; + } + } + + gegl_buffer_set (output, + GEGL_RECTANGLE (roi->x, roi->y + y, + roi->width, 1), + 0, output_format, out, + GEGL_AUTO_ROWSTRIDE); + } + + /* undo the offsets to the pointers so we can free the malloced memory */ + circ -= self->radius_x; + max -= self->radius_x; + + g_free (circ); + g_free (buffer); + g_free (max); + + for (i = 0; i < self->radius_y + 1; i++) + g_free (buf[i]); + + g_free (buf); + g_free (out); + + return TRUE; +} diff --git a/app/operations/gimpoperationgrow.h b/app/operations/gimpoperationgrow.h new file mode 100644 index 0000000..d680997 --- /dev/null +++ b/app/operations/gimpoperationgrow.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrow.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_GROW_H__ +#define __GIMP_OPERATION_GROW_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_GROW (gimp_operation_grow_get_type ()) +#define GIMP_OPERATION_GROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GROW, GimpOperationGrow)) +#define GIMP_OPERATION_GROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GROW, GimpOperationGrowClass)) +#define GIMP_IS_OPERATION_GROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GROW)) +#define GIMP_IS_OPERATION_GROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GROW)) +#define GIMP_OPERATION_GROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GROW, GimpOperationGrowClass)) + + +typedef struct _GimpOperationGrow GimpOperationGrow; +typedef struct _GimpOperationGrowClass GimpOperationGrowClass; + +struct _GimpOperationGrow +{ + GeglOperationFilter parent_instance; + + gint radius_x; + gint radius_y; +}; + +struct _GimpOperationGrowClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_grow_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_GROW_H__ */ diff --git a/app/operations/gimpoperationhistogramsink.c b/app/operations/gimpoperationhistogramsink.c new file mode 100644 index 0000000..cb0a7ac --- /dev/null +++ b/app/operations/gimpoperationhistogramsink.c @@ -0,0 +1,244 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhistogramsink.c + * Copyright (C) 2012 Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "core/gimphistogram.h" + +#include "gimpoperationhistogramsink.h" + + +enum +{ + PROP_0, + PROP_AUX, + PROP_HISTOGRAM +}; + + +static void gimp_operation_histogram_sink_finalize (GObject *object); +static void gimp_operation_histogram_sink_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_histogram_sink_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_histogram_sink_attach (GeglOperation *operation); +static void gimp_operation_histogram_sink_prepare (GeglOperation *operation); +static GeglRectangle + gimp_operation_histogram_sink_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static gboolean gimp_operation_histogram_sink_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); + + +G_DEFINE_TYPE (GimpOperationHistogramSink, gimp_operation_histogram_sink, + GEGL_TYPE_OPERATION_SINK) + +#define parent_class gimp_operation_histogram_sink_parent_class + + +static void +gimp_operation_histogram_sink_class_init (GimpOperationHistogramSinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + + object_class->finalize = gimp_operation_histogram_sink_finalize; + object_class->set_property = gimp_operation_histogram_sink_set_property; + object_class->get_property = gimp_operation_histogram_sink_get_property; + + gegl_operation_class_set_keys (operation_class, + "name" , "gimp:histogram-sink", + "categories" , "color", + "description", "GIMP Histogram sink operation", + NULL); + + operation_class->attach = gimp_operation_histogram_sink_attach; + operation_class->prepare = gimp_operation_histogram_sink_prepare; + operation_class->get_required_for_output = gimp_operation_histogram_sink_get_required_for_output; + operation_class->process = gimp_operation_histogram_sink_process; + + g_object_class_install_property (object_class, PROP_AUX, + g_param_spec_object ("aux", + "Aux", + "Auxiliary image buffer input pad.", + GEGL_TYPE_BUFFER, + G_PARAM_READWRITE | + GEGL_PARAM_PAD_INPUT)); + + g_object_class_install_property (object_class, PROP_HISTOGRAM, + g_param_spec_object ("histogram", + "Histogram", + "The result histogram", + GIMP_TYPE_HISTOGRAM, + G_PARAM_READWRITE)); +} + +static void +gimp_operation_histogram_sink_init (GimpOperationHistogramSink *self) +{ +} + +static void +gimp_operation_histogram_sink_finalize (GObject *object) +{ + GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object); + + g_clear_object (&sink->histogram); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_histogram_sink_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object); + + switch (prop_id) + { + case PROP_AUX: + break; + + case PROP_HISTOGRAM: + g_value_set_pointer (value, sink->histogram); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gimp_operation_histogram_sink_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object); + + switch (prop_id) + { + case PROP_AUX: + break; + + case PROP_HISTOGRAM: + if (sink->histogram) + g_object_unref (sink->histogram); + sink->histogram = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gimp_operation_histogram_sink_attach (GeglOperation *self) +{ + GeglOperation *operation = GEGL_OPERATION (self); + GObjectClass *object_class = G_OBJECT_GET_CLASS (self); + + GEGL_OPERATION_CLASS (parent_class)->attach (self); + + gegl_operation_create_pad (operation, + g_object_class_find_property (object_class, + "aux")); +} + +static void +gimp_operation_histogram_sink_prepare (GeglOperation *operation) +{ + /* XXX gegl_operation_set_format (operation, "input", babl_format ("Y u8")); */ + gegl_operation_set_format (operation, "aux", babl_format ("Y float")); +} + +static GeglRectangle +gimp_operation_histogram_sink_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi) +{ + /* dunno what to do here, make a wild guess */ + return *roi; +} + +static gboolean +gimp_operation_histogram_sink_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GeglBuffer *input; + GeglBuffer *aux; + + if (strcmp (output_prop, "output")) + { + g_warning ("requested processing of %s pad on a sink", output_prop); + return FALSE; + } + + input = (GeglBuffer*) gegl_operation_context_dup_object (context, "input"); + aux = (GeglBuffer*) gegl_operation_context_dup_object (context, "aux"); + + if (! input) + { + g_warning ("received NULL input"); + + return FALSE; + } + + if (aux) + { + /* do hist with mask */ + + g_printerr ("aux format: %s\n", + babl_get_name (gegl_buffer_get_format (aux))); + + g_object_unref (aux); + } + else + { + /* without */ + } + + g_printerr ("input format: %s\n", + babl_get_name (gegl_buffer_get_format (input))); + + g_object_unref (input); + + return TRUE; +} diff --git a/app/operations/gimpoperationhistogramsink.h b/app/operations/gimpoperationhistogramsink.h new file mode 100644 index 0000000..719719c --- /dev/null +++ b/app/operations/gimpoperationhistogramsink.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhistogramsink.h + * Copyright (C) 2012 Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HISTOGRAM_SINK_H__ +#define __GIMP_OPERATION_HISTOGRAM_SINK_H__ + + +#include +#include + + +#define GIMP_TYPE_OPERATION_HISTOGRAM_SINK (gimp_operation_histogram_sink_get_type ()) +#define GIMP_OPERATION_HISTOGRAM_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSink)) +#define GIMP_OPERATION_HISTOGRAM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSinkClass)) +#define GEGL_IS_OPERATION_HISTOGRAM_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK)) +#define GEGL_IS_OPERATION_HISTOGRAM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HISTOGRAM_SINK)) +#define GIMP_OPERATION_HISTOGRAM_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSinkClass)) + + +typedef struct _GimpOperationHistogramSink GimpOperationHistogramSink; +typedef struct _GimpOperationHistogramSinkClass GimpOperationHistogramSinkClass; + +struct _GimpOperationHistogramSink +{ + GeglOperation parent_instance; + + GimpHistogram *histogram; +}; + +struct _GimpOperationHistogramSinkClass +{ + GeglOperationSinkClass parent_class; +}; + + +GType gimp_operation_histogram_sink_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HISTOGRAM_SINK_C__ */ diff --git a/app/operations/gimpoperationhuesaturation.c b/app/operations/gimpoperationhuesaturation.c new file mode 100644 index 0000000..cdfb7d7 --- /dev/null +++ b/app/operations/gimpoperationhuesaturation.c @@ -0,0 +1,302 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhuesaturation.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimphuesaturationconfig.h" +#include "gimpoperationhuesaturation.h" + +#include "gimp-intl.h" + + +static gboolean gimp_operation_hue_saturation_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHueSaturation, gimp_operation_hue_saturation, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_hue_saturation_parent_class + + +static void +gimp_operation_hue_saturation_class_init (GimpOperationHueSaturationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_point_filter_set_property; + object_class->get_property = gimp_operation_point_filter_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hue-saturation", + "categories", "color", + "description", _("Adjust hue, saturation, and lightness"), + NULL); + + point_class->process = gimp_operation_hue_saturation_process; + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "The config object", + GIMP_TYPE_HUE_SATURATION_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_hue_saturation_init (GimpOperationHueSaturation *self) +{ +} + +static inline gdouble +map_hue (GimpHueSaturationConfig *config, + GimpHueRange range, + gdouble value) +{ + value += (config->hue[GIMP_HUE_RANGE_ALL] + config->hue[range]) / 2.0; + + if (value < 0) + return value + 1.0; + else if (value > 1.0) + return value - 1.0; + else + return value; +} + +static inline gdouble +map_hue_overlap (GimpHueSaturationConfig *config, + GimpHueRange primary_range, + GimpHueRange secondary_range, + gdouble value, + gdouble primary_intensity, + gdouble secondary_intensity) +{ + /* When calculating an overlap between two ranges, interpolate the + * hue adjustment from config->hue[primary_range] and + * config->hue[secondary_range] BEFORE mapping it to the input + * value. This fixes odd edge cases where only one of the ranges + * crosses the red/magenta wraparound (bug #527085), or if + * adjustments to different channels yield more than 180 degree + * difference from each other. (Why anyone would do that is beyond + * me, but still.) + * + * See bugs #527085 and #644032 for examples of such cases. + */ + gdouble v = config->hue[primary_range] * primary_intensity + + config->hue[secondary_range] * secondary_intensity; + + value += (config->hue[GIMP_HUE_RANGE_ALL] + v) / 2.0; + + if (value < 0) + return value + 1.0; + else if (value > 1.0) + return value - 1.0; + else + return value; +} + +static inline gdouble +map_saturation (GimpHueSaturationConfig *config, + GimpHueRange range, + gdouble value) +{ + gdouble v = config->saturation[GIMP_HUE_RANGE_ALL] + config->saturation[range]; + + /* This change affects the way saturation is computed. With the old + * code (different code for value < 0), increasing the saturation + * affected muted colors very much, and bright colors less. With the + * new code, it affects muted colors and bright colors more or less + * evenly. For enhancing the color in photos, the new behavior is + * exactly what you want. It's hard for me to imagine a case in + * which the old behavior is better. + */ + value *= (v + 1.0); + + return CLAMP (value, 0.0, 1.0); +} + +static inline gdouble +map_lightness (GimpHueSaturationConfig *config, + GimpHueRange range, + gdouble value) +{ + gdouble v = (config->lightness[GIMP_HUE_RANGE_ALL] + config->lightness[range]) / 2.0; + + if (v < 0) + return value * (v + 1.0); + else + return value + (v * (1.0 - value)); +} + +static gboolean +gimp_operation_hue_saturation_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation); + GimpHueSaturationConfig *config = GIMP_HUE_SATURATION_CONFIG (point->config); + gfloat *src = in_buf; + gfloat *dest = out_buf; + gfloat overlap; + + if (! config) + return FALSE; + + overlap = config->overlap / 2.0; + + while (samples--) + { + GimpRGB rgb; + GimpHSL hsl; + gdouble h; + gint hue_counter; + gint hue = 0; + gint secondary_hue = 0; + gboolean use_secondary_hue = FALSE; + gfloat primary_intensity = 0.0; + gfloat secondary_intensity = 0.0; + + rgb.r = src[RED]; + rgb.g = src[GREEN]; + rgb.b = src[BLUE]; + rgb.a = src[ALPHA]; + + gimp_rgb_to_hsl (&rgb, &hsl); + + h = hsl.h * 6.0; + + for (hue_counter = 0; hue_counter < 7; hue_counter++) + { + gdouble hue_threshold = (gdouble) hue_counter + 0.5; + + if (h < ((gdouble) hue_threshold + overlap)) + { + hue = hue_counter; + + if (overlap > 0.0 && h > ((gdouble) hue_threshold - overlap)) + { + use_secondary_hue = TRUE; + + secondary_hue = hue_counter + 1; + + secondary_intensity = + (h - (gdouble) hue_threshold + overlap) / (2.0 * overlap); + + primary_intensity = 1.0 - secondary_intensity; + } + else + { + use_secondary_hue = FALSE; + } + + break; + } + } + + if (hue >= 6) + { + hue = 0; + use_secondary_hue = FALSE; + } + + if (secondary_hue >= 6) + { + secondary_hue = 0; + } + + /* transform into GimpHueRange values */ + hue++; + secondary_hue++; + + if (use_secondary_hue) + { + hsl.h = map_hue_overlap (config, hue, secondary_hue, hsl.h, + primary_intensity, secondary_intensity); + + hsl.s = (map_saturation (config, hue, hsl.s) * primary_intensity + + map_saturation (config, secondary_hue, hsl.s) * secondary_intensity); + + hsl.l = (map_lightness (config, hue, hsl.l) * primary_intensity + + map_lightness (config, secondary_hue, hsl.l) * secondary_intensity); + } + else + { + hsl.h = map_hue (config, hue, hsl.h); + hsl.s = map_saturation (config, hue, hsl.s); + hsl.l = map_lightness (config, hue, hsl.l); + } + + gimp_hsl_to_rgb (&hsl, &rgb); + + dest[RED] = rgb.r; + dest[GREEN] = rgb.g; + dest[BLUE] = rgb.b; + dest[ALPHA] = rgb.a; + + src += 4; + dest += 4; + } + + return TRUE; +} + + +/* public functions */ + +void +gimp_operation_hue_saturation_map (GimpHueSaturationConfig *config, + const GimpRGB *color, + GimpHueRange range, + GimpRGB *result) +{ + GimpHSL hsl; + + g_return_if_fail (GIMP_IS_HUE_SATURATION_CONFIG (config)); + g_return_if_fail (color != NULL); + g_return_if_fail (result != NULL); + + gimp_rgb_to_hsl (color, &hsl); + + hsl.h = map_hue (config, range, hsl.h); + hsl.s = map_saturation (config, range, hsl.s); + hsl.l = map_lightness (config, range, hsl.l); + + gimp_hsl_to_rgb (&hsl, result); +} diff --git a/app/operations/gimpoperationhuesaturation.h b/app/operations/gimpoperationhuesaturation.h new file mode 100644 index 0000000..b084bbe --- /dev/null +++ b/app/operations/gimpoperationhuesaturation.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhuesaturation.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HUE_SATURATION_H__ +#define __GIMP_OPERATION_HUE_SATURATION_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_HUE_SATURATION (gimp_operation_hue_saturation_get_type ()) +#define GIMP_OPERATION_HUE_SATURATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturation)) +#define GIMP_OPERATION_HUE_SATURATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturationClass)) +#define GIMP_IS_OPERATION_HUE_SATURATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION)) +#define GIMP_IS_OPERATION_HUE_SATURATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HUE_SATURATION)) +#define GIMP_OPERATION_HUE_SATURATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturationClass)) + + +typedef struct _GimpOperationHueSaturation GimpOperationHueSaturation; +typedef struct _GimpOperationHueSaturationClass GimpOperationHueSaturationClass; + +struct _GimpOperationHueSaturation +{ + GimpOperationPointFilter parent_instance; +}; + +struct _GimpOperationHueSaturationClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_hue_saturation_get_type (void) G_GNUC_CONST; + +void gimp_operation_hue_saturation_map (GimpHueSaturationConfig *config, + const GimpRGB *color, + GimpHueRange range, + GimpRGB *result); + + +#endif /* __GIMP_OPERATION_HUE_SATURATION_H__ */ diff --git a/app/operations/gimpoperationlevels.c b/app/operations/gimpoperationlevels.c new file mode 100644 index 0000000..1bf0b06 --- /dev/null +++ b/app/operations/gimpoperationlevels.c @@ -0,0 +1,208 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlevels.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimplevelsconfig.h" +#include "gimpoperationlevels.h" + +#include "gimp-intl.h" + + +static gboolean gimp_operation_levels_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationLevels, gimp_operation_levels, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_levels_parent_class + + +static void +gimp_operation_levels_class_init (GimpOperationLevelsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_point_filter_set_property; + object_class->get_property = gimp_operation_point_filter_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:levels", + "categories", "color", + "description", _("Adjust color levels"), + NULL); + + point_class->process = gimp_operation_levels_process; + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_LINEAR, + g_param_spec_boolean ("linear", + "Linear", + "Whether to operate on linear RGB", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG, + g_param_spec_object ("config", + "Config", + "The config object", + GIMP_TYPE_LEVELS_CONFIG, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_levels_init (GimpOperationLevels *self) +{ +} + +static inline gdouble +gimp_operation_levels_map (gdouble value, + gdouble low_input, + gdouble high_input, + gboolean clamp_input, + gdouble inv_gamma, + gdouble low_output, + gdouble high_output, + gboolean clamp_output) +{ + /* determine input intensity */ + if (high_input != low_input) + value = (value - low_input) / (high_input - low_input); + else + value = (value - low_input); + + if (clamp_input) + value = CLAMP (value, 0.0, 1.0); + + if (inv_gamma != 1.0 && value > 0) + value = pow (value, inv_gamma); + + /* determine the output intensity */ + if (high_output >= low_output) + value = value * (high_output - low_output) + low_output; + else if (high_output < low_output) + value = low_output - value * (low_output - high_output); + + if (clamp_output) + value = CLAMP (value, 0.0, 1.0); + + return value; +} + +static gboolean +gimp_operation_levels_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation); + GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (point->config); + gfloat *src = in_buf; + gfloat *dest = out_buf; + gfloat inv_gamma[5]; + gint channel; + + if (! config) + return FALSE; + + for (channel = 0; channel < 5; channel++) + { + g_return_val_if_fail (config->gamma[channel] != 0.0, FALSE); + + inv_gamma[channel] = 1.0 / config->gamma[channel]; + } + + while (samples--) + { + for (channel = 0; channel < 4; channel++) + { + gdouble value; + + value = gimp_operation_levels_map (src[channel], + config->low_input[channel + 1], + config->high_input[channel + 1], + config->clamp_input, + inv_gamma[channel + 1], + config->low_output[channel + 1], + config->high_output[channel + 1], + config->clamp_output); + + /* don't apply the overall curve to the alpha channel */ + if (channel != ALPHA) + value = gimp_operation_levels_map (value, + config->low_input[0], + config->high_input[0], + config->clamp_input, + inv_gamma[0], + config->low_output[0], + config->high_output[0], + config->clamp_output); + + dest[channel] = value; + } + + src += 4; + dest += 4; + } + + return TRUE; +} + + +/* public functions */ + +gdouble +gimp_operation_levels_map_input (GimpLevelsConfig *config, + GimpHistogramChannel channel, + gdouble value) +{ + g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), 0.0); + + /* determine input intensity */ + if (config->high_input[channel] != config->low_input[channel]) + value = ((value - config->low_input[channel]) / + (config->high_input[channel] - config->low_input[channel])); + else + value = (value - config->low_input[channel]); + + if (config->gamma[channel] != 0.0 && value > 0.0) + value = pow (value, 1.0 / config->gamma[channel]); + + return value; +} diff --git a/app/operations/gimpoperationlevels.h b/app/operations/gimpoperationlevels.h new file mode 100644 index 0000000..d977f42 --- /dev/null +++ b/app/operations/gimpoperationlevels.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlevels.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_LEVELS_H__ +#define __GIMP_OPERATION_LEVELS_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_LEVELS (gimp_operation_levels_get_type ()) +#define GIMP_OPERATION_LEVELS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevels)) +#define GIMP_OPERATION_LEVELS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevelsClass)) +#define GIMP_IS_OPERATION_LEVELS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LEVELS)) +#define GIMP_IS_OPERATION_LEVELS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LEVELS)) +#define GIMP_OPERATION_LEVELS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevelsClass)) + + +typedef struct _GimpOperationLevels GimpOperationLevels; +typedef struct _GimpOperationLevelsClass GimpOperationLevelsClass; + +struct _GimpOperationLevels +{ + GimpOperationPointFilter parent_instance; +}; + +struct _GimpOperationLevelsClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_levels_get_type (void) G_GNUC_CONST; + +gdouble gimp_operation_levels_map_input (GimpLevelsConfig *config, + GimpHistogramChannel channel, + gdouble value); + + +#endif /* __GIMP_OPERATION_LEVELS_H__ */ diff --git a/app/operations/gimpoperationmaskcomponents.cc b/app/operations/gimpoperationmaskcomponents.cc new file mode 100644 index 0000000..779dfac --- /dev/null +++ b/app/operations/gimpoperationmaskcomponents.cc @@ -0,0 +1,586 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmaskcomponents.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +extern "C" +{ + +#include "operations-types.h" + +#include "gimpoperationmaskcomponents.h" + +} /* extern "C" */ + + +enum +{ + PROP_0, + PROP_MASK, + PROP_ALPHA +}; + + +static void gimp_operation_mask_components_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_mask_components_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_mask_components_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_mask_components_get_bounding_box (GeglOperation *operation); +static gboolean gimp_operation_mask_components_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); + +static gboolean gimp_operation_mask_components_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationMaskComponents, gimp_operation_mask_components, + GEGL_TYPE_OPERATION_POINT_COMPOSER) + +#define parent_class gimp_operation_mask_components_parent_class + + +static void +gimp_operation_mask_components_class_init (GimpOperationMaskComponentsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointComposerClass *point_class = GEGL_OPERATION_POINT_COMPOSER_CLASS (klass); + + object_class->set_property = gimp_operation_mask_components_set_property; + object_class->get_property = gimp_operation_mask_components_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:mask-components", + "categories", "gimp", + "description", "Selectively pick components from src or aux", + NULL); + + operation_class->prepare = gimp_operation_mask_components_prepare; + operation_class->get_bounding_box = gimp_operation_mask_components_get_bounding_box; + operation_class->process = gimp_operation_mask_components_parent_process; + + point_class->process = gimp_operation_mask_components_process; + + g_object_class_install_property (object_class, PROP_MASK, + g_param_spec_flags ("mask", + "Mask", + "The component mask", + GIMP_TYPE_COMPONENT_MASK, + GIMP_COMPONENT_MASK_ALL, + (GParamFlags) ( + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT))); + + g_object_class_install_property (object_class, PROP_ALPHA, + g_param_spec_double ("alpha", + "Alpha", + "The masked-in alpha value when there's no aux input", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + (GParamFlags) ( + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT))); +} + +static void +gimp_operation_mask_components_init (GimpOperationMaskComponents *self) +{ +} + +static void +gimp_operation_mask_components_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (object); + + switch (property_id) + { + case PROP_MASK: + g_value_set_flags (value, self->mask); + break; + + case PROP_ALPHA: + g_value_set_double (value, self->alpha); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_mask_components_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (object); + + switch (property_id) + { + case PROP_MASK: + self->mask = (GimpComponentMask) g_value_get_flags (value); + break; + + case PROP_ALPHA: + self->alpha = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static guint32 +get_alpha_value (const Babl *format, + gfloat alpha) +{ + switch (babl_format_get_bytes_per_pixel (format)) + { + #define DEF_CASE(bpp, type) \ + case bpp: \ + { \ + type alpha_value; \ + \ + babl_process ( \ + babl_fish (babl_format_n (babl_type ("float"), 1), \ + babl_format_n (babl_format_get_type (format, 0), 1)), \ + &alpha, &alpha_value, 1); \ + \ + return alpha_value; \ + } + + DEF_CASE ( 4, guint8) + DEF_CASE ( 8, guint16) + DEF_CASE (16, guint32) + + #undef DEF_CASE + + default: + g_return_val_if_reached (0); + } +} + +template +struct ProcessGeneric +{ + static void + process (gconstpointer in_buf, + gconstpointer aux_buf, + gpointer out_buf, + gint n, + GimpComponentMask mask, + T alpha_value) + { + T *out = (T *) out_buf; + gint i; + gint c; + + if (aux_buf) + { + const T *in[4]; + + for (c = 0; c < 4; c++) + { + if (mask & (1 << c)) + in[c] = (const T *) aux_buf + c; + else + in[c] = (const T *) in_buf + c; + } + + for (i = 0; i < n; i++) + { + for (c = 0; c < 4; c++) + { + out[c] = *in[c]; + + in[c] += 4; + } + + out += 4; + } + } + else + { + const T *in = (const T*) in_buf; + + for (i = 0; i < n; i++) + { + for (c = 0; c < 3; c++) + { + if (mask & (1 << c)) + out[c] = 0; + else + out[c] = in[c]; + } + + if (mask & (1 << 3)) + out[3] = alpha_value; + else + out[3] = in[3]; + + in += 4; + out += 4; + } + } + } +}; + +template +struct Process : ProcessGeneric +{ +}; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + +template <> +struct Process +{ + static void + process (gconstpointer in_buf, + gconstpointer aux_buf, + gpointer out_buf, + gint n, + GimpComponentMask mask, + guint8 alpha_value) + { + const guint32 *in; + guint32 *out; + guint32 in_mask = 0; + gint i; + gint c; + + if (((guintptr) in_buf | (guintptr) aux_buf | (guintptr) out_buf) % 4) + { + ProcessGeneric::process (in_buf, aux_buf, out_buf, n, + mask, alpha_value); + + return; + } + + in = (const guint32 *) in_buf; + out = (guint32 *) out_buf; + + for (c = 0; c < 4; c++) + { + if (! (mask & (1 << c))) + in_mask |= 0xff << (8 * c); + } + + if (aux_buf) + { + const guint32 *aux = (const guint32 *) aux_buf; + guint32 aux_mask = ~in_mask; + + for (i = 0; i < n; i++) + { + *out = (*in & in_mask) | (*aux & aux_mask); + + in++; + aux++; + out++; + } + } + else + { + if (! (mask & GIMP_COMPONENT_MASK_ALPHA) || ! alpha_value) + { + for (i = 0; i < n; i++) + { + *out = *in & in_mask; + + in++; + out++; + } + } + else + { + guint32 alpha_mask = alpha_value << 24; + + for (i = 0; i < n; i++) + { + *out = (*in & in_mask) | alpha_mask; + + in++; + out++; + } + } + } + } +}; + +#endif /* G_BYTE_ORDER == G_LITTLE_ENDIAN */ + +template +static gboolean +gimp_operation_mask_components_process (GimpOperationMaskComponents *self, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + Process::process (in_buf, aux_buf, out_buf, samples, + self->mask, self->alpha_value); + + return TRUE; +} + +static void +gimp_operation_mask_components_prepare (GeglOperation *operation) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + const Babl *format; + + format = gimp_operation_mask_components_get_format ( + gegl_operation_get_source_format (operation, "input")); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "aux", format); + gegl_operation_set_format (operation, "output", format); + + if (format != self->format) + { + self->format = format; + + self->alpha_value = get_alpha_value (format, self->alpha); + + switch (babl_format_get_bytes_per_pixel (format)) + { + case 4: + self->process = (gpointer) + gimp_operation_mask_components_process; + break; + + case 8: + self->process = (gpointer) + gimp_operation_mask_components_process; + break; + + case 16: + self->process = (gpointer) + gimp_operation_mask_components_process; + break; + + default: + g_return_if_reached (); + } + } +} + +static GeglRectangle +gimp_operation_mask_components_get_bounding_box (GeglOperation *operation) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + GeglRectangle *in_rect; + GeglRectangle *aux_rect; + GeglRectangle result = {}; + + in_rect = gegl_operation_source_get_bounding_box (operation, "input"); + aux_rect = gegl_operation_source_get_bounding_box (operation, "aux"); + + if (self->mask == 0) + { + if (in_rect) + return *in_rect; + } + else if (self->mask == GIMP_COMPONENT_MASK_ALL) + { + if (aux_rect) + return *aux_rect; + } + + if (in_rect) + gegl_rectangle_bounding_box (&result, &result, in_rect); + + if (aux_rect) + gegl_rectangle_bounding_box (&result, &result, aux_rect); + + return result; +} + +static gboolean +gimp_operation_mask_components_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + + if (self->mask == 0) + { + GObject *input = gegl_operation_context_get_object (context, "input"); + + gegl_operation_context_set_object (context, "output", input); + + return TRUE; + } + else if (self->mask == GIMP_COMPONENT_MASK_ALL) + { + GObject *aux = gegl_operation_context_get_object (context, "aux"); + + /* when there's no aux and the alpha component is masked-in, we set the + * result's alpha component to the value of the "alpha" property; if it + * doesn't equal 0, we can't forward an empty aux. + */ + if (aux || ! self->alpha_value) + { + gegl_operation_context_set_object (context, "output", aux); + + return TRUE; + } + } + + return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, + output_prop, result, + level); +} + +static gboolean +gimp_operation_mask_components_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + typedef gboolean (* ProcessFunc) (GimpOperationMaskComponents *self, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + GimpOperationMaskComponents *self = (GimpOperationMaskComponents *) operation; + + return ((ProcessFunc) self->process) (self, + in_buf, aux_buf, out_buf, samples, + roi, level); +} + +const Babl * +gimp_operation_mask_components_get_format (const Babl *input_format) +{ + const Babl *format = NULL; + + if (input_format) + { + const Babl *model = babl_format_get_model (input_format); + const gchar *model_name = babl_get_name (model); + const Babl *type = babl_format_get_type (input_format, 0); + const gchar *type_name = babl_get_name (type); + + if (! strcmp (model_name, "Y") || + ! strcmp (model_name, "YA") || + ! strcmp (model_name, "RGB") || + ! strcmp (model_name, "RGBA")) + { + if (! strcmp (type_name, "u8")) + format = babl_format ("RGBA u8"); + else if (! strcmp (type_name, "u16")) + format = babl_format ("RGBA u16"); + else if (! strcmp (type_name, "u32")) + format = babl_format ("RGBA u32"); + else if (! strcmp (type_name, "half")) + format = babl_format ("RGBA half"); + else if (! strcmp (type_name, "float")) + format = babl_format ("RGBA float"); + } + else if (! strcmp (model_name, "Y'") || + ! strcmp (model_name, "Y'A") || + ! strcmp (model_name, "R'G'B'") || + ! strcmp (model_name, "R'G'B'A") || + babl_format_is_palette (input_format)) + { + if (! strcmp (type_name, "u8")) + format = babl_format ("R'G'B'A u8"); + else if (! strcmp (type_name, "u16")) + format = babl_format ("R'G'B'A u16"); + else if (! strcmp (type_name, "u32")) + format = babl_format ("R'G'B'A u32"); + else if (! strcmp (type_name, "half")) + format = babl_format ("R'G'B'A half"); + else if (! strcmp (type_name, "float")) + format = babl_format ("R'G'B'A float"); + } + } + + if (! format) + format = babl_format ("RGBA float"); + + return format; +} + +void +gimp_operation_mask_components_process (const Babl *format, + gconstpointer in, + gconstpointer aux, + gpointer out, + gint n, + GimpComponentMask mask) +{ + g_return_if_fail (format != NULL); + g_return_if_fail (in != NULL); + g_return_if_fail (out != NULL); + g_return_if_fail (n >= 0); + + switch (babl_format_get_bytes_per_pixel (format)) + { + case 4: + Process::process (in, aux, out, n, mask, 0); + break; + + case 8: + Process::process (in, aux, out, n, mask, 0); + break; + + case 16: + Process::process (in, aux, out, n, mask, 0); + break; + + default: + g_return_if_reached (); + } +} diff --git a/app/operations/gimpoperationmaskcomponents.h b/app/operations/gimpoperationmaskcomponents.h new file mode 100644 index 0000000..0919a38 --- /dev/null +++ b/app/operations/gimpoperationmaskcomponents.h @@ -0,0 +1,68 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmaskcomponents.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_MASK_COMPONENTS_H__ +#define __GIMP_OPERATION_MASK_COMPONENTS_H__ + +#include + + +#define GIMP_TYPE_OPERATION_MASK_COMPONENTS (gimp_operation_mask_components_get_type ()) +#define GIMP_OPERATION_MASK_COMPONENTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponents)) +#define GIMP_OPERATION_MASK_COMPONENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponentsClass)) +#define GIMP_IS_OPERATION_MASK_COMPONENTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS)) +#define GIMP_IS_OPERATION_MASK_COMPONENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MASK_COMPONENTS)) +#define GIMP_OPERATION_MASK_COMPONENTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponentsClass)) + + +typedef struct _GimpOperationMaskComponents GimpOperationMaskComponents; +typedef struct _GimpOperationMaskComponentsClass GimpOperationMaskComponentsClass; + +struct _GimpOperationMaskComponents +{ + GeglOperationPointComposer parent_instance; + + GimpComponentMask mask; + gdouble alpha; + + guint32 alpha_value; + gpointer process; + const Babl *format; +}; + +struct _GimpOperationMaskComponentsClass +{ + GeglOperationPointComposerClass parent_class; +}; + + +GType gimp_operation_mask_components_get_type (void) G_GNUC_CONST; + +const Babl * gimp_operation_mask_components_get_format (const Babl *input_format); + +void gimp_operation_mask_components_process (const Babl *format, + gconstpointer in, + gconstpointer aux, + gpointer out, + gint n, + GimpComponentMask mask); + + +#endif /* __GIMP_OPERATION_MASK_COMPONENTS_H__ */ diff --git a/app/operations/gimpoperationoffset.c b/app/operations/gimpoperationoffset.c new file mode 100644 index 0000000..ee269ed --- /dev/null +++ b/app/operations/gimpoperationoffset.c @@ -0,0 +1,497 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationoffset.c + * Copyright (C) 2019 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "operations-types.h" + +#include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimpcontext.h" + +#include "gimpoperationoffset.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CONTEXT, + PROP_TYPE, + PROP_X, + PROP_Y +}; + + +static void gimp_operation_offset_dispose (GObject *object); +static void gimp_operation_offset_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_offset_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static GeglRectangle gimp_operation_offset_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *output_roi); +static GeglRectangle gimp_operation_offset_get_invalidated_by_change (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *input_roi); +static void gimp_operation_offset_prepare (GeglOperation *operation); +static gboolean gimp_operation_offset_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level); + +static gboolean gimp_operation_offset_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + +static void gimp_operation_offset_get_offset (GimpOperationOffset *offset, + gboolean invert, + gint *x, + gint *y); +static void gimp_operation_offset_get_rect (GimpOperationOffset *offset, + gboolean invert, + const GeglRectangle *roi, + GeglRectangle *rect); + + +G_DEFINE_TYPE (GimpOperationOffset, gimp_operation_offset, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_offset_parent_class + + +static void +gimp_operation_offset_class_init (GimpOperationOffsetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + object_class->dispose = gimp_operation_offset_dispose; + object_class->set_property = gimp_operation_offset_set_property; + object_class->get_property = gimp_operation_offset_get_property; + + operation_class->get_required_for_output = gimp_operation_offset_get_required_for_output; + operation_class->get_invalidated_by_change = gimp_operation_offset_get_invalidated_by_change; + operation_class->prepare = gimp_operation_offset_prepare; + operation_class->process = gimp_operation_offset_parent_process; + + operation_class->threaded = FALSE; + operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER; + + filter_class->process = gimp_operation_offset_process; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:offset", + "categories", "transform", + "description", _("Shift the pixels, optionally wrapping them at the borders"), + NULL); + + g_object_class_install_property (object_class, PROP_CONTEXT, + g_param_spec_object ("context", + "Context", + "A GimpContext", + GIMP_TYPE_CONTEXT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", + "Type", + "Offset type", + GIMP_TYPE_OFFSET_TYPE, + GIMP_OFFSET_WRAP_AROUND, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_int ("x", + "X Offset", + "X offset", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_int ("y", + "Y Offset", + "Y offset", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_offset_init (GimpOperationOffset *self) +{ +} + +static void +gimp_operation_offset_dispose (GObject *object) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object); + + g_clear_object (&offset->context); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_operation_offset_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object); + + switch (property_id) + { + case PROP_CONTEXT: + g_value_set_object (value, offset->context); + break; + + case PROP_TYPE: + g_value_set_enum (value, offset->type); + break; + + case PROP_X: + g_value_set_int (value, offset->x); + break; + + case PROP_Y: + g_value_set_int (value, offset->y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_offset_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object); + + switch (property_id) + { + case PROP_CONTEXT: + g_set_object (&offset->context, g_value_get_object (value)); + break; + + case PROP_TYPE: + offset->type = g_value_get_enum (value); + break; + + case PROP_X: + offset->x = g_value_get_int (value); + break; + + case PROP_Y: + offset->y = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GeglRectangle +gimp_operation_offset_get_required_for_output (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *output_roi) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation); + GeglRectangle rect; + + gimp_operation_offset_get_rect (offset, TRUE, output_roi, &rect); + + return rect; +} + +static GeglRectangle +gimp_operation_offset_get_invalidated_by_change (GeglOperation *operation, + const gchar *input_pad, + const GeglRectangle *input_roi) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation); + GeglRectangle rect; + + gimp_operation_offset_get_rect (offset, FALSE, input_roi, &rect); + + return rect; +} + +static void +gimp_operation_offset_prepare (GeglOperation *operation) +{ + const Babl *format; + + format = gegl_operation_get_source_format (operation, "input"); + + if (! format) + format = babl_format ("RGBA float"); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "output", format); +} + +static gboolean +gimp_operation_offset_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_pad, + const GeglRectangle *result, + gint level) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation); + GObject *input; + gint x; + gint y; + + input = gegl_operation_context_get_object (context, "input"); + + gimp_operation_offset_get_offset (offset, FALSE, &x, &y); + + if (x == 0 && y == 0) + { + gegl_operation_context_set_object (context, "output", input); + + return TRUE; + } + else if (offset->type == GIMP_OFFSET_TRANSPARENT || + (offset->type == GIMP_OFFSET_BACKGROUND && + ! offset->context)) + { + GObject *output = NULL; + + if (input) + { + GeglRectangle bounds; + GeglRectangle extent; + + bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset)); + + extent = *gegl_buffer_get_extent (GEGL_BUFFER (input)); + + extent.x += x; + extent.y += y; + + if (gegl_rectangle_intersect (&extent, &extent, &bounds)) + { + output = g_object_new (GEGL_TYPE_BUFFER, + "source", input, + "x", extent.x, + "y", extent.y, + "width", extent.width, + "height", extent.height, + "shift-x", -x, + "shift-y", -y, + NULL); + + if (gegl_object_get_has_forked (input)) + gegl_object_set_has_forked (output); + } + } + + gegl_operation_context_take_object (context, "output", output); + + return TRUE; + } + + return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, + output_pad, result, + level); +} + +static gboolean +gimp_operation_offset_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation); + GeglColor *color = NULL; + GeglRectangle bounds; + gint x; + gint y; + gint i; + + bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset)); + + gimp_operation_offset_get_offset (offset, FALSE, &x, &y); + + if (offset->type == GIMP_OFFSET_BACKGROUND && offset->context) + { + GimpRGB bg; + + gimp_context_get_background (offset->context, &bg); + + color = gimp_gegl_color_new (&bg); + } + + for (i = 0; i < 4; i++) + { + GeglRectangle offset_bounds = bounds; + gint offset_x = x; + gint offset_y = y; + + if (i & 1) + offset_x += x < 0 ? bounds.width : -bounds.width; + if (i & 2) + offset_y += y < 0 ? bounds.height : -bounds.height; + + offset_bounds.x += offset_x; + offset_bounds.y += offset_y; + + if (gegl_rectangle_intersect (&offset_bounds, &offset_bounds, roi)) + { + if (i == 0 || offset->type == GIMP_OFFSET_WRAP_AROUND) + { + GeglRectangle offset_roi = offset_bounds; + + offset_roi.x -= offset_x; + offset_roi.y -= offset_y; + + gimp_gegl_buffer_copy (input, &offset_roi, GEGL_ABYSS_NONE, + output, &offset_bounds); + } + else if (color) + { + gegl_buffer_set_color (output, &offset_bounds, color); + } + } + } + + g_clear_object (&color); + + return TRUE; +} + +static void +gimp_operation_offset_get_offset (GimpOperationOffset *offset, + gboolean invert, + gint *x, + gint *y) +{ + GeglRectangle bounds; + + bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset)); + + if (gegl_rectangle_is_empty (&bounds)) + { + *x = 0; + *y = 0; + + return; + } + + *x = offset->x; + *y = offset->y; + + if (invert) + { + *x = -*x; + *y = -*y; + } + + if (offset->type == GIMP_OFFSET_WRAP_AROUND) + { + *x %= bounds.width; + + if (*x < 0) + *x += bounds.width; + + *y %= bounds.height; + + if (*y < 0) + *y += bounds.height; + } + else + { + *x = CLAMP (*x, -bounds.width, +bounds.width); + *y = CLAMP (*y, -bounds.height, +bounds.height); + } +} + +static void +gimp_operation_offset_get_rect (GimpOperationOffset *offset, + gboolean invert, + const GeglRectangle *roi, + GeglRectangle *rect) +{ + GeglRectangle bounds; + gint x; + gint y; + + bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset)); + + if (gegl_rectangle_is_empty (&bounds)) + { + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; + + return; + } + + gimp_operation_offset_get_offset (offset, invert, &x, &y); + + *rect = *roi; + + rect->x += x; + rect->y += y; + + if (offset->type == GIMP_OFFSET_WRAP_AROUND) + { + if (rect->x + rect->width > bounds.x + bounds.width) + { + rect->x = bounds.x; + rect->width = bounds.width; + } + + if (rect->y + rect->height > bounds.y + bounds.height) + { + rect->y = bounds.y; + rect->height = bounds.height; + } + } + + gegl_rectangle_intersect (rect, rect, &bounds); +} diff --git a/app/operations/gimpoperationoffset.h b/app/operations/gimpoperationoffset.h new file mode 100644 index 0000000..dcd0e0b --- /dev/null +++ b/app/operations/gimpoperationoffset.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationoffset.h + * Copyright (C) 2019 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 . + */ + +#ifndef __GIMP_OPERATION_OFFSET_H__ +#define __GIMP_OPERATION_OFFSET_H__ + + +#define GIMP_TYPE_OPERATION_OFFSET (gimp_operation_offset_get_type ()) +#define GIMP_OPERATION_OFFSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffset)) +#define GIMP_OPERATION_OFFSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffsetClass)) +#define GIMP_IS_OPERATION_OFFSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_OFFSET)) +#define GIMP_IS_OPERATION_OFFSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_OFFSET)) +#define GIMP_OPERATION_OFFSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffsetClass)) + + +typedef struct _GimpOperationOffset GimpOperationOffset; +typedef struct _GimpOperationOffsetClass GimpOperationOffsetClass; + +struct _GimpOperationOffset +{ + GeglOperationFilter parent_instance; + + GimpContext *context; + GimpOffsetType type; + gint x; + gint y; +}; + +struct _GimpOperationOffsetClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_offset_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_OFFSET_H__ */ diff --git a/app/operations/gimpoperationpointfilter.c b/app/operations/gimpoperationpointfilter.c new file mode 100644 index 0000000..b9bfa1d --- /dev/null +++ b/app/operations/gimpoperationpointfilter.c @@ -0,0 +1,131 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationpointfilter.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "gimpoperationpointfilter.h" + + +static void gimp_operation_point_filter_finalize (GObject *object); +static void gimp_operation_point_filter_prepare (GeglOperation *operation); + + +G_DEFINE_ABSTRACT_TYPE (GimpOperationPointFilter, gimp_operation_point_filter, + GEGL_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_point_filter_parent_class + + +static void +gimp_operation_point_filter_class_init (GimpOperationPointFilterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + + object_class->finalize = gimp_operation_point_filter_finalize; + + operation_class->prepare = gimp_operation_point_filter_prepare; +} + +static void +gimp_operation_point_filter_init (GimpOperationPointFilter *self) +{ +} + +static void +gimp_operation_point_filter_finalize (GObject *object) +{ + GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object); + + g_clear_object (&self->config); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +void +gimp_operation_point_filter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object); + + switch (property_id) + { + case GIMP_OPERATION_POINT_FILTER_PROP_LINEAR: + g_value_set_boolean (value, self->linear); + break; + + case GIMP_OPERATION_POINT_FILTER_PROP_CONFIG: + g_value_set_object (value, self->config); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gimp_operation_point_filter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object); + + switch (property_id) + { + case GIMP_OPERATION_POINT_FILTER_PROP_LINEAR: + self->linear = g_value_get_boolean (value); + break; + + case GIMP_OPERATION_POINT_FILTER_PROP_CONFIG: + if (self->config) + g_object_unref (self->config); + self->config = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_point_filter_prepare (GeglOperation *operation) +{ + GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (operation); + const Babl *space = gegl_operation_get_source_space (operation, + "input"); + const Babl *format; + + if (self->linear) + format = babl_format_with_space ("RGBA float", space); + else + format = babl_format_with_space ("R'G'B'A float", space); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "output", format); +} diff --git a/app/operations/gimpoperationpointfilter.h b/app/operations/gimpoperationpointfilter.h new file mode 100644 index 0000000..f039af7 --- /dev/null +++ b/app/operations/gimpoperationpointfilter.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationpointfilter.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_POINT_FILTER_H__ +#define __GIMP_OPERATION_POINT_FILTER_H__ + + +#include +#include + + +enum +{ + GIMP_OPERATION_POINT_FILTER_PROP_0, + GIMP_OPERATION_POINT_FILTER_PROP_LINEAR, + GIMP_OPERATION_POINT_FILTER_PROP_CONFIG +}; + + +#define GIMP_TYPE_OPERATION_POINT_FILTER (gimp_operation_point_filter_get_type ()) +#define GIMP_OPERATION_POINT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilter)) +#define GIMP_OPERATION_POINT_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilterClass)) +#define GIMP_IS_OPERATION_POINT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_POINT_FILTER)) +#define GIMP_IS_OPERATION_POINT_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_POINT_FILTER)) +#define GIMP_OPERATION_POINT_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilterClass)) + + +typedef struct _GimpOperationPointFilterClass GimpOperationPointFilterClass; + +struct _GimpOperationPointFilter +{ + GeglOperationPointFilter parent_instance; + + gboolean linear; + GObject *config; +}; + +struct _GimpOperationPointFilterClass +{ + GeglOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_point_filter_get_type (void) G_GNUC_CONST; + +void gimp_operation_point_filter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +void gimp_operation_point_filter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +#endif /* __GIMP_OPERATION_POINT_FILTER_H__ */ diff --git a/app/operations/gimpoperationposterize.c b/app/operations/gimpoperationposterize.c new file mode 100644 index 0000000..88f5d26 --- /dev/null +++ b/app/operations/gimpoperationposterize.c @@ -0,0 +1,165 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationposterize.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationposterize.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_LEVELS +}; + + +static void gimp_operation_posterize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_posterize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_operation_posterize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationPosterize, gimp_operation_posterize, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_posterize_parent_class + + +static void +gimp_operation_posterize_class_init (GimpOperationPosterizeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_posterize_set_property; + object_class->get_property = gimp_operation_posterize_get_property; + + point_class->process = gimp_operation_posterize_process; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:posterize", + "categories", "color", + "description", _("Reduce to a limited set of colors"), + NULL); + + GIMP_CONFIG_PROP_INT (object_class, PROP_LEVELS, + "levels", + _("Posterize levels"), + NULL, + 2, 256, 3, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_operation_posterize_init (GimpOperationPosterize *self) +{ +} + +static void +gimp_operation_posterize_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (object); + + switch (property_id) + { + case PROP_LEVELS: + g_value_set_int (value, posterize->levels); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_posterize_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (object); + + switch (property_id) + { + case PROP_LEVELS: + posterize->levels = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_operation_posterize_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + gfloat levels; + + levels = posterize->levels - 1.0; + + while (samples--) + { + dest[RED] = RINT (src[RED] * levels) / levels; + dest[GREEN] = RINT (src[GREEN] * levels) / levels; + dest[BLUE] = RINT (src[BLUE] * levels) / levels; + dest[ALPHA] = RINT (src[ALPHA] * levels) / levels; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationposterize.h b/app/operations/gimpoperationposterize.h new file mode 100644 index 0000000..06e5731 --- /dev/null +++ b/app/operations/gimpoperationposterize.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationposterize.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_POSTERIZE_H__ +#define __GIMP_OPERATION_POSTERIZE_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_POSTERIZE (gimp_operation_posterize_get_type ()) +#define GIMP_OPERATION_POSTERIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterize)) +#define GIMP_OPERATION_POSTERIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterizeClass)) +#define GIMP_IS_OPERATION_POSTERIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_POSTERIZE)) +#define GIMP_IS_OPERATION_POSTERIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_POSTERIZE)) +#define GIMP_OPERATION_POSTERIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterizeClass)) + + +typedef struct _GimpOperationPosterize GimpOperationPosterize; +typedef struct _GimpOperationPosterizeClass GimpOperationPosterizeClass; + +struct _GimpOperationPosterize +{ + GimpOperationPointFilter parent_instance; + + gint levels; +}; + +struct _GimpOperationPosterizeClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_posterize_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_POSTERIZE_H__ */ diff --git a/app/operations/gimpoperationprofiletransform.c b/app/operations/gimpoperationprofiletransform.c new file mode 100644 index 0000000..2e42459 --- /dev/null +++ b/app/operations/gimpoperationprofiletransform.c @@ -0,0 +1,307 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationprofiletransform.c + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpcolor/gimpcolor.h" + +#include "operations-types.h" + +#include "gimpoperationprofiletransform.h" + + +enum +{ + PROP_0, + PROP_SRC_PROFILE, + PROP_SRC_FORMAT, + PROP_DEST_PROFILE, + PROP_DEST_FORMAT, + PROP_RENDERING_INTENT, + PROP_BLACK_POINT_COMPENSATION +}; + + +static void gimp_operation_profile_transform_finalize (GObject *object); + +static void gimp_operation_profile_transform_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_profile_transform_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_profile_transform_prepare (GeglOperation *operation); +static gboolean gimp_operation_profile_transform_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationProfileTransform, gimp_operation_profile_transform, + GEGL_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_profile_transform_parent_class + + +static void +gimp_operation_profile_transform_class_init (GimpOperationProfileTransformClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->finalize = gimp_operation_profile_transform_finalize; + object_class->set_property = gimp_operation_profile_transform_set_property; + object_class->get_property = gimp_operation_profile_transform_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:profile-transform", + "categories", "color", + "description", + "Transform between two color profiles", + NULL); + + operation_class->prepare = gimp_operation_profile_transform_prepare; + + point_class->process = gimp_operation_profile_transform_process; + + g_object_class_install_property (object_class, PROP_SRC_PROFILE, + g_param_spec_object ("src-profile", + "Source Profile", + "Source Profile", + GIMP_TYPE_COLOR_PROFILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_SRC_FORMAT, + g_param_spec_pointer ("src-format", + "Source Format", + "Source Format", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DEST_PROFILE, + g_param_spec_object ("dest-profile", + "Destination Profile", + "Destination Profile", + GIMP_TYPE_COLOR_PROFILE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_DEST_FORMAT, + g_param_spec_pointer ("dest-format", + "Destination Format", + "Destination Format", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RENDERING_INTENT, + g_param_spec_enum ("rendering-intent", + "Rendering Intent", + "Rendering Intent", + GIMP_TYPE_COLOR_RENDERING_INTENT, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_BLACK_POINT_COMPENSATION, + g_param_spec_boolean ("black-point-compensation", + "Black Point Compensation", + "Black Point Compensation", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_profile_transform_init (GimpOperationProfileTransform *self) +{ +} + +static void +gimp_operation_profile_transform_finalize (GObject *object) +{ + GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object); + + g_clear_object (&self->src_profile); + g_clear_object (&self->dest_profile); + g_clear_object (&self->transform); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_operation_profile_transform_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object); + + switch (property_id) + { + case PROP_SRC_PROFILE: + g_value_set_object (value, self->src_profile); + break; + + case PROP_SRC_FORMAT: + g_value_set_pointer (value, (gpointer) self->src_format); + break; + + case PROP_DEST_PROFILE: + g_value_set_object (value, self->dest_profile); + break; + + case PROP_DEST_FORMAT: + g_value_set_pointer (value, (gpointer) self->dest_format); + break; + + case PROP_RENDERING_INTENT: + g_value_set_enum (value, self->rendering_intent); + break; + + case PROP_BLACK_POINT_COMPENSATION: + g_value_set_boolean (value, self->black_point_compensation); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_profile_transform_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object); + + switch (property_id) + { + case PROP_SRC_PROFILE: + if (self->src_profile) + g_object_unref (self->src_profile); + self->src_profile = g_value_dup_object (value); + break; + + case PROP_SRC_FORMAT: + self->src_format = g_value_get_pointer (value); + break; + + case PROP_DEST_PROFILE: + if (self->dest_profile) + g_object_unref (self->dest_profile); + self->dest_profile = g_value_dup_object (value); + break; + + case PROP_DEST_FORMAT: + self->dest_format = g_value_get_pointer (value); + break; + + case PROP_RENDERING_INTENT: + self->rendering_intent = g_value_get_enum (value); + break; + + case PROP_BLACK_POINT_COMPENSATION: + self->black_point_compensation = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_profile_transform_prepare (GeglOperation *operation) +{ + GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (operation); + + g_clear_object (&self->transform); + + if (! self->src_format) + self->src_format = babl_format ("RGBA float"); + + if (! self->dest_format) + self->dest_format = babl_format ("RGBA float"); + + if (self->src_profile && self->dest_profile) + { + GimpColorTransformFlags flags = 0; + + if (self->black_point_compensation) + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; + + self->transform = gimp_color_transform_new (self->src_profile, + self->src_format, + self->dest_profile, + self->dest_format, + self->rendering_intent, + flags); + } + + gegl_operation_set_format (operation, "input", self->src_format); + gegl_operation_set_format (operation, "output", self->dest_format); +} + +static gboolean +gimp_operation_profile_transform_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (operation); + gpointer *src = in_buf; + gpointer *dest = out_buf; + + if (self->transform) + { + gimp_color_transform_process_pixels (self->transform, + self->src_format, + src, + self->dest_format, + dest, + samples); + } + else + { + babl_process (babl_fish (self->src_format, + self->dest_format), + src, dest, samples); + } + + return TRUE; +} diff --git a/app/operations/gimpoperationprofiletransform.h b/app/operations/gimpoperationprofiletransform.h new file mode 100644 index 0000000..52aaf4a --- /dev/null +++ b/app/operations/gimpoperationprofiletransform.h @@ -0,0 +1,65 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationprofiletransform.h + * Copyright (C) 2016 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_PROFILE_TRANSFORM_H__ +#define __GIMP_OPERATION_PROFILE_TRANSFORM_H__ + + +#include +#include + + +#define GIMP_TYPE_OPERATION_PROFILE_TRANSFORM (gimp_operation_profile_transform_get_type ()) +#define GIMP_OPERATION_PROFILE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransform)) +#define GIMP_OPERATION_PROFILE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransformClass)) +#define GIMP_IS_OPERATION_PROFILE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM)) +#define GIMP_IS_OPERATION_PROFILE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM)) +#define GIMP_OPERATION_PROFILE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransformClass)) + + +typedef struct _GimpOperationProfileTransform GimpOperationProfileTransform; +typedef struct _GimpOperationProfileTransformClass GimpOperationProfileTransformClass; + +struct _GimpOperationProfileTransform +{ + GeglOperationPointFilter parent_instance; + + GimpColorProfile *src_profile; + const Babl *src_format; + + GimpColorProfile *dest_profile; + const Babl *dest_format; + + GimpColorRenderingIntent rendering_intent; + gboolean black_point_compensation; + + GimpColorTransform *transform; +}; + +struct _GimpOperationProfileTransformClass +{ + GeglOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_profile_transform_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_PROFILE_TRANSFORM_H__ */ diff --git a/app/operations/gimpoperationscalarmultiply.c b/app/operations/gimpoperationscalarmultiply.c new file mode 100644 index 0000000..6416300 --- /dev/null +++ b/app/operations/gimpoperationscalarmultiply.c @@ -0,0 +1,189 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationscalarmultiply.c + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "gimpoperationscalarmultiply.h" + + +enum +{ + PROP_0, + PROP_N_COMPONENTS, + PROP_FACTOR +}; + + +static void gimp_operation_scalar_multiply_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_scalar_multiply_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_scalar_multiply_prepare (GeglOperation *operation); +static gboolean gimp_operation_scalar_multiply_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationScalarMultiply, gimp_operation_scalar_multiply, + GEGL_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_scalar_multiply_parent_class + + +static void +gimp_operation_scalar_multiply_class_init (GimpOperationScalarMultiplyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_scalar_multiply_set_property; + object_class->get_property = gimp_operation_scalar_multiply_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:scalar-multiply", + "categories", "gimp", + "description", "Multiply all floats in a buffer by a factor", + NULL); + + operation_class->prepare = gimp_operation_scalar_multiply_prepare; + + point_class->process = gimp_operation_scalar_multiply_process; + + g_object_class_install_property (object_class, PROP_N_COMPONENTS, + g_param_spec_int ("n-components", + "N Components", + "Number of components in the input/output vectors", + 1, 16, 2, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_FACTOR, + g_param_spec_double ("factor", + "Factor", + "The scalar factor", + G_MINFLOAT, G_MAXFLOAT, + 1.0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_scalar_multiply_init (GimpOperationScalarMultiply *self) +{ +} + +static void +gimp_operation_scalar_multiply_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (object); + + switch (property_id) + { + case PROP_N_COMPONENTS: + g_value_set_int (value, self->n_components); + break; + + case PROP_FACTOR: + g_value_set_double (value, self->factor); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_scalar_multiply_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (object); + + switch (property_id) + { + case PROP_N_COMPONENTS: + self->n_components = g_value_get_int (value); + break; + + case PROP_FACTOR: + self->factor = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_scalar_multiply_prepare (GeglOperation *operation) +{ + GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (operation); + const Babl *format; + + format = babl_format_n (babl_type ("float"), self->n_components); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "output", format); +} + +static gboolean +gimp_operation_scalar_multiply_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + glong n_samples; + + n_samples = samples * self->n_components; + + while (n_samples--) + { + *dest = *src * self->factor; + + src++; + dest++; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationscalarmultiply.h b/app/operations/gimpoperationscalarmultiply.h new file mode 100644 index 0000000..61122b7 --- /dev/null +++ b/app/operations/gimpoperationscalarmultiply.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationscalarmultiply.h + * Copyright (C) 2014 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SCALAR_MULTIPLY_H__ +#define __GIMP_OPERATION_SCALAR_MULTIPLY_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_SCALAR_MULTIPLY (gimp_operation_scalar_multiply_get_type ()) +#define GIMP_OPERATION_SCALAR_MULTIPLY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiply)) +#define GIMP_OPERATION_SCALAR_MULTIPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiplyClass)) +#define GIMP_IS_OPERATION_SCALAR_MULTIPLY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY)) +#define GIMP_IS_OPERATION_SCALAR_MULTIPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY)) +#define GIMP_OPERATION_SCALAR_MULTIPLY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiplyClass)) + + +typedef struct _GimpOperationScalarMultiply GimpOperationScalarMultiply; +typedef struct _GimpOperationScalarMultiplyClass GimpOperationScalarMultiplyClass; + +struct _GimpOperationScalarMultiply +{ + GeglOperationPointFilter parent_instance; + + gint n_components; + gdouble factor; +}; + +struct _GimpOperationScalarMultiplyClass +{ + GeglOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_scalar_multiply_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SCALAR_MULTIPLY_H__ */ diff --git a/app/operations/gimpoperationsemiflatten.c b/app/operations/gimpoperationsemiflatten.c new file mode 100644 index 0000000..1352f02 --- /dev/null +++ b/app/operations/gimpoperationsemiflatten.c @@ -0,0 +1,191 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsemiflatten.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Ported from the semi-flatten plug-in + * by Adam D. Moss, adam@foxbox.org. 1998/01/27 + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "operations-types.h" + +#include "gimpoperationsemiflatten.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_COLOR +}; + + +static void gimp_operation_semi_flatten_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_semi_flatten_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_semi_flatten_prepare (GeglOperation *operation); +static gboolean gimp_operation_semi_flatten_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationSemiFlatten, gimp_operation_semi_flatten, + GEGL_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_semi_flatten_parent_class + + +static void +gimp_operation_semi_flatten_class_init (GimpOperationSemiFlattenClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + GimpRGB white; + + object_class->set_property = gimp_operation_semi_flatten_set_property; + object_class->get_property = gimp_operation_semi_flatten_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:semi-flatten", + "categories", "color", + "description", _("Replace partial transparency with a color"), + NULL); + + operation_class->prepare = gimp_operation_semi_flatten_prepare; + + point_class->process = gimp_operation_semi_flatten_process; + + gimp_rgba_set (&white, 1.0, 1.0, 1.0, 1.0); + + g_object_class_install_property (object_class, PROP_COLOR, + gimp_param_spec_rgb ("color", + _("Color"), + _("The color"), + FALSE, &white, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_semi_flatten_init (GimpOperationSemiFlatten *self) +{ +} + +static void +gimp_operation_semi_flatten_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (object); + + switch (property_id) + { + case PROP_COLOR: + gimp_value_set_rgb (value, &self->color); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_semi_flatten_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (object); + + switch (property_id) + { + case PROP_COLOR: + gimp_value_get_rgb (value, &self->color); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_semi_flatten_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space)); +} + +static gboolean +gimp_operation_semi_flatten_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + while (samples--) + { + gfloat alpha = src[ALPHA]; + + if (alpha <= 0.0 || alpha >= 1.0) + { + dest[RED] = src[RED]; + dest[GREEN] = src[GREEN]; + dest[BLUE] = src[BLUE]; + dest[ALPHA] = alpha; + } + else + { + dest[RED] = src[RED] * alpha + self->color.r * (1.0 - alpha); + dest[GREEN] = src[GREEN] * alpha + self->color.g * (1.0 - alpha); + dest[BLUE] = src[BLUE] * alpha + self->color.b * (1.0 - alpha); + dest[ALPHA] = 1.0; + } + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationsemiflatten.h b/app/operations/gimpoperationsemiflatten.h new file mode 100644 index 0000000..be6311e --- /dev/null +++ b/app/operations/gimpoperationsemiflatten.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsemiflatten.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SEMI_FLATTEN_H__ +#define __GIMP_OPERATION_SEMI_FLATTEN_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_SEMI_FLATTEN (gimp_operation_semi_flatten_get_type ()) +#define GIMP_OPERATION_SEMI_FLATTEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlatten)) +#define GIMP_OPERATION_SEMI_FLATTEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlattenClass)) +#define GIMP_IS_OPERATION_SEMI_FLATTEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN)) +#define GIMP_IS_OPERATION_SEMI_FLATTEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SEMI_FLATTEN)) +#define GIMP_OPERATION_SEMI_FLATTEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlattenClass)) + + +typedef struct _GimpOperationSemiFlatten GimpOperationSemiFlatten; +typedef struct _GimpOperationSemiFlattenClass GimpOperationSemiFlattenClass; + +struct _GimpOperationSemiFlatten +{ + GeglOperationPointFilter parent_instance; + + GimpRGB color; +}; + +struct _GimpOperationSemiFlattenClass +{ + GeglOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_semi_flatten_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SEMI_FLATTEN_H__ */ diff --git a/app/operations/gimpoperationsetalpha.c b/app/operations/gimpoperationsetalpha.c new file mode 100644 index 0000000..b41af3c --- /dev/null +++ b/app/operations/gimpoperationsetalpha.c @@ -0,0 +1,188 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsetalpha.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "gimpoperationsetalpha.h" + + +enum +{ + PROP_0, + PROP_VALUE +}; + + +static void gimp_operation_set_alpha_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_set_alpha_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_set_alpha_prepare (GeglOperation *operation); +static gboolean gimp_operation_set_alpha_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationSetAlpha, gimp_operation_set_alpha, + GEGL_TYPE_OPERATION_POINT_COMPOSER) + +#define parent_class gimp_operation_set_alpha_parent_class + + +static void +gimp_operation_set_alpha_class_init (GimpOperationSetAlphaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointComposerClass *point_class = GEGL_OPERATION_POINT_COMPOSER_CLASS (klass); + + object_class->set_property = gimp_operation_set_alpha_set_property; + object_class->get_property = gimp_operation_set_alpha_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:set-alpha", + "categories", "color", + "description", "Set a buffer's alpha channel to a value", + NULL); + + operation_class->prepare = gimp_operation_set_alpha_prepare; + + point_class->process = gimp_operation_set_alpha_process; + + g_object_class_install_property (object_class, PROP_VALUE, + g_param_spec_double ("value", + "Value", + "The alpha value", + 0.0, 1.0, 1.0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_set_alpha_init (GimpOperationSetAlpha *self) +{ +} + +static void +gimp_operation_set_alpha_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (object); + + switch (property_id) + { + case PROP_VALUE: + g_value_set_double (value, self->value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_set_alpha_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (object); + + switch (property_id) + { + case PROP_VALUE: + self->value = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_set_alpha_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space)); + gegl_operation_set_format (operation, "aux", babl_format_with_space ("Y float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space)); +} + +static gboolean +gimp_operation_set_alpha_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (operation); + gfloat *src = in_buf; + gfloat *aux = aux_buf; + gfloat *dest = out_buf; + + if (aux) + { + while (samples--) + { + dest[RED] = src[RED]; + dest[GREEN] = src[GREEN]; + dest[BLUE] = src[BLUE]; + dest[ALPHA] = self->value * *aux; + + src += 4; + aux += 1; + dest += 4; + } + } + else + { + while (samples--) + { + dest[RED] = src[RED]; + dest[GREEN] = src[GREEN]; + dest[BLUE] = src[BLUE]; + dest[ALPHA] = self->value; + + src += 4; + dest += 4; + } + } + + return TRUE; +} diff --git a/app/operations/gimpoperationsetalpha.h b/app/operations/gimpoperationsetalpha.h new file mode 100644 index 0000000..741ae1e --- /dev/null +++ b/app/operations/gimpoperationsetalpha.h @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsetalpha.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SET_ALPHA_H__ +#define __GIMP_OPERATION_SET_ALPHA_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_SET_ALPHA (gimp_operation_set_alpha_get_type ()) +#define GIMP_OPERATION_SET_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlpha)) +#define GIMP_OPERATION_SET_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlphaClass)) +#define GIMP_IS_OPERATION_SET_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SET_ALPHA)) +#define GIMP_IS_OPERATION_SET_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SET_ALPHA)) +#define GIMP_OPERATION_SET_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlphaClass)) + + +typedef struct _GimpOperationSetAlpha GimpOperationSetAlpha; +typedef struct _GimpOperationSetAlphaClass GimpOperationSetAlphaClass; + +struct _GimpOperationSetAlpha +{ + GeglOperationPointComposer parent_instance; + + gdouble value; +}; + +struct _GimpOperationSetAlphaClass +{ + GeglOperationPointComposerClass parent_class; +}; + + +GType gimp_operation_set_alpha_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SET_ALPHA_H__ */ diff --git a/app/operations/gimpoperationsettings.c b/app/operations/gimpoperationsettings.c new file mode 100644 index 0000000..88a6ace --- /dev/null +++ b/app/operations/gimpoperationsettings.c @@ -0,0 +1,320 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsettings.c + * Copyright (C) 2020 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimpdrawable.h" +#include "core/gimpdrawablefilter.h" + +#include "gimpoperationsettings.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CLIP, + PROP_REGION, + PROP_MODE, + PROP_OPACITY, + PROP_COLOR_MANAGED, + PROP_GAMMA_HACK +}; + + +static void gimp_operation_settings_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_settings_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpOperationSettings, gimp_operation_settings, + GIMP_TYPE_SETTINGS) + +#define parent_class gimp_operation_settings_parent_class + + +static void +gimp_operation_settings_class_init (GimpOperationSettingsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_operation_settings_set_property; + object_class->get_property = gimp_operation_settings_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CLIP, + "gimp-clip", + _("Clipping"), + _("How to clip"), + GIMP_TYPE_TRANSFORM_RESIZE, + GIMP_TRANSFORM_RESIZE_ADJUST, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_REGION, + "gimp-region", + NULL, NULL, + GIMP_TYPE_FILTER_REGION, + GIMP_FILTER_REGION_SELECTION, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE, + "gimp-mode", + _("Mode"), + NULL, + GIMP_TYPE_LAYER_MODE, + GIMP_LAYER_MODE_REPLACE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OPACITY, + "gimp-opacity", + _("Opacity"), + NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COLOR_MANAGED, + "gimp-color-managed", + _("Color _managed"), + NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GAMMA_HACK, + "gimp-gamma-hack", + "Gamma hack (temp hack, please ignore)", + NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS | + GIMP_CONFIG_PARAM_DEFAULTS); +} + +static void +gimp_operation_settings_init (GimpOperationSettings *settings) +{ +} + +static void +gimp_operation_settings_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationSettings *settings = GIMP_OPERATION_SETTINGS (object); + + switch (property_id) + { + case PROP_CLIP: + g_value_set_enum (value, settings->clip); + break; + + case PROP_REGION: + g_value_set_enum (value, settings->region); + break; + + case PROP_MODE: + g_value_set_enum (value, settings->mode); + break; + + case PROP_OPACITY: + g_value_set_double (value, settings->opacity); + break; + + case PROP_COLOR_MANAGED: + g_value_set_boolean (value, settings->color_managed); + break; + + case PROP_GAMMA_HACK: + g_value_set_boolean (value, settings->gamma_hack); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_settings_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationSettings *settings = GIMP_OPERATION_SETTINGS (object); + + switch (property_id) + { + case PROP_CLIP: + settings->clip = g_value_get_enum (value); + break; + + case PROP_REGION: + settings->region = g_value_get_enum (value); + break; + + case PROP_MODE: + settings->mode = g_value_get_enum (value); + break; + + case PROP_OPACITY: + settings->opacity = g_value_get_double (value); + break; + + case PROP_COLOR_MANAGED: + settings->color_managed = g_value_get_boolean (value); + break; + + case PROP_GAMMA_HACK: + settings->gamma_hack = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* public functions */ + +void +gimp_operation_settings_sync_drawable_filter (GimpOperationSettings *settings, + GimpDrawableFilter *filter) +{ + gboolean clip; + + g_return_if_fail (GIMP_IS_OPERATION_SETTINGS (settings)); + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter)); + + clip = settings->clip == GIMP_TRANSFORM_RESIZE_CLIP || + ! babl_format_has_alpha (gimp_drawable_filter_get_format (filter)); + + gimp_drawable_filter_set_region (filter, settings->region); + gimp_drawable_filter_set_clip (filter, clip); + gimp_drawable_filter_set_mode (filter, + settings->mode, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COMPOSITE_AUTO); + gimp_drawable_filter_set_opacity (filter, settings->opacity); + gimp_drawable_filter_set_color_managed (filter, settings->color_managed); + gimp_drawable_filter_set_gamma_hack (filter, settings->gamma_hack); +} + + +/* protected functions */ + +static const gchar * const base_properties[] = +{ + "time", + "gimp-clip", + "gimp-region", + "gimp-mode", + "gimp-opacity", + "gimp-color-managed", + "gimp-gamma-hack" +}; + +gboolean +gimp_operation_settings_config_serialize_base (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (base_properties); i++) + { + if (! gimp_config_serialize_property_by_name (config, + base_properties[i], + writer)) + { + return FALSE; + } + } + + return TRUE; +} + +gboolean +gimp_operation_settings_config_equal_base (GimpConfig *a, + GimpConfig *b) +{ + GimpOperationSettings *settings_a = GIMP_OPERATION_SETTINGS (a); + GimpOperationSettings *settings_b = GIMP_OPERATION_SETTINGS (b); + + return settings_a->clip == settings_b->clip && + settings_a->region == settings_b->region && + settings_a->mode == settings_b->mode && + settings_a->opacity == settings_b->opacity && + settings_a->color_managed == settings_b->color_managed && + settings_a->gamma_hack == settings_b->gamma_hack; +} + +void +gimp_operation_settings_config_reset_base (GimpConfig *config) +{ + gint i; + + g_object_freeze_notify (G_OBJECT (config)); + + for (i = 0; i < G_N_ELEMENTS (base_properties); i++) + gimp_config_reset_property (G_OBJECT (config), base_properties[i]); + + g_object_thaw_notify (G_OBJECT (config)); +} + +gboolean +gimp_operation_settings_config_copy_base (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags) +{ + gint i; + + g_object_freeze_notify (G_OBJECT (dest)); + + for (i = 0; i < G_N_ELEMENTS (base_properties); i++) + { + g_object_unref (g_object_bind_property (src, base_properties[i], + dest, base_properties[i], + G_BINDING_SYNC_CREATE)); + } + + g_object_thaw_notify (G_OBJECT (dest)); + + return TRUE; +} diff --git a/app/operations/gimpoperationsettings.h b/app/operations/gimpoperationsettings.h new file mode 100644 index 0000000..a081fce --- /dev/null +++ b/app/operations/gimpoperationsettings.h @@ -0,0 +1,75 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsettings.h + * Copyright (C) 2020 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 . + */ + +#ifndef __GIMP_OPERATION_SETTINGS_H__ +#define __GIMP_OPERATION_SETTINGS_H__ + + +#include "core/gimpsettings.h" + + +#define GIMP_TYPE_OPERATION_SETTINGS (gimp_operation_settings_get_type ()) +#define GIMP_OPERATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettings)) +#define GIMP_OPERATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettingsClass)) +#define GIMP_IS_OPERATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SETTINGS)) +#define GIMP_IS_OPERATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SETTINGS)) +#define GIMP_OPERATION_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettingsClass)) + + +typedef struct _GimpOperationSettingsClass GimpOperationSettingsClass; + +struct _GimpOperationSettings +{ + GimpSettings parent_instance; + + GimpTransformResize clip; + GimpFilterRegion region; + GimpLayerMode mode; + gdouble opacity; + gboolean color_managed; + gboolean gamma_hack; +}; + +struct _GimpOperationSettingsClass +{ + GimpSettingsClass parent_class; +}; + + +GType gimp_operation_settings_get_type (void) G_GNUC_CONST; + +void gimp_operation_settings_sync_drawable_filter (GimpOperationSettings *settings, + GimpDrawableFilter *filter); + + +/* protected */ + +gboolean gimp_operation_settings_config_serialize_base (GimpConfig *config, + GimpConfigWriter *writer, + gpointer data); +gboolean gimp_operation_settings_config_equal_base (GimpConfig *a, + GimpConfig *b); +void gimp_operation_settings_config_reset_base (GimpConfig *config); +gboolean gimp_operation_settings_config_copy_base (GimpConfig *src, + GimpConfig *dest, + GParamFlags flags); + + +#endif /* __GIMP_OPERATION_SETTINGS_H__ */ diff --git a/app/operations/gimpoperationshrink.c b/app/operations/gimpoperationshrink.c new file mode 100644 index 0000000..c862edb --- /dev/null +++ b/app/operations/gimpoperationshrink.c @@ -0,0 +1,443 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationshrink.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationshrink.h" + + +enum +{ + PROP_0, + PROP_RADIUS_X, + PROP_RADIUS_Y, + PROP_EDGE_LOCK +}; + + +static void gimp_operation_shrink_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_shrink_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_shrink_prepare (GeglOperation *operation); +static GeglRectangle + gimp_operation_shrink_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static GeglRectangle + gimp_operation_shrink_get_cached_region (GeglOperation *self, + const GeglRectangle *roi); + +static gboolean gimp_operation_shrink_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationShrink, gimp_operation_shrink, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_shrink_parent_class + + +static void +gimp_operation_shrink_class_init (GimpOperationShrinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_shrink_set_property; + object_class->get_property = gimp_operation_shrink_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:shrink", + "categories", "gimp", + "description", "GIMP Shrink operation", + NULL); + + operation_class->prepare = gimp_operation_shrink_prepare; + operation_class->get_required_for_output = gimp_operation_shrink_get_required_for_output; + operation_class->get_cached_region = gimp_operation_shrink_get_cached_region; + operation_class->threaded = FALSE; + + filter_class->process = gimp_operation_shrink_process; + + g_object_class_install_property (object_class, PROP_RADIUS_X, + g_param_spec_int ("radius-x", + "Radius X", + "Shrink radius in X diection", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_RADIUS_Y, + g_param_spec_int ("radius-y", + "Radius Y", + "Shrink radius in Y diection", + 1, 2342, 1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_EDGE_LOCK, + g_param_spec_boolean ("edge-lock", + "Edge Lock", + "Shrink from border", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_shrink_init (GimpOperationShrink *self) +{ +} + +static void +gimp_operation_shrink_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationShrink *self = GIMP_OPERATION_SHRINK (object); + + switch (property_id) + { + case PROP_RADIUS_X: + g_value_set_int (value, self->radius_x); + break; + + case PROP_RADIUS_Y: + g_value_set_int (value, self->radius_y); + break; + + case PROP_EDGE_LOCK: + g_value_set_boolean (value, self->edge_lock); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_shrink_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationShrink *self = GIMP_OPERATION_SHRINK (object); + + switch (property_id) + { + case PROP_RADIUS_X: + self->radius_x = g_value_get_int (value); + break; + + case PROP_RADIUS_Y: + self->radius_y = g_value_get_int (value); + break; + + case PROP_EDGE_LOCK: + self->edge_lock = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_shrink_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space)); +} + +static GeglRectangle +gimp_operation_shrink_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static GeglRectangle +gimp_operation_shrink_get_cached_region (GeglOperation *self, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static void +compute_border (gint16 *circ, + guint16 xradius, + guint16 yradius) +{ + gint32 i; + gint32 diameter = xradius * 2 + 1; + gdouble tmp; + + for (i = 0; i < diameter; i++) + { + if (i > xradius) + tmp = (i - xradius) - 0.5; + else if (i < xradius) + tmp = (xradius - i) - 0.5; + else + tmp = 0.0; + + circ[i] = RINT (yradius / + (gdouble) xradius * sqrt (SQR (xradius) - SQR (tmp))); + } +} + +static inline void +rotate_pointers (gfloat **p, + guint32 n) +{ + guint32 i; + gfloat *tmp; + + tmp = p[0]; + + for (i = 0; i < n - 1; i++) + p[i] = p[i + 1]; + + p[i] = tmp; +} + +static gboolean +gimp_operation_shrink_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level) +{ + /* Pretty much the same as fatten_region only different. + * Blame all bugs in this function on jaycox@gimp.org + * + * If edge_lock is true we assume that pixels outside the region we + * are passed are identical to the edge pixels. If edge_lock is + * false, we assume that pixels outside the region are 0 + */ + GimpOperationShrink *self = GIMP_OPERATION_SHRINK (operation); + const Babl *input_format = babl_format ("Y float"); + const Babl *output_format = babl_format ("Y float"); + gint32 i, j, x, y; + gfloat **buf; /* caches the the region's pixels */ + gfloat *out; /* holds the new scan line we are computing */ + gfloat **max; /* caches the smallest values for each column */ + gint16 *circ; /* holds the y coords of the filter's mask */ + gfloat last_max; + gint16 last_index; + gfloat *buffer; + gint buffer_size; + + max = g_new (gfloat *, roi->width + 2 * self->radius_x); + buf = g_new (gfloat *, self->radius_y + 1); + + for (i = 0; i < self->radius_y + 1; i++) + buf[i] = g_new (gfloat, roi->width); + + buffer_size = (roi->width+ 2 * self->radius_x + 1) * (self->radius_y + 1); + buffer = g_new (gfloat, buffer_size); + + if (self->edge_lock) + { + for (i = 0; i < buffer_size; i++) + buffer[i] = 1.0; + } + else + { + memset (buffer, 0, buffer_size * sizeof (gfloat)); + } + + for (i = 0; i < roi->width + 2 * self->radius_x; i++) + { + if (i < self->radius_x) + { + if (self->edge_lock) + max[i] = buffer; + else + max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x)]; + } + else if (i < roi->width + self->radius_x) + { + max[i] = &buffer[(self->radius_y + 1) * (i - self->radius_x)]; + } + else + { + if (self->edge_lock) + max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x - 1)]; + else + max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x)]; + } + } + + if (! self->edge_lock) + for (j = 0 ; j < self->radius_y + 1; j++) + max[0][j] = 0.0; + + /* offset the max pointer by self->radius_x so the range of the + * array is [-self->radius_x] to [roi->width + self->radius_x] + */ + max += self->radius_x; + + out = g_new (gfloat, roi->width); + + circ = g_new (gint16, 2 * self->radius_x + 1); + compute_border (circ, self->radius_x, self->radius_y); + + /* offset the circ pointer by self->radius_x so the range of the + * array is [-self->radius_x] to [self->radius_x] + */ + circ += self->radius_x; + + for (i = 0; i < self->radius_y && i < roi->height; i++) /* load top of image */ + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + i, + roi->width, 1), + 1.0, input_format, buf[i + 1], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + if (self->edge_lock) + memcpy (buf[0], buf[1], roi->width * sizeof (gfloat)); + else + memset (buf[0], 0, roi->width * sizeof (gfloat)); + + for (x = 0; x < roi->width; x++) /* set up max for top of image */ + { + max[x][0] = buf[0][x]; + + for (j = 1; j < self->radius_y + 1; j++) + max[x][j] = MIN (buf[j][x], max[x][j - 1]); + } + + for (y = 0; y < roi->height; y++) + { + rotate_pointers (buf, self->radius_y + 1); + + if (y < roi->height - self->radius_y) + gegl_buffer_get (input, + GEGL_RECTANGLE (roi->x, roi->y + y + self->radius_y, + roi->width, 1), + 1.0, input_format, buf[self->radius_y], + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + else if (self->edge_lock) + memcpy (buf[self->radius_y], buf[self->radius_y - 1], + roi->width * sizeof (gfloat)); + else + memset (buf[self->radius_y], 0, roi->width * sizeof (gfloat)); + + for (x = 0 ; x < roi->width; x++) /* update max array */ + { + for (i = self->radius_y; i > 0; i--) + max[x][i] = MIN (MIN (max[x][i - 1], buf[i - 1][x]), buf[i][x]); + + max[x][0] = buf[0][x]; + } + + last_max = max[0][circ[-1]]; + last_index = 0; + + for (x = 0 ; x < roi->width; x++) /* render scan line */ + { + last_index--; + + if (last_index >= 0) + { + if (last_max <= 0.0) + { + out[x] = 0.0; + } + else + { + last_max = 1.0; + + for (i = self->radius_x; i >= 0; i--) + if (last_max > max[x + i][circ[i]]) + { + last_max = max[x + i][circ[i]]; + last_index = i; + } + + out[x] = last_max; + } + } + else + { + last_index = self->radius_x; + last_max = max[x + self->radius_x][circ[self->radius_x]]; + + for (i = self->radius_x - 1; i >= -self->radius_x; i--) + if (last_max > max[x + i][circ[i]]) + { + last_max = max[x + i][circ[i]]; + last_index = i; + } + + out[x] = last_max; + } + } + + gegl_buffer_set (output, + GEGL_RECTANGLE (roi->x, roi->y + y, + roi->width, 1), + 0, output_format, out, + GEGL_AUTO_ROWSTRIDE); + } + + /* undo the offsets to the pointers so we can free the malloced memory */ + circ -= self->radius_x; + max -= self->radius_x; + + /* free the memory */ + g_free (circ); + g_free (buffer); + g_free (max); + + for (i = 0; i < self->radius_y + 1; i++) + g_free (buf[i]); + + g_free (buf); + g_free (out); + + return TRUE; +} diff --git a/app/operations/gimpoperationshrink.h b/app/operations/gimpoperationshrink.h new file mode 100644 index 0000000..1ba58e6 --- /dev/null +++ b/app/operations/gimpoperationshrink.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationshrink.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SHRINK_H__ +#define __GIMP_OPERATION_SHRINK_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_SHRINK (gimp_operation_shrink_get_type ()) +#define GIMP_OPERATION_SHRINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrink)) +#define GIMP_OPERATION_SHRINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrinkClass)) +#define GIMP_IS_OPERATION_SHRINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SHRINK)) +#define GIMP_IS_OPERATION_SHRINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SHRINK)) +#define GIMP_OPERATION_SHRINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrinkClass)) + + +typedef struct _GimpOperationShrink GimpOperationShrink; +typedef struct _GimpOperationShrinkClass GimpOperationShrinkClass; + +struct _GimpOperationShrink +{ + GeglOperationFilter parent_instance; + + gint radius_x; + gint radius_y; + gboolean edge_lock; +}; + +struct _GimpOperationShrinkClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_shrink_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SHRINK_H__ */ diff --git a/app/operations/gimpoperationthreshold.c b/app/operations/gimpoperationthreshold.c new file mode 100644 index 0000000..e33a9ec --- /dev/null +++ b/app/operations/gimpoperationthreshold.c @@ -0,0 +1,232 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationthreshold.c + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" + +#include "operations-types.h" + +#include "gimpoperationthreshold.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CHANNEL, + PROP_LOW, + PROP_HIGH +}; + + +static void gimp_operation_threshold_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_threshold_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static gboolean gimp_operation_threshold_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationThreshold, gimp_operation_threshold, + GIMP_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_threshold_parent_class + + +static void +gimp_operation_threshold_class_init (GimpOperationThresholdClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_threshold_set_property; + object_class->get_property = gimp_operation_threshold_get_property; + + point_class->process = gimp_operation_threshold_process; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:threshold", + "categories", "color", + "description", _("Reduce image to two colors using a threshold"), + NULL); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL, + "channel", + _("Channel"), + NULL, + GIMP_TYPE_HISTOGRAM_CHANNEL, + GIMP_HISTOGRAM_VALUE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW, + "low", + _("Low threshold"), + NULL, + 0.0, 1.0, 0.5, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH, + "high", + _("High threshold"), + NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_operation_threshold_init (GimpOperationThreshold *self) +{ +} + +static void +gimp_operation_threshold_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationThreshold *self = GIMP_OPERATION_THRESHOLD (object); + + switch (property_id) + { + case PROP_CHANNEL: + g_value_set_enum (value, self->channel); + break; + + case PROP_LOW: + g_value_set_double (value, self->low); + break; + + case PROP_HIGH: + g_value_set_double (value, self->high); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_threshold_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationThreshold *self = GIMP_OPERATION_THRESHOLD (object); + + switch (property_id) + { + case PROP_CHANNEL: + self->channel = g_value_get_enum (value); + break; + + case PROP_LOW: + self->low = g_value_get_double (value); + break; + + case PROP_HIGH: + self->high = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + static gboolean +gimp_operation_threshold_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationThreshold *threshold = GIMP_OPERATION_THRESHOLD (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + while (samples--) + { + gfloat value = 0.0; + + switch (threshold->channel) + { + case GIMP_HISTOGRAM_VALUE: + value = MAX (src[RED], src[GREEN]); + value = MAX (value, src[BLUE]); + break; + + case GIMP_HISTOGRAM_RED: + value = src[RED]; + break; + + case GIMP_HISTOGRAM_GREEN: + value = src[GREEN]; + break; + + case GIMP_HISTOGRAM_BLUE: + value = src[BLUE]; + break; + + case GIMP_HISTOGRAM_ALPHA: + value = src[ALPHA]; + break; + + case GIMP_HISTOGRAM_RGB: + value = MIN (src[RED], src[GREEN]); + value = MIN (value, src[BLUE]); + break; + + case GIMP_HISTOGRAM_LUMINANCE: + value = GIMP_RGB_LUMINANCE (src[RED], src[GREEN], src[BLUE]); + break; + } + + value = (value >= threshold->low && value <= threshold->high) ? 1.0 : 0.0; + + dest[RED] = value; + dest[GREEN] = value; + dest[BLUE] = value; + dest[ALPHA] = src[ALPHA]; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationthreshold.h b/app/operations/gimpoperationthreshold.h new file mode 100644 index 0000000..dcdf52b --- /dev/null +++ b/app/operations/gimpoperationthreshold.h @@ -0,0 +1,57 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationthreshold.h + * Copyright (C) 2007 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_THRESHOLD_H__ +#define __GIMP_OPERATION_THRESHOLD_H__ + + +#include "gimpoperationpointfilter.h" + + +#define GIMP_TYPE_OPERATION_THRESHOLD (gimp_operation_threshold_get_type ()) +#define GIMP_OPERATION_THRESHOLD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThreshold)) +#define GIMP_OPERATION_THRESHOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThresholdClass)) +#define GIMP_IS_OPERATION_THRESHOLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_THRESHOLD)) +#define GIMP_IS_OPERATION_THRESHOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_THRESHOLD)) +#define GIMP_OPERATION_THRESHOLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThresholdClass)) + + +typedef struct _GimpOperationThreshold GimpOperationThreshold; +typedef struct _GimpOperationThresholdClass GimpOperationThresholdClass; + +struct _GimpOperationThreshold +{ + GimpOperationPointFilter parent_instance; + + GimpHistogramChannel channel; + gdouble low; + gdouble high; +}; + +struct _GimpOperationThresholdClass +{ + GimpOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_threshold_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_THRESHOLD_H__ */ diff --git a/app/operations/gimpoperationthresholdalpha.c b/app/operations/gimpoperationthresholdalpha.c new file mode 100644 index 0000000..94cbaf5 --- /dev/null +++ b/app/operations/gimpoperationthresholdalpha.c @@ -0,0 +1,178 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationthresholdalpha.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Ported from the threshold-alpha plug-in + * Copyright (C) 1997 Shuji Narazaki + */ + +#include "config.h" + +#include + +#include "operations-types.h" + +#include "gimpoperationthresholdalpha.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_VALUE +}; + + +static void gimp_operation_threshold_alpha_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_threshold_alpha_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_threshold_alpha_prepare (GeglOperation *operation); +static gboolean gimp_operation_threshold_alpha_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationThresholdAlpha, gimp_operation_threshold_alpha, + GEGL_TYPE_OPERATION_POINT_FILTER) + +#define parent_class gimp_operation_threshold_alpha_parent_class + + +static void +gimp_operation_threshold_alpha_class_init (GimpOperationThresholdAlphaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); + + object_class->set_property = gimp_operation_threshold_alpha_set_property; + object_class->get_property = gimp_operation_threshold_alpha_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:threshold-alpha", + "categories", "color", + "description", + _("Make transparency all-or-nothing, by " + "thresholding the alpha channel to a value"), + NULL); + + operation_class->prepare = gimp_operation_threshold_alpha_prepare; + + point_class->process = gimp_operation_threshold_alpha_process; + + g_object_class_install_property (object_class, PROP_VALUE, + g_param_spec_double ("value", + _("Value"), + _("The alpha value"), + 0.0, 1.0, 0.5, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_operation_threshold_alpha_init (GimpOperationThresholdAlpha *self) +{ +} + +static void +gimp_operation_threshold_alpha_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (object); + + switch (property_id) + { + case PROP_VALUE: + g_value_set_double (value, self->value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_threshold_alpha_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (object); + + switch (property_id) + { + case PROP_VALUE: + self->value = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_threshold_alpha_prepare (GeglOperation *operation) +{ + const Babl *space = gegl_operation_get_source_space (operation, "input"); + gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space)); + gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space)); +} + +static gboolean +gimp_operation_threshold_alpha_process (GeglOperation *operation, + void *in_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (operation); + gfloat *src = in_buf; + gfloat *dest = out_buf; + + while (samples--) + { + dest[RED] = src[RED]; + dest[GREEN] = src[GREEN]; + dest[BLUE] = src[BLUE]; + + if (src[ALPHA] > self->value) + dest[ALPHA] = 1.0; + else + dest[ALPHA] = 0.0; + + src += 4; + dest += 4; + } + + return TRUE; +} diff --git a/app/operations/gimpoperationthresholdalpha.h b/app/operations/gimpoperationthresholdalpha.h new file mode 100644 index 0000000..0339289 --- /dev/null +++ b/app/operations/gimpoperationthresholdalpha.h @@ -0,0 +1,56 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationthresholdalpha.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_THRESHOLD_ALPHA_H__ +#define __GIMP_OPERATION_THRESHOLD_ALPHA_H__ + + +#include +#include + + +#define GIMP_TYPE_OPERATION_THRESHOLD_ALPHA (gimp_operation_threshold_alpha_get_type ()) +#define GIMP_OPERATION_THRESHOLD_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlpha)) +#define GIMP_OPERATION_THRESHOLD_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlphaClass)) +#define GIMP_IS_OPERATION_THRESHOLD_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA)) +#define GIMP_IS_OPERATION_THRESHOLD_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA)) +#define GIMP_OPERATION_THRESHOLD_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlphaClass)) + + +typedef struct _GimpOperationThresholdAlpha GimpOperationThresholdAlpha; +typedef struct _GimpOperationThresholdAlphaClass GimpOperationThresholdAlphaClass; + +struct _GimpOperationThresholdAlpha +{ + GeglOperationPointFilter parent_instance; + + gdouble value; +}; + +struct _GimpOperationThresholdAlphaClass +{ + GeglOperationPointFilterClass parent_class; +}; + + +GType gimp_operation_threshold_alpha_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_THRESHOLD_ALPHA_H__ */ diff --git a/app/operations/layer-modes-legacy/Makefile.am b/app/operations/layer-modes-legacy/Makefile.am new file mode 100644 index 0000000..b8241da --- /dev/null +++ b/app/operations/layer-modes-legacy/Makefile.am @@ -0,0 +1,54 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Layer-Modes-Legacy\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libapplayermodeslegacy.a + +libapplayermodeslegacy_a_SOURCES = \ + gimpoperationadditionlegacy.c \ + gimpoperationadditionlegacy.h \ + gimpoperationburnlegacy.c \ + gimpoperationburnlegacy.h \ + gimpoperationdarkenonlylegacy.c \ + gimpoperationdarkenonlylegacy.h \ + gimpoperationdifferencelegacy.c \ + gimpoperationdifferencelegacy.h \ + gimpoperationdividelegacy.c \ + gimpoperationdividelegacy.h \ + gimpoperationdodgelegacy.c \ + gimpoperationdodgelegacy.h \ + gimpoperationgrainextractlegacy.c \ + gimpoperationgrainextractlegacy.h \ + gimpoperationgrainmergelegacy.c \ + gimpoperationgrainmergelegacy.h \ + gimpoperationhardlightlegacy.c \ + gimpoperationhardlightlegacy.h \ + gimpoperationhslcolorlegacy.c \ + gimpoperationhslcolorlegacy.h \ + gimpoperationhsvhuelegacy.c \ + gimpoperationhsvhuelegacy.h \ + gimpoperationhsvsaturationlegacy.c \ + gimpoperationhsvsaturationlegacy.h \ + gimpoperationhsvvaluelegacy.c \ + gimpoperationhsvvaluelegacy.h \ + gimpoperationlightenonlylegacy.c \ + gimpoperationlightenonlylegacy.h \ + gimpoperationmultiplylegacy.c \ + gimpoperationmultiplylegacy.h \ + gimpoperationscreenlegacy.c \ + gimpoperationscreenlegacy.h \ + gimpoperationsoftlightlegacy.c \ + gimpoperationsoftlightlegacy.h \ + gimpoperationsubtractlegacy.c \ + gimpoperationsubtractlegacy.h + diff --git a/app/operations/layer-modes-legacy/Makefile.in b/app/operations/layer-modes-legacy/Makefile.in new file mode 100644 index 0000000..f51eeed --- /dev/null +++ b/app/operations/layer-modes-legacy/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@ + +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 = app/operations/layer-modes-legacy +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 = +libapplayermodeslegacy_a_AR = $(AR) $(ARFLAGS) +libapplayermodeslegacy_a_LIBADD = +am_libapplayermodeslegacy_a_OBJECTS = \ + gimpoperationadditionlegacy.$(OBJEXT) \ + gimpoperationburnlegacy.$(OBJEXT) \ + gimpoperationdarkenonlylegacy.$(OBJEXT) \ + gimpoperationdifferencelegacy.$(OBJEXT) \ + gimpoperationdividelegacy.$(OBJEXT) \ + gimpoperationdodgelegacy.$(OBJEXT) \ + gimpoperationgrainextractlegacy.$(OBJEXT) \ + gimpoperationgrainmergelegacy.$(OBJEXT) \ + gimpoperationhardlightlegacy.$(OBJEXT) \ + gimpoperationhslcolorlegacy.$(OBJEXT) \ + gimpoperationhsvhuelegacy.$(OBJEXT) \ + gimpoperationhsvsaturationlegacy.$(OBJEXT) \ + gimpoperationhsvvaluelegacy.$(OBJEXT) \ + gimpoperationlightenonlylegacy.$(OBJEXT) \ + gimpoperationmultiplylegacy.$(OBJEXT) \ + gimpoperationscreenlegacy.$(OBJEXT) \ + gimpoperationsoftlightlegacy.$(OBJEXT) \ + gimpoperationsubtractlegacy.$(OBJEXT) +libapplayermodeslegacy_a_OBJECTS = \ + $(am_libapplayermodeslegacy_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)/gimpoperationadditionlegacy.Po \ + ./$(DEPDIR)/gimpoperationburnlegacy.Po \ + ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po \ + ./$(DEPDIR)/gimpoperationdifferencelegacy.Po \ + ./$(DEPDIR)/gimpoperationdividelegacy.Po \ + ./$(DEPDIR)/gimpoperationdodgelegacy.Po \ + ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po \ + ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po \ + ./$(DEPDIR)/gimpoperationhardlightlegacy.Po \ + ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po \ + ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po \ + ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po \ + ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po \ + ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po \ + ./$(DEPDIR)/gimpoperationmultiplylegacy.Po \ + ./$(DEPDIR)/gimpoperationscreenlegacy.Po \ + ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po \ + ./$(DEPDIR)/gimpoperationsubtractlegacy.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 = $(libapplayermodeslegacy_a_SOURCES) +DIST_SOURCES = $(libapplayermodeslegacy_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Layer-Modes-Legacy\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libapplayermodeslegacy.a + +libapplayermodeslegacy_a_SOURCES = \ + gimpoperationadditionlegacy.c \ + gimpoperationadditionlegacy.h \ + gimpoperationburnlegacy.c \ + gimpoperationburnlegacy.h \ + gimpoperationdarkenonlylegacy.c \ + gimpoperationdarkenonlylegacy.h \ + gimpoperationdifferencelegacy.c \ + gimpoperationdifferencelegacy.h \ + gimpoperationdividelegacy.c \ + gimpoperationdividelegacy.h \ + gimpoperationdodgelegacy.c \ + gimpoperationdodgelegacy.h \ + gimpoperationgrainextractlegacy.c \ + gimpoperationgrainextractlegacy.h \ + gimpoperationgrainmergelegacy.c \ + gimpoperationgrainmergelegacy.h \ + gimpoperationhardlightlegacy.c \ + gimpoperationhardlightlegacy.h \ + gimpoperationhslcolorlegacy.c \ + gimpoperationhslcolorlegacy.h \ + gimpoperationhsvhuelegacy.c \ + gimpoperationhsvhuelegacy.h \ + gimpoperationhsvsaturationlegacy.c \ + gimpoperationhsvsaturationlegacy.h \ + gimpoperationhsvvaluelegacy.c \ + gimpoperationhsvvaluelegacy.h \ + gimpoperationlightenonlylegacy.c \ + gimpoperationlightenonlylegacy.h \ + gimpoperationmultiplylegacy.c \ + gimpoperationmultiplylegacy.h \ + gimpoperationscreenlegacy.c \ + gimpoperationscreenlegacy.h \ + gimpoperationsoftlightlegacy.c \ + gimpoperationsoftlightlegacy.h \ + gimpoperationsubtractlegacy.c \ + gimpoperationsubtractlegacy.h + +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 app/operations/layer-modes-legacy/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/operations/layer-modes-legacy/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) + +libapplayermodeslegacy.a: $(libapplayermodeslegacy_a_OBJECTS) $(libapplayermodeslegacy_a_DEPENDENCIES) $(EXTRA_libapplayermodeslegacy_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapplayermodeslegacy.a + $(AM_V_AR)$(libapplayermodeslegacy_a_AR) libapplayermodeslegacy.a $(libapplayermodeslegacy_a_OBJECTS) $(libapplayermodeslegacy_a_LIBADD) + $(AM_V_at)$(RANLIB) libapplayermodeslegacy.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationadditionlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationburnlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdifferencelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdividelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdodgelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrainextractlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrainmergelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhardlightlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhslcolorlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvhuelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlightenonlylegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmultiplylegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationscreenlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsoftlightlegacy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsubtractlegacy.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)/gimpoperationadditionlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationburnlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdifferencelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdividelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdodgelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhardlightlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationmultiplylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationscreenlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationsubtractlegacy.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)/gimpoperationadditionlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationburnlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdifferencelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdividelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationdodgelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhardlightlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationmultiplylegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationscreenlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po + -rm -f ./$(DEPDIR)/gimpoperationsubtractlegacy.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/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c new file mode 100644 index 0000000..02992f2 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c @@ -0,0 +1,126 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationadditionmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationadditionlegacy.h" + + +static gboolean gimp_operation_addition_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationAdditionLegacy, gimp_operation_addition_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_addition_legacy_class_init (GimpOperationAdditionLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:addition-legacy", + "description", "GIMP addition mode operation", + NULL); + + layer_mode_class->process = gimp_operation_addition_legacy_process; +} + +static void +gimp_operation_addition_legacy_init (GimpOperationAdditionLegacy *self) +{ +} + +static gboolean +gimp_operation_addition_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (has_mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] + layer[b]; + comp = CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h new file mode 100644 index 0000000..7f9df60 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationadditionlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_ADDITION_LEGACY_H__ +#define __GIMP_OPERATION_ADDITION_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_ADDITION_LEGACY (gimp_operation_addition_legacy_get_type ()) +#define GIMP_OPERATION_ADDITION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacy)) +#define GIMP_OPERATION_ADDITION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacyClass)) +#define GIMP_IS_OPERATION_ADDITION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY)) +#define GIMP_IS_OPERATION_ADDITION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ADDITION_LEGACY)) +#define GIMP_OPERATION_ADDITION_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacyClass)) + + +typedef struct _GimpOperationAdditionLegacy GimpOperationAdditionLegacy; +typedef struct _GimpOperationAdditionLegacyClass GimpOperationAdditionLegacyClass; + +struct _GimpOperationAdditionLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationAdditionLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_addition_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_ADDITION_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c new file mode 100644 index 0000000..5511b69 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c @@ -0,0 +1,128 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationburnmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationburnlegacy.h" + + +static gboolean gimp_operation_burn_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationBurnLegacy, gimp_operation_burn_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_burn_legacy_class_init (GimpOperationBurnLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:burn-legacy", + "description", "GIMP burn mode operation", + NULL); + + layer_mode_class->process = gimp_operation_burn_legacy_process; +} + +static void +gimp_operation_burn_legacy_init (GimpOperationBurnLegacy *self) +{ +} + +static gboolean +gimp_operation_burn_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = 1.0f - (1.0f - in[b]) / layer[b]; + /* The CLAMP macro is deliberately inlined and + * written to map comp == NAN (0 / 0) -> 1 + */ + comp = comp < 0.0f ? 0.0f : comp < 1.0f ? comp : 1.0f; + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h new file mode 100644 index 0000000..dc13e3f --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationburnlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_BURN_LEGACY_H__ +#define __GIMP_OPERATION_BURN_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_BURN_LEGACY (gimp_operation_burn_legacy_get_type ()) +#define GIMP_OPERATION_BURN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacy)) +#define GIMP_OPERATION_BURN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacyClass)) +#define GIMP_IS_OPERATION_BURN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY)) +#define GIMP_IS_OPERATION_BURN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BURN_LEGACY)) +#define GIMP_OPERATION_BURN_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacyClass)) + + +typedef struct _GimpOperationBurnLegacy GimpOperationBurnLegacy; +typedef struct _GimpOperationBurnLegacyClass GimpOperationBurnLegacyClass; + +struct _GimpOperationBurnLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationBurnLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_burn_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_BURN_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c new file mode 100644 index 0000000..1a3ebdf --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdarkenonlymode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationdarkenonlylegacy.h" + + +static gboolean gimp_operation_darken_only_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationDarkenOnlyLegacy, gimp_operation_darken_only_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_darken_only_legacy_class_init (GimpOperationDarkenOnlyLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:darken-only-legacy", + "description", "GIMP darken only mode operation", + NULL); + + layer_mode_class->process = gimp_operation_darken_only_legacy_process; +} + +static void +gimp_operation_darken_only_legacy_init (GimpOperationDarkenOnlyLegacy *self) +{ +} + +static gboolean +gimp_operation_darken_only_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (new_alpha && comp_alpha) + { + gint b; + gfloat ratio = comp_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = MIN (in[b], layer[b]); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h new file mode 100644 index 0000000..c0406a1 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdarkenonlylegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__ +#define __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_DARKEN_ONLY_LEGACY (gimp_operation_darken_only_legacy_get_type ()) +#define GIMP_OPERATION_DARKEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacy)) +#define GIMP_OPERATION_DARKEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacyClass)) +#define GIMP_IS_OPERATION_DARKEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE)) +#define GIMP_IS_OPERATION_DARKEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE)) +#define GIMP_OPERATION_DARKEN_ONLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacyClass)) + + +typedef struct _GimpOperationDarkenOnlyLegacy GimpOperationDarkenOnlyLegacy; +typedef struct _GimpOperationDarkenOnlyLegacyClass GimpOperationDarkenOnlyLegacyClass; + +struct _GimpOperationDarkenOnlyLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationDarkenOnlyLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_darken_only_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c new file mode 100644 index 0000000..55f3328 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c @@ -0,0 +1,126 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdifferencemode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationdifferencelegacy.h" + + +static gboolean gimp_operation_difference_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationDifferenceLegacy, gimp_operation_difference_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_difference_legacy_class_init (GimpOperationDifferenceLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:difference-legacy", + "description", "GIMP difference mode operation", + NULL); + + layer_mode_class->process = gimp_operation_difference_legacy_process; +} + +static void +gimp_operation_difference_legacy_init (GimpOperationDifferenceLegacy *self) +{ +} + + +static gboolean +gimp_operation_difference_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] - layer[b]; + comp = (comp < 0.0f) ? -comp : comp; + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h new file mode 100644 index 0000000..145b9c2 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdifferencelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DIFFERENCE_LEGACY_H__ +#define __GIMP_OPERATION_DIFFERENCE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY (gimp_operation_difference_legacy_get_type ()) +#define GIMP_OPERATION_DIFFERENCE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacy)) +#define GIMP_OPERATION_DIFFERENCE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacyClass)) +#define GIMP_IS_OPERATION_DIFFERENCE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY)) +#define GIMP_IS_OPERATION_DIFFERENCE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY)) +#define GIMP_OPERATION_DIFFERENCE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacyClass)) + + +typedef struct _GimpOperationDifferenceLegacy GimpOperationDifferenceLegacy; +typedef struct _GimpOperationDifferenceLegacyClass GimpOperationDifferenceLegacyClass; + +struct _GimpOperationDifferenceLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationDifferenceLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_difference_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DIFFERENCE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c new file mode 100644 index 0000000..8fe8dd8 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c @@ -0,0 +1,127 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdividemode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "../operations-types.h" + +#include "gimpoperationdividelegacy.h" + + +static gboolean gimp_operation_divide_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationDivideLegacy, gimp_operation_divide_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_divide_legacy_class_init (GimpOperationDivideLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:divide-legacy", + "description", "GIMP divide mode operation", + NULL); + + layer_mode_class->process = gimp_operation_divide_legacy_process; +} + +static void +gimp_operation_divide_legacy_init (GimpOperationDivideLegacy *self) +{ +} + +static gboolean +gimp_operation_divide_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat ratio = comp_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] / layer[b]; + comp = SAFE_CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h new file mode 100644 index 0000000..fa5de12 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdividelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DIVIDE_LEGACY_H__ +#define __GIMP_OPERATION_DIVIDE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_DIVIDE_LEGACY (gimp_operation_divide_legacy_get_type ()) +#define GIMP_OPERATION_DIVIDE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacy)) +#define GIMP_OPERATION_DIVIDE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacyClass)) +#define GIMP_IS_OPERATION_DIVIDE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY)) +#define GIMP_IS_OPERATION_DIVIDE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DIVIDE_LEGACY)) +#define GIMP_OPERATION_DIVIDE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacyClass)) + + +typedef struct _GimpOperationDivideLegacy GimpOperationDivideLegacy; +typedef struct _GimpOperationDivideLegacyClass GimpOperationDivideLegacyClass; + +struct _GimpOperationDivideLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationDivideLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_divide_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DIVIDE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c new file mode 100644 index 0000000..65adaef --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c @@ -0,0 +1,127 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdodgelegacy.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "../operations-types.h" + +#include "gimpoperationdodgelegacy.h" + + +static gboolean gimp_operation_dodge_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationDodgeLegacy, gimp_operation_dodge_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_dodge_legacy_class_init (GimpOperationDodgeLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:dodge-legacy", + "description", "GIMP dodge mode operation", + NULL); + + layer_mode_class->process = gimp_operation_dodge_legacy_process; +} + +static void +gimp_operation_dodge_legacy_init (GimpOperationDodgeLegacy *self) +{ +} + +static gboolean +gimp_operation_dodge_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat ratio = comp_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] / (1.0f - layer[b]); + comp = SAFE_CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h new file mode 100644 index 0000000..b640fed --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdodgelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DODGE_LEGACY_H__ +#define __GIMP_OPERATION_DODGE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_DODGE_LEGACY (gimp_operation_dodge_legacy_get_type ()) +#define GIMP_OPERATION_DODGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacy)) +#define GIMP_OPERATION_DODGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacyClass)) +#define GIMP_IS_OPERATION_DODGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY)) +#define GIMP_IS_OPERATION_DODGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DODGE_LEGACY)) +#define GIMP_OPERATION_DODGE_LEGACY_GET_CLASS(obj)(G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacyClass)) + + +typedef struct _GimpOperationDodgeLegacy GimpOperationDodgeLegacy; +typedef struct _GimpOperationDodgeLegacyClass GimpOperationDodgeLegacyClass; + +struct _GimpOperationDodgeLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationDodgeLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_dodge_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DODGE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c new file mode 100644 index 0000000..77338c4 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c @@ -0,0 +1,125 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrainextractmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationgrainextractlegacy.h" + + +static gboolean gimp_operation_grain_extract_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationGrainExtractLegacy, gimp_operation_grain_extract_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_grain_extract_legacy_class_init (GimpOperationGrainExtractLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:grain-extract-legacy", + "description", "GIMP grain extract mode operation", + NULL); + + layer_mode_class->process = gimp_operation_grain_extract_legacy_process; +} + +static void +gimp_operation_grain_extract_legacy_init (GimpOperationGrainExtractLegacy *self) +{ +} + +static gboolean +gimp_operation_grain_extract_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] - layer[b] + 128.0f / 255.0f; + comp = CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h new file mode 100644 index 0000000..149ee09 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrainextractlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__ +#define __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY (gimp_operation_grain_extract_legacy_get_type ()) +#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacy)) +#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacyClass)) +#define GIMP_IS_OPERATION_GRAIN_EXTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY)) +#define GIMP_IS_OPERATION_GRAIN_EXTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY)) +#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacyClass)) + + +typedef struct _GimpOperationGrainExtractLegacy GimpOperationGrainExtractLegacy; +typedef struct _GimpOperationGrainExtractLegacyClass GimpOperationGrainExtractLegacyClass; + +struct _GimpOperationGrainExtractLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationGrainExtractLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_grain_extract_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c new file mode 100644 index 0000000..c12a995 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c @@ -0,0 +1,125 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrainmergemode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationgrainmergelegacy.h" + + +static gboolean gimp_operation_grain_merge_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationGrainMergeLegacy, gimp_operation_grain_merge_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_grain_merge_legacy_class_init (GimpOperationGrainMergeLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:grain-merge-legacy", + "description", "GIMP grain merge mode operation", + NULL); + + layer_mode_class->process = gimp_operation_grain_merge_legacy_process; +} + +static void +gimp_operation_grain_merge_legacy_init (GimpOperationGrainMergeLegacy *self) +{ +} + +static gboolean +gimp_operation_grain_merge_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] + layer[b] - 128.0f / 255.0f; + comp = CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask ++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h new file mode 100644 index 0000000..344e895 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationgrainmergelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__ +#define __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY (gimp_operation_grain_merge_legacy_get_type ()) +#define GIMP_OPERATION_GRAIN_MERGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacy)) +#define GIMP_OPERATION_GRAIN_MERGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacyClass)) +#define GIMP_IS_OPERATION_GRAIN_MERGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY)) +#define GIMP_IS_OPERATION_GRAIN_MERGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY)) +#define GIMP_OPERATION_GRAIN_MERGE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacyClass)) + + +typedef struct _GimpOperationGrainMergeLegacy GimpOperationGrainMergeLegacy; +typedef struct _GimpOperationGrainMergeLegacyClass GimpOperationGrainMergeLegacyClass; + +struct _GimpOperationGrainMergeLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationGrainMergeLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_grain_merge_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c new file mode 100644 index 0000000..68f8eda --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c @@ -0,0 +1,135 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhardlightmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationhardlightlegacy.h" + + +static gboolean gimp_operation_hardlight_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHardlightLegacy, gimp_operation_hardlight_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_hardlight_legacy_class_init (GimpOperationHardlightLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hardlight-legacy", + "description", "GIMP hardlight mode operation", + NULL); + + layer_mode_class->process = gimp_operation_hardlight_legacy_process; +} + +static void +gimp_operation_hardlight_legacy_init (GimpOperationHardlightLegacy *self) +{ +} + +static gboolean +gimp_operation_hardlight_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp; + + if (layer[b] > 128.0f / 255.0f) + { + comp = (1.0 - in[b]) * (1.0 - (layer[b] - 128.0f / 255.0f) * 2.0f); + comp = MIN (1.0f - comp, 1.0f); + } + else + { + comp = in[b] * (layer[b] * 2.0f); + comp = MIN (comp, 1.0f); + } + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask ++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h new file mode 100644 index 0000000..38791be --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhardlightlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HARDLIGHT_LEGACY_H__ +#define __GIMP_OPERATION_HARDLIGHT_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY (gimp_operation_hardlight_legacy_get_type ()) +#define GIMP_OPERATION_HARDLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacy)) +#define GIMP_OPERATION_HARDLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacyClass)) +#define GIMP_IS_OPERATION_HARDLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY)) +#define GIMP_IS_OPERATION_HARDLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY)) +#define GIMP_OPERATION_HARDLIGHT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacyClass)) + + +typedef struct _GimpOperationHardlightLegacy GimpOperationHardlightLegacy; +typedef struct _GimpOperationHardlightLegacyClass GimpOperationHardlightLegacyClass; + +struct _GimpOperationHardlightLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationHardlightLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_hardlight_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HARDLIGHT_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c new file mode 100644 index 0000000..6fd7a95 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c @@ -0,0 +1,141 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationcolormode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "../operations-types.h" + +#include "gimpoperationhslcolorlegacy.h" + + +static gboolean gimp_operation_hsl_color_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHslColorLegacy, gimp_operation_hsl_color_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_hsl_color_legacy_class_init (GimpOperationHslColorLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hsl-color-legacy", + "description", "GIMP color mode operation", + NULL); + + layer_mode_class->process = gimp_operation_hsl_color_legacy_process; +} + +static void +gimp_operation_hsl_color_legacy_init (GimpOperationHslColorLegacy *self) +{ +} + +static gboolean +gimp_operation_hsl_color_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + GimpHSL layer_hsl, out_hsl; + GimpRGB layer_rgb = {layer[0], layer[1], layer[2]}; + GimpRGB out_rgb = {in[0], in[1], in[2]}; + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat out_tmp[3]; + gfloat ratio = comp_alpha / new_alpha; + + gimp_rgb_to_hsl (&layer_rgb, &layer_hsl); + gimp_rgb_to_hsl (&out_rgb, &out_hsl); + + out_hsl.h = layer_hsl.h; + out_hsl.s = layer_hsl.s; + gimp_hsl_to_rgb (&out_hsl, &out_rgb); + + out_tmp[0] = out_rgb.r; + out_tmp[1] = out_rgb.g; + out_tmp[2] = out_rgb.b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h new file mode 100644 index 0000000..add2933 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhslcolorlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HSL_COLOR_LEGACY_H__ +#define __GIMP_OPERATION_HSL_COLOR_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY (gimp_operation_hsl_color_legacy_get_type ()) +#define GIMP_OPERATION_HSL_COLOR_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacy)) +#define GIMP_OPERATION_HSL_COLOR_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacyClass)) +#define GIMP_IS_OPERATION_HSL_COLOR_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY)) +#define GIMP_IS_OPERATION_HSL_COLOR_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY)) +#define GIMP_OPERATION_HSL_COLOR_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacyClass)) + + +typedef struct _GimpOperationHslColorLegacy GimpOperationHslColorLegacy; +typedef struct _GimpOperationHslColorLegacyClass GimpOperationHslColorLegacyClass; + +struct _GimpOperationHslColorLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationHslColorLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_hsl_color_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HSL_COLOR_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c new file mode 100644 index 0000000..eb9c040 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c @@ -0,0 +1,146 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhuemode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "../operations-types.h" + +#include "gimpoperationhsvhuelegacy.h" + + +static gboolean gimp_operation_hsv_hue_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHsvHueLegacy, gimp_operation_hsv_hue_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_hsv_hue_legacy_class_init (GimpOperationHsvHueLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hsv-hue-legacy", + "description", "GIMP hue mode operation", + NULL); + + layer_mode_class->process = gimp_operation_hsv_hue_legacy_process; +} + +static void +gimp_operation_hsv_hue_legacy_init (GimpOperationHsvHueLegacy *self) +{ +} + +static gboolean +gimp_operation_hsv_hue_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + GimpHSV layer_hsv, out_hsv; + GimpRGB layer_rgb = {layer[0], layer[1], layer[2]}; + GimpRGB out_rgb = {in[0], in[1], in[2]}; + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat out_tmp[3]; + gfloat ratio = comp_alpha / new_alpha; + + gimp_rgb_to_hsv (&layer_rgb, &layer_hsv); + gimp_rgb_to_hsv (&out_rgb, &out_hsv); + + /* Composition should have no effect if saturation is zero. + * otherwise, black would be painted red (see bug #123296). + */ + if (layer_hsv.s) + { + out_hsv.h = layer_hsv.h; + } + gimp_hsv_to_rgb (&out_hsv, &out_rgb); + + out_tmp[0] = out_rgb.r; + out_tmp[1] = out_rgb.g; + out_tmp[2] = out_rgb.b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h new file mode 100644 index 0000000..59ef125 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhsvhuelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HSV_HUE_LEGACY_H__ +#define __GIMP_OPERATION_HSV_HUE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_HSV_HUE_LEGACY (gimp_operation_hsv_hue_legacy_get_type ()) +#define GIMP_OPERATION_HSV_HUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacy)) +#define GIMP_OPERATION_HSV_HUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacyClass)) +#define GIMP_IS_OPERATION_HSV_HUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY)) +#define GIMP_IS_OPERATION_HSV_HUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY)) +#define GIMP_OPERATION_HSV_HUE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacyClass)) + + +typedef struct _GimpOperationHsvHueLegacy GimpOperationHsvHueLegacy; +typedef struct _GimpOperationHsvHueLegacyClass GimpOperationHsvHueLegacyClass; + +struct _GimpOperationHsvHueLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationHsvHueLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_hsv_hue_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HSV_HUE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c new file mode 100644 index 0000000..d150aa1 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c @@ -0,0 +1,140 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsaturationmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "../operations-types.h" + +#include "gimpoperationhsvsaturationlegacy.h" + + +static gboolean gimp_operation_hsv_saturation_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHsvSaturationLegacy, gimp_operation_hsv_saturation_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_hsv_saturation_legacy_class_init (GimpOperationHsvSaturationLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hsv-saturation-legacy", + "description", "GIMP saturation mode operation", + NULL); + + layer_mode_class->process = gimp_operation_hsv_saturation_legacy_process; +} + +static void +gimp_operation_hsv_saturation_legacy_init (GimpOperationHsvSaturationLegacy *self) +{ +} + +static gboolean +gimp_operation_hsv_saturation_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + GimpHSV layer_hsv, out_hsv; + GimpRGB layer_rgb = {layer[0], layer[1], layer[2]}; + GimpRGB out_rgb = {in[0], in[1], in[2]}; + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat out_tmp[3]; + gfloat ratio = comp_alpha / new_alpha; + + gimp_rgb_to_hsv (&layer_rgb, &layer_hsv); + gimp_rgb_to_hsv (&out_rgb, &out_hsv); + + out_hsv.s = layer_hsv.s; + gimp_hsv_to_rgb (&out_hsv, &out_rgb); + + out_tmp[0] = out_rgb.r; + out_tmp[1] = out_rgb.g; + out_tmp[2] = out_rgb.b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h new file mode 100644 index 0000000..ecb5589 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhsvsaturationlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__ +#define __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY (gimp_operation_hsv_saturation_legacy_get_type ()) +#define GIMP_OPERATION_HSV_SATURATION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacy)) +#define GIMP_OPERATION_HSV_SATURATION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacyClass)) +#define GIMP_IS_OPERATION_HSV_SATURATION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY)) +#define GIMP_IS_OPERATION_HSV_SATURATION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY)) +#define GIMP_OPERATION_HSV_SATURATION_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacyClass)) + + +typedef struct _GimpOperationHsvSaturationLegacy GimpOperationHsvSaturationLegacy; +typedef struct _GimpOperationHsvSaturationLegacyClass GimpOperationHsvSaturationLegacyClass; + +struct _GimpOperationHsvSaturationLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationHsvSaturationLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_hsv_saturation_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c new file mode 100644 index 0000000..b873325 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c @@ -0,0 +1,140 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationvaluemode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "../operations-types.h" + +#include "gimpoperationhsvvaluelegacy.h" + + +static gboolean gimp_operation_hsv_value_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationHsvValueLegacy, gimp_operation_hsv_value_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_hsv_value_legacy_class_init (GimpOperationHsvValueLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:hsv-value-legacy", + "description", "GIMP value mode operation", + NULL); + + layer_mode_class->process = gimp_operation_hsv_value_legacy_process; +} + +static void +gimp_operation_hsv_value_legacy_init (GimpOperationHsvValueLegacy *self) +{ +} + +static gboolean +gimp_operation_hsv_value_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + GimpHSV layer_hsv, out_hsv; + GimpRGB layer_rgb = {layer[0], layer[1], layer[2]}; + GimpRGB out_rgb = {in[0], in[1], in[2]}; + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat out_tmp[3]; + gfloat ratio = comp_alpha / new_alpha; + + gimp_rgb_to_hsv (&layer_rgb, &layer_hsv); + gimp_rgb_to_hsv (&out_rgb, &out_hsv); + + out_hsv.v = layer_hsv.v; + gimp_hsv_to_rgb (&out_hsv, &out_rgb); + + out_tmp[0] = out_rgb.r; + out_tmp[1] = out_rgb.g; + out_tmp[2] = out_rgb.b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h new file mode 100644 index 0000000..7701ffd --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationhsvvaluelegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_HSV_VALUE_LEGACY_H__ +#define __GIMP_OPERATION_HSV_VALUE_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY (gimp_operation_hsv_value_legacy_get_type ()) +#define GIMP_OPERATION_HSV_VALUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacy)) +#define GIMP_OPERATION_HSV_VALUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacyClass)) +#define GIMP_IS_OPERATION_HSV_VALUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY)) +#define GIMP_IS_OPERATION_HSV_VALUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY)) +#define GIMP_OPERATION_HSV_VALUE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacyClass)) + + +typedef struct _GimpOperationHsvValueLegacy GimpOperationHsvValueLegacy; +typedef struct _GimpOperationHsvValueLegacyClass GimpOperationHsvValueLegacyClass; + +struct _GimpOperationHsvValueLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationHsvValueLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_hsv_value_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_HSV_VALUE_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c new file mode 100644 index 0000000..0e54a0c --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlightenonlylegacy.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationlightenonlylegacy.h" + + +static gboolean gimp_operation_lighten_only_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationLightenOnlyLegacy, gimp_operation_lighten_only_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_lighten_only_legacy_class_init (GimpOperationLightenOnlyLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:lighten-only-legacy", + "description", "GIMP lighten only legacy operation", + NULL); + + layer_mode_class->process = gimp_operation_lighten_only_legacy_process; +} + +static void +gimp_operation_lighten_only_legacy_init (GimpOperationLightenOnlyLegacy *self) +{ +} + +static gboolean +gimp_operation_lighten_only_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat ratio = comp_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = MAX (layer[b], in[b]); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h new file mode 100644 index 0000000..2417d57 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlightenonlylegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__ +#define __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY (gimp_operation_lighten_only_legacy_get_type ()) +#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacy)) +#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacyClass)) +#define GIMP_IS_OPERATION_LIGHTEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY)) +#define GIMP_IS_OPERATION_LIGHTEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY)) +#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacyClass)) + + +typedef struct _GimpOperationLightenOnlyLegacy GimpOperationLightenOnlyLegacy; +typedef struct _GimpOperationLightenOnlyLegacyClass GimpOperationLightenOnlyLegacyClass; + +struct _GimpOperationLightenOnlyLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationLightenOnlyLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_lighten_only_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c new file mode 100644 index 0000000..69c63c4 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmultiplylegacy.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations/operations-types.h" + +#include "gimpoperationmultiplylegacy.h" + + +static gboolean gimp_operation_multiply_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationMultiplyLegacy, gimp_operation_multiply_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_multiply_legacy_class_init (GimpOperationMultiplyLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:multiply-legacy", + "description", "GIMP multiply legacy operation", + NULL); + + layer_mode_class->process = gimp_operation_multiply_legacy_process; +} + +static void +gimp_operation_multiply_legacy_init (GimpOperationMultiplyLegacy *self) +{ +} + +static gboolean +gimp_operation_multiply_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = layer[b] * in[b]; + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h new file mode 100644 index 0000000..f099ebd --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmultiplylegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_MULTIPLY_LEGACY_H__ +#define __GIMP_OPERATION_MULTIPLY_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_MULTIPLY_LEGACY (gimp_operation_multiply_legacy_get_type ()) +#define GIMP_OPERATION_MULTIPLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacy)) +#define GIMP_OPERATION_MULTIPLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacyClass)) +#define GIMP_IS_OPERATION_MULTIPLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY)) +#define GIMP_IS_OPERATION_MULTIPLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY)) +#define GIMP_OPERATION_MULTIPLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacyClass)) + + +typedef struct _GimpOperationMultiplyLegacy GimpOperationMultiplyLegacy; +typedef struct _GimpOperationMultiplyLegacyClass GimpOperationMultiplyLegacyClass; + +struct _GimpOperationMultiplyLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationMultiplyLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_multiply_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_MULTIPLY_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c new file mode 100644 index 0000000..f2ff2fc --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c @@ -0,0 +1,124 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationscreenlegacy.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationscreenlegacy.h" + + +static gboolean gimp_operation_screen_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationScreenLegacy, gimp_operation_screen_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_screen_legacy_class_init (GimpOperationScreenLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:screen-legacy", + "description", "GIMP screen mode operation", + NULL); + + layer_mode_class->process = gimp_operation_screen_legacy_process; +} + +static void +gimp_operation_screen_legacy_init (GimpOperationScreenLegacy *self) +{ +} + +static gboolean +gimp_operation_screen_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = 1.0f - (1.0f - in[b]) * (1.0f - layer[b]); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h new file mode 100644 index 0000000..5d0dc9e --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationscreenlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SCREEN_LEGACY_H__ +#define __GIMP_OPERATION_SCREEN_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_SCREEN_LEGACY (gimp_operation_screen_legacy_get_type ()) +#define GIMP_OPERATION_SCREEN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacy)) +#define GIMP_OPERATION_SCREEN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacyClass)) +#define GIMP_IS_OPERATION_SCREEN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY)) +#define GIMP_IS_OPERATION_SCREEN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SCREEN_LEGACY)) +#define GIMP_OPERATION_SCREEN_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacyClass)) + + +typedef struct _GimpOperationScreenLegacy GimpOperationScreenLegacy; +typedef struct _GimpOperationScreenLegacyClass GimpOperationScreenLegacyClass; + +struct _GimpOperationScreenLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationScreenLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_screen_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SCREEN_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c new file mode 100644 index 0000000..1736e00 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c @@ -0,0 +1,157 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsoftlightmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationsoftlightlegacy.h" + + +static gboolean gimp_operation_softlight_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationSoftlightLegacy, gimp_operation_softlight_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static const gchar* reference_xml = "" +"" +"" +" " +" " +" B.png" +" " +" " +"" +"" +" " +" A.png" +" " +"" +""; + + +static void +gimp_operation_softlight_legacy_class_init (GimpOperationSoftlightLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:softlight-legacy", + "description", "GIMP softlight mode operation", + "reference-image", "soft-light-mode.png", + "reference-composition", reference_xml, + NULL); + + layer_mode_class->process = gimp_operation_softlight_legacy_process; +} + +static void +gimp_operation_softlight_legacy_init (GimpOperationSoftlightLegacy *self) +{ +} + +static gboolean +gimp_operation_softlight_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + { +#if 0 + /* softlight is now used for what GIMP formerly called + * OVERLAY. We fixed OVERLAY to use the right math + * (under the name NEW_OVERLAY), and redirect uses of + * the old OVERLAY blend mode here. This math was + * formerly used for OVERLAY and is exactly the same as + * the multiply, screen, comp math used below. + * See bug #673501. + */ + gfloat comp = in[b] * (in[b] + (2.0f * layer[b]) * (1.0f - in[b])); +#endif + + gfloat multiply = in[b] * layer[b]; + gfloat screen = 1.0f - (1.0f - in[b]) * (1.0f - layer[b]); + gfloat comp = (1.0f - in[b]) * multiply + in[b] * screen; + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask ++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h new file mode 100644 index 0000000..aa8930e --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsoftlightlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__ +#define __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY (gimp_operation_softlight_legacy_get_type ()) +#define GIMP_OPERATION_SOFTLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacy)) +#define GIMP_OPERATION_SOFTLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacyClass)) +#define GIMP_IS_OPERATION_SOFTLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY)) +#define GIMP_IS_OPERATION_SOFTLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY)) +#define GIMP_OPERATION_SOFTLIGHT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacyClass)) + + +typedef struct _GimpOperationSoftlightLegacy GimpOperationSoftlightLegacy; +typedef struct _GimpOperationSoftlightLegacyClass GimpOperationSoftlightLegacyClass; + +struct _GimpOperationSoftlightLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationSoftlightLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_softlight_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__ */ diff --git a/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c new file mode 100644 index 0000000..0bf3b5e --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c @@ -0,0 +1,125 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsubtractmode.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationsubtractlegacy.h" + + +static gboolean gimp_operation_subtract_legacy_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationSubtractLegacy, gimp_operation_subtract_legacy, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_subtract_legacy_class_init (GimpOperationSubtractLegacyClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:subtract-legacy", + "description", "GIMP subtract mode operation", + NULL); + + layer_mode_class->process = gimp_operation_subtract_legacy_process; +} + +static void +gimp_operation_subtract_legacy_init (GimpOperationSubtractLegacy *self) +{ +} + +static gboolean +gimp_operation_subtract_legacy_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + + while (samples--) + { + gfloat comp_alpha, new_alpha; + + comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity; + if (mask) + comp_alpha *= *mask; + + new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha; + + if (comp_alpha && new_alpha) + { + gint b; + gfloat ratio = comp_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + gfloat comp = in[b] - layer[b]; + comp = CLAMP (comp, 0.0f, 1.0f); + + out[b] = comp * ratio + in[b] * (1.0f - ratio); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + layer += 4; + out += 4; + + if (mask) + mask++; + } + + return TRUE; +} diff --git a/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h new file mode 100644 index 0000000..34a55d5 --- /dev/null +++ b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsubtractlegacy.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_SUBTRACT_LEGACY_H__ +#define __GIMP_OPERATION_SUBTRACT_LEGACY_H__ + + +#include "operations/layer-modes/gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_SUBTRACT_LEGACY (gimp_operation_subtract_legacy_get_type ()) +#define GIMP_OPERATION_SUBTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacy)) +#define GIMP_OPERATION_SUBTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacyClass)) +#define GIMP_IS_OPERATION_SUBTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY)) +#define GIMP_IS_OPERATION_SUBTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY)) +#define GIMP_OPERATION_SUBTRACT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacyClass)) + + +typedef struct _GimpOperationSubtractLegacy GimpOperationSubtractLegacy; +typedef struct _GimpOperationSubtractLegacyClass GimpOperationSubtractLegacyClass; + +struct _GimpOperationSubtractLegacy +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationSubtractLegacyClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_subtract_legacy_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SUBTRACT_LEGACY_H__ */ diff --git a/app/operations/layer-modes/Makefile.am b/app/operations/layer-modes/Makefile.am new file mode 100644 index 0000000..24af8fe --- /dev/null +++ b/app/operations/layer-modes/Makefile.am @@ -0,0 +1,79 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Layer-Modes\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libapplayermodes-generic.a \ + libapplayermodes-sse2.a \ + libapplayermodes-sse4.a \ + libapplayermodes.a + +libapplayermodes_generic_a_sources = \ + gimp-layer-modes.c \ + gimp-layer-modes.h \ + \ + gimpoperationlayermode.c \ + gimpoperationlayermode.h \ + gimpoperationlayermode-blend.c \ + gimpoperationlayermode-blend.h \ + gimpoperationlayermode-composite.c \ + gimpoperationlayermode-composite.h \ + \ + gimpoperationantierase.c \ + gimpoperationantierase.h \ + gimpoperationbehind.c \ + gimpoperationbehind.h \ + gimpoperationdissolve.c \ + gimpoperationdissolve.h \ + gimpoperationerase.c \ + gimpoperationerase.h \ + gimpoperationmerge.c \ + gimpoperationmerge.h \ + gimpoperationnormal.c \ + gimpoperationnormal.h \ + gimpoperationpassthrough.c \ + gimpoperationpassthrough.h \ + gimpoperationreplace.c \ + gimpoperationreplace.h \ + gimpoperationsplit.c \ + gimpoperationsplit.h + +libapplayermodes_sse2_a_sources = \ + gimpoperationlayermode-composite-sse2.c \ + \ + gimpoperationnormal-sse2.c + +libapplayermodes_sse4_a_sources = \ + gimpoperationnormal-sse4.c + + +libapplayermodes_generic_a_SOURCES = $(libapplayermodes_generic_a_sources) + +libapplayermodes_sse2_a_SOURCES = $(libapplayermodes_sse2_a_sources) + +libapplayermodes_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS) + +libapplayermodes_sse4_a_SOURCES = $(libapplayermodes_sse4_a_sources) + +libapplayermodes_sse4_a_CFLAGS = $(SSE4_1_EXTRA_CFLAGS) + +libapplayermodes_a_SOURCES = + + +libapplayermodes.a: libapplayermodes-generic.a \ + libapplayermodes-sse2.a \ + libapplayermodes-sse4.a + $(AR) $(ARFLAGS) libapplayermodes.a \ + $(libapplayermodes_generic_a_OBJECTS) \ + $(libapplayermodes_sse2_a_OBJECTS) \ + $(libapplayermodes_sse4_a_OBJECTS) + $(RANLIB) libapplayermodes.a diff --git a/app/operations/layer-modes/Makefile.in b/app/operations/layer-modes/Makefile.in new file mode 100644 index 0000000..ad8d4c1 --- /dev/null +++ b/app/operations/layer-modes/Makefile.in @@ -0,0 +1,1115 @@ +# 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 = app/operations/layer-modes +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 = +libapplayermodes_generic_a_AR = $(AR) $(ARFLAGS) +libapplayermodes_generic_a_LIBADD = +am__objects_1 = gimp-layer-modes.$(OBJEXT) \ + gimpoperationlayermode.$(OBJEXT) \ + gimpoperationlayermode-blend.$(OBJEXT) \ + gimpoperationlayermode-composite.$(OBJEXT) \ + gimpoperationantierase.$(OBJEXT) gimpoperationbehind.$(OBJEXT) \ + gimpoperationdissolve.$(OBJEXT) gimpoperationerase.$(OBJEXT) \ + gimpoperationmerge.$(OBJEXT) gimpoperationnormal.$(OBJEXT) \ + gimpoperationpassthrough.$(OBJEXT) \ + gimpoperationreplace.$(OBJEXT) gimpoperationsplit.$(OBJEXT) +am_libapplayermodes_generic_a_OBJECTS = $(am__objects_1) +libapplayermodes_generic_a_OBJECTS = \ + $(am_libapplayermodes_generic_a_OBJECTS) +libapplayermodes_sse2_a_AR = $(AR) $(ARFLAGS) +libapplayermodes_sse2_a_LIBADD = +am__objects_2 = libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.$(OBJEXT) \ + libapplayermodes_sse2_a-gimpoperationnormal-sse2.$(OBJEXT) +am_libapplayermodes_sse2_a_OBJECTS = $(am__objects_2) +libapplayermodes_sse2_a_OBJECTS = \ + $(am_libapplayermodes_sse2_a_OBJECTS) +libapplayermodes_sse4_a_AR = $(AR) $(ARFLAGS) +libapplayermodes_sse4_a_LIBADD = +am__objects_3 = \ + libapplayermodes_sse4_a-gimpoperationnormal-sse4.$(OBJEXT) +am_libapplayermodes_sse4_a_OBJECTS = $(am__objects_3) +libapplayermodes_sse4_a_OBJECTS = \ + $(am_libapplayermodes_sse4_a_OBJECTS) +libapplayermodes_a_AR = $(AR) $(ARFLAGS) +libapplayermodes_a_LIBADD = +am_libapplayermodes_a_OBJECTS = +libapplayermodes_a_OBJECTS = $(am_libapplayermodes_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)/gimp-layer-modes.Po \ + ./$(DEPDIR)/gimpoperationantierase.Po \ + ./$(DEPDIR)/gimpoperationbehind.Po \ + ./$(DEPDIR)/gimpoperationdissolve.Po \ + ./$(DEPDIR)/gimpoperationerase.Po \ + ./$(DEPDIR)/gimpoperationlayermode-blend.Po \ + ./$(DEPDIR)/gimpoperationlayermode-composite.Po \ + ./$(DEPDIR)/gimpoperationlayermode.Po \ + ./$(DEPDIR)/gimpoperationmerge.Po \ + ./$(DEPDIR)/gimpoperationnormal.Po \ + ./$(DEPDIR)/gimpoperationpassthrough.Po \ + ./$(DEPDIR)/gimpoperationreplace.Po \ + ./$(DEPDIR)/gimpoperationsplit.Po \ + ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po \ + ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po \ + ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po +am__mv = mv -f +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 = +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 = $(libapplayermodes_generic_a_SOURCES) \ + $(libapplayermodes_sse2_a_SOURCES) \ + $(libapplayermodes_sse4_a_SOURCES) \ + $(libapplayermodes_a_SOURCES) +DIST_SOURCES = $(libapplayermodes_generic_a_SOURCES) \ + $(libapplayermodes_sse2_a_SOURCES) \ + $(libapplayermodes_sse4_a_SOURCES) \ + $(libapplayermodes_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Layer-Modes\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = \ + libapplayermodes-generic.a \ + libapplayermodes-sse2.a \ + libapplayermodes-sse4.a \ + libapplayermodes.a + +libapplayermodes_generic_a_sources = \ + gimp-layer-modes.c \ + gimp-layer-modes.h \ + \ + gimpoperationlayermode.c \ + gimpoperationlayermode.h \ + gimpoperationlayermode-blend.c \ + gimpoperationlayermode-blend.h \ + gimpoperationlayermode-composite.c \ + gimpoperationlayermode-composite.h \ + \ + gimpoperationantierase.c \ + gimpoperationantierase.h \ + gimpoperationbehind.c \ + gimpoperationbehind.h \ + gimpoperationdissolve.c \ + gimpoperationdissolve.h \ + gimpoperationerase.c \ + gimpoperationerase.h \ + gimpoperationmerge.c \ + gimpoperationmerge.h \ + gimpoperationnormal.c \ + gimpoperationnormal.h \ + gimpoperationpassthrough.c \ + gimpoperationpassthrough.h \ + gimpoperationreplace.c \ + gimpoperationreplace.h \ + gimpoperationsplit.c \ + gimpoperationsplit.h + +libapplayermodes_sse2_a_sources = \ + gimpoperationlayermode-composite-sse2.c \ + \ + gimpoperationnormal-sse2.c + +libapplayermodes_sse4_a_sources = \ + gimpoperationnormal-sse4.c + +libapplayermodes_generic_a_SOURCES = $(libapplayermodes_generic_a_sources) +libapplayermodes_sse2_a_SOURCES = $(libapplayermodes_sse2_a_sources) +libapplayermodes_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS) +libapplayermodes_sse4_a_SOURCES = $(libapplayermodes_sse4_a_sources) +libapplayermodes_sse4_a_CFLAGS = $(SSE4_1_EXTRA_CFLAGS) +libapplayermodes_a_SOURCES = +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 app/operations/layer-modes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/operations/layer-modes/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) + +libapplayermodes-generic.a: $(libapplayermodes_generic_a_OBJECTS) $(libapplayermodes_generic_a_DEPENDENCIES) $(EXTRA_libapplayermodes_generic_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapplayermodes-generic.a + $(AM_V_AR)$(libapplayermodes_generic_a_AR) libapplayermodes-generic.a $(libapplayermodes_generic_a_OBJECTS) $(libapplayermodes_generic_a_LIBADD) + $(AM_V_at)$(RANLIB) libapplayermodes-generic.a + +libapplayermodes-sse2.a: $(libapplayermodes_sse2_a_OBJECTS) $(libapplayermodes_sse2_a_DEPENDENCIES) $(EXTRA_libapplayermodes_sse2_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapplayermodes-sse2.a + $(AM_V_AR)$(libapplayermodes_sse2_a_AR) libapplayermodes-sse2.a $(libapplayermodes_sse2_a_OBJECTS) $(libapplayermodes_sse2_a_LIBADD) + $(AM_V_at)$(RANLIB) libapplayermodes-sse2.a + +libapplayermodes-sse4.a: $(libapplayermodes_sse4_a_OBJECTS) $(libapplayermodes_sse4_a_DEPENDENCIES) $(EXTRA_libapplayermodes_sse4_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapplayermodes-sse4.a + $(AM_V_AR)$(libapplayermodes_sse4_a_AR) libapplayermodes-sse4.a $(libapplayermodes_sse4_a_OBJECTS) $(libapplayermodes_sse4_a_LIBADD) + $(AM_V_at)$(RANLIB) libapplayermodes-sse4.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-layer-modes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationantierase.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbehind.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdissolve.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationerase.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode-blend.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode-composite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmerge.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationnormal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationpassthrough.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationreplace.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsplit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.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 $@ $< + +libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o: gimpoperationlayermode-composite-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o `test -f 'gimpoperationlayermode-composite-sse2.c' || echo '$(srcdir)/'`gimpoperationlayermode-composite-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationlayermode-composite-sse2.c' object='libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.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) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o `test -f 'gimpoperationlayermode-composite-sse2.c' || echo '$(srcdir)/'`gimpoperationlayermode-composite-sse2.c + +libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj: gimpoperationlayermode-composite-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj `if test -f 'gimpoperationlayermode-composite-sse2.c'; then $(CYGPATH_W) 'gimpoperationlayermode-composite-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationlayermode-composite-sse2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationlayermode-composite-sse2.c' object='libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.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) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj `if test -f 'gimpoperationlayermode-composite-sse2.c'; then $(CYGPATH_W) 'gimpoperationlayermode-composite-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationlayermode-composite-sse2.c'; fi` + +libapplayermodes_sse2_a-gimpoperationnormal-sse2.o: gimpoperationnormal-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationnormal-sse2.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.o `test -f 'gimpoperationnormal-sse2.c' || echo '$(srcdir)/'`gimpoperationnormal-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse2.c' object='libapplayermodes_sse2_a-gimpoperationnormal-sse2.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) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.o `test -f 'gimpoperationnormal-sse2.c' || echo '$(srcdir)/'`gimpoperationnormal-sse2.c + +libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj: gimpoperationnormal-sse2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj `if test -f 'gimpoperationnormal-sse2.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse2.c' object='libapplayermodes_sse2_a-gimpoperationnormal-sse2.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) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj `if test -f 'gimpoperationnormal-sse2.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse2.c'; fi` + +libapplayermodes_sse4_a-gimpoperationnormal-sse4.o: gimpoperationnormal-sse4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse4_a-gimpoperationnormal-sse4.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.o `test -f 'gimpoperationnormal-sse4.c' || echo '$(srcdir)/'`gimpoperationnormal-sse4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse4.c' object='libapplayermodes_sse4_a-gimpoperationnormal-sse4.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) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.o `test -f 'gimpoperationnormal-sse4.c' || echo '$(srcdir)/'`gimpoperationnormal-sse4.c + +libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj: gimpoperationnormal-sse4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj `if test -f 'gimpoperationnormal-sse4.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse4.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse4.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse4.c' object='libapplayermodes_sse4_a-gimpoperationnormal-sse4.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) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj `if test -f 'gimpoperationnormal-sse4.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse4.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse4.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 $(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)/gimp-layer-modes.Po + -rm -f ./$(DEPDIR)/gimpoperationantierase.Po + -rm -f ./$(DEPDIR)/gimpoperationbehind.Po + -rm -f ./$(DEPDIR)/gimpoperationdissolve.Po + -rm -f ./$(DEPDIR)/gimpoperationerase.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode-blend.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode-composite.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode.Po + -rm -f ./$(DEPDIR)/gimpoperationmerge.Po + -rm -f ./$(DEPDIR)/gimpoperationnormal.Po + -rm -f ./$(DEPDIR)/gimpoperationpassthrough.Po + -rm -f ./$(DEPDIR)/gimpoperationreplace.Po + -rm -f ./$(DEPDIR)/gimpoperationsplit.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.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)/gimp-layer-modes.Po + -rm -f ./$(DEPDIR)/gimpoperationantierase.Po + -rm -f ./$(DEPDIR)/gimpoperationbehind.Po + -rm -f ./$(DEPDIR)/gimpoperationdissolve.Po + -rm -f ./$(DEPDIR)/gimpoperationerase.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode-blend.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode-composite.Po + -rm -f ./$(DEPDIR)/gimpoperationlayermode.Po + -rm -f ./$(DEPDIR)/gimpoperationmerge.Po + -rm -f ./$(DEPDIR)/gimpoperationnormal.Po + -rm -f ./$(DEPDIR)/gimpoperationpassthrough.Po + -rm -f ./$(DEPDIR)/gimpoperationreplace.Po + -rm -f ./$(DEPDIR)/gimpoperationsplit.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po + -rm -f ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.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 + + +libapplayermodes.a: libapplayermodes-generic.a \ + libapplayermodes-sse2.a \ + libapplayermodes-sse4.a + $(AR) $(ARFLAGS) libapplayermodes.a \ + $(libapplayermodes_generic_a_OBJECTS) \ + $(libapplayermodes_sse2_a_OBJECTS) \ + $(libapplayermodes_sse4_a_OBJECTS) + $(RANLIB) libapplayermodes.a + +# 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/app/operations/layer-modes/gimp-layer-modes.c b/app/operations/layer-modes/gimp-layer-modes.c new file mode 100644 index 0000000..deb1f3d --- /dev/null +++ b/app/operations/layer-modes/gimp-layer-modes.c @@ -0,0 +1,1522 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-layer-modes.c + * Copyright (C) 2017 Michael Natterer + * Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "../operations-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimpoperationlayermode.h" +#include "gimpoperationlayermode-blend.h" + +#include "gimp-layer-modes.h" + + +typedef struct _GimpLayerModeInfo GimpLayerModeInfo; + +struct _GimpLayerModeInfo +{ + GimpLayerMode layer_mode; + const gchar *op_name; + GimpLayerModeBlendFunc blend_function; + GimpLayerModeFlags flags; + GimpLayerModeContext context; + GimpLayerCompositeMode paint_composite_mode; + GimpLayerCompositeMode composite_mode; + GimpLayerColorSpace composite_space; + GimpLayerColorSpace blend_space; +}; + + +/* static variables */ + +static const GimpLayerModeInfo layer_mode_infos[] = +{ + { GIMP_LAYER_MODE_NORMAL_LEGACY, + + .op_name = "gimp:normal", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DISSOLVE, + + .op_name = "gimp:dissolve", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION + }, + + { GIMP_LAYER_MODE_BEHIND_LEGACY, + + .op_name = "gimp:behind", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT | + GIMP_LAYER_MODE_CONTEXT_FILTER, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_MULTIPLY_LEGACY, + + .op_name = "gimp:multiply-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_SCREEN_LEGACY, + + .op_name = "gimp:screen-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_OVERLAY_LEGACY, + + .op_name = "gimp:softlight-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, + + .op_name = "gimp:difference-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_ADDITION_LEGACY, + + .op_name = "gimp:addition-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_SUBTRACT_LEGACY, + + .op_name = "gimp:subtract-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, + + .op_name = "gimp:darken-only-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, + + .op_name = "gimp:lighten-only-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSV_HUE_LEGACY, + + .op_name = "gimp:hsv-hue-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, + + .op_name = "gimp:hsv-saturation-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, + + .op_name = "gimp:hsl-color-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, + + .op_name = "gimp:hsv-value-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DIVIDE_LEGACY, + + .op_name = "gimp:divide-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DODGE_LEGACY, + + .op_name = "gimp:dodge-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_BURN_LEGACY, + + .op_name = "gimp:burn-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, + + .op_name = "gimp:hardlight-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, + + .op_name = "gimp:softlight-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, + + .op_name = "gimp:grain-extract-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, + + .op_name = "gimp:grain-merge-legacy", + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_color_erase, + .flags = GIMP_LAYER_MODE_FLAG_LEGACY | + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_SUBTRACTIVE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT | + GIMP_LAYER_MODE_CONTEXT_FILTER, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_OVERLAY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_overlay, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LCH_HUE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_lch_hue, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_LAB + }, + + { GIMP_LAYER_MODE_LCH_CHROMA, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_lch_chroma, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_LAB + }, + + { GIMP_LAYER_MODE_LCH_COLOR, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_lch_color, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_LAB + }, + + { GIMP_LAYER_MODE_LCH_LIGHTNESS, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_lch_lightness, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_LAB + }, + + { GIMP_LAYER_MODE_NORMAL, + + .op_name = "gimp:normal", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_BEHIND, + + .op_name = "gimp:behind", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT | + GIMP_LAYER_MODE_CONTEXT_FILTER, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_MULTIPLY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_multiply, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_SCREEN, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_screen, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DIFFERENCE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_difference, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_ADDITION, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_addition, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_SUBTRACT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_subtract, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_DARKEN_ONLY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_darken_only, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + /* no blend_space: reuse composite space, no conversion thus fewer copies */ + }, + + { GIMP_LAYER_MODE_LIGHTEN_ONLY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_lighten_only, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + /* no blend_space: reuse composite space, no conversion thus fewer copies */ + }, + + { GIMP_LAYER_MODE_HSV_HUE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hsv_hue, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSV_SATURATION, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hsv_saturation, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSL_COLOR, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hsl_color, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HSV_VALUE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hsv_value, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_DIVIDE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_divide, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_DODGE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_dodge, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_BURN, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_burn, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HARDLIGHT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hardlight, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_SOFTLIGHT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_softlight, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_GRAIN_EXTRACT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_grain_extract, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_GRAIN_MERGE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_grain_merge, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_VIVID_LIGHT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_vivid_light, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_PIN_LIGHT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_pin_light, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LINEAR_LIGHT, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_linear_light, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_HARD_MIX, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_hard_mix, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_EXCLUSION, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_exclusion, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LINEAR_BURN, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_linear_burn, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_luma_darken_only, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_luma_lighten_only, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL + }, + + { GIMP_LAYER_MODE_LUMINANCE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_luminance, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_COLOR_ERASE, + + .op_name = "gimp:layer-mode", + .blend_function = gimp_operation_layer_mode_blend_color_erase, + .flags = GIMP_LAYER_MODE_FLAG_SUBTRACTIVE, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_ERASE, + + .op_name = "gimp:erase", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_SUBTRACTIVE | + GIMP_LAYER_MODE_FLAG_ALPHA_ONLY | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_MERGE, + + .op_name = "gimp:merge", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_SPLIT, + + .op_name = "gimp:split", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_SUBTRACTIVE | + GIMP_LAYER_MODE_FLAG_ALPHA_ONLY | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_ALL, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, + .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP + }, + + { GIMP_LAYER_MODE_PASS_THROUGH, + + .op_name = "gimp:pass-through", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_GROUP, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_REPLACE, + + .op_name = "gimp:replace", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_FILTER, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + + { GIMP_LAYER_MODE_ANTI_ERASE, + + .op_name = "gimp:anti-erase", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_ALPHA_ONLY, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION + } +}; + +static const GimpLayerMode layer_mode_group_default[] = +{ + GIMP_LAYER_MODE_PASS_THROUGH, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_REPLACE, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_NORMAL, + GIMP_LAYER_MODE_DISSOLVE, + GIMP_LAYER_MODE_BEHIND, + GIMP_LAYER_MODE_COLOR_ERASE, + GIMP_LAYER_MODE_ERASE, + GIMP_LAYER_MODE_ANTI_ERASE, + GIMP_LAYER_MODE_MERGE, + GIMP_LAYER_MODE_SPLIT, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_LIGHTEN_ONLY, + GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, + GIMP_LAYER_MODE_SCREEN, + GIMP_LAYER_MODE_DODGE, + GIMP_LAYER_MODE_ADDITION, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_DARKEN_ONLY, + GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, + GIMP_LAYER_MODE_MULTIPLY, + GIMP_LAYER_MODE_BURN, + GIMP_LAYER_MODE_LINEAR_BURN, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_OVERLAY, + GIMP_LAYER_MODE_SOFTLIGHT, + GIMP_LAYER_MODE_HARDLIGHT, + GIMP_LAYER_MODE_VIVID_LIGHT, + GIMP_LAYER_MODE_PIN_LIGHT, + GIMP_LAYER_MODE_LINEAR_LIGHT, + GIMP_LAYER_MODE_HARD_MIX, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_DIFFERENCE, + GIMP_LAYER_MODE_EXCLUSION, + GIMP_LAYER_MODE_SUBTRACT, + GIMP_LAYER_MODE_GRAIN_EXTRACT, + GIMP_LAYER_MODE_GRAIN_MERGE, + GIMP_LAYER_MODE_DIVIDE, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_HSV_HUE, + GIMP_LAYER_MODE_HSV_SATURATION, + GIMP_LAYER_MODE_HSL_COLOR, + GIMP_LAYER_MODE_HSV_VALUE, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_LCH_HUE, + GIMP_LAYER_MODE_LCH_CHROMA, + GIMP_LAYER_MODE_LCH_COLOR, + GIMP_LAYER_MODE_LCH_LIGHTNESS, + GIMP_LAYER_MODE_LUMINANCE +}; + +static const GimpLayerMode layer_mode_group_legacy[] = +{ + GIMP_LAYER_MODE_NORMAL_LEGACY, + GIMP_LAYER_MODE_DISSOLVE, + GIMP_LAYER_MODE_BEHIND_LEGACY, + GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, + GIMP_LAYER_MODE_SCREEN_LEGACY, + GIMP_LAYER_MODE_DODGE_LEGACY, + GIMP_LAYER_MODE_ADDITION_LEGACY, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, + GIMP_LAYER_MODE_MULTIPLY_LEGACY, + GIMP_LAYER_MODE_BURN_LEGACY, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_OVERLAY, + GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, + GIMP_LAYER_MODE_HARDLIGHT_LEGACY, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_DIFFERENCE_LEGACY, + GIMP_LAYER_MODE_SUBTRACT_LEGACY, + GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, + GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, + GIMP_LAYER_MODE_DIVIDE_LEGACY, + + GIMP_LAYER_MODE_SEPARATOR, + + GIMP_LAYER_MODE_HSV_HUE_LEGACY, + GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, + GIMP_LAYER_MODE_HSL_COLOR_LEGACY, + GIMP_LAYER_MODE_HSV_VALUE_LEGACY +}; + +static const GimpLayerMode layer_mode_groups[][2] = +{ + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_NORMAL, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_NORMAL_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DISSOLVE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DISSOLVE + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_BEHIND, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_BEHIND_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_MULTIPLY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_MULTIPLY_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SCREEN, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SCREEN_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_OVERLAY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DIFFERENCE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DIFFERENCE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ADDITION, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_ADDITION_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SUBTRACT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SUBTRACT_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DARKEN_ONLY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LIGHTEN_ONLY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_HUE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_HUE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_SATURATION, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_SATURATION_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSL_COLOR, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSL_COLOR_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_VALUE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_VALUE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DIVIDE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DIVIDE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DODGE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DODGE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_BURN, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_BURN_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HARDLIGHT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HARDLIGHT_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SOFTLIGHT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_GRAIN_EXTRACT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_GRAIN_MERGE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_COLOR_ERASE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_VIVID_LIGHT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_PIN_LIGHT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LINEAR_LIGHT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HARD_MIX, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_EXCLUSION, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LINEAR_BURN, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMINANCE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ERASE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_MERGE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SPLIT, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_PASS_THROUGH, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1, + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_REPLACE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ANTI_ERASE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + } +}; + + +/* public functions */ + +void +gimp_layer_modes_init (void) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (layer_mode_infos); i++) + { + gimp_assert ((GimpLayerMode) i == layer_mode_infos[i].layer_mode); + } +} + +static const GimpLayerModeInfo * +gimp_layer_mode_info (GimpLayerMode mode) +{ + g_return_val_if_fail (mode >= 0 && mode < G_N_ELEMENTS (layer_mode_infos), + &layer_mode_infos[0]); + + return &layer_mode_infos[mode]; +} + +gboolean +gimp_layer_mode_is_legacy (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_LEGACY) != 0; +} + +gboolean +gimp_layer_mode_is_blend_space_mutable (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE) == 0; +} + +gboolean +gimp_layer_mode_is_composite_space_mutable (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE) == 0; +} + +gboolean +gimp_layer_mode_is_composite_mode_mutable (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE) == 0; +} + +gboolean +gimp_layer_mode_is_subtractive (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_SUBTRACTIVE) != 0; +} + +gboolean +gimp_layer_mode_is_alpha_only (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_ALPHA_ONLY) != 0; +} + +gboolean +gimp_layer_mode_is_trivial (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return FALSE; + + return (info->flags & GIMP_LAYER_MODE_FLAG_TRIVIAL) != 0; +} + +GimpLayerColorSpace +gimp_layer_mode_get_blend_space (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return GIMP_LAYER_COLOR_SPACE_RGB_LINEAR; + + return info->blend_space; +} + +GimpLayerColorSpace +gimp_layer_mode_get_composite_space (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return GIMP_LAYER_COLOR_SPACE_RGB_LINEAR; + + return info->composite_space; +} + +GimpLayerCompositeMode +gimp_layer_mode_get_composite_mode (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return GIMP_LAYER_COMPOSITE_UNION; + + return info->composite_mode; +} + +GimpLayerCompositeMode +gimp_layer_mode_get_paint_composite_mode (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return GIMP_LAYER_COMPOSITE_UNION; + + return info->paint_composite_mode; +} + +const gchar * +gimp_layer_mode_get_operation (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return "gimp:layer-mode"; + + return info->op_name; +} + +GimpLayerModeFunc +gimp_layer_mode_get_function (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + static GimpLayerModeFunc funcs[G_N_ELEMENTS (layer_mode_infos)]; + + if (! info) + info = layer_mode_infos; + + mode = info - layer_mode_infos; + + if (! funcs[mode]) + { + GeglNode *node; + GeglOperation *operation; + + node = gegl_node_new_child (NULL, + "operation", info->op_name, + NULL); + + operation = gegl_node_get_gegl_operation (node); + + funcs[mode] = GIMP_OPERATION_LAYER_MODE_GET_CLASS (operation)->process; + + g_object_unref (node); + } + + return funcs[mode]; +} + +GimpLayerModeBlendFunc +gimp_layer_mode_get_blend_function (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return NULL; + + return info->blend_function; +} + +GimpLayerModeContext +gimp_layer_mode_get_context (GimpLayerMode mode) +{ + const GimpLayerModeInfo *info = gimp_layer_mode_info (mode); + + if (! info) + return 0; + + return info->context; +} + +GimpLayerMode * +gimp_layer_mode_get_context_array (GimpLayerMode mode, + GimpLayerModeContext context, + gint *n_modes) +{ + GimpLayerModeGroup group; + const GimpLayerMode *group_modes; + gint n_group_modes; + GimpLayerMode *array; + gint i; + + group = gimp_layer_mode_get_group (mode); + + group_modes = gimp_layer_mode_get_group_array (group, &n_group_modes); + + array = g_new0 (GimpLayerMode, n_group_modes); + *n_modes = 0; + + for (i = 0; i < n_group_modes; i++) + { + if (group_modes[i] != GIMP_LAYER_MODE_SEPARATOR && + (gimp_layer_mode_get_context (group_modes[i]) & context)) + { + array[*n_modes] = group_modes[i]; + (*n_modes)++; + } + } + + return array; +} + +static gboolean +is_mode_in_array (const GimpLayerMode *modes, + gint n_modes, + GimpLayerMode mode) +{ + gint i; + + for (i = 0; i < n_modes; i++) + { + if (modes[i] == mode) + return TRUE; + } + + return FALSE; +} + +GimpLayerModeGroup +gimp_layer_mode_get_group (GimpLayerMode mode) +{ + if (is_mode_in_array (layer_mode_group_default, + G_N_ELEMENTS (layer_mode_group_default), mode)) + { + return GIMP_LAYER_MODE_GROUP_DEFAULT; + } + else if (is_mode_in_array (layer_mode_group_legacy, + G_N_ELEMENTS (layer_mode_group_legacy), mode)) + { + return GIMP_LAYER_MODE_GROUP_LEGACY; + } + + return GIMP_LAYER_MODE_GROUP_DEFAULT; +} + +const GimpLayerMode * +gimp_layer_mode_get_group_array (GimpLayerModeGroup group, + gint *n_modes) +{ + g_return_val_if_fail (n_modes != NULL, NULL); + + switch (group) + { + case GIMP_LAYER_MODE_GROUP_DEFAULT: + *n_modes = G_N_ELEMENTS (layer_mode_group_default); + return layer_mode_group_default; + + case GIMP_LAYER_MODE_GROUP_LEGACY: + *n_modes = G_N_ELEMENTS (layer_mode_group_legacy); + return layer_mode_group_legacy; + + default: + g_return_val_if_reached (NULL); + } +} + +gboolean +gimp_layer_mode_get_for_group (GimpLayerMode old_mode, + GimpLayerModeGroup new_group, + GimpLayerMode *new_mode) +{ + gint i; + + g_return_val_if_fail (new_mode != NULL, FALSE); + + for (i = 0; i < G_N_ELEMENTS (layer_mode_groups); i++) + { + if (is_mode_in_array (layer_mode_groups[i], 2, old_mode)) + { + *new_mode = layer_mode_groups[i][new_group]; + + if (*new_mode != -1) + return TRUE; + + return FALSE; + } + } + + *new_mode = -1; + + return FALSE; +} + +const Babl * +gimp_layer_mode_get_format (GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + const Babl *preferred_format) +{ + GimpLayerCompositeRegion composite_region; + + /* for now, all modes perform i/o in the composite space. */ + (void) mode; + (void) blend_space; + + if (composite_space == GIMP_LAYER_COLOR_SPACE_AUTO) + composite_space = gimp_layer_mode_get_composite_space (mode); + + if (composite_mode == GIMP_LAYER_COMPOSITE_AUTO) + composite_mode = gimp_layer_mode_get_composite_mode (mode); + + composite_region = gimp_layer_mode_get_included_region (mode, composite_mode); + + if (gimp_layer_mode_is_alpha_only (mode)) + { + if (composite_region != GIMP_LAYER_COMPOSITE_REGION_UNION) + { + /* alpha-only layer modes don't combine colors in non-union composite + * modes, hence we can disregard the composite space. + */ + composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + } + } + else if (gimp_layer_mode_is_trivial (mode)) + { + if (! (composite_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION)) + { + /* trivial layer modes don't combine colors when only the source + * region is included, hence we can disregard the composite space. + */ + composite_space = GIMP_LAYER_COLOR_SPACE_AUTO; + } + } + + switch (composite_space) + { + case GIMP_LAYER_COLOR_SPACE_AUTO: + /* compositing is color-space agnostic. return a format that has a fast + * conversion path to/from the preferred format. + */ + if (! preferred_format || gimp_babl_format_get_linear (preferred_format)) + return babl_format ("RGBA float"); + else + return babl_format ("R'G'B'A float"); + + case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR: + return babl_format ("RGBA float"); + + case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL: + return babl_format ("R'G'B'A float"); + + case GIMP_LAYER_COLOR_SPACE_LAB: + return babl_format ("CIE Lab alpha float"); + } + + g_return_val_if_reached (babl_format ("RGBA float")); +} + +GimpLayerCompositeRegion +gimp_layer_mode_get_included_region (GimpLayerMode mode, + GimpLayerCompositeMode composite_mode) +{ + if (composite_mode == GIMP_LAYER_COMPOSITE_AUTO) + composite_mode = gimp_layer_mode_get_composite_mode (mode); + + switch (composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + return GIMP_LAYER_COMPOSITE_REGION_UNION; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + return GIMP_LAYER_COMPOSITE_REGION_DESTINATION; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + return GIMP_LAYER_COMPOSITE_REGION_SOURCE; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + return GIMP_LAYER_COMPOSITE_REGION_INTERSECTION; + + default: + g_return_val_if_reached (GIMP_LAYER_COMPOSITE_REGION_INTERSECTION); + } +} diff --git a/app/operations/layer-modes/gimp-layer-modes.h b/app/operations/layer-modes/gimp-layer-modes.h new file mode 100644 index 0000000..30c5e15 --- /dev/null +++ b/app/operations/layer-modes/gimp-layer-modes.h @@ -0,0 +1,73 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimp-layer-modes.h + * Copyright (C) 2017 Michael Natterer + * Øyvind Kolås + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_LAYER_MODES_H__ +#define __GIMP_LAYER_MODES_H__ + + +void gimp_layer_modes_init (void); + +gboolean gimp_layer_mode_is_legacy (GimpLayerMode mode); + +gboolean gimp_layer_mode_is_blend_space_mutable (GimpLayerMode mode); +gboolean gimp_layer_mode_is_composite_space_mutable (GimpLayerMode mode); +gboolean gimp_layer_mode_is_composite_mode_mutable (GimpLayerMode mode); + +gboolean gimp_layer_mode_is_subtractive (GimpLayerMode mode); +gboolean gimp_layer_mode_is_alpha_only (GimpLayerMode mode); +gboolean gimp_layer_mode_is_trivial (GimpLayerMode mode); + +GimpLayerColorSpace gimp_layer_mode_get_blend_space (GimpLayerMode mode); +GimpLayerColorSpace gimp_layer_mode_get_composite_space (GimpLayerMode mode); +GimpLayerCompositeMode gimp_layer_mode_get_composite_mode (GimpLayerMode mode); +GimpLayerCompositeMode gimp_layer_mode_get_paint_composite_mode (GimpLayerMode mode); + +const gchar * gimp_layer_mode_get_operation (GimpLayerMode mode); + +GimpLayerModeFunc gimp_layer_mode_get_function (GimpLayerMode mode); +GimpLayerModeBlendFunc gimp_layer_mode_get_blend_function (GimpLayerMode mode); + +GimpLayerModeContext gimp_layer_mode_get_context (GimpLayerMode mode); + +GimpLayerMode * gimp_layer_mode_get_context_array (GimpLayerMode mode, + GimpLayerModeContext context, + gint *n_modes); + +GimpLayerModeGroup gimp_layer_mode_get_group (GimpLayerMode mode); + +const GimpLayerMode * gimp_layer_mode_get_group_array (GimpLayerModeGroup group, + gint *n_modes); + +gboolean gimp_layer_mode_get_for_group (GimpLayerMode old_mode, + GimpLayerModeGroup new_group, + GimpLayerMode *new_mode); + +const Babl * gimp_layer_mode_get_format (GimpLayerMode mode, + GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, + const Babl *preferred_format); + +GimpLayerCompositeRegion gimp_layer_mode_get_included_region (GimpLayerMode mode, + GimpLayerCompositeMode composite_mode); + + +#endif /* __GIMP_LAYER_MODES_H__ */ diff --git a/app/operations/layer-modes/gimpoperationantierase.c b/app/operations/layer-modes/gimpoperationantierase.c new file mode 100644 index 0000000..3fb2947 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationantierase.c @@ -0,0 +1,188 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationantierase.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationantierase.h" + + + +static gboolean gimp_operation_anti_erase_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); +static GimpLayerCompositeRegion gimp_operation_anti_erase_get_affected_region (GimpOperationLayerMode *layer_mode); + + +G_DEFINE_TYPE (GimpOperationAntiErase, gimp_operation_anti_erase, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_anti_erase_class_init (GimpOperationAntiEraseClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:anti-erase", + "description", "GIMP anti erase mode operation", + NULL); + + layer_mode_class->process = gimp_operation_anti_erase_process; + layer_mode_class->get_affected_region = gimp_operation_anti_erase_get_affected_region; +} + +static void +gimp_operation_anti_erase_init (GimpOperationAntiErase *self) +{ +} + +static gboolean +gimp_operation_anti_erase_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat value = opacity; + gint b; + + if (has_mask) + value *= *mask; + + out[ALPHA] = in[ALPHA] + (1.0 - in[ALPHA]) * layer[ALPHA] * value; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gint b; + + out[ALPHA] = in[ALPHA]; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + in += 4; + out += 4; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat value = opacity; + gint b; + + if (has_mask) + value *= *mask; + + out[ALPHA] = layer[ALPHA] * value; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gfloat value = opacity; + gint b; + + if (has_mask) + value *= *mask; + + out[ALPHA] = in[ALPHA] * layer[ALPHA] * value; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + } + + return TRUE; +} + +static GimpLayerCompositeRegion +gimp_operation_anti_erase_get_affected_region (GimpOperationLayerMode *layer_mode) +{ + return GIMP_LAYER_COMPOSITE_REGION_SOURCE; +} diff --git a/app/operations/layer-modes/gimpoperationantierase.h b/app/operations/layer-modes/gimpoperationantierase.h new file mode 100644 index 0000000..0c5ddf8 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationantierase.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationantierase.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_ANTI_ERASE_H__ +#define __GIMP_OPERATION_ANTI_ERASE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_ANTI_ERASE (gimp_operation_anti_erase_get_type ()) +#define GIMP_OPERATION_ANTI_ERASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiErase)) +#define GIMP_OPERATION_ANTI_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiEraseClass)) +#define GIMP_IS_OPERATION_ANTI_ERASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE)) +#define GIMP_IS_OPERATION_ANTI_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ANTI_ERASE)) +#define GIMP_OPERATION_ANTI_ERASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiEraseClass)) + + +typedef struct _GimpOperationAntiErase GimpOperationAntiErase; +typedef struct _GimpOperationAntiEraseClass GimpOperationAntiEraseClass; + +struct _GimpOperationAntiErase +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationAntiEraseClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_anti_erase_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_ANTI_ERASE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationbehind.c b/app/operations/layer-modes/gimpoperationbehind.c new file mode 100644 index 0000000..1dc2630 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationbehind.c @@ -0,0 +1,236 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbehind.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationbehind.h" + + + +static gboolean gimp_operation_behind_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationBehind, gimp_operation_behind, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_behind_class_init (GimpOperationBehindClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:behind", + "description", "GIMP behind mode operation", + NULL); + + layer_mode_class->process = gimp_operation_behind_process; +} + +static void +gimp_operation_behind_init (GimpOperationBehind *self) +{ +} + +static gboolean +gimp_operation_behind_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat src1_alpha = in[ALPHA]; + gfloat src2_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + src2_alpha *= *mask; + + new_alpha = src2_alpha + (1.0 - src2_alpha) * src1_alpha; + + if (new_alpha) + { + gfloat ratio = in[ALPHA] / new_alpha; + gfloat compl_ratio = 1.0f - ratio; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] * ratio + layer[b] * compl_ratio; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gfloat src1_alpha = in[ALPHA]; + gfloat new_alpha; + gint b; + + new_alpha = src1_alpha; + + if (new_alpha) + { + for (b = RED; b < ALPHA; b++) + out[b] = in[b]; + } + else + { + for (b = RED; b < ALPHA; b++) + out[b] = layer[b]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat src1_alpha = in[ALPHA]; + gfloat src2_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + src2_alpha *= *mask; + + new_alpha = src2_alpha; + + if (new_alpha) + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b] + (in[b] - layer[b]) * src1_alpha; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gfloat src1_alpha = in[ALPHA]; + gfloat src2_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + src2_alpha *= *mask; + + new_alpha = src1_alpha * src2_alpha; + + if (new_alpha) + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationbehind.h b/app/operations/layer-modes/gimpoperationbehind.h new file mode 100644 index 0000000..46b9ac4 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationbehind.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationbehind.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_BEHIND_H__ +#define __GIMP_OPERATION_BEHIND_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_BEHIND (gimp_operation_behind_get_type ()) +#define GIMP_OPERATION_BEHIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehind)) +#define GIMP_OPERATION_BEHIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehindClass)) +#define GIMP_IS_OPERATION_BEHIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BEHIND)) +#define GIMP_IS_OPERATION_BEHIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BEHIND)) +#define GIMP_OPERATION_BEHIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehindClass)) + + +typedef struct _GimpOperationBehind GimpOperationBehind; +typedef struct _GimpOperationBehindClass GimpOperationBehindClass; + +struct _GimpOperationBehind +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationBehindClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_behind_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_BEHIND_H__ */ diff --git a/app/operations/layer-modes/gimpoperationdissolve.c b/app/operations/layer-modes/gimpoperationdissolve.c new file mode 100644 index 0000000..fb4b1d0 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationdissolve.c @@ -0,0 +1,175 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdissolve.c + * Copyright (C) 2012 Ville Sokk + * 2012 Øyvind Kolås + * 2003 Helvetix Victorinox + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationdissolve.h" + + +#define RANDOM_TABLE_SIZE 4096 + + +static gboolean gimp_operation_dissolve_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *result, + gint level); +static GimpLayerCompositeRegion gimp_operation_dissolve_get_affected_region (GimpOperationLayerMode *layer_mode); + + +G_DEFINE_TYPE (GimpOperationDissolve, gimp_operation_dissolve, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static gint32 random_table[RANDOM_TABLE_SIZE]; + + +static void +gimp_operation_dissolve_class_init (GimpOperationDissolveClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + GRand *gr; + gint i; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:dissolve", + "description", "GIMP dissolve mode operation", + "categories", "compositors", + NULL); + + layer_mode_class->process = gimp_operation_dissolve_process; + layer_mode_class->get_affected_region = gimp_operation_dissolve_get_affected_region; + + /* generate a table of random seeds */ + gr = g_rand_new_with_seed (314159265); + for (i = 0; i < RANDOM_TABLE_SIZE; i++) + random_table[i] = g_rand_int (gr); + + g_rand_free (gr); +} + +static void +gimp_operation_dissolve_init (GimpOperationDissolve *self) +{ +} + +static gboolean +gimp_operation_dissolve_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *result, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + gint x, y; + + for (y = result->y; y < result->y + result->height; y++) + { + GRand *gr; + + /* The offset can be negative. I could just abs() the result, but we + * probably prefer to use different indexes of the table when possible for + * nicer randomization, so let's cycle the modulo so that -1 is the last + * table index. + */ + gr = g_rand_new_with_seed (random_table[((y % RANDOM_TABLE_SIZE) + RANDOM_TABLE_SIZE) % RANDOM_TABLE_SIZE]); + + /* fast forward through the rows pseudo random sequence */ + for (x = 0; x < result->x; x++) + g_rand_int (gr); + + for (x = result->x; x < result->x + result->width; x++) + { + gfloat value = layer[ALPHA] * opacity * 255; + + if (has_mask) + value *= *mask; + + if (g_rand_int_range (gr, 0, 255) >= value) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + + if (layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_UNION || + layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP) + { + out[3] = in[3]; + } + else + { + out[3] = 0.0f; + } + } + else + { + out[0] = layer[0]; + out[1] = layer[1]; + out[2] = layer[2]; + + if (layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_UNION || + layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER) + { + out[3] = 1.0f; + } + else + { + out[3] = in[3]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + + g_rand_free (gr); + } + + return TRUE; +} + +static GimpLayerCompositeRegion +gimp_operation_dissolve_get_affected_region (GimpOperationLayerMode *layer_mode) +{ + return GIMP_LAYER_COMPOSITE_REGION_SOURCE; +} diff --git a/app/operations/layer-modes/gimpoperationdissolve.h b/app/operations/layer-modes/gimpoperationdissolve.h new file mode 100644 index 0000000..d448918 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationdissolve.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationdissolve.h + * Copyright (C) 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_DISSOLVE_H__ +#define __GIMP_OPERATION_DISSOLVE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_DISSOLVE (gimp_operation_dissolve_get_type ()) +#define GIMP_OPERATION_DISSOLVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolve)) +#define GIMP_OPERATION_DISSOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolveClass)) +#define GIMP_IS_OPERATION_DISSOLVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DISSOLVE)) +#define GIMP_IS_OPERATION_DISSOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DISSOLVE)) +#define GIMP_OPERATION_DISSOLVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolveClass)) + + +typedef struct _GimpOperationDissolve GimpOperationDissolve; +typedef struct _GimpOperationDissolveClass GimpOperationDissolveClass; + +struct _GimpOperationDissolveClass +{ + GimpOperationLayerModeClass parent_class; +}; + +struct _GimpOperationDissolve +{ + GimpOperationLayerMode parent_instance; +}; + + +GType gimp_operation_dissolve_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_DISSOLVE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationerase.c b/app/operations/layer-modes/gimpoperationerase.c new file mode 100644 index 0000000..aae6eac --- /dev/null +++ b/app/operations/layer-modes/gimpoperationerase.c @@ -0,0 +1,214 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationerase.c + * Copyright (C) 2008 Michael Natterer + * 2012 Ville Sokk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationerase.h" + + +static gboolean gimp_operation_erase_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationErase, gimp_operation_erase, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_erase_class_init (GimpOperationEraseClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:erase", + "description", "GIMP erase mode operation", + NULL); + + layer_mode_class->process = gimp_operation_erase_process; +} + +static void +gimp_operation_erase_init (GimpOperationErase *self) +{ +} + +static gboolean +gimp_operation_erase_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + while (samples--) + { + gfloat layer_alpha; + gfloat new_alpha; + gint b; + + layer_alpha = layer[ALPHA] * opacity; + + if (has_mask) + layer_alpha *= (*mask); + + new_alpha = in[ALPHA] + layer_alpha - 2.0f * in[ALPHA] * layer_alpha; + + if (new_alpha != 0.0f) + { + gfloat ratio; + + ratio = (1.0f - in[ALPHA]) * layer_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + out[b] = ratio * layer[b] + (1.0f - ratio) * in[b]; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask ++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat layer_alpha; + gfloat new_alpha; + gint b; + + layer_alpha = layer[ALPHA] * opacity; + + if (has_mask) + layer_alpha *= (*mask); + + new_alpha = (1.0f - layer_alpha) * in[ALPHA]; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask ++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat layer_alpha; + gfloat new_alpha; + const gfloat *src; + gint b; + + layer_alpha = layer[ALPHA] * opacity; + + if (has_mask) + layer_alpha *= (*mask); + + new_alpha = (1.0f - in[ALPHA]) * layer_alpha; + + src = layer; + + if (new_alpha == 0.0f) + src = in; + + for (b = RED; b < ALPHA; b++) + { + out[b] = src[b]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask ++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + out[ALPHA] = 0.0f; + + in += 4; + out += 4; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationerase.h b/app/operations/layer-modes/gimpoperationerase.h new file mode 100644 index 0000000..fec309b --- /dev/null +++ b/app/operations/layer-modes/gimpoperationerase.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationerase.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_ERASE_H__ +#define __GIMP_OPERATION_ERASE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_ERASE (gimp_operation_erase_get_type ()) +#define GIMP_OPERATION_ERASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ERASE, GimpOperationErase)) +#define GIMP_OPERATION_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ERASE, GimpOperationEraseClass)) +#define GIMP_IS_OPERATION_ERASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ERASE)) +#define GIMP_IS_OPERATION_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ERASE)) +#define GIMP_OPERATION_ERASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ERASE, GimpOperationEraseClass)) + + +typedef struct _GimpOperationErase GimpOperationErase; +typedef struct _GimpOperationEraseClass GimpOperationEraseClass; + +struct _GimpOperationErase +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationEraseClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_erase_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_ERASE_MODE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.c b/app/operations/layer-modes/gimpoperationlayermode-blend.c new file mode 100644 index 0000000..e107462 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode-blend.c @@ -0,0 +1,1215 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode-blend.c + * Copyright (C) 2017 Michael Natterer + * 2017 Øyvind Kolås + * 2017 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "../operations-types.h" + +#include "gimpoperationlayermode-blend.h" + + +#define EPSILON 1e-6f + +#define SAFE_DIV_MIN EPSILON +#define SAFE_DIV_MAX (1.0f / SAFE_DIV_MIN) + + +/* local function prototypes */ + +static inline gfloat safe_div (gfloat a, + gfloat b); + + +/* private functions */ + + +/* returns a / b, clamped to [-SAFE_DIV_MAX, SAFE_DIV_MAX]. + * if -SAFE_DIV_MIN <= a <= SAFE_DIV_MIN, returns 0. + */ +static inline gfloat +safe_div (gfloat a, + gfloat b) +{ + gfloat result = 0.0f; + + if (fabsf (a) > SAFE_DIV_MIN) + { + result = a / b; + result = CLAMP (result, -SAFE_DIV_MAX, SAFE_DIV_MAX); + } + + return result; +} + + +/* public functions */ + + +/* non-subtractive blending functions. these functions must set comp[ALPHA] + * to the same value as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are + * zero, the value of comp[RED..BLUE] is unconstrained (in particular, it may + * be NaN). + */ + + +void /* aka linear_dodge */ +gimp_operation_layer_mode_blend_addition (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] + layer[c]; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = 1.0f - safe_div (1.0f - in[c], layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = MIN (in[c], layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_difference (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = fabsf (in[c] - layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_divide (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = safe_div (in[c], layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_dodge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = safe_div (in[c], 1.0f - layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_exclusion (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = 0.5f - 2.0f * (in[c] - 0.5f) * (layer[c] - 0.5f); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_grain_extract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] - layer[c] + 0.5f; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_grain_merge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] + layer[c] - 0.5f; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hard_mix (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] + layer[c] < 1.0f ? 0.0f : 1.0f; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hardlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat val; + + if (layer[c] > 0.5f) + { + val = (1.0f - in[c]) * (1.0f - (layer[c] - 0.5f) * 2.0f); + val = MIN (1.0f - val, 1.0f); + } + else + { + val = in[c] * (layer[c] * 2.0f); + val = MIN (val, 1.0f); + } + + comp[c] = val; + } + } + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hsl_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat dest_min, dest_max, dest_l; + gfloat src_min, src_max, src_l; + + dest_min = MIN (in[0], in[1]); + dest_min = MIN (dest_min, in[2]); + dest_max = MAX (in[0], in[1]); + dest_max = MAX (dest_max, in[2]); + dest_l = (dest_min + dest_max) / 2.0f; + + src_min = MIN (layer[0], layer[1]); + src_min = MIN (src_min, layer[2]); + src_max = MAX (layer[0], layer[1]); + src_max = MAX (src_max, layer[2]); + src_l = (src_min + src_max) / 2.0f; + + if (fabs (src_l) > EPSILON && fabs (1.0 - src_l) > EPSILON) + { + gboolean dest_high; + gboolean src_high; + gfloat ratio; + gfloat offset; + gint c; + + dest_high = dest_l > 0.5f; + src_high = src_l > 0.5f; + + dest_l = MIN (dest_l, 1.0f - dest_l); + src_l = MIN (src_l, 1.0f - src_l); + + ratio = dest_l / src_l; + + offset = 0.0f; + if (dest_high) offset += 1.0f - 2.0f * dest_l; + if (src_high) offset += 2.0f * dest_l - ratio; + + for (c = 0; c < 3; c++) + comp[c] = layer[c] * ratio + offset; + } + else + { + comp[RED] = dest_l; + comp[GREEN] = dest_l; + comp[BLUE] = dest_l; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hsv_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat src_min, src_max, src_delta; + gfloat dest_min, dest_max, dest_delta, dest_s; + + src_min = MIN (layer[0], layer[1]); + src_min = MIN (src_min, layer[2]); + src_max = MAX (layer[0], layer[1]); + src_max = MAX (src_max, layer[2]); + src_delta = src_max - src_min; + + if (src_delta > EPSILON) + { + gfloat ratio; + gfloat offset; + gint c; + + dest_min = MIN (in[0], in[1]); + dest_min = MIN (dest_min, in[2]); + dest_max = MAX (in[0], in[1]); + dest_max = MAX (dest_max, in[2]); + dest_delta = dest_max - dest_min; + dest_s = dest_max ? dest_delta / dest_max : 0.0f; + + ratio = dest_s * dest_max / src_delta; + offset = dest_max - src_max * ratio; + + for (c = 0; c < 3; c++) + comp[c] = layer[c] * ratio + offset; + } + else + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c]; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hsv_saturation (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat src_min, src_max, src_delta, src_s; + gfloat dest_min, dest_max, dest_delta; + + dest_min = MIN (in[0], in[1]); + dest_min = MIN (dest_min, in[2]); + dest_max = MAX (in[0], in[1]); + dest_max = MAX (dest_max, in[2]); + dest_delta = dest_max - dest_min; + + if (dest_delta > EPSILON) + { + gfloat ratio; + gfloat offset; + gint c; + + src_min = MIN (layer[0], layer[1]); + src_min = MIN (src_min, layer[2]); + src_max = MAX (layer[0], layer[1]); + src_max = MAX (src_max, layer[2]); + src_delta = src_max - src_min; + src_s = src_max ? src_delta / src_max : 0.0f; + + ratio = src_s * dest_max / dest_delta; + offset = (1.0f - ratio) * dest_max; + + for (c = 0; c < 3; c++) + comp[c] = in[c] * ratio + offset; + } + else + { + comp[RED] = dest_max; + comp[GREEN] = dest_max; + comp[BLUE] = dest_max; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_hsv_value (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat dest_v; + gfloat src_v; + + dest_v = MAX (in[0], in[1]); + dest_v = MAX (dest_v, in[2]); + + src_v = MAX (layer[0], layer[1]); + src_v = MAX (src_v, layer[2]); + + if (fabs (dest_v) > EPSILON) + { + gfloat ratio = src_v / dest_v; + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] * ratio; + } + else + { + comp[RED] = src_v; + comp[GREEN] = src_v; + comp[BLUE] = src_v; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_lch_chroma (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat A1 = in[1]; + gfloat B1 = in[2]; + gfloat c1 = hypotf (A1, B1); + + if (c1 > EPSILON) + { + gfloat A2 = layer[1]; + gfloat B2 = layer[2]; + gfloat c2 = hypotf (A2, B2); + gfloat A = c2 * A1 / c1; + gfloat B = c2 * B1 / c1; + + comp[0] = in[0]; + comp[1] = A; + comp[2] = B; + } + else + { + comp[0] = in[0]; + comp[1] = in[1]; + comp[2] = in[2]; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_lch_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + comp[0] = in[0]; + comp[1] = layer[1]; + comp[2] = layer[2]; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_lch_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat A2 = layer[1]; + gfloat B2 = layer[2]; + gfloat c2 = hypotf (A2, B2); + + if (c2 > EPSILON) + { + gfloat A1 = in[1]; + gfloat B1 = in[2]; + gfloat c1 = hypotf (A1, B1); + gfloat A = c1 * A2 / c2; + gfloat B = c1 * B2 / c2; + + comp[0] = in[0]; + comp[1] = A; + comp[2] = B; + } + else + { + comp[0] = in[0]; + comp[1] = in[1]; + comp[2] = in[2]; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_lch_lightness (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + comp[0] = layer[0]; + comp[1] = in[1]; + comp[2] = in[2]; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = MAX (in[c], layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_linear_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] + layer[c] - 1.0f; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +/* added according to: + http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */ +void +gimp_operation_layer_mode_blend_linear_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat val; + + if (layer[c] <= 0.5f) + val = in[c] + 2.0f * layer[c] - 1.0f; + else + val = in[c] + 2.0f * (layer[c] - 0.5f); + + comp[c] = val; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_luma_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat dest_luminance; + gfloat src_luminance; + gint c; + + dest_luminance = GIMP_RGB_LUMINANCE (in[0], in[1], in[2]); + src_luminance = GIMP_RGB_LUMINANCE (layer[0], layer[1], layer[2]); + + if (dest_luminance <= src_luminance) + { + for (c = 0; c < 3; c++) + comp[c] = in[c]; + } + else + { + for (c = 0; c < 3; c++) + comp[c] = layer[c]; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_luma_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gfloat dest_luminance; + gfloat src_luminance; + gint c; + + dest_luminance = GIMP_RGB_LUMINANCE (in[0], in[1], in[2]); + src_luminance = GIMP_RGB_LUMINANCE (layer[0], layer[1], layer[2]); + + if (dest_luminance >= src_luminance) + { + for (c = 0; c < 3; c++) + comp[c] = in[c]; + } + else + { + for (c = 0; c < 3; c++) + comp[c] = layer[c]; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_luminance (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + static const Babl *fish; + gfloat *scratch; + gfloat *in_Y; + gfloat *layer_Y; + + if (! fish) + fish = babl_fish ("RGBA float", "Y float"); + + scratch = gegl_scratch_new (gfloat, 2 * samples); + + in_Y = scratch; + layer_Y = scratch + samples; + + babl_process (fish, in, in_Y, samples); + babl_process (fish, layer, layer_Y, samples); + + while (samples--) + { + if (layer[ALPHA] != 0.0f && in[ALPHA] != 0.0f) + { + gfloat ratio = safe_div (layer_Y[0], in_Y[0]); + gint c; + + for (c = 0; c < 3; c ++) + comp[c] = in[c] * ratio; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + in += 4; + layer += 4; + in_Y ++; + layer_Y ++; + } + + gegl_scratch_free (scratch); +} + +void +gimp_operation_layer_mode_blend_multiply (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] * layer[c]; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_overlay (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat val; + + if (in[c] < 0.5f) + val = 2.0f * in[c] * layer[c]; + else + val = 1.0f - 2.0f * (1.0f - layer[c]) * (1.0f - in[c]); + + comp[c] = val; + } + } + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +/* added according to: + http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */ +void +gimp_operation_layer_mode_blend_pin_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat val; + + if (layer[c] > 0.5f) + val = MAX(in[c], 2.0f * (layer[c] - 0.5f)); + else + val = MIN(in[c], 2.0f * layer[c]); + + comp[c] = val; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_screen (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = 1.0f - (1.0f - in[c]) * (1.0f - layer[c]); + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_softlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat multiply = in[c] * layer[c]; + gfloat screen = 1.0f - (1.0f - in[c]) * (1.0f - layer[c]); + gfloat val = (1.0f - in[c]) * multiply + in[c] * screen; + + comp[c] = val; + } + } + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +void +gimp_operation_layer_mode_blend_subtract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + comp[c] = in[c] - layer[c]; + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + +/* added according to: + http://www.simplefilter.de/en/basics/mixmods.html */ +void +gimp_operation_layer_mode_blend_vivid_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + gint c; + + for (c = 0; c < 3; c++) + { + gfloat val; + + if (layer[c] <= 0.5f) + { + val = 1.0f - safe_div (1.0f - in[c], 2.0f * layer[c]); + val = MAX (val, 0.0f); + } + else + { + val = safe_div (in[c], 2.0f * (1.0f - layer[c])); + val = MIN (val, 1.0f); + } + + comp[c] = val; + } + } + + comp[ALPHA] = layer[ALPHA]; + + comp += 4; + layer += 4; + in += 4; + } +} + + +/* subtractive blending functions. these functions must set comp[ALPHA] to + * the modified alpha of the overlapping content, as a fraction of the + * original overlapping content (i.e., an alpha of 1.0 specifies that no + * content is subtracted.) when in[ALPHA] or layer[ALPHA] are zero, the value + * of comp[RED..BLUE] is unconstrained (in particular, it may be NaN). + */ + + +void +gimp_operation_layer_mode_blend_color_erase (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) +{ + while (samples--) + { + if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f) + { + const gfloat *color = in; + const gfloat *bgcolor = layer; + gfloat alpha; + gint c; + + alpha = 0.0f; + + for (c = 0; c < 3; c++) + { + gfloat col = CLAMP (color[c], 0.0f, 1.0f); + gfloat bgcol = CLAMP (bgcolor[c], 0.0f, 1.0f); + + if (fabs (col - bgcol) > EPSILON) + { + gfloat a; + + if (col > bgcol) + a = (col - bgcol) / (1.0f - bgcol); + else + a = (bgcol - col) / bgcol; + + alpha = MAX (alpha, a); + } + } + + if (alpha > EPSILON) + { + gfloat alpha_inv = 1.0f / alpha; + + for (c = 0; c < 3; c++) + comp[c] = (color[c] - bgcolor[c]) * alpha_inv + bgcolor[c]; + } + else + { + comp[RED] = comp[GREEN] = comp[BLUE] = 0.0f; + } + + comp[ALPHA] = alpha; + } + else + comp[ALPHA] = 0.0f; + + comp += 4; + layer += 4; + in += 4; + } +} diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.h b/app/operations/layer-modes/gimpoperationlayermode-blend.h new file mode 100644 index 0000000..3a8f995 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode-blend.h @@ -0,0 +1,200 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode-blend.h + * Copyright (C) 2017 Michael Natterer + * 2017 Øyvind Kolås + * 2017 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; withcomp even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_LAYER_MODE_BLEND_H__ +#define __GIMP_OPERATION_LAYER_MODE_BLEND_H__ + + +/* nonsubtractive blend functions */ + +void gimp_operation_layer_mode_blend_addition (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_difference (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_divide (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_dodge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_exclusion (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_grain_extract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_grain_merge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hard_mix (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hardlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hsl_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hsv_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hsv_saturation (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_hsv_value (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_lch_chroma (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_lch_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_lch_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_lch_lightness (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_linear_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_linear_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_luma_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_luma_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_luminance (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_multiply (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_overlay (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_pin_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_screen (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_softlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_subtract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); +void gimp_operation_layer_mode_blend_vivid_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); + + +/* subtractive blend functions */ + +void gimp_operation_layer_mode_blend_color_erase (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples); + + +#endif /* __GIMP_OPERATION_LAYER_MODE_BLEND_H__ */ diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c b/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c new file mode 100644 index 0000000..2630751 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c @@ -0,0 +1,105 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode-composite-sse2.c + * Copyright (C) 2017 Michael Natterer + * 2017 Øyvind Kolås + * 2017 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "../operations-types.h" + +#include "gimpoperationlayermode-composite.h" + + +#if COMPILE_SSE2_INTRINISICS + +/* SSE2 */ +#include + + +/* non-subtractive compositing functions. these functions expect comp[ALPHA] + * to be the same as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are zero, + * the value of comp[RED..BLUE] is unconstrained (in particular, it may be + * NaN). + */ + + +void +gimp_operation_layer_mode_composite_clip_to_backdrop_sse2 (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + if ((((uintptr_t)in) | /* alignment check */ + ((uintptr_t)comp) | + ((uintptr_t)out) ) & 0x0F) + { + gimp_operation_layer_mode_composite_clip_to_backdrop (in, layer, comp, + mask, opacity, out, + samples); + } + else + { + const __v4sf *v_in = (const __v4sf*) in; + const __v4sf *v_comp = (const __v4sf*) comp; + __v4sf *v_out = (__v4sf*) out; + const __v4sf v_one = _mm_set1_ps (1.0f); + const __v4sf v_opacity = _mm_set1_ps (opacity); + + while (samples--) + { + __v4sf alpha, rgba_in, rgba_comp; + + rgba_in = *v_in ++; + rgba_comp = *v_comp++; + + alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_comp,_MM_SHUFFLE(3,3,3,3)) * v_opacity; + + if (mask) + { + alpha = alpha * _mm_set1_ps (*mask++); + } + + if (rgba_in[ALPHA] != 0.0f && _mm_ucomineq_ss (alpha, _mm_setzero_ps ())) + { + __v4sf out_pixel, out_pixel_rbaa, out_alpha; + + out_alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_in,_MM_SHUFFLE(3,3,3,3)); + out_pixel = rgba_comp * alpha + rgba_in * (v_one - alpha); + out_pixel_rbaa = _mm_shuffle_ps (out_pixel, out_alpha, _MM_SHUFFLE (3, 3, 2, 0)); + out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0)); + + *v_out++ = out_pixel; + } + else + { + *v_out ++ = rgba_in; + } + } + } +} + +#endif /* COMPILE_SSE2_INTRINISICS */ diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite.c b/app/operations/layer-modes/gimpoperationlayermode-composite.c new file mode 100644 index 0000000..4275818 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode-composite.c @@ -0,0 +1,434 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode-composite.c + * Copyright (C) 2017 Michael Natterer + * 2017 Øyvind Kolås + * 2017 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "../operations-types.h" + +#include "gimpoperationlayermode-composite.h" + + +/* non-subtractive compositing functions. these functions expect comp[ALPHA] + * to be the same as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are zero, + * the value of comp[RED..BLUE] is unconstrained (in particular, it may be + * NaN). + */ + + +void +gimp_operation_layer_mode_composite_union (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat new_alpha; + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + + if (mask) + layer_alpha *= *mask; + + new_alpha = layer_alpha + (1.0f - layer_alpha) * in_alpha; + + if (layer_alpha == 0.0f || new_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else if (in_alpha == 0.0f) + { + out[RED] = layer[RED]; + out[GREEN] = layer[GREEN]; + out[BLUE] = layer[BLUE]; + } + else + { + gfloat ratio = layer_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = ratio * (in_alpha * (comp[b] - layer[b]) + layer[b] - in[b]) + in[b]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_clip_to_backdrop (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat layer_alpha = comp[ALPHA] * opacity; + + if (mask) + layer_alpha *= *mask; + + if (in[ALPHA] == 0.0f || layer_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = comp[b] * layer_alpha + in[b] * (1.0f - layer_alpha); + } + + out[ALPHA] = in[ALPHA]; + + in += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_clip_to_layer (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat layer_alpha = layer[ALPHA] * opacity; + + if (mask) + layer_alpha *= *mask; + + if (layer_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else if (in[ALPHA] == 0.0f) + { + out[RED] = layer[RED]; + out[GREEN] = layer[GREEN]; + out[BLUE] = layer[BLUE]; + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = comp[b] * in[ALPHA] + layer[b] * (1.0f - in[ALPHA]); + } + + out[ALPHA] = layer_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_intersection (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat new_alpha = in[ALPHA] * comp[ALPHA] * opacity; + + if (mask) + new_alpha *= *mask; + + if (new_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else + { + out[RED] = comp[RED]; + out[GREEN] = comp[GREEN]; + out[BLUE] = comp[BLUE]; + } + + out[ALPHA] = new_alpha; + + in += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +/* subtractive compositing functions. these functions expect comp[ALPHA] to + * specify the modified alpha of the overlapping content, as a fraction of the + * original overlapping content (i.e., an alpha of 1.0 specifies that no + * content is subtracted.) when in[ALPHA] or layer[ALPHA] are zero, the value + * of comp[RED..BLUE] is unconstrained (in particular, it may be NaN). + */ + +void +gimp_operation_layer_mode_composite_union_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat comp_alpha = comp[ALPHA]; + gfloat new_alpha; + + if (mask) + layer_alpha *= *mask; + + new_alpha = in_alpha + layer_alpha - + (2.0f - comp_alpha) * in_alpha * layer_alpha; + + if (layer_alpha == 0.0f || new_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else if (in_alpha == 0.0f) + { + out[RED] = layer[RED]; + out[GREEN] = layer[GREEN]; + out[BLUE] = layer[BLUE]; + } + else + { + gfloat ratio = in_alpha / new_alpha; + gfloat layer_coeff = 1.0f / in_alpha - 1.0f; + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = ratio * (layer_alpha * (comp_alpha * comp[b] + layer_coeff * layer[b] - in[b]) + in[b]); + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_clip_to_backdrop_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat comp_alpha = comp[ALPHA]; + gfloat new_alpha; + + if (mask) + layer_alpha *= *mask; + + comp_alpha *= layer_alpha; + + new_alpha = 1.0f - layer_alpha + comp_alpha; + + if (in[ALPHA] == 0.0f || comp_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = comp[b] * ratio + in[b] * (1.0f - ratio); + } + + new_alpha *= in[ALPHA]; + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_clip_to_layer_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat comp_alpha = comp[ALPHA]; + gfloat new_alpha; + + if (mask) + layer_alpha *= *mask; + + comp_alpha *= in_alpha; + + new_alpha = 1.0f - in_alpha + comp_alpha; + + if (layer_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else if (in_alpha == 0.0f) + { + out[RED] = layer[RED]; + out[GREEN] = layer[GREEN]; + out[BLUE] = layer[BLUE]; + } + else + { + gfloat ratio = comp_alpha / new_alpha; + gint b; + + for (b = RED; b < ALPHA; b++) + out[b] = comp[b] * ratio + layer[b] * (1.0f - ratio); + } + + new_alpha *= layer_alpha; + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} + +void +gimp_operation_layer_mode_composite_intersection_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples) +{ + while (samples--) + { + gfloat new_alpha = in[ALPHA] * layer[ALPHA] * comp[ALPHA] * opacity; + + if (mask) + new_alpha *= *mask; + + if (new_alpha == 0.0f) + { + out[RED] = in[RED]; + out[GREEN] = in[GREEN]; + out[BLUE] = in[BLUE]; + } + else + { + out[RED] = comp[RED]; + out[GREEN] = comp[GREEN]; + out[BLUE] = comp[BLUE]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + comp += 4; + out += 4; + + if (mask) + mask++; + } +} diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite.h b/app/operations/layer-modes/gimpoperationlayermode-composite.h new file mode 100644 index 0000000..f9ec2a5 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode-composite.h @@ -0,0 +1,98 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode-composite.h + * Copyright (C) 2017 Michael Natterer + * 2017 Øyvind Kolås + * 2017 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 . + */ + +#ifndef __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__ +#define __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__ + + +void gimp_operation_layer_mode_composite_union (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_clip_to_backdrop (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_clip_to_layer (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_intersection (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); + +void gimp_operation_layer_mode_composite_union_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_clip_to_backdrop_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_clip_to_layer_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); +void gimp_operation_layer_mode_composite_intersection_sub (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); + +#if COMPILE_SSE2_INTRINISICS + +void gimp_operation_layer_mode_composite_clip_to_backdrop_sse2 (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + gfloat opacity, + gfloat *out, + gint samples); + +#endif /* COMPILE_SSE2_INTRINISICS */ + + +#endif /* __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationlayermode.c b/app/operations/layer-modes/gimpoperationlayermode.c new file mode 100644 index 0000000..db74ed5 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode.c @@ -0,0 +1,914 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode.c + * Copyright (C) 2008 Michael Natterer + * Copyright (C) 2008 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "../operations-types.h" + +#include "gimp-layer-modes.h" +#include "gimpoperationlayermode.h" +#include "gimpoperationlayermode-composite.h" + + +/* the maximum number of samples to process in one go. used to limit + * the size of the buffers we allocate on the stack. + */ +#define GIMP_COMPOSITE_BLEND_MAX_SAMPLES ((1 << 18) /* 256 KiB */ / \ + 16 /* bytes per pixel */ / \ + 2 /* max number of buffers */) + +/* number of consecutive unblended samples (whose source or destination alpha + * is zero) above which to split the blending process, in order to avoid + * performing too many unnecessary conversions. + */ +#define GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD 32 + + +enum +{ + PROP_0, + PROP_LAYER_MODE, + PROP_OPACITY, + PROP_BLEND_SPACE, + PROP_COMPOSITE_SPACE, + PROP_COMPOSITE_MODE +}; + + +typedef void (* CompositeFunc) (const gfloat *in, + const gfloat *layer, + const gfloat *comp, + const gfloat *mask, + float opacity, + gfloat *out, + gint samples); + + +static void gimp_operation_layer_mode_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_operation_layer_mode_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_operation_layer_mode_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_layer_mode_get_bounding_box (GeglOperation *operation); +static gboolean gimp_operation_layer_mode_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); + +static gboolean gimp_operation_layer_mode_process (GeglOperation *operation, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +static gboolean gimp_operation_layer_mode_real_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); +static gboolean gimp_operation_layer_mode_real_process (GeglOperation *operation, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +static gboolean process_last_node (GeglOperation *operation, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationLayerMode, gimp_operation_layer_mode, + GEGL_TYPE_OPERATION_POINT_COMPOSER3) + +#define parent_class gimp_operation_layer_mode_parent_class + + +static const Babl *gimp_layer_color_space_fish[3 /* from */][3 /* to */]; + +static CompositeFunc composite_union = gimp_operation_layer_mode_composite_union; +static CompositeFunc composite_clip_to_backdrop = gimp_operation_layer_mode_composite_clip_to_backdrop; +static CompositeFunc composite_clip_to_layer = gimp_operation_layer_mode_composite_clip_to_layer; +static CompositeFunc composite_intersection = gimp_operation_layer_mode_composite_intersection; + +static CompositeFunc composite_union_sub = gimp_operation_layer_mode_composite_union_sub; +static CompositeFunc composite_clip_to_backdrop_sub = gimp_operation_layer_mode_composite_clip_to_backdrop_sub; +static CompositeFunc composite_clip_to_layer_sub = gimp_operation_layer_mode_composite_clip_to_layer_sub; +static CompositeFunc composite_intersection_sub = gimp_operation_layer_mode_composite_intersection_sub; + + +static void +gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointComposer3Class *point_composer3_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:layer-mode", NULL); + + object_class->set_property = gimp_operation_layer_mode_set_property; + object_class->get_property = gimp_operation_layer_mode_get_property; + + operation_class->prepare = gimp_operation_layer_mode_prepare; + operation_class->get_bounding_box = gimp_operation_layer_mode_get_bounding_box; + operation_class->process = gimp_operation_layer_mode_parent_process; + + point_composer3_class->process = gimp_operation_layer_mode_process; + + klass->parent_process = gimp_operation_layer_mode_real_parent_process; + klass->process = gimp_operation_layer_mode_real_process; + klass->get_affected_region = NULL; + + g_object_class_install_property (object_class, PROP_LAYER_MODE, + g_param_spec_enum ("layer-mode", + NULL, NULL, + GIMP_TYPE_LAYER_MODE, + GIMP_LAYER_MODE_NORMAL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_OPACITY, + g_param_spec_double ("opacity", + NULL, NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_BLEND_SPACE, + g_param_spec_enum ("blend-space", + NULL, NULL, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + + g_object_class_install_property (object_class, PROP_COMPOSITE_SPACE, + g_param_spec_enum ("composite-space", + NULL, NULL, + GIMP_TYPE_LAYER_COLOR_SPACE, + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_COMPOSITE_MODE, + g_param_spec_enum ("composite-mode", + NULL, NULL, + GIMP_TYPE_LAYER_COMPOSITE_MODE, + GIMP_LAYER_COMPOSITE_UNION, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] = + babl_fish ("RGBA float", "R'G'B'A float"); + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] = + babl_fish ("RGBA float", "CIE Lab alpha float"); + + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] = + babl_fish ("R'G'B'A float", "RGBA float"); + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] = + babl_fish ("R'G'B'A float", "CIE Lab alpha float"); + + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] = + babl_fish ("CIE Lab alpha float", "RGBA float"); + gimp_layer_color_space_fish + /* from */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] + /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] = + babl_fish ("CIE Lab alpha float", "R'G'B'A float"); + +#if COMPILE_SSE2_INTRINISICS + if (gimp_cpu_accel_get_support () & GIMP_CPU_ACCEL_X86_SSE2) + composite_clip_to_backdrop = gimp_operation_layer_mode_composite_clip_to_backdrop_sse2; +#endif +} + +static void +gimp_operation_layer_mode_init (GimpOperationLayerMode *self) +{ +} + +static void +gimp_operation_layer_mode_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object); + + switch (property_id) + { + case PROP_LAYER_MODE: + self->layer_mode = g_value_get_enum (value); + break; + + case PROP_OPACITY: + self->prop_opacity = g_value_get_double (value); + break; + + case PROP_BLEND_SPACE: + self->blend_space = g_value_get_enum (value); + break; + + case PROP_COMPOSITE_SPACE: + self->composite_space = g_value_get_enum (value); + break; + + case PROP_COMPOSITE_MODE: + self->prop_composite_mode = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_layer_mode_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object); + + switch (property_id) + { + case PROP_LAYER_MODE: + g_value_set_enum (value, self->layer_mode); + break; + + case PROP_OPACITY: + g_value_set_double (value, self->prop_opacity); + break; + + case PROP_BLEND_SPACE: + g_value_set_enum (value, self->blend_space); + break; + + case PROP_COMPOSITE_SPACE: + g_value_set_enum (value, self->composite_space); + break; + + case PROP_COMPOSITE_MODE: + g_value_set_enum (value, self->prop_composite_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_layer_mode_prepare (GeglOperation *operation) +{ + GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (operation); + const GeglRectangle *input_extent; + const GeglRectangle *mask_extent; + const Babl *preferred_format; + const Babl *format; + + self->composite_mode = self->prop_composite_mode; + + if (self->composite_mode == GIMP_LAYER_COMPOSITE_AUTO) + { + self->composite_mode = + gimp_layer_mode_get_composite_mode (self->layer_mode); + + g_warn_if_fail (self->composite_mode != GIMP_LAYER_COMPOSITE_AUTO); + } + + self->function = gimp_layer_mode_get_function (self->layer_mode); + self->blend_function = gimp_layer_mode_get_blend_function (self->layer_mode); + + input_extent = gegl_operation_source_get_bounding_box (operation, "input"); + mask_extent = gegl_operation_source_get_bounding_box (operation, "aux2"); + + /* if the input pad has data, work as usual. */ + if (input_extent && ! gegl_rectangle_is_empty (input_extent)) + { + self->is_last_node = FALSE; + + preferred_format = gegl_operation_get_source_format (operation, "input"); + } + /* otherwise, we're the last node (corresponding to the bottom layer). + * in this case, we render the layer (as if) using UNION mode. + */ + else + { + self->is_last_node = TRUE; + + /* if the layer mode doesn't affect the source, use a shortcut + * function that only applies the opacity/mask to the layer. + */ + if (! (gimp_operation_layer_mode_get_affected_region (self) & + GIMP_LAYER_COMPOSITE_REGION_SOURCE)) + { + self->function = process_last_node; + } + /* otherwise, use the original process function, but force the + * composite mode to UNION. + */ + else + { + self->composite_mode = GIMP_LAYER_COMPOSITE_UNION; + } + + preferred_format = gegl_operation_get_source_format (operation, "aux"); + } + + self->has_mask = mask_extent && ! gegl_rectangle_is_empty (mask_extent); + + format = gimp_layer_mode_get_format (self->layer_mode, + self->blend_space, + self->composite_space, + self->composite_mode, + preferred_format); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "output", format); + gegl_operation_set_format (operation, "aux", format); + gegl_operation_set_format (operation, "aux2", babl_format ("Y float")); +} + +static GeglRectangle +gimp_operation_layer_mode_get_bounding_box (GeglOperation *op) +{ + GimpOperationLayerMode *self = (gpointer) op; + GeglRectangle *in_rect; + GeglRectangle *aux_rect; + GeglRectangle *aux2_rect; + GeglRectangle src_rect = {}; + GeglRectangle dst_rect = {}; + GeglRectangle result; + GimpLayerCompositeRegion included_region; + + in_rect = gegl_operation_source_get_bounding_box (op, "input"); + aux_rect = gegl_operation_source_get_bounding_box (op, "aux"); + aux2_rect = gegl_operation_source_get_bounding_box (op, "aux2"); + + if (in_rect) + dst_rect = *in_rect; + + if (aux_rect) + { + src_rect = *aux_rect; + + if (aux2_rect) + gegl_rectangle_intersect (&src_rect, &src_rect, aux2_rect); + } + + if (self->is_last_node) + { + included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE; + } + else + { + included_region = gimp_layer_mode_get_included_region (self->layer_mode, + self->composite_mode); + } + + if (self->prop_opacity == 0.0) + included_region &= ~GIMP_LAYER_COMPOSITE_REGION_SOURCE; + + gegl_rectangle_intersect (&result, &src_rect, &dst_rect); + + if (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE) + gegl_rectangle_bounding_box (&result, &result, &src_rect); + + if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION) + gegl_rectangle_bounding_box (&result, &result, &dst_rect); + + return result; +} + +static gboolean +gimp_operation_layer_mode_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation); + + point->opacity = point->prop_opacity; + + /* if we have a mask, but it's not included in the output, pretend the + * opacity is 0, so that we don't composite 'aux' over 'input' as if there + * was no mask. + */ + if (point->has_mask) + { + GObject *mask; + gboolean has_mask; + + /* get the raw value. this does not increase the reference count. */ + mask = gegl_operation_context_get_object (context, "aux2"); + + /* disregard 'mask' if it's not included in the roi. */ + has_mask = + mask && + gegl_rectangle_intersect (NULL, + gegl_buffer_get_extent (GEGL_BUFFER (mask)), + result); + + if (! has_mask) + point->opacity = 0.0; + } + + return GIMP_OPERATION_LAYER_MODE_GET_CLASS (point)->parent_process ( + operation, context, output_prop, result, level); +} + +static gboolean +gimp_operation_layer_mode_process (GeglOperation *operation, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level) +{ + return ((GimpOperationLayerMode *) operation)->function ( + operation, in, layer, mask, out, samples, roi, level); +} + +static gboolean +gimp_operation_layer_mode_real_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation); + GObject *input; + GObject *aux; + gboolean has_input; + gboolean has_aux; + GimpLayerCompositeRegion included_region; + + /* get the raw values. this does not increase the reference count. */ + input = gegl_operation_context_get_object (context, "input"); + aux = gegl_operation_context_get_object (context, "aux"); + + /* disregard 'input' if it's not included in the roi. */ + has_input = + input && + gegl_rectangle_intersect (NULL, + gegl_buffer_get_extent (GEGL_BUFFER (input)), + result); + + /* disregard 'aux' if it's not included in the roi, or if it's fully + * transparent. + */ + has_aux = + aux && + point->opacity != 0.0 && + gegl_rectangle_intersect (NULL, + gegl_buffer_get_extent (GEGL_BUFFER (aux)), + result); + + if (point->is_last_node) + { + included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE; + } + else + { + included_region = gimp_layer_mode_get_included_region (point->layer_mode, + point->composite_mode); + } + + /* if there's no 'input' ... */ + if (! has_input) + { + /* ... and there's 'aux', and the composite mode includes it (or we're + * the last node) ... + */ + if (has_aux && (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE)) + { + GimpLayerCompositeRegion affected_region; + + affected_region = + gimp_operation_layer_mode_get_affected_region (point); + + /* ... and the op doesn't otherwise affect 'aux', or changes its + * alpha ... + */ + if (! (affected_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE) && + point->opacity == 1.0 && + ! gegl_operation_context_get_object (context, "aux2")) + { + /* pass 'aux' directly as output; */ + gegl_operation_context_set_object (context, "output", aux); + return TRUE; + } + + /* otherwise, if the op affects 'aux', or changes its alpha, process + * it even though there's no 'input'; + */ + } + /* otherwise, there's no 'aux', or the composite mode doesn't include it, + * and so ... + */ + else + { + /* ... the output is empty. */ + gegl_operation_context_set_object (context, "output", NULL); + return TRUE; + } + } + /* otherwise, if there's 'input' but no 'aux' ... */ + else if (! has_aux) + { + /* ... and the composite mode includes 'input' ... */ + if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION) + { + GimpLayerCompositeRegion affected_region; + + affected_region = + gimp_operation_layer_mode_get_affected_region (point); + + /* ... and the op doesn't otherwise affect 'input' ... */ + if (! (affected_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION)) + { + /* pass 'input' directly as output; */ + gegl_operation_context_set_object (context, "output", input); + return TRUE; + } + + /* otherwise, if the op affects 'input', process it even though + * there's no 'aux'; + */ + } + + /* otherwise, the output is fully transparent, but we process it anyway + * to maintain the 'input' color values. + */ + } + + /* FIXME: we don't actually handle the case where one of the inputs + * is NULL -- it'll just segfault. 'input' is not expected to be NULL, + * but 'aux' might be, currently. + */ + if (! input || ! aux) + { + GObject *empty = G_OBJECT (gegl_buffer_new (NULL, NULL)); + + if (! input) gegl_operation_context_set_object (context, "input", empty); + if (! aux) gegl_operation_context_set_object (context, "aux", empty); + + if (! input && ! aux) + gegl_object_set_has_forked (G_OBJECT (empty)); + + g_object_unref (empty); + } + + /* chain up, which will create the needed buffers for our actual + * process function + */ + return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, + output_prop, result, + level); +} + +static gboolean +gimp_operation_layer_mode_real_process (GeglOperation *operation, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) operation; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + GimpLayerColorSpace blend_space = layer_mode->blend_space; + GimpLayerColorSpace composite_space = layer_mode->composite_space; + GimpLayerCompositeMode composite_mode = layer_mode->composite_mode; + GimpLayerModeBlendFunc blend_function = layer_mode->blend_function; + gboolean composite_needs_in_color; + gfloat *blend_in; + gfloat *blend_layer; + gfloat *blend_out; + const Babl *composite_to_blend_fish = NULL; + const Babl *blend_to_composite_fish = NULL; + + /* make sure we don't process more than GIMP_COMPOSITE_BLEND_MAX_SAMPLES + * at a time, so that we don't overflow the stack if we allocate buffers + * on it. note that this has to be done with a nested function call, + * because alloca'd buffers remain for the duration of the stack frame. + */ + while (samples > GIMP_COMPOSITE_BLEND_MAX_SAMPLES) + { + gimp_operation_layer_mode_real_process (operation, + in, layer, mask, out, + GIMP_COMPOSITE_BLEND_MAX_SAMPLES, + roi, level); + + in += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES; + layer += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES; + if (mask) + mask += GIMP_COMPOSITE_BLEND_MAX_SAMPLES; + out += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES; + + samples -= GIMP_COMPOSITE_BLEND_MAX_SAMPLES; + } + + composite_needs_in_color = + composite_mode == GIMP_LAYER_COMPOSITE_UNION || + composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP; + + blend_in = in; + blend_layer = layer; + blend_out = out; + + if (blend_space != GIMP_LAYER_COLOR_SPACE_AUTO) + { + gimp_assert (composite_space >= 1 && composite_space < 4); + gimp_assert (blend_space >= 1 && blend_space < 4); + + composite_to_blend_fish = gimp_layer_color_space_fish [composite_space - 1] + [blend_space - 1]; + + blend_to_composite_fish = gimp_layer_color_space_fish [blend_space - 1] + [composite_space - 1]; + } + + /* if we need to convert the samples between the composite and blend + * spaces... + */ + if (composite_to_blend_fish) + { + gint i; + gint end; + + if (in != out || composite_needs_in_color) + { + /* don't convert input in-place if we're not doing in-place output, + * or if we're going to need the original input for compositing. + */ + blend_in = g_alloca (sizeof (gfloat) * 4 * samples); + } + blend_layer = g_alloca (sizeof (gfloat) * 4 * samples); + + if (in == out) /* in-place detected, avoid clobbering since we need to + read 'in' for the compositing stage */ + { + if (blend_layer != layer) + blend_out = blend_layer; + else + blend_out = g_alloca (sizeof (gfloat) * 4 * samples); + } + + /* samples whose the source or destination alpha is zero are not blended, + * and therefore do not need to be converted. while it's generally + * desirable to perform conversion and blending in bulk, when we have + * more than a certain number of consecutive unblended samples, the cost + * of converting them outweighs the cost of splitting the process around + * them to avoid the conversion. + */ + + i = ALPHA; + end = 4 * samples + ALPHA; + + while (TRUE) + { + gint first; + gint last; + gint count; + + /* skip any unblended samples. the color values of `blend_out` for + * these samples are unconstrained, in particular, they may be NaN, + * but the alpha values should generally be finite, and specifically + * 0 when the source alpha is 0. + */ + while (i < end && (in[i] == 0.0f || layer[i] == 0.0f)) + { + blend_out[i] = 0.0f; + i += 4; + } + + /* stop if there are no more samples */ + if (i == end) + break; + + /* otherwise, keep scanning the samples until we find + * GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD consecutive unblended + * samples. + */ + + first = i; + i += 4; + last = i; + + while (i < end && i - last < 4 * GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD) + { + gboolean blended; + + blended = (in[i] != 0.0f && layer[i] != 0.0f); + + i += 4; + if (blended) + last = i; + } + + /* convert and blend the samples in the range [first, last) */ + + count = (last - first) / 4; + first -= ALPHA; + + babl_process (composite_to_blend_fish, + in + first, blend_in + first, count); + babl_process (composite_to_blend_fish, + layer + first, blend_layer + first, count); + + blend_function (operation, blend_in + first, blend_layer + first, + blend_out + first, count); + + babl_process (blend_to_composite_fish, + blend_out + first, blend_out + first, count); + + /* make sure the alpha values of `blend_out` are valid for the + * trailing unblended samples. + */ + for (; last < i; last += 4) + blend_out[last] = 0.0f; + } + } + else + { + /* if both blending and compositing use the same color space, things are + * much simpler. + */ + + if (in == out) /* in-place detected, avoid clobbering since we need to + read 'in' for the compositing stage */ + { + blend_out = g_alloca (sizeof (gfloat) * 4 * samples); + } + + blend_function (operation, blend_in, blend_layer, blend_out, samples); + } + + if (! gimp_layer_mode_is_subtractive (layer_mode->layer_mode)) + { + switch (composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + composite_union (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + composite_clip_to_backdrop (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + composite_clip_to_layer (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + composite_intersection (in, layer, blend_out, mask, opacity, + out, samples); + break; + } + } + else + { + switch (composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + composite_union_sub (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + composite_clip_to_backdrop_sub (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + composite_clip_to_layer_sub (in, layer, blend_out, mask, opacity, + out, samples); + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + composite_intersection_sub (in, layer, blend_out, mask, opacity, + out, samples); + break; + } + } + + return TRUE; +} + +static gboolean +process_last_node (GeglOperation *operation, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = GIMP_OPERATION_LAYER_MODE (operation)->opacity; + + while (samples--) + { + memcpy (out, layer, 3 * sizeof (gfloat)); + + out[ALPHA] = layer[ALPHA] * opacity; + if (mask) + out[ALPHA] *= *mask++; + + layer += 4; + out += 4; + } + + return TRUE; +} + + +/* public functions */ + + +GimpLayerCompositeRegion +gimp_operation_layer_mode_get_affected_region (GimpOperationLayerMode *layer_mode) +{ + GimpOperationLayerModeClass *klass; + + g_return_val_if_fail (GIMP_IS_OPERATION_LAYER_MODE (layer_mode), + GIMP_LAYER_COMPOSITE_REGION_INTERSECTION); + + klass = GIMP_OPERATION_LAYER_MODE_GET_CLASS (layer_mode); + + if (klass->get_affected_region) + return klass->get_affected_region (layer_mode); + + return GIMP_LAYER_COMPOSITE_REGION_INTERSECTION; +} diff --git a/app/operations/layer-modes/gimpoperationlayermode.h b/app/operations/layer-modes/gimpoperationlayermode.h new file mode 100644 index 0000000..a2a5463 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationlayermode.h @@ -0,0 +1,89 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationlayermode.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_LAYER_MODE_H__ +#define __GIMP_OPERATION_LAYER_MODE_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_LAYER_MODE (gimp_operation_layer_mode_get_type ()) +#define GIMP_OPERATION_LAYER_MODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerMode)) +#define GIMP_OPERATION_LAYER_MODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerModeClass)) +#define GIMP_IS_OPERATION_LAYER_MODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LAYER_MODE)) +#define GIMP_IS_OPERATION_LAYER_MODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LAYER_MODE)) +#define GIMP_OPERATION_LAYER_MODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerModeClass)) + + +typedef struct _GimpOperationLayerModeClass GimpOperationLayerModeClass; + +struct _GimpOperationLayerMode +{ + GeglOperationPointComposer3 parent_instance; + + GimpLayerMode layer_mode; + gdouble opacity; + GimpLayerColorSpace blend_space; + GimpLayerColorSpace composite_space; + GimpLayerCompositeMode composite_mode; + + gdouble prop_opacity; + GimpLayerCompositeMode prop_composite_mode; + + GimpLayerModeFunc function; + GimpLayerModeBlendFunc blend_function; + gboolean is_last_node; + gboolean has_mask; +}; + +struct _GimpOperationLayerModeClass +{ + GeglOperationPointComposer3Class parent_class; + + /* virtual functions */ + gboolean (* parent_process) (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); + gboolean (* process) (GeglOperation *operation, + void *in, + void *aux, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + /* Returns the composite region (any combination of the layer and the + * backdrop) that the layer mode affects. Most modes only affect the + * overlapping region, and don't need to override this function. + */ + GimpLayerCompositeRegion (* get_affected_region) (GimpOperationLayerMode *layer_mode); +}; + + +GType gimp_operation_layer_mode_get_type (void) G_GNUC_CONST; + +GimpLayerCompositeRegion gimp_operation_layer_mode_get_affected_region (GimpOperationLayerMode *layer_mode); + + +#endif /* __GIMP_OPERATION_LAYER_MODE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationmerge.c b/app/operations/layer-modes/gimpoperationmerge.c new file mode 100644 index 0000000..e1b25ca --- /dev/null +++ b/app/operations/layer-modes/gimpoperationmerge.c @@ -0,0 +1,243 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmerge.c + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationmerge.h" + + +static gboolean gimp_operation_merge_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationMerge, gimp_operation_merge, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_merge_class_init (GimpOperationMergeClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:merge", + "description", "GIMP merge mode operation", + NULL); + + layer_mode_class->process = gimp_operation_merge_process; +} + +static void +gimp_operation_merge_init (GimpOperationMerge *self) +{ +} + +static gboolean +gimp_operation_merge_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + in_alpha = MIN (in_alpha, 1.0f - layer_alpha); + new_alpha = in_alpha + layer_alpha; + + if (new_alpha) + { + gfloat ratio = layer_alpha / new_alpha; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] + (layer[b] - in[b]) * ratio; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + layer_alpha -= 1.0f - in_alpha; + + if (layer_alpha > 0.0f) + { + gfloat ratio = layer_alpha / in_alpha; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] + (layer[b] - in[b]) * ratio; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = in_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat layer_alpha = layer[ALPHA] * opacity; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + if (layer_alpha != 0.0f) + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = layer_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + layer_alpha -= 1.0f - in_alpha; + layer_alpha = MAX (layer_alpha, 0.0f); + + if (layer_alpha != 0.0f) + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = layer_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationmerge.h b/app/operations/layer-modes/gimpoperationmerge.h new file mode 100644 index 0000000..01afde1 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationmerge.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmerge.h + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#ifndef __GIMP_OPERATION_MERGE_H__ +#define __GIMP_OPERATION_MERGE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_MERGE (gimp_operation_merge_get_type ()) +#define GIMP_OPERATION_MERGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MERGE, GimpOperationMerge)) +#define GIMP_OPERATION_MERGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MERGE, GimpOperationMergeClass)) +#define GIMP_IS_OPERATION_MERGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MERGE)) +#define GIMP_IS_OPERATION_MERGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MERGE)) +#define GIMP_OPERATION_MERGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MERGE, GimpOperationMergeClass)) + + +typedef struct _GimpOperationMerge GimpOperationMerge; +typedef struct _GimpOperationMergeClass GimpOperationMergeClass; + +struct _GimpOperationMerge +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationMergeClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_merge_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_MERGE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationnormal-sse2.c b/app/operations/layer-modes/gimpoperationnormal-sse2.c new file mode 100644 index 0000000..73e42ed --- /dev/null +++ b/app/operations/layer-modes/gimpoperationnormal-sse2.c @@ -0,0 +1,264 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationnormal-sse2.c + * Copyright (C) 2013 Daniel Sabo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations/operations-types.h" + +#include "gimpoperationnormal.h" + + +#if COMPILE_SSE2_INTRINISICS + +/* SSE2 */ +#include + + +gboolean +gimp_operation_normal_process_sse2 (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + /* check alignment */ + if ((((uintptr_t)in_p) | ((uintptr_t)layer_p) | ((uintptr_t)out_p)) & 0x0F) + { + return gimp_operation_normal_process (op, + in_p, layer_p, mask_p, out_p, + samples, roi, level); + } + else + { + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat opacity = layer_mode->opacity; + gfloat *mask = mask_p; + const __v4sf *v_in = (const __v4sf*) in_p; + const __v4sf *v_layer = (const __v4sf*) layer_p; + __v4sf *v_out = ( __v4sf*) out_p; + + const __v4sf one = _mm_set1_ps (1.0f); + const __v4sf v_opacity = _mm_set1_ps (opacity); + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + __v4sf dst_alpha, a_term, out_pixel, out_alpha, out_pixel_rbaa; + + /* expand alpha */ + dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + /* a_term = dst_a * (1.0 - src_a) */ + a_term = dst_alpha * (one - alpha); + + /* out(color) = src * src_a + dst * a_term */ + out_pixel = rgba_layer * alpha + rgba_in * a_term; + + /* out(alpha) = 1.0 * src_a + 1.0 * a_term */ + out_alpha = alpha + a_term; + + /* un-premultiply */ + out_pixel = out_pixel / out_alpha; + + /* swap in the real alpha */ + out_pixel_rbaa = _mm_shuffle_ps (out_pixel, out_alpha, _MM_SHUFFLE (3, 3, 2, 0)); + out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0)); + + *v_out++ = out_pixel; + } + else + { + *v_out++ = rgba_in; + } + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + __v4sf dst_alpha, out_pixel, out_pixel_rbaa; + + /* expand alpha */ + dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + /* out(color) = dst * (1 - src_a) + src * src_a */ + out_pixel = rgba_in + (rgba_layer - rgba_in) * alpha; + + /* swap in the real alpha */ + out_pixel_rbaa = _mm_shuffle_ps (out_pixel, dst_alpha, _MM_SHUFFLE (3, 3, 2, 0)); + out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0)); + + *v_out++ = out_pixel; + } + else + { + *v_out++ = rgba_in; + } + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + __v4sf out_pixel, out_pixel_rbaa; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + /* out(color) = src */ + out_pixel = rgba_layer; + } + else + { + out_pixel = rgba_in; + } + + /* swap in the real alpha */ + out_pixel_rbaa = _mm_shuffle_ps (out_pixel, alpha, _MM_SHUFFLE (3, 3, 2, 0)); + out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0)); + + *v_out++ = out_pixel; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + __v4sf out_pixel, out_pixel_rbaa; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + /* multiply the alpha by in's alpha */ + alpha *= (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + /* out(color) = src */ + out_pixel = rgba_layer; + } + else + { + out_pixel = rgba_in; + } + + /* swap in the real alpha */ + out_pixel_rbaa = _mm_shuffle_ps (out_pixel, alpha, _MM_SHUFFLE (3, 3, 2, 0)); + out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0)); + + *v_out++ = out_pixel; + } + break; + } + } + + return TRUE; +} + +#endif /* COMPILE_SSE2_INTRINISICS */ diff --git a/app/operations/layer-modes/gimpoperationnormal-sse4.c b/app/operations/layer-modes/gimpoperationnormal-sse4.c new file mode 100644 index 0000000..ba621a2 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationnormal-sse4.c @@ -0,0 +1,260 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationnormalmode-sse2.c + * Copyright (C) 2013 Daniel Sabo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "operations/operations-types.h" + +#include "gimpoperationnormal.h" + + +#if COMPILE_SSE4_1_INTRINISICS + +/* SSE4 */ +#include + + +gboolean +gimp_operation_normal_process_sse4 (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + /* check alignment */ + if ((((uintptr_t)in_p) | ((uintptr_t)layer_p) | ((uintptr_t)out_p)) & 0x0F) + { + return gimp_operation_normal_process (op, + in_p, layer_p, mask_p, out_p, + samples, roi, level); + } + else + { + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat opacity = layer_mode->opacity; + gfloat *mask = mask_p; + const __v4sf *v_in = (const __v4sf*) in_p; + const __v4sf *v_layer = (const __v4sf*) layer_p; + __v4sf *v_out = ( __v4sf*) out_p; + + const __v4sf one = _mm_set1_ps (1.0f); + const __v4sf v_opacity = _mm_set1_ps (opacity); + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + __v4sf dst_alpha, a_term, out_pixel, out_alpha; + + /* expand alpha */ + dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + /* a_term = dst_a * (1.0 - src_a) */ + a_term = dst_alpha * (one - alpha); + + /* out(color) = src * src_a + dst * a_term */ + out_pixel = rgba_layer * alpha + rgba_in * a_term; + + /* out(alpha) = 1.0 * src_a + 1.0 * a_term */ + out_alpha = alpha + a_term; + + /* un-premultiply */ + out_pixel = out_pixel / out_alpha; + + /* swap in the real alpha */ + out_pixel = _mm_blend_ps (out_pixel, out_alpha, 0x08); + + *v_out++ = out_pixel; + } + else + { + *v_out++ = rgba_in; + } + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + __v4sf dst_alpha, out_pixel; + + /* expand alpha */ + dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + /* out(color) = dst * (1 - src_a) + src * src_a */ + out_pixel = rgba_in + (rgba_layer - rgba_in) * alpha; + + /* swap in the real alpha */ + out_pixel = _mm_blend_ps (out_pixel, dst_alpha, 0x08); + + *v_out++ = out_pixel; + } + else + { + *v_out++ = rgba_in; + } + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + __v4sf out_pixel; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + /* out(color) = src */ + out_pixel = rgba_layer; + } + else + { + out_pixel = rgba_in; + } + + /* swap in the real alpha */ + out_pixel = _mm_blend_ps (out_pixel, alpha, 0x08); + + *v_out++ = out_pixel; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + __v4sf rgba_in, rgba_layer, alpha; + __v4sf out_pixel; + + rgba_in = *v_in++; + rgba_layer = *v_layer++; + + /* expand alpha */ + alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (mask) + { + __v4sf mask_alpha; + + /* multiply layer's alpha by the mask */ + mask_alpha = _mm_set1_ps (*mask++); + alpha = alpha * mask_alpha; + } + + alpha = alpha * v_opacity; + + /* multiply the alpha by in's alpha */ + alpha *= (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in, + _MM_SHUFFLE (3, 3, 3, 3)); + + if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ())) + { + /* out(color) = src */ + out_pixel = rgba_layer; + } + else + { + out_pixel = rgba_in; + } + + /* swap in the real alpha */ + out_pixel = _mm_blend_ps (out_pixel, alpha, 0x08); + + *v_out++ = out_pixel; + } + break; + } + } + + return TRUE; +} + +#endif /* COMPILE_SSE4_1_INTRINISICS */ diff --git a/app/operations/layer-modes/gimpoperationnormal.c b/app/operations/layer-modes/gimpoperationnormal.c new file mode 100644 index 0000000..9bd8705 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationnormal.c @@ -0,0 +1,266 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationnormalmode.c + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "../operations-types.h" + +#include "gimpoperationnormal.h" + + +G_DEFINE_TYPE (GimpOperationNormal, gimp_operation_normal, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static const gchar* reference_xml = "" +"" +"" +" " +" " +" blending-test-B.png" +" " +" " +"" +"" +" " +" blending-test-A.png" +" " +"" +""; + + +static void +gimp_operation_normal_class_init (GimpOperationNormalClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:normal", + "description", "GIMP normal mode operation", + "reference-image", "normal-mode.png", + "reference-composition", reference_xml, + NULL); + + layer_mode_class->process = gimp_operation_normal_process; + +#if COMPILE_SSE2_INTRINISICS + if (gimp_cpu_accel_get_support() & GIMP_CPU_ACCEL_X86_SSE2) + layer_mode_class->process = gimp_operation_normal_process_sse2; +#endif /* COMPILE_SSE2_INTRINISICS */ + +#if COMPILE_SSE4_1_INTRINISICS + if (gimp_cpu_accel_get_support() & GIMP_CPU_ACCEL_X86_SSE4_1) + layer_mode_class->process = gimp_operation_normal_process_sse4; +#endif /* COMPILE_SSE4_1_INTRINISICS */ +} + +static void +gimp_operation_normal_init (GimpOperationNormal *self) +{ +} + +gboolean +gimp_operation_normal_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = layer_alpha + in[ALPHA] - layer_alpha * in[ALPHA]; + + if (out[ALPHA]) + { + gfloat layer_weight = layer_alpha / out[ALPHA]; + gfloat in_weight = 1.0f - layer_weight; + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b] * layer_weight + in[b] * in_weight; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = in[ALPHA]; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] + (layer[b] - in[b]) * layer_alpha; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = layer_alpha; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = in[ALPHA] * layer_alpha; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationnormal.h b/app/operations/layer-modes/gimpoperationnormal.h new file mode 100644 index 0000000..6fa1b36 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationnormal.h @@ -0,0 +1,91 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationnormal.h + * Copyright (C) 2012 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_NORMAL_H__ +#define __GIMP_OPERATION_NORMAL_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_NORMAL (gimp_operation_normal_get_type ()) +#define GIMP_OPERATION_NORMAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormal)) +#define GIMP_OPERATION_NORMAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormalClass)) +#define GIMP_IS_OPERATION_NORMAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_NORMAL)) +#define GIMP_IS_OPERATION_NORMAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_NORMAL)) +#define GIMP_OPERATION_NORMAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormalClass)) + + +typedef struct _GimpOperationNormal GimpOperationNormal; +typedef struct _GimpOperationNormalClass GimpOperationNormalClass; + +struct _GimpOperationNormal +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationNormalClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_normal_get_type (void) G_GNUC_CONST; + + +/* protected */ + +gboolean gimp_operation_normal_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +#if COMPILE_SSE2_INTRINISICS + +gboolean gimp_operation_normal_process_sse2 (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +#endif /* COMPILE_SSE2_INTRINISICS */ + +#if COMPILE_SSE4_1_INTRINISICS + +gboolean gimp_operation_normal_process_sse4 (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +#endif /* COMPILE_SSE4_1_INTRINISICS */ + + +#endif /* __GIMP_OPERATION_NORMAL_H__ */ diff --git a/app/operations/layer-modes/gimpoperationpassthrough.c b/app/operations/layer-modes/gimpoperationpassthrough.c new file mode 100644 index 0000000..bc7d328 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationpassthrough.c @@ -0,0 +1,55 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationpassthrough.c + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationpassthrough.h" + + +G_DEFINE_TYPE (GimpOperationPassThrough, gimp_operation_pass_through, + GIMP_TYPE_OPERATION_REPLACE) + + +static void +gimp_operation_pass_through_class_init (GimpOperationPassThroughClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:pass-through", + "description", "GIMP pass through mode operation", + NULL); + + /* don't use REPLACE mode's specialized get_affected_region(); PASS_THROUGH + * behaves like an ordinary layer mode here. + */ + layer_mode_class->get_affected_region = NULL; +} + +static void +gimp_operation_pass_through_init (GimpOperationPassThrough *self) +{ +} diff --git a/app/operations/layer-modes/gimpoperationpassthrough.h b/app/operations/layer-modes/gimpoperationpassthrough.h new file mode 100644 index 0000000..5a5b838 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationpassthrough.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationpassthrough.h + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#ifndef __GIMP_OPERATION_PASS_THROUGH_H__ +#define __GIMP_OPERATION_PASS_THROUGH_H__ + + +#include "gimpoperationreplace.h" + + +#define GIMP_TYPE_OPERATION_PASS_THROUGH (gimp_operation_pass_through_get_type ()) +#define GIMP_OPERATION_PASS_THROUGH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThrough)) +#define GIMP_OPERATION_PASS_THROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThroughClass)) +#define GIMP_IS_OPERATION_PASS_THROUGH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH)) +#define GIMP_IS_OPERATION_PASS_THROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_PASS_THROUGH)) +#define GIMP_OPERATION_PASS_THROUGH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThroughClass)) + + +typedef struct _GimpOperationPassThrough GimpOperationPassThrough; +typedef struct _GimpOperationPassThroughClass GimpOperationPassThroughClass; + +struct _GimpOperationPassThrough +{ + GimpOperationReplace parent_instance; +}; + +struct _GimpOperationPassThroughClass +{ + GimpOperationReplaceClass parent_class; +}; + + +GType gimp_operation_pass_through_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_PASS_THROUGH_H__ */ diff --git a/app/operations/layer-modes/gimpoperationreplace.c b/app/operations/layer-modes/gimpoperationreplace.c new file mode 100644 index 0000000..ed1ac19 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationreplace.c @@ -0,0 +1,347 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationreplace.c + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include + +#include "../operations-types.h" + +#include "gimp-layer-modes.h" +#include "gimpoperationreplace.h" + + +static GeglRectangle gimp_operation_replace_get_bounding_box (GeglOperation *op); + +static gboolean gimp_operation_replace_parent_process (GeglOperation *op, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); +static gboolean gimp_operation_replace_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); +static GimpLayerCompositeRegion gimp_operation_replace_get_affected_region (GimpOperationLayerMode *layer_mode); + + +G_DEFINE_TYPE (GimpOperationReplace, gimp_operation_replace, + GIMP_TYPE_OPERATION_LAYER_MODE) + +#define parent_class gimp_operation_replace_parent_class + + +static void +gimp_operation_replace_class_init (GimpOperationReplaceClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:replace", + "description", "GIMP replace mode operation", + NULL); + + operation_class->get_bounding_box = gimp_operation_replace_get_bounding_box; + + layer_mode_class->parent_process = gimp_operation_replace_parent_process; + layer_mode_class->process = gimp_operation_replace_process; + layer_mode_class->get_affected_region = gimp_operation_replace_get_affected_region; +} + +static void +gimp_operation_replace_init (GimpOperationReplace *self) +{ +} + +static GeglRectangle +gimp_operation_replace_get_bounding_box (GeglOperation *op) +{ + GimpOperationLayerMode *self = (gpointer) op; + GeglRectangle *in_rect; + GeglRectangle *aux_rect; + GeglRectangle *aux2_rect; + GeglRectangle src_rect = {}; + GeglRectangle dst_rect = {}; + GeglRectangle result; + GimpLayerCompositeRegion included_region; + + in_rect = gegl_operation_source_get_bounding_box (op, "input"); + aux_rect = gegl_operation_source_get_bounding_box (op, "aux"); + aux2_rect = gegl_operation_source_get_bounding_box (op, "aux2"); + + if (in_rect) + dst_rect = *in_rect; + + if (aux_rect) + { + src_rect = *aux_rect; + + if (aux2_rect) + gegl_rectangle_intersect (&src_rect, &src_rect, aux2_rect); + } + + if (self->is_last_node) + { + included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE; + } + else + { + included_region = gimp_layer_mode_get_included_region (self->layer_mode, + self->composite_mode); + } + + if (self->prop_opacity == 0.0) + included_region &= ~GIMP_LAYER_COMPOSITE_REGION_SOURCE; + else if (self->prop_opacity == 1.0 && ! aux2_rect) + included_region &= ~GIMP_LAYER_COMPOSITE_REGION_DESTINATION; + + gegl_rectangle_intersect (&result, &src_rect, &dst_rect); + + if (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE) + gegl_rectangle_bounding_box (&result, &result, &src_rect); + + if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION) + gegl_rectangle_bounding_box (&result, &result, &dst_rect); + + return result; +} + +static gboolean +gimp_operation_replace_parent_process (GeglOperation *op, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + GimpLayerCompositeRegion included_region; + + included_region = gimp_layer_mode_get_included_region + (layer_mode->layer_mode, layer_mode->composite_mode); + + /* if the layer's opacity is 100%, it has no mask, and its composite mode + * contains "aux" (the latter should always be the case in practice, + * currently,) we can just pass "aux" directly as output. + */ + if (layer_mode->opacity == 1.0 && + ! gegl_operation_context_get_object (context, "aux2") && + (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE)) + { + GObject *aux; + + aux = gegl_operation_context_get_object (context, "aux"); + + gegl_operation_context_set_object (context, "output", aux); + + return TRUE; + } + /* the opposite case, where the opacity is 0%, is handled by + * GimpOperationLayerMode. + */ + else if (layer_mode->opacity == 0.0) + { + } + /* if both buffers are included in the result, and if both of them have the + * same content -- i.e., if they share the same storage, same alignment, and + * same abyss (or if the abyss is irrelevant) -- we can just pass either of + * them directly as output. + */ + else if (included_region == GIMP_LAYER_COMPOSITE_REGION_UNION) + { + GObject *input; + GObject *aux; + + input = gegl_operation_context_get_object (context, "input"); + aux = gegl_operation_context_get_object (context, "aux"); + + if (input && aux && + gegl_buffer_share_storage (GEGL_BUFFER (input), GEGL_BUFFER (aux))) + { + gint input_shift_x; + gint input_shift_y; + gint aux_shift_x; + gint aux_shift_y; + + g_object_get (input, + "shift-x", &input_shift_x, + "shift-y", &input_shift_y, + NULL); + g_object_get (aux, + "shift-x", &aux_shift_x, + "shift-y", &aux_shift_y, + NULL); + + if (input_shift_x == aux_shift_x && input_shift_y == aux_shift_y) + { + const GeglRectangle *input_abyss; + const GeglRectangle *aux_abyss; + + input_abyss = gegl_buffer_get_abyss (GEGL_BUFFER (input)); + aux_abyss = gegl_buffer_get_abyss (GEGL_BUFFER (aux)); + + if (gegl_rectangle_equal (input_abyss, aux_abyss) || + (gegl_rectangle_contains (input_abyss, result) && + gegl_rectangle_contains (aux_abyss, result))) + { + gegl_operation_context_set_object (context, "output", input); + + return TRUE; + } + } + } + } + + return GIMP_OPERATION_LAYER_MODE_CLASS (parent_class)->parent_process ( + op, context, output_prop, result, level); +} + +static gboolean +gimp_operation_replace_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat opacity_value = opacity; + gfloat new_alpha; + gfloat ratio; + gint b; + + if (has_mask) + opacity_value *= *mask; + + new_alpha = (layer[ALPHA] - in[ALPHA]) * opacity_value + in[ALPHA]; + + ratio = opacity_value; + + if (new_alpha) + ratio *= layer[ALPHA] / new_alpha; + + for (b = RED; b < ALPHA; b++) + out[b] = (layer[b] - in[b]) * ratio + in[b]; + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gfloat opacity_value = opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + opacity_value *= *mask; + + new_alpha = in[ALPHA] * (1.0f - opacity_value); + + for (b = RED; b < ALPHA; b++) + out[b] = in[b]; + + out[ALPHA] = new_alpha; + + in += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat opacity_value = opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + opacity_value *= *mask; + + new_alpha = layer[ALPHA] * opacity_value; + + for (b = RED; b < ALPHA; b++) + out[b] = layer[b]; + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + memset (out, 0, 4 * samples * sizeof (gfloat)); + break; + } + + return TRUE; +} + +static GimpLayerCompositeRegion +gimp_operation_replace_get_affected_region (GimpOperationLayerMode *layer_mode) +{ + GimpLayerCompositeRegion affected_region = GIMP_LAYER_COMPOSITE_REGION_INTERSECTION; + + if (layer_mode->prop_opacity != 0.0) + affected_region |= GIMP_LAYER_COMPOSITE_REGION_DESTINATION; + + /* if opacity != 1.0, or we have a mask, then we also affect SOURCE, but this + * is considered the case anyway, so no need for special handling. + */ + + return affected_region; +} diff --git a/app/operations/layer-modes/gimpoperationreplace.h b/app/operations/layer-modes/gimpoperationreplace.h new file mode 100644 index 0000000..f8d7b0c --- /dev/null +++ b/app/operations/layer-modes/gimpoperationreplace.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationreplace.h + * Copyright (C) 2008 Michael Natterer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_OPERATION_REPLACE_H__ +#define __GIMP_OPERATION_REPLACE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_REPLACE (gimp_operation_replace_get_type ()) +#define GIMP_OPERATION_REPLACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplace)) +#define GIMP_OPERATION_REPLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplaceClass)) +#define GIMP_IS_OPERATION_REPLACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_REPLACE)) +#define GIMP_IS_OPERATION_REPLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_REPLACE)) +#define GIMP_OPERATION_REPLACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplaceClass)) + + +typedef struct _GimpOperationReplace GimpOperationReplace; +typedef struct _GimpOperationReplaceClass GimpOperationReplaceClass; + +struct _GimpOperationReplace +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationReplaceClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_replace_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_REPLACE_H__ */ diff --git a/app/operations/layer-modes/gimpoperationsplit.c b/app/operations/layer-modes/gimpoperationsplit.c new file mode 100644 index 0000000..fe83d3f --- /dev/null +++ b/app/operations/layer-modes/gimpoperationsplit.c @@ -0,0 +1,213 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsplit.c + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationsplit.h" + + +static gboolean gimp_operation_split_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationSplit, gimp_operation_split, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_split_class_init (GimpOperationSplitClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:split", + "description", "GIMP split mode operation", + NULL); + + layer_mode_class->process = gimp_operation_split_process; +} + +static void +gimp_operation_split_init (GimpOperationSplit *self) +{ +} + +static gboolean +gimp_operation_split_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + if (layer_alpha <= in_alpha) + { + new_alpha = in_alpha - layer_alpha; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + else + { + new_alpha = layer_alpha - in_alpha; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + new_alpha = MAX (in_alpha - layer_alpha, 0.0f); + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat in_alpha = in[ALPHA]; + gfloat layer_alpha = layer[ALPHA] * opacity; + gfloat new_alpha; + gint b; + + if (has_mask) + layer_alpha *= *mask; + + new_alpha = MAX (layer_alpha - in_alpha, 0.0f); + + if (new_alpha != 0.0f) + { + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + out[ALPHA] = new_alpha; + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + + out[ALPHA] = 0.0f; + + in += 4; + out += 4; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationsplit.h b/app/operations/layer-modes/gimpoperationsplit.h new file mode 100644 index 0000000..e1dbbe9 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationsplit.h @@ -0,0 +1,54 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationsplit.h + * Copyright (C) 2008 Michael Natterer + * 2017 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 . + */ + +#ifndef __GIMP_OPERATION_SPLIT_H__ +#define __GIMP_OPERATION_SPLIT_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_SPLIT (gimp_operation_split_get_type ()) +#define GIMP_OPERATION_SPLIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplit)) +#define GIMP_OPERATION_SPLIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplitClass)) +#define GIMP_IS_OPERATION_SPLIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SPLIT)) +#define GIMP_IS_OPERATION_SPLIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SPLIT)) +#define GIMP_OPERATION_SPLIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplitClass)) + + +typedef struct _GimpOperationSplit GimpOperationSplit; +typedef struct _GimpOperationSplitClass GimpOperationSplitClass; + +struct _GimpOperationSplit +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationSplitClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_split_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_SPLIT_H__ */ diff --git a/app/operations/operations-enums.c b/app/operations/operations-enums.c new file mode 100644 index 0000000..1e8101d --- /dev/null +++ b/app/operations/operations-enums.c @@ -0,0 +1,370 @@ + +/* Generated data (by gimp-mkenums) */ + +#include "config.h" +#include +#include "libgimpbase/gimpbase.h" +#include "operations-enums.h" +#include "gimp-intl.h" + +/* enumerations from "operations-enums.h" */ +GType +gimp_layer_color_space_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_LAYER_COLOR_SPACE_AUTO, "GIMP_LAYER_COLOR_SPACE_AUTO", "auto" }, + { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, "GIMP_LAYER_COLOR_SPACE_RGB_LINEAR", "rgb-linear" }, + { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, "GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL", "rgb-perceptual" }, + { GIMP_LAYER_COLOR_SPACE_LAB, "GIMP_LAYER_COLOR_SPACE_LAB", "lab" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_LAYER_COLOR_SPACE_AUTO, NC_("layer-color-space", "Auto"), NULL }, + { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, NC_("layer-color-space", "RGB (linear)"), NULL }, + { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, NC_("layer-color-space", "RGB (perceptual)"), NULL }, + { GIMP_LAYER_COLOR_SPACE_LAB, NC_("layer-color-space", "LAB"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpLayerColorSpace", values); + gimp_type_set_translation_context (type, "layer-color-space"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_layer_composite_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_LAYER_COMPOSITE_AUTO, "GIMP_LAYER_COMPOSITE_AUTO", "auto" }, + { GIMP_LAYER_COMPOSITE_UNION, "GIMP_LAYER_COMPOSITE_UNION", "union" }, + { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, "GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP", "clip-to-backdrop" }, + { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, "GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER", "clip-to-layer" }, + { GIMP_LAYER_COMPOSITE_INTERSECTION, "GIMP_LAYER_COMPOSITE_INTERSECTION", "intersection" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_LAYER_COMPOSITE_AUTO, NC_("layer-composite-mode", "Auto"), NULL }, + { GIMP_LAYER_COMPOSITE_UNION, NC_("layer-composite-mode", "Union"), NULL }, + { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, NC_("layer-composite-mode", "Clip to backdrop"), NULL }, + { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, NC_("layer-composite-mode", "Clip to layer"), NULL }, + { GIMP_LAYER_COMPOSITE_INTERSECTION, NC_("layer-composite-mode", "Intersection"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpLayerCompositeMode", values); + gimp_type_set_translation_context (type, "layer-composite-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_layer_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_LAYER_MODE_NORMAL_LEGACY, "GIMP_LAYER_MODE_NORMAL_LEGACY", "normal-legacy" }, + { GIMP_LAYER_MODE_DISSOLVE, "GIMP_LAYER_MODE_DISSOLVE", "dissolve" }, + { GIMP_LAYER_MODE_BEHIND_LEGACY, "GIMP_LAYER_MODE_BEHIND_LEGACY", "behind-legacy" }, + { GIMP_LAYER_MODE_MULTIPLY_LEGACY, "GIMP_LAYER_MODE_MULTIPLY_LEGACY", "multiply-legacy" }, + { GIMP_LAYER_MODE_SCREEN_LEGACY, "GIMP_LAYER_MODE_SCREEN_LEGACY", "screen-legacy" }, + { GIMP_LAYER_MODE_OVERLAY_LEGACY, "GIMP_LAYER_MODE_OVERLAY_LEGACY", "overlay-legacy" }, + { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, "GIMP_LAYER_MODE_DIFFERENCE_LEGACY", "difference-legacy" }, + { GIMP_LAYER_MODE_ADDITION_LEGACY, "GIMP_LAYER_MODE_ADDITION_LEGACY", "addition-legacy" }, + { GIMP_LAYER_MODE_SUBTRACT_LEGACY, "GIMP_LAYER_MODE_SUBTRACT_LEGACY", "subtract-legacy" }, + { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, "GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY", "darken-only-legacy" }, + { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, "GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY", "lighten-only-legacy" }, + { GIMP_LAYER_MODE_HSV_HUE_LEGACY, "GIMP_LAYER_MODE_HSV_HUE_LEGACY", "hsv-hue-legacy" }, + { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, "GIMP_LAYER_MODE_HSV_SATURATION_LEGACY", "hsv-saturation-legacy" }, + { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, "GIMP_LAYER_MODE_HSL_COLOR_LEGACY", "hsl-color-legacy" }, + { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, "GIMP_LAYER_MODE_HSV_VALUE_LEGACY", "hsv-value-legacy" }, + { GIMP_LAYER_MODE_DIVIDE_LEGACY, "GIMP_LAYER_MODE_DIVIDE_LEGACY", "divide-legacy" }, + { GIMP_LAYER_MODE_DODGE_LEGACY, "GIMP_LAYER_MODE_DODGE_LEGACY", "dodge-legacy" }, + { GIMP_LAYER_MODE_BURN_LEGACY, "GIMP_LAYER_MODE_BURN_LEGACY", "burn-legacy" }, + { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, "GIMP_LAYER_MODE_HARDLIGHT_LEGACY", "hardlight-legacy" }, + { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, "GIMP_LAYER_MODE_SOFTLIGHT_LEGACY", "softlight-legacy" }, + { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, "GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY", "grain-extract-legacy" }, + { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, "GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY", "grain-merge-legacy" }, + { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, "GIMP_LAYER_MODE_COLOR_ERASE_LEGACY", "color-erase-legacy" }, + { GIMP_LAYER_MODE_OVERLAY, "GIMP_LAYER_MODE_OVERLAY", "overlay" }, + { GIMP_LAYER_MODE_LCH_HUE, "GIMP_LAYER_MODE_LCH_HUE", "lch-hue" }, + { GIMP_LAYER_MODE_LCH_CHROMA, "GIMP_LAYER_MODE_LCH_CHROMA", "lch-chroma" }, + { GIMP_LAYER_MODE_LCH_COLOR, "GIMP_LAYER_MODE_LCH_COLOR", "lch-color" }, + { GIMP_LAYER_MODE_LCH_LIGHTNESS, "GIMP_LAYER_MODE_LCH_LIGHTNESS", "lch-lightness" }, + { GIMP_LAYER_MODE_NORMAL, "GIMP_LAYER_MODE_NORMAL", "normal" }, + { GIMP_LAYER_MODE_BEHIND, "GIMP_LAYER_MODE_BEHIND", "behind" }, + { GIMP_LAYER_MODE_MULTIPLY, "GIMP_LAYER_MODE_MULTIPLY", "multiply" }, + { GIMP_LAYER_MODE_SCREEN, "GIMP_LAYER_MODE_SCREEN", "screen" }, + { GIMP_LAYER_MODE_DIFFERENCE, "GIMP_LAYER_MODE_DIFFERENCE", "difference" }, + { GIMP_LAYER_MODE_ADDITION, "GIMP_LAYER_MODE_ADDITION", "addition" }, + { GIMP_LAYER_MODE_SUBTRACT, "GIMP_LAYER_MODE_SUBTRACT", "subtract" }, + { GIMP_LAYER_MODE_DARKEN_ONLY, "GIMP_LAYER_MODE_DARKEN_ONLY", "darken-only" }, + { GIMP_LAYER_MODE_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LIGHTEN_ONLY", "lighten-only" }, + { GIMP_LAYER_MODE_HSV_HUE, "GIMP_LAYER_MODE_HSV_HUE", "hsv-hue" }, + { GIMP_LAYER_MODE_HSV_SATURATION, "GIMP_LAYER_MODE_HSV_SATURATION", "hsv-saturation" }, + { GIMP_LAYER_MODE_HSL_COLOR, "GIMP_LAYER_MODE_HSL_COLOR", "hsl-color" }, + { GIMP_LAYER_MODE_HSV_VALUE, "GIMP_LAYER_MODE_HSV_VALUE", "hsv-value" }, + { GIMP_LAYER_MODE_DIVIDE, "GIMP_LAYER_MODE_DIVIDE", "divide" }, + { GIMP_LAYER_MODE_DODGE, "GIMP_LAYER_MODE_DODGE", "dodge" }, + { GIMP_LAYER_MODE_BURN, "GIMP_LAYER_MODE_BURN", "burn" }, + { GIMP_LAYER_MODE_HARDLIGHT, "GIMP_LAYER_MODE_HARDLIGHT", "hardlight" }, + { GIMP_LAYER_MODE_SOFTLIGHT, "GIMP_LAYER_MODE_SOFTLIGHT", "softlight" }, + { GIMP_LAYER_MODE_GRAIN_EXTRACT, "GIMP_LAYER_MODE_GRAIN_EXTRACT", "grain-extract" }, + { GIMP_LAYER_MODE_GRAIN_MERGE, "GIMP_LAYER_MODE_GRAIN_MERGE", "grain-merge" }, + { GIMP_LAYER_MODE_VIVID_LIGHT, "GIMP_LAYER_MODE_VIVID_LIGHT", "vivid-light" }, + { GIMP_LAYER_MODE_PIN_LIGHT, "GIMP_LAYER_MODE_PIN_LIGHT", "pin-light" }, + { GIMP_LAYER_MODE_LINEAR_LIGHT, "GIMP_LAYER_MODE_LINEAR_LIGHT", "linear-light" }, + { GIMP_LAYER_MODE_HARD_MIX, "GIMP_LAYER_MODE_HARD_MIX", "hard-mix" }, + { GIMP_LAYER_MODE_EXCLUSION, "GIMP_LAYER_MODE_EXCLUSION", "exclusion" }, + { GIMP_LAYER_MODE_LINEAR_BURN, "GIMP_LAYER_MODE_LINEAR_BURN", "linear-burn" }, + { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, "GIMP_LAYER_MODE_LUMA_DARKEN_ONLY", "luma-darken-only" }, + { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY", "luma-lighten-only" }, + { GIMP_LAYER_MODE_LUMINANCE, "GIMP_LAYER_MODE_LUMINANCE", "luminance" }, + { GIMP_LAYER_MODE_COLOR_ERASE, "GIMP_LAYER_MODE_COLOR_ERASE", "color-erase" }, + { GIMP_LAYER_MODE_ERASE, "GIMP_LAYER_MODE_ERASE", "erase" }, + { GIMP_LAYER_MODE_MERGE, "GIMP_LAYER_MODE_MERGE", "merge" }, + { GIMP_LAYER_MODE_SPLIT, "GIMP_LAYER_MODE_SPLIT", "split" }, + { GIMP_LAYER_MODE_PASS_THROUGH, "GIMP_LAYER_MODE_PASS_THROUGH", "pass-through" }, + { GIMP_LAYER_MODE_REPLACE, "GIMP_LAYER_MODE_REPLACE", "replace" }, + { GIMP_LAYER_MODE_ANTI_ERASE, "GIMP_LAYER_MODE_ANTI_ERASE", "anti-erase" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_LAYER_MODE_NORMAL_LEGACY, NC_("layer-mode", "Normal (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Normal (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_NORMAL_LEGACY, NC_("layer-mode", "Normal (l)"), NULL }, + { GIMP_LAYER_MODE_DISSOLVE, NC_("layer-mode", "Dissolve"), NULL }, + { GIMP_LAYER_MODE_BEHIND_LEGACY, NC_("layer-mode", "Behind (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Behind (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_BEHIND_LEGACY, NC_("layer-mode", "Behind (l)"), NULL }, + { GIMP_LAYER_MODE_MULTIPLY_LEGACY, NC_("layer-mode", "Multiply (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Multiply (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_MULTIPLY_LEGACY, NC_("layer-mode", "Multiply (l)"), NULL }, + { GIMP_LAYER_MODE_SCREEN_LEGACY, NC_("layer-mode", "Screen (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Screen (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_SCREEN_LEGACY, NC_("layer-mode", "Screen (l)"), NULL }, + { GIMP_LAYER_MODE_OVERLAY_LEGACY, NC_("layer-mode", "Old broken Overlay"), NULL }, + /* Translators: this is an abbreviated version of "Old broken Overlay". + Keep it short. */ + { GIMP_LAYER_MODE_OVERLAY_LEGACY, NC_("layer-mode", "Old Overlay"), NULL }, + { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, NC_("layer-mode", "Difference (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Difference (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, NC_("layer-mode", "Difference (l)"), NULL }, + { GIMP_LAYER_MODE_ADDITION_LEGACY, NC_("layer-mode", "Addition (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Addition (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_ADDITION_LEGACY, NC_("layer-mode", "Addition (l)"), NULL }, + { GIMP_LAYER_MODE_SUBTRACT_LEGACY, NC_("layer-mode", "Subtract (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Subtract (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_SUBTRACT_LEGACY, NC_("layer-mode", "Subtract (l)"), NULL }, + { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, NC_("layer-mode", "Darken only (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Darken only (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, NC_("layer-mode", "Darken only (l)"), NULL }, + { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, NC_("layer-mode", "Lighten only (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Lighten only (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, NC_("layer-mode", "Lighten only (l)"), NULL }, + { GIMP_LAYER_MODE_HSV_HUE_LEGACY, NC_("layer-mode", "HSV Hue (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "HSV Hue (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_HSV_HUE_LEGACY, NC_("layer-mode", "HSV Hue (l)"), NULL }, + { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, NC_("layer-mode", "HSV Saturation (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "HSV Saturation (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, NC_("layer-mode", "HSV Saturation (l)"), NULL }, + { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, NC_("layer-mode", "HSL Color (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "HSL Color (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, NC_("layer-mode", "HSL Color (l)"), NULL }, + { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, NC_("layer-mode", "HSV Value (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "HSV Value (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, NC_("layer-mode", "HSV Value (l)"), NULL }, + { GIMP_LAYER_MODE_DIVIDE_LEGACY, NC_("layer-mode", "Divide (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Divide (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_DIVIDE_LEGACY, NC_("layer-mode", "Divide (l)"), NULL }, + { GIMP_LAYER_MODE_DODGE_LEGACY, NC_("layer-mode", "Dodge (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Dodge (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_DODGE_LEGACY, NC_("layer-mode", "Dodge (l)"), NULL }, + { GIMP_LAYER_MODE_BURN_LEGACY, NC_("layer-mode", "Burn (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Burn (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_BURN_LEGACY, NC_("layer-mode", "Burn (l)"), NULL }, + { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, NC_("layer-mode", "Hard light (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Hard light (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, NC_("layer-mode", "Hard light (l)"), NULL }, + { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, NC_("layer-mode", "Soft light (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Soft light (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, NC_("layer-mode", "Soft light (l)"), NULL }, + { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, NC_("layer-mode", "Grain extract (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Grain extract (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, NC_("layer-mode", "Grain extract (l)"), NULL }, + { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, NC_("layer-mode", "Grain merge (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Grain merge (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, NC_("layer-mode", "Grain merge (l)"), NULL }, + { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, NC_("layer-mode", "Color erase (legacy)"), NULL }, + /* Translators: this is an abbreviated version of "Color erase (legacy)". + Keep it short. */ + { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, NC_("layer-mode", "Color erase (l)"), NULL }, + { GIMP_LAYER_MODE_OVERLAY, NC_("layer-mode", "Overlay"), NULL }, + { GIMP_LAYER_MODE_LCH_HUE, NC_("layer-mode", "LCh Hue"), NULL }, + { GIMP_LAYER_MODE_LCH_CHROMA, NC_("layer-mode", "LCh Chroma"), NULL }, + { GIMP_LAYER_MODE_LCH_COLOR, NC_("layer-mode", "LCh Color"), NULL }, + { GIMP_LAYER_MODE_LCH_LIGHTNESS, NC_("layer-mode", "LCh Lightness"), NULL }, + { GIMP_LAYER_MODE_NORMAL, NC_("layer-mode", "Normal"), NULL }, + { GIMP_LAYER_MODE_BEHIND, NC_("layer-mode", "Behind"), NULL }, + { GIMP_LAYER_MODE_MULTIPLY, NC_("layer-mode", "Multiply"), NULL }, + { GIMP_LAYER_MODE_SCREEN, NC_("layer-mode", "Screen"), NULL }, + { GIMP_LAYER_MODE_DIFFERENCE, NC_("layer-mode", "Difference"), NULL }, + { GIMP_LAYER_MODE_ADDITION, NC_("layer-mode", "Addition"), NULL }, + { GIMP_LAYER_MODE_SUBTRACT, NC_("layer-mode", "Subtract"), NULL }, + { GIMP_LAYER_MODE_DARKEN_ONLY, NC_("layer-mode", "Darken only"), NULL }, + { GIMP_LAYER_MODE_LIGHTEN_ONLY, NC_("layer-mode", "Lighten only"), NULL }, + { GIMP_LAYER_MODE_HSV_HUE, NC_("layer-mode", "HSV Hue"), NULL }, + { GIMP_LAYER_MODE_HSV_SATURATION, NC_("layer-mode", "HSV Saturation"), NULL }, + { GIMP_LAYER_MODE_HSL_COLOR, NC_("layer-mode", "HSL Color"), NULL }, + { GIMP_LAYER_MODE_HSV_VALUE, NC_("layer-mode", "HSV Value"), NULL }, + { GIMP_LAYER_MODE_DIVIDE, NC_("layer-mode", "Divide"), NULL }, + { GIMP_LAYER_MODE_DODGE, NC_("layer-mode", "Dodge"), NULL }, + { GIMP_LAYER_MODE_BURN, NC_("layer-mode", "Burn"), NULL }, + { GIMP_LAYER_MODE_HARDLIGHT, NC_("layer-mode", "Hard light"), NULL }, + { GIMP_LAYER_MODE_SOFTLIGHT, NC_("layer-mode", "Soft light"), NULL }, + { GIMP_LAYER_MODE_GRAIN_EXTRACT, NC_("layer-mode", "Grain extract"), NULL }, + { GIMP_LAYER_MODE_GRAIN_MERGE, NC_("layer-mode", "Grain merge"), NULL }, + { GIMP_LAYER_MODE_VIVID_LIGHT, NC_("layer-mode", "Vivid light"), NULL }, + { GIMP_LAYER_MODE_PIN_LIGHT, NC_("layer-mode", "Pin light"), NULL }, + { GIMP_LAYER_MODE_LINEAR_LIGHT, NC_("layer-mode", "Linear light"), NULL }, + { GIMP_LAYER_MODE_HARD_MIX, NC_("layer-mode", "Hard mix"), NULL }, + { GIMP_LAYER_MODE_EXCLUSION, NC_("layer-mode", "Exclusion"), NULL }, + { GIMP_LAYER_MODE_LINEAR_BURN, NC_("layer-mode", "Linear burn"), NULL }, + { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, NC_("layer-mode", "Luma/Luminance darken only"), NULL }, + /* Translators: this is an abbreviated version of "Luma/Luminance darken only". + Keep it short. */ + { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, NC_("layer-mode", "Luma darken only"), NULL }, + { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, NC_("layer-mode", "Luma/Luminance lighten only"), NULL }, + /* Translators: this is an abbreviated version of "Luma/Luminance lighten only". + Keep it short. */ + { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, NC_("layer-mode", "Luma lighten only"), NULL }, + { GIMP_LAYER_MODE_LUMINANCE, NC_("layer-mode", "Luminance"), NULL }, + { GIMP_LAYER_MODE_COLOR_ERASE, NC_("layer-mode", "Color erase"), NULL }, + { GIMP_LAYER_MODE_ERASE, NC_("layer-mode", "Erase"), NULL }, + { GIMP_LAYER_MODE_MERGE, NC_("layer-mode", "Merge"), NULL }, + { GIMP_LAYER_MODE_SPLIT, NC_("layer-mode", "Split"), NULL }, + { GIMP_LAYER_MODE_PASS_THROUGH, NC_("layer-mode", "Pass through"), NULL }, + { GIMP_LAYER_MODE_REPLACE, NC_("layer-mode", "Replace"), NULL }, + { GIMP_LAYER_MODE_ANTI_ERASE, NC_("layer-mode", "Anti erase"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpLayerMode", values); + gimp_type_set_translation_context (type, "layer-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_layer_mode_group_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_LAYER_MODE_GROUP_DEFAULT, "GIMP_LAYER_MODE_GROUP_DEFAULT", "default" }, + { GIMP_LAYER_MODE_GROUP_LEGACY, "GIMP_LAYER_MODE_GROUP_LEGACY", "legacy" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_LAYER_MODE_GROUP_DEFAULT, NC_("layer-mode-group", "Default"), NULL }, + { GIMP_LAYER_MODE_GROUP_LEGACY, NC_("layer-mode-group", "Legacy"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpLayerModeGroup", values); + gimp_type_set_translation_context (type, "layer-mode-group"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + +GType +gimp_layer_mode_context_get_type (void) +{ + static const GFlagsValue values[] = + { + { GIMP_LAYER_MODE_CONTEXT_LAYER, "GIMP_LAYER_MODE_CONTEXT_LAYER", "layer" }, + { GIMP_LAYER_MODE_CONTEXT_GROUP, "GIMP_LAYER_MODE_CONTEXT_GROUP", "group" }, + { GIMP_LAYER_MODE_CONTEXT_PAINT, "GIMP_LAYER_MODE_CONTEXT_PAINT", "paint" }, + { GIMP_LAYER_MODE_CONTEXT_FILTER, "GIMP_LAYER_MODE_CONTEXT_FILTER", "filter" }, + { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", "all" }, + { 0, NULL, NULL } + }; + + static const GimpFlagsDesc descs[] = + { + { GIMP_LAYER_MODE_CONTEXT_LAYER, "GIMP_LAYER_MODE_CONTEXT_LAYER", NULL }, + { GIMP_LAYER_MODE_CONTEXT_GROUP, "GIMP_LAYER_MODE_CONTEXT_GROUP", NULL }, + { GIMP_LAYER_MODE_CONTEXT_PAINT, "GIMP_LAYER_MODE_CONTEXT_PAINT", NULL }, + { GIMP_LAYER_MODE_CONTEXT_FILTER, "GIMP_LAYER_MODE_CONTEXT_FILTER", NULL }, + { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_flags_register_static ("GimpLayerModeContext", values); + gimp_type_set_translation_context (type, "layer-mode-context"); + gimp_flags_set_value_descriptions (type, descs); + } + + return type; +} + + +/* Generated data ends here */ + diff --git a/app/operations/operations-enums.h b/app/operations/operations-enums.h new file mode 100644 index 0000000..0dc51b9 --- /dev/null +++ b/app/operations/operations-enums.h @@ -0,0 +1,190 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * operations-enums.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 . + */ + +#ifndef __OPERATIONS_ENUMS_H__ +#define __OPERATIONS_ENUMS_H__ + + +#define GIMP_TYPE_LAYER_COLOR_SPACE (gimp_layer_color_space_get_type ()) + +GType gimp_layer_color_space_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_LAYER_COLOR_SPACE_AUTO, /*< desc="Auto" >*/ + GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, /*< desc="RGB (linear)" >*/ + GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, /*< desc="RGB (perceptual)" >*/ + GIMP_LAYER_COLOR_SPACE_LAB, /*< desc="LAB", pdb-skip >*/ +} GimpLayerColorSpace; + + +#define GIMP_TYPE_LAYER_COMPOSITE_MODE (gimp_layer_composite_mode_get_type ()) + +GType gimp_layer_composite_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_LAYER_COMPOSITE_AUTO, /*< desc="Auto" >*/ + GIMP_LAYER_COMPOSITE_UNION, /*< desc="Union" >*/ + GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, /*< desc="Clip to backdrop" >*/ + GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, /*< desc="Clip to layer" >*/ + GIMP_LAYER_COMPOSITE_INTERSECTION /*< desc="Intersection" >*/ +} GimpLayerCompositeMode; + + +#define GIMP_TYPE_LAYER_MODE (gimp_layer_mode_get_type ()) + +GType gimp_layer_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + /* Modes that exist since ancient times */ + GIMP_LAYER_MODE_NORMAL_LEGACY, /*< desc="Normal (legacy)", abbrev="Normal (l)" >*/ + GIMP_LAYER_MODE_DISSOLVE, /*< desc="Dissolve" >*/ + GIMP_LAYER_MODE_BEHIND_LEGACY, /*< desc="Behind (legacy)", abbrev="Behind (l)" >*/ + GIMP_LAYER_MODE_MULTIPLY_LEGACY, /*< desc="Multiply (legacy)", abbrev="Multiply (l)" >*/ + GIMP_LAYER_MODE_SCREEN_LEGACY, /*< desc="Screen (legacy)", abbrev="Screen (l)" >*/ + GIMP_LAYER_MODE_OVERLAY_LEGACY, /*< desc="Old broken Overlay", abbrev="Old Overlay" >*/ + GIMP_LAYER_MODE_DIFFERENCE_LEGACY, /*< desc="Difference (legacy)", abbrev="Difference (l)" >*/ + GIMP_LAYER_MODE_ADDITION_LEGACY, /*< desc="Addition (legacy)", abbrev="Addition (l)" >*/ + GIMP_LAYER_MODE_SUBTRACT_LEGACY, /*< desc="Subtract (legacy)", abbrev="Subtract (l)" >*/ + GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, /*< desc="Darken only (legacy)", abbrev="Darken only (l)" >*/ + GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, /*< desc="Lighten only (legacy)", abbrev="Lighten only (l)" >*/ + GIMP_LAYER_MODE_HSV_HUE_LEGACY, /*< desc="HSV Hue (legacy)", abbrev="HSV Hue (l)" >*/ + GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, /*< desc="HSV Saturation (legacy)", abbrev="HSV Saturation (l)" >*/ + GIMP_LAYER_MODE_HSL_COLOR_LEGACY, /*< desc="HSL Color (legacy)", abbrev="HSL Color (l)" >*/ + GIMP_LAYER_MODE_HSV_VALUE_LEGACY, /*< desc="HSV Value (legacy)", abbrev="HSV Value (l)" >*/ + GIMP_LAYER_MODE_DIVIDE_LEGACY, /*< desc="Divide (legacy)", abbrev="Divide (l)" >*/ + GIMP_LAYER_MODE_DODGE_LEGACY, /*< desc="Dodge (legacy)", abbrev="Dodge (l)" >*/ + GIMP_LAYER_MODE_BURN_LEGACY, /*< desc="Burn (legacy)", abbrev="Burn (l)" >*/ + GIMP_LAYER_MODE_HARDLIGHT_LEGACY, /*< desc="Hard light (legacy)", abbrev="Hard light (l)" >*/ + + /* Since 2.8 (XCF version 2) */ + GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, /*< desc="Soft light (legacy)", abbrev="Soft light (l)" >*/ + GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, /*< desc="Grain extract (legacy)", abbrev="Grain extract (l)" >*/ + GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, /*< desc="Grain merge (legacy)", abbrev="Grain merge (l)" >*/ + GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, /*< desc="Color erase (legacy)", abbrev="Color erase (l)" >*/ + + /* Since 2.10 (XCF version 9) */ + GIMP_LAYER_MODE_OVERLAY, /*< desc="Overlay" >*/ + GIMP_LAYER_MODE_LCH_HUE, /*< desc="LCh Hue" >*/ + GIMP_LAYER_MODE_LCH_CHROMA, /*< desc="LCh Chroma" >*/ + GIMP_LAYER_MODE_LCH_COLOR, /*< desc="LCh Color" >*/ + GIMP_LAYER_MODE_LCH_LIGHTNESS, /*< desc="LCh Lightness" >*/ + + /* Since 2.10 (XCF version 10) */ + GIMP_LAYER_MODE_NORMAL, /*< desc="Normal" >*/ + GIMP_LAYER_MODE_BEHIND, /*< desc="Behind" >*/ + GIMP_LAYER_MODE_MULTIPLY, /*< desc="Multiply" >*/ + GIMP_LAYER_MODE_SCREEN, /*< desc="Screen" >*/ + GIMP_LAYER_MODE_DIFFERENCE, /*< desc="Difference" >*/ + GIMP_LAYER_MODE_ADDITION, /*< desc="Addition" >*/ + GIMP_LAYER_MODE_SUBTRACT, /*< desc="Subtract" >*/ + GIMP_LAYER_MODE_DARKEN_ONLY, /*< desc="Darken only" >*/ + GIMP_LAYER_MODE_LIGHTEN_ONLY, /*< desc="Lighten only" >*/ + GIMP_LAYER_MODE_HSV_HUE, /*< desc="HSV Hue" >*/ + GIMP_LAYER_MODE_HSV_SATURATION, /*< desc="HSV Saturation" >*/ + GIMP_LAYER_MODE_HSL_COLOR, /*< desc="HSL Color" >*/ + GIMP_LAYER_MODE_HSV_VALUE, /*< desc="HSV Value" >*/ + GIMP_LAYER_MODE_DIVIDE, /*< desc="Divide" >*/ + GIMP_LAYER_MODE_DODGE, /*< desc="Dodge" >*/ + GIMP_LAYER_MODE_BURN, /*< desc="Burn" >*/ + GIMP_LAYER_MODE_HARDLIGHT, /*< desc="Hard light" >*/ + GIMP_LAYER_MODE_SOFTLIGHT, /*< desc="Soft light" >*/ + GIMP_LAYER_MODE_GRAIN_EXTRACT, /*< desc="Grain extract" >*/ + GIMP_LAYER_MODE_GRAIN_MERGE, /*< desc="Grain merge" >*/ + GIMP_LAYER_MODE_VIVID_LIGHT, /*< desc="Vivid light" >*/ + GIMP_LAYER_MODE_PIN_LIGHT, /*< desc="Pin light" >*/ + GIMP_LAYER_MODE_LINEAR_LIGHT, /*< desc="Linear light" >*/ + GIMP_LAYER_MODE_HARD_MIX, /*< desc="Hard mix" >*/ + GIMP_LAYER_MODE_EXCLUSION, /*< desc="Exclusion" >*/ + GIMP_LAYER_MODE_LINEAR_BURN, /*< desc="Linear burn" >*/ + GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, /*< desc="Luma/Luminance darken only", abbrev="Luma darken only" >*/ + GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, /*< desc="Luma/Luminance lighten only", abbrev="Luma lighten only" >*/ + GIMP_LAYER_MODE_LUMINANCE, /*< desc="Luminance" >*/ + GIMP_LAYER_MODE_COLOR_ERASE, /*< desc="Color erase" >*/ + GIMP_LAYER_MODE_ERASE, /*< desc="Erase" >*/ + GIMP_LAYER_MODE_MERGE, /*< desc="Merge" >*/ + GIMP_LAYER_MODE_SPLIT, /*< desc="Split" >*/ + GIMP_LAYER_MODE_PASS_THROUGH, /*< desc="Pass through" >*/ + + /* Internal modes, not available to the PDB, must be kept at the end */ + GIMP_LAYER_MODE_REPLACE, /*< pdb-skip, desc="Replace" >*/ + GIMP_LAYER_MODE_ANTI_ERASE, /*< pdb-skip, desc="Anti erase" >*/ + + /* Layer mode menu separator */ + GIMP_LAYER_MODE_SEPARATOR = -1 /*< pdb-skip, skip >*/ +} GimpLayerMode; + + +#define GIMP_TYPE_LAYER_MODE_GROUP (gimp_layer_mode_group_get_type ()) + +GType gimp_layer_mode_group_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_LAYER_MODE_GROUP_DEFAULT, /*< desc="Default" >*/ + GIMP_LAYER_MODE_GROUP_LEGACY, /*< desc="Legacy" >*/ +} GimpLayerModeGroup; + + +#define GIMP_TYPE_LAYER_MODE_CONTEXT (gimp_layer_mode_context_get_type ()) + +GType gimp_layer_mode_context_get_type (void) G_GNUC_CONST; + +typedef enum /*< pdb-skip >*/ +{ + GIMP_LAYER_MODE_CONTEXT_LAYER = 1 << 0, + GIMP_LAYER_MODE_CONTEXT_GROUP = 1 << 1, + GIMP_LAYER_MODE_CONTEXT_PAINT = 1 << 2, + GIMP_LAYER_MODE_CONTEXT_FILTER = 1 << 3, + + GIMP_LAYER_MODE_CONTEXT_ALL = (GIMP_LAYER_MODE_CONTEXT_LAYER | + GIMP_LAYER_MODE_CONTEXT_GROUP | + GIMP_LAYER_MODE_CONTEXT_PAINT | + GIMP_LAYER_MODE_CONTEXT_FILTER) +} GimpLayerModeContext; + + +/* + * non-registered enums; register them if needed + */ + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_LAYER_COMPOSITE_REGION_INTERSECTION = 0, + GIMP_LAYER_COMPOSITE_REGION_DESTINATION = 1 << 0, + GIMP_LAYER_COMPOSITE_REGION_SOURCE = 1 << 1, + GIMP_LAYER_COMPOSITE_REGION_UNION = (GIMP_LAYER_COMPOSITE_REGION_DESTINATION | + GIMP_LAYER_COMPOSITE_REGION_SOURCE), +} GimpLayerCompositeRegion; + +typedef enum /*< pdb-skip, skip >*/ +{ + GIMP_LAYER_MODE_FLAG_LEGACY = 1 << 0, + GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE = 1 << 1, + GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE = 1 << 2, + GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE = 1 << 3, + GIMP_LAYER_MODE_FLAG_SUBTRACTIVE = 1 << 4, + GIMP_LAYER_MODE_FLAG_ALPHA_ONLY = 1 << 5, + GIMP_LAYER_MODE_FLAG_TRIVIAL = 1 << 6 +} GimpLayerModeFlags; + + +#endif /* __OPERATIONS_ENUMS_H__ */ diff --git a/app/operations/operations-types.h b/app/operations/operations-types.h new file mode 100644 index 0000000..15e97d8 --- /dev/null +++ b/app/operations/operations-types.h @@ -0,0 +1,76 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * operations-types.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 . + */ + +#ifndef __OPERATIONS_TYPES_H__ +#define __OPERATIONS_TYPES_H__ + + +#include + +#include "gegl/gimp-gegl-types.h" + +#include "operations-enums.h" + + +/* operations */ + +typedef struct _GimpOperationPointFilter GimpOperationPointFilter; +typedef struct _GimpOperationLayerMode GimpOperationLayerMode; + + +/* operation config objects */ + +typedef struct _GimpOperationSettings GimpOperationSettings; + +typedef struct _GimpBrightnessContrastConfig GimpBrightnessContrastConfig; +typedef struct _GimpCageConfig GimpCageConfig; +typedef struct _GimpColorBalanceConfig GimpColorBalanceConfig; +typedef struct _GimpColorizeConfig GimpColorizeConfig; +typedef struct _GimpCurvesConfig GimpCurvesConfig; +typedef struct _GimpDesaturateConfig GimpDesaturateConfig; +typedef struct _GimpHueSaturationConfig GimpHueSaturationConfig; +typedef struct _GimpLevelsConfig GimpLevelsConfig; +typedef struct _GimpPosterizeConfig GimpPosterizeConfig; +typedef struct _GimpThresholdConfig GimpThresholdConfig; + + +/* non-object types */ + +typedef struct _GimpCagePoint GimpCagePoint; + + +/* functions */ + +typedef gboolean (* GimpLayerModeFunc) (GeglOperation *operation, + void *in, + void *aux, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + +typedef void (* GimpLayerModeBlendFunc) (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *out, + gint samples); + + +#endif /* __OPERATIONS_TYPES_H__ */ diff --git a/app/operations/tests/Makefile.am b/app/operations/tests/Makefile.am new file mode 100644 index 0000000..fdf0a9a --- /dev/null +++ b/app/operations/tests/Makefile.am @@ -0,0 +1,71 @@ +#TESTS = test-operations + +EXTRA_PROGRAMS = $(TESTS) +CLEANFILES = $(EXTRA_PROGRAMS) + +$(TESTS): output-dir + +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la + +if OS_WIN32 +else +libm = -lm +endif + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + -I$(includedir) + +# We need this due to circular dependencies, see more detailed +# comments about it in app/Makefile.am +AM_LDFLAGS = \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_prop_undo_get_type + +# Note that we have some duplicate entries here too to work around +# circular dependencies and systems on the same architectural layer as +# an alternative to LDFLAGS above +LDADD = \ + $(top_builddir)/app/xcf/libappxcf.a \ + $(top_builddir)/app/pdb/libappinternal-procs.a \ + $(top_builddir)/app/pdb/libapppdb.a \ + $(top_builddir)/app/plug-in/libappplug-in.a \ + $(top_builddir)/app/vectors/libappvectors.a \ + $(top_builddir)/app/core/libappcore.a \ + $(top_builddir)/app/file/libappfile.a \ + $(top_builddir)/app/text/libapptext.a \ + $(top_builddir)/app/paint/libapppaint.a \ + $(top_builddir)/app/config/libappconfig.a \ + $(top_builddir)/app/libapp.a \ + $(top_builddir)/app/gegl/libappgegl.a \ + $(top_builddir)/app/operations/libappoperations.a \ + $(libgimpconfig) \ + $(libgimpmath) \ + $(libgimpthumb) \ + $(libgimpcolor) \ + $(libgimpmodule) \ + $(libgimpbase) \ + $(GDK_PIXBUF_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(GEGL_LIBS) \ + $(GLIB_LIBS) \ + $(libm) + +output-dir: + mkdir -p output + +clean-local: + rm -rf output diff --git a/app/operations/tests/Makefile.in b/app/operations/tests/Makefile.in new file mode 100644 index 0000000..41af881 --- /dev/null +++ b/app/operations/tests/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@ + +#TESTS = test-operations +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@ +EXTRA_PROGRAMS = +subdir = app/operations/tests +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__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@ +CLEANFILES = $(EXTRA_PROGRAMS) +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 +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la +@OS_WIN32_FALSE@libm = -lm +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/app \ + $(GEGL_CFLAGS) \ + -I$(includedir) + + +# We need this due to circular dependencies, see more detailed +# comments about it in app/Makefile.am +AM_LDFLAGS = \ + -Wl,-u,$(SYMPREFIX)xcf_init \ + -Wl,-u,$(SYMPREFIX)internal_procs_init \ + -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \ + -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \ + -Wl,-u,$(SYMPREFIX)gimp_vectors_prop_undo_get_type + + +# Note that we have some duplicate entries here too to work around +# circular dependencies and systems on the same architectural layer as +# an alternative to LDFLAGS above +LDADD = \ + $(top_builddir)/app/xcf/libappxcf.a \ + $(top_builddir)/app/pdb/libappinternal-procs.a \ + $(top_builddir)/app/pdb/libapppdb.a \ + $(top_builddir)/app/plug-in/libappplug-in.a \ + $(top_builddir)/app/vectors/libappvectors.a \ + $(top_builddir)/app/core/libappcore.a \ + $(top_builddir)/app/file/libappfile.a \ + $(top_builddir)/app/text/libapptext.a \ + $(top_builddir)/app/paint/libapppaint.a \ + $(top_builddir)/app/config/libappconfig.a \ + $(top_builddir)/app/libapp.a \ + $(top_builddir)/app/gegl/libappgegl.a \ + $(top_builddir)/app/operations/libappoperations.a \ + $(libgimpconfig) \ + $(libgimpmath) \ + $(libgimpthumb) \ + $(libgimpcolor) \ + $(libgimpmodule) \ + $(libgimpbase) \ + $(GDK_PIXBUF_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(GEGL_LIBS) \ + $(GLIB_LIBS) \ + $(libm) + +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 app/operations/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/operations/tests/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 +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 clean-local 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 \ + clean-local 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 + + +$(TESTS): output-dir + +output-dir: + mkdir -p output + +clean-local: + rm -rf output + +# 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/app/paint/Makefile.am b/app/paint/Makefile.am new file mode 100644 index 0000000..1cc625e --- /dev/null +++ b/app/paint/Makefile.am @@ -0,0 +1,125 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Paint\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(LIBMYPAINT_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libapppaint.a + +libapppaint_a_sources = \ + paint-enums.h \ + paint-types.h \ + gimp-paint.c \ + gimp-paint.h \ + gimpairbrush.c \ + gimpairbrush.h \ + gimpairbrushoptions.c \ + gimpairbrushoptions.h \ + gimpbrushcore.c \ + gimpbrushcore.h \ + gimpbrushcore-loops.cc \ + gimpbrushcore-loops.h \ + gimpbrushcore-kernels.h \ + gimpclone.c \ + gimpclone.h \ + gimpcloneoptions.c \ + gimpcloneoptions.h \ + gimpconvolve.c \ + gimpconvolve.h \ + gimpconvolveoptions.c \ + gimpconvolveoptions.h \ + gimpdodgeburn.c \ + gimpdodgeburn.h \ + gimpdodgeburnoptions.c \ + gimpdodgeburnoptions.h \ + gimperaser.c \ + gimperaser.h \ + gimperaseroptions.c \ + gimperaseroptions.h \ + gimpheal.c \ + gimpheal.h \ + gimpink.c \ + gimpink.h \ + gimpink-blob.c \ + gimpink-blob.h \ + gimpinkoptions.c \ + gimpinkoptions.h \ + gimpinkundo.c \ + gimpinkundo.h \ + gimpmybrushcore.c \ + gimpmybrushcore.h \ + gimpmybrushoptions.c \ + gimpmybrushoptions.h \ + gimpmybrushsurface.c \ + gimpmybrushsurface.h \ + gimppaintcore.c \ + gimppaintcore.h \ + gimppaintcore-loops.cc \ + gimppaintcore-loops.h \ + gimppaintcore-stroke.c \ + gimppaintcore-stroke.h \ + gimppaintcoreundo.c \ + gimppaintcoreundo.h \ + gimppaintoptions.c \ + gimppaintoptions.h \ + gimppencil.c \ + gimppencil.h \ + gimppenciloptions.c \ + gimppenciloptions.h \ + gimppaintbrush.c \ + gimppaintbrush.h \ + gimpperspectiveclone.c \ + gimpperspectiveclone.h \ + gimpperspectivecloneoptions.c \ + gimpperspectivecloneoptions.h \ + gimpsmudge.c \ + gimpsmudge.h \ + gimpsmudgeoptions.c \ + gimpsmudgeoptions.h \ + gimpsourcecore.c \ + gimpsourcecore.h \ + gimpsourceoptions.c \ + gimpsourceoptions.h + +libapppaint_a_built_sources = paint-enums.c + +libapppaint_a_SOURCES = $(libapppaint_a_built_sources) $(libapppaint_a_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-pec +CLEANFILES = $(gen_sources) + +xgen-pec: $(srcdir)/paint-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"paint-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/paint-enums.c: xgen-pec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + fi diff --git a/app/paint/Makefile.in b/app/paint/Makefile.in new file mode 100644 index 0000000..7cdaca4 --- /dev/null +++ b/app/paint/Makefile.in @@ -0,0 +1,1208 @@ +# 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 = app/paint +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 = +libapppaint_a_AR = $(AR) $(ARFLAGS) +libapppaint_a_LIBADD = +am__objects_1 = paint-enums.$(OBJEXT) +am__objects_2 = gimp-paint.$(OBJEXT) gimpairbrush.$(OBJEXT) \ + gimpairbrushoptions.$(OBJEXT) gimpbrushcore.$(OBJEXT) \ + gimpbrushcore-loops.$(OBJEXT) gimpclone.$(OBJEXT) \ + gimpcloneoptions.$(OBJEXT) gimpconvolve.$(OBJEXT) \ + gimpconvolveoptions.$(OBJEXT) gimpdodgeburn.$(OBJEXT) \ + gimpdodgeburnoptions.$(OBJEXT) gimperaser.$(OBJEXT) \ + gimperaseroptions.$(OBJEXT) gimpheal.$(OBJEXT) \ + gimpink.$(OBJEXT) gimpink-blob.$(OBJEXT) \ + gimpinkoptions.$(OBJEXT) gimpinkundo.$(OBJEXT) \ + gimpmybrushcore.$(OBJEXT) gimpmybrushoptions.$(OBJEXT) \ + gimpmybrushsurface.$(OBJEXT) gimppaintcore.$(OBJEXT) \ + gimppaintcore-loops.$(OBJEXT) gimppaintcore-stroke.$(OBJEXT) \ + gimppaintcoreundo.$(OBJEXT) gimppaintoptions.$(OBJEXT) \ + gimppencil.$(OBJEXT) gimppenciloptions.$(OBJEXT) \ + gimppaintbrush.$(OBJEXT) gimpperspectiveclone.$(OBJEXT) \ + gimpperspectivecloneoptions.$(OBJEXT) gimpsmudge.$(OBJEXT) \ + gimpsmudgeoptions.$(OBJEXT) gimpsourcecore.$(OBJEXT) \ + gimpsourceoptions.$(OBJEXT) +am_libapppaint_a_OBJECTS = $(am__objects_1) $(am__objects_2) +libapppaint_a_OBJECTS = $(am_libapppaint_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)/gimp-paint.Po \ + ./$(DEPDIR)/gimpairbrush.Po ./$(DEPDIR)/gimpairbrushoptions.Po \ + ./$(DEPDIR)/gimpbrushcore-loops.Po \ + ./$(DEPDIR)/gimpbrushcore.Po ./$(DEPDIR)/gimpclone.Po \ + ./$(DEPDIR)/gimpcloneoptions.Po ./$(DEPDIR)/gimpconvolve.Po \ + ./$(DEPDIR)/gimpconvolveoptions.Po \ + ./$(DEPDIR)/gimpdodgeburn.Po \ + ./$(DEPDIR)/gimpdodgeburnoptions.Po ./$(DEPDIR)/gimperaser.Po \ + ./$(DEPDIR)/gimperaseroptions.Po ./$(DEPDIR)/gimpheal.Po \ + ./$(DEPDIR)/gimpink-blob.Po ./$(DEPDIR)/gimpink.Po \ + ./$(DEPDIR)/gimpinkoptions.Po ./$(DEPDIR)/gimpinkundo.Po \ + ./$(DEPDIR)/gimpmybrushcore.Po \ + ./$(DEPDIR)/gimpmybrushoptions.Po \ + ./$(DEPDIR)/gimpmybrushsurface.Po \ + ./$(DEPDIR)/gimppaintbrush.Po \ + ./$(DEPDIR)/gimppaintcore-loops.Po \ + ./$(DEPDIR)/gimppaintcore-stroke.Po \ + ./$(DEPDIR)/gimppaintcore.Po ./$(DEPDIR)/gimppaintcoreundo.Po \ + ./$(DEPDIR)/gimppaintoptions.Po ./$(DEPDIR)/gimppencil.Po \ + ./$(DEPDIR)/gimppenciloptions.Po \ + ./$(DEPDIR)/gimpperspectiveclone.Po \ + ./$(DEPDIR)/gimpperspectivecloneoptions.Po \ + ./$(DEPDIR)/gimpsmudge.Po ./$(DEPDIR)/gimpsmudgeoptions.Po \ + ./$(DEPDIR)/gimpsourcecore.Po ./$(DEPDIR)/gimpsourceoptions.Po \ + ./$(DEPDIR)/paint-enums.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 = +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 = $(libapppaint_a_SOURCES) +DIST_SOURCES = $(libapppaint_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 +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_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Gimp-Paint\" \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_builddir)/app \ + -I$(top_srcdir)/app \ + $(CAIRO_CFLAGS) \ + $(GEGL_CFLAGS) \ + $(GDK_PIXBUF_CFLAGS) \ + $(LIBMYPAINT_CFLAGS) \ + -I$(includedir) + +noinst_LIBRARIES = libapppaint.a +libapppaint_a_sources = \ + paint-enums.h \ + paint-types.h \ + gimp-paint.c \ + gimp-paint.h \ + gimpairbrush.c \ + gimpairbrush.h \ + gimpairbrushoptions.c \ + gimpairbrushoptions.h \ + gimpbrushcore.c \ + gimpbrushcore.h \ + gimpbrushcore-loops.cc \ + gimpbrushcore-loops.h \ + gimpbrushcore-kernels.h \ + gimpclone.c \ + gimpclone.h \ + gimpcloneoptions.c \ + gimpcloneoptions.h \ + gimpconvolve.c \ + gimpconvolve.h \ + gimpconvolveoptions.c \ + gimpconvolveoptions.h \ + gimpdodgeburn.c \ + gimpdodgeburn.h \ + gimpdodgeburnoptions.c \ + gimpdodgeburnoptions.h \ + gimperaser.c \ + gimperaser.h \ + gimperaseroptions.c \ + gimperaseroptions.h \ + gimpheal.c \ + gimpheal.h \ + gimpink.c \ + gimpink.h \ + gimpink-blob.c \ + gimpink-blob.h \ + gimpinkoptions.c \ + gimpinkoptions.h \ + gimpinkundo.c \ + gimpinkundo.h \ + gimpmybrushcore.c \ + gimpmybrushcore.h \ + gimpmybrushoptions.c \ + gimpmybrushoptions.h \ + gimpmybrushsurface.c \ + gimpmybrushsurface.h \ + gimppaintcore.c \ + gimppaintcore.h \ + gimppaintcore-loops.cc \ + gimppaintcore-loops.h \ + gimppaintcore-stroke.c \ + gimppaintcore-stroke.h \ + gimppaintcoreundo.c \ + gimppaintcoreundo.h \ + gimppaintoptions.c \ + gimppaintoptions.h \ + gimppencil.c \ + gimppencil.h \ + gimppenciloptions.c \ + gimppenciloptions.h \ + gimppaintbrush.c \ + gimppaintbrush.h \ + gimpperspectiveclone.c \ + gimpperspectiveclone.h \ + gimpperspectivecloneoptions.c \ + gimpperspectivecloneoptions.h \ + gimpsmudge.c \ + gimpsmudge.h \ + gimpsmudgeoptions.c \ + gimpsmudgeoptions.h \ + gimpsourcecore.c \ + gimpsourcecore.h \ + gimpsourceoptions.c \ + gimpsourceoptions.h + +libapppaint_a_built_sources = paint-enums.c +libapppaint_a_SOURCES = $(libapppaint_a_built_sources) $(libapppaint_a_sources) + +# +# rules to generate built sources +# +# setup autogeneration dependencies +gen_sources = xgen-pec +CLEANFILES = $(gen_sources) +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 app/paint/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu app/paint/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) + +libapppaint.a: $(libapppaint_a_OBJECTS) $(libapppaint_a_DEPENDENCIES) $(EXTRA_libapppaint_a_DEPENDENCIES) + $(AM_V_at)-rm -f libapppaint.a + $(AM_V_AR)$(libapppaint_a_AR) libapppaint.a $(libapppaint_a_OBJECTS) $(libapppaint_a_LIBADD) + $(AM_V_at)$(RANLIB) libapppaint.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-paint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpairbrush.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpairbrushoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushcore-loops.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushcore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpclone.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcloneoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconvolve.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconvolveoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdodgeburn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdodgeburnoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimperaser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimperaseroptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpheal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpink-blob.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpinkoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpinkundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmybrushcore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmybrushoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmybrushsurface.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintbrush.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintcore-loops.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintcore-stroke.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintcore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintcoreundo.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaintoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppencil.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppenciloptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpperspectiveclone.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpperspectivecloneoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsmudge.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsmudgeoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsourcecore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsourceoptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paint-enums.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 $(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: + -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 clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gimp-paint.Po + -rm -f ./$(DEPDIR)/gimpairbrush.Po + -rm -f ./$(DEPDIR)/gimpairbrushoptions.Po + -rm -f ./$(DEPDIR)/gimpbrushcore-loops.Po + -rm -f ./$(DEPDIR)/gimpbrushcore.Po + -rm -f ./$(DEPDIR)/gimpclone.Po + -rm -f ./$(DEPDIR)/gimpcloneoptions.Po + -rm -f ./$(DEPDIR)/gimpconvolve.Po + -rm -f ./$(DEPDIR)/gimpconvolveoptions.Po + -rm -f ./$(DEPDIR)/gimpdodgeburn.Po + -rm -f ./$(DEPDIR)/gimpdodgeburnoptions.Po + -rm -f ./$(DEPDIR)/gimperaser.Po + -rm -f ./$(DEPDIR)/gimperaseroptions.Po + -rm -f ./$(DEPDIR)/gimpheal.Po + -rm -f ./$(DEPDIR)/gimpink-blob.Po + -rm -f ./$(DEPDIR)/gimpink.Po + -rm -f ./$(DEPDIR)/gimpinkoptions.Po + -rm -f ./$(DEPDIR)/gimpinkundo.Po + -rm -f ./$(DEPDIR)/gimpmybrushcore.Po + -rm -f ./$(DEPDIR)/gimpmybrushoptions.Po + -rm -f ./$(DEPDIR)/gimpmybrushsurface.Po + -rm -f ./$(DEPDIR)/gimppaintbrush.Po + -rm -f ./$(DEPDIR)/gimppaintcore-loops.Po + -rm -f ./$(DEPDIR)/gimppaintcore-stroke.Po + -rm -f ./$(DEPDIR)/gimppaintcore.Po + -rm -f ./$(DEPDIR)/gimppaintcoreundo.Po + -rm -f ./$(DEPDIR)/gimppaintoptions.Po + -rm -f ./$(DEPDIR)/gimppencil.Po + -rm -f ./$(DEPDIR)/gimppenciloptions.Po + -rm -f ./$(DEPDIR)/gimpperspectiveclone.Po + -rm -f ./$(DEPDIR)/gimpperspectivecloneoptions.Po + -rm -f ./$(DEPDIR)/gimpsmudge.Po + -rm -f ./$(DEPDIR)/gimpsmudgeoptions.Po + -rm -f ./$(DEPDIR)/gimpsourcecore.Po + -rm -f ./$(DEPDIR)/gimpsourceoptions.Po + -rm -f ./$(DEPDIR)/paint-enums.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)/gimp-paint.Po + -rm -f ./$(DEPDIR)/gimpairbrush.Po + -rm -f ./$(DEPDIR)/gimpairbrushoptions.Po + -rm -f ./$(DEPDIR)/gimpbrushcore-loops.Po + -rm -f ./$(DEPDIR)/gimpbrushcore.Po + -rm -f ./$(DEPDIR)/gimpclone.Po + -rm -f ./$(DEPDIR)/gimpcloneoptions.Po + -rm -f ./$(DEPDIR)/gimpconvolve.Po + -rm -f ./$(DEPDIR)/gimpconvolveoptions.Po + -rm -f ./$(DEPDIR)/gimpdodgeburn.Po + -rm -f ./$(DEPDIR)/gimpdodgeburnoptions.Po + -rm -f ./$(DEPDIR)/gimperaser.Po + -rm -f ./$(DEPDIR)/gimperaseroptions.Po + -rm -f ./$(DEPDIR)/gimpheal.Po + -rm -f ./$(DEPDIR)/gimpink-blob.Po + -rm -f ./$(DEPDIR)/gimpink.Po + -rm -f ./$(DEPDIR)/gimpinkoptions.Po + -rm -f ./$(DEPDIR)/gimpinkundo.Po + -rm -f ./$(DEPDIR)/gimpmybrushcore.Po + -rm -f ./$(DEPDIR)/gimpmybrushoptions.Po + -rm -f ./$(DEPDIR)/gimpmybrushsurface.Po + -rm -f ./$(DEPDIR)/gimppaintbrush.Po + -rm -f ./$(DEPDIR)/gimppaintcore-loops.Po + -rm -f ./$(DEPDIR)/gimppaintcore-stroke.Po + -rm -f ./$(DEPDIR)/gimppaintcore.Po + -rm -f ./$(DEPDIR)/gimppaintcoreundo.Po + -rm -f ./$(DEPDIR)/gimppaintoptions.Po + -rm -f ./$(DEPDIR)/gimppencil.Po + -rm -f ./$(DEPDIR)/gimppenciloptions.Po + -rm -f ./$(DEPDIR)/gimpperspectiveclone.Po + -rm -f ./$(DEPDIR)/gimpperspectivecloneoptions.Po + -rm -f ./$(DEPDIR)/gimpsmudge.Po + -rm -f ./$(DEPDIR)/gimpsmudgeoptions.Po + -rm -f ./$(DEPDIR)/gimpsourcecore.Po + -rm -f ./$(DEPDIR)/gimpsourceoptions.Po + -rm -f ./$(DEPDIR)/paint-enums.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 + + +xgen-pec: $(srcdir)/paint-enums.h $(GIMP_MKENUMS) Makefile.am + $(AM_V_GEN) $(GIMP_MKENUMS) \ + --fhead "#include \"config.h\"\n#include \n#include \"libgimpbase/gimpbase.h\"\n#include \"paint-enums.h\"\n#include \"gimp-intl.h\"" \ + --fprod "\n/* enumerations from \"@basename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n" \ + --dhead " static const Gimp@Type@Desc descs[] =\n {" \ + --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \ + --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \ + $< > $@ + +# copy the generated enum file back to the source directory only if it's +# changed; otherwise, only update its timestamp, so that the recipe isn't +# executed again on the next build, however, allow this to (harmlessly) fail, +# to support building from a read-only source tree. +$(srcdir)/paint-enums.c: xgen-pec + $(AM_V_GEN) if ! cmp -s $< $@; then \ + cp $< $@; \ + else \ + touch $@ 2> /dev/null \ + || true; \ + 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/app/paint/gimp-paint.c b/app/paint/gimp-paint.c new file mode 100644 index 0000000..f3904a3 --- /dev/null +++ b/app/paint/gimp-paint.c @@ -0,0 +1,140 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "paint-types.h" + +#include "core/gimp.h" +#include "core/gimplist.h" +#include "core/gimppaintinfo.h" + +#include "gimp-paint.h" +#include "gimpairbrush.h" +#include "gimpclone.h" +#include "gimpconvolve.h" +#include "gimpdodgeburn.h" +#include "gimperaser.h" +#include "gimpheal.h" +#include "gimpink.h" +#include "gimpmybrushcore.h" +#include "gimppaintoptions.h" +#include "gimppaintbrush.h" +#include "gimppencil.h" +#include "gimpperspectiveclone.h" +#include "gimpsmudge.h" + + +/* local function prototypes */ + +static void gimp_paint_register (Gimp *gimp, + GType paint_type, + GType paint_options_type, + const gchar *identifier, + const gchar *blurb, + const gchar *icon_name); + + +/* public functions */ + +void +gimp_paint_init (Gimp *gimp) +{ + GimpPaintRegisterFunc register_funcs[] = + { + gimp_dodge_burn_register, + gimp_smudge_register, + gimp_convolve_register, + gimp_perspective_clone_register, + gimp_heal_register, + gimp_clone_register, + gimp_mybrush_core_register, + gimp_ink_register, + gimp_airbrush_register, + gimp_eraser_register, + gimp_paintbrush_register, + gimp_pencil_register + }; + + gint i; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp->paint_info_list = gimp_list_new (GIMP_TYPE_PAINT_INFO, FALSE); + gimp_object_set_static_name (GIMP_OBJECT (gimp->paint_info_list), + "paint infos"); + + gimp_container_freeze (gimp->paint_info_list); + + for (i = 0; i < G_N_ELEMENTS (register_funcs); i++) + { + register_funcs[i] (gimp, gimp_paint_register); + } + + gimp_container_thaw (gimp->paint_info_list); +} + +void +gimp_paint_exit (Gimp *gimp) +{ + g_return_if_fail (GIMP_IS_GIMP (gimp)); + + gimp_paint_info_set_standard (gimp, NULL); + + if (gimp->paint_info_list) + { + gimp_container_foreach (gimp->paint_info_list, + (GFunc) g_object_run_dispose, NULL); + g_clear_object (&gimp->paint_info_list); + } +} + + +/* private functions */ + +static void +gimp_paint_register (Gimp *gimp, + GType paint_type, + GType paint_options_type, + const gchar *identifier, + const gchar *blurb, + const gchar *icon_name) +{ + GimpPaintInfo *paint_info; + + g_return_if_fail (GIMP_IS_GIMP (gimp)); + g_return_if_fail (g_type_is_a (paint_type, GIMP_TYPE_PAINT_CORE)); + g_return_if_fail (g_type_is_a (paint_options_type, GIMP_TYPE_PAINT_OPTIONS)); + g_return_if_fail (identifier != NULL); + g_return_if_fail (blurb != NULL); + + paint_info = gimp_paint_info_new (gimp, + paint_type, + paint_options_type, + identifier, + blurb, + icon_name); + + gimp_container_add (gimp->paint_info_list, GIMP_OBJECT (paint_info)); + g_object_unref (paint_info); + + if (paint_type == GIMP_TYPE_PAINTBRUSH) + gimp_paint_info_set_standard (gimp, paint_info); +} diff --git a/app/paint/gimp-paint.h b/app/paint/gimp-paint.h new file mode 100644 index 0000000..97dee10 --- /dev/null +++ b/app/paint/gimp-paint.h @@ -0,0 +1,26 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_PAINT_H__ +#define __GIMP_PAINT_H__ + + +void gimp_paint_init (Gimp *gimp); +void gimp_paint_exit (Gimp *gimp); + + +#endif /* __GIMP_PAINT_H__ */ diff --git a/app/paint/gimpairbrush.c b/app/paint/gimpairbrush.c new file mode 100644 index 0000000..b4bf5b4 --- /dev/null +++ b/app/paint/gimpairbrush.c @@ -0,0 +1,266 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "paint-types.h" + +#include "core/gimp.h" +#include "core/gimpbrush.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimpsymmetry.h" + +#include "gimpairbrush.h" +#include "gimpairbrushoptions.h" + +#include "gimp-intl.h" + + +#define STAMP_MAX_FPS 60 + + +enum +{ + STAMP, + LAST_SIGNAL +}; + + +static void gimp_airbrush_finalize (GObject *object); + +static void gimp_airbrush_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); +static void gimp_airbrush_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym); + +static gboolean gimp_airbrush_timeout (gpointer data); + + +G_DEFINE_TYPE (GimpAirbrush, gimp_airbrush, GIMP_TYPE_PAINTBRUSH) + +#define parent_class gimp_airbrush_parent_class + +static guint airbrush_signals[LAST_SIGNAL] = { 0 }; + + +void +gimp_airbrush_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_AIRBRUSH, + GIMP_TYPE_AIRBRUSH_OPTIONS, + "gimp-airbrush", + _("Airbrush"), + "gimp-tool-airbrush"); +} + +static void +gimp_airbrush_class_init (GimpAirbrushClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + + object_class->finalize = gimp_airbrush_finalize; + + paint_core_class->paint = gimp_airbrush_paint; + + airbrush_signals[STAMP] = + g_signal_new ("stamp", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpAirbrushClass, stamp), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gimp_airbrush_init (GimpAirbrush *airbrush) +{ +} + +static void +gimp_airbrush_finalize (GObject *object) +{ + GimpAirbrush *airbrush = GIMP_AIRBRUSH (object); + + if (airbrush->timeout_id) + { + g_source_remove (airbrush->timeout_id); + airbrush->timeout_id = 0; + } + + g_clear_object (&airbrush->sym); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_airbrush_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + GimpAirbrush *airbrush = GIMP_AIRBRUSH (paint_core); + GimpAirbrushOptions *options = GIMP_AIRBRUSH_OPTIONS (paint_options); + GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; + + if (airbrush->timeout_id) + { + g_source_remove (airbrush->timeout_id); + airbrush->timeout_id = 0; + } + + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + GIMP_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawable, + paint_options, + sym, + paint_state, time); + break; + + case GIMP_PAINT_STATE_MOTION: + gimp_airbrush_motion (paint_core, drawable, paint_options, sym); + + if ((options->rate != 0.0) && ! options->motion_only) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble fade_point; + gdouble dynamic_rate; + gint timeout; + GimpCoords *coords; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + airbrush->drawable = drawable; + airbrush->paint_options = paint_options; + + if (airbrush->sym) + g_object_unref (airbrush->sym); + airbrush->sym = g_object_ref (sym); + + /* Base our timeout on the original stroke. */ + coords = gimp_symmetry_get_origin (sym); + + airbrush->coords = *coords; + + dynamic_rate = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_RATE, + coords, + paint_options, + fade_point); + + timeout = (1000.0 / STAMP_MAX_FPS) / + ((options->rate / 100.0) * dynamic_rate); + + airbrush->timeout_id = g_timeout_add_full (G_PRIORITY_HIGH, + timeout, + gimp_airbrush_timeout, + airbrush, NULL); + } + break; + + case GIMP_PAINT_STATE_FINISH: + GIMP_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawable, + paint_options, + sym, + paint_state, time); + + g_clear_object (&airbrush->sym); + break; + } +} + +static void +gimp_airbrush_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym) + +{ + GimpAirbrushOptions *options = GIMP_AIRBRUSH_OPTIONS (paint_options); + GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble opacity; + gdouble fade_point; + GimpCoords *coords; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + coords = gimp_symmetry_get_origin (sym); + + opacity = (options->flow / 100.0 * + gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_FLOW, + coords, + paint_options, + fade_point)); + + _gimp_paintbrush_motion (paint_core, drawable, paint_options, + sym, opacity); +} + +static gboolean +gimp_airbrush_timeout (gpointer data) +{ + GimpAirbrush *airbrush = GIMP_AIRBRUSH (data); + + airbrush->timeout_id = 0; + + g_signal_emit (airbrush, airbrush_signals[STAMP], 0); + + return G_SOURCE_REMOVE; +} + + +/* public functions */ + + +void +gimp_airbrush_stamp (GimpAirbrush *airbrush) +{ + g_return_if_fail (GIMP_IS_AIRBRUSH (airbrush)); + + gimp_symmetry_set_origin (airbrush->sym, + airbrush->drawable, &airbrush->coords); + + gimp_airbrush_paint (GIMP_PAINT_CORE (airbrush), + airbrush->drawable, + airbrush->paint_options, + airbrush->sym, + GIMP_PAINT_STATE_MOTION, 0); + + gimp_symmetry_clear_origin (airbrush->sym); +} diff --git a/app/paint/gimpairbrush.h b/app/paint/gimpairbrush.h new file mode 100644 index 0000000..23b2b85 --- /dev/null +++ b/app/paint/gimpairbrush.h @@ -0,0 +1,64 @@ +/* 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 . + */ + +#ifndef __GIMP_AIRBRUSH_H__ +#define __GIMP_AIRBRUSH_H__ + + +#include "gimppaintbrush.h" + + +#define GIMP_TYPE_AIRBRUSH (gimp_airbrush_get_type ()) +#define GIMP_AIRBRUSH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_AIRBRUSH, GimpAirbrush)) +#define GIMP_AIRBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_AIRBRUSH, GimpAirbrushClass)) +#define GIMP_IS_AIRBRUSH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_AIRBRUSH)) +#define GIMP_IS_AIRBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_AIRBRUSH)) +#define GIMP_AIRBRUSH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_AIRBRUSH, GimpAirbrushClass)) + + +typedef struct _GimpAirbrushClass GimpAirbrushClass; + +struct _GimpAirbrush +{ + GimpPaintbrush parent_instance; + + guint timeout_id; + + GimpSymmetry *sym; + GimpDrawable *drawable; + GimpPaintOptions *paint_options; + GimpCoords coords; +}; + +struct _GimpAirbrushClass +{ + GimpPaintbrushClass parent_class; + + /* signals */ + void (* stamp) (GimpAirbrush *airbrush); +}; + + +void gimp_airbrush_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_airbrush_get_type (void) G_GNUC_CONST; + +void gimp_airbrush_stamp (GimpAirbrush *airbrush); + + +#endif /* __GIMP_AIRBRUSH_H__ */ diff --git a/app/paint/gimpairbrushoptions.c b/app/paint/gimpairbrushoptions.c new file mode 100644 index 0000000..37c7e78 --- /dev/null +++ b/app/paint/gimpairbrushoptions.c @@ -0,0 +1,155 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "gimpairbrushoptions.h" + +#include "gimp-intl.h" + + +#define AIRBRUSH_DEFAULT_RATE 50.0 +#define AIRBRUSH_DEFAULT_FLOW 10.0 +#define AIRBRUSH_DEFAULT_MOTION_ONLY FALSE + +enum +{ + PROP_0, + PROP_RATE, + PROP_MOTION_ONLY, + PROP_FLOW, + PROP_PRESSURE /*for backwards copatibility of tool options*/ +}; + + +static void gimp_airbrush_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_airbrush_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpAirbrushOptions, gimp_airbrush_options, + GIMP_TYPE_PAINT_OPTIONS) + + +static void +gimp_airbrush_options_class_init (GimpAirbrushOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_airbrush_options_set_property; + object_class->get_property = gimp_airbrush_options_get_property; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RATE, + "rate", + C_("airbrush-tool", "Rate"), + NULL, + 0.0, 100.0, AIRBRUSH_DEFAULT_RATE, + GIMP_PARAM_STATIC_STRINGS); + + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MOTION_ONLY, + "motion-only", + _("Motion only"), + NULL, + AIRBRUSH_DEFAULT_MOTION_ONLY, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FLOW, + "flow", + _("Flow"), + NULL, + 0.0, 100.0, AIRBRUSH_DEFAULT_FLOW, + GIMP_PARAM_STATIC_STRINGS); + + /* backwads-compadibility prop for flow fomerly known as pressure */ + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PRESSURE, + "pressure", + NULL, NULL, + 0.0, 100.0, AIRBRUSH_DEFAULT_FLOW, + GIMP_CONFIG_PARAM_IGNORE); +} + +static void +gimp_airbrush_options_init (GimpAirbrushOptions *options) +{ +} + +static void +gimp_airbrush_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpAirbrushOptions *options = GIMP_AIRBRUSH_OPTIONS (object); + + switch (property_id) + { + case PROP_RATE: + options->rate = g_value_get_double (value); + break; + case PROP_MOTION_ONLY: + options->motion_only = g_value_get_boolean (value); + break; + case PROP_PRESSURE: + case PROP_FLOW: + options->flow = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_airbrush_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpAirbrushOptions *options = GIMP_AIRBRUSH_OPTIONS (object); + + switch (property_id) + { + case PROP_RATE: + g_value_set_double (value, options->rate); + break; + case PROP_MOTION_ONLY: + g_value_set_boolean (value, options->motion_only); + break; + case PROP_PRESSURE: + case PROP_FLOW: + g_value_set_double (value, options->flow); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimpairbrushoptions.h b/app/paint/gimpairbrushoptions.h new file mode 100644 index 0000000..d7d8bcd --- /dev/null +++ b/app/paint/gimpairbrushoptions.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __GIMP_AIRBRUSH_OPTIONS_H__ +#define __GIMP_AIRBRUSH_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_AIRBRUSH_OPTIONS (gimp_airbrush_options_get_type ()) +#define GIMP_AIRBRUSH_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_AIRBRUSH_OPTIONS, GimpAirbrushOptions)) +#define GIMP_AIRBRUSH_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_AIRBRUSH_OPTIONS, GimpAirbrushOptionsClass)) +#define GIMP_IS_AIRBRUSH_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_AIRBRUSH_OPTIONS)) +#define GIMP_IS_AIRBRUSH_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_AIRBRUSH_OPTIONS)) +#define GIMP_AIRBRUSH_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_AIRBRUSH_OPTIONS, GimpAirbrushOptionsClass)) + + +typedef struct _GimpAirbrushOptionsClass GimpAirbrushOptionsClass; + +struct _GimpAirbrushOptions +{ + GimpPaintOptions parent_instance; + + gdouble rate; + gboolean motion_only; + gdouble flow; +}; + +struct _GimpAirbrushOptionsClass +{ + GimpPaintOptionsClass parent_class; +}; + + +GType gimp_airbrush_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_AIRBRUSH_OPTIONS_H__ */ diff --git a/app/paint/gimpbrushcore-kernels.h b/app/paint/gimpbrushcore-kernels.h new file mode 100644 index 0000000..ea5eb45 --- /dev/null +++ b/app/paint/gimpbrushcore-kernels.h @@ -0,0 +1,116 @@ +/* gimpbrushcore-kernels.h + * + * This file was generated using kernelgen as found in the tools dir. + * (threshold = 0.25) + */ + +#ifndef __GIMP_BRUSH_CORE_KERNELS_H__ +#define __GIMP_BRUSH_CORE_KERNELS_H__ + + +#define KERNEL_WIDTH 3 +#define KERNEL_HEIGHT 3 +#define KERNEL_SUBSAMPLE 4 + + +#ifdef __cplusplus + +template +struct Kernel; + +template <> +struct Kernel +{ + using value_type = guchar; + using kernel_type = guint; + using accum_type = gulong; + + static constexpr kernel_type + coeff (kernel_type x) + { + return x; + } + + static constexpr value_type + round (accum_type x) + { + return (x + 128) / 256; + } +}; + +template <> +struct Kernel +{ + using value_type = gfloat; + using kernel_type = gfloat; + using accum_type = gfloat; + + static constexpr kernel_type + coeff (kernel_type x) + { + return x / 256.0f; + } + + static constexpr value_type + round (accum_type x) + { + return x; + } +}; + + +/* Brush pixel subsampling kernels */ +template +struct Subsample : Kernel +{ + #define C(x) (Subsample::coeff (x)) + + static constexpr typename Subsample::kernel_type kernel[5][5][9] = + { + { + { C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), C( 0), C( 0), C( 0), }, + { C( 25), C(103), C( 0), C( 25), C(103), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C(128), C( 0), C( 0), C(128), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C(103), C( 25), C( 0), C(103), C( 25), C( 0), C( 0), C( 0), }, + { C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), C( 0), C( 0), } + }, + { + { C( 25), C( 25), C( 0), C(103), C(103), C( 0), C( 0), C( 0), C( 0), }, + { C( 6), C( 44), C( 0), C( 44), C(162), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C( 50), C( 0), C( 0), C(206), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C( 44), C( 6), C( 0), C(162), C( 44), C( 0), C( 0), C( 0), }, + { C( 0), C( 25), C( 25), C( 0), C(103), C(103), C( 0), C( 0), C( 0), } + }, + { + { C( 0), C( 0), C( 0), C(128), C(128), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C( 0), C( 0), C( 50), C(206), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(256), C( 0), C( 0), C( 0), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(206), C( 50), C( 0), C( 0), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(128), C(128), C( 0), C( 0), C( 0), } + }, + { + { C( 0), C( 0), C( 0), C(103), C(103), C( 0), C( 25), C( 25), C( 0), }, + { C( 0), C( 0), C( 0), C( 44), C(162), C( 0), C( 6), C( 44), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(206), C( 0), C( 0), C( 50), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(162), C( 44), C( 0), C( 44), C( 6), }, + { C( 0), C( 0), C( 0), C( 0), C(103), C(103), C( 0), C( 25), C( 25), } + }, + { + { C( 0), C( 0), C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), C( 0), }, + { C( 0), C( 0), C( 0), C( 25), C(103), C( 0), C( 25), C(103), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(128), C( 0), C( 0), C(128), C( 0), }, + { C( 0), C( 0), C( 0), C( 0), C(103), C( 25), C( 0), C(103), C( 25), }, + { C( 0), C( 0), C( 0), C( 0), C( 64), C( 64), C( 0), C( 64), C( 64), } + } + }; + + #undef C +}; + +template +constexpr typename Subsample::kernel_type Subsample::kernel[5][5][9]; + +#endif /* __cplusplus */ + + +#endif /* __GIMP_BRUSH_CORE_KERNELS_H__ */ diff --git a/app/paint/gimpbrushcore-loops.cc b/app/paint/gimpbrushcore-loops.cc new file mode 100644 index 0000000..31b4afb --- /dev/null +++ b/app/paint/gimpbrushcore-loops.cc @@ -0,0 +1,647 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +extern "C" +{ + +#include "paint-types.h" + +#include "core/gimptempbuf.h" + +#include "gimpbrushcore.h" +#include "gimpbrushcore-loops.h" + +} /* extern "C" */ + +#include "gimpbrushcore-kernels.h" + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + +#define EPSILON 1e-6 + + +static void +clear_edges (GimpTempBuf *buf, + gint top, + gint bottom, + gint left, + gint right) +{ + guchar *data; + const Babl *format = gimp_temp_buf_get_format (buf); + gint bpp = babl_format_get_bytes_per_pixel (format); + gint width = gimp_temp_buf_get_width (buf); + gint height = gimp_temp_buf_get_height (buf); + + if (top + bottom >= height || left + right >= width) + { + gimp_temp_buf_data_clear (buf); + + return; + } + + data = gimp_temp_buf_get_data (buf); + + memset (data, 0, (top * width + left) * bpp); + + if (left + right) + { + gint stride = width * bpp; + gint size = (left + right) * bpp; + gint y; + + data = gimp_temp_buf_get_data (buf) + + ((top + 1) * width - right) * bpp; + + for (y = top; y < height - bottom - 1; y++) + { + memset (data, 0, size); + + data += stride; + } + } + + data = gimp_temp_buf_get_data (buf) + + ((height - bottom) * width - right) * bpp; + + memset (data, 0, (bottom * width + right) * bpp); +} + +template +static inline void +rotate_pointers (T **p, + gint n) +{ + T *tmp; + gint i; + + tmp = p[0]; + + for (i = 0; i < n - 1; i++) + p[i] = p[i + 1]; + + p[i] = tmp; +} + +template +static void +gimp_brush_core_subsample_mask_impl (const GimpTempBuf *mask, + GimpTempBuf *dest, + gint dest_offset_x, + gint dest_offset_y, + gint index1, + gint index2) +{ + using value_type = typename Subsample::value_type; + using kernel_type = typename Subsample::kernel_type; + using accum_type = typename Subsample::accum_type; + + Subsample subsample; + const kernel_type *kernel = subsample.kernel[index2][index1]; + gint mask_width = gimp_temp_buf_get_width (mask); + gint mask_height = gimp_temp_buf_get_height (mask); + gint dest_width = gimp_temp_buf_get_width (dest); + gint dest_height = gimp_temp_buf_get_height (dest); + + gegl_parallel_distribute_range ( + mask_height, PIXELS_PER_THREAD / mask_width, + [=] (gint y, gint height) + { + const value_type *m; + value_type *d; + const kernel_type *k; + gint y0; + gint i, j; + gint r, s; + gint offs; + accum_type *accum[KERNEL_HEIGHT]; + + /* Allocate and initialize the accum buffer */ + for (i = 0; i < KERNEL_HEIGHT ; i++) + accum[i] = gegl_scratch_new0 (accum_type, dest_width + 1); + + y0 = MAX (y - (KERNEL_HEIGHT - 1), 0); + + m = (const value_type *) gimp_temp_buf_get_data (mask) + + y0 * mask_width; + + for (i = y0; i < y; i++) + { + for (j = 0; j < mask_width; j++) + { + k = kernel + KERNEL_WIDTH * (y - i); + for (r = y - i; r < KERNEL_HEIGHT; r++) + { + offs = j + dest_offset_x; + s = KERNEL_WIDTH; + while (s--) + accum[r][offs++] += *m * *k++; + } + m++; + } + + rotate_pointers (accum, KERNEL_HEIGHT); + } + + for (i = y; i < y + height; i++) + { + for (j = 0; j < mask_width; j++) + { + k = kernel; + for (r = 0; r < KERNEL_HEIGHT; r++) + { + offs = j + dest_offset_x; + s = KERNEL_WIDTH; + while (s--) + accum[r][offs++] += *m * *k++; + } + m++; + } + + /* store the accum buffer into the destination mask */ + d = (value_type *) gimp_temp_buf_get_data (dest) + + (i + dest_offset_y) * dest_width; + for (j = 0; j < dest_width; j++) + *d++ = subsample.round (accum[0][j]); + + rotate_pointers (accum, KERNEL_HEIGHT); + + memset (accum[KERNEL_HEIGHT - 1], 0, + sizeof (accum_type) * dest_width); + } + + if (y + height == mask_height) + { + /* store the rest of the accum buffer into the dest mask */ + while (i + dest_offset_y < dest_height) + { + d = (value_type *) gimp_temp_buf_get_data (dest) + + (i + dest_offset_y) * dest_width; + for (j = 0; j < dest_width; j++) + *d++ = subsample.round (accum[0][j]); + + rotate_pointers (accum, KERNEL_HEIGHT); + i++; + } + } + + for (i = KERNEL_HEIGHT - 1; i >= 0; i--) + gegl_scratch_free (accum[i]); + }); +} + +const GimpTempBuf * +gimp_brush_core_subsample_mask (GimpBrushCore *core, + const GimpTempBuf *mask, + gdouble x, + gdouble y) +{ + GimpTempBuf *dest; + const Babl *mask_format; + gdouble left; + gint index1; + gint index2; + gint dest_offset_x = 0; + gint dest_offset_y = 0; + gint mask_width = gimp_temp_buf_get_width (mask); + gint mask_height = gimp_temp_buf_get_height (mask); + + left = x - floor (x); + index1 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1)); + + left = y - floor (y); + index2 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1)); + + if ((mask_width % 2) == 0) + { + index1 += KERNEL_SUBSAMPLE >> 1; + + if (index1 > KERNEL_SUBSAMPLE) + { + index1 -= KERNEL_SUBSAMPLE + 1; + dest_offset_x = 1; + } + } + + if ((mask_height % 2) == 0) + { + index2 += KERNEL_SUBSAMPLE >> 1; + + if (index2 > KERNEL_SUBSAMPLE) + { + index2 -= KERNEL_SUBSAMPLE + 1; + dest_offset_y = 1; + } + } + + if (mask == core->last_subsample_brush_mask && + ! core->subsample_cache_invalid) + { + if (core->subsample_brushes[index2][index1]) + return core->subsample_brushes[index2][index1]; + } + else + { + gint i, j; + + for (i = 0; i < KERNEL_SUBSAMPLE + 1; i++) + for (j = 0; j < KERNEL_SUBSAMPLE + 1; j++) + g_clear_pointer (&core->subsample_brushes[i][j], gimp_temp_buf_unref); + + core->last_subsample_brush_mask = mask; + core->subsample_cache_invalid = FALSE; + } + + mask_format = gimp_temp_buf_get_format (mask); + + dest = gimp_temp_buf_new (mask_width + 2, + mask_height + 2, + mask_format); + clear_edges (dest, dest_offset_y, 0, 0, 0); + + core->subsample_brushes[index2][index1] = dest; + + if (mask_format == babl_format ("Y u8")) + { + gimp_brush_core_subsample_mask_impl (mask, dest, + dest_offset_x, dest_offset_y, + index1, index2); + } + else if (mask_format == babl_format ("Y float")) + { + gimp_brush_core_subsample_mask_impl (mask, dest, + dest_offset_x, dest_offset_y, + index1, index2); + } + else + { + g_warn_if_reached (); + } + + return dest; +} + +/* The simple pressure profile + * + * It is: I'(I) = MIN (2 * pressure * I, 1) + */ +class SimplePressure +{ + gfloat scale; + +public: + SimplePressure (gdouble pressure) + { + scale = 2.0 * pressure; + } + + guchar + operator () (guchar x) const + { + gint v = RINT (scale * x); + + return MIN (v, 255); + } + + gfloat + operator () (gfloat x) const + { + gfloat v = scale * x; + + return MIN (v, 1.0f); + } + + template + T + operator () (T x) const = delete; +}; + +/* The fancy pressure profile + * + * It is: I'(I) = tanh (20 * (pressure - 0.5) * I) : pressure > 0.5 + * I'(I) = 1 - tanh (20 * (0.5 - pressure) * (1 - I)) : pressure < 0.5 + * + * It looks like: + * + * low pressure medium pressure high pressure + * + * | / -- + * | / / + * / / | + * -- / | + */ +class FancyPressure +{ + gfloat map[257]; + +public: + FancyPressure (gdouble pressure) + { + gdouble ds, s, c; + gint i; + + ds = (pressure - 0.5) * (20.0 / 256.0); + s = 0; + c = 1.0; + + if (ds > 0) + { + for (i = 0; i < 256; i++) + { + map[i] = s / c; + s += c * ds; + c += s * ds; + } + + for (i = 0; i < 256; i++) + map[i] = map[i] / map[255]; + } + else + { + ds = -ds; + + for (i = 255; i >= 0; i--) + { + map[i] = s / c; + s += c * ds; + c += s * ds; + } + + for (i = 255; i >= 0; i--) + map[i] = 1.0f - map[i] / map[0]; + } + + map[256] = map[255]; + } + + guchar + operator () (guchar x) const + { + return RINT (255.0f * map[x]); + } + + gfloat + operator () (gfloat x) const + { + gint i; + gfloat f; + + x *= 255.0f; + + i = floorf (x); + f = x - i; + + return map[i] + (map[i + 1] - map[i]) * f; + } + + template + T + operator () (T x) const = delete; +}; + +template +class CachedPressure +{ + T map[T (~0) + 1]; + +public: + template + CachedPressure (Pressure pressure) + { + gint i; + + for (i = 0; i < (gint) G_N_ELEMENTS (map); i++) + map[i] = pressure (T (i)); + } + + T + operator () (T x) const + { + return map[x]; + } + + template + U + operator () (U x) const = delete; +}; + +template +void +gimp_brush_core_pressurize_mask_impl (const GimpTempBuf *mask, + GimpTempBuf *dest, + Pressure pressure) +{ + gegl_parallel_distribute_range ( + gimp_temp_buf_get_width (mask) * gimp_temp_buf_get_height (mask), + PIXELS_PER_THREAD, + [=] (gint offset, gint size) + { + const T *m; + T *d; + gint i; + + m = (const T *) gimp_temp_buf_get_data (mask) + offset; + d = ( T *) gimp_temp_buf_get_data (dest) + offset; + + for (i = 0; i < size; i++) + *d++ = pressure (*m++); + }); +} + +/* #define FANCY_PRESSURE */ + +const GimpTempBuf * +gimp_brush_core_pressurize_mask (GimpBrushCore *core, + const GimpTempBuf *brush_mask, + gdouble x, + gdouble y, + gdouble pressure) +{ + const GimpTempBuf *subsample_mask; + const Babl *subsample_mask_format; + + /* Get the raw subsampled mask */ + subsample_mask = gimp_brush_core_subsample_mask (core, + brush_mask, + x, y); + + /* Special case pressure = 0.5 */ + if (fabs (pressure - 0.5) <= EPSILON) + return subsample_mask; + + g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref); + + subsample_mask_format = gimp_temp_buf_get_format (subsample_mask); + + core->pressure_brush = + gimp_temp_buf_new (gimp_temp_buf_get_width (brush_mask) + 2, + gimp_temp_buf_get_height (brush_mask) + 2, + subsample_mask_format); + +#ifdef FANCY_PRESSURE + using Pressure = FancyPressure; +#else + using Pressure = SimplePressure; +#endif + + if (subsample_mask_format == babl_format ("Y u8")) + { + gimp_brush_core_pressurize_mask_impl (subsample_mask, + core->pressure_brush, + CachedPressure ( + Pressure (pressure))); + } + else if (subsample_mask_format == babl_format ("Y float")) + { + gimp_brush_core_pressurize_mask_impl (subsample_mask, + core->pressure_brush, + Pressure (pressure)); + } + else + { + g_warn_if_reached (); + } + + return core->pressure_brush; +} + +template +static void +gimp_brush_core_solidify_mask_impl (const GimpTempBuf *mask, + GimpTempBuf *dest, + gint dest_offset_x, + gint dest_offset_y) +{ + gint mask_width = gimp_temp_buf_get_width (mask); + gint mask_height = gimp_temp_buf_get_height (mask); + gint dest_width = gimp_temp_buf_get_width (dest); + + gegl_parallel_distribute_area ( + GEGL_RECTANGLE (0, 0, mask_width, mask_height), + PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) + { + const T *m; + gfloat *d; + gint i, j; + + m = (const T *) gimp_temp_buf_get_data (mask) + + area->y * mask_width + area->x; + d = ((gfloat *) gimp_temp_buf_get_data (dest) + + ((dest_offset_y + 1 + area->y) * dest_width + + (dest_offset_x + 1 + area->x))); + + for (i = 0; i < area->height; i++) + { + for (j = 0; j < area->width; j++) + *d++ = (*m++) ? 1.0 : 0.0; + + m += mask_width - area->width; + d += dest_width - area->width; + } + }); +} + +const GimpTempBuf * +gimp_brush_core_solidify_mask (GimpBrushCore *core, + const GimpTempBuf *brush_mask, + gdouble x, + gdouble y) +{ + GimpTempBuf *dest; + const Babl *brush_mask_format; + gint dest_offset_x = 0; + gint dest_offset_y = 0; + gint brush_mask_width = gimp_temp_buf_get_width (brush_mask); + gint brush_mask_height = gimp_temp_buf_get_height (brush_mask); + + if ((brush_mask_width % 2) == 0) + { + if (x < 0.0) + x = fmod (x, brush_mask_width) + brush_mask_width; + + if ((x - floor (x)) >= 0.5) + dest_offset_x++; + } + + if ((brush_mask_height % 2) == 0) + { + if (y < 0.0) + y = fmod (y, brush_mask_height) + brush_mask_height; + + if ((y - floor (y)) >= 0.5) + dest_offset_y++; + } + + if (! core->solid_cache_invalid && + brush_mask == core->last_solid_brush_mask) + { + if (core->solid_brushes[dest_offset_y][dest_offset_x]) + return core->solid_brushes[dest_offset_y][dest_offset_x]; + } + else + { + gint i, j; + + for (i = 0; i < BRUSH_CORE_SOLID_SUBSAMPLE; i++) + for (j = 0; j < BRUSH_CORE_SOLID_SUBSAMPLE; j++) + g_clear_pointer (&core->solid_brushes[i][j], gimp_temp_buf_unref); + + core->last_solid_brush_mask = brush_mask; + core->solid_cache_invalid = FALSE; + } + + brush_mask_format = gimp_temp_buf_get_format (brush_mask); + + dest = gimp_temp_buf_new (brush_mask_width + 2, + brush_mask_height + 2, + babl_format ("Y float")); + clear_edges (dest, + 1 + dest_offset_y, 1 - dest_offset_y, + 1 + dest_offset_x, 1 - dest_offset_x); + + core->solid_brushes[dest_offset_y][dest_offset_x] = dest; + + if (brush_mask_format == babl_format ("Y u8")) + { + gimp_brush_core_solidify_mask_impl (brush_mask, dest, + dest_offset_x, dest_offset_y); + } + else if (brush_mask_format == babl_format ("Y float")) + { + gimp_brush_core_solidify_mask_impl (brush_mask, dest, + dest_offset_x, dest_offset_y); + } + else + { + g_warn_if_reached (); + } + + return dest; +} diff --git a/app/paint/gimpbrushcore-loops.h b/app/paint/gimpbrushcore-loops.h new file mode 100644 index 0000000..6fde397 --- /dev/null +++ b/app/paint/gimpbrushcore-loops.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#ifndef __GIMP_BRUSH_CORE_LOOPS_H__ +#define __GIMP_BRUSH_CORE_LOOPS_H__ + + +const GimpTempBuf * gimp_brush_core_subsample_mask (GimpBrushCore *core, + const GimpTempBuf *mask, + gdouble x, + gdouble y); +const GimpTempBuf * gimp_brush_core_pressurize_mask (GimpBrushCore *core, + const GimpTempBuf *brush_mask, + gdouble x, + gdouble y, + gdouble pressure); +const GimpTempBuf * gimp_brush_core_solidify_mask (GimpBrushCore *core, + const GimpTempBuf *brush_mask, + gdouble x, + gdouble y); + + +#endif /* __GIMP_BRUSH_CORE_LOOPS_H__ */ diff --git a/app/paint/gimpbrushcore.c b/app/paint/gimpbrushcore.c new file mode 100644 index 0000000..da4f26b --- /dev/null +++ b/app/paint/gimpbrushcore.c @@ -0,0 +1,1344 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "paint-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "core/gimpbrush-header.h" +#include "core/gimpbrushgenerated.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpdynamicsoutput.h" +#include "core/gimperror.h" +#include "core/gimpimage.h" +#include "core/gimpmarshal.h" +#include "core/gimpsymmetry.h" +#include "core/gimptempbuf.h" + +#include "gimpbrushcore.h" +#include "gimpbrushcore-loops.h" +#include "gimpbrushcore-kernels.h" + +#include "gimppaintoptions.h" + +#include "gimp-intl.h" + + +#define EPSILON 0.00001 + +enum +{ + SET_BRUSH, + SET_DYNAMICS, + LAST_SIGNAL +}; + + +/* local function prototypes */ + +static void gimp_brush_core_finalize (GObject *object); + +static gboolean gimp_brush_core_start (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error); +static gboolean gimp_brush_core_pre_paint (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time); +static void gimp_brush_core_post_paint (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time); +static void gimp_brush_core_interpolate (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + guint32 time); + +static GeglBuffer * gimp_brush_core_get_paint_buffer(GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height); + +static void gimp_brush_core_real_set_brush (GimpBrushCore *core, + GimpBrush *brush); +static void gimp_brush_core_real_set_dynamics (GimpBrushCore *core, + GimpDynamics *dynamics); + +static gdouble gimp_brush_core_get_angle (GimpBrushCore *core); +static gboolean gimp_brush_core_get_reflect (GimpBrushCore *core); + +static const GimpTempBuf * + gimp_brush_core_transform_mask (GimpBrushCore *core, + GimpBrush *brush); + +static void gimp_brush_core_invalidate_cache (GimpBrush *brush, + GimpBrushCore *core); + + +G_DEFINE_TYPE (GimpBrushCore, gimp_brush_core, GIMP_TYPE_PAINT_CORE) + +#define parent_class gimp_brush_core_parent_class + +static guint core_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_brush_core_class_init (GimpBrushCoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + + core_signals[SET_BRUSH] = + g_signal_new ("set-brush", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpBrushCoreClass, set_brush), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_BRUSH); + + core_signals[SET_DYNAMICS] = + g_signal_new ("set-dynamics", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpBrushCoreClass, set_dynamics), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_DYNAMICS); + + object_class->finalize = gimp_brush_core_finalize; + + paint_core_class->start = gimp_brush_core_start; + paint_core_class->pre_paint = gimp_brush_core_pre_paint; + paint_core_class->post_paint = gimp_brush_core_post_paint; + paint_core_class->interpolate = gimp_brush_core_interpolate; + paint_core_class->get_paint_buffer = gimp_brush_core_get_paint_buffer; + + klass->handles_changing_brush = FALSE; + klass->handles_transforming_brush = TRUE; + klass->handles_dynamic_transforming_brush = TRUE; + + klass->set_brush = gimp_brush_core_real_set_brush; + klass->set_dynamics = gimp_brush_core_real_set_dynamics; +} + +static void +gimp_brush_core_init (GimpBrushCore *core) +{ + gint i, j; + + core->main_brush = NULL; + core->brush = NULL; + core->dynamics = NULL; + core->spacing = 1.0; + core->scale = 1.0; + core->angle = 0.0; + core->reflect = FALSE; + core->hardness = 1.0; + core->aspect_ratio = 0.0; + + core->symmetry_angle = 0.0; + core->symmetry_reflect = FALSE; + + core->pressure_brush = NULL; + + core->last_solid_brush_mask = NULL; + core->solid_cache_invalid = FALSE; + + core->transform_brush = NULL; + core->transform_pixmap = NULL; + + core->last_subsample_brush_mask = NULL; + core->subsample_cache_invalid = FALSE; + + core->rand = g_rand_new (); + + for (i = 0; i < BRUSH_CORE_SOLID_SUBSAMPLE; i++) + { + for (j = 0; j < BRUSH_CORE_SOLID_SUBSAMPLE; j++) + { + core->solid_brushes[i][j] = NULL; + } + } + + for (i = 0; i < BRUSH_CORE_JITTER_LUTSIZE - 1; ++i) + { + core->jitter_lut_y[i] = cos (gimp_deg_to_rad (i * 360 / + BRUSH_CORE_JITTER_LUTSIZE)); + core->jitter_lut_x[i] = sin (gimp_deg_to_rad (i * 360 / + BRUSH_CORE_JITTER_LUTSIZE)); + } + + gimp_assert (BRUSH_CORE_SUBSAMPLE == KERNEL_SUBSAMPLE); + + for (i = 0; i < KERNEL_SUBSAMPLE + 1; i++) + { + for (j = 0; j < KERNEL_SUBSAMPLE + 1; j++) + { + core->subsample_brushes[i][j] = NULL; + } + } +} + +static void +gimp_brush_core_finalize (GObject *object) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (object); + gint i, j; + + g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref); + + for (i = 0; i < BRUSH_CORE_SOLID_SUBSAMPLE; i++) + for (j = 0; j < BRUSH_CORE_SOLID_SUBSAMPLE; j++) + g_clear_pointer (&core->solid_brushes[i][j], gimp_temp_buf_unref); + + g_clear_pointer (&core->rand, g_rand_free); + + for (i = 0; i < KERNEL_SUBSAMPLE + 1; i++) + for (j = 0; j < KERNEL_SUBSAMPLE + 1; j++) + g_clear_pointer (&core->subsample_brushes[i][j], gimp_temp_buf_unref); + + if (core->main_brush) + { + g_signal_handlers_disconnect_by_func (core->main_brush, + gimp_brush_core_invalidate_cache, + core); + gimp_brush_end_use (core->main_brush); + g_clear_object (&core->main_brush); + } + + g_clear_object (&core->dynamics); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gimp_brush_core_pre_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core); + + if (paint_state == GIMP_PAINT_STATE_MOTION) + { + GimpCoords last_coords; + GimpCoords current_coords; + gdouble scale; + + gimp_paint_core_get_last_coords (paint_core, &last_coords); + gimp_paint_core_get_current_coords (paint_core, ¤t_coords); + + /* If we current point == last point, check if the brush + * wants to be painted in that case. (Direction dependent + * pixmap brush pipes don't, as they don't know which + * pixmap to select.) + */ + if (last_coords.x == current_coords.x && + last_coords.y == current_coords.y && + ! gimp_brush_want_null_motion (core->main_brush, + &last_coords, + ¤t_coords)) + { + return FALSE; + } + /*No drawing anything if the scale is too small*/ + if (GIMP_BRUSH_CORE_GET_CLASS (core)->handles_transforming_brush) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble fade_point; + + if (GIMP_BRUSH_CORE_GET_CLASS (core)->handles_dynamic_transforming_brush) + { + gdouble width; + gdouble height; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + width = gimp_brush_get_width (core->main_brush); + height = gimp_brush_get_height (core->main_brush); + + scale = paint_options->brush_size / + MAX (width, height) * + gimp_dynamics_get_linear_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_SIZE, + ¤t_coords, + paint_options, + fade_point); + + if (paint_options->brush_lock_to_view && + MAX (current_coords.xscale, current_coords.yscale) > 0) + { + scale /= MAX (current_coords.xscale, current_coords.yscale); + + /* Cap transform result for brushes or OOM can occur */ + if ((scale * MAX (width, height)) > GIMP_BRUSH_MAX_SIZE) + { + scale = GIMP_BRUSH_MAX_SIZE / MAX (width, height); + } + } + + if (scale < 0.0000001) + return FALSE; + } + } + + if (GIMP_BRUSH_CORE_GET_CLASS (paint_core)->handles_changing_brush) + { + core->brush = gimp_brush_select_brush (core->main_brush, + &last_coords, + ¤t_coords); + } + if ((! GIMP_IS_BRUSH_GENERATED(core->main_brush)) && + (paint_options->brush_hardness != gimp_brush_get_blur_hardness(core->main_brush))) + { + gimp_brush_flush_blur_caches(core->main_brush); + } + } + + return TRUE; +} + +static void +gimp_brush_core_post_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpPaintState paint_state, + guint32 time) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core); + + if (paint_state == GIMP_PAINT_STATE_MOTION) + { + core->brush = core->main_brush; + } +} + +static gboolean +gimp_brush_core_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core); + GimpContext *context = GIMP_CONTEXT (paint_options); + + gimp_brush_core_set_brush (core, gimp_context_get_brush (context)); + + gimp_brush_core_set_dynamics (core, gimp_context_get_dynamics (context)); + + if (! core->main_brush) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No brushes available for use with this tool.")); + return FALSE; + } + + if (! core->dynamics) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No paint dynamics available for use with this tool.")); + return FALSE; + } + + if (GIMP_BRUSH_CORE_GET_CLASS (core)->handles_transforming_brush) + { + gimp_brush_core_eval_transform_dynamics (core, + drawable, + paint_options, + coords); + + gimp_brush_core_eval_transform_symmetry (core, NULL, 0); + } + + core->spacing = paint_options->brush_spacing; + + core->brush = core->main_brush; + + core->jitter = + gimp_paint_options_get_jitter (paint_options, + gimp_item_get_image (GIMP_ITEM (drawable))); + + return TRUE; +} + +/** + * gimp_avoid_exact_integer + * @x: points to a gdouble + * + * Adjusts *x such that it is not too close to an integer. This is used + * for decision algorithms that would be vulnerable to rounding glitches + * if exact integers were input. + * + * Side effects: Changes the value of *x + **/ +static void +gimp_avoid_exact_integer (gdouble *x) +{ + const gdouble integral = floor (*x); + const gdouble fractional = *x - integral; + + if (fractional < EPSILON) + { + *x = integral + EPSILON; + } + else if (fractional > (1 -EPSILON)) + { + *x = integral + (1 - EPSILON); + } +} + +static void +gimp_brush_core_interpolate (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + guint32 time) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpDynamicsOutput *spacing_output; + GimpCoords last_coords; + GimpCoords current_coords; + GimpVector2 delta_vec; + gdouble delta_pressure; + gdouble delta_xtilt, delta_ytilt; + gdouble delta_wheel; + gdouble delta_velocity; + gdouble temp_direction; + GimpVector2 temp_vec; + gint n, num_points; + gdouble t0, dt, tn; + gdouble st_factor, st_offset; + gdouble initial; + gdouble dist; + gdouble total; + gdouble pixel_dist; + gdouble pixel_initial; + gdouble xd, yd; + gdouble mag; + gdouble dyn_spacing = core->spacing; + gdouble fade_point; + gboolean use_dyn_spacing; + + g_return_if_fail (GIMP_IS_BRUSH (core->brush)); + + gimp_paint_core_get_last_coords (paint_core, &last_coords); + gimp_paint_core_get_current_coords (paint_core, ¤t_coords); + + gimp_avoid_exact_integer (&last_coords.x); + gimp_avoid_exact_integer (&last_coords.y); + gimp_avoid_exact_integer (¤t_coords.x); + gimp_avoid_exact_integer (¤t_coords.y); + + delta_vec.x = current_coords.x - last_coords.x; + delta_vec.y = current_coords.y - last_coords.y; + delta_pressure = current_coords.pressure - last_coords.pressure; + delta_xtilt = current_coords.xtilt - last_coords.xtilt; + delta_ytilt = current_coords.ytilt - last_coords.ytilt; + delta_wheel = current_coords.wheel - last_coords.wheel; + delta_velocity = current_coords.velocity - last_coords.velocity; + temp_direction = current_coords.direction; + + /* return if there has been no motion */ + if (! delta_vec.x && + ! delta_vec.y && + ! delta_pressure && + ! delta_xtilt && + ! delta_ytilt && + ! delta_wheel && + ! delta_velocity) + return; + + pixel_dist = gimp_vector2_length (&delta_vec); + pixel_initial = paint_core->pixel_dist; + + /* Zero sized brushes are unfit for interpolate, so we just let + * paint core fail on its own + */ + if (core->scale == 0.0) + { + gimp_paint_core_set_last_coords (paint_core, ¤t_coords); + + gimp_paint_core_paint (paint_core, drawable, paint_options, + GIMP_PAINT_STATE_MOTION, time); + + paint_core->pixel_dist = pixel_initial + pixel_dist; /* Don't forget to update pixel distance*/ + + return; + } + + /* Handle dynamic spacing */ + spacing_output = gimp_dynamics_get_output (core->dynamics, + GIMP_DYNAMICS_OUTPUT_SPACING); + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + use_dyn_spacing = gimp_dynamics_output_is_enabled (spacing_output); + + if (use_dyn_spacing) + { + dyn_spacing = gimp_dynamics_output_get_linear_value (spacing_output, + ¤t_coords, + paint_options, + fade_point); + + /* Dynamic spacing assumes that the value set in core is the min + * value and the max is full 200% spacing. This approach differs + * from the usual factor from user input approach because making + * spacing smaller than the nominal value is unlikely and + * spacing has a hard defined max. + */ + dyn_spacing = (core->spacing + + ((2.0 - core->spacing) * (1.0 - dyn_spacing))); + + /* Limiting spacing to minimum 1% */ + dyn_spacing = MAX (core->spacing, dyn_spacing); + } + + /* calculate the distance traveled in the coordinate space of the brush */ + temp_vec = gimp_brush_get_x_axis (core->brush); + gimp_vector2_mul (&temp_vec, core->scale); + gimp_vector2_rotate (&temp_vec, core->angle * G_PI * 2); + + mag = gimp_vector2_length (&temp_vec); + xd = gimp_vector2_inner_product (&delta_vec, &temp_vec) / (mag * mag); + + temp_vec = gimp_brush_get_y_axis (core->brush); + gimp_vector2_mul (&temp_vec, core->scale); + gimp_vector2_rotate (&temp_vec, core->angle * G_PI * 2); + + mag = gimp_vector2_length (&temp_vec); + yd = gimp_vector2_inner_product (&delta_vec, &temp_vec) / (mag * mag); + + dist = 0.5 * sqrt (xd * xd + yd * yd); + total = dist + paint_core->distance; + initial = paint_core->distance; + + + if (delta_vec.x * delta_vec.x > delta_vec.y * delta_vec.y) + { + st_factor = delta_vec.x; + st_offset = last_coords.x - 0.5; + } + else + { + st_factor = delta_vec.y; + st_offset = last_coords.y - 0.5; + } + + if (use_dyn_spacing) + { + gint s0; + + num_points = dist / dyn_spacing; + + s0 = (gint) floor (st_offset + 0.5); + t0 = (s0 - st_offset) / st_factor; + dt = dyn_spacing / dist; + + if (num_points == 0) + return; + } + else if (fabs (st_factor) > dist / core->spacing) + { + /* The stripe principle leads to brush positions that are spaced + * *closer* than the official brush spacing. Use the official + * spacing instead. This is the common case when the brush spacing + * is large. + * The net effect is then to put a lower bound on the spacing, but + * one that varies with the slope of the line. This is suppose to + * make thin lines (say, with a 1x1 brush) prettier while leaving + * lines with larger brush spacing as they used to look in 1.2.x. + */ + + dt = core->spacing / dist; + n = (gint) (initial / core->spacing + 1.0 + EPSILON); + t0 = (n * core->spacing - initial) / dist; + num_points = 1 + (gint) floor ((1 + EPSILON - t0) / dt); + + /* if we arnt going to paint anything this time and the brush + * has only moved on one axis return without updating the brush + * position, distance etc. so that we can more accurately space + * brush strokes when curves are supplied to us in single pixel + * chunks. + */ + + if (num_points == 0 && (delta_vec.x == 0 || delta_vec.y == 0)) + return; + } + else if (fabs (st_factor) < EPSILON) + { + /* Hm, we've hardly moved at all. Don't draw anything, but reset the + * old coordinates and hope we've gone longer the next time... + */ + current_coords.x = last_coords.x; + current_coords.y = last_coords.y; + + gimp_paint_core_set_current_coords (paint_core, ¤t_coords); + + /* ... but go along with the current pressure, tilt and wheel */ + return; + } + else + { + gint direction = st_factor > 0 ? 1 : -1; + gint x, y; + gint s0, sn; + + /* Choose the first and last stripe to paint. + * FIRST PRIORITY is to avoid gaps painting with a 1x1 aliasing + * brush when a horizontalish line segment follows a verticalish + * one or vice versa - no matter what the angle between the two + * lines is. This will also limit the local thinning that a 1x1 + * subsampled brush may suffer in the same situation. + * SECOND PRIORITY is to avoid making free-hand drawings + * unpleasantly fat by plotting redundant points. + * These are achieved by the following rules, but it is a little + * tricky to see just why. Do not change this algorithm unless you + * are sure you know what you're doing! + */ + + /* Basic case: round the beginning and ending point to nearest + * stripe center. + */ + s0 = (gint) floor (st_offset + 0.5); + sn = (gint) floor (st_offset + st_factor + 0.5); + + t0 = (s0 - st_offset) / st_factor; + tn = (sn - st_offset) / st_factor; + + x = (gint) floor (last_coords.x + t0 * delta_vec.x); + y = (gint) floor (last_coords.y + t0 * delta_vec.y); + + if (t0 < 0.0 && !( x == (gint) floor (last_coords.x) && + y == (gint) floor (last_coords.y) )) + { + /* Exception A: If the first stripe's brush position is + * EXTRApolated into a different pixel square than the + * ideal starting point, don't plot it. + */ + s0 += direction; + } + else if (x == (gint) floor (paint_core->last_paint.x) && + y == (gint) floor (paint_core->last_paint.y)) + { + /* Exception B: If first stripe's brush position is within the + * same pixel square as the last plot of the previous line, + * don't plot it either. + */ + s0 += direction; + } + + x = (gint) floor (last_coords.x + tn * delta_vec.x); + y = (gint) floor (last_coords.y + tn * delta_vec.y); + + if (tn > 1.0 && !( x == (gint) floor (current_coords.x) && + y == (gint) floor (current_coords.y))) + { + /* Exception C: If the last stripe's brush position is + * EXTRApolated into a different pixel square than the + * ideal ending point, don't plot it. + */ + sn -= direction; + } + + t0 = (s0 - st_offset) / st_factor; + tn = (sn - st_offset) / st_factor; + dt = direction * 1.0 / st_factor; + num_points = 1 + direction * (sn - s0); + + if (num_points >= 1) + { + /* Hack the reported total distance such that it looks to the + * next line as if the the last pixel plotted were at an integer + * multiple of the brush spacing. This helps prevent artifacts + * for connected lines when the brush spacing is such that some + * slopes will use the stripe regime and other slopes will use + * the nominal brush spacing. + */ + + if (tn < 1) + total = initial + tn * dist; + + total = core->spacing * (gint) (total / core->spacing + 0.5); + total += (1.0 - tn) * dist; + } + } + + for (n = 0; n < num_points; n++) + { + gdouble t = t0 + n * dt; + gdouble p = (gdouble) n / num_points; + + current_coords.x = last_coords.x + t * delta_vec.x; + current_coords.y = last_coords.y + t * delta_vec.y; + current_coords.pressure = last_coords.pressure + p * delta_pressure; + current_coords.xtilt = last_coords.xtilt + p * delta_xtilt; + current_coords.ytilt = last_coords.ytilt + p * delta_ytilt; + current_coords.wheel = last_coords.wheel + p * delta_wheel; + current_coords.velocity = last_coords.velocity + p * delta_velocity; + current_coords.direction = temp_direction; + current_coords.xscale = last_coords.xscale; + current_coords.yscale = last_coords.yscale; + current_coords.angle = last_coords.angle; + current_coords.reflect = last_coords.reflect; + + if (core->jitter > 0.0) + { + GimpVector2 x_axis; + GimpVector2 y_axis; + gdouble dyn_jitter; + gdouble jitter_dist; + gint32 jitter_angle; + + x_axis = gimp_brush_get_x_axis (core->brush); + y_axis = gimp_brush_get_y_axis (core->brush); + + dyn_jitter = (core->jitter * + gimp_dynamics_get_linear_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_JITTER, + ¤t_coords, + paint_options, + fade_point)); + + jitter_dist = g_rand_double_range (core->rand, 0, dyn_jitter); + jitter_angle = g_rand_int_range (core->rand, + 0, BRUSH_CORE_JITTER_LUTSIZE); + + current_coords.x += + (x_axis.x + y_axis.x) * + jitter_dist * core->jitter_lut_x[jitter_angle] * core->scale; + + current_coords.y += + (y_axis.y + x_axis.y) * + jitter_dist * core->jitter_lut_y[jitter_angle] * core->scale; + } + + gimp_paint_core_set_current_coords (paint_core, ¤t_coords); + + paint_core->distance = initial + t * dist; + paint_core->pixel_dist = pixel_initial + t * pixel_dist; + + gimp_paint_core_paint (paint_core, drawable, paint_options, + GIMP_PAINT_STATE_MOTION, time); + } + + current_coords.x = last_coords.x + delta_vec.x; + current_coords.y = last_coords.y + delta_vec.y; + current_coords.pressure = last_coords.pressure + delta_pressure; + current_coords.xtilt = last_coords.xtilt + delta_xtilt; + current_coords.ytilt = last_coords.ytilt + delta_ytilt; + current_coords.wheel = last_coords.wheel + delta_wheel; + current_coords.velocity = last_coords.velocity + delta_velocity; + current_coords.xscale = last_coords.xscale; + current_coords.yscale = last_coords.yscale; + current_coords.angle = last_coords.angle; + current_coords.reflect = last_coords.reflect; + + gimp_paint_core_set_current_coords (paint_core, ¤t_coords); + gimp_paint_core_set_last_coords (paint_core, ¤t_coords); + + paint_core->distance = total; + paint_core->pixel_dist = pixel_initial + pixel_dist; +} + +static GeglBuffer * +gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height) +{ + GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core); + gint x, y; + gint x1, y1, x2, y2; + gint drawable_width, drawable_height; + gint brush_width, brush_height; + + gimp_brush_transform_size (core->brush, + core->scale, core->aspect_ratio, + gimp_brush_core_get_angle (core), + gimp_brush_core_get_reflect (core), + &brush_width, &brush_height); + + if (paint_width) + *paint_width = brush_width; + if (paint_height) + *paint_height = brush_height; + + /* adjust the x and y coordinates to the upper left corner of the brush */ + x = (gint) floor (coords->x) - (brush_width / 2); + y = (gint) floor (coords->y) - (brush_height / 2); + + drawable_width = gimp_item_get_width (GIMP_ITEM (drawable)); + drawable_height = gimp_item_get_height (GIMP_ITEM (drawable)); + + x1 = CLAMP (x - 1, 0, drawable_width); + y1 = CLAMP (y - 1, 0, drawable_height); + x2 = CLAMP (x + brush_width + 1, 0, drawable_width); + y2 = CLAMP (y + brush_height + 1, 0, drawable_height); + + /* configure the canvas buffer */ + if ((x2 - x1) && (y2 - y1)) + { + GimpTempBuf *temp_buf; + const Babl *format; + GimpLayerCompositeMode composite_mode; + + composite_mode = gimp_layer_mode_get_paint_composite_mode (paint_mode); + + format = gimp_layer_mode_get_format (paint_mode, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + composite_mode, + gimp_drawable_get_format (drawable)); + + if (paint_core->paint_buffer && + gegl_buffer_get_width (paint_core->paint_buffer) == (x2 - x1) && + gegl_buffer_get_height (paint_core->paint_buffer) == (y2 - y1) && + gegl_buffer_get_format (paint_core->paint_buffer) == format) + { + *paint_buffer_x = x1; + *paint_buffer_y = y1; + + return paint_core->paint_buffer; + } + + g_clear_object (&paint_core->paint_buffer); + + temp_buf = gimp_temp_buf_new ((x2 - x1), (y2 - y1), + format); + + *paint_buffer_x = x1; + *paint_buffer_y = y1; + + paint_core->paint_buffer = gimp_temp_buf_create_buffer (temp_buf); + + gimp_temp_buf_unref (temp_buf); + + return paint_core->paint_buffer; + } + + return NULL; +} + +static void +gimp_brush_core_real_set_brush (GimpBrushCore *core, + GimpBrush *brush) +{ + if (brush == core->main_brush) + return; + + if (core->main_brush) + { + g_signal_handlers_disconnect_by_func (core->main_brush, + gimp_brush_core_invalidate_cache, + core); + gimp_brush_end_use (core->main_brush); + } + + g_set_object (&core->main_brush, brush); + + if (core->main_brush) + { + gimp_brush_begin_use (core->main_brush); + g_signal_connect (core->main_brush, "invalidate-preview", + G_CALLBACK (gimp_brush_core_invalidate_cache), + core); + } +} + +static void +gimp_brush_core_real_set_dynamics (GimpBrushCore *core, + GimpDynamics *dynamics) +{ + g_set_object (&core->dynamics, dynamics); +} + +void +gimp_brush_core_set_brush (GimpBrushCore *core, + GimpBrush *brush) +{ + g_return_if_fail (GIMP_IS_BRUSH_CORE (core)); + g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush)); + + if (brush != core->main_brush) + g_signal_emit (core, core_signals[SET_BRUSH], 0, brush); +} + +void +gimp_brush_core_set_dynamics (GimpBrushCore *core, + GimpDynamics *dynamics) +{ + g_return_if_fail (GIMP_IS_BRUSH_CORE (core)); + g_return_if_fail (dynamics == NULL || GIMP_IS_DYNAMICS (dynamics)); + + if (dynamics != core->dynamics) + g_signal_emit (core, core_signals[SET_DYNAMICS], 0, dynamics); +} + +void +gimp_brush_core_paste_canvas (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + gdouble brush_opacity, + gdouble image_opacity, + GimpLayerMode paint_mode, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_force, + GimpPaintApplicationMode mode) +{ + const GimpTempBuf *brush_mask; + + brush_mask = gimp_brush_core_get_brush_mask (core, coords, + brush_hardness, + dynamic_force); + + if (brush_mask) + { + GimpPaintCore *paint_core = GIMP_PAINT_CORE (core); + gint x; + gint y; + gint off_x; + gint off_y; + + x = (gint) floor (coords->x) - (gimp_temp_buf_get_width (brush_mask) >> 1); + y = (gint) floor (coords->y) - (gimp_temp_buf_get_height (brush_mask) >> 1); + + off_x = (x < 0) ? -x : 0; + off_y = (y < 0) ? -y : 0; + + gimp_paint_core_paste (paint_core, brush_mask, + off_x, off_y, + drawable, + brush_opacity, + image_opacity, + paint_mode, + mode); + } +} + +/* Similar to gimp_brush_core_paste_canvas, but replaces the alpha channel + * rather than using it to composite (i.e. transparent over opaque + * becomes transparent rather than opauqe. + */ +void +gimp_brush_core_replace_canvas (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + gdouble brush_opacity, + gdouble image_opacity, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_force, + GimpPaintApplicationMode mode) +{ + const GimpTempBuf *brush_mask; + + brush_mask = gimp_brush_core_get_brush_mask (core, coords, + brush_hardness, + dynamic_force); + + if (brush_mask) + { + GimpPaintCore *paint_core = GIMP_PAINT_CORE (core); + gint x; + gint y; + gint off_x; + gint off_y; + + x = (gint) floor (coords->x) - (gimp_temp_buf_get_width (brush_mask) >> 1); + y = (gint) floor (coords->y) - (gimp_temp_buf_get_height (brush_mask) >> 1); + + off_x = (x < 0) ? -x : 0; + off_y = (y < 0) ? -y : 0; + + gimp_paint_core_replace (paint_core, brush_mask, + off_x, off_y, + drawable, + brush_opacity, + image_opacity, + mode); + } +} + + +static void +gimp_brush_core_invalidate_cache (GimpBrush *brush, + GimpBrushCore *core) +{ + /* Make sure we don't cache data for a brush that has changed */ + + core->subsample_cache_invalid = TRUE; + core->solid_cache_invalid = TRUE; + + /* Notify of the brush change */ + + g_signal_emit (core, core_signals[SET_BRUSH], 0, brush); +} + + +/************************************************************ + * LOCAL FUNCTION DEFINITIONS * + ************************************************************/ + +static gdouble +gimp_brush_core_get_angle (GimpBrushCore *core) +{ + gdouble angle = core->angle; + + if (core->reflect) + angle -= core->symmetry_angle; + else + angle += core->symmetry_angle; + + angle = fmod (angle, 1.0); + + if (angle < 0.0) + angle += 1.0; + + return angle; +} + +static gboolean +gimp_brush_core_get_reflect (GimpBrushCore *core) +{ + return core->reflect ^ core->symmetry_reflect; +} + +static const GimpTempBuf * +gimp_brush_core_transform_mask (GimpBrushCore *core, + GimpBrush *brush) +{ + const GimpTempBuf *mask; + + if (core->scale <= 0.0) + return NULL; + + mask = gimp_brush_transform_mask (brush, + core->scale, + core->aspect_ratio, + gimp_brush_core_get_angle (core), + gimp_brush_core_get_reflect (core), + core->hardness); + + if (mask == core->transform_brush) + return mask; + + core->transform_brush = mask; + core->subsample_cache_invalid = TRUE; + core->solid_cache_invalid = TRUE; + + return core->transform_brush; +} + +const GimpTempBuf * +gimp_brush_core_get_brush_mask (GimpBrushCore *core, + const GimpCoords *coords, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_force) +{ + const GimpTempBuf *mask; + + if (dynamic_force <= 0.0) + return NULL; + + mask = gimp_brush_core_transform_mask (core, core->brush); + + if (! mask) + return NULL; + + switch (brush_hardness) + { + case GIMP_BRUSH_SOFT: + return gimp_brush_core_subsample_mask (core, mask, + coords->x, + coords->y); + break; + + case GIMP_BRUSH_HARD: + return gimp_brush_core_solidify_mask (core, mask, + coords->x, + coords->y); + break; + + case GIMP_BRUSH_PRESSURE: + return gimp_brush_core_pressurize_mask (core, mask, + coords->x, + coords->y, + dynamic_force); + break; + } + + g_return_val_if_reached (NULL); +} + +const GimpTempBuf * +gimp_brush_core_get_brush_pixmap (GimpBrushCore *core) +{ + const GimpTempBuf *pixmap; + + if (core->scale <= 0.0) + return NULL; + + pixmap = gimp_brush_transform_pixmap (core->brush, + core->scale, + core->aspect_ratio, + gimp_brush_core_get_angle (core), + gimp_brush_core_get_reflect (core), + core->hardness); + + if (pixmap == core->transform_pixmap) + return pixmap; + + core->transform_pixmap = pixmap; + core->subsample_cache_invalid = TRUE; + + return core->transform_pixmap; +} + +void +gimp_brush_core_eval_transform_dynamics (GimpBrushCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords) +{ + if (core->main_brush) + { + gdouble max_side; + + max_side = MAX (gimp_brush_get_width (core->main_brush), + gimp_brush_get_height (core->main_brush)); + + core->scale = paint_options->brush_size / max_side; + + if (paint_options->brush_lock_to_view && + MAX (coords->xscale, coords->yscale) > 0) + { + core->scale /= MAX (coords->xscale, coords->yscale); + + /* Cap transform result for brushes or OOM can occur */ + if ((core->scale * max_side) > GIMP_BRUSH_MAX_SIZE) + { + core->scale = GIMP_BRUSH_MAX_SIZE / max_side; + } + } + } + else + core->scale = -1; + + core->aspect_ratio = paint_options->brush_aspect_ratio; + core->angle = paint_options->brush_angle; + core->reflect = FALSE; + core->hardness = paint_options->brush_hardness; + + if (paint_options->brush_lock_to_view) + { + core->angle += coords->angle; + core->reflect = coords->reflect; + } + + if (! GIMP_IS_DYNAMICS (core->dynamics)) + return; + + if (GIMP_BRUSH_CORE_GET_CLASS (core)->handles_dynamic_transforming_brush) + { + gdouble fade_point = 1.0; + + if (drawable) + { + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpPaintCore *paint_core = GIMP_PAINT_CORE (core); + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + } + + core->scale *= gimp_dynamics_get_linear_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_SIZE, + coords, + paint_options, + fade_point); + + core->angle += gimp_dynamics_get_angular_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_ANGLE, + coords, + paint_options, + fade_point); + + core->hardness *= gimp_dynamics_get_linear_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_HARDNESS, + coords, + paint_options, + fade_point); + + if (gimp_dynamics_is_output_enabled (core->dynamics, + GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO)) + { + gdouble dyn_aspect; + + dyn_aspect = gimp_dynamics_get_aspect_value (core->dynamics, + GIMP_DYNAMICS_OUTPUT_ASPECT_RATIO, + coords, + paint_options, + fade_point); + + /* Zero aspect ratio is special cased to half of all ar range, + * to force dynamics to have any effect. Forcing to full results + * in disappearing stamp if applied to maximum. + */ + if (core->aspect_ratio == 0.0) + core->aspect_ratio = 10.0 * dyn_aspect; + else + core->aspect_ratio *= dyn_aspect; + } + } +} + +void +gimp_brush_core_eval_transform_symmetry (GimpBrushCore *core, + GimpSymmetry *symmetry, + gint stroke) +{ + g_return_if_fail (GIMP_IS_BRUSH_CORE (core)); + g_return_if_fail (symmetry == NULL || GIMP_IS_SYMMETRY (symmetry)); + + core->symmetry_angle = 0.0; + core->symmetry_reflect = FALSE; + + if (symmetry) + { + gimp_symmetry_get_transform (symmetry, + stroke, + &core->symmetry_angle, + &core->symmetry_reflect); + + core->symmetry_angle /= 360.0; + } +} + +void +gimp_brush_core_color_area_with_pixmap (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + GeglBuffer *area, + gint area_x, + gint area_y, + gboolean apply_mask) +{ + const GimpTempBuf *pixmap; + GeglBuffer *pixmap_buffer; + const GimpTempBuf *mask; + GeglBuffer *mask_buffer; + gint area_width; + gint area_height; + gint ul_x; + gint ul_y; + gint offset_x; + gint offset_y; + + g_return_if_fail (GIMP_IS_BRUSH (core->brush)); + g_return_if_fail (gimp_brush_get_pixmap (core->brush) != NULL); + + /* scale the brush */ + pixmap = gimp_brush_core_get_brush_pixmap (core); + + if (! pixmap) + return; + + if (apply_mask) + mask = gimp_brush_core_transform_mask (core, core->brush); + else + mask = NULL; + + /* Calculate upper left corner of brush as in + * gimp_paint_core_get_paint_area. Ugly to have to do this here, too. + */ + ul_x = (gint) floor (coords->x) - (gimp_temp_buf_get_width (pixmap) >> 1); + ul_y = (gint) floor (coords->y) - (gimp_temp_buf_get_height (pixmap) >> 1); + + /* Not sure why this is necessary, but empirically the code does + * not work without it for even-sided brushes. See bug #166622. + */ + if (gimp_temp_buf_get_width (pixmap) % 2 == 0) + ul_x += ROUND (coords->x) - floor (coords->x); + if (gimp_temp_buf_get_height (pixmap) % 2 == 0) + ul_y += ROUND (coords->y) - floor (coords->y); + + offset_x = area_x - ul_x; + offset_y = area_y - ul_y; + + area_width = gegl_buffer_get_width (area); + area_height = gegl_buffer_get_height (area); + + pixmap_buffer = gimp_temp_buf_create_buffer (pixmap); + + gimp_gegl_buffer_copy (pixmap_buffer, + GEGL_RECTANGLE (offset_x, offset_y, + area_width, area_height), + GEGL_ABYSS_NONE, + area, + GEGL_RECTANGLE (0, 0, + area_width, area_height)); + + g_object_unref (pixmap_buffer); + + if (mask) + { + mask_buffer = gimp_temp_buf_create_buffer (mask); + + gimp_gegl_apply_mask (mask_buffer, + GEGL_RECTANGLE (offset_x, offset_y, + area_width, area_height), + area, + GEGL_RECTANGLE (0, 0, + area_width, area_height), + 1.0); + + g_object_unref (mask_buffer); + } +} diff --git a/app/paint/gimpbrushcore.h b/app/paint/gimpbrushcore.h new file mode 100644 index 0000000..3ba4a41 --- /dev/null +++ b/app/paint/gimpbrushcore.h @@ -0,0 +1,152 @@ +/* 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 . + */ + +#ifndef __GIMP_BRUSH_CORE_H__ +#define __GIMP_BRUSH_CORE_H__ + + +#include "gimppaintcore.h" + + +#define BRUSH_CORE_SUBSAMPLE 4 +#define BRUSH_CORE_SOLID_SUBSAMPLE 2 +#define BRUSH_CORE_JITTER_LUTSIZE 360 + + +#define GIMP_TYPE_BRUSH_CORE (gimp_brush_core_get_type ()) +#define GIMP_BRUSH_CORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_CORE, GimpBrushCore)) +#define GIMP_BRUSH_CORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_CORE, GimpBrushCoreClass)) +#define GIMP_IS_BRUSH_CORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_CORE)) +#define GIMP_IS_BRUSH_CORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_CORE)) +#define GIMP_BRUSH_CORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_CORE, GimpBrushCoreClass)) + + +typedef struct _GimpBrushCoreClass GimpBrushCoreClass; + +struct _GimpBrushCore +{ + GimpPaintCore parent_instance; + + GimpBrush *main_brush; + GimpBrush *brush; + GimpDynamics *dynamics; + gdouble spacing; + gdouble scale; + gdouble aspect_ratio; + gdouble angle; + gboolean reflect; + gdouble hardness; + + gdouble symmetry_angle; + gboolean symmetry_reflect; + + /* brush buffers */ + GimpTempBuf *pressure_brush; + + GimpTempBuf *solid_brushes[BRUSH_CORE_SOLID_SUBSAMPLE][BRUSH_CORE_SOLID_SUBSAMPLE]; + const GimpTempBuf *last_solid_brush_mask; + gboolean solid_cache_invalid; + + const GimpTempBuf *transform_brush; + const GimpTempBuf *transform_pixmap; + + GimpTempBuf *subsample_brushes[BRUSH_CORE_SUBSAMPLE + 1][BRUSH_CORE_SUBSAMPLE + 1]; + const GimpTempBuf *last_subsample_brush_mask; + gboolean subsample_cache_invalid; + + gdouble jitter; + gdouble jitter_lut_x[BRUSH_CORE_JITTER_LUTSIZE]; + gdouble jitter_lut_y[BRUSH_CORE_JITTER_LUTSIZE]; + + GRand *rand; +}; + +struct _GimpBrushCoreClass +{ + GimpPaintCoreClass parent_class; + + /* Set for tools that don't mind if the brush changes while painting */ + gboolean handles_changing_brush; + + /* Set for tools that don't mind if the brush scales while painting */ + gboolean handles_transforming_brush; + + /* Set for tools that don't mind if the brush scales mid stroke */ + gboolean handles_dynamic_transforming_brush; + + void (* set_brush) (GimpBrushCore *core, + GimpBrush *brush); + void (* set_dynamics) (GimpBrushCore *core, + GimpDynamics *brush); +}; + + +GType gimp_brush_core_get_type (void) G_GNUC_CONST; + +void gimp_brush_core_set_brush (GimpBrushCore *core, + GimpBrush *brush); + +void gimp_brush_core_set_dynamics (GimpBrushCore *core, + GimpDynamics *dynamics); + +void gimp_brush_core_paste_canvas (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + gdouble brush_opacity, + gdouble image_opacity, + GimpLayerMode paint_mode, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_hardness, + GimpPaintApplicationMode mode); +void gimp_brush_core_replace_canvas (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + gdouble brush_opacity, + gdouble image_opacity, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_hardness, + GimpPaintApplicationMode mode); + +void gimp_brush_core_color_area_with_pixmap + (GimpBrushCore *core, + GimpDrawable *drawable, + const GimpCoords *coords, + GeglBuffer *area, + gint area_x, + gint area_y, + gboolean apply_mask); + +const GimpTempBuf * gimp_brush_core_get_brush_mask + (GimpBrushCore *core, + const GimpCoords *coords, + GimpBrushApplicationMode brush_hardness, + gdouble dynamic_hardness); +const GimpTempBuf * gimp_brush_core_get_brush_pixmap + (GimpBrushCore *core); + +void gimp_brush_core_eval_transform_dynamics + (GimpBrushCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords); +void gimp_brush_core_eval_transform_symmetry + (GimpBrushCore *core, + GimpSymmetry *symmetry, + gint stroke); + + +#endif /* __GIMP_BRUSH_CORE_H__ */ diff --git a/app/paint/gimpclone.c b/app/paint/gimpclone.c new file mode 100644 index 0000000..9c807f4 --- /dev/null +++ b/app/paint/gimpclone.c @@ -0,0 +1,256 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" + +#include "paint-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimperror.h" +#include "core/gimpimage.h" +#include "core/gimppattern.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" + +#include "gimpclone.h" +#include "gimpcloneoptions.h" + +#include "gimp-intl.h" + + +static gboolean gimp_clone_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error); + +static void gimp_clone_motion (GimpSourceCore *source_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GeglNode *op, + gdouble opacity, + GimpPickable *src_pickable, + GeglBuffer *src_buffer, + GeglRectangle *src_rect, + gint src_offset_x, + gint src_offset_y, + GeglBuffer *paint_buffer, + gint paint_buffer_x, + gint paint_buffer_y, + gint paint_area_offset_x, + gint paint_area_offset_y, + gint paint_area_width, + gint paint_area_height); + +static gboolean gimp_clone_use_source (GimpSourceCore *source_core, + GimpSourceOptions *options); + + +G_DEFINE_TYPE (GimpClone, gimp_clone, GIMP_TYPE_SOURCE_CORE) + +#define parent_class gimp_clone_parent_class + + +void +gimp_clone_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_CLONE, + GIMP_TYPE_CLONE_OPTIONS, + "gimp-clone", + _("Clone"), + "gimp-tool-clone"); +} + +static void +gimp_clone_class_init (GimpCloneClass *klass) +{ + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + GimpSourceCoreClass *source_core_class = GIMP_SOURCE_CORE_CLASS (klass); + + paint_core_class->start = gimp_clone_start; + + source_core_class->use_source = gimp_clone_use_source; + source_core_class->motion = gimp_clone_motion; +} + +static void +gimp_clone_init (GimpClone *clone) +{ +} + +static gboolean +gimp_clone_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error) +{ + GimpCloneOptions *options = GIMP_CLONE_OPTIONS (paint_options); + + if (! GIMP_PAINT_CORE_CLASS (parent_class)->start (paint_core, drawable, + paint_options, coords, + error)) + { + return FALSE; + } + + if (options->clone_type == GIMP_CLONE_PATTERN) + { + if (! gimp_context_get_pattern (GIMP_CONTEXT (options))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No patterns available for use with this tool.")); + return FALSE; + } + } + + return TRUE; +} + +static void +gimp_clone_motion (GimpSourceCore *source_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GeglNode *op, + gdouble opacity, + GimpPickable *src_pickable, + GeglBuffer *src_buffer, + GeglRectangle *src_rect, + gint src_offset_x, + gint src_offset_y, + GeglBuffer *paint_buffer, + gint paint_buffer_x, + gint paint_buffer_y, + gint paint_area_offset_x, + gint paint_area_offset_y, + gint paint_area_width, + gint paint_area_height) +{ + GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (source_core); + GimpCloneOptions *options = GIMP_CLONE_OPTIONS (paint_options); + GimpSourceOptions *source_options = GIMP_SOURCE_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpDynamics *dynamics = brush_core->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble fade_point; + gdouble force; + + if (gimp_source_core_use_source (source_core, source_options)) + { + if (! op) + { + gimp_gegl_buffer_copy (src_buffer, + GEGL_RECTANGLE (src_rect->x, + src_rect->y, + paint_area_width, + paint_area_height), + GEGL_ABYSS_NONE, + paint_buffer, + GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + 0, 0)); + } + else + { + gimp_gegl_apply_operation (src_buffer, NULL, NULL, op, + paint_buffer, + GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + paint_area_width, + paint_area_height), + FALSE); + } + } + else if (options->clone_type == GIMP_CLONE_PATTERN) + { + GimpPattern *pattern = gimp_context_get_pattern (context); + GeglBuffer *src_buffer = gimp_pattern_create_buffer (pattern); + + src_offset_x += gegl_buffer_get_width (src_buffer) / 2; + src_offset_y += gegl_buffer_get_height (src_buffer) / 2; + + gegl_buffer_set_pattern (paint_buffer, + GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + paint_area_width, + paint_area_height), + src_buffer, + - paint_buffer_x - src_offset_x, + - paint_buffer_y - src_offset_y); + + g_object_unref (src_buffer); + } + else + { + g_return_if_reached (); + } + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE)) + force = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_FORCE, + coords, + paint_options, + fade_point); + else + force = paint_options->brush_force; + + gimp_brush_core_paste_canvas (GIMP_BRUSH_CORE (paint_core), drawable, + coords, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + gimp_context_get_paint_mode (context), + gimp_paint_options_get_brush_mode (paint_options), + force, + + /* In fixed mode, paint incremental so the + * individual brushes are properly applied + * on top of each other. + * Otherwise the stuff we paint is seamless + * and we don't need intermediate masking. + */ + source_options->align_mode == + GIMP_SOURCE_ALIGN_FIXED ? + GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT); +} + +static gboolean +gimp_clone_use_source (GimpSourceCore *source_core, + GimpSourceOptions *options) +{ + return GIMP_CLONE_OPTIONS (options)->clone_type == GIMP_CLONE_IMAGE; +} diff --git a/app/paint/gimpclone.h b/app/paint/gimpclone.h new file mode 100644 index 0000000..dced05b --- /dev/null +++ b/app/paint/gimpclone.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_CLONE_H__ +#define __GIMP_CLONE_H__ + + +#include "gimpsourcecore.h" + + +#define GIMP_TYPE_CLONE (gimp_clone_get_type ()) +#define GIMP_CLONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CLONE, GimpClone)) +#define GIMP_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CLONE, GimpCloneClass)) +#define GIMP_IS_CLONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CLONE)) +#define GIMP_IS_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CLONE)) +#define GIMP_CLONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CLONE, GimpCloneClass)) + + +typedef struct _GimpCloneClass GimpCloneClass; + +struct _GimpClone +{ + GimpSourceCore parent_instance; +}; + +struct _GimpCloneClass +{ + GimpSourceCoreClass parent_class; +}; + + +void gimp_clone_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_clone_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CLONE_H__ */ diff --git a/app/paint/gimpcloneoptions.c b/app/paint/gimpcloneoptions.c new file mode 100644 index 0000000..4f398d6 --- /dev/null +++ b/app/paint/gimpcloneoptions.c @@ -0,0 +1,114 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "gimpcloneoptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_CLONE_TYPE +}; + + +static void gimp_clone_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_clone_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpCloneOptions, gimp_clone_options, GIMP_TYPE_SOURCE_OPTIONS) + +#define parent_class gimp_clone_options_parent_class + + +static void +gimp_clone_options_class_init (GimpCloneOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_clone_options_set_property; + object_class->get_property = gimp_clone_options_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_CLONE_TYPE, + "clone-type", + _("Source"), + NULL, + GIMP_TYPE_CLONE_TYPE, + GIMP_CLONE_IMAGE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_clone_options_init (GimpCloneOptions *options) +{ +} + +static void +gimp_clone_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCloneOptions *options = GIMP_CLONE_OPTIONS (object); + + switch (property_id) + { + case PROP_CLONE_TYPE: + options->clone_type = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_clone_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCloneOptions *options = GIMP_CLONE_OPTIONS (object); + + switch (property_id) + { + case PROP_CLONE_TYPE: + g_value_set_enum (value, options->clone_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimpcloneoptions.h b/app/paint/gimpcloneoptions.h new file mode 100644 index 0000000..b71f217 --- /dev/null +++ b/app/paint/gimpcloneoptions.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_CLONE_OPTIONS_H__ +#define __GIMP_CLONE_OPTIONS_H__ + + +#include "gimpsourceoptions.h" + + +#define GIMP_TYPE_CLONE_OPTIONS (gimp_clone_options_get_type ()) +#define GIMP_CLONE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CLONE_OPTIONS, GimpCloneOptions)) +#define GIMP_CLONE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CLONE_OPTIONS, GimpCloneOptionsClass)) +#define GIMP_IS_CLONE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CLONE_OPTIONS)) +#define GIMP_IS_CLONE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CLONE_OPTIONS)) +#define GIMP_CLONE_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CLONE_OPTIONS, GimpCloneOptionsClass)) + + +typedef struct _GimpCloneOptionsClass GimpCloneOptionsClass; + +struct _GimpCloneOptions +{ + GimpSourceOptions parent_instance; + + GimpCloneType clone_type; +}; + +struct _GimpCloneOptionsClass +{ + GimpSourceOptionsClass parent_class; +}; + + +GType gimp_clone_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CLONE_OPTIONS_H__ */ diff --git a/app/paint/gimpconvolve.c b/app/paint/gimpconvolve.c new file mode 100644 index 0000000..f2eea47 --- /dev/null +++ b/app/paint/gimpconvolve.c @@ -0,0 +1,277 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "paint-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "core/gimp.h" +#include "core/gimpbrush.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" +#include "core/gimptempbuf.h" + +#include "gimpconvolve.h" +#include "gimpconvolveoptions.h" + +#include "gimp-intl.h" + + +#define FIELD_COLS 4 +#define MIN_BLUR 64 /* (8/9 original pixel) */ +#define MAX_BLUR 0.25 /* (1/33 original pixel) */ +#define MIN_SHARPEN -512 +#define MAX_SHARPEN -64 + + +static void gimp_convolve_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); +static void gimp_convolve_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym); + +static void gimp_convolve_calculate_matrix (GimpConvolve *convolve, + GimpConvolveType type, + gint radius_x, + gint radius_y, + gdouble rate); +static gdouble gimp_convolve_sum_matrix (const gfloat *matrix); + + +G_DEFINE_TYPE (GimpConvolve, gimp_convolve, GIMP_TYPE_BRUSH_CORE) + + +void +gimp_convolve_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_CONVOLVE, + GIMP_TYPE_CONVOLVE_OPTIONS, + "gimp-convolve", + _("Convolve"), + "gimp-tool-blur"); +} + +static void +gimp_convolve_class_init (GimpConvolveClass *klass) +{ + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + + paint_core_class->paint = gimp_convolve_paint; +} + +static void +gimp_convolve_init (GimpConvolve *convolve) +{ + gint i; + + for (i = 0; i < 9; i++) + convolve->matrix[i] = 1.0; + + convolve->matrix_divisor = 9.0; +} + +static void +gimp_convolve_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + switch (paint_state) + { + case GIMP_PAINT_STATE_MOTION: + gimp_convolve_motion (paint_core, drawable, paint_options, sym); + break; + + default: + break; + } +} + +static void +gimp_convolve_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym) +{ + GimpConvolve *convolve = GIMP_CONVOLVE (paint_core); + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); + GimpConvolveOptions *options = GIMP_CONVOLVE_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GeglBuffer *paint_buffer; + gint paint_buffer_x; + gint paint_buffer_y; + GimpTempBuf *temp_buf; + GeglBuffer *convolve_buffer; + gdouble fade_point; + gdouble opacity; + gdouble rate; + const GimpCoords *coords; + gint paint_width, paint_height; + gint n_strokes; + gint i; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + coords = gimp_symmetry_get_origin (sym); + opacity = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_OPACITY, + coords, + paint_options, + fade_point); + if (opacity == 0.0) + return; + + gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core), + drawable, + paint_options, + coords); + n_strokes = gimp_symmetry_get_size (sym); + for (i = 0; i < n_strokes; i++) + { + coords = gimp_symmetry_get_coords (sym, i); + + gimp_brush_core_eval_transform_symmetry (brush_core, sym, i); + + paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, + paint_options, + GIMP_LAYER_MODE_NORMAL, + coords, + &paint_buffer_x, + &paint_buffer_y, + &paint_width, + &paint_height); + if (! paint_buffer) + continue; + + rate = (options->rate * + gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_RATE, + coords, + paint_options, + fade_point)); + + gimp_convolve_calculate_matrix (convolve, options->type, + gimp_brush_get_width (brush_core->brush) / 2, + gimp_brush_get_height (brush_core->brush) / 2, + rate); + + /* need a linear buffer for gimp_gegl_convolve() */ + temp_buf = gimp_temp_buf_new (gegl_buffer_get_width (paint_buffer), + gegl_buffer_get_height (paint_buffer), + gegl_buffer_get_format (paint_buffer)); + convolve_buffer = gimp_temp_buf_create_buffer (temp_buf); + gimp_temp_buf_unref (temp_buf); + + gimp_gegl_buffer_copy ( + gimp_drawable_get_buffer (drawable), + GEGL_RECTANGLE (paint_buffer_x, + paint_buffer_y, + gegl_buffer_get_width (paint_buffer), + gegl_buffer_get_height (paint_buffer)), + GEGL_ABYSS_NONE, + convolve_buffer, + GEGL_RECTANGLE (0, 0, 0, 0)); + + gimp_gegl_convolve (convolve_buffer, + GEGL_RECTANGLE (0, 0, + gegl_buffer_get_width (convolve_buffer), + gegl_buffer_get_height (convolve_buffer)), + paint_buffer, + GEGL_RECTANGLE (0, 0, + gegl_buffer_get_width (paint_buffer), + gegl_buffer_get_height (paint_buffer)), + convolve->matrix, 3, convolve->matrix_divisor, + GIMP_NORMAL_CONVOL, TRUE); + + g_object_unref (convolve_buffer); + + gimp_brush_core_replace_canvas (brush_core, drawable, + coords, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + gimp_paint_options_get_brush_mode (paint_options), + 1.0, + GIMP_PAINT_INCREMENTAL); + } +} + +static void +gimp_convolve_calculate_matrix (GimpConvolve *convolve, + GimpConvolveType type, + gint radius_x, + gint radius_y, + gdouble rate) +{ + /* find percent of tool pressure */ + const gdouble percent = MIN (rate / 100.0, 1.0); + + convolve->matrix[0] = (radius_x && radius_y) ? 1.0 : 0.0; + convolve->matrix[1] = (radius_y) ? 1.0 : 0.0; + convolve->matrix[2] = (radius_x && radius_y) ? 1.0 : 0.0; + convolve->matrix[3] = (radius_x) ? 1.0 : 0.0; + + /* get the appropriate convolution matrix and size and divisor */ + switch (type) + { + case GIMP_CONVOLVE_BLUR: + convolve->matrix[4] = MIN_BLUR + percent * (MAX_BLUR - MIN_BLUR); + break; + + case GIMP_CONVOLVE_SHARPEN: + convolve->matrix[4] = MIN_SHARPEN + percent * (MAX_SHARPEN - MIN_SHARPEN); + break; + } + + convolve->matrix[5] = (radius_x) ? 1.0 : 0.0; + convolve->matrix[6] = (radius_x && radius_y) ? 1.0 : 0.0; + convolve->matrix[7] = (radius_y) ? 1.0 : 0.0; + convolve->matrix[8] = (radius_x && radius_y) ? 1.0 : 0.0; + + convolve->matrix_divisor = gimp_convolve_sum_matrix (convolve->matrix); +} + +static gdouble +gimp_convolve_sum_matrix (const gfloat *matrix) +{ + gdouble sum = 0.0; + gint i; + + for (i = 0; i < 9; i++) + sum += matrix[i]; + + return sum; +} diff --git a/app/paint/gimpconvolve.h b/app/paint/gimpconvolve.h new file mode 100644 index 0000000..1aecf03 --- /dev/null +++ b/app/paint/gimpconvolve.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 . + */ + +#ifndef __GIMP_CONVOLVE_H__ +#define __GIMP_CONVOLVE_H__ + + +#include "gimpbrushcore.h" + + +#define GIMP_TYPE_CONVOLVE (gimp_convolve_get_type ()) +#define GIMP_CONVOLVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONVOLVE, GimpConvolve)) +#define GIMP_CONVOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CONVOLVE, GimpConvolveClass)) +#define GIMP_IS_CONVOLVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONVOLVE)) +#define GIMP_IS_CONVOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CONVOLVE)) +#define GIMP_CONVOLVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CONVOLVE, GimpConvolveClass)) + + +typedef struct _GimpConvolveClass GimpConvolveClass; + +struct _GimpConvolve +{ + GimpBrushCore parent_instance; + gfloat matrix[9]; + gfloat matrix_divisor; +}; + +struct _GimpConvolveClass +{ + GimpBrushCoreClass parent_class; +}; + + +void gimp_convolve_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_convolve_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CONVOLVE_H__ */ diff --git a/app/paint/gimpconvolveoptions.c b/app/paint/gimpconvolveoptions.c new file mode 100644 index 0000000..dffec68 --- /dev/null +++ b/app/paint/gimpconvolveoptions.c @@ -0,0 +1,129 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "gimpconvolveoptions.h" + +#include "gimp-intl.h" + + +#define DEFAULT_CONVOLVE_TYPE GIMP_CONVOLVE_BLUR +#define DEFAULT_CONVOLVE_RATE 50.0 + + +enum +{ + PROP_0, + PROP_TYPE, + PROP_RATE +}; + + +static void gimp_convolve_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_convolve_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpConvolveOptions, gimp_convolve_options, + GIMP_TYPE_PAINT_OPTIONS) + + +static void +gimp_convolve_options_class_init (GimpConvolveOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_convolve_options_set_property; + object_class->get_property = gimp_convolve_options_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TYPE, + "type", + _("Convolve Type"), + NULL, + GIMP_TYPE_CONVOLVE_TYPE, + DEFAULT_CONVOLVE_TYPE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RATE, + "rate", + C_("convolve-tool", "Rate"), + NULL, + 0.0, 100.0, DEFAULT_CONVOLVE_RATE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_convolve_options_init (GimpConvolveOptions *options) +{ +} + +static void +gimp_convolve_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpConvolveOptions *options = GIMP_CONVOLVE_OPTIONS (object); + + switch (property_id) + { + case PROP_TYPE: + options->type = g_value_get_enum (value); + break; + case PROP_RATE: + options->rate = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_convolve_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpConvolveOptions *options = GIMP_CONVOLVE_OPTIONS (object); + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, options->type); + break; + case PROP_RATE: + g_value_set_double (value, options->rate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimpconvolveoptions.h b/app/paint/gimpconvolveoptions.h new file mode 100644 index 0000000..62bd99b --- /dev/null +++ b/app/paint/gimpconvolveoptions.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_CONVOLVE_OPTIONS_H__ +#define __GIMP_CONVOLVE_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_CONVOLVE_OPTIONS (gimp_convolve_options_get_type ()) +#define GIMP_CONVOLVE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONVOLVE_OPTIONS, GimpConvolveOptions)) +#define GIMP_CONVOLVE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CONVOLVE_OPTIONS, GimpConvolveOptionsClass)) +#define GIMP_IS_CONVOLVE_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONVOLVE_OPTIONS)) +#define GIMP_IS_CONVOLVE_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CONVOLVE_OPTIONS)) +#define GIMP_CONVOLVE_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CONVOLVE_OPTIONS, GimpConvolveOptionsClass)) + + +typedef struct _GimpConvolveOptionsClass GimpConvolveOptionsClass; + +struct _GimpConvolveOptions +{ + GimpPaintOptions parent_instance; + + GimpConvolveType type; + gdouble rate; +}; + +struct _GimpConvolveOptionsClass +{ + GimpPaintOptionsClass parent_class; +}; + + +GType gimp_convolve_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_CONVOLVE_OPTIONS_H__ */ diff --git a/app/paint/gimpdodgeburn.c b/app/paint/gimpdodgeburn.c new file mode 100644 index 0000000..cdfc176 --- /dev/null +++ b/app/paint/gimpdodgeburn.c @@ -0,0 +1,201 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "paint-types.h" + +#include "gegl/gimp-gegl-loops.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpimage.h" +#include "core/gimpsymmetry.h" + +#include "gimpdodgeburn.h" +#include "gimpdodgeburnoptions.h" + +#include "gimp-intl.h" + + +static void gimp_dodge_burn_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); +static void gimp_dodge_burn_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym); + + +G_DEFINE_TYPE (GimpDodgeBurn, gimp_dodge_burn, GIMP_TYPE_BRUSH_CORE) + +#define parent_class gimp_dodge_burn_parent_class + + +void +gimp_dodge_burn_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_DODGE_BURN, + GIMP_TYPE_DODGE_BURN_OPTIONS, + "gimp-dodge-burn", + _("Dodge/Burn"), + "gimp-tool-dodge"); +} + +static void +gimp_dodge_burn_class_init (GimpDodgeBurnClass *klass) +{ + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass); + + paint_core_class->paint = gimp_dodge_burn_paint; + + brush_core_class->handles_changing_brush = TRUE; +} + +static void +gimp_dodge_burn_init (GimpDodgeBurn *dodgeburn) +{ +} + +static void +gimp_dodge_burn_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + break; + + case GIMP_PAINT_STATE_MOTION: + gimp_dodge_burn_motion (paint_core, drawable, paint_options, sym); + break; + + case GIMP_PAINT_STATE_FINISH: + break; + } +} + +static void +gimp_dodge_burn_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym) +{ + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); + GimpDodgeBurnOptions *options = GIMP_DODGE_BURN_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GeglBuffer *src_buffer; + GeglBuffer *paint_buffer; + gint paint_buffer_x; + gint paint_buffer_y; + gdouble fade_point; + gdouble opacity; + gdouble force; + const GimpCoords *coords; + gint paint_width, paint_height; + gint n_strokes; + gint i; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + coords = gimp_symmetry_get_origin (sym); + opacity = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_OPACITY, + coords, + paint_options, + fade_point); + if (opacity == 0.0) + return; + + if (paint_options->application_mode == GIMP_PAINT_CONSTANT) + src_buffer = gimp_paint_core_get_orig_image (paint_core); + else + src_buffer = gimp_drawable_get_buffer (drawable); + + gimp_brush_core_eval_transform_dynamics (brush_core, + drawable, + paint_options, + coords); + n_strokes = gimp_symmetry_get_size (sym); + for (i = 0; i < n_strokes; i++) + { + coords = gimp_symmetry_get_coords (sym, i); + + gimp_brush_core_eval_transform_symmetry (brush_core, sym, i); + + paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, + paint_options, + GIMP_LAYER_MODE_NORMAL, + coords, + &paint_buffer_x, + &paint_buffer_y, + &paint_width, + &paint_height); + if (! paint_buffer) + continue; + + /* DodgeBurn the region */ + gimp_gegl_dodgeburn (src_buffer, + GEGL_RECTANGLE (paint_buffer_x, + paint_buffer_y, + gegl_buffer_get_width (paint_buffer), + gegl_buffer_get_height (paint_buffer)), + paint_buffer, + GEGL_RECTANGLE (0, 0, 0, 0), + options->exposure / 100.0, + options->type, + options->mode); + + if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE)) + force = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_FORCE, + coords, + paint_options, + fade_point); + else + force = paint_options->brush_force; + + /* Replace the newly dodgedburned area (paint_area) to the image */ + gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable, + coords, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + gimp_paint_options_get_brush_mode (paint_options), + force, + paint_options->application_mode); + } +} diff --git a/app/paint/gimpdodgeburn.h b/app/paint/gimpdodgeburn.h new file mode 100644 index 0000000..859887b --- /dev/null +++ b/app/paint/gimpdodgeburn.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_DODGE_BURN_H__ +#define __GIMP_DODGE_BURN_H__ + + +#include "gimpbrushcore.h" + + +#define GIMP_TYPE_DODGE_BURN (gimp_dodge_burn_get_type ()) +#define GIMP_DODGE_BURN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DODGE_BURN, GimpDodgeBurn)) +#define GIMP_IS_DODGE_BURN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DODGE_BURN)) +#define GIMP_DODGE_BURN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DODGEBURN, GimpDodgeBurnClass)) +#define GIMP_IS_DODGE_BURN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DODGE_BURN)) + + +typedef struct _GimpDodgeBurnClass GimpDodgeBurnClass; + +struct _GimpDodgeBurn +{ + GimpBrushCore parent_instance; +}; + +struct _GimpDodgeBurnClass +{ + GimpBrushCoreClass parent_class; +}; + + +void gimp_dodge_burn_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_dodge_burn_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_DODGE_BURN_H__ */ diff --git a/app/paint/gimpdodgeburnoptions.c b/app/paint/gimpdodgeburnoptions.c new file mode 100644 index 0000000..b897b7e --- /dev/null +++ b/app/paint/gimpdodgeburnoptions.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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "gimpdodgeburnoptions.h" + +#include "gimp-intl.h" + + +#define DODGE_BURN_DEFAULT_TYPE GIMP_DODGE_BURN_TYPE_DODGE +#define DODGE_BURN_DEFAULT_MODE GIMP_TRANSFER_MIDTONES +#define DODGE_BURN_DEFAULT_EXPOSURE 50.0 + + +enum +{ + PROP_0, + PROP_TYPE, + PROP_MODE, + PROP_EXPOSURE +}; + + +static void gimp_dodge_burn_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_dodge_burn_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpDodgeBurnOptions, gimp_dodge_burn_options, + GIMP_TYPE_PAINT_OPTIONS) + + +static void +gimp_dodge_burn_options_class_init (GimpDodgeBurnOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_dodge_burn_options_set_property; + object_class->get_property = gimp_dodge_burn_options_get_property; + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_TYPE, + "type", + _("Type"), + NULL, + GIMP_TYPE_DODGE_BURN_TYPE, + DODGE_BURN_DEFAULT_TYPE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE, + "mode", + _("Range"), + NULL, + GIMP_TYPE_TRANSFER_MODE, + DODGE_BURN_DEFAULT_MODE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_EXPOSURE, + "exposure", + _("Exposure"), + NULL, + 0.0, 100.0, DODGE_BURN_DEFAULT_EXPOSURE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_dodge_burn_options_init (GimpDodgeBurnOptions *options) +{ +} + +static void +gimp_dodge_burn_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpDodgeBurnOptions *options = GIMP_DODGE_BURN_OPTIONS (object); + + switch (property_id) + { + case PROP_TYPE: + options->type = g_value_get_enum (value); + break; + case PROP_MODE: + options->mode = g_value_get_enum (value); + break; + case PROP_EXPOSURE: + options->exposure = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_dodge_burn_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpDodgeBurnOptions *options = GIMP_DODGE_BURN_OPTIONS (object); + + switch (property_id) + { + case PROP_TYPE: + g_value_set_enum (value, options->type); + break; + case PROP_MODE: + g_value_set_enum (value, options->mode); + break; + case PROP_EXPOSURE: + g_value_set_double (value, options->exposure); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimpdodgeburnoptions.h b/app/paint/gimpdodgeburnoptions.h new file mode 100644 index 0000000..02eed8a --- /dev/null +++ b/app/paint/gimpdodgeburnoptions.h @@ -0,0 +1,53 @@ +/* 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 . + */ + +#ifndef __GIMP_DODGE_BURN_OPTIONS_H__ +#define __GIMP_DODGE_BURN_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_DODGE_BURN_OPTIONS (gimp_dodge_burn_options_get_type ()) +#define GIMP_DODGE_BURN_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DODGE_BURN_OPTIONS, GimpDodgeBurnOptions)) +#define GIMP_DODGE_BURN_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DODGE_BURN_OPTIONS, GimpDodgeBurnOptionsClass)) +#define GIMP_IS_DODGE_BURN_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DODGE_BURN_OPTIONS)) +#define GIMP_IS_DODGE_BURN_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DODGE_BURN_OPTIONS)) +#define GIMP_DODGE_BURN_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DODGE_BURN_OPTIONS, GimpDodgeBurnOptionsClass)) + + +typedef struct _GimpDodgeBurnOptionsClass GimpDodgeBurnOptionsClass; + +struct _GimpDodgeBurnOptions +{ + GimpPaintOptions parent_instance; + + GimpDodgeBurnType type; + GimpTransferMode mode; /*highlights, midtones, shadows*/ + gdouble exposure; +}; + +struct _GimpDodgeBurnOptionsClass +{ + GimpPaintOptionsClass parent_class; +}; + + +GType gimp_dodge_burn_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_DODGE_BURN_OPTIONS_H__ */ diff --git a/app/paint/gimperaser.c b/app/paint/gimperaser.c new file mode 100644 index 0000000..68a535e --- /dev/null +++ b/app/paint/gimperaser.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 . + */ + +#include "config.h" + +#include +#include + +#include "paint-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimp.h" +#include "core/gimp-palettes.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" + +#include "gimperaser.h" +#include "gimperaseroptions.h" + +#include "gimp-intl.h" + + +static gboolean gimp_eraser_get_color_history_color (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color); +static void gimp_eraser_get_paint_params (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble grad_point, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); + + +G_DEFINE_TYPE (GimpEraser, gimp_eraser, GIMP_TYPE_PAINTBRUSH) + + +void +gimp_eraser_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_ERASER, + GIMP_TYPE_ERASER_OPTIONS, + "gimp-eraser", + _("Eraser"), + "gimp-tool-eraser"); +} + +static void +gimp_eraser_class_init (GimpEraserClass *klass) +{ + GimpPaintbrushClass *paintbrush_class = GIMP_PAINTBRUSH_CLASS (klass); + + paintbrush_class->get_color_history_color = gimp_eraser_get_color_history_color; + paintbrush_class->get_paint_params = gimp_eraser_get_paint_params; +} + +static void +gimp_eraser_init (GimpEraser *eraser) +{ +} + +static gboolean +gimp_eraser_get_color_history_color (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color) +{ + /* Erasing on a drawable without alpha is equivalent to + * drawing with background color. So let's save history. + */ + if (! gimp_drawable_has_alpha (drawable)) + { + GimpContext *context = GIMP_CONTEXT (paint_options); + + gimp_context_get_background (context, color); + + return TRUE; + } + + return FALSE; +} + +static void +gimp_eraser_get_paint_params (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble grad_point, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color) +{ + GimpEraserOptions *options = GIMP_ERASER_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + + gimp_context_get_background (context, paint_color); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + paint_color, paint_color); + + if (options->anti_erase) + *paint_mode = GIMP_LAYER_MODE_ANTI_ERASE; + else if (gimp_drawable_has_alpha (drawable)) + *paint_mode = GIMP_LAYER_MODE_ERASE; + else + *paint_mode = GIMP_LAYER_MODE_NORMAL_LEGACY; +} diff --git a/app/paint/gimperaser.h b/app/paint/gimperaser.h new file mode 100644 index 0000000..5b04a32 --- /dev/null +++ b/app/paint/gimperaser.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_ERASER_H__ +#define __GIMP_ERASER_H__ + + +#include "gimppaintbrush.h" + + +#define GIMP_TYPE_ERASER (gimp_eraser_get_type ()) +#define GIMP_ERASER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ERASER, GimpEraser)) +#define GIMP_ERASER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ERASER, GimpEraserClass)) +#define GIMP_IS_ERASER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ERASER)) +#define GIMP_IS_ERASER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ERASER)) +#define GIMP_ERASER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ERASER, GimpEraserClass)) + + +typedef struct _GimpEraserClass GimpEraserClass; + +struct _GimpEraser +{ + GimpPaintbrush parent_instance; +}; + +struct _GimpEraserClass +{ + GimpPaintbrushClass parent_class; +}; + + +void gimp_eraser_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_eraser_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_ERASER_H__ */ diff --git a/app/paint/gimperaseroptions.c b/app/paint/gimperaseroptions.c new file mode 100644 index 0000000..0b5a3fc --- /dev/null +++ b/app/paint/gimperaseroptions.c @@ -0,0 +1,113 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "gimperaseroptions.h" + +#include "gimp-intl.h" + + +#define ERASER_DEFAULT_ANTI_ERASE FALSE + + +enum +{ + PROP_0, + PROP_ANTI_ERASE +}; + + +static void gimp_eraser_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_eraser_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpEraserOptions, gimp_eraser_options, + GIMP_TYPE_PAINT_OPTIONS) + + +static void +gimp_eraser_options_class_init (GimpEraserOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_eraser_options_set_property; + object_class->get_property = gimp_eraser_options_get_property; + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTI_ERASE, + "anti-erase", + _("Anti erase"), + NULL, + ERASER_DEFAULT_ANTI_ERASE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_eraser_options_init (GimpEraserOptions *options) +{ +} + +static void +gimp_eraser_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpEraserOptions *options = GIMP_ERASER_OPTIONS (object); + + switch (property_id) + { + case PROP_ANTI_ERASE: + options->anti_erase = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_eraser_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpEraserOptions *options = GIMP_ERASER_OPTIONS (object); + + switch (property_id) + { + case PROP_ANTI_ERASE: + g_value_set_boolean (value, options->anti_erase); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimperaseroptions.h b/app/paint/gimperaseroptions.h new file mode 100644 index 0000000..3551d11 --- /dev/null +++ b/app/paint/gimperaseroptions.h @@ -0,0 +1,51 @@ +/* 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 . + */ + +#ifndef __GIMP_ERASER_OPTIONS_H__ +#define __GIMP_ERASER_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_ERASER_OPTIONS (gimp_eraser_options_get_type ()) +#define GIMP_ERASER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ERASER_OPTIONS, GimpEraserOptions)) +#define GIMP_ERASER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ERASER_OPTIONS, GimpEraserOptionsClass)) +#define GIMP_IS_ERASER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ERASER_OPTIONS)) +#define GIMP_IS_ERASER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ERASER_OPTIONS)) +#define GIMP_ERASER_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ERASER_OPTIONS, GimpEraserOptionsClass)) + + +typedef struct _GimpEraserOptionsClass GimpEraserOptionsClass; + +struct _GimpEraserOptions +{ + GimpPaintOptions parent_instance; + + gboolean anti_erase; +}; + +struct _GimpEraserOptionsClass +{ + GimpPaintOptionsClass parent_class; +}; + + +GType gimp_eraser_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_ERASER_OPTIONS_H__ */ diff --git a/app/paint/gimpheal.c b/app/paint/gimpheal.c new file mode 100644 index 0000000..814fd26 --- /dev/null +++ b/app/paint/gimpheal.c @@ -0,0 +1,642 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpheal.c + * Copyright (C) Jean-Yves Couleaud + * Copyright (C) 2013 Loren Merritt + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "paint-types.h" + +#include "gegl/gimp-gegl-apply-operation.h" +#include "gegl/gimp-gegl-loops.h" + +#include "core/gimpbrush.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimperror.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimptempbuf.h" + +#include "gimpheal.h" +#include "gimpsourceoptions.h" + +#include "gimp-intl.h" + + + +/* NOTES + * + * The method used here is similar to the lighting invariant correction + * method but slightly different: we do not divide the RGB components, + * but subtract them I2 = I0 - I1, where I0 is the sample image to be + * corrected, I1 is the reference pattern. Then we solve DeltaI=0 + * (Laplace) with I2 Dirichlet conditions at the borders of the + * mask. The solver is a red/black checker Gauss-Seidel with over-relaxation. + * It could benefit from a multi-grid evaluation of an initial solution + * before the main iteration loop. + * + * I reduced the convergence criteria to 0.1% (0.001) as we are + * dealing here with RGB integer components, more is overkill. + * + * Jean-Yves Couleaud cjyves@free.fr + */ + +static gboolean gimp_heal_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error); +static GeglBuffer * gimp_heal_get_paint_buffer (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height); + +static void gimp_heal_motion (GimpSourceCore *source_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GeglNode *op, + gdouble opacity, + GimpPickable *src_pickable, + GeglBuffer *src_buffer, + GeglRectangle *src_rect, + gint src_offset_x, + gint src_offset_y, + GeglBuffer *paint_buffer, + gint paint_buffer_x, + gint paint_buffer_y, + gint paint_area_offset_x, + gint paint_area_offset_y, + gint paint_area_width, + gint paint_area_height); + + +G_DEFINE_TYPE (GimpHeal, gimp_heal, GIMP_TYPE_SOURCE_CORE) + +#define parent_class gimp_heal_parent_class + + +void +gimp_heal_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_HEAL, + GIMP_TYPE_SOURCE_OPTIONS, + "gimp-heal", + _("Healing"), + "gimp-tool-heal"); +} + +static void +gimp_heal_class_init (GimpHealClass *klass) +{ + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + GimpSourceCoreClass *source_core_class = GIMP_SOURCE_CORE_CLASS (klass); + + paint_core_class->start = gimp_heal_start; + paint_core_class->get_paint_buffer = gimp_heal_get_paint_buffer; + + source_core_class->motion = gimp_heal_motion; +} + +static void +gimp_heal_init (GimpHeal *heal) +{ +} + +static gboolean +gimp_heal_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error) +{ + GimpSourceCore *source_core = GIMP_SOURCE_CORE (paint_core); + + if (! GIMP_PAINT_CORE_CLASS (parent_class)->start (paint_core, drawable, + paint_options, coords, + error)) + { + return FALSE; + } + + if (! source_core->set_source && gimp_drawable_is_indexed (drawable)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Healing does not operate on indexed layers.")); + return FALSE; + } + + return TRUE; +} + +static GeglBuffer * +gimp_heal_get_paint_buffer (GimpPaintCore *core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height) +{ + return GIMP_PAINT_CORE_CLASS (parent_class)->get_paint_buffer (core, + drawable, + paint_options, + GIMP_LAYER_MODE_NORMAL, + coords, + paint_buffer_x, + paint_buffer_y, + paint_width, + paint_height); +} + +/* Subtract bottom from top and store in result as a float + */ +static void +gimp_heal_sub (GeglBuffer *top_buffer, + const GeglRectangle *top_rect, + GeglBuffer *bottom_buffer, + const GeglRectangle *bottom_rect, + GeglBuffer *result_buffer, + const GeglRectangle *result_rect) +{ + GeglBufferIterator *iter; + const Babl *format = gegl_buffer_get_format (top_buffer); + gint n_components = babl_format_get_n_components (format); + + if (n_components == 2) + format = babl_format ("Y'A float"); + else if (n_components == 4) + format = babl_format ("R'G'B'A float"); + else + g_return_if_reached (); + + iter = gegl_buffer_iterator_new (top_buffer, top_rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3); + + gegl_buffer_iterator_add (iter, bottom_buffer, bottom_rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + + gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0, + babl_format_n (babl_type ("float"), n_components), + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *t = iter->items[0].data; + gfloat *b = iter->items[1].data; + gfloat *r = iter->items[2].data; + gint length = iter->length * n_components; + + while (length--) + *r++ = *t++ - *b++; + } +} + +/* Add first to second and store in result + */ +static void +gimp_heal_add (GeglBuffer *first_buffer, + const GeglRectangle *first_rect, + GeglBuffer *second_buffer, + const GeglRectangle *second_rect, + GeglBuffer *result_buffer, + const GeglRectangle *result_rect) +{ + GeglBufferIterator *iter; + const Babl *format = gegl_buffer_get_format (result_buffer); + gint n_components = babl_format_get_n_components (format); + + if (n_components == 2) + format = babl_format ("Y'A float"); + else if (n_components == 4) + format = babl_format ("R'G'B'A float"); + else + g_return_if_reached (); + + iter = gegl_buffer_iterator_new (first_buffer, first_rect, 0, + babl_format_n (babl_type ("float"), + n_components), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 3); + + gegl_buffer_iterator_add (iter, second_buffer, second_rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + + gegl_buffer_iterator_add (iter, result_buffer, result_rect, 0, format, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *f = iter->items[0].data; + gfloat *s = iter->items[1].data; + gfloat *r = iter->items[2].data; + gint length = iter->length * n_components; + + while (length--) + *r++ = *f++ + *s++; + } +} + +#if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4 +static float +gimp_heal_laplace_iteration_sse (gfloat *pixels, + gfloat *Adiag, + gint *Aidx, + gfloat w, + gint nmask) +{ + typedef float v4sf __attribute__((vector_size(16))); + gint i; + v4sf wv = { w, w, w, w }; + v4sf err = { 0, 0, 0, 0 }; + union { v4sf v; float f[4]; } erru; + +#define Xv(j) (*(v4sf*)&pixels[Aidx[i * 5 + j]]) + + for (i = 0; i < nmask; i++) + { + v4sf a = { Adiag[i], Adiag[i], Adiag[i], Adiag[i] }; + v4sf diff = a * Xv(0) - wv * (Xv(1) + Xv(2) + Xv(3) + Xv(4)); + + Xv(0) -= diff; + err += diff * diff; + } + + erru.v = err; + + return erru.f[0] + erru.f[1] + erru.f[2] + erru.f[3]; +} +#endif + +/* Perform one iteration of Gauss-Seidel, and return the sum squared residual. + */ +static float +gimp_heal_laplace_iteration (gfloat *pixels, + gfloat *Adiag, + gint *Aidx, + gfloat w, + gint nmask, + gint depth) +{ + gint i, k; + gfloat err = 0; + +#if defined(__SSE__) && defined(__GNUC__) && __GNUC__ >= 4 + if (depth == 4) + return gimp_heal_laplace_iteration_sse (pixels, Adiag, Aidx, w, nmask); +#endif + + for (i = 0; i < nmask; i++) + { + gint j0 = Aidx[i * 5 + 0]; + gint j1 = Aidx[i * 5 + 1]; + gint j2 = Aidx[i * 5 + 2]; + gint j3 = Aidx[i * 5 + 3]; + gint j4 = Aidx[i * 5 + 4]; + gfloat a = Adiag[i]; + + for (k = 0; k < depth; k++) + { + gfloat diff = (a * pixels[j0 + k] - + w * (pixels[j1 + k] + + pixels[j2 + k] + + pixels[j3 + k] + + pixels[j4 + k])); + + pixels[j0 + k] -= diff; + err += diff * diff; + } + } + + return err; +} + +/* Solve the laplace equation for pixels and store the result in-place. + */ +static void +gimp_heal_laplace_loop (gfloat *pixels, + gint height, + gint depth, + gint width, + guchar *mask) +{ + /* Tolerate a total deviation-from-smoothness of 0.1 LSBs at 8bit depth. */ +#define EPSILON (0.1/255) +#define MAX_ITER 500 + + gint i, j, iter, parity, nmask, zero; + gfloat *Adiag; + gint *Aidx; + gfloat w; + + Adiag = g_new (gfloat, width * height); + Aidx = g_new (gint, 5 * width * height); + + /* All off-diagonal elements of A are either -1 or 0. We could store it as a + * general-purpose sparse matrix, but that adds some unnecessary overhead to + * the inner loop. Instead, assume exactly 4 off-diagonal elements in each + * row, all of which have value -1. Any row that in fact wants less than 4 + * coefs can put them in a dummy column to be multiplied by an empty pixel. + */ + zero = depth * width * height; + memset (pixels + zero, 0, depth * sizeof (gfloat)); + + /* Construct the system of equations. + * Arrange Aidx in checkerboard order, so that a single linear pass over that + * array results updating all of the red cells and then all of the black cells. + */ + nmask = 0; + for (parity = 0; parity < 2; parity++) + for (i = 0; i < height; i++) + for (j = (i&1)^parity; j < width; j+=2) + if (mask[j + i * width]) + { +#define A_NEIGHBOR(o,di,dj) \ + if ((dj<0 && j==0) || (dj>0 && j==width-1) || (di<0 && i==0) || (di>0 && i==height-1)) \ + Aidx[o + nmask * 5] = zero; \ + else \ + Aidx[o + nmask * 5] = ((i + di) * width + (j + dj)) * depth; + + /* Omit Dirichlet conditions for any neighbors off the + * edge of the canvas. + */ + Adiag[nmask] = 4 - (i==0) - (j==0) - (i==height-1) - (j==width-1); + A_NEIGHBOR (0, 0, 0); + A_NEIGHBOR (1, 0, 1); + A_NEIGHBOR (2, 1, 0); + A_NEIGHBOR (3, 0, -1); + A_NEIGHBOR (4, -1, 0); + nmask++; + } + + /* Empirically optimal over-relaxation factor. (Benchmarked on + * round brushes, at least. I don't know whether aspect ratio + * affects it.) + */ + w = 2.0 - 1.0 / (0.1575 * sqrt (nmask) + 0.8); + w *= 0.25; + for (i = 0; i < nmask; i++) + Adiag[i] *= w; + + /* Gauss-Seidel with successive over-relaxation */ + for (iter = 0; iter < MAX_ITER; iter++) + { + gfloat err = gimp_heal_laplace_iteration (pixels, Adiag, Aidx, + w, nmask, depth); + if (err < EPSILON * EPSILON * w * w) + break; + } + + g_free (Adiag); + g_free (Aidx); +} + +/* Original Algorithm Design: + * + * T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning + * http://www.tgeorgiev.net/Photoshop_Healing.pdf + */ +static void +gimp_heal (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + GeglBuffer *mask_buffer, + const GeglRectangle *mask_rect) +{ + const Babl *src_format; + const Babl *dest_format; + gint src_components; + gint dest_components; + gint width; + gint height; + gfloat *diff, *diff_alloc; + GeglBuffer *diff_buffer; + guchar *mask; + + src_format = gegl_buffer_get_format (src_buffer); + dest_format = gegl_buffer_get_format (dest_buffer); + + src_components = babl_format_get_n_components (src_format); + dest_components = babl_format_get_n_components (dest_format); + + width = gegl_buffer_get_width (src_buffer); + height = gegl_buffer_get_height (src_buffer); + + g_return_if_fail (src_components == dest_components); + + diff_alloc = g_new (gfloat, 4 + (width * height + 1) * src_components); + diff = (gfloat*)(((uintptr_t)diff_alloc + 15) & ~15); + + diff_buffer = + gegl_buffer_linear_new_from_data (diff, + babl_format_n (babl_type ("float"), + src_components), + GEGL_RECTANGLE (0, 0, width, height), + GEGL_AUTO_ROWSTRIDE, + (GDestroyNotify) g_free, diff_alloc); + + /* subtract pattern from image and store the result as a float in diff */ + gimp_heal_sub (dest_buffer, dest_rect, + src_buffer, src_rect, + diff_buffer, GEGL_RECTANGLE (0, 0, width, height)); + + mask = g_new (guchar, mask_rect->width * mask_rect->height); + + gegl_buffer_get (mask_buffer, mask_rect, 1.0, babl_format ("Y u8"), + mask, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gimp_heal_laplace_loop (diff, height, src_components, width, mask); + + g_free (mask); + + /* add solution to original image and store in dest */ + gimp_heal_add (diff_buffer, GEGL_RECTANGLE (0, 0, width, height), + src_buffer, src_rect, + dest_buffer, dest_rect); + + g_object_unref (diff_buffer); +} + +static void +gimp_heal_motion (GimpSourceCore *source_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GeglNode *op, + gdouble opacity, + GimpPickable *src_pickable, + GeglBuffer *src_buffer, + GeglRectangle *src_rect, + gint src_offset_x, + gint src_offset_y, + GeglBuffer *paint_buffer, + gint paint_buffer_x, + gint paint_buffer_y, + gint paint_area_offset_x, + gint paint_area_offset_y, + gint paint_area_width, + gint paint_area_height) +{ + GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpSourceOptions *src_options = GIMP_SOURCE_OPTIONS (paint_options); + GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GeglBuffer *src_copy; + GeglBuffer *mask_buffer; + GimpPickable *dest_pickable; + const GimpTempBuf *mask_buf; + gdouble fade_point; + gdouble force; + gint mask_off_x; + gint mask_off_y; + gint dest_pickable_off_x; + gint dest_pickable_off_y; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE)) + force = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_FORCE, + coords, + paint_options, + fade_point); + else + force = paint_options->brush_force; + + mask_buf = gimp_brush_core_get_brush_mask (GIMP_BRUSH_CORE (source_core), + coords, + GIMP_BRUSH_HARD, + force); + + if (! mask_buf) + return; + + /* check that all buffers are of the same size */ + if (src_rect->width != gegl_buffer_get_width (paint_buffer) || + src_rect->height != gegl_buffer_get_height (paint_buffer)) + { + /* this generally means that the source point has hit the edge + * of the layer, so it is not an error and we should not + * complain, just don't do anything + */ + return; + } + + /* heal should work in perceptual space, use R'G'B' instead of RGB */ + src_copy = gegl_buffer_new (GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + src_rect->width, + src_rect->height), + babl_format ("R'G'B'A float")); + + if (! op) + { + gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE, + src_copy, gegl_buffer_get_extent (src_copy)); + } + else + { + gimp_gegl_apply_operation (src_buffer, NULL, NULL, op, + src_copy, gegl_buffer_get_extent (src_copy), + FALSE); + } + + if (src_options->sample_merged) + { + dest_pickable = GIMP_PICKABLE (image); + + gimp_item_get_offset (GIMP_ITEM (drawable), + &dest_pickable_off_x, + &dest_pickable_off_y); + } + else + { + dest_pickable = GIMP_PICKABLE (drawable); + + dest_pickable_off_x = 0; + dest_pickable_off_y = 0; + } + + gimp_gegl_buffer_copy (gimp_pickable_get_buffer (dest_pickable), + GEGL_RECTANGLE (paint_buffer_x + dest_pickable_off_x, + paint_buffer_y + dest_pickable_off_y, + gegl_buffer_get_width (paint_buffer), + gegl_buffer_get_height (paint_buffer)), + GEGL_ABYSS_NONE, + paint_buffer, + GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + paint_area_width, + paint_area_height)); + + mask_buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask_buf); + + /* find the offset of the brush mask's rect */ + { + gint x = (gint) floor (coords->x) - (gegl_buffer_get_width (mask_buffer) >> 1); + gint y = (gint) floor (coords->y) - (gegl_buffer_get_height (mask_buffer) >> 1); + + mask_off_x = (x < 0) ? -x : 0; + mask_off_y = (y < 0) ? -y : 0; + } + + gimp_heal (src_copy, gegl_buffer_get_extent (src_copy), + paint_buffer, + GEGL_RECTANGLE (paint_area_offset_x, + paint_area_offset_y, + paint_area_width, + paint_area_height), + mask_buffer, + GEGL_RECTANGLE (mask_off_x, mask_off_y, + paint_area_width, + paint_area_height)); + + g_object_unref (src_copy); + g_object_unref (mask_buffer); + + /* replace the canvas with our healed data */ + gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable, + coords, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + gimp_paint_options_get_brush_mode (paint_options), + force, + GIMP_PAINT_INCREMENTAL); +} diff --git a/app/paint/gimpheal.h b/app/paint/gimpheal.h new file mode 100644 index 0000000..780609d --- /dev/null +++ b/app/paint/gimpheal.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_HEAL_H__ +#define __GIMP_HEAL_H__ + + +#include "gimpsourcecore.h" + + +#define GIMP_TYPE_HEAL (gimp_heal_get_type ()) +#define GIMP_HEAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HEAL, GimpHeal)) +#define GIMP_HEAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HEAL, GimpHealClass)) +#define GIMP_IS_HEAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HEAL)) +#define GIMP_IS_HEAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HEAL)) +#define GIMP_HEAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HEAL, GimpHealClass)) + + +typedef struct _GimpHealClass GimpHealClass; + +struct _GimpHeal +{ + GimpSourceCore parent_instance; +}; + +struct _GimpHealClass +{ + GimpSourceCoreClass parent_class; +}; + + +void gimp_heal_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_heal_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_HEAL_H__ */ diff --git a/app/paint/gimpink-blob.c b/app/paint/gimpink-blob.c new file mode 100644 index 0000000..68a135a --- /dev/null +++ b/app/paint/gimpink-blob.c @@ -0,0 +1,875 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpink-blob.c: routines for manipulating scan converted convex polygons. + * Copyright 1998-1999, Owen Taylor + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +#include "config.h" + +#include + +#include "libgimpmath/gimpmath.h" + +#include "paint-types.h" + +#include "gimpink-blob.h" + + +typedef enum +{ + EDGE_NONE = 0, + EDGE_LEFT = 1 << 0, + EDGE_RIGHT = 1 << 1 +} EdgeType; + + +/* local function prototypes */ + +static GimpBlob * gimp_blob_new (gint y, + gint height); +static void gimp_blob_fill (GimpBlob *b, + EdgeType *present); +static void gimp_blob_make_convex (GimpBlob *b, + EdgeType *present); + +#if 0 +static void gimp_blob_line_add_pixel (GimpBlob *b, + gint x, + gint y); +static void gimp_blob_line (GimpBlob *b, + gint x0, + gint y0, + gint x1, + gint y1); +#endif + + +/* public functions */ + +/* Return blob for the given (convex) polygon + */ +GimpBlob * +gimp_blob_polygon (GimpBlobPoint *points, + gint n_points) +{ + GimpBlob *result; + EdgeType *present; + gint i; + gint im1; + gint ip1; + gint ymin, ymax; + + ymax = points[0].y; + ymin = points[0].y; + + for (i = 1; i < n_points; i++) + { + if (points[i].y > ymax) + ymax = points[i].y; + if (points[i].y < ymin) + ymin = points[i].y; + } + + result = gimp_blob_new (ymin, ymax - ymin + 1); + present = g_new0 (EdgeType, result->height); + + im1 = n_points - 1; + i = 0; + ip1 = 1; + + for (; i < n_points ; i++) + { + gint sides = 0; + gint j = points[i].y - ymin; + + if (points[i].y < points[im1].y) + sides |= EDGE_RIGHT; + else if (points[i].y > points[im1].y) + sides |= EDGE_LEFT; + + if (points[ip1].y < points[i].y) + sides |= EDGE_RIGHT; + else if (points[ip1].y > points[i].y) + sides |= EDGE_LEFT; + + if (sides & EDGE_RIGHT) + { + if (present[j] & EDGE_RIGHT) + { + result->data[j].right = MAX (result->data[j].right, points[i].x); + } + else + { + present[j] |= EDGE_RIGHT; + result->data[j].right = points[i].x; + } + } + + if (sides & EDGE_LEFT) + { + if (present[j] & EDGE_LEFT) + { + result->data[j].left = MIN (result->data[j].left, points[i].x); + } + else + { + present[j] |= EDGE_LEFT; + result->data[j].left = points[i].x; + } + } + + im1 = i; + ip1++; + if (ip1 == n_points) + ip1 = 0; + } + + gimp_blob_fill (result, present); + g_free (present); + + return result; +} + +/* Scan convert a square specified by _offsets_ of major and minor + * axes, and by center into a blob + */ +GimpBlob * +gimp_blob_square (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq) +{ + GimpBlobPoint points[4]; + + /* Make sure we order points ccw */ + + if (xp * yq - xq * yp < 0) + { + xq = -xq; + yq = -yq; + } + + points[0].x = xc + xp + xq; + points[0].y = yc + yp + yq; + points[1].x = xc + xp - xq; + points[1].y = yc + yp - yq; + points[2].x = xc - xp - xq; + points[2].y = yc - yp - yq; + points[3].x = xc - xp + xq; + points[3].y = yc - yp + yq; + + return gimp_blob_polygon (points, 4); +} + +/* Scan convert a diamond specified by _offsets_ of major and minor + * axes, and by center into a blob + */ +GimpBlob * +gimp_blob_diamond (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq) +{ + GimpBlobPoint points[4]; + + /* Make sure we order points ccw */ + + if (xp * yq - xq * yp < 0) + { + xq = -xq; + yq = -yq; + } + + points[0].x = xc + xp; + points[0].y = yc + yp; + points[1].x = xc - xq; + points[1].y = yc - yq; + points[2].x = xc - xp; + points[2].y = yc - yp; + points[3].x = xc + xq; + points[3].y = yc + yq; + + return gimp_blob_polygon (points, 4); +} + + +#define TABLE_SIZE 256 + +#define ELLIPSE_SHIFT 2 +#define TABLE_SHIFT 12 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +/* + * The choose of this values limits the maximal image_size to + * 16384 x 16384 pixels. The values will overflow as soon as + * x or y > INT_MAX / (1 << (ELLIPSE_SHIFT + TABLE_SHIFT)) / SUBSAMPLE + * + * Alternatively the code could be change the code as follows: + * + * xc_base = floor (xc) + * xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + * + * gint x = xc_base + (xc_shift + c * xp_shift + s * xq_shift + + * (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + * + * which would change the limit from the image to the ellipse size + * + * Update: this change was done, and now there apparently is a limit + * on the ellipse size. I'm too lazy to fully understand what's going + * on here and simply leave this comment here for + * documentation. --Mitch + */ + +static gboolean trig_initialized = FALSE; +static gint trig_table[TABLE_SIZE]; + +/* Scan convert an ellipse specified by _offsets_ of major and + * minor axes, and by center into a blob + */ +GimpBlob * +gimp_blob_ellipse (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq) +{ + GimpBlob *result; + EdgeType *present; + gint i; + gdouble r1, r2; + gint maxy, miny; + gint step; + gdouble max_radius; + + gint xc_shift, yc_shift; + gint xp_shift, yp_shift; + gint xq_shift, yq_shift; + gint xc_base, yc_base; + + if (! trig_initialized) + { + trig_initialized = TRUE; + + for (i = 0; i < 256; i++) + trig_table[i] = 0.5 + sin (i * (G_PI / 128.0)) * (1 << TABLE_SHIFT); + } + + /* Make sure we traverse ellipse in ccw direction */ + + if (xp * yq - xq * yp < 0) + { + xq = -xq; + yq = -yq; + } + + /* Compute bounds as if we were drawing a rectangle */ + + maxy = ceil (yc + fabs (yp) + fabs (yq)); + miny = floor (yc - fabs (yp) - fabs (yq)); + + result = gimp_blob_new (miny, maxy - miny + 1); + present = g_new0 (EdgeType, result->height); + + xc_base = floor (xc); + yc_base = floor (yc); + + /* Figure out a step that will draw most of the points */ + + r1 = sqrt (xp * xp + yp * yp); + r2 = sqrt (xq * xq + yq * yq); + max_radius = MAX (r1, r2); + step = TABLE_SIZE; + + while (step > 1 && (TABLE_SIZE / step < 4 * max_radius)) + step >>= 1; + + /* Fill in the edge points */ + + xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + yc_shift = 0.5 + (yc - yc_base) * (1 << TOTAL_SHIFT); + xp_shift = 0.5 + xp * (1 << ELLIPSE_SHIFT); + yp_shift = 0.5 + yp * (1 << ELLIPSE_SHIFT); + xq_shift = 0.5 + xq * (1 << ELLIPSE_SHIFT); + yq_shift = 0.5 + yq * (1 << ELLIPSE_SHIFT); + + for (i = 0 ; i < TABLE_SIZE ; i += step) + { + gint s = trig_table[i]; + gint c = trig_table[(TABLE_SIZE + TABLE_SIZE / 4 - i) % TABLE_SIZE]; + + gint x = ((xc_shift + c * xp_shift + s * xq_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT) + xc_base; + gint y = (((yc_shift + c * yp_shift + s * yq_shift + + (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT)) + yc_base + - result->y; + + gint dydi = c * yq_shift - s * yp_shift; + + if (dydi <= 0) /* left edge */ + { + if (present[y] & EDGE_LEFT) + { + result->data[y].left = MIN (result->data[y].left, x); + } + else + { + present[y] |= EDGE_LEFT; + result->data[y].left = x; + } + } + + if (dydi >= 0) /* right edge */ + { + if (present[y] & EDGE_RIGHT) + { + result->data[y].right = MAX (result->data[y].right, x); + } + else + { + present[y] |= EDGE_RIGHT; + result->data[y].right = x; + } + } + } + + /* Now fill in missing points */ + + gimp_blob_fill (result, present); + g_free (present); + + return result; +} + +void +gimp_blob_bounds (GimpBlob *b, + gint *x, + gint *y, + gint *width, + gint *height) +{ + gint i; + gint x0, x1, y0, y1; + + i = 0; + while (i < b->height && b->data[i].left > b->data[i].right) + i++; + + if (i < b->height) + { + y0 = b->y + i; + x0 = b->data[i].left; + x1 = b->data[i].right + 1; + + while (i < b->height && b->data[i].left <= b->data[i].right) + { + x0 = MIN (b->data[i].left, x0); + x1 = MAX (b->data[i].right + 1, x1); + i++; + } + + y1 = b->y + i; + } + else + { + x0 = y0 = 0; + x1 = y1 = 0; + } + + *x = x0; + *y = y0; + *width = x1 - x0; + *height = y1 - y0; +} + +GimpBlob * +gimp_blob_convex_union (GimpBlob *b1, + GimpBlob *b2) +{ + GimpBlob *result; + gint y; + gint i, j; + EdgeType *present; + + /* Create the storage for the result */ + + y = MIN (b1->y, b2->y); + result = gimp_blob_new (y, MAX (b1->y + b1->height, b2->y + b2->height)-y); + + if (result->height == 0) + return result; + + present = g_new0 (EdgeType, result->height); + + /* Initialize spans from original objects */ + + for (i = 0, j = b1->y-y; i < b1->height; i++, j++) + { + if (b1->data[i].right >= b1->data[i].left) + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b1->data[i].left; + result->data[j].right = b1->data[i].right; + } + } + + for (i = 0, j = b2->y - y; i < b2->height; i++, j++) + { + if (b2->data[i].right >= b2->data[i].left) + { + if (present[j]) + { + if (result->data[j].left > b2->data[i].left) + result->data[j].left = b2->data[i].left; + if (result->data[j].right < b2->data[i].right) + result->data[j].right = b2->data[i].right; + } + else + { + present[j] = EDGE_LEFT | EDGE_RIGHT; + result->data[j].left = b2->data[i].left; + result->data[j].right = b2->data[i].right; + } + } + } + + gimp_blob_make_convex (result, present); + + g_free (present); + + return result; +} + +GimpBlob * +gimp_blob_duplicate (GimpBlob *b) +{ + g_return_val_if_fail (b != NULL, NULL); + + return g_memdup (b, sizeof (GimpBlob) + sizeof (GimpBlobSpan) * (b->height - 1)); +} + +#if 0 +void +gimp_blob_dump (GimpBlob *b) +{ + gint i,j; + + for (i = 0; i < b->height; i++) + { + for (j = 0; j < b->data[i].left; j++) + putchar (' '); + + for (j = b->data[i].left; j <= b->data[i].right; j++) + putchar ('*'); + + putchar ('\n'); + } +} +#endif + + +/* private functions */ + +static GimpBlob * +gimp_blob_new (gint y, + gint height) +{ + GimpBlob *result; + + result = g_malloc (sizeof (GimpBlob) + sizeof (GimpBlobSpan) * (height - 1)); + + result->y = y; + result->height = height; + + return result; +} + +static void +gimp_blob_fill (GimpBlob *b, + EdgeType *present) +{ + gint start; + gint x1, x2, i1, i2; + gint i; + + /* Mark empty lines at top and bottom as unused */ + + start = 0; + while (! present[start]) + { + b->data[start].left = 0; + b->data[start].right = -1; + start++; + } + + if (present[start] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[start] == EDGE_RIGHT) + b->data[start].left = b->data[start].right; + else + b->data[start].right = b->data[start].left; + + present[start] = EDGE_RIGHT | EDGE_LEFT; + } + + for (i = b->height - 1; ! present[i]; i--) + { + b->data[i].left = 0; + b->data[i].right = -1; + } + + if (present[i] != (EDGE_RIGHT | EDGE_LEFT)) + { + if (present[i] == EDGE_RIGHT) + b->data[i].left = b->data[i].right; + else + b->data[i].right = b->data[i].left; + + present[i] = EDGE_RIGHT | EDGE_LEFT; + } + + + /* Restore missing edges */ + + /* We fill only interior regions of convex hull, as if we were + * filling polygons. But since we draw ellipses with nearest points, + * not interior points, maybe it would look better if we did the + * same here. Probably not a big deal either way after anti-aliasing + */ + + /* left edge */ + for (i1 = start; i1 < b->height - 2; i1++) + { + /* Find empty gaps */ + if (! (present[i1 + 1] & EDGE_LEFT)) + { + gint increment; /* fractional part */ + gint denom; /* denominator of fraction */ + gint step; /* integral step */ + gint frac; /* fractional step */ + gint reverse; + + /* find bottom of gap */ + i2 = i1 + 2; + while (i2 < b->height && ! (present[i2] & EDGE_LEFT)) + i2++; + + if (i2 < b->height) + { + denom = i2 - i1; + x1 = b->data[i1].left; + x2 = b->data[i2].left; + step = (x2 - x1) / denom; + frac = x2 - x1 - step * denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i = i1 + 1; i < i2; i++) + { + x1 += step; + increment += frac; + if (increment >= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (increment == 0 || reverse) + b->data[i].left = x1; + else + b->data[i].left = x1 + 1; + } + } + + i1 = i2 - 1; /* advance to next possibility */ + } + } + + /* right edge */ + for (i1 = start; i1 < b->height - 2; i1++) + { + /* Find empty gaps */ + if (! (present[i1 + 1] & EDGE_RIGHT)) + { + gint increment; /* fractional part */ + gint denom; /* denominator of fraction */ + gint step; /* integral step */ + gint frac; /* fractional step */ + gint reverse; + + /* find bottom of gap */ + i2 = i1 + 2; + while (i2 < b->height && ! (present[i2] & EDGE_RIGHT)) + i2++; + + if (i2 < b->height) + { + denom = i2 - i1; + x1 = b->data[i1].right; + x2 = b->data[i2].right; + step = (x2 - x1) / denom; + frac = x2 - x1 - step * denom; + if (frac < 0) + { + frac = -frac; + reverse = 1; + } + else + reverse = 0; + + increment = 0; + for (i = i1 + 1; i= denom) + { + increment -= denom; + x1 += reverse ? -1 : 1; + } + if (reverse && increment != 0) + b->data[i].right = x1 - 1; + else + b->data[i].right = x1; + } + } + + i1 = i2 - 1; /* advance to next possibility */ + } + } + +} + +static void +gimp_blob_make_convex (GimpBlob *b, + EdgeType *present) +{ + gint x1, x2, y1, y2, i1, i2; + gint i; + gint start; + + /* Walk through edges, deleting points that aren't on convex hull */ + + start = 0; + while (! present[start]) + start++; + + /* left edge */ + + i1 = start - 1; + i2 = start; + x1 = b->data[start].left - b->data[start].right; + y1 = 0; + + for (i = start + 1; i < b->height; i++) + { + if (! (present[i] & EDGE_LEFT)) + continue; + + x2 = b->data[i].left - b->data[i2].left; + y2 = i - i2; + + while (x2 * y1 - x1 * y2 < 0) /* clockwise rotation */ + { + present[i2] &= ~EDGE_LEFT; + i2 = i1; + while ((--i1) >= start && (! (present[i1] & EDGE_LEFT))); + + if (i1 < start) + { + x1 = b->data[start].left - b->data[start].right; + y1 = 0; + } + else + { + x1 = b->data[i2].left - b->data[i1].left; + y1 = i2 - i1; + } + x2 = b->data[i].left - b->data[i2].left; + y2 = i - i2; + } + + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + /* Right edge */ + + i1 = start -1; + i2 = start; + x1 = b->data[start].right - b->data[start].left; + y1 = 0; + + for (i = start + 1; i < b->height; i++) + { + if (! (present[i] & EDGE_RIGHT)) + continue; + + x2 = b->data[i].right - b->data[i2].right; + y2 = i - i2; + + while (x2 * y1 - x1 * y2 > 0) /* counter-clockwise rotation */ + { + present[i2] &= ~EDGE_RIGHT; + i2 = i1; + while ((--i1) >= start && (! (present[i1] & EDGE_RIGHT))); + + if (i1 < start) + { + x1 = b->data[start].right - b->data[start].left; + y1 = 0; + } + else + { + x1 = b->data[i2].right - b->data[i1].right; + y1 = i2 - i1; + } + + x2 = b->data[i].right - b->data[i2].right; + y2 = i - i2; + } + + x1 = x2; + y1 = y2; + i1 = i2; + i2 = i; + } + + gimp_blob_fill (b, present); +} + + +#if 0 + +static void +gimp_blob_line_add_pixel (GimpBlob *b, + gint x, + gint y) +{ + if (b->data[y - b->y].left > b->data[y - b->y].right) + { + b->data[y - b->y].left = b->data[y - b->y].right = x; + } + else + { + b->data[y - b->y].left = MIN (b->data[y - b->y].left, x); + b->data[y - b->y].right = MAX (b->data[y - b->y].right, x); + } +} + +static void +gimp_blob_line (GimpBlob *b, + gint x0, + gint y0, + gint x1, + gint y1) +{ + gint dx, dy, d; + gint incrE, incrNE; + gint x, y; + + gint xstep = 1; + gint ystep = 1; + + dx = x1 - x0; + dy = y1 - y0; + + if (dx < 0) + { + dx = -dx; + xstep = -1; + } + + if (dy < 0) + { + dy = -dy; + ystep = -1; + } + + /* for (y = y0; y != y1 + ystep ; y += ystep) + { + b->data[y-b->y].left = 0; + b->data[y-b->y].right = -1; + }*/ + + x = x0; + y = y0; + + if (dy < dx) + { + d = 2 * dy - dx; /* initial value of d */ + incrE = 2 * dy; /* increment used for move to E */ + incrNE = 2 * (dy - dx); /* increment used for move to NE */ + + gimp_blob_line_add_pixel (b, x, y); + + while (x != x1) + { + if (d <= 0) + { + d += incrE; + x += xstep; + } + else + { + d += incrNE; + x += xstep; + y += ystep; + } + + gimp_blob_line_add_pixel (b, x, y); + } + } + else + { + d = 2 * dx - dy; /* initial value of d */ + incrE = 2 * dx; /* increment used for move to E */ + incrNE = 2 * (dx - dy); /* increment used for move to NE */ + + gimp_blob_line_add_pixel (b, x, y); + + while (y != y1) + { + if (d <= 0) + { + d += incrE; + y += ystep; + } + else + { + d += incrNE; + x += xstep; + y += ystep; + } + + gimp_blob_line_add_pixel (b, x, y); + } + } +} + +#endif diff --git a/app/paint/gimpink-blob.h b/app/paint/gimpink-blob.h new file mode 100644 index 0000000..2ddbf76 --- /dev/null +++ b/app/paint/gimpink-blob.h @@ -0,0 +1,87 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * gimpink-blob.h: routines for manipulating scan converted convex polygons. + * Copyright 1998, Owen Taylor + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * +*/ + +#ifndef __GIMP_INK_BLOB_H__ +#define __GIMP_INK_BLOB_H__ + + +typedef struct _GimpBlobPoint GimpBlobPoint; +typedef struct _GimpBlobSpan GimpBlobSpan; +typedef struct _GimpBlob GimpBlob; + +typedef GimpBlob * (* GimpBlobFunc) (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq); + +struct _GimpBlobPoint +{ + gint x; + gint y; +}; + +struct _GimpBlobSpan +{ + gint left; + gint right; +}; + +struct _GimpBlob +{ + gint y; + gint height; + GimpBlobSpan data[1]; +}; + + +GimpBlob * gimp_blob_polygon (GimpBlobPoint *points, + gint n_points); +GimpBlob * gimp_blob_square (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq); +GimpBlob * gimp_blob_diamond (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq); +GimpBlob * gimp_blob_ellipse (gdouble xc, + gdouble yc, + gdouble xp, + gdouble yp, + gdouble xq, + gdouble yq); +void gimp_blob_bounds (GimpBlob *b, + gint *x, + gint *y, + gint *width, + gint *height); +GimpBlob * gimp_blob_convex_union (GimpBlob *b1, + GimpBlob *b2); +GimpBlob * gimp_blob_duplicate (GimpBlob *b); + + +#endif /* __GIMP_INK_BLOB_H__ */ diff --git a/app/paint/gimpink.c b/app/paint/gimpink.c new file mode 100644 index 0000000..50f72b8 --- /dev/null +++ b/app/paint/gimpink.c @@ -0,0 +1,785 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "libgimpmath/gimpmath.h" + +#include "paint-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimp-palettes.h" +#include "core/gimpdrawable.h" +#include "core/gimpimage.h" +#include "core/gimpimage-undo.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" +#include "core/gimptempbuf.h" + +#include "gimpinkoptions.h" +#include "gimpink.h" +#include "gimpink-blob.h" +#include "gimpinkundo.h" + +#include "gimp-intl.h" + + +#define SUBSAMPLE 8 + + +/* local function prototypes */ + +static void gimp_ink_finalize (GObject *object); + +static void gimp_ink_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); +static GeglBuffer * gimp_ink_get_paint_buffer (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height); +static GimpUndo * gimp_ink_push_undo (GimpPaintCore *core, + GimpImage *image, + const gchar *undo_desc); + +static void gimp_ink_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + guint32 time); + +static GimpBlob * ink_pen_ellipse (GimpInkOptions *options, + gdouble x_center, + gdouble y_center, + gdouble pressure, + gdouble xtilt, + gdouble ytilt, + gdouble velocity, + const GimpMatrix3 *transform); + +static void render_blob (GeglBuffer *buffer, + GeglRectangle *rect, + GimpBlob *blob); + + +G_DEFINE_TYPE (GimpInk, gimp_ink, GIMP_TYPE_PAINT_CORE) + +#define parent_class gimp_ink_parent_class + + +void +gimp_ink_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_INK, + GIMP_TYPE_INK_OPTIONS, + "gimp-ink", + _("Ink"), + "gimp-tool-ink"); +} + +static void +gimp_ink_class_init (GimpInkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + + object_class->finalize = gimp_ink_finalize; + + paint_core_class->paint = gimp_ink_paint; + paint_core_class->get_paint_buffer = gimp_ink_get_paint_buffer; + paint_core_class->push_undo = gimp_ink_push_undo; +} + +static void +gimp_ink_init (GimpInk *ink) +{ +} + +static void +gimp_ink_finalize (GObject *object) +{ + GimpInk *ink = GIMP_INK (object); + + if (ink->start_blobs) + { + g_list_free_full (ink->start_blobs, g_free); + ink->start_blobs = NULL; + } + + if (ink->last_blobs) + { + g_list_free_full (ink->last_blobs, g_free); + ink->last_blobs = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_ink_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + GimpInk *ink = GIMP_INK (paint_core); + GimpCoords *cur_coords; + GimpCoords last_coords; + + gimp_paint_core_get_last_coords (paint_core, &last_coords); + cur_coords = gimp_symmetry_get_origin (sym); + + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + { + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpRGB foreground; + + gimp_symmetry_set_stateful (sym, TRUE); + gimp_context_get_foreground (context, &foreground); + gimp_palettes_add_color_history (context->gimp, + &foreground); + + if (cur_coords->x == last_coords.x && + cur_coords->y == last_coords.y) + { + if (ink->start_blobs) + { + g_list_free_full (ink->start_blobs, g_free); + ink->start_blobs = NULL; + } + + if (ink->last_blobs) + { + g_list_free_full (ink->last_blobs, g_free); + ink->last_blobs = NULL; + } + } + else if (ink->last_blobs) + { + GimpBlob *last_blob; + GList *iter; + gint i; + + if (ink->start_blobs) + { + g_list_free_full (ink->start_blobs, g_free); + ink->start_blobs = NULL; + } + + /* save the start blobs of each stroke for undo otherwise */ + for (iter = ink->last_blobs, i = 0; iter; iter = g_list_next (iter), i++) + { + last_blob = g_list_nth_data (ink->last_blobs, i); + + ink->start_blobs = g_list_prepend (ink->start_blobs, + gimp_blob_duplicate (last_blob)); + } + ink->start_blobs = g_list_reverse (ink->start_blobs); + } + } + break; + + case GIMP_PAINT_STATE_MOTION: + gimp_ink_motion (paint_core, drawable, paint_options, sym, time); + break; + + case GIMP_PAINT_STATE_FINISH: + gimp_symmetry_set_stateful (sym, FALSE); + break; + } +} + +static GeglBuffer * +gimp_ink_get_paint_buffer (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpLayerMode paint_mode, + const GimpCoords *coords, + gint *paint_buffer_x, + gint *paint_buffer_y, + gint *paint_width, + gint *paint_height) +{ + GimpInk *ink = GIMP_INK (paint_core); + gint x, y; + gint width, height; + gint dwidth, dheight; + gint x1, y1, x2, y2; + + gimp_blob_bounds (ink->cur_blob, &x, &y, &width, &height); + + dwidth = gimp_item_get_width (GIMP_ITEM (drawable)); + dheight = gimp_item_get_height (GIMP_ITEM (drawable)); + + x1 = CLAMP (x / SUBSAMPLE - 1, 0, dwidth); + y1 = CLAMP (y / SUBSAMPLE - 1, 0, dheight); + x2 = CLAMP ((x + width) / SUBSAMPLE + 2, 0, dwidth); + y2 = CLAMP ((y + height) / SUBSAMPLE + 2, 0, dheight); + + if (paint_width) + *paint_width = width / SUBSAMPLE + 3; + if (paint_height) + *paint_height = height / SUBSAMPLE + 3; + + /* configure the canvas buffer */ + if ((x2 - x1) && (y2 - y1)) + { + GimpTempBuf *temp_buf; + const Babl *format; + GimpLayerCompositeMode composite_mode; + + composite_mode = gimp_layer_mode_get_paint_composite_mode (paint_mode); + + format = gimp_layer_mode_get_format (paint_mode, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + composite_mode, + gimp_drawable_get_format (drawable)); + + temp_buf = gimp_temp_buf_new ((x2 - x1), (y2 - y1), + format); + + *paint_buffer_x = x1; + *paint_buffer_y = y1; + + if (paint_core->paint_buffer) + g_object_unref (paint_core->paint_buffer); + + paint_core->paint_buffer = gimp_temp_buf_create_buffer (temp_buf); + + gimp_temp_buf_unref (temp_buf); + + return paint_core->paint_buffer; + } + + return NULL; +} + +static GimpUndo * +gimp_ink_push_undo (GimpPaintCore *core, + GimpImage *image, + const gchar *undo_desc) +{ + return gimp_image_undo_push (image, GIMP_TYPE_INK_UNDO, + GIMP_UNDO_INK, undo_desc, + 0, + "paint-core", core, + NULL); +} + +static void +gimp_ink_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + guint32 time) +{ + GimpInk *ink = GIMP_INK (paint_core); + GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GList *blob_unions = NULL; + GList *blobs_to_render = NULL; + GeglBuffer *paint_buffer; + gint paint_buffer_x; + gint paint_buffer_y; + GimpLayerMode paint_mode; + GimpRGB foreground; + GeglColor *color; + GimpBlob *last_blob; + GimpCoords *coords; + gint n_strokes; + gint i; + + n_strokes = gimp_symmetry_get_size (sym); + + if (ink->last_blobs && + g_list_length (ink->last_blobs) != n_strokes) + { + g_list_free_full (ink->last_blobs, g_free); + ink->last_blobs = NULL; + } + + if (! ink->last_blobs) + { + if (ink->start_blobs) + { + g_list_free_full (ink->start_blobs, g_free); + ink->start_blobs = NULL; + } + + for (i = 0; i < n_strokes; i++) + { + GimpMatrix3 transform; + + coords = gimp_symmetry_get_coords (sym, i); + + gimp_symmetry_get_matrix (sym, i, &transform); + + last_blob = ink_pen_ellipse (options, + coords->x, + coords->y, + coords->pressure, + coords->xtilt, + coords->ytilt, + 100, + &transform); + + ink->last_blobs = g_list_prepend (ink->last_blobs, + last_blob); + ink->start_blobs = g_list_prepend (ink->start_blobs, + gimp_blob_duplicate (last_blob)); + blobs_to_render = g_list_prepend (blobs_to_render, last_blob); + } + ink->start_blobs = g_list_reverse (ink->start_blobs); + ink->last_blobs = g_list_reverse (ink->last_blobs); + blobs_to_render = g_list_reverse (blobs_to_render); + } + else + { + for (i = 0; i < n_strokes; i++) + { + GimpBlob *blob; + GimpBlob *blob_union = NULL; + GimpMatrix3 transform; + + coords = gimp_symmetry_get_coords (sym, i); + + gimp_symmetry_get_matrix (sym, i, &transform); + + blob = ink_pen_ellipse (options, + coords->x, + coords->y, + coords->pressure, + coords->xtilt, + coords->ytilt, + coords->velocity * 100, + &transform); + + last_blob = g_list_nth_data (ink->last_blobs, i); + blob_union = gimp_blob_convex_union (last_blob, blob); + + g_free (last_blob); + g_list_nth (ink->last_blobs, i)->data = blob; + + blobs_to_render = g_list_prepend (blobs_to_render, blob_union); + blob_unions = g_list_prepend (blob_unions, blob_union); + } + blobs_to_render = g_list_reverse (blobs_to_render); + } + + paint_mode = gimp_context_get_paint_mode (context); + + gimp_context_get_foreground (context, &foreground); + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + &foreground, &foreground); + color = gimp_gegl_color_new (&foreground); + + for (i = 0; i < n_strokes; i++) + { + GimpBlob *blob_to_render = g_list_nth_data (blobs_to_render, i); + + coords = gimp_symmetry_get_coords (sym, i); + + ink->cur_blob = blob_to_render; + paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, + paint_options, + paint_mode, + coords, + &paint_buffer_x, + &paint_buffer_y, + NULL, NULL); + ink->cur_blob = NULL; + + if (! paint_buffer) + continue; + + gegl_buffer_set_color (paint_buffer, NULL, color); + + /* draw the blob directly to the canvas_buffer */ + render_blob (paint_core->canvas_buffer, + GEGL_RECTANGLE (paint_core->paint_buffer_x, + paint_core->paint_buffer_y, + gegl_buffer_get_width (paint_core->paint_buffer), + gegl_buffer_get_height (paint_core->paint_buffer)), + blob_to_render); + + /* draw the paint_area using the just rendered canvas_buffer as mask */ + gimp_paint_core_paste (paint_core, + NULL, + paint_core->paint_buffer_x, + paint_core->paint_buffer_y, + drawable, + GIMP_OPACITY_OPAQUE, + gimp_context_get_opacity (context), + paint_mode, + GIMP_PAINT_CONSTANT); + + } + + g_object_unref (color); + + g_list_free_full (blob_unions, g_free); +} + +static GimpBlob * +ink_pen_ellipse (GimpInkOptions *options, + gdouble x_center, + gdouble y_center, + gdouble pressure, + gdouble xtilt, + gdouble ytilt, + gdouble velocity, + const GimpMatrix3 *transform) +{ + GimpBlobFunc blob_function; + gdouble size; + gdouble tsin, tcos; + gdouble aspect, radmin; + gdouble x,y; + gdouble tscale; + gdouble tscale_c; + gdouble tscale_s; + + /* Adjust the size depending on pressure. */ + + size = options->size * (1.0 + options->size_sensitivity * + (2.0 * pressure - 1.0)); + + /* Adjust the size further depending on pointer velocity and + * velocity-sensitivity. These 'magic constants' are 'feels + * natural' tigert-approved. --ADM + */ + + if (velocity < 3.0) + velocity = 3.0; + +#ifdef VERBOSE + g_printerr ("%g (%g) -> ", size, velocity); +#endif + + size = (options->vel_sensitivity * + ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity))) + + (1.0 - options->vel_sensitivity) * size); + +#ifdef VERBOSE + g_printerr ("%g\n", (gfloat) size); +#endif + + /* Clamp resulting size to sane limits */ + + if (size > options->size * (1.0 + options->size_sensitivity)) + size = options->size * (1.0 + options->size_sensitivity); + + if (size * SUBSAMPLE < 1.0) + size = 1.0 / SUBSAMPLE; + + /* Add brush angle/aspect to tilt vectorially */ + + /* I'm not happy with the way the brush widget info is combined with + * tilt info from the brush. My personal feeling is that + * representing both as affine transforms would make the most + * sense. -RLL + */ + + tscale = options->tilt_sensitivity * 10.0; + tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle)); + tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle)); + + x = (options->blob_aspect * cos (options->blob_angle) + + xtilt * tscale_c - ytilt * tscale_s); + y = (options->blob_aspect * sin (options->blob_angle) + + ytilt * tscale_c + xtilt * tscale_s); + +#ifdef VERBOSE + g_printerr ("angle %g aspect %g; %g %g; %g %g\n", + options->blob_angle, options->blob_aspect, + tscale_c, tscale_s, x, y); +#endif + + aspect = sqrt (SQR (x) + SQR (y)); + + if (aspect != 0) + { + tcos = x / aspect; + tsin = y / aspect; + } + else + { + tcos = cos (options->blob_angle); + tsin = sin (options->blob_angle); + } + + gimp_matrix3_transform_point (transform, + tcos, tsin, + &tcos, &tsin); + + aspect = CLAMP (aspect, 1.0, 10.0); + + radmin = MAX (1.0, SUBSAMPLE * size / aspect); + + switch (options->blob_type) + { + case GIMP_INK_BLOB_TYPE_CIRCLE: + blob_function = gimp_blob_ellipse; + break; + + case GIMP_INK_BLOB_TYPE_SQUARE: + blob_function = gimp_blob_square; + break; + + case GIMP_INK_BLOB_TYPE_DIAMOND: + blob_function = gimp_blob_diamond; + break; + + default: + g_return_val_if_reached (NULL); + break; + } + + return (* blob_function) (x_center * SUBSAMPLE, + y_center * SUBSAMPLE, + radmin * aspect * tcos, + radmin * aspect * tsin, + -radmin * tsin, + radmin * tcos); +} + + +/*********************************/ +/* Rendering functions */ +/*********************************/ + +/* Some of this stuff should probably be combined with the + * code it was copied from in paint_core.c; but I wanted + * to learn this stuff, so I've kept it simple. + * + * The following only supports CONSTANT mode. Incremental + * would, I think, interact strangely with the way we + * do things. But it wouldn't be hard to implement at all. + */ + +enum +{ + ROW_START, + ROW_STOP +}; + +/* The insertion sort here, for SUBSAMPLE = 8, tends to beat out + * qsort() by 4x with CFLAGS=-O2, 2x with CFLAGS=-g + */ +static void +insert_sort (gint *data, + gint n) +{ + gint i, j, k; + + for (i = 2; i < 2 * n; i += 2) + { + gint tmp1 = data[i]; + gint tmp2 = data[i + 1]; + + j = 0; + + while (data[j] < tmp1) + j += 2; + + for (k = i; k > j; k -= 2) + { + data[k] = data[k - 2]; + data[k + 1] = data[k - 1]; + } + + data[j] = tmp1; + data[j + 1] = tmp2; + } +} + +static void +fill_run (gfloat *dest, + gfloat alpha, + gint w) +{ + if (alpha == 1.0) + { + while (w--) + { + *dest = 1.0; + dest++; + } + } + else + { + while (w--) + { + *dest = MAX (*dest, alpha); + dest++; + } + } +} + +static void +render_blob_line (GimpBlob *blob, + gfloat *dest, + gint x, + gint y, + gint width) +{ + gint buf[4 * SUBSAMPLE]; + gint *data = buf; + gint n = 0; + gint i, j; + gint current = 0; /* number of filled rows at this point + * in the scan line + */ + gint last_x; + + /* Sort start and ends for all lines */ + + j = y * SUBSAMPLE - blob->y; + for (i = 0; i < SUBSAMPLE; i++) + { + if (j >= blob->height) + break; + + if ((j > 0) && (blob->data[j].left <= blob->data[j].right)) + { + data[2 * n] = blob->data[j].left; + data[2 * n + 1] = ROW_START; + data[2 * SUBSAMPLE + 2 * n] = blob->data[j].right; + data[2 * SUBSAMPLE + 2 * n + 1] = ROW_STOP; + n++; + } + j++; + } + + /* If we have less than SUBSAMPLE rows, compress */ + if (n < SUBSAMPLE) + { + for (i = 0; i < 2 * n; i++) + data[2 * n + i] = data[2 * SUBSAMPLE + i]; + } + + /* Now count start and end separately */ + n *= 2; + + insert_sort (data, n); + + /* Discard portions outside of tile */ + + while ((n > 0) && (data[0] < SUBSAMPLE*x)) + { + if (data[1] == ROW_START) + current++; + else + current--; + data += 2; + n--; + } + + while ((n > 0) && (data[2*(n-1)] >= SUBSAMPLE*(x+width))) + n--; + + /* Render the row */ + + last_x = 0; + for (i = 0; i < n;) + { + gint cur_x = data[2 * i] / SUBSAMPLE - x; + gint pixel; + + /* Fill in portion leading up to this pixel */ + if (current && cur_x != last_x) + fill_run (dest + last_x, (gfloat) current / SUBSAMPLE, cur_x - last_x); + + /* Compute the value for this pixel */ + pixel = current * SUBSAMPLE; + + while (iitems[0].roi; + + while (gegl_buffer_iterator_next (iter)) + { + gfloat *d = iter->items[0].data; + gint h = roi->height; + gint y; + + for (y = 0; y < h; y++, d += roi->width * 1) + { + render_blob_line (blob, d, roi->x, roi->y + y, roi->width); + } + } +} diff --git a/app/paint/gimpink.h b/app/paint/gimpink.h new file mode 100644 index 0000000..ac31592 --- /dev/null +++ b/app/paint/gimpink.h @@ -0,0 +1,58 @@ +/* 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 . + */ + +#ifndef __GIMP_INK_H__ +#define __GIMP_INK_H__ + + +#include "gimppaintcore.h" +#include "gimpink-blob.h" + + +#define GIMP_TYPE_INK (gimp_ink_get_type ()) +#define GIMP_INK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INK, GimpInk)) +#define GIMP_INK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INK, GimpInkClass)) +#define GIMP_IS_INK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INK)) +#define GIMP_IS_INK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INK)) +#define GIMP_INK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INK, GimpInkClass)) + + +typedef struct _GimpInkClass GimpInkClass; + +struct _GimpInk +{ + GimpPaintCore parent_instance; + + GList *start_blobs; /* starting blobs per stroke (for undo) */ + + GimpBlob *cur_blob; /* current blob */ + GList *last_blobs; /* blobs for last stroke positions */ +}; + +struct _GimpInkClass +{ + GimpPaintCoreClass parent_class; +}; + + +void gimp_ink_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_ink_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_INK_H__ */ diff --git a/app/paint/gimpinkoptions.c b/app/paint/gimpinkoptions.c new file mode 100644 index 0000000..d660179 --- /dev/null +++ b/app/paint/gimpinkoptions.c @@ -0,0 +1,208 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimppaintinfo.h" + +#include "gimpinkoptions.h" +#include "gimpink-blob.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_SIZE, + PROP_TILT_ANGLE, + PROP_SIZE_SENSITIVITY, + PROP_VEL_SENSITIVITY, + PROP_TILT_SENSITIVITY, + PROP_BLOB_TYPE, + PROP_BLOB_ASPECT, + PROP_BLOB_ANGLE +}; + + +static void gimp_ink_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_ink_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpInkOptions, gimp_ink_options, GIMP_TYPE_PAINT_OPTIONS) + + +static void +gimp_ink_options_class_init (GimpInkOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_ink_options_set_property; + object_class->get_property = gimp_ink_options_get_property; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SIZE, + "size", + _("Size"), + _("Ink Blob Size"), + 0.0, 200.0, 16.0, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_TILT_ANGLE, + "tilt-angle", + _("Angle"), + NULL, + -90.0, 90.0, 0.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SIZE_SENSITIVITY, + "size-sensitivity", + _("Size"), + NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_VEL_SENSITIVITY, + "vel-sensitivity", + _("Speed"), + NULL, + 0.0, 1.0, 0.8, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_TILT_SENSITIVITY, + "tilt-sensitivity", + _("Tilt"), + NULL, + 0.0, 1.0, 0.4, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_ENUM (object_class, PROP_BLOB_TYPE, + "blob-type", + _("Shape"), + NULL, + GIMP_TYPE_INK_BLOB_TYPE, + GIMP_INK_BLOB_TYPE_CIRCLE, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BLOB_ASPECT, + "blob-aspect", + _("Aspect ratio"), + _("Ink Blob Aspect Ratio"), + 1.0, 10.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BLOB_ANGLE, + "blob-angle", + _("Angle"), + _("Ink Blob Angle"), + -G_PI, G_PI, 0.0, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_ink_options_init (GimpInkOptions *options) +{ +} + +static void +gimp_ink_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpInkOptions *options = GIMP_INK_OPTIONS (object); + + switch (property_id) + { + case PROP_SIZE: + options->size = g_value_get_double (value); + break; + case PROP_TILT_ANGLE: + options->tilt_angle = g_value_get_double (value); + break; + case PROP_SIZE_SENSITIVITY: + options->size_sensitivity = g_value_get_double (value); + break; + case PROP_VEL_SENSITIVITY: + options->vel_sensitivity = g_value_get_double (value); + break; + case PROP_TILT_SENSITIVITY: + options->tilt_sensitivity = g_value_get_double (value); + break; + case PROP_BLOB_TYPE: + options->blob_type = g_value_get_enum (value); + break; + case PROP_BLOB_ASPECT: + options->blob_aspect = g_value_get_double (value); + break; + case PROP_BLOB_ANGLE: + options->blob_angle = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_ink_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpInkOptions *options = GIMP_INK_OPTIONS (object); + + switch (property_id) + { + case PROP_SIZE: + g_value_set_double (value, options->size); + break; + case PROP_TILT_ANGLE: + g_value_set_double (value, options->tilt_angle); + break; + case PROP_SIZE_SENSITIVITY: + g_value_set_double (value, options->size_sensitivity); + break; + case PROP_VEL_SENSITIVITY: + g_value_set_double (value, options->vel_sensitivity); + break; + case PROP_TILT_SENSITIVITY: + g_value_set_double (value, options->tilt_sensitivity); + break; + case PROP_BLOB_TYPE: + g_value_set_enum (value, options->blob_type); + break; + case PROP_BLOB_ASPECT: + g_value_set_double (value, options->blob_aspect); + break; + case PROP_BLOB_ANGLE: + g_value_set_double (value, options->blob_angle); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} diff --git a/app/paint/gimpinkoptions.h b/app/paint/gimpinkoptions.h new file mode 100644 index 0000000..7776b60 --- /dev/null +++ b/app/paint/gimpinkoptions.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 . + */ + +#ifndef __GIMP_INK_OPTIONS_H__ +#define __GIMP_INK_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_INK_OPTIONS (gimp_ink_options_get_type ()) +#define GIMP_INK_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INK_OPTIONS, GimpInkOptions)) +#define GIMP_INK_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INK_OPTIONS, GimpInkOptionsClass)) +#define GIMP_IS_INK_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INK_OPTIONS)) +#define GIMP_IS_INK_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INK_OPTIONS)) +#define GIMP_INK_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INK_OPTIONS, GimpInkOptionsClass)) + + +typedef struct _GimpInkOptionsClass GimpInkOptionsClass; + +struct _GimpInkOptions +{ + GimpPaintOptions parent_instance; + + gdouble size; + gdouble tilt_angle; + + gdouble size_sensitivity; + gdouble vel_sensitivity; + gdouble tilt_sensitivity; + + GimpInkBlobType blob_type; + gdouble blob_aspect; + gdouble blob_angle; +}; + +struct _GimpInkOptionsClass +{ + GimpPaintOptionsClass parent_instance; +}; + + +GType gimp_ink_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_INK_OPTIONS_H__ */ diff --git a/app/paint/gimpinkundo.c b/app/paint/gimpinkundo.c new file mode 100644 index 0000000..a0b604b --- /dev/null +++ b/app/paint/gimpinkundo.c @@ -0,0 +1,125 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include + +#include "paint-types.h" + +#include "gimpink.h" +#include "gimpink-blob.h" +#include "gimpinkundo.h" + + +static void gimp_ink_undo_constructed (GObject *object); + +static void gimp_ink_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum); +static void gimp_ink_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode); + + +G_DEFINE_TYPE (GimpInkUndo, gimp_ink_undo, GIMP_TYPE_PAINT_CORE_UNDO) + +#define parent_class gimp_ink_undo_parent_class + + +static void +gimp_ink_undo_class_init (GimpInkUndoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass); + + object_class->constructed = gimp_ink_undo_constructed; + + undo_class->pop = gimp_ink_undo_pop; + undo_class->free = gimp_ink_undo_free; +} + +static void +gimp_ink_undo_init (GimpInkUndo *undo) +{ + undo->last_blobs = NULL; +} + +static void +gimp_ink_undo_constructed (GObject *object) +{ + GimpInkUndo *ink_undo = GIMP_INK_UNDO (object); + GimpInk *ink; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + gimp_assert (GIMP_IS_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core)); + + ink = GIMP_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core); + + if (ink->start_blobs) + { + gint i; + GimpBlob *blob; + + for (i = 0; i < g_list_length (ink->start_blobs); i++) + { + blob = g_list_nth_data (ink->start_blobs, i); + + ink_undo->last_blobs = g_list_prepend (ink_undo->last_blobs, + gimp_blob_duplicate (blob)); + } + ink_undo->last_blobs = g_list_reverse (ink_undo->last_blobs); + } +} + +static void +gimp_ink_undo_pop (GimpUndo *undo, + GimpUndoMode undo_mode, + GimpUndoAccumulator *accum) +{ + GimpInkUndo *ink_undo = GIMP_INK_UNDO (undo); + + GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); + + if (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core) + { + GimpInk *ink = GIMP_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core); + GList *tmp_blobs; + + tmp_blobs = ink->last_blobs; + ink->last_blobs = ink_undo->last_blobs; + ink_undo->last_blobs = tmp_blobs; + } +} + +static void +gimp_ink_undo_free (GimpUndo *undo, + GimpUndoMode undo_mode) +{ + GimpInkUndo *ink_undo = GIMP_INK_UNDO (undo); + + if (ink_undo->last_blobs) + { + g_list_free_full (ink_undo->last_blobs, g_free); + ink_undo->last_blobs = NULL; + } + + GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); +} diff --git a/app/paint/gimpinkundo.h b/app/paint/gimpinkundo.h new file mode 100644 index 0000000..59abf02 --- /dev/null +++ b/app/paint/gimpinkundo.h @@ -0,0 +1,52 @@ +/* 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 . + */ + +#ifndef __GIMP_INK_UNDO_H__ +#define __GIMP_INK_UNDO_H__ + + +#include "gimppaintcoreundo.h" + + +#define GIMP_TYPE_INK_UNDO (gimp_ink_undo_get_type ()) +#define GIMP_INK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INK_UNDO, GimpInkUndo)) +#define GIMP_INK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INK_UNDO, GimpInkUndoClass)) +#define GIMP_IS_INK_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INK_UNDO)) +#define GIMP_IS_INK_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INK_UNDO)) +#define GIMP_INK_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INK_UNDO, GimpInkUndoClass)) + + +typedef struct _GimpInkUndo GimpInkUndo; +typedef struct _GimpInkUndoClass GimpInkUndoClass; + +struct _GimpInkUndo +{ + GimpPaintCoreUndo parent_instance; + + GList *last_blobs; +}; + +struct _GimpInkUndoClass +{ + GimpPaintCoreUndoClass parent_class; +}; + + +GType gimp_ink_undo_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_INK_UNDO_H__ */ diff --git a/app/paint/gimpmybrushcore.c b/app/paint/gimpmybrushcore.c new file mode 100644 index 0000000..9e96821 --- /dev/null +++ b/app/paint/gimpmybrushcore.c @@ -0,0 +1,421 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include + +#include "libgimpmath/gimpmath.h" +#include "libgimpcolor/gimpcolor.h" + +#include "paint-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimp.h" +#include "core/gimp-palettes.h" +#include "core/gimpdrawable.h" +#include "core/gimperror.h" +#include "core/gimpmybrush.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" + +#include "gimpmybrushcore.h" +#include "gimpmybrushsurface.h" +#include "gimpmybrushoptions.h" + +#include "gimp-intl.h" + + +struct _GimpMybrushCorePrivate +{ + GimpMybrush *mybrush; + GimpMybrushSurface *surface; + GList *brushes; + gboolean synthetic; + gint64 last_time; +}; + + +/* local function prototypes */ + +static void gimp_mybrush_core_finalize (GObject *object); + +static gboolean gimp_mybrush_core_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error); +static void gimp_mybrush_core_interpolate (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + guint32 time); +static void gimp_mybrush_core_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); +static void gimp_mybrush_core_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + guint32 time); +static void gimp_mybrush_core_create_brushes (GimpMybrushCore *mybrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpMybrushCore, gimp_mybrush_core, + GIMP_TYPE_PAINT_CORE) + +#define parent_class gimp_mybrush_core_parent_class + + +void +gimp_mybrush_core_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_MYBRUSH_CORE, + GIMP_TYPE_MYBRUSH_OPTIONS, + "gimp-mybrush", + _("Mybrush"), + "gimp-tool-mypaint-brush"); +} + +static void +gimp_mybrush_core_class_init (GimpMybrushCoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + + object_class->finalize = gimp_mybrush_core_finalize; + + paint_core_class->start = gimp_mybrush_core_start; + paint_core_class->paint = gimp_mybrush_core_paint; + paint_core_class->interpolate = gimp_mybrush_core_interpolate; +} + +static void +gimp_mybrush_core_init (GimpMybrushCore *mybrush) +{ + mybrush->private = gimp_mybrush_core_get_instance_private (mybrush); +} + +static void +gimp_mybrush_core_finalize (GObject *object) +{ + GimpMybrushCore *core = GIMP_MYBRUSH_CORE (object); + + if (core->private->brushes) + { + g_list_free_full (core->private->brushes, + (GDestroyNotify) mypaint_brush_unref); + core->private->brushes = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gimp_mybrush_core_start (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + const GimpCoords *coords, + GError **error) +{ + GimpMybrushCore *core = GIMP_MYBRUSH_CORE (paint_core); + GimpContext *context = GIMP_CONTEXT (paint_options); + + core->private->mybrush = gimp_context_get_mybrush (context); + + if (! core->private->mybrush) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No MyPaint brushes available for use with this tool.")); + return FALSE; + } + + return TRUE; +} + +static void +gimp_mybrush_core_interpolate (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + guint32 time) +{ + GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core); + + /* If this is the first motion the brush has received then + * we're being asked to draw a synthetic stroke in line mode + */ + if (mybrush->private->last_time < 0) + { + GimpCoords saved_coords = paint_core->cur_coords; + + paint_core->cur_coords = paint_core->last_coords; + + mybrush->private->synthetic = TRUE; + + gimp_paint_core_paint (paint_core, drawable, paint_options, + GIMP_PAINT_STATE_MOTION, time); + + paint_core->cur_coords = saved_coords; + } + + gimp_paint_core_paint (paint_core, drawable, paint_options, + GIMP_PAINT_STATE_MOTION, time); + + paint_core->last_coords = paint_core->cur_coords; +} + +static void +gimp_mybrush_core_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpRGB fg; + + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + gimp_context_get_foreground (context, &fg); + gimp_palettes_add_color_history (context->gimp, &fg); + gimp_symmetry_set_stateful (sym, TRUE); + + mybrush->private->surface = + gimp_mypaint_surface_new (gimp_drawable_get_buffer (drawable), + gimp_drawable_get_active_mask (drawable), + paint_core->mask_buffer, + paint_core->mask_x_offset, + paint_core->mask_y_offset, + GIMP_MYBRUSH_OPTIONS (paint_options)); + + gimp_mybrush_core_create_brushes (mybrush, drawable, paint_options, sym); + + mybrush->private->last_time = -1; + mybrush->private->synthetic = FALSE; + break; + + case GIMP_PAINT_STATE_MOTION: + gimp_mybrush_core_motion (paint_core, drawable, paint_options, + sym, time); + break; + + case GIMP_PAINT_STATE_FINISH: + gimp_symmetry_set_stateful (sym, FALSE); + mypaint_surface_unref ((MyPaintSurface *) mybrush->private->surface); + mybrush->private->surface = NULL; + + g_list_free_full (mybrush->private->brushes, + (GDestroyNotify) mypaint_brush_unref); + mybrush->private->brushes = NULL; + break; + } +} + +static void +gimp_mybrush_core_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + guint32 time) +{ + GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core); + MyPaintRectangle rect; + GList *iter; + gdouble dt = 0.0; + gint n_strokes; + gint i; + + n_strokes = gimp_symmetry_get_size (sym); + + /* The number of strokes may change during a motion, depending on + * the type of symmetry. When that happens, reset the brushes. + */ + if (g_list_length (mybrush->private->brushes) != n_strokes) + { + gimp_mybrush_core_create_brushes (mybrush, drawable, paint_options, sym); + } + + mypaint_surface_begin_atomic ((MyPaintSurface *) mybrush->private->surface); + + if (mybrush->private->last_time < 0) + { + /* First motion, so we need zero pressure events to start the strokes */ + for (iter = mybrush->private->brushes, i = 0; + iter; + iter = g_list_next (iter), i++) + { + MyPaintBrush *brush = iter->data; + GimpCoords *coords = gimp_symmetry_get_coords (sym, i); + + mypaint_brush_stroke_to (brush, + (MyPaintSurface *) mybrush->private->surface, + coords->x, + coords->y, + 0.0f, + coords->xtilt, + coords->ytilt, + 1.0f /* Pretend the cursor hasn't moved in a while */); + } + + dt = 0.015; + } + else if (mybrush->private->synthetic) + { + GimpVector2 v = { paint_core->cur_coords.x - paint_core->last_coords.x, + paint_core->cur_coords.y - paint_core->last_coords.y }; + + dt = 0.0005 * gimp_vector2_length_val (v); + } + else + { + dt = (time - mybrush->private->last_time) * 0.001; + } + + for (iter = mybrush->private->brushes, i = 0; + iter; + iter = g_list_next (iter), i++) + { + MyPaintBrush *brush = iter->data; + GimpCoords *coords = gimp_symmetry_get_coords (sym, i); + gdouble pressure = coords->pressure; + + /* libmypaint expects non-extended devices to default to 0.5 pressure */ + if (! coords->extended) + pressure = 0.5f; + + mypaint_brush_stroke_to (brush, + (MyPaintSurface *) mybrush->private->surface, + coords->x, + coords->y, + pressure, + coords->xtilt, + coords->ytilt, + dt); + } + + mybrush->private->last_time = time; + + mypaint_surface_end_atomic ((MyPaintSurface *) mybrush->private->surface, + &rect); + + if (rect.width > 0 && rect.height > 0) + { + paint_core->x1 = MIN (paint_core->x1, rect.x); + paint_core->y1 = MIN (paint_core->y1, rect.y); + paint_core->x2 = MAX (paint_core->x2, rect.x + rect.width); + paint_core->y2 = MAX (paint_core->y2, rect.y + rect.height); + + gimp_drawable_update (drawable, rect.x, rect.y, rect.width, rect.height); + } +} + +static void +gimp_mybrush_core_create_brushes (GimpMybrushCore *mybrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym) +{ + GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpRGB fg; + GimpHSV hsv; + gint n_strokes; + gint i; + + if (mybrush->private->brushes) + { + g_list_free_full (mybrush->private->brushes, + (GDestroyNotify) mypaint_brush_unref); + mybrush->private->brushes = NULL; + } + + if (options->eraser) + gimp_context_get_background (context, &fg); + else + gimp_context_get_foreground (context, &fg); + + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + &fg, &fg); + gimp_rgb_to_hsv (&fg, &hsv); + + n_strokes = gimp_symmetry_get_size (sym); + + for (i = 0; i < n_strokes; i++) + { + MyPaintBrush *brush = mypaint_brush_new (); + const gchar *brush_data; + + mypaint_brush_from_defaults (brush); + brush_data = gimp_mybrush_get_brush_json (mybrush->private->mybrush); + if (brush_data) + mypaint_brush_from_string (brush, brush_data); + + if (! mypaint_brush_get_base_value (brush, + MYPAINT_BRUSH_SETTING_RESTORE_COLOR)) + { + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_COLOR_H, + hsv.h); + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_COLOR_S, + hsv.s); + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_COLOR_V, + hsv.v); + } + + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, + options->radius); + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_OPAQUE, + options->opaque * + gimp_context_get_opacity (context)); + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_HARDNESS, + options->hardness); + mypaint_brush_set_base_value (brush, + MYPAINT_BRUSH_SETTING_ERASER, + (options->eraser && + gimp_drawable_has_alpha (drawable)) ? + 1.0f : 0.0f); + + mypaint_brush_new_stroke (brush); + + mybrush->private->brushes = g_list_prepend (mybrush->private->brushes, + brush); + } + + mybrush->private->brushes = g_list_reverse (mybrush->private->brushes); +} diff --git a/app/paint/gimpmybrushcore.h b/app/paint/gimpmybrushcore.h new file mode 100644 index 0000000..9dadfb8 --- /dev/null +++ b/app/paint/gimpmybrushcore.h @@ -0,0 +1,55 @@ +/* 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 . + */ + +#ifndef __GIMP_MYBRUSH_CORE_H__ +#define __GIMP_MYBRUSH_CORE_H__ + + +#include "gimppaintcore.h" + + +#define GIMP_TYPE_MYBRUSH_CORE (gimp_mybrush_core_get_type ()) +#define GIMP_MYBRUSH_CORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MYBRUSH_CORE, GimpMybrushCore)) +#define GIMP_MYBRUSH_CORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MYBRUSH_CORE, GimpMybrushCoreClass)) +#define GIMP_IS_MYBRUSH_CORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MYBRUSH_CORE)) +#define GIMP_IS_MYBRUSH_CORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MYBRUSH_CORE)) +#define GIMP_MYBRUSH_CORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MYBRUSH_CORE, GimpMybrushCoreClass)) + + +typedef struct _GimpMybrushCorePrivate GimpMybrushCorePrivate; +typedef struct _GimpMybrushCoreClass GimpMybrushCoreClass; + +struct _GimpMybrushCore +{ + GimpPaintCore parent_instance; + + GimpMybrushCorePrivate *private; +}; + +struct _GimpMybrushCoreClass +{ + GimpPaintCoreClass parent_class; +}; + + +void gimp_mybrush_core_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_mybrush_core_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_MYBRUSH_CORE_H__ */ diff --git a/app/paint/gimpmybrushoptions.c b/app/paint/gimpmybrushoptions.c new file mode 100644 index 0000000..40fa517 --- /dev/null +++ b/app/paint/gimpmybrushoptions.c @@ -0,0 +1,219 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include + +#include "libgimpconfig/gimpconfig.h" + +#include "paint-types.h" + +#include "core/gimp.h" +#include "core/gimpdrawable.h" +#include "core/gimpmybrush.h" +#include "core/gimppaintinfo.h" + +#include "gimpmybrushoptions.h" + +#include "gimp-intl.h" + + +enum +{ + PROP_0, + PROP_RADIUS, + PROP_OPAQUE, + PROP_HARDNESS, + PROP_ERASER, + PROP_NO_ERASING +}; + + +static void gimp_mybrush_options_config_iface_init (GimpConfigInterface *config_iface); + +static void gimp_mybrush_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_mybrush_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_mybrush_options_mybrush_changed (GimpContext *context, + GimpMybrush *brush); + +static void gimp_mybrush_options_reset (GimpConfig *config); + + +G_DEFINE_TYPE_WITH_CODE (GimpMybrushOptions, gimp_mybrush_options, + GIMP_TYPE_PAINT_OPTIONS, + G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, + gimp_mybrush_options_config_iface_init)) + +static GimpConfigInterface *parent_config_iface = NULL; + + +static void +gimp_mybrush_options_class_init (GimpMybrushOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpContextClass *context_class = GIMP_CONTEXT_CLASS (klass); + + object_class->set_property = gimp_mybrush_options_set_property; + object_class->get_property = gimp_mybrush_options_get_property; + + context_class->mybrush_changed = gimp_mybrush_options_mybrush_changed; + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_RADIUS, + "radius", + _("Radius"), + NULL, + -2.0, 6.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OPAQUE, + "opaque", + _("Base Opacity"), + NULL, + 0.0, 2.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HARDNESS, + "hardness", + _("Hardness"), + NULL, + 0.0, 1.0, 1.0, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ERASER, + "eraser", + _("Erase with this brush"), + NULL, + FALSE, + GIMP_PARAM_STATIC_STRINGS); + + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_NO_ERASING, + "no-erasing", + _("No erasing effect"), + _("Never decrease alpha of existing pixels"), + FALSE, + GIMP_PARAM_STATIC_STRINGS); +} + +static void +gimp_mybrush_options_config_iface_init (GimpConfigInterface *config_iface) +{ + parent_config_iface = g_type_interface_peek_parent (config_iface); + + config_iface->reset = gimp_mybrush_options_reset; +} + +static void +gimp_mybrush_options_init (GimpMybrushOptions *options) +{ +} + +static void +gimp_mybrush_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (object); + + switch (property_id) + { + case PROP_RADIUS: + options->radius = g_value_get_double (value); + break; + case PROP_HARDNESS: + options->hardness = g_value_get_double (value); + break; + case PROP_OPAQUE: + options->opaque = g_value_get_double (value); + break; + case PROP_ERASER: + options->eraser = g_value_get_boolean (value); + break; + case PROP_NO_ERASING: + options->no_erasing = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mybrush_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (object); + + switch (property_id) + { + case PROP_RADIUS: + g_value_set_double (value, options->radius); + break; + case PROP_OPAQUE: + g_value_set_double (value, options->opaque); + break; + case PROP_HARDNESS: + g_value_set_double (value, options->hardness); + break; + case PROP_ERASER: + g_value_set_boolean (value, options->eraser); + break; + case PROP_NO_ERASING: + g_value_set_boolean (value, options->no_erasing); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_mybrush_options_mybrush_changed (GimpContext *context, + GimpMybrush *brush) +{ + if (brush) + g_object_set (context, + "radius", gimp_mybrush_get_radius (brush), + "opaque", gimp_mybrush_get_opaque (brush), + "hardness", gimp_mybrush_get_hardness (brush), + "eraser", gimp_mybrush_get_is_eraser (brush), + NULL); +} + +static void +gimp_mybrush_options_reset (GimpConfig *config) +{ + GimpContext *context = GIMP_CONTEXT (config); + GimpMybrush *brush = gimp_context_get_mybrush (context); + + parent_config_iface->reset (config); + + gimp_mybrush_options_mybrush_changed (context, brush); +} diff --git a/app/paint/gimpmybrushoptions.h b/app/paint/gimpmybrushoptions.h new file mode 100644 index 0000000..b1b5cd5 --- /dev/null +++ b/app/paint/gimpmybrushoptions.h @@ -0,0 +1,55 @@ +/* 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 . + */ + +#ifndef __GIMP_MYBRUSH_OPTIONS_H__ +#define __GIMP_MYBRUSH_OPTIONS_H__ + + +#include "gimppaintoptions.h" + + +#define GIMP_TYPE_MYBRUSH_OPTIONS (gimp_mybrush_options_get_type ()) +#define GIMP_MYBRUSH_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MYBRUSH_OPTIONS, GimpMybrushOptions)) +#define GIMP_MYBRUSH_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MYBRUSH_OPTIONS, GimpMybrushOptionsClass)) +#define GIMP_IS_MYBRUSH_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MYBRUSH_OPTIONS)) +#define GIMP_IS_MYBRUSH_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MYBRUSH_OPTIONS)) +#define GIMP_MYBRUSH_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MYBRUSH_OPTIONS, GimpMybrushOptionsClass)) + + +typedef struct _GimpMybrushOptionsClass GimpMybrushOptionsClass; + +struct _GimpMybrushOptions +{ + GimpPaintOptions parent_instance; + + gdouble radius; + gdouble opaque; + gdouble hardness; + gboolean eraser; + gboolean no_erasing; +}; + +struct _GimpMybrushOptionsClass +{ + GimpPaintOptionsClass parent_instance; +}; + + +GType gimp_mybrush_options_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_MYBRUSH_OPTIONS_H__ */ diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c new file mode 100644 index 0000000..9b4283c --- /dev/null +++ b/app/paint/gimpmybrushsurface.c @@ -0,0 +1,560 @@ +/* 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 . + */ + +#include "config.h" +#include + +#include + +#include "paint-types.h" + +#include "libgimpmath/gimpmath.h" + +#include +#include +#include "libgimpcolor/gimpcolor.h" + +#include "gimpmybrushoptions.h" +#include "gimpmybrushsurface.h" + + +struct _GimpMybrushSurface +{ + MyPaintSurface surface; + GeglBuffer *buffer; + GeglBuffer *paint_mask; + gint paint_mask_x; + gint paint_mask_y; + GeglRectangle dirty; + GimpComponentMask component_mask; + GimpMybrushOptions *options; +}; + +/* --- Taken from mypaint-tiled-surface.c --- */ +static inline float +calculate_rr (int xp, + int yp, + float x, + float y, + float aspect_ratio, + float sn, + float cs, + float one_over_radius2) +{ + /* code duplication, see brush::count_dabs_to() */ + const float yy = (yp + 0.5f - y); + const float xx = (xp + 0.5f - x); + const float yyr=(yy*cs-xx*sn)*aspect_ratio; + const float xxr=yy*sn+xx*cs; + const float rr = (yyr*yyr + xxr*xxr) * one_over_radius2; + /* rr is in range 0.0..1.0*sqrt(2) */ + return rr; +} + +static inline float +calculate_r_sample (float x, + float y, + float aspect_ratio, + float sn, + float cs) +{ + const float yyr=(y*cs-x*sn)*aspect_ratio; + const float xxr=y*sn+x*cs; + const float r = (yyr*yyr + xxr*xxr); + return r; +} + +static inline float +sign_point_in_line (float px, + float py, + float vx, + float vy) +{ + return (px - vx) * (-vy) - (vx) * (py - vy); +} + +static inline void +closest_point_to_line (float lx, + float ly, + float px, + float py, + float *ox, + float *oy) +{ + const float l2 = lx*lx + ly*ly; + const float ltp_dot = px*lx + py*ly; + const float t = ltp_dot / l2; + *ox = lx * t; + *oy = ly * t; +} + + +/* This works by taking the visibility at the nearest point + * and dividing by 1.0 + delta. + * + * - nearest point: point where the dab has more influence + * - farthest point: point at a fixed distance away from + * the nearest point + * - delta: how much occluded is the farthest point relative + * to the nearest point + */ +static inline float +calculate_rr_antialiased (int xp, + int yp, + float x, + float y, + float aspect_ratio, + float sn, + float cs, + float one_over_radius2, + float r_aa_start) +{ + /* calculate pixel position and borders in a way + * that the dab's center is always at zero */ + float pixel_right = x - (float)xp; + float pixel_bottom = y - (float)yp; + float pixel_center_x = pixel_right - 0.5f; + float pixel_center_y = pixel_bottom - 0.5f; + float pixel_left = pixel_right - 1.0f; + float pixel_top = pixel_bottom - 1.0f; + + float nearest_x, nearest_y; /* nearest to origin, but still inside pixel */ + float farthest_x, farthest_y; /* farthest from origin, but still inside pixel */ + float r_near, r_far, rr_near, rr_far; + float center_sign, rad_area_1, visibilityNear, delta, delta2; + + /* Dab's center is inside pixel? */ + if( pixel_left<0 && pixel_right>0 && + pixel_top<0 && pixel_bottom>0 ) + { + nearest_x = 0; + nearest_y = 0; + r_near = rr_near = 0; + } + else + { + closest_point_to_line( cs, sn, pixel_center_x, pixel_center_y, &nearest_x, &nearest_y ); + nearest_x = CLAMP( nearest_x, pixel_left, pixel_right ); + nearest_y = CLAMP( nearest_y, pixel_top, pixel_bottom ); + /* XXX: precision of "nearest" values could be improved + * by intersecting the line that goes from nearest_x/Y to 0 + * with the pixel's borders here, however the improvements + * would probably not justify the perdormance cost. + */ + r_near = calculate_r_sample( nearest_x, nearest_y, aspect_ratio, sn, cs ); + rr_near = r_near * one_over_radius2; + } + + /* out of dab's reach? */ + if( rr_near > 1.0f ) + return rr_near; + + /* check on which side of the dab's line is the pixel center */ + center_sign = sign_point_in_line( pixel_center_x, pixel_center_y, cs, -sn ); + + /* radius of a circle with area=1 + * A = pi * r * r + * r = sqrt(1/pi) + */ + rad_area_1 = sqrtf( 1.0f / M_PI ); + + /* center is below dab */ + if( center_sign < 0 ) + { + farthest_x = nearest_x - sn*rad_area_1; + farthest_y = nearest_y + cs*rad_area_1; + } + /* above dab */ + else + { + farthest_x = nearest_x + sn*rad_area_1; + farthest_y = nearest_y - cs*rad_area_1; + } + + r_far = calculate_r_sample( farthest_x, farthest_y, aspect_ratio, sn, cs ); + rr_far = r_far * one_over_radius2; + + /* check if we can skip heavier AA */ + if( r_far < r_aa_start ) + return (rr_far+rr_near) * 0.5f; + + /* calculate AA approximate */ + visibilityNear = 1.0f - rr_near; + delta = rr_far - rr_near; + delta2 = 1.0f + delta; + visibilityNear /= delta2; + + return 1.0f - visibilityNear; +} +/* -- end mypaint code */ + +static inline float +calculate_alpha_for_rr (float rr, + float hardness, + float slope1, + float slope2) +{ + if (rr > 1.0f) + return 0.0f; + else if (rr <= hardness) + return 1.0f + rr * slope1; + else + return rr * slope2 - slope2; +} + +static GeglRectangle +calculate_dab_roi (float x, + float y, + float radius) +{ + int x0 = floor (x - radius); + int x1 = ceil (x + radius); + int y0 = floor (y - radius); + int y1 = ceil (y + radius); + + return *GEGL_RECTANGLE (x0, y0, x1 - x0, y1 - y0); +} + +static void +gimp_mypaint_surface_get_color (MyPaintSurface *base_surface, + float x, + float y, + float radius, + float *color_r, + float *color_g, + float *color_b, + float *color_a) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + GeglRectangle dabRect; + + if (radius < 1.0f) + radius = 1.0f; + + dabRect = calculate_dab_roi (x, y, radius); + + *color_r = 0.0f; + *color_g = 0.0f; + *color_b = 0.0f; + *color_a = 0.0f; + + if (dabRect.width > 0 || dabRect.height > 0) + { + const float one_over_radius2 = 1.0f / (radius * radius); + float sum_weight = 0.0f; + float sum_r = 0.0f; + float sum_g = 0.0f; + float sum_b = 0.0f; + float sum_a = 0.0f; + + /* Read in clamp mode to avoid transparency bleeding in at the edges */ + GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, + babl_format ("R'aG'aB'aA float"), + GEGL_BUFFER_READ, + GEGL_ABYSS_CLAMP, 2); + if (surface->paint_mask) + { + GeglRectangle mask_roi = dabRect; + mask_roi.x -= surface->paint_mask_x; + mask_roi.y -= surface->paint_mask_y; + gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + } + + while (gegl_buffer_iterator_next (iter)) + { + float *pixel = (float *)iter->items[0].data; + float *mask; + int iy, ix; + + if (surface->paint_mask) + mask = iter->items[1].data; + else + mask = NULL; + + for (iy = iter->items[0].roi.y; iy < iter->items[0].roi.y + iter->items[0].roi.height; iy++) + { + float yy = (iy + 0.5f - y); + for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++) + { + /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */ + float xx = (ix + 0.5f - x); + float rr = (yy * yy + xx * xx) * one_over_radius2; + float pixel_weight = 0.0f; + if (rr <= 1.0f) + pixel_weight = 1.0f - rr; + if (mask) + pixel_weight *= *mask; + + sum_r += pixel_weight * pixel[RED]; + sum_g += pixel_weight * pixel[GREEN]; + sum_b += pixel_weight * pixel[BLUE]; + sum_a += pixel_weight * pixel[ALPHA]; + sum_weight += pixel_weight; + + pixel += 4; + if (mask) + mask += 1; + } + } + } + + if (sum_a > 0.0f && sum_weight > 0.0f) + { + sum_r /= sum_weight; + sum_g /= sum_weight; + sum_b /= sum_weight; + sum_a /= sum_weight; + + sum_r /= sum_a; + sum_g /= sum_a; + sum_b /= sum_a; + + /* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */ + *color_r = CLAMP(sum_r, 0.0f, 1.0f); + *color_g = CLAMP(sum_g, 0.0f, 1.0f); + *color_b = CLAMP(sum_b, 0.0f, 1.0f); + *color_a = CLAMP(sum_a, 0.0f, 1.0f); + } + } + +} + +static int +gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface, + float x, + float y, + float radius, + float color_r, + float color_g, + float color_b, + float opaque, + float hardness, + float color_a, + float aspect_ratio, + float angle, + float lock_alpha, + float colorize) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + GeglBufferIterator *iter; + GeglRectangle dabRect; + GimpComponentMask component_mask = surface->component_mask; + + const float one_over_radius2 = 1.0f / (radius * radius); + const double angle_rad = angle / 360 * 2 * M_PI; + const float cs = cos(angle_rad); + const float sn = sin(angle_rad); + float normal_mode; + float segment1_slope; + float segment2_slope; + float r_aa_start; + + hardness = CLAMP (hardness, 0.0f, 1.0f); + segment1_slope = -(1.0f / hardness - 1.0f); + segment2_slope = -hardness / (1.0f - hardness); + aspect_ratio = MAX (1.0f, aspect_ratio); + + r_aa_start = radius - 1.0f; + r_aa_start = MAX (r_aa_start, 0); + r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio; + + normal_mode = opaque * (1.0f - colorize); + colorize = opaque * colorize; + + /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */ + dabRect = calculate_dab_roi (x, y, radius); + gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer)); + + if (dabRect.width <= 0 || dabRect.height <= 0) + return 0; + + gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect); + + iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, + babl_format ("R'G'B'A float"), + GEGL_BUFFER_READWRITE, + GEGL_ABYSS_NONE, 2); + if (surface->paint_mask) + { + GeglRectangle mask_roi = dabRect; + mask_roi.x -= surface->paint_mask_x; + mask_roi.y -= surface->paint_mask_y; + gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0, + babl_format ("Y float"), + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + } + + while (gegl_buffer_iterator_next (iter)) + { + float *pixel = (float *)iter->items[0].data; + float *mask; + int iy, ix; + + if (surface->paint_mask) + mask = iter->items[1].data; + else + mask = NULL; + + for (iy = iter->items[0].roi.y; iy < iter->items[0].roi.y + iter->items[0].roi.height; iy++) + { + for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++) + { + float rr, base_alpha, alpha, dst_alpha, r, g, b, a; + if (radius < 3.0f) + rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start); + else + rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2); + base_alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope); + alpha = base_alpha * normal_mode; + if (mask) + alpha *= *mask; + dst_alpha = pixel[ALPHA]; + /* a = alpha * color_a + dst_alpha * (1.0f - alpha); + * which converts to: */ + a = alpha * (color_a - dst_alpha) + dst_alpha; + r = pixel[RED]; + g = pixel[GREEN]; + b = pixel[BLUE]; + + if (a > 0.0f) + { + /* By definition the ratio between each color[] and pixel[] component in a non-pre-multipled blend always sums to 1.0f. + * Originally this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha * (1.0f - alpha)) / a", + * instead we only calculate the cheaper term. */ + float src_term = (alpha * color_a) / a; + float dst_term = 1.0f - src_term; + r = color_r * src_term + r * dst_term; + g = color_g * src_term + g * dst_term; + b = color_b * src_term + b * dst_term; + } + + if (colorize > 0.0f && base_alpha > 0.0f) + { + alpha = base_alpha * colorize; + a = alpha + dst_alpha - alpha * dst_alpha; + if (a > 0.0f) + { + GimpHSL pixel_hsl, out_hsl; + GimpRGB pixel_rgb = {color_r, color_g, color_b}; + GimpRGB out_rgb = {r, g, b}; + float src_term = alpha / a; + float dst_term = 1.0f - src_term; + + gimp_rgb_to_hsl (&pixel_rgb, &pixel_hsl); + gimp_rgb_to_hsl (&out_rgb, &out_hsl); + + out_hsl.h = pixel_hsl.h; + out_hsl.s = pixel_hsl.s; + gimp_hsl_to_rgb (&out_hsl, &out_rgb); + + r = (float)out_rgb.r * src_term + r * dst_term; + g = (float)out_rgb.g * src_term + g * dst_term; + b = (float)out_rgb.b * src_term + b * dst_term; + } + } + + if (surface->options->no_erasing) + a = MAX (a, pixel[ALPHA]); + + if (component_mask != GIMP_COMPONENT_MASK_ALL) + { + if (component_mask & GIMP_COMPONENT_MASK_RED) + pixel[RED] = r; + if (component_mask & GIMP_COMPONENT_MASK_GREEN) + pixel[GREEN] = g; + if (component_mask & GIMP_COMPONENT_MASK_BLUE) + pixel[BLUE] = b; + if (component_mask & GIMP_COMPONENT_MASK_ALPHA) + pixel[ALPHA] = a; + } + else + { + pixel[RED] = r; + pixel[GREEN] = g; + pixel[BLUE] = b; + pixel[ALPHA] = a; + } + + pixel += 4; + if (mask) + mask += 1; + } + } + } + + return 1; +} + +static void +gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface) +{ + +} + +static void +gimp_mypaint_surface_end_atomic (MyPaintSurface *base_surface, + MyPaintRectangle *roi) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + + roi->x = surface->dirty.x; + roi->y = surface->dirty.y; + roi->width = surface->dirty.width; + roi->height = surface->dirty.height; + surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0); +} + +static void +gimp_mypaint_surface_destroy (MyPaintSurface *base_surface) +{ + GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; + + g_clear_object (&surface->buffer); + g_clear_object (&surface->paint_mask); +} + +GimpMybrushSurface * +gimp_mypaint_surface_new (GeglBuffer *buffer, + GimpComponentMask component_mask, + GeglBuffer *paint_mask, + gint paint_mask_x, + gint paint_mask_y, + GimpMybrushOptions *options) +{ + GimpMybrushSurface *surface = g_malloc0 (sizeof (GimpMybrushSurface)); + + mypaint_surface_init ((MyPaintSurface *)surface); + + surface->surface.get_color = gimp_mypaint_surface_get_color; + surface->surface.draw_dab = gimp_mypaint_surface_draw_dab; + surface->surface.begin_atomic = gimp_mypaint_surface_begin_atomic; + surface->surface.end_atomic = gimp_mypaint_surface_end_atomic; + surface->surface.destroy = gimp_mypaint_surface_destroy; + surface->component_mask = component_mask; + surface->options = options; + surface->buffer = g_object_ref (buffer); + if (paint_mask) + surface->paint_mask = g_object_ref (paint_mask); + + surface->paint_mask_x = paint_mask_x; + surface->paint_mask_y = paint_mask_y; + surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0); + + return surface; +} diff --git a/app/paint/gimpmybrushsurface.h b/app/paint/gimpmybrushsurface.h new file mode 100644 index 0000000..71bc0ba --- /dev/null +++ b/app/paint/gimpmybrushsurface.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 . + */ + +#ifndef __GIMP_MYBRUSH_SURFACE_H__ +#define __GIMP_MYBRUSH_SURFACE_H__ + + +typedef struct _GimpMybrushSurface GimpMybrushSurface; + +GimpMybrushSurface * +gimp_mypaint_surface_new (GeglBuffer *buffer, + GimpComponentMask component_mask, + GeglBuffer *paint_mask, + gint paint_mask_x, + gint paint_mask_y, + GimpMybrushOptions *options); + + +#endif /* __GIMP_MYBRUSH_SURFACE_H__ */ diff --git a/app/paint/gimppaintbrush.c b/app/paint/gimppaintbrush.c new file mode 100644 index 0000000..2afd13c --- /dev/null +++ b/app/paint/gimppaintbrush.c @@ -0,0 +1,395 @@ +/* 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpbase/gimpbase.h" + +#include "paint-types.h" + +#include "gegl/gimp-gegl-utils.h" + +#include "core/gimp.h" +#include "core/gimp-palettes.h" +#include "core/gimpbrush.h" +#include "core/gimpdrawable.h" +#include "core/gimpdynamics.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimppickable.h" +#include "core/gimpsymmetry.h" +#include "core/gimptempbuf.h" + +#include "gimppaintbrush.h" +#include "gimppaintoptions.h" + +#include "gimp-intl.h" + + +static void gimp_paintbrush_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time); + +static gboolean gimp_paintbrush_real_get_color_history_color (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color); +static void gimp_paintbrush_real_get_paint_params (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble grad_point, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); + + +G_DEFINE_TYPE (GimpPaintbrush, gimp_paintbrush, GIMP_TYPE_BRUSH_CORE) + + +void +gimp_paintbrush_register (Gimp *gimp, + GimpPaintRegisterCallback callback) +{ + (* callback) (gimp, + GIMP_TYPE_PAINTBRUSH, + GIMP_TYPE_PAINT_OPTIONS, + "gimp-paintbrush", + _("Paintbrush"), + "gimp-tool-paintbrush"); +} + +static void +gimp_paintbrush_class_init (GimpPaintbrushClass *klass) +{ + GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); + GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass); + + paint_core_class->paint = gimp_paintbrush_paint; + + brush_core_class->handles_changing_brush = TRUE; + + klass->get_color_history_color = gimp_paintbrush_real_get_color_history_color; + klass->get_paint_params = gimp_paintbrush_real_get_paint_params; +} + +static void +gimp_paintbrush_init (GimpPaintbrush *paintbrush) +{ +} + +static void +gimp_paintbrush_paint (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + GimpPaintState paint_state, + guint32 time) +{ + GimpPaintbrush *paintbrush = GIMP_PAINTBRUSH (paint_core); + + switch (paint_state) + { + case GIMP_PAINT_STATE_INIT: + { + GimpRGB color; + + if (GIMP_PAINTBRUSH_GET_CLASS (paintbrush)->get_color_history_color && + GIMP_PAINTBRUSH_GET_CLASS (paintbrush)->get_color_history_color ( + paintbrush, drawable, paint_options, &color)) + { + GimpContext *context = GIMP_CONTEXT (paint_options); + + gimp_palettes_add_color_history (context->gimp, &color); + } + } + break; + + case GIMP_PAINT_STATE_MOTION: + _gimp_paintbrush_motion (paint_core, drawable, paint_options, + sym, GIMP_OPACITY_OPAQUE); + break; + + case GIMP_PAINT_STATE_FINISH: + { + if (paintbrush->paint_buffer) + { + g_object_remove_weak_pointer ( + G_OBJECT (paintbrush->paint_buffer), + (gpointer) &paintbrush->paint_buffer); + + paintbrush->paint_buffer = NULL; + } + + g_clear_pointer (&paintbrush->paint_pixmap, gimp_temp_buf_unref); + } + break; + } +} + +static gboolean +gimp_paintbrush_real_get_color_history_color (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color) +{ + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paintbrush); + GimpDynamics *dynamics = gimp_context_get_dynamics (context); + + /* We don't save gradient color history and pixmap brushes + * have no color to save. + */ + if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_COLOR) || + (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush))) + { + return FALSE; + } + + gimp_context_get_foreground (context, color); + + return TRUE; +} + +static void +gimp_paintbrush_real_get_paint_params (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble grad_point, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color) +{ + GimpPaintCore *paint_core = GIMP_PAINT_CORE (paintbrush); + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paintbrush); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + + *paint_mode = gimp_context_get_paint_mode (context); + + if (gimp_paint_options_get_gradient_color (paint_options, image, + grad_point, + paint_core->pixel_dist, + paint_color)) + { + /* optionally take the color from the current gradient */ + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + paint_color, paint_color); + + *paint_appl_mode = GIMP_PAINT_INCREMENTAL; + } + else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush)) + { + /* otherwise check if the brush has a pixmap and use that to + * color the area + */ + *paint_pixmap = gimp_brush_core_get_brush_pixmap (brush_core); + + *paint_appl_mode = GIMP_PAINT_INCREMENTAL; + } + else + { + /* otherwise fill the area with the foreground color */ + gimp_context_get_foreground (context, paint_color); + + gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), + paint_color, paint_color); + } +} + +void +_gimp_paintbrush_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble opacity) +{ + GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); + GimpPaintbrush *paintbrush = GIMP_PAINTBRUSH (paint_core); + GimpContext *context = GIMP_CONTEXT (paint_options); + GimpDynamics *dynamics = brush_core->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + gdouble fade_point; + gdouble grad_point; + gdouble force; + const GimpCoords *coords; + gint n_strokes; + gint i; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + coords = gimp_symmetry_get_origin (sym); + /* Some settings are based on the original stroke. */ + opacity *= gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_OPACITY, + coords, + paint_options, + fade_point); + if (opacity == 0.0) + return; + + if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush) + { + gimp_brush_core_eval_transform_dynamics (brush_core, + drawable, + paint_options, + coords); + } + + grad_point = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_COLOR, + coords, + paint_options, + fade_point); + + n_strokes = gimp_symmetry_get_size (sym); + for (i = 0; i < n_strokes; i++) + { + GimpLayerMode paint_mode; + GimpPaintApplicationMode paint_appl_mode; + GeglBuffer *paint_buffer; + gint paint_buffer_x; + gint paint_buffer_y; + const GimpTempBuf *paint_pixmap = NULL; + GimpRGB paint_color; + gint paint_width, paint_height; + + paint_appl_mode = paint_options->application_mode; + + GIMP_PAINTBRUSH_GET_CLASS (paintbrush)->get_paint_params (paintbrush, + drawable, + paint_options, + sym, + grad_point, + &paint_mode, + &paint_appl_mode, + &paint_pixmap, + &paint_color); + + coords = gimp_symmetry_get_coords (sym, i); + + if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush) + gimp_brush_core_eval_transform_symmetry (brush_core, sym, i); + + paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, + paint_options, + paint_mode, + coords, + &paint_buffer_x, + &paint_buffer_y, + &paint_width, + &paint_height); + if (! paint_buffer) + continue; + + if (! paint_pixmap) + { + opacity *= paint_color.a; + gimp_rgb_set_alpha (&paint_color, GIMP_OPACITY_OPAQUE); + } + + /* fill the paint buffer. we can skip this step when reusing the + * previous paint buffer, if the paint color/pixmap hasn't changed + * (unless using an applicator, which currently modifies the paint buffer + * in-place). + */ + if (paint_core->applicator || + paint_buffer != paintbrush->paint_buffer || + paint_pixmap != paintbrush->paint_pixmap || + (! paint_pixmap && (gimp_rgba_distance (&paint_color, + &paintbrush->paint_color)))) + { + if (paint_buffer != paintbrush->paint_buffer) + { + if (paintbrush->paint_buffer) + { + g_object_remove_weak_pointer ( + G_OBJECT (paintbrush->paint_buffer), + (gpointer) &paintbrush->paint_buffer); + } + + paintbrush->paint_buffer = paint_buffer; + + g_object_add_weak_pointer ( + G_OBJECT (paintbrush->paint_buffer), + (gpointer) &paintbrush->paint_buffer); + } + + if (paint_pixmap != paintbrush->paint_pixmap) + { + g_clear_pointer (&paintbrush->paint_pixmap, gimp_temp_buf_unref); + + if (paint_pixmap) + paintbrush->paint_pixmap = gimp_temp_buf_ref (paint_pixmap); + } + + paintbrush->paint_color = paint_color; + + if (paint_pixmap) + { + gimp_brush_core_color_area_with_pixmap (brush_core, drawable, + coords, + paint_buffer, + paint_buffer_x, + paint_buffer_y, + FALSE); + } + else + { + GeglColor *color; + + color = gimp_gegl_color_new (&paint_color); + + gegl_buffer_set_color (paint_buffer, NULL, color); + + g_object_unref (color); + } + } + + if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE)) + force = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_FORCE, + coords, + paint_options, + fade_point); + else + force = paint_options->brush_force; + + /* finally, let the brush core paste the colored area on the canvas */ + gimp_brush_core_paste_canvas (brush_core, drawable, + coords, + MIN (opacity, GIMP_OPACITY_OPAQUE), + gimp_context_get_opacity (context), + paint_mode, + gimp_paint_options_get_brush_mode (paint_options), + force, + paint_appl_mode); + } +} diff --git a/app/paint/gimppaintbrush.h b/app/paint/gimppaintbrush.h new file mode 100644 index 0000000..61f8d85 --- /dev/null +++ b/app/paint/gimppaintbrush.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 . + */ + +#ifndef __GIMP_PAINTBRUSH_H__ +#define __GIMP_PAINTBRUSH_H__ + + +#include "gimpbrushcore.h" + + +#define GIMP_TYPE_PAINTBRUSH (gimp_paintbrush_get_type ()) +#define GIMP_PAINTBRUSH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PAINTBRUSH, GimpPaintbrush)) +#define GIMP_PAINTBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PAINTBRUSH, GimpPaintbrushClass)) +#define GIMP_IS_PAINTBRUSH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PAINTBRUSH)) +#define GIMP_IS_PAINTBRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PAINTBRUSH)) +#define GIMP_PAINTBRUSH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PAINTBRUSH, GimpPaintbrushClass)) + + +typedef struct _GimpPaintbrushClass GimpPaintbrushClass; + +struct _GimpPaintbrush +{ + GimpBrushCore parent_instance; + + GeglBuffer *paint_buffer; + const GimpTempBuf *paint_pixmap; + GimpRGB paint_color; +}; + +struct _GimpPaintbrushClass +{ + GimpBrushCoreClass parent_class; + + /* virtual functions */ + gboolean (* get_color_history_color) (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color); + void (* get_paint_params) (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble grad_point, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); +}; + + +void gimp_paintbrush_register (Gimp *gimp, + GimpPaintRegisterCallback callback); + +GType gimp_paintbrush_get_type (void) G_GNUC_CONST; + + +/* protected */ + +void _gimp_paintbrush_motion (GimpPaintCore *paint_core, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + gdouble opacity); + + +#endif /* __GIMP_PAINTBRUSH_H__ */ diff --git a/app/paint/gimppaintcore-loops.cc b/app/paint/gimppaintcore-loops.cc new file mode 100644 index 0000000..c92370d --- /dev/null +++ b/app/paint/gimppaintcore-loops.cc @@ -0,0 +1,2268 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 2013 Daniel Sabo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include +#include + +extern "C" +{ + +#include "paint-types.h" + +#include "operations/layer-modes/gimp-layer-modes.h" + +#include "core/gimptempbuf.h" + +#include "operations/gimpoperationmaskcomponents.h" + +#include "operations/layer-modes/gimpoperationlayermode.h" + +#include "gimppaintcore-loops.h" + +} /* extern "C" */ + + +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + + +/* In order to avoid iterating over the same region of the same buffers + * multiple times, when calling more than one of the paint-core loop functions + * (hereafter referred to as "algorithms") in succession, we provide a single + * function, gimp_paint_core_loops_process(), which can be used to perform + * multiple algorithms in a row. This function takes a pointer to a + * GimpPaintCoreLoopsParams structure, providing the parameters for the + * algorithms, and a GimpPaintCoreLoopsAlgorithm bitset, which specifies the + * set of algorithms to run; currently, the algorithms are always run in a + * fixed order. For convenience, we provide public functions for the + * individual algorithms, but they're merely wrappers around + * gimp_paint_core_loops_process(). + * + * We use some C++ magic to statically generate specialized versions of + * gimp_paint_core_loops_process() for all possible combinations of algorithms, + * and, where relevant, formats and input parameters, and to dispatch to the + * correct version at runtime. + * + * To achieve this, each algorithm provides two components: + * + * - The algorithm class template, which implements the algorithm, following + * a common interface. See the AlgorithmBase class for a description of + * the interface. Each algorithm class takes its base class as a template + * parameter, which allows us to construct a class hierarchy corresponding + * to a specific set of algorithms. Some classes in the hierarchy are not + * algorithms themselves, but are rather helpers, which provide some + * functionality to the algorithms further down the hierarchy, such as + * access to specific buffers. + * + * - A dispatch function, which takes the input parameters, the requested set + * of algorithms, the (type of) the current algorithm hierarchy, and a + * visitor object. The function calls the visitor with a (potentially) + * modified hierarchy, depending on the input. Ihe dispatch function for + * an algorithm checks if the requested set of algorithms contains a + * certain algorithm, adds the said algorithm to the hierarchy accordingly, + * and calls the visitor with the new hierarchy. See the AlgorithmDispatch + * class, which provides a dispatch-function implementation which + * algorithms can use instead of providing their own dispatch function. + * + * Helper classes in the hierarchy may also provide dispatch functions, + * which likewise modify the hierarchy based on the input parameters. For + * example, the dispatch_paint_mask() function adds a certain PaintMask + * specialization to the hierarchy, depending on the format of the paint + * mask buffer; this can be used to specialize algorithms based on the mask + * format; an algorithm that depends on the paint mask may dispatch through + * this function, before modifying the hierarchy itself. + * + * The dispatch() function is used to construct an algorithm hierarchy by + * dispatching through a list of functions. gimp_paint_core_loops_process() + * calls dispatch() with the full list of algorithm dispatch functions, + * receiving in return the algorithm hierarchy matching the input. It then + * uses the algorithm interface to perform the actual processing. + */ + + +enum +{ + ALGORITHM_PAINT_BUF = 1u << 31, + ALGORITHM_PAINT_MASK = 1u << 30, + ALGORITHM_STIPPLE = 1u << 29, + ALGORITHM_COMP_MASK = 1u << 28, + ALGORITHM_TEMP_COMP_MASK = 1u << 27, + ALGORITHM_COMP_BUFFER = 1u << 26, + ALGORITHM_TEMP_COMP_BUFFER = 1u << 25, + ALGORITHM_CANVAS_BUFFER_ITERATOR = 1u << 24, + ALGORITHM_MASK_BUFFER_ITERATOR = 1u << 23 +}; + + +template +struct identity +{ + using type = T; +}; + + +/* dispatch(): + * + * Takes a list of dispatch function objects, and calls each of them, in order, + * with the same 'params' and 'algorithms' parameters, passing 'algorithm' as + * the input hierarchy to the first dispatch function, and passing the output + * hierarchy of the previous dispatch function as the input hierarchy for the + * next dispatch function. Calls 'visitor' with the output hierarchy of the + * last dispatch function. + * + * Each algorithm hierarchy should provide a 'filter' static data member, and + * each dispatch function object should provide a 'mask' static data member. + * If the bitwise-AND of the current hierarchy's 'filter' member and the + * current dispatch function's 'mask' member is equal to 'mask', the dispatch + * function is skipped. This can be used to make sure that a class appears + * only once in the hierarchy, even if its dispatch function is used multiple + * times, or to prevent an algorithm from being dispatched, if it cannot be + * used together with another algorithm. + */ + +template +static inline void +dispatch (Visitor visitor, + const GimpPaintCoreLoopsParams *params, + GimpPaintCoreLoopsAlgorithm algorithms, + identity algorithm) +{ + visitor (algorithm); +} + +template +struct dispatch_impl +{ + template + static void + apply (Visitor visitor, + const GimpPaintCoreLoopsParams *params, + GimpPaintCoreLoopsAlgorithm algorithms, + identity algorithm, + Dispatch disp, + DispatchRest... disp_rest) + { + disp ( + [&] (auto algorithm) + { + dispatch (visitor, params, algorithms, algorithm, disp_rest...); + }, + params, algorithms, algorithm); + } +}; + +template +struct dispatch_impl +{ + template + static void + apply (Visitor visitor, + const GimpPaintCoreLoopsParams *params, + GimpPaintCoreLoopsAlgorithm algorithms, + identity algorithm, + Dispatch disp, + DispatchRest... disp_rest) + { + dispatch (visitor, params, algorithms, algorithm, disp_rest...); + } +}; + +template +static inline void +dispatch (Visitor visitor, + const GimpPaintCoreLoopsParams *params, + GimpPaintCoreLoopsAlgorithm algorithms, + identity algorithm, + Dispatch disp, + DispatchRest... disp_rest) +{ + dispatch_impl::apply ( + visitor, params, algorithms, algorithm, disp, disp_rest...); +} + + +/* value_to_float(): + * + * Converts a component value to float. + */ + +static inline gfloat +value_to_float (guint8 value) +{ + return value / 255.0f; +} + +static inline gfloat +value_to_float (gfloat value) +{ + return value; +} + +template +static inline gfloat +value_to_float (T value) = delete; + + +/* AlgorithmBase: + * + * The base class of the algorithm hierarchy. + */ + +struct AlgorithmBase +{ + /* Used to filter-out dispatch functions; see the description of dispatch(). + * Algorithms that redefine 'filter' should bitwise-OR their filter with that + * of their base class. + */ + static constexpr guint filter = 0; + + /* The current maximal number of iterators used by the hierarchy. Algorithms + * should redefine 'max_n_iterators' by adding the maximal number of + * iterators they use to this value. + */ + static constexpr gint max_n_iterators = 0; + + /* Non-static data members should be initialized in the constructor, and + * should not be further modified. + */ + explicit + AlgorithmBase (const GimpPaintCoreLoopsParams *params) + { + } + + /* Algorithms should store their dynamic state in the 'State' member class + * template. This template will be instantiated with the most-derived type + * of the hierarchy, which allows an algorithm to depend on the properties of + * its descendants. Algorithms that provide their own 'State' class should + * derive it from the 'State' class of their base class, passing 'Derived' as + * the template argument. + * + * Algorithms can be run in parallel on multiple threads. In this case, each + * thread uses its own 'State' object, while the algorithm object itself is + * either shared, or is a copy of a shared algorithm object. Either way, the + * algorithm object itself is immutable, while the state object is mutable. + */ + template + struct State + { + }; + + /* The 'init()' function is called once per state object before processing + * starts, and should initialize the state object, and, if necessary, the + * iterator. + * + * 'params' is the same parameter struct passed to the constructor. 'state' + * is the state object. 'iter' is the iterator; each distinct state object + * uses a distinct iterator; if the algorithm hierarchy doesn't use any + * iterator, 'iter' may be NULL. 'roi' is the full region to be processed. + * 'area' is the subregion to be processed by the current state object. + * + * An algorithm that overrides this function should call the 'init()' + * function of its base class first, using the same arguments. + */ + template + void + init (const GimpPaintCoreLoopsParams *params, + State *state, + GeglBufferIterator *iter, + const GeglRectangle *roi, + const GeglRectangle *area) const + { + } + + /* The 'init_step()' function is called once after each + * 'gegl_buffer_iterator_next()' call, and should perform any necessary + * initialization required before processing the current chunk. + * + * The parameters are the same as for 'init()', with the addition of 'rect', + * which is the area of the current chunk. + * + * An algorithm that overrides this function should call the 'init_step()' + * function of its base class first, using the same arguments. + */ + template + void + init_step (const GimpPaintCoreLoopsParams *params, + State *state, + GeglBufferIterator *iter, + const GeglRectangle *roi, + const GeglRectangle *area, + const GeglRectangle *rect) const + { + } + + /* The 'process_row()' function is called for each row in the current chunk, + * and should perform the actual processing. + * + * The parameters are the same as for 'init_step()', with the addition of + * 'y', which is the current row. + * + * An algorithm that overrides this function should call the 'process_row()' + * function of its base class first, using the same arguments. + */ + template + void + process_row (const GimpPaintCoreLoopsParams *params, + State *state, + GeglBufferIterator *iter, + const GeglRectangle *roi, + const GeglRectangle *area, + const GeglRectangle *rect, + gint y) const + { + } + + /* The 'finalize_step()' function is called once per chunk after its + * processing is done, and should finalize any chunk-specific resources of + * the state object. + * + * 'params' is the same parameter struct passed to the constructor. 'state' + * is the state object. + * + * An algorithm that overrides this function should call the + * 'finalize_step()' function of its base class after performing its own + * finalization, using the same arguments. + */ + template + void + finalize_step (const GimpPaintCoreLoopsParams *params, + State *state) const + { + } + + /* The 'finalize()' function is called once per state object after processing + * is done, and should finalize the state object. + * + * 'params' is the same parameter struct passed to the constructor. 'state' + * is the state object. + * + * An algorithm that overrides this function should call the 'finalize()' + * function of its base class after performing its own finalization, using + * the same arguments. + */ + template + void + finalize (const GimpPaintCoreLoopsParams *params, + State *state) const + { + } +}; + + +/* BasicDispatch: + * + * A class template implementing a simple dispatch function object, which adds + * an algorithm to the hierarchy unconditionally. 'AlgorithmTemplate' is the + * alogithm class template (usually a helper class, rather than an actual + * algorithm), 'Mask' is the dispatch function mask, as described in + * 'dispatch()', and 'Dependencies' is a list of (types of) dispatch functions + * the algorithm depends on. + * + * Before adding the algorithm to the hierarchy, the hierarchy is augmented by + * dispatching through the list of dependencies, in order. + */ + +template